/* * Copyright (C) 2014 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 dagger.internal.codegen; import com.google.auto.common.MoreTypes; import com.google.common.base.Equivalence; import com.google.common.base.Optional; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ListenableFuture; import com.google.testing.compile.CompilationRule; import dagger.Module; import dagger.Provides; import dagger.producers.ProducerModule; import dagger.producers.Produces; import java.util.Set; import javax.inject.Inject; import javax.inject.Qualifier; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; import static dagger.Provides.Type.SET; import static dagger.Provides.Type.SET_VALUES; /** * Tests {@link Key}. */ @RunWith(JUnit4.class) public class KeyTest { @Rule public CompilationRule compilationRule = new CompilationRule(); private Elements elements; private Types types; private Key.Factory keyFactory; @Before public void setUp() { this.types = compilationRule.getTypes(); this.elements = compilationRule.getElements(); this.keyFactory = new Key.Factory(types, elements); } @Test public void forInjectConstructorWithResolvedType() { TypeElement typeElement = compilationRule.getElements().getTypeElement(InjectedClass.class.getCanonicalName()); ExecutableElement constructor = Iterables.getOnlyElement(ElementFilter.constructorsIn(typeElement.getEnclosedElements())); assertThat( keyFactory.forInjectConstructorWithResolvedType(constructor.getEnclosingElement().asType())) .isEqualTo(new AutoValue_Key( Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(), MoreTypes.equivalence().wrap(typeElement.asType()))); } static final class InjectedClass { @SuppressWarnings("unused") @Inject InjectedClass(String s, int i) {} } @Test public void forProvidesMethod() { TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeElement moduleElement = elements.getTypeElement(ProvidesMethodModule.class.getCanonicalName()); ExecutableElement providesMethod = Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements())); assertThat( keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod)) .isEqualTo(new AutoValue_Key( Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(), MoreTypes.equivalence().wrap(stringType))); } @Module static final class ProvidesMethodModule { @Provides String provideString() { return null; } } @Test public void forProvidesMethod_qualified() { TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeElement qualifierElement = elements.getTypeElement(TestQualifier.class.getCanonicalName()); TypeElement moduleElement = elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName()); ExecutableElement providesMethod = Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements())); Key key = keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod); assertThat(MoreTypes.equivalence().wrap(key.qualifier().get().getAnnotationType())) .isEqualTo(MoreTypes.equivalence().wrap(qualifierElement.asType())); assertThat(key.wrappedType()).isEqualTo(MoreTypes.equivalence().wrap(stringType)); } @Test public void qualifiedKeyEquivalents() { TypeElement moduleElement = elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName()); ExecutableElement providesMethod = Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements())); Key provisionKey = keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod); TypeMirror type = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeElement injectableElement = elements.getTypeElement(QualifiedFieldHolder.class.getCanonicalName()); Element injectionField = Iterables.getOnlyElement(ElementFilter.fieldsIn(injectableElement.getEnclosedElements())); AnnotationMirror qualifier = Iterables.getOnlyElement(injectionField.getAnnotationMirrors()); Key injectionKey = keyFactory.forQualifiedType(Optional.<AnnotationMirror>of(qualifier), type); assertThat(provisionKey).isEqualTo(injectionKey); } @Module static final class QualifiedProvidesMethodModule { @Provides @TestQualifier(@InnerAnnotation) String provideQualifiedString() { return null; } } static final class QualifiedFieldHolder { @TestQualifier(@InnerAnnotation) String aString; } @Qualifier @interface TestQualifier { InnerAnnotation[] value(); } @interface InnerAnnotation {} @Test public void forProvidesMethod_sets() { TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName()); TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType); TypeElement moduleElement = elements.getTypeElement(SetProvidesMethodsModule.class.getCanonicalName()); for (ExecutableElement providesMethod : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) { assertThat( keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod)) .isEqualTo(new AutoValue_Key( Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(), MoreTypes.equivalence().wrap(setOfStringsType))); } } @Module static final class SetProvidesMethodsModule { @Provides(type = SET) String provideString() { return null; } @Provides(type = SET_VALUES) Set<String> provideStrings() { return null; } } @Module static final class PrimitiveTypes { @Provides int foo() { return 0; } } @Module static final class BoxedPrimitiveTypes { @Provides Integer foo() { return 0; } } @Test public void primitiveKeysMatchBoxedKeys() { TypeElement primitiveHolder = elements.getTypeElement(PrimitiveTypes.class.getCanonicalName()); ExecutableElement intMethod = Iterables.getOnlyElement(ElementFilter.methodsIn(primitiveHolder.getEnclosedElements())); TypeElement boxedPrimitiveHolder = elements.getTypeElement(BoxedPrimitiveTypes.class.getCanonicalName()); ExecutableElement integerMethod = Iterables.getOnlyElement( ElementFilter.methodsIn(boxedPrimitiveHolder.getEnclosedElements())); // TODO(cgruber): Truth subject for TypeMirror and TypeElement TypeMirror intType = intMethod.getReturnType(); assertThat(intType.getKind().isPrimitive()).isTrue(); TypeMirror integerType = integerMethod.getReturnType(); assertThat(integerType.getKind().isPrimitive()).isFalse(); assertThat(types.isSameType(intType, integerType)).named("type equality").isFalse(); Key intKey = keyFactory.forProvidesMethod((ExecutableType) intMethod.asType(), intMethod); Key integerKey = keyFactory.forProvidesMethod((ExecutableType) integerMethod.asType(), integerMethod); assertThat(intKey).isEqualTo(integerKey); } @Test public void forProducesMethod() { TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeElement moduleElement = elements.getTypeElement(ProducesMethodsModule.class.getCanonicalName()); for (ExecutableElement producesMethod : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) { assertThat(keyFactory.forProducesMethod( (ExecutableType) producesMethod.asType(), producesMethod)) .isEqualTo(new AutoValue_Key( Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(), MoreTypes.equivalence().wrap(stringType))); } } @ProducerModule static final class ProducesMethodsModule { @Produces String produceString() { return null; } @Produces ListenableFuture<String> produceFutureString() { return null; } } @Test public void forProducesMethod_sets() { TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName()); TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType); TypeElement moduleElement = elements.getTypeElement(SetProducesMethodsModule.class.getCanonicalName()); for (ExecutableElement producesMethod : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) { assertThat(keyFactory.forProducesMethod( (ExecutableType) producesMethod.asType(), producesMethod)) .isEqualTo(new AutoValue_Key( Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(), MoreTypes.equivalence().wrap(setOfStringsType))); } } @ProducerModule static final class SetProducesMethodsModule { @Produces(type = Produces.Type.SET) String produceString() { return null; } @Produces(type = Produces.Type.SET) ListenableFuture<String> produceFutureString() { return null; } @Produces(type = Produces.Type.SET_VALUES) Set<String> produceStrings() { return null; } @Produces(type = Produces.Type.SET_VALUES) ListenableFuture<Set<String>> produceFutureStrings() { return null; } } }