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 */
16package dagger.internal.codegen;
17
18import com.google.auto.common.MoreTypes;
19import com.google.common.base.Equivalence;
20import com.google.common.base.Optional;
21import com.google.common.collect.Iterables;
22import com.google.common.util.concurrent.ListenableFuture;
23import com.google.testing.compile.CompilationRule;
24import dagger.Module;
25import dagger.Provides;
26import dagger.producers.ProducerModule;
27import dagger.producers.Produces;
28import java.util.Set;
29import javax.inject.Inject;
30import javax.inject.Qualifier;
31import javax.lang.model.element.AnnotationMirror;
32import javax.lang.model.element.Element;
33import javax.lang.model.element.ExecutableElement;
34import javax.lang.model.element.TypeElement;
35import javax.lang.model.type.ExecutableType;
36import javax.lang.model.type.TypeMirror;
37import javax.lang.model.util.ElementFilter;
38import javax.lang.model.util.Elements;
39import javax.lang.model.util.Types;
40import org.junit.Before;
41import org.junit.Rule;
42import org.junit.Test;
43import org.junit.runner.RunWith;
44import org.junit.runners.JUnit4;
45
46import static com.google.common.truth.Truth.assertThat;
47import static dagger.Provides.Type.SET;
48import static dagger.Provides.Type.SET_VALUES;
49
50/**
51 * Tests {@link Key}.
52 */
53@RunWith(JUnit4.class)
54public 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