blob: fdf62cbbf33a77661a1460dbe31f54a850796d89 [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.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.auto.common.MoreTypes;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.testing.compile.CompilationRule;
import dagger.BindsInstance;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import dagger.internal.codegen.binding.KeyFactory;
import dagger.internal.codegen.langmodel.DaggerElements;
import dagger.internal.codegen.langmodel.DaggerTypes;
import dagger.model.Key;
import dagger.model.Key.MultibindingContributionIdentifier;
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 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.TypeMirror;
import javax.lang.model.util.ElementFilter;
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 DaggerElements elements;
@Inject DaggerTypes types;
@Inject KeyFactory keyFactory;
@Before public void setUp() {
DaggerKeyFactoryTest_TestComponent.factory().create(compilationRule).inject(this);
}
@Test public void forInjectConstructorWithResolvedType() {
TypeElement typeElement =
compilationRule.getElements().getTypeElement(InjectedClass.class.getCanonicalName());
ExecutableElement constructor =
Iterables.getOnlyElement(ElementFilter.constructorsIn(typeElement.getEnclosedElements()));
Key key =
keyFactory.forInjectConstructorWithResolvedType(constructor.getEnclosingElement().asType());
assertThat(key).isEqualTo(Key.builder(typeElement.asType()).build());
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() {
TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
TypeElement moduleElement =
elements.getTypeElement(ProvidesMethodModule.class.getCanonicalName());
ExecutableElement providesMethod =
Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
assertThat(key).isEqualTo(Key.builder(stringType).build());
assertThat(key.toString()).isEqualTo("java.lang.String");
}
@Module
static final class ProvidesMethodModule {
@Provides String provideString() {
throw new UnsupportedOperationException();
}
}
@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(providesMethod, moduleElement);
assertThat(MoreTypes.equivalence().wrap(key.qualifier().get().getAnnotationType()))
.isEqualTo(MoreTypes.equivalence().wrap(qualifierElement.asType()));
assertThat(MoreTypes.equivalence().wrap(key.type()))
.isEqualTo(MoreTypes.equivalence().wrap(stringType));
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() {
TypeElement moduleElement =
elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
ExecutableElement providesMethod =
Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
Key provisionKey = keyFactory.forProvidesMethod(providesMethod, moduleElement);
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 = Key.builder(type).qualifier(qualifier).build();
assertThat(provisionKey).isEqualTo(injectionKey);
assertThat(injectionKey.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() {
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())) {
Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
assertThat(key)
.isEqualTo(
Key.builder(setOfStringsType)
.multibindingContributionIdentifier(
new MultibindingContributionIdentifier(providesMethod, moduleElement))
.build());
assertThat(key.toString())
.isEqualTo(
String.format(
"java.util.Set<java.lang.String> "
+ "dagger.internal.codegen.KeyFactoryTest.SetProvidesMethodsModule#%s",
providesMethod.getSimpleName()));
}
}
@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() {
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(user): Truth subject for TypeMirror and TypeElement
TypeMirror intType = intMethod.getReturnType();
assertThat(intType.getKind().isPrimitive()).isTrue();
TypeMirror integerType = integerMethod.getReturnType();
assertThat(integerType.getKind().isPrimitive()).isFalse();
assertWithMessage("type equality").that(types.isSameType(intType, 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() {
TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
TypeElement moduleElement =
elements.getTypeElement(ProducesMethodsModule.class.getCanonicalName());
for (ExecutableElement producesMethod
: ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
assertThat(key).isEqualTo(Key.builder(stringType).build());
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() {
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())) {
Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
assertThat(key)
.isEqualTo(
Key.builder(setOfStringsType)
.multibindingContributionIdentifier(
new MultibindingContributionIdentifier(producesMethod, moduleElement))
.build());
assertThat(key.toString())
.isEqualTo(
String.format(
"java.util.Set<java.lang.String> "
+ "dagger.internal.codegen.KeyFactoryTest.SetProducesMethodsModule#%s",
producesMethod.getSimpleName()));
}
}
@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 = {TestModule.class})
interface TestComponent {
void inject(KeyFactoryTest test);
@Component.Factory
interface Factory {
TestComponent create(@BindsInstance CompilationRule compilationRule);
}
}
@Module
static class TestModule {
@Provides
static DaggerElements elements(CompilationRule compilationRule) {
return new DaggerElements(compilationRule.getElements(), compilationRule.getTypes());
}
@Provides
static DaggerTypes types(CompilationRule compilationRule, DaggerElements elements) {
return new DaggerTypes(compilationRule.getTypes(), elements);
}
}
}