blob: f11f375e8b2686d1094bc6993d5b97af93829b12 [file] [log] [blame]
/*
* 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<java.lang.String> "
+ "dagger.internal.codegen.KeyFactoryTest.SetProvidesMethodsModule#%s",
getSimpleName(providesMethod)));
}
}
@Module
static final class SetProvidesMethodsModule {
@Provides @IntoSet String provideString() {
throw new UnsupportedOperationException();
}
@Provides @ElementsIntoSet Set<String> 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<String> 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<java.lang.String> "
+ "dagger.internal.codegen.KeyFactoryTest.SetProducesMethodsModule#%s",
getSimpleName(producesMethod)));
}
}
@ProducerModule
static final class SetProducesMethodsModule {
@Produces @IntoSet String produceString() {
throw new UnsupportedOperationException();
}
@Produces @IntoSet ListenableFuture<String> produceFutureString() {
throw new UnsupportedOperationException();
}
@Produces @ElementsIntoSet Set<String> produceStrings() {
throw new UnsupportedOperationException();
}
@Produces @ElementsIntoSet
ListenableFuture<Set<String>> produceFutureStrings() {
throw new UnsupportedOperationException();
}
}
@Singleton
@Component(modules = JavacPluginModule.class)
interface TestComponent {
void inject(KeyFactoryTest test);
}
}