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