/* * Copyright (C) 2014 The Dagger Authors. * * 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 static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive; import static java.lang.annotation.RetentionPolicy.RUNTIME; import androidx.room.compiler.processing.XConstructorElement; import androidx.room.compiler.processing.XMethodElement; import androidx.room.compiler.processing.XProcessingEnv; import androidx.room.compiler.processing.XType; import androidx.room.compiler.processing.XTypeElement; import com.google.common.util.concurrent.ListenableFuture; import com.google.testing.compile.CompilationRule; import dagger.Component; import dagger.Module; import dagger.Provides; import dagger.internal.codegen.binding.KeyFactory; import dagger.internal.codegen.javac.JavacPluginModule; import dagger.internal.codegen.model.Key; import dagger.multibindings.ElementsIntoSet; import dagger.multibindings.IntoSet; import dagger.producers.ProducerModule; import dagger.producers.Produces; import java.lang.annotation.Retention; import java.util.Set; import javax.inject.Inject; import javax.inject.Qualifier; import javax.inject.Singleton; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests {@link Key}. */ @RunWith(JUnit4.class) public class KeyFactoryTest { @Rule public CompilationRule compilationRule = new CompilationRule(); @Inject XProcessingEnv processingEnv; @Inject KeyFactory keyFactory; @Before public void setUp() { DaggerKeyFactoryTest_TestComponent.builder() .javacPluginModule( new JavacPluginModule(compilationRule.getElements(), compilationRule.getTypes())) .build() .inject(this); } @Test public void forInjectConstructorWithResolvedType() { XTypeElement typeElement = processingEnv.requireTypeElement(InjectedClass.class.getCanonicalName()); XConstructorElement constructor = getOnlyElement(typeElement.getConstructors()); Key key = keyFactory.forInjectConstructorWithResolvedType( constructor.getEnclosingElement().getType()); assertThat(key.toString()).isEqualTo("dagger.internal.codegen.KeyFactoryTest.InjectedClass"); } static final class InjectedClass { @SuppressWarnings("unused") @Inject InjectedClass(String s, int i) {} } @Test public void forProvidesMethod() { XTypeElement moduleElement = processingEnv.requireTypeElement(ProvidesMethodModule.class.getCanonicalName()); XMethodElement providesMethod = getOnlyElement(moduleElement.getDeclaredMethods()); Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement); assertThat(key.toString()).isEqualTo("java.lang.String"); } @Module static final class ProvidesMethodModule { @Provides String provideString() { throw new UnsupportedOperationException(); } } @Test public void forProvidesMethod_qualified() { XType stringType = processingEnv.requireType(String.class.getCanonicalName()); XTypeElement qualifierElement = processingEnv.requireTypeElement(TestQualifier.class.getCanonicalName()); XTypeElement moduleElement = processingEnv.requireTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName()); XMethodElement providesMethod = getOnlyElement(moduleElement.getDeclaredMethods()); Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement); assertThat(key.qualifier().get().className()).isEqualTo(qualifierElement.getClassName()); assertThat(key.type().xprocessing().getTypeName()).isEqualTo(stringType.getTypeName()); assertThat(key.toString()) .isEqualTo( "@dagger.internal.codegen.KeyFactoryTest.TestQualifier({" + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation(" + "param1=1, value=\"value a\"), " + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation(" + "param1=2, value=\"value b\"), " + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation(" + "param1=3145, value=\"default\")" + "}) java.lang.String"); } @Test public void qualifiedKeyEquivalents() { XTypeElement moduleElement = processingEnv.requireTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName()); XMethodElement providesMethod = getOnlyElement(moduleElement.getDeclaredMethods()); Key provisionKey = keyFactory.forProvidesMethod(providesMethod, moduleElement); assertThat(provisionKey.toString()) .isEqualTo( "@dagger.internal.codegen.KeyFactoryTest.TestQualifier({" + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation(" + "param1=1, value=\"value a\"), " + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation(" + "param1=2, value=\"value b\"), " + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation(" + "param1=3145, value=\"default\")" + "}) java.lang.String"); } @Module static final class QualifiedProvidesMethodModule { @Provides @TestQualifier({ @InnerAnnotation(value = "value a", param1 = 1), // please note the order of 'param' and 'value' is inverse @InnerAnnotation(param1 = 2, value = "value b"), @InnerAnnotation() }) static String provideQualifiedString() { throw new UnsupportedOperationException(); } } static final class QualifiedFieldHolder { @TestQualifier({ @InnerAnnotation(value = "value a", param1 = 1), // please note the order of 'param' and 'value' is inverse @InnerAnnotation(param1 = 2, value = "value b"), @InnerAnnotation() }) String aString; } @Retention(RUNTIME) @Qualifier @interface TestQualifier { InnerAnnotation[] value(); } @interface InnerAnnotation { int param1() default 3145; String value() default "default"; } @Test public void forProvidesMethod_sets() { XTypeElement moduleElement = processingEnv.requireTypeElement(SetProvidesMethodsModule.class.getCanonicalName()); for (XMethodElement providesMethod : moduleElement.getDeclaredMethods()) { Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement); assertThat(key.toString()) .isEqualTo( String.format( "java.util.Set " + "dagger.internal.codegen.KeyFactoryTest.SetProvidesMethodsModule#%s", getSimpleName(providesMethod))); } } @Module static final class SetProvidesMethodsModule { @Provides @IntoSet String provideString() { throw new UnsupportedOperationException(); } @Provides @ElementsIntoSet Set provideStrings() { throw new UnsupportedOperationException(); } } @Module static final class PrimitiveTypes { @Provides int foo() { return 0; } } @Module static final class BoxedPrimitiveTypes { @Provides Integer foo() { return 0; } } @Test public void primitiveKeysMatchBoxedKeys() { XTypeElement primitiveHolder = processingEnv.requireTypeElement(PrimitiveTypes.class.getCanonicalName()); XMethodElement intMethod = getOnlyElement(primitiveHolder.getDeclaredMethods()); XTypeElement boxedPrimitiveHolder = processingEnv.requireTypeElement(BoxedPrimitiveTypes.class.getCanonicalName()); XMethodElement integerMethod = getOnlyElement(boxedPrimitiveHolder.getDeclaredMethods()); // TODO(cgruber): Truth subject for TypeMirror and TypeElement XType intType = intMethod.getReturnType(); assertThat(isPrimitive(intType)).isTrue(); XType integerType = integerMethod.getReturnType(); assertThat(isPrimitive(integerType)).isFalse(); assertWithMessage("type equality").that(intType.isSameType(integerType)).isFalse(); Key intKey = keyFactory.forProvidesMethod(intMethod, primitiveHolder); Key integerKey = keyFactory.forProvidesMethod(integerMethod, boxedPrimitiveHolder); assertThat(intKey).isEqualTo(integerKey); assertThat(intKey.toString()).isEqualTo("java.lang.Integer"); assertThat(integerKey.toString()).isEqualTo("java.lang.Integer"); } @Test public void forProducesMethod() { XTypeElement moduleElement = processingEnv.requireTypeElement(ProducesMethodsModule.class.getCanonicalName()); for (XMethodElement producesMethod : moduleElement.getDeclaredMethods()) { Key key = keyFactory.forProducesMethod(producesMethod, moduleElement); assertThat(key.toString()).isEqualTo("java.lang.String"); } } @ProducerModule static final class ProducesMethodsModule { @Produces String produceString() { throw new UnsupportedOperationException(); } @Produces ListenableFuture produceFutureString() { throw new UnsupportedOperationException(); } } @Test public void forProducesMethod_sets() { XTypeElement moduleElement = processingEnv.requireTypeElement(SetProducesMethodsModule.class.getCanonicalName()); for (XMethodElement producesMethod : moduleElement.getDeclaredMethods()) { Key key = keyFactory.forProducesMethod(producesMethod, moduleElement); assertThat(key.toString()) .isEqualTo( String.format( "java.util.Set " + "dagger.internal.codegen.KeyFactoryTest.SetProducesMethodsModule#%s", getSimpleName(producesMethod))); } } @ProducerModule static final class SetProducesMethodsModule { @Produces @IntoSet String produceString() { throw new UnsupportedOperationException(); } @Produces @IntoSet ListenableFuture produceFutureString() { throw new UnsupportedOperationException(); } @Produces @ElementsIntoSet Set produceStrings() { throw new UnsupportedOperationException(); } @Produces @ElementsIntoSet ListenableFuture> produceFutureStrings() { throw new UnsupportedOperationException(); } } @Singleton @Component(modules = JavacPluginModule.class) interface TestComponent { void inject(KeyFactoryTest test); } }