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.testing.compile.CompilationSubject.assertThat; 20 import static dagger.internal.codegen.Compilers.compilerWithOptions; 21 import static dagger.internal.codegen.Compilers.daggerCompiler; 22 import static dagger.internal.codegen.TestUtils.message; 23 24 import com.google.testing.compile.Compilation; 25 import com.google.testing.compile.JavaFileObjects; 26 import javax.tools.JavaFileObject; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.junit.runners.JUnit4; 30 31 @RunWith(JUnit4.class) 32 public final class ComponentValidationTest { 33 @Test componentOnConcreteClass()34 public void componentOnConcreteClass() { 35 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent", 36 "package test;", 37 "", 38 "import dagger.Component;", 39 "", 40 "@Component", 41 "final class NotAComponent {}"); 42 Compilation compilation = daggerCompiler().compile(componentFile); 43 assertThat(compilation).failed(); 44 assertThat(compilation).hadErrorContaining("interface"); 45 } 46 componentOnEnum()47 @Test public void componentOnEnum() { 48 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent", 49 "package test;", 50 "", 51 "import dagger.Component;", 52 "", 53 "@Component", 54 "enum NotAComponent {", 55 " INSTANCE", 56 "}"); 57 Compilation compilation = daggerCompiler().compile(componentFile); 58 assertThat(compilation).failed(); 59 assertThat(compilation).hadErrorContaining("interface"); 60 } 61 componentOnAnnotation()62 @Test public void componentOnAnnotation() { 63 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent", 64 "package test;", 65 "", 66 "import dagger.Component;", 67 "", 68 "@Component", 69 "@interface NotAComponent {}"); 70 Compilation compilation = daggerCompiler().compile(componentFile); 71 assertThat(compilation).failed(); 72 assertThat(compilation).hadErrorContaining("interface"); 73 } 74 nonModuleModule()75 @Test public void nonModuleModule() { 76 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAComponent", 77 "package test;", 78 "", 79 "import dagger.Component;", 80 "", 81 "@Component(modules = Object.class)", 82 "interface NotAComponent {}"); 83 Compilation compilation = daggerCompiler().compile(componentFile); 84 assertThat(compilation).failed(); 85 assertThat(compilation).hadErrorContaining("is not annotated with @Module"); 86 } 87 88 @Test componentWithInvalidModule()89 public void componentWithInvalidModule() { 90 JavaFileObject module = 91 JavaFileObjects.forSourceLines( 92 "test.BadModule", 93 "package test;", 94 "", 95 "import dagger.Binds;", 96 "import dagger.Module;", 97 "", 98 "@Module", 99 "abstract class BadModule {", 100 " @Binds abstract Object noParameters();", 101 "}"); 102 JavaFileObject component = 103 JavaFileObjects.forSourceLines( 104 "test.BadComponent", 105 "package test;", 106 "", 107 "import dagger.Component;", 108 "", 109 "@Component(modules = BadModule.class)", 110 "interface BadComponent {", 111 " Object object();", 112 "}"); 113 Compilation compilation = daggerCompiler().compile(module, component); 114 assertThat(compilation) 115 .hadErrorContaining("test.BadModule has errors") 116 .inFile(component) 117 .onLine(5); 118 } 119 120 @Test attemptToInjectWildcardGenerics()121 public void attemptToInjectWildcardGenerics() { 122 JavaFileObject testComponent = 123 JavaFileObjects.forSourceLines( 124 "test.TestComponent", 125 "package test;", 126 "", 127 "import dagger.Component;", 128 "import dagger.Lazy;", 129 "import javax.inject.Provider;", 130 "", 131 "@Component", 132 "interface TestComponent {", 133 " Lazy<? extends Number> wildcardNumberLazy();", 134 " Provider<? super Number> wildcardNumberProvider();", 135 "}"); 136 Compilation compilation = daggerCompiler().compile(testComponent); 137 assertThat(compilation).failed(); 138 assertThat(compilation).hadErrorContaining("wildcard type").inFile(testComponent).onLine(9); 139 assertThat(compilation).hadErrorContaining("wildcard type").inFile(testComponent).onLine(10); 140 } 141 142 @Test invalidComponentDependencies()143 public void invalidComponentDependencies() { 144 JavaFileObject testComponent = 145 JavaFileObjects.forSourceLines( 146 "test.TestComponent", 147 "package test;", 148 "", 149 "import dagger.Component;", 150 "", 151 "@Component(dependencies = int.class)", 152 "interface TestComponent {}"); 153 Compilation compilation = daggerCompiler().compile(testComponent); 154 assertThat(compilation).failed(); 155 assertThat(compilation).hadErrorContaining("int is not a valid component dependency type"); 156 } 157 158 @Test invalidComponentModules()159 public void invalidComponentModules() { 160 JavaFileObject testComponent = 161 JavaFileObjects.forSourceLines( 162 "test.TestComponent", 163 "package test;", 164 "", 165 "import dagger.Component;", 166 "", 167 "@Component(modules = int.class)", 168 "interface TestComponent {}"); 169 Compilation compilation = daggerCompiler().compile(testComponent); 170 assertThat(compilation).failed(); 171 assertThat(compilation).hadErrorContaining("int is not a valid module type"); 172 } 173 174 @Test moduleInDependencies()175 public void moduleInDependencies() { 176 JavaFileObject testModule = 177 JavaFileObjects.forSourceLines( 178 "test.TestModule", 179 "package test;", 180 "", 181 "import dagger.Module;", 182 "import dagger.Provides;", 183 "", 184 "@Module", 185 "final class TestModule {", 186 " @Provides String s() { return null; }", 187 "}"); 188 JavaFileObject testComponent = 189 JavaFileObjects.forSourceLines( 190 "test.TestComponent", 191 "package test;", 192 "", 193 "import dagger.Component;", 194 "", 195 "@Component(dependencies = TestModule.class)", 196 "interface TestComponent {}"); 197 Compilation compilation = daggerCompiler().compile(testModule, testComponent); 198 assertThat(compilation).failed(); 199 assertThat(compilation) 200 .hadErrorContaining("test.TestModule is a module, which cannot be a component dependency"); 201 } 202 203 @Test componentDependencyMustNotCycle_Direct()204 public void componentDependencyMustNotCycle_Direct() { 205 JavaFileObject shortLifetime = 206 JavaFileObjects.forSourceLines( 207 "test.ComponentShort", 208 "package test;", 209 "", 210 "import dagger.Component;", 211 "", 212 "@Component(dependencies = ComponentShort.class)", 213 "interface ComponentShort {", 214 "}"); 215 216 Compilation compilation = daggerCompiler().compile(shortLifetime); 217 assertThat(compilation).failed(); 218 assertThat(compilation) 219 .hadErrorContaining( 220 message( 221 "test.ComponentShort contains a cycle in its component dependencies:", 222 " test.ComponentShort")); 223 224 // Test that this also fails when transitive validation is disabled. 225 compilation = 226 compilerWithOptions("-Adagger.validateTransitiveComponentDependencies=DISABLED") 227 .compile(shortLifetime); 228 assertThat(compilation).failed(); 229 assertThat(compilation) 230 .hadErrorContaining( 231 message( 232 "test.ComponentShort contains a cycle in its component dependencies:", 233 " test.ComponentShort")); 234 } 235 236 @Test componentDependencyMustNotCycle_Indirect()237 public void componentDependencyMustNotCycle_Indirect() { 238 JavaFileObject longLifetime = 239 JavaFileObjects.forSourceLines( 240 "test.ComponentLong", 241 "package test;", 242 "", 243 "import dagger.Component;", 244 "", 245 "@Component(dependencies = ComponentMedium.class)", 246 "interface ComponentLong {", 247 "}"); 248 JavaFileObject mediumLifetime = 249 JavaFileObjects.forSourceLines( 250 "test.ComponentMedium", 251 "package test;", 252 "", 253 "import dagger.Component;", 254 "", 255 "@Component(dependencies = ComponentLong.class)", 256 "interface ComponentMedium {", 257 "}"); 258 JavaFileObject shortLifetime = 259 JavaFileObjects.forSourceLines( 260 "test.ComponentShort", 261 "package test;", 262 "", 263 "import dagger.Component;", 264 "", 265 "@Component(dependencies = ComponentMedium.class)", 266 "interface ComponentShort {", 267 "}"); 268 269 Compilation compilation = daggerCompiler().compile(longLifetime, mediumLifetime, shortLifetime); 270 assertThat(compilation).failed(); 271 assertThat(compilation) 272 .hadErrorContaining( 273 message( 274 "test.ComponentLong contains a cycle in its component dependencies:", 275 " test.ComponentLong", 276 " test.ComponentMedium", 277 " test.ComponentLong")) 278 .inFile(longLifetime); 279 assertThat(compilation) 280 .hadErrorContaining( 281 message( 282 "test.ComponentMedium contains a cycle in its component dependencies:", 283 " test.ComponentMedium", 284 " test.ComponentLong", 285 " test.ComponentMedium")) 286 .inFile(mediumLifetime); 287 assertThat(compilation) 288 .hadErrorContaining( 289 message( 290 "test.ComponentShort contains a cycle in its component dependencies:", 291 " test.ComponentMedium", 292 " test.ComponentLong", 293 " test.ComponentMedium", 294 " test.ComponentShort")) 295 .inFile(shortLifetime); 296 297 // Test that compilation succeeds when transitive validation is disabled because the cycle 298 // cannot be detected. 299 compilation = 300 compilerWithOptions("-Adagger.validateTransitiveComponentDependencies=DISABLED") 301 .compile(longLifetime, mediumLifetime, shortLifetime); 302 assertThat(compilation).succeeded(); 303 } 304 305 @Test abstractModuleWithInstanceMethod()306 public void abstractModuleWithInstanceMethod() { 307 JavaFileObject module = 308 JavaFileObjects.forSourceLines( 309 "test.TestModule", 310 "package test;", 311 "", 312 "import dagger.Module;", 313 "import dagger.Provides;", 314 "", 315 "@Module", 316 "abstract class TestModule {", 317 " @Provides int i() { return 1; }", 318 "}"); 319 JavaFileObject component = 320 JavaFileObjects.forSourceLines( 321 "test.TestComponent", 322 "package test;", 323 "", 324 "import dagger.Component;", 325 "", 326 "@Component(modules = TestModule.class)", 327 "interface TestComponent {", 328 " int i();", 329 "}"); 330 Compilation compilation = daggerCompiler().compile(module, component); 331 assertThat(compilation).failed(); 332 assertThat(compilation) 333 .hadErrorContaining("TestModule is abstract and has instance @Provides methods") 334 .inFile(component) 335 .onLineContaining("interface TestComponent"); 336 } 337 338 @Test abstractModuleWithInstanceMethod_subclassedIsAllowed()339 public void abstractModuleWithInstanceMethod_subclassedIsAllowed() { 340 JavaFileObject abstractModule = 341 JavaFileObjects.forSourceLines( 342 "test.AbstractModule", 343 "package test;", 344 "", 345 "import dagger.Module;", 346 "import dagger.Provides;", 347 "", 348 "@Module", 349 "abstract class AbstractModule {", 350 " @Provides int i() { return 1; }", 351 "}"); 352 JavaFileObject subclassedModule = 353 JavaFileObjects.forSourceLines( 354 "test.SubclassedModule", 355 "package test;", 356 "", 357 "import dagger.Module;", 358 "", 359 "@Module", 360 "class SubclassedModule extends AbstractModule {}"); 361 JavaFileObject component = 362 JavaFileObjects.forSourceLines( 363 "test.TestComponent", 364 "package test;", 365 "", 366 "import dagger.Component;", 367 "", 368 "@Component(modules = SubclassedModule.class)", 369 "interface TestComponent {", 370 " int i();", 371 "}"); 372 Compilation compilation = daggerCompiler().compile(abstractModule, subclassedModule, component); 373 assertThat(compilation).succeeded(); 374 } 375 } 376