• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
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 
17 package dagger.internal.codegen;
18 
19 import static com.google.common.collect.Iterables.getOnlyElement;
20 import static com.google.common.truth.Truth.assertThat;
21 import static com.google.common.truth.Truth.assertWithMessage;
22 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
23 import static dagger.internal.codegen.xprocessing.XTypes.isPrimitive;
24 import static java.lang.annotation.RetentionPolicy.RUNTIME;
25 
26 import androidx.room.compiler.processing.XConstructorElement;
27 import androidx.room.compiler.processing.XMethodElement;
28 import androidx.room.compiler.processing.XProcessingEnv;
29 import androidx.room.compiler.processing.XType;
30 import androidx.room.compiler.processing.XTypeElement;
31 import com.google.common.util.concurrent.ListenableFuture;
32 import com.google.testing.compile.CompilationRule;
33 import dagger.Component;
34 import dagger.Module;
35 import dagger.Provides;
36 import dagger.internal.codegen.binding.KeyFactory;
37 import dagger.internal.codegen.javac.JavacPluginModule;
38 import dagger.internal.codegen.model.Key;
39 import dagger.multibindings.ElementsIntoSet;
40 import dagger.multibindings.IntoSet;
41 import dagger.producers.ProducerModule;
42 import dagger.producers.Produces;
43 import java.lang.annotation.Retention;
44 import java.util.Set;
45 import javax.inject.Inject;
46 import javax.inject.Qualifier;
47 import javax.inject.Singleton;
48 import org.junit.Before;
49 import org.junit.Rule;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.junit.runners.JUnit4;
53 
54 /**
55  * Tests {@link Key}.
56  */
57 @RunWith(JUnit4.class)
58 public class KeyFactoryTest {
59   @Rule public CompilationRule compilationRule = new CompilationRule();
60 
61   @Inject XProcessingEnv processingEnv;
62   @Inject KeyFactory keyFactory;
63 
setUp()64   @Before public void setUp() {
65     DaggerKeyFactoryTest_TestComponent.builder()
66         .javacPluginModule(
67             new JavacPluginModule(compilationRule.getElements(), compilationRule.getTypes()))
68         .build()
69         .inject(this);
70   }
71 
72   @Test
forInjectConstructorWithResolvedType()73   public void forInjectConstructorWithResolvedType() {
74     XTypeElement typeElement =
75         processingEnv.requireTypeElement(InjectedClass.class.getCanonicalName());
76     XConstructorElement constructor = getOnlyElement(typeElement.getConstructors());
77     Key key =
78         keyFactory.forInjectConstructorWithResolvedType(
79             constructor.getEnclosingElement().getType());
80     assertThat(key.toString()).isEqualTo("dagger.internal.codegen.KeyFactoryTest.InjectedClass");
81   }
82 
83   static final class InjectedClass {
84     @SuppressWarnings("unused")
InjectedClass(String s, int i)85     @Inject InjectedClass(String s, int i) {}
86   }
87 
88   @Test
forProvidesMethod()89   public void forProvidesMethod() {
90     XTypeElement moduleElement =
91         processingEnv.requireTypeElement(ProvidesMethodModule.class.getCanonicalName());
92     XMethodElement providesMethod = getOnlyElement(moduleElement.getDeclaredMethods());
93     Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
94     assertThat(key.toString()).isEqualTo("java.lang.String");
95   }
96 
97   @Module
98   static final class ProvidesMethodModule {
provideString()99     @Provides String provideString() {
100       throw new UnsupportedOperationException();
101     }
102   }
103 
104   @Test
forProvidesMethod_qualified()105   public void forProvidesMethod_qualified() {
106     XType stringType = processingEnv.requireType(String.class.getCanonicalName());
107     XTypeElement qualifierElement =
108         processingEnv.requireTypeElement(TestQualifier.class.getCanonicalName());
109     XTypeElement moduleElement =
110         processingEnv.requireTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
111     XMethodElement providesMethod = getOnlyElement(moduleElement.getDeclaredMethods());
112     Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
113     assertThat(key.qualifier().get().className()).isEqualTo(qualifierElement.getClassName());
114     assertThat(key.type().xprocessing().getTypeName()).isEqualTo(stringType.getTypeName());
115     assertThat(key.toString())
116         .isEqualTo(
117             "@dagger.internal.codegen.KeyFactoryTest.TestQualifier({"
118                 + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
119                 + "param1=1, value=\"value a\"), "
120                 + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
121                 + "param1=2, value=\"value b\"), "
122                 + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
123                 + "param1=3145, value=\"default\")"
124                 + "}) java.lang.String");
125   }
126 
127   @Test
qualifiedKeyEquivalents()128   public void qualifiedKeyEquivalents() {
129     XTypeElement moduleElement =
130         processingEnv.requireTypeElement(QualifiedProvidesMethodModule.class.getCanonicalName());
131     XMethodElement providesMethod = getOnlyElement(moduleElement.getDeclaredMethods());
132     Key provisionKey = keyFactory.forProvidesMethod(providesMethod, moduleElement);
133     assertThat(provisionKey.toString())
134         .isEqualTo(
135             "@dagger.internal.codegen.KeyFactoryTest.TestQualifier({"
136                 + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
137                 + "param1=1, value=\"value a\"), "
138                 + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
139                 + "param1=2, value=\"value b\"), "
140                 + "@dagger.internal.codegen.KeyFactoryTest.InnerAnnotation("
141                 + "param1=3145, value=\"default\")"
142                 + "}) java.lang.String");
143   }
144 
145   @Module
146   static final class QualifiedProvidesMethodModule {
147     @Provides
148     @TestQualifier({
149       @InnerAnnotation(value = "value a", param1 = 1),
150       // please note the order of 'param' and 'value' is inverse
151       @InnerAnnotation(param1 = 2, value = "value b"),
152       @InnerAnnotation()
153     })
provideQualifiedString()154     static String provideQualifiedString() {
155       throw new UnsupportedOperationException();
156     }
157   }
158 
159   static final class QualifiedFieldHolder {
160     @TestQualifier({
161       @InnerAnnotation(value = "value a", param1 = 1),
162       // please note the order of 'param' and 'value' is inverse
163       @InnerAnnotation(param1 = 2, value = "value b"),
164       @InnerAnnotation()
165     })
166     String aString;
167   }
168 
169   @Retention(RUNTIME)
170   @Qualifier
171   @interface TestQualifier {
value()172     InnerAnnotation[] value();
173   }
174 
175   @interface InnerAnnotation {
param1()176     int param1() default 3145;
177 
value()178     String value() default "default";
179   }
180 
181   @Test
forProvidesMethod_sets()182   public void forProvidesMethod_sets() {
183     XTypeElement moduleElement =
184         processingEnv.requireTypeElement(SetProvidesMethodsModule.class.getCanonicalName());
185     for (XMethodElement providesMethod : moduleElement.getDeclaredMethods()) {
186       Key key = keyFactory.forProvidesMethod(providesMethod, moduleElement);
187       assertThat(key.toString())
188           .isEqualTo(
189               String.format(
190                   "java.util.Set<java.lang.String> "
191                       + "dagger.internal.codegen.KeyFactoryTest.SetProvidesMethodsModule#%s",
192                   getSimpleName(providesMethod)));
193     }
194   }
195 
196   @Module
197   static final class SetProvidesMethodsModule {
provideString()198     @Provides @IntoSet String provideString() {
199       throw new UnsupportedOperationException();
200     }
201 
provideStrings()202     @Provides @ElementsIntoSet Set<String> provideStrings() {
203       throw new UnsupportedOperationException();
204     }
205   }
206 
207   @Module
208   static final class PrimitiveTypes {
foo()209     @Provides int foo() {
210       return 0;
211     }
212   }
213 
214   @Module
215   static final class BoxedPrimitiveTypes {
foo()216     @Provides Integer foo() {
217       return 0;
218     }
219   }
220 
primitiveKeysMatchBoxedKeys()221   @Test public void primitiveKeysMatchBoxedKeys() {
222     XTypeElement primitiveHolder =
223         processingEnv.requireTypeElement(PrimitiveTypes.class.getCanonicalName());
224     XMethodElement intMethod = getOnlyElement(primitiveHolder.getDeclaredMethods());
225     XTypeElement boxedPrimitiveHolder =
226         processingEnv.requireTypeElement(BoxedPrimitiveTypes.class.getCanonicalName());
227     XMethodElement integerMethod = getOnlyElement(boxedPrimitiveHolder.getDeclaredMethods());
228 
229     // TODO(cgruber): Truth subject for TypeMirror and TypeElement
230     XType intType = intMethod.getReturnType();
231     assertThat(isPrimitive(intType)).isTrue();
232     XType integerType = integerMethod.getReturnType();
233     assertThat(isPrimitive(integerType)).isFalse();
234     assertWithMessage("type equality").that(intType.isSameType(integerType)).isFalse();
235     Key intKey = keyFactory.forProvidesMethod(intMethod, primitiveHolder);
236     Key integerKey = keyFactory.forProvidesMethod(integerMethod, boxedPrimitiveHolder);
237     assertThat(intKey).isEqualTo(integerKey);
238     assertThat(intKey.toString()).isEqualTo("java.lang.Integer");
239     assertThat(integerKey.toString()).isEqualTo("java.lang.Integer");
240   }
241 
forProducesMethod()242   @Test public void forProducesMethod() {
243     XTypeElement moduleElement =
244         processingEnv.requireTypeElement(ProducesMethodsModule.class.getCanonicalName());
245     for (XMethodElement producesMethod : moduleElement.getDeclaredMethods()) {
246       Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
247       assertThat(key.toString()).isEqualTo("java.lang.String");
248     }
249   }
250 
251   @ProducerModule
252   static final class ProducesMethodsModule {
produceString()253     @Produces String produceString() {
254       throw new UnsupportedOperationException();
255     }
256 
produceFutureString()257     @Produces ListenableFuture<String> produceFutureString() {
258       throw new UnsupportedOperationException();
259     }
260   }
261 
forProducesMethod_sets()262   @Test public void forProducesMethod_sets() {
263     XTypeElement moduleElement =
264         processingEnv.requireTypeElement(SetProducesMethodsModule.class.getCanonicalName());
265     for (XMethodElement producesMethod : moduleElement.getDeclaredMethods()) {
266       Key key = keyFactory.forProducesMethod(producesMethod, moduleElement);
267       assertThat(key.toString())
268           .isEqualTo(
269               String.format(
270                   "java.util.Set<java.lang.String> "
271                       + "dagger.internal.codegen.KeyFactoryTest.SetProducesMethodsModule#%s",
272                   getSimpleName(producesMethod)));
273     }
274   }
275 
276   @ProducerModule
277   static final class SetProducesMethodsModule {
produceString()278     @Produces @IntoSet String produceString() {
279       throw new UnsupportedOperationException();
280     }
281 
produceFutureString()282     @Produces @IntoSet ListenableFuture<String> produceFutureString() {
283       throw new UnsupportedOperationException();
284     }
285 
produceStrings()286     @Produces @ElementsIntoSet Set<String> produceStrings() {
287       throw new UnsupportedOperationException();
288     }
289 
290     @Produces @ElementsIntoSet
produceFutureStrings()291     ListenableFuture<Set<String>> produceFutureStrings() {
292       throw new UnsupportedOperationException();
293     }
294   }
295 
296   @Singleton
297   @Component(modules = JavacPluginModule.class)
298   interface TestComponent {
inject(KeyFactoryTest test)299     void inject(KeyFactoryTest test);
300   }
301 }
302