Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2014 Google, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package dagger.internal.codegen;
     17 
     18 import com.google.auto.common.MoreTypes;
     19 import com.google.common.base.Equivalence;
     20 import com.google.common.base.Optional;
     21 import com.google.common.collect.Iterables;
     22 import com.google.common.util.concurrent.ListenableFuture;
     23 import com.google.testing.compile.CompilationRule;
     24 import dagger.Module;
     25 import dagger.Provides;
     26 import dagger.producers.ProducerModule;
     27 import dagger.producers.Produces;
     28 import java.util.Set;
     29 import javax.inject.Inject;
     30 import javax.inject.Qualifier;
     31 import javax.lang.model.element.AnnotationMirror;
     32 import javax.lang.model.element.Element;
     33 import javax.lang.model.element.ExecutableElement;
     34 import javax.lang.model.element.TypeElement;
     35 import javax.lang.model.type.ExecutableType;
     36 import javax.lang.model.type.TypeMirror;
     37 import javax.lang.model.util.ElementFilter;
     38 import javax.lang.model.util.Elements;
     39 import javax.lang.model.util.Types;
     40 import org.junit.Before;
     41 import org.junit.Rule;
     42 import org.junit.Test;
     43 import org.junit.runner.RunWith;
     44 import org.junit.runners.JUnit4;
     45 
     46 import static com.google.common.truth.Truth.assertThat;
     47 import static dagger.Provides.Type.SET;
     48 import static dagger.Provides.Type.SET_VALUES;
     49 
     50 /**
     51  * Tests {@link Key}.
     52  */
     53 @RunWith(JUnit4.class)
     54 public class KeyTest {
     55   @Rule public CompilationRule compilationRule = new CompilationRule();
     56 
     57   private Elements elements;
     58   private Types types;
     59   private Key.Factory keyFactory;
     60 
     61   @Before public void setUp() {
     62     this.types = compilationRule.getTypes();
     63     this.elements = compilationRule.getElements();
     64     this.keyFactory = new Key.Factory(types, elements);
     65   }
     66 
     67   @Test public void forInjectConstructorWithResolvedType() {
     68     TypeElement typeElement =
     69         compilationRule.getElements().getTypeElement(InjectedClass.class.getCanonicalName());
     70     ExecutableElement constructor =
     71         Iterables.getOnlyElement(ElementFilter.constructorsIn(typeElement.getEnclosedElements()));
     72     assertThat(
     73         keyFactory.forInjectConstructorWithResolvedType(constructor.getEnclosingElement().asType()))
     74         .isEqualTo(new AutoValue_Key(
     75             Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
     76             MoreTypes.equivalence().wrap(typeElement.asType())));
     77   }
     78 
     79   static final class InjectedClass {
     80     @SuppressWarnings("unused")
     81     @Inject InjectedClass(String s, int i) {}
     82   }
     83 
     84   @Test public void forProvidesMethod() {
     85     TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
     86     TypeElement moduleElement =
     87         elements.getTypeElement(ProvidesMethodModule.class.getCanonicalName());
     88     ExecutableElement providesMethod =
     89         Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
     90     assertThat(
     91         keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod))
     92         .isEqualTo(new AutoValue_Key(
     93             Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
     94             MoreTypes.equivalence().wrap(stringType)));
     95   }
     96 
     97   @Module
     98   static final class ProvidesMethodModule {
     99     @Provides String provideString() {
    100       return null;
    101     }
    102   }
    103 
    104   @Test public void forProvidesMethod_qualified() {
    105     TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
    106     TypeElement qualifierElement =
    107         elements.getTypeElement(TestQualifier.class.getCanonicalName());
    108     TypeElement moduleElement =
    109         elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
    110     ExecutableElement providesMethod =
    111         Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
    112     Key key =
    113         keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod);
    114     assertThat(MoreTypes.equivalence().wrap(key.qualifier().get().getAnnotationType()))
    115         .isEqualTo(MoreTypes.equivalence().wrap(qualifierElement.asType()));
    116     assertThat(key.wrappedType()).isEqualTo(MoreTypes.equivalence().wrap(stringType));
    117   }
    118 
    119   @Test public void qualifiedKeyEquivalents() {
    120     TypeElement moduleElement =
    121         elements.getTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
    122     ExecutableElement providesMethod =
    123         Iterables.getOnlyElement(ElementFilter.methodsIn(moduleElement.getEnclosedElements()));
    124     Key provisionKey =
    125         keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod);
    126 
    127     TypeMirror type = elements.getTypeElement(String.class.getCanonicalName()).asType();
    128     TypeElement injectableElement =
    129         elements.getTypeElement(QualifiedFieldHolder.class.getCanonicalName());
    130     Element injectionField =
    131         Iterables.getOnlyElement(ElementFilter.fieldsIn(injectableElement.getEnclosedElements()));
    132     AnnotationMirror qualifier = Iterables.getOnlyElement(injectionField.getAnnotationMirrors());
    133     Key injectionKey = keyFactory.forQualifiedType(Optional.<AnnotationMirror>of(qualifier), type);
    134 
    135     assertThat(provisionKey).isEqualTo(injectionKey);
    136   }
    137 
    138   @Module
    139   static final class QualifiedProvidesMethodModule {
    140     @Provides
    141     @TestQualifier(@InnerAnnotation)
    142     String provideQualifiedString() {
    143       return null;
    144     }
    145   }
    146 
    147   static final class QualifiedFieldHolder {
    148     @TestQualifier(@InnerAnnotation) String aString;
    149   }
    150 
    151   @Qualifier
    152   @interface TestQualifier {
    153     InnerAnnotation[] value();
    154   }
    155 
    156   @interface InnerAnnotation {}
    157 
    158   @Test public void forProvidesMethod_sets() {
    159     TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName());
    160     TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
    161     TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType);
    162     TypeElement moduleElement =
    163         elements.getTypeElement(SetProvidesMethodsModule.class.getCanonicalName());
    164     for (ExecutableElement providesMethod
    165         : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
    166       assertThat(
    167           keyFactory.forProvidesMethod((ExecutableType) providesMethod.asType(), providesMethod))
    168               .isEqualTo(new AutoValue_Key(
    169                   Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
    170                   MoreTypes.equivalence().wrap(setOfStringsType)));
    171     }
    172   }
    173 
    174   @Module
    175   static final class SetProvidesMethodsModule {
    176     @Provides(type = SET) String provideString() {
    177       return null;
    178     }
    179 
    180     @Provides(type = SET_VALUES) Set<String> provideStrings() {
    181       return null;
    182     }
    183   }
    184 
    185   @Module
    186   static final class PrimitiveTypes {
    187     @Provides int foo() {
    188       return 0;
    189     }
    190   }
    191 
    192   @Module
    193   static final class BoxedPrimitiveTypes {
    194     @Provides Integer foo() {
    195       return 0;
    196     }
    197   }
    198 
    199   @Test public void primitiveKeysMatchBoxedKeys() {
    200     TypeElement primitiveHolder = elements.getTypeElement(PrimitiveTypes.class.getCanonicalName());
    201     ExecutableElement intMethod =
    202         Iterables.getOnlyElement(ElementFilter.methodsIn(primitiveHolder.getEnclosedElements()));
    203     TypeElement boxedPrimitiveHolder =
    204         elements.getTypeElement(BoxedPrimitiveTypes.class.getCanonicalName());
    205     ExecutableElement integerMethod = Iterables.getOnlyElement(
    206         ElementFilter.methodsIn(boxedPrimitiveHolder.getEnclosedElements()));
    207 
    208     // TODO(cgruber): Truth subject for TypeMirror and TypeElement
    209     TypeMirror intType = intMethod.getReturnType();
    210     assertThat(intType.getKind().isPrimitive()).isTrue();
    211     TypeMirror integerType = integerMethod.getReturnType();
    212     assertThat(integerType.getKind().isPrimitive()).isFalse();
    213     assertThat(types.isSameType(intType, integerType)).named("type equality").isFalse();
    214 
    215     Key intKey = keyFactory.forProvidesMethod((ExecutableType) intMethod.asType(), intMethod);
    216     Key integerKey =
    217         keyFactory.forProvidesMethod((ExecutableType) integerMethod.asType(), integerMethod);
    218     assertThat(intKey).isEqualTo(integerKey);
    219   }
    220 
    221   @Test public void forProducesMethod() {
    222     TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
    223     TypeElement moduleElement =
    224         elements.getTypeElement(ProducesMethodsModule.class.getCanonicalName());
    225     for (ExecutableElement producesMethod
    226         : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
    227       assertThat(keyFactory.forProducesMethod(
    228           (ExecutableType) producesMethod.asType(), producesMethod))
    229               .isEqualTo(new AutoValue_Key(
    230                   Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
    231                   MoreTypes.equivalence().wrap(stringType)));
    232     }
    233   }
    234 
    235   @ProducerModule
    236   static final class ProducesMethodsModule {
    237     @Produces String produceString() {
    238       return null;
    239     }
    240 
    241     @Produces ListenableFuture<String> produceFutureString() {
    242       return null;
    243     }
    244   }
    245 
    246   @Test public void forProducesMethod_sets() {
    247     TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName());
    248     TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType();
    249     TypeMirror setOfStringsType = types.getDeclaredType(setElement, stringType);
    250     TypeElement moduleElement =
    251         elements.getTypeElement(SetProducesMethodsModule.class.getCanonicalName());
    252     for (ExecutableElement producesMethod
    253         : ElementFilter.methodsIn(moduleElement.getEnclosedElements())) {
    254       assertThat(keyFactory.forProducesMethod(
    255           (ExecutableType) producesMethod.asType(), producesMethod))
    256           .isEqualTo(new AutoValue_Key(
    257                   Optional.<Equivalence.Wrapper<AnnotationMirror>>absent(),
    258                   MoreTypes.equivalence().wrap(setOfStringsType)));
    259     }
    260   }
    261 
    262   @ProducerModule
    263   static final class SetProducesMethodsModule {
    264     @Produces(type = Produces.Type.SET) String produceString() {
    265       return null;
    266     }
    267 
    268     @Produces(type = Produces.Type.SET) ListenableFuture<String> produceFutureString() {
    269       return null;
    270     }
    271 
    272     @Produces(type = Produces.Type.SET_VALUES) Set<String> produceStrings() {
    273       return null;
    274     }
    275 
    276     @Produces(type = Produces.Type.SET_VALUES)
    277     ListenableFuture<Set<String>> produceFutureStrings() {
    278       return null;
    279     }
    280   }
    281 }
    282