Creating a canonical form for Keys with arrays in them.
Fixing Serialization for Key, CreationException and TypeLiteral.
More SerializationTests.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@492 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/CreationException.java b/src/com/google/inject/CreationException.java
index 819c7cf..ba78de4 100644
--- a/src/com/google/inject/CreationException.java
+++ b/src/com/google/inject/CreationException.java
@@ -17,12 +17,8 @@
package com.google.inject;
import com.google.inject.spi.Message;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Formatter;
-import java.util.List;
+
+import java.util.*;
/**
* Thrown when errors occur while creating a {@link Injector}. Includes a list
@@ -45,7 +41,7 @@
this.errorMessages = new ArrayList<Message>(errorMessages);
Collections.sort(this.errorMessages, new Comparator<Message>() {
public int compare(Message a, Message b) {
- return a.getSourceString().compareTo(b.getSourceString());
+ return a.getSource().compareTo(b.getSource());
}
});
}
@@ -59,7 +55,7 @@
Formatter fmt = new Formatter().format("Guice configuration errors:%n%n");
int index = 1;
for (Message errorMessage : errorMessages) {
- fmt.format("%s) Error at %s:%n", index++, errorMessage.getSourceString())
+ fmt.format("%s) Error at %s:%n", index++, errorMessage.getSource())
.format(" %s%n%n", errorMessage.getMessage());
}
return fmt.format("%s error[s]", errorMessages.size()).toString();
diff --git a/src/com/google/inject/Key.java b/src/com/google/inject/Key.java
index 3c6ebfc..b96822a 100644
--- a/src/com/google/inject/Key.java
+++ b/src/com/google/inject/Key.java
@@ -43,12 +43,12 @@
*
* @author crazybob@google.com (Bob Lee)
*/
-public abstract class Key<T> implements Serializable {
+public class Key<T> implements Serializable {
- final AnnotationStrategy annotationStrategy;
+ private final AnnotationStrategy annotationStrategy;
- final TypeLiteral<T> typeLiteral;
- final int hashCode;
+ private final TypeLiteral<T> typeLiteral;
+ private final int hashCode;
/**
* Constructs a new key. Derives the type from this class's type parameter.
@@ -104,7 +104,7 @@
*/
@SuppressWarnings("unchecked")
protected Key() {
- this.annotationStrategy = NULL_STRATEGY;
+ this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
this.typeLiteral
= (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass());
this.hashCode = computeHashCode();
@@ -135,21 +135,21 @@
/**
* Gets the key type.
*/
- public TypeLiteral<T> getTypeLiteral() {
+ public final TypeLiteral<T> getTypeLiteral() {
return typeLiteral;
}
/**
* Gets the annotation type.
*/
- public Class<? extends Annotation> getAnnotationType() {
+ public final Class<? extends Annotation> getAnnotationType() {
return annotationStrategy.getAnnotationType();
}
/**
* Gets the annotation.
*/
- public Annotation getAnnotation() {
+ public final Annotation getAnnotation() {
return annotationStrategy.getAnnotation();
}
@@ -167,10 +167,6 @@
return annotationStrategy.getAnnotationType().toString();
}
- public int hashCode() {
- return this.hashCode;
- }
-
Class<? super T> getRawType() {
return typeLiteral.getRawType();
}
@@ -182,7 +178,7 @@
return ofType(typeLiteral.providerType());
}
- public boolean equals(Object o) {
+ @Override public final boolean equals(Object o) {
if (o == this) {
return true;
}
@@ -194,7 +190,11 @@
&& typeLiteral.equals(other.typeLiteral);
}
- public String toString() {
+ @Override public final int hashCode() {
+ return this.hashCode;
+ }
+
+ @Override public final String toString() {
return new ToStringBuilder(Key.class)
.add("type", typeLiteral)
.add("annotation", annotationStrategy)
@@ -206,14 +206,14 @@
*/
static <T> Key<T> get(Class<T> type,
AnnotationStrategy annotationStrategy) {
- return new SimpleKey<T>(type, annotationStrategy);
+ return new Key<T>(type, annotationStrategy);
}
/**
* Gets a key for an injection type.
*/
public static <T> Key<T> get(Class<T> type) {
- return new SimpleKey<T>(type, NULL_STRATEGY);
+ return new Key<T>(type, NullAnnotationStrategy.INSTANCE);
}
/**
@@ -221,21 +221,21 @@
*/
public static <T> Key<T> get(Class<T> type,
Class<? extends Annotation> annotationType) {
- return new SimpleKey<T>(type, strategyFor(annotationType));
+ return new Key<T>(type, strategyFor(annotationType));
}
/**
* Gets a key for an injection type and an annotation.
*/
public static <T> Key<T> get(Class<T> type, Annotation annotation) {
- return new SimpleKey<T>(type, strategyFor(annotation));
+ return new Key<T>(type, strategyFor(annotation));
}
/**
* Gets a key for an injection type.
*/
public static Key<?> get(Type type) {
- return new SimpleKey<Object>(type, NULL_STRATEGY);
+ return new Key<Object>(type, NullAnnotationStrategy.INSTANCE);
}
/**
@@ -243,21 +243,21 @@
*/
public static Key<?> get(Type type,
Class<? extends Annotation> annotationType) {
- return new SimpleKey<Object>(type, strategyFor(annotationType));
+ return new Key<Object>(type, strategyFor(annotationType));
}
/**
* Gets a key for an injection type and an annotation.
*/
public static Key<?> get(Type type, Annotation annotation) {
- return new SimpleKey<Object>(type, strategyFor(annotation));
+ return new Key<Object>(type, strategyFor(annotation));
}
/**
* Gets a key for an injection type.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
- return new SimpleKey<T>(typeLiteral, NULL_STRATEGY);
+ return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE);
}
/**
@@ -265,7 +265,7 @@
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
Class<? extends Annotation> annotationType) {
- return new SimpleKey<T>(typeLiteral, strategyFor(annotationType));
+ return new Key<T>(typeLiteral, strategyFor(annotationType));
}
/**
@@ -273,7 +273,7 @@
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
Annotation annotation) {
- return new SimpleKey<T>(typeLiteral, strategyFor(annotation));
+ return new Key<T>(typeLiteral, strategyFor(annotation));
}
/**
@@ -281,7 +281,7 @@
* key.
*/
<T> Key<T> ofType(Class<T> type) {
- return new SimpleKey<T>(type, annotationStrategy);
+ return new Key<T>(type, annotationStrategy);
}
/**
@@ -289,7 +289,7 @@
* key.
*/
Key<?> ofType(Type type) {
- return new SimpleKey<Object>(type, annotationStrategy);
+ return new Key<Object>(type, annotationStrategy);
}
/**
@@ -297,12 +297,11 @@
* key.
*/
<T> Key<T> ofType(TypeLiteral<T> type) {
- return new SimpleKey<T>(type, annotationStrategy);
+ return new Key<T>(type, annotationStrategy);
}
/**
* Returns true if this key has annotation attributes.
- * @return
*/
boolean hasAttributes() {
return annotationStrategy.hasAttributes();
@@ -313,19 +312,7 @@
* annotation type.
*/
Key<T> withoutAttributes() {
- return new SimpleKey<T>(typeLiteral, annotationStrategy.withoutAttributes());
- }
-
- private static class SimpleKey<T> extends Key<T> {
-
- private SimpleKey(Type type, AnnotationStrategy annotationStrategy) {
- super(type, annotationStrategy);
- }
-
- private SimpleKey(TypeLiteral<T> typeLiteral,
- AnnotationStrategy annotationStrategy) {
- super(typeLiteral, annotationStrategy);
- }
+ return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
}
interface AnnotationStrategy extends Serializable {
@@ -336,42 +323,6 @@
AnnotationStrategy withoutAttributes();
}
- static final AnnotationStrategy NULL_STRATEGY = new AnnotationStrategy() {
-
- public boolean hasAttributes() {
- return false;
- }
-
- public AnnotationStrategy withoutAttributes() {
- throw new UnsupportedOperationException("Key already has no attributes.");
- }
-
- public Annotation getAnnotation() {
- return null;
- }
-
- public Class<? extends Annotation> getAnnotationType() {
- return null;
- }
-
- public boolean equals(Object o) {
- return o == NULL_STRATEGY;
- }
-
- public int hashCode() {
- return 0;
- }
-
- public String toString() {
- return "[none]";
- }
-
- Object readResolve() {
- return NULL_STRATEGY;
- }
- private static final long serialVersionUID = 0;
- };
-
/**
* Returns {@code true} if the given annotation type has no attributes.
*/
@@ -424,6 +375,30 @@
}
}
+ static enum NullAnnotationStrategy implements AnnotationStrategy {
+ INSTANCE;
+
+ public boolean hasAttributes() {
+ return false;
+ }
+
+ public AnnotationStrategy withoutAttributes() {
+ throw new UnsupportedOperationException("Key already has no attributes.");
+ }
+
+ public Annotation getAnnotation() {
+ return null;
+ }
+
+ public Class<? extends Annotation> getAnnotationType() {
+ return null;
+ }
+
+ @Override public String toString() {
+ return "[none]";
+ }
+ }
+
// this class not test-covered
static class AnnotationInstanceStrategy implements AnnotationStrategy {
@@ -449,7 +424,7 @@
return annotation.annotationType();
}
- public boolean equals(Object o) {
+ @Override public boolean equals(Object o) {
if (!(o instanceof AnnotationInstanceStrategy)) {
return false;
}
@@ -458,11 +433,11 @@
return annotation.equals(other.annotation);
}
- public int hashCode() {
+ @Override public int hashCode() {
return annotation.hashCode();
}
- public String toString() {
+ @Override public String toString() {
return annotation.toString();
}
@@ -498,7 +473,7 @@
return annotationType;
}
- public boolean equals(Object o) {
+ @Override public boolean equals(Object o) {
if (!(o instanceof AnnotationTypeStrategy)) {
return false;
}
@@ -507,11 +482,11 @@
return annotationType.equals(other.annotationType);
}
- public int hashCode() {
+ @Override public int hashCode() {
return annotationType.hashCode();
}
- public String toString() {
+ @Override public String toString() {
return "@" + annotationType.getName();
}
@@ -527,5 +502,16 @@
return annotationType.isAnnotationPresent(BindingAnnotation.class);
}
+ /**
+ * Returns the canonical form of this key for serialization. The returned
+ * instance is always a {@code Key}, never a subclass. This prevents problems
+ * caused by serializing anonymous types.
+ */
+ protected final Object writeReplace() {
+ return getClass() == Key.class
+ ? this
+ : new Key<T>(typeLiteral, annotationStrategy);
+ }
+
private static final long serialVersionUID = 0;
}
diff --git a/src/com/google/inject/ProviderMethods.java b/src/com/google/inject/ProviderMethods.java
index 20acdef..a6d021b 100644
--- a/src/com/google/inject/ProviderMethods.java
+++ b/src/com/google/inject/ProviderMethods.java
@@ -16,10 +16,11 @@
package com.google.inject;
-import com.google.inject.internal.StackTraceElements;
import com.google.inject.internal.ErrorMessages;
+import com.google.inject.internal.StackTraceElements;
import com.google.inject.spi.SourceProvider;
import com.google.inject.spi.SourceProviders;
+
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -28,7 +29,7 @@
import java.util.List;
/**
- * Creates bindings to methods annotated with
+ * Creates bindings to methods annotated with {@literal @}
* {@link com.google.inject.Provides}. Use the scope and binding annotations
* on the provider method to configure the binding.
*/
diff --git a/src/com/google/inject/TypeLiteral.java b/src/com/google/inject/TypeLiteral.java
index 2d80a31..932950d 100644
--- a/src/com/google/inject/TypeLiteral.java
+++ b/src/com/google/inject/TypeLiteral.java
@@ -18,6 +18,7 @@
import static com.google.inject.internal.Objects.nonNull;
import com.google.inject.internal.Types;
+import static com.google.inject.internal.Types.canonicalize;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
@@ -66,20 +67,22 @@
*/
@SuppressWarnings("unchecked")
TypeLiteral(Type type) {
- this.rawType = (Class<? super T>) Types.getRawType(nonNull(type, "type"));
- this.type = type;
- this.hashCode = Types.hashCode(type);
+ this.type = canonicalize(nonNull(type, "type"));
+ this.rawType = (Class<? super T>) Types.getRawType(this.type);
+ this.hashCode = Types.hashCode(this.type);
}
/**
- * Gets type from super class's type parameter.
+ * Returns the type from super class's type parameter in {@link
+ * Types#canonicalize(Type) canonical form}.
*/
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
- return ((ParameterizedType) superclass).getActualTypeArguments()[0];
+ ParameterizedType parameterized = (ParameterizedType) superclass;
+ return canonicalize(parameterized.getActualTypeArguments()[0]);
}
/**
@@ -141,8 +144,15 @@
return new TypeLiteral<T>(type);
}
+ /**
+ * Returns the canonical form of this type literal for serialization. The
+ * returned instance is always a {@code TypeLiteral}, never a subclass. This
+ * prevents problems caused by serializing anonymous types.
+ */
protected final Object writeReplace() {
- return get(Types.makeSerializable(type));
+ return getClass() == TypeLiteral.class
+ ? this
+ : get(type);
}
private static final long serialVersionUID = 0;
diff --git a/src/com/google/inject/internal/Types.java b/src/com/google/inject/internal/Types.java
index a18a563..1b4380c 100644
--- a/src/com/google/inject/internal/Types.java
+++ b/src/com/google/inject/internal/Types.java
@@ -30,11 +30,11 @@
private Types() {}
/**
- * Returns an equal Type that implements {@code Serializable}. Unfortunately,
- * the Sun JDK 1.5 Type classes (among others) are not serializable, so we
- * replace them with our own serializable implementations here.
+ * Returns a type that is functionally equal but not necessarily equal
+ * according to {@link Object#equals(Object) Object.equals()}. The returned
+ * type is {@link Serializable}.
*/
- public static Type makeSerializable(Type type) {
+ public static Type canonicalize(Type type) {
if (type instanceof ParameterizedTypeImpl
|| type instanceof GenericArrayTypeImpl) {
return type;
@@ -48,6 +48,10 @@
GenericArrayType g = (GenericArrayType) type;
return newGenericArrayType(g.getGenericComponentType());
+ } else if (type instanceof Class<?> && ((Class<?>) type).isArray()) {
+ Class<?> c = (Class<?>) type;
+ return newGenericArrayType(c.getComponentType());
+
} else {
// type is either serializable as-is or unsupported
return type;
@@ -198,11 +202,11 @@
private final Type[] typeArguments;
private ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
- this.ownerType = ownerType == null ? null : makeSerializable(ownerType);
- this.rawType = makeSerializable(rawType);
+ this.ownerType = ownerType == null ? null : canonicalize(ownerType);
+ this.rawType = canonicalize(rawType);
this.typeArguments = typeArguments.clone();
for (int t = 0; t < this.typeArguments.length; t++) {
- this.typeArguments[t] = makeSerializable(this.typeArguments[t]);
+ this.typeArguments[t] = canonicalize(this.typeArguments[t]);
}
}
@@ -238,7 +242,7 @@
private final Type componentType;
private GenericArrayTypeImpl(Type componentType) {
- this.componentType = componentType;
+ this.componentType = canonicalize(componentType);
}
public Type getGenericComponentType() {
diff --git a/src/com/google/inject/matcher/Matchers.java b/src/com/google/inject/matcher/Matchers.java
index 8ca5658..a9f4ff4 100644
--- a/src/com/google/inject/matcher/Matchers.java
+++ b/src/com/google/inject/matcher/Matchers.java
@@ -300,7 +300,7 @@
}
@Override public String toString() {
- return "package(" + targetPackage.getName() + ")";
+ return "inPackage(" + targetPackage.getName() + ")";
}
public Object readResolve() {
diff --git a/src/com/google/inject/name/Names.java b/src/com/google/inject/name/Names.java
index e0059f9..758538e 100644
--- a/src/com/google/inject/name/Names.java
+++ b/src/com/google/inject/name/Names.java
@@ -18,8 +18,8 @@
import com.google.inject.Binder;
import com.google.inject.Key;
-import com.google.inject.spi.SourceProvider;
import com.google.inject.spi.SourceProviders;
+
import java.util.Map;
import java.util.Properties;
@@ -49,7 +49,7 @@
public static void bindProperties(final Binder binder,
final Map<String, String> properties) {
SourceProviders.withDefault(
- new SimpleSourceProvider(SourceProviders.defaultSource()),
+ SourceProviders.defaultSource(),
new Runnable() {
public void run() {
for (Map.Entry<String, String> entry : properties.entrySet()) {
@@ -67,7 +67,7 @@
public static void bindProperties(final Binder binder,
final Properties properties) {
SourceProviders.withDefault(
- new SimpleSourceProvider(SourceProviders.defaultSource()),
+ SourceProviders.defaultSource(),
new Runnable() {
public void run() {
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
@@ -78,17 +78,4 @@
}
});
}
-
- static class SimpleSourceProvider implements SourceProvider {
-
- final Object source;
-
- SimpleSourceProvider(Object source) {
- this.source = source;
- }
-
- public Object source() {
- return source;
- }
- }
}
diff --git a/src/com/google/inject/spi/Message.java b/src/com/google/inject/spi/Message.java
index 35cd795..1bf78f5 100644
--- a/src/com/google/inject/spi/Message.java
+++ b/src/com/google/inject/spi/Message.java
@@ -18,19 +18,21 @@
import static com.google.inject.internal.Objects.nonNull;
+import java.io.Serializable;
+
/**
* A message. Contains a source pointing to the code which resulted
* in this message and a text message.
*
* @author crazybob@google.com (Bob Lee)
*/
-public class Message {
+public final class Message implements Serializable {
- final Object source;
- final String message;
+ private final String source;
+ private final String message;
public Message(Object source, String message) {
- this.source = nonNull(source, "source");
+ this.source = nonNull(source, "source").toString();
this.message = nonNull(message, "message");
}
@@ -39,22 +41,10 @@
}
/**
- * Gets the source of the configuration which resulted in this error message.
- */
- public Object getSource() {
- return source;
- }
-
- String sourceString = null;
-
- /**
* Returns a string representation of the source object.
*/
- public String getSourceString() {
- if (sourceString == null) {
- sourceString = source.toString();
- }
- return sourceString;
+ public String getSource() {
+ return source;
}
/**
@@ -65,7 +55,7 @@
}
public String toString() {
- return getSourceString() + " " + message;
+ return source + " " + message;
}
public int hashCode() {
diff --git a/test/com/google/inject/Asserts.java b/test/com/google/inject/Asserts.java
index b083d8b..507b9bc 100644
--- a/test/com/google/inject/Asserts.java
+++ b/test/com/google/inject/Asserts.java
@@ -76,4 +76,12 @@
throw new RuntimeException(e);
}
}
+
+ public static void assertNotSerializable(Object object) throws IOException {
+ try {
+ reserialize(object);
+ Assert.fail();
+ } catch (NotSerializableException expected) {
+ }
+ }
}
diff --git a/test/com/google/inject/BinderTest.java b/test/com/google/inject/BinderTest.java
index 7086cfa..bd62347 100644
--- a/test/com/google/inject/BinderTest.java
+++ b/test/com/google/inject/BinderTest.java
@@ -17,9 +17,11 @@
package com.google.inject;
import static com.google.inject.Asserts.assertContains;
+import static com.google.inject.Asserts.assertNotSerializable;
import com.google.inject.name.Names;
import junit.framework.TestCase;
+import java.io.IOException;
import java.util.List;
/**
@@ -40,7 +42,7 @@
}
});
- assertTrue(fooProvider.get() instanceof Foo);
+ assertNotNull(fooProvider.get());
}
static class Foo {}
@@ -94,6 +96,67 @@
}
}
+ public void testNothingIsSerializableInBinderApi() {
+ try {
+ Guice.createInjector(new AbstractModule() {
+ @Override public void configure() {
+ try {
+ assertNotSerializable(binder());
+ assertNotSerializable(getProvider(Integer.class));
+ assertNotSerializable(getProvider(Key.get(new TypeLiteral<List<String>>() {})));
+ assertNotSerializable(bind(Integer.class));
+ assertNotSerializable(bind(Integer.class).annotatedWith(Names.named("a")));
+ assertNotSerializable(bindConstant());
+ assertNotSerializable(bindConstant().annotatedWith(Names.named("b")));
+ } catch (IOException e) {
+ fail(e.getMessage());
+ }
+ }
+ });
+ fail();
+ } catch (CreationException ignored) {
+ }
+ }
+
+ /**
+ * Although {@code String[].class} isn't equal to {@code new
+ * GenericArrayTypeImpl(String.class)}, Guice should treat these two types
+ * interchangeably.
+ */
+ public void testArrayTypeCanonicalization() {
+ final String[] strings = new String[] { "A" };
+ final Integer[] integers = new Integer[] { 1 };
+
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bind(String[].class).toInstance(strings);
+ bind(new TypeLiteral<Integer[]>() {}).toInstance(integers);
+ }
+ });
+
+ assertSame(integers, injector.getInstance(Key.get(new TypeLiteral<Integer[]>() {})));
+ assertSame(integers, injector.getInstance(new Key<Integer[]>() {}));
+ assertSame(integers, injector.getInstance(Integer[].class));
+ assertSame(strings, injector.getInstance(Key.get(new TypeLiteral<String[]>() {})));
+ assertSame(strings, injector.getInstance(new Key<String[]>() {}));
+ assertSame(strings, injector.getInstance(String[].class));
+
+ try {
+ Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bind(String[].class).toInstance(strings);
+ bind(new TypeLiteral<String[]>() {}).toInstance(strings);
+ }
+ });
+ fail();
+ } catch (CreationException expected) {
+ Asserts.assertContains(expected.getMessage(),
+ "A binding to java.lang.String[] was already configured");
+ Asserts.assertContains(expected.getMessage(),
+ "1 error[s]");
+ }
+ }
+
// public void testBindInterfaceWithoutImplementation() {
// Guice.createInjector(new AbstractModule() {
// protected void configure() {
diff --git a/test/com/google/inject/InjectorTest.java b/test/com/google/inject/InjectorTest.java
index 40a3dc7..8edba4a 100644
--- a/test/com/google/inject/InjectorTest.java
+++ b/test/com/google/inject/InjectorTest.java
@@ -16,9 +16,12 @@
package com.google.inject;
+import static com.google.inject.Asserts.assertNotSerializable;
+import junit.framework.TestCase;
+
+import java.io.IOException;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
-import junit.framework.TestCase;
/**
* @author crazybob@google.com (Bob Lee)
@@ -108,6 +111,16 @@
assertEquals(5, (int) iw.i);
}
+ public void testInjectorApiIsNotSerializable() throws IOException {
+ Injector injector = Guice.createInjector();
+ assertNotSerializable(injector);
+ assertNotSerializable(injector.getProvider(String.class));
+ assertNotSerializable(injector.getBinding(String.class));
+ for (Binding<?> binding : injector.getBindings().values()) {
+ assertNotSerializable(binding);
+ }
+ }
+
static class IntegerWrapper {
@Inject @I Integer i;
}
diff --git a/test/com/google/inject/KeyTest.java b/test/com/google/inject/KeyTest.java
index 008a9d0..76e0e14 100644
--- a/test/com/google/inject/KeyTest.java
+++ b/test/com/google/inject/KeyTest.java
@@ -80,6 +80,7 @@
assertEqualWhenReserialized(Key.get(B[].class));
assertEqualWhenReserialized(Key.get(new TypeLiteral<Map<List<B>, B>>() {}));
assertEqualWhenReserialized(Key.get(new TypeLiteral<List<B[]>>() {}));
+ assertEqualWhenReserialized(new Key<List<B[]>>() {});
}
interface B {}
diff --git a/test/com/google/inject/ScopesTest.java b/test/com/google/inject/ScopesTest.java
index 29236d7..2ff3999 100644
--- a/test/com/google/inject/ScopesTest.java
+++ b/test/com/google/inject/ScopesTest.java
@@ -18,36 +18,128 @@
import junit.framework.TestCase;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* @author crazybob@google.com (Bob Lee)
*/
public class ScopesTest extends TestCase {
- public void testSingletonAnnotation() throws CreationException {
+ public void testSingletonAnnotation() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
- bind(SampleSingleton.class);
+ bind(AnnotatedSingleton.class);
}
});
assertSame(
- injector.getInstance(SampleSingleton.class),
- injector.getInstance(SampleSingleton.class));
+ injector.getInstance(AnnotatedSingleton.class),
+ injector.getInstance(AnnotatedSingleton.class));
}
- @Singleton
- static class SampleSingleton {}
-
- public void testOverriddingAnnotation()
- throws CreationException {
+ public void testBoundAsSingleton() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
- bind(SampleSingleton.class).in(Scopes.NO_SCOPE);
+ bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
+ }
+ });
+
+ assertSame(
+ injector.getInstance(BoundAsSingleton.class),
+ injector.getInstance(BoundAsSingleton.class));
+ }
+
+ public void testJustInTimeAnnotatedSingleton() {
+ Injector injector = Guice.createInjector();
+
+ assertSame(
+ injector.getInstance(AnnotatedSingleton.class),
+ injector.getInstance(AnnotatedSingleton.class));
+ }
+
+ public void testSingletonIsPerInjector() {
+ assertNotSame(
+ Guice.createInjector().getInstance(AnnotatedSingleton.class),
+ Guice.createInjector().getInstance(AnnotatedSingleton.class));
+ }
+
+ public void testOverriddingAnnotation() {
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bind(AnnotatedSingleton.class).in(Scopes.NO_SCOPE);
}
});
assertNotSame(
- injector.getInstance(SampleSingleton.class),
- injector.getInstance(SampleSingleton.class));
+ injector.getInstance(AnnotatedSingleton.class),
+ injector.getInstance(AnnotatedSingleton.class));
+ }
+
+ public void testAnnotatedSingletonsInProductionAreEager() {
+ int nextInstanceId = AnnotatedSingleton.nextInstanceId.intValue();
+
+ Guice.createInjector(Stage.PRODUCTION, new AbstractModule() {
+ protected void configure() {
+ bind(AnnotatedSingleton.class);
+ }
+ });
+
+ assertEquals(nextInstanceId + 1, AnnotatedSingleton.nextInstanceId.intValue());
+ }
+
+ public void testAnnotatedSingletonsInDevelopmentAreNotEager() {
+ int nextInstanceId = AnnotatedSingleton.nextInstanceId.intValue();
+
+ Guice.createInjector(Stage.DEVELOPMENT, new AbstractModule() {
+ protected void configure() {
+ bind(AnnotatedSingleton.class);
+ }
+ });
+
+ assertEquals(nextInstanceId, AnnotatedSingleton.nextInstanceId.intValue());
+ }
+
+ public void testBoundAsSingletonInProductionAreEager() {
+ int nextInstanceId = BoundAsSingleton.nextInstanceId.intValue();
+
+ Guice.createInjector(Stage.PRODUCTION, new AbstractModule() {
+ protected void configure() {
+ bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
+ }
+ });
+
+ assertEquals(nextInstanceId + 1, BoundAsSingleton.nextInstanceId.intValue());
+ }
+
+ public void testBoundAsSingletonInDevelopmentAreNotEager() {
+ int nextInstanceId = BoundAsSingleton.nextInstanceId.intValue();
+
+ Guice.createInjector(Stage.DEVELOPMENT, new AbstractModule() {
+ protected void configure() {
+ bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
+ }
+ });
+
+ assertEquals(nextInstanceId, BoundAsSingleton.nextInstanceId.intValue());
+ }
+
+ public void testSingletonScopeIsNotSerializable() throws IOException {
+ Asserts.assertNotSerializable(Scopes.SINGLETON);
+ }
+
+ public void testNoScopeIsNotSerializable() throws IOException {
+ Asserts.assertNotSerializable(Scopes.NO_SCOPE);
+ }
+
+ @Singleton
+ static class AnnotatedSingleton {
+ static final AtomicInteger nextInstanceId = new AtomicInteger(1);
+ final int instanceId = nextInstanceId.getAndIncrement();
+ }
+
+ static class BoundAsSingleton {
+ static final AtomicInteger nextInstanceId = new AtomicInteger(1);
+ final int instanceId = nextInstanceId.getAndIncrement();
}
}
diff --git a/test/com/google/inject/SerializationTest.java b/test/com/google/inject/SerializationTest.java
index 40da32a..ad93956 100644
--- a/test/com/google/inject/SerializationTest.java
+++ b/test/com/google/inject/SerializationTest.java
@@ -17,9 +17,7 @@
package com.google.inject;
-import static com.google.inject.Asserts.assertEqualWhenReserialized;
import static com.google.inject.Asserts.assertSimilarWhenReserialized;
-import junit.framework.Assert;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
@@ -40,13 +38,9 @@
protected void configure() {}
}
- public void testSingletonScopeIsNotSerializable() throws IOException {
- assertNotSerializable(Scopes.SINGLETON);
- }
-
public void testCreationExceptionIsSerializable() throws IOException {
try {
- assertEqualWhenReserialized(createCreationException());
+ assertSimilarWhenReserialized(createCreationException());
} catch (NotSerializableException e) {
fail("Known failure. CreationException is not Serializable.");
}
@@ -78,21 +72,9 @@
}
}
- public void testInjectorIsNotSerializable() throws IOException {
- assertNotSerializable(Guice.createInjector());
- }
-
static class A {
@Inject B b;
}
static class B {}
-
- public static void assertNotSerializable(Object object) throws IOException {
- try {
- Asserts.reserialize(object);
- Assert.fail();
- } catch (NotSerializableException expected) {
- }
- }
}
diff --git a/test/com/google/inject/TypeLiteralTest.java b/test/com/google/inject/TypeLiteralTest.java
index d061905..e87c17f 100644
--- a/test/com/google/inject/TypeLiteralTest.java
+++ b/test/com/google/inject/TypeLiteralTest.java
@@ -73,9 +73,7 @@
public void testEqualityOfGenericArrayAndClassArray() {
TypeLiteral<String[]> arrayAsClass = TypeLiteral.get(String[].class);
TypeLiteral<String[]> arrayAsType = new TypeLiteral<String[]>() {};
- assertEquals("Known failure. Although they're functionally equal, Java has two non-equal "
- + "Types that can represent a String[]. We should probably choose a canonical form.",
- arrayAsClass, arrayAsType);
+ assertEquals(arrayAsClass, arrayAsType);
}
public void testSerialization() throws IOException {
diff --git a/test/com/google/inject/matcher/MatcherTest.java b/test/com/google/inject/matcher/MatcherTest.java
index 9d1b077..bc6a3d8 100644
--- a/test/com/google/inject/matcher/MatcherTest.java
+++ b/test/com/google/inject/matcher/MatcherTest.java
@@ -17,6 +17,7 @@
package com.google.inject.matcher;
import static com.google.inject.Asserts.assertEqualWhenReserialized;
+import static com.google.inject.Asserts.assertEqualsBothWays;
import static com.google.inject.matcher.Matchers.*;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
@@ -35,21 +36,41 @@
public void testAny() {
assertTrue(any().matches(null));
+ assertEquals("any()", any().toString());
+ assertEqualsBothWays(any(), any());
+ assertFalse(any().equals(not(any())));
}
public void testNot() {
assertFalse(not(any()).matches(null));
+ assertEquals("not(any())", not(any()).toString());
+ assertEqualsBothWays(not(any()), not(any()));
+ assertFalse(not(any()).equals(any()));
}
public void testAnd() {
assertTrue(any().and(any()).matches(null));
assertFalse(any().and(not(any())).matches(null));
+ assertEquals("and(any(), any())", any().and(any()).toString());
+ assertEqualsBothWays(any().and(any()), any().and(any()));
+ assertFalse(any().and(any()).equals(not(any())));
+ }
+
+ public void testOr() {
+ assertTrue(any().or(not(any())).matches(null));
+ assertFalse(not(any()).or(not(any())).matches(null));
+ assertEquals("or(any(), any())", any().or(any()).toString());
+ assertEqualsBothWays(any().or(any()), any().or(any()));
+ assertFalse(any().or(any()).equals(not(any())));
}
public void testAnnotatedWith() {
assertTrue(annotatedWith(Foo.class).matches(Bar.class));
assertFalse(annotatedWith(Foo.class).matches(
MatcherTest.class.getMethods()[0]));
+ assertEquals("annotatedWith(Foo.class)", annotatedWith(Foo.class).toString());
+ assertEqualsBothWays(annotatedWith(Foo.class), annotatedWith(Foo.class));
+ assertFalse(annotatedWith(Foo.class).equals(annotatedWith(Named.class)));
try {
annotatedWith(Baz.class);
@@ -62,24 +83,36 @@
assertTrue(subclassesOf(Runnable.class).matches(Runnable.class));
assertTrue(subclassesOf(Runnable.class).matches(MyRunnable.class));
assertFalse(subclassesOf(Runnable.class).matches(Object.class));
+ assertEquals("subclassesOf(Runnable.class)", subclassesOf(Runnable.class).toString());
+ assertEqualsBothWays(subclassesOf(Runnable.class), subclassesOf(Runnable.class));
+ assertFalse(subclassesOf(Runnable.class).equals(subclassesOf(Object.class)));
}
public void testOnly() {
- assertTrue(only(1000).matches(new Integer(1000)));
- assertFalse(only(1).matches(new Integer(1000)));
+ assertTrue(only(1000).matches(1000));
+ assertFalse(only(1).matches(1000));
+ assertEquals("only(1)", only(1).toString());
+ assertEqualsBothWays(only(1), only(1));
+ assertFalse(only(1).equals(only(2)));
}
- public void testSameAs() {
+ @SuppressWarnings("UnnecessaryBoxing")
+ public void testIdenticalTo() {
Object o = new Object();
- assertTrue(only(o).matches(o));
- assertFalse(only(o).matches(new Object()));
+ assertEquals("identicalTo(1)", identicalTo(1).toString());
+ assertTrue(identicalTo(o).matches(o));
+ assertFalse(identicalTo(o).matches(new Object()));
+ assertEqualsBothWays(identicalTo(o), identicalTo(o));
+ assertFalse(identicalTo(1).equals(identicalTo(new Integer(1))));
}
public void testInPackage() {
- assertTrue(inPackage(Matchers.class.getPackage())
- .matches(MatcherTest.class));
- assertFalse(inPackage(Matchers.class.getPackage())
- .matches(Object.class));
+ Package matchersPackage = Matchers.class.getPackage();
+ assertEquals("inPackage(com.google.inject.matcher)", inPackage(matchersPackage).toString());
+ assertTrue(inPackage(matchersPackage).matches(MatcherTest.class));
+ assertFalse(inPackage(matchersPackage).matches(Object.class));
+ assertEqualsBothWays(inPackage(matchersPackage), inPackage(matchersPackage));
+ assertFalse(inPackage(matchersPackage).equals(inPackage(Object.class.getPackage())));
}
public void testReturns() throws NoSuchMethodException {
@@ -88,20 +121,23 @@
Object.class.getMethod("toString")));
assertFalse(predicate.matches(
Object.class.getMethod("hashCode")));
+ assertEquals("returns(only(class java.lang.String))", returns(only(String.class)).toString());
+ assertEqualsBothWays(predicate, returns(only(String.class)));
+ assertFalse(predicate.equals(returns(only(Integer.class))));
}
public void testSerialization() throws IOException {
- assertEqualWhenReserialized(Matchers.any());
- assertEqualWhenReserialized(Matchers.not(Matchers.any()));
- assertEqualWhenReserialized(Matchers.annotatedWith(Named.class));
- assertEqualWhenReserialized(Matchers.annotatedWith(Names.named("foo")));
- assertEqualWhenReserialized(Matchers.only("foo"));
- assertEqualWhenReserialized(Matchers.identicalTo(Object.class));
- assertEqualWhenReserialized(Matchers.inPackage(String.class.getPackage()));
- assertEqualWhenReserialized(Matchers.returns(Matchers.any()));
- assertEqualWhenReserialized(Matchers.subclassesOf(AbstractList.class));
- assertEqualWhenReserialized(Matchers.only("a").or(Matchers.only("b")));
- assertEqualWhenReserialized(Matchers.only("a").and(Matchers.only("b")));
+ assertEqualWhenReserialized(any());
+ assertEqualWhenReserialized(not(any()));
+ assertEqualWhenReserialized(annotatedWith(Named.class));
+ assertEqualWhenReserialized(annotatedWith(Names.named("foo")));
+ assertEqualWhenReserialized(only("foo"));
+ assertEqualWhenReserialized(identicalTo(Object.class));
+ assertEqualWhenReserialized(inPackage(String.class.getPackage()));
+ assertEqualWhenReserialized(returns(any()));
+ assertEqualWhenReserialized(subclassesOf(AbstractList.class));
+ assertEqualWhenReserialized(only("a").or(only("b")));
+ assertEqualWhenReserialized(only("a").and(only("b")));
}
static abstract class MyRunnable implements Runnable {}
diff --git a/test/com/google/inject/name/NamesTest.java b/test/com/google/inject/name/NamesTest.java
new file mode 100644
index 0000000..113ae49
--- /dev/null
+++ b/test/com/google/inject/name/NamesTest.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.google.inject.name;
+
+import static com.google.inject.Asserts.assertEqualWhenReserialized;
+import static com.google.inject.Asserts.assertEqualsBothWays;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+
+/**
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public class NamesTest extends TestCase {
+
+ @Named("foo") private String foo;
+ private Named namedFoo;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ namedFoo = getClass().getDeclaredField("foo").getAnnotation(Named.class);
+ }
+
+ public void testConsistentEqualsAndHashcode() {
+ Named actual = Names.named("foo");
+ assertEqualsBothWays(namedFoo, actual);
+ assertEquals(namedFoo.toString(), actual.toString());
+ }
+
+ public void testNamedIsSerializable() throws IOException {
+ assertEqualWhenReserialized(Names.named("foo"));
+ }
+}