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 
setUp()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 
forInjectConstructorWithResolvedType()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")
InjectedClass(String s, int i)81     @Inject InjectedClass(String s, int i) {}
82   }
83 
forProvidesMethod()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 {
provideString()99     @Provides String provideString() {
100       return null;
101     }
102   }
103 
forProvidesMethod_qualified()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 
qualifiedKeyEquivalents()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)
provideQualifiedString()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 {
value()153     InnerAnnotation[] value();
154   }
155 
156   @interface InnerAnnotation {}
157 
forProvidesMethod_sets()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 {
provideString()176     @Provides(type = SET) String provideString() {
177       return null;
178     }
179 
provideStrings()180     @Provides(type = SET_VALUES) Set<String> provideStrings() {
181       return null;
182     }
183   }
184 
185   @Module
186   static final class PrimitiveTypes {
foo()187     @Provides int foo() {
188       return 0;
189     }
190   }
191 
192   @Module
193   static final class BoxedPrimitiveTypes {
foo()194     @Provides Integer foo() {
195       return 0;
196     }
197   }
198 
primitiveKeysMatchBoxedKeys()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 
forProducesMethod()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 {
produceString()237     @Produces String produceString() {
238       return null;
239     }
240 
produceFutureString()241     @Produces ListenableFuture<String> produceFutureString() {
242       return null;
243     }
244   }
245 
forProducesMethod_sets()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 {
produceString()264     @Produces(type = Produces.Type.SET) String produceString() {
265       return null;
266     }
267 
produceFutureString()268     @Produces(type = Produces.Type.SET) ListenableFuture<String> produceFutureString() {
269       return null;
270     }
271 
produceStrings()272     @Produces(type = Produces.Type.SET_VALUES) Set<String> produceStrings() {
273       return null;
274     }
275 
276     @Produces(type = Produces.Type.SET_VALUES)
produceFutureStrings()277     ListenableFuture<Set<String>> produceFutureStrings() {
278       return null;
279     }
280   }
281 }
282