1 /* 2 * Copyright (C) 2016 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 androidx.room.compiler.processing.util.Source; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableMap; 22 import dagger.testing.compile.CompilerTests; 23 import org.junit.Test; 24 import org.junit.runner.RunWith; 25 import org.junit.runners.Parameterized; 26 import org.junit.runners.Parameterized.Parameters; 27 28 /** Tests for {ComponentHierarchyValidator}. */ 29 @RunWith(Parameterized.class) 30 public class ComponentHierarchyValidationTest { 31 @Parameters(name = "{0}") parameters()32 public static ImmutableList<Object[]> parameters() { 33 return CompilerMode.TEST_PARAMETERS; 34 } 35 36 private final CompilerMode compilerMode; 37 ComponentHierarchyValidationTest(CompilerMode compilerMode)38 public ComponentHierarchyValidationTest(CompilerMode compilerMode) { 39 this.compilerMode = compilerMode; 40 } 41 42 @Test singletonSubcomponent()43 public void singletonSubcomponent() { 44 Source component = 45 CompilerTests.javaSource( 46 "test.Parent", 47 "package test;", 48 "", 49 "import dagger.Component;", 50 "import javax.inject.Singleton;", 51 "", 52 "@Singleton", 53 "@Component", 54 "interface Parent {", 55 " Child child();", 56 "}"); 57 Source subcomponent = 58 CompilerTests.javaSource( 59 "test.Child", 60 "package test;", 61 "", 62 "import dagger.Subcomponent;", 63 "import javax.inject.Singleton;", 64 "", 65 "@Singleton", 66 "@Subcomponent", 67 "interface Child {}"); 68 69 CompilerTests.daggerCompiler(component, subcomponent) 70 .withProcessingOptions(compilerMode.processorOptions()) 71 .compile( 72 subject -> { 73 subject.hasErrorCount(1); 74 subject.hasErrorContaining( 75 String.join( 76 "\n", 77 "test.Child has conflicting scopes:", 78 " test.Parent also has @Singleton")); 79 }); 80 81 // Check that compiling with disableInterComponentScopeValidation=none flag succeeds. 82 CompilerTests.daggerCompiler(component, subcomponent) 83 .withProcessingOptions( 84 ImmutableMap.<String, String>builder() 85 .putAll(compilerMode.processorOptions()) 86 .put("dagger.disableInterComponentScopeValidation", "none") 87 .buildOrThrow()) 88 .compile(subject -> subject.hasErrorCount(0)); 89 } 90 91 @Test productionComponents_productionScopeImplicitOnBoth()92 public void productionComponents_productionScopeImplicitOnBoth() { 93 Source component = 94 CompilerTests.javaSource( 95 "test.Parent", 96 "package test;", 97 "", 98 "import dagger.producers.ProductionComponent;", 99 "", 100 "@ProductionComponent(modules = ParentModule.class)", 101 "interface Parent {", 102 " Child child();", 103 " Object productionScopedObject();", 104 "}"); 105 Source parentModule = 106 CompilerTests.javaSource( 107 "test.ParentModule", 108 "package test;", 109 "", 110 "import dagger.Provides;", 111 "import dagger.producers.ProducerModule;", 112 "import dagger.producers.ProductionScope;", 113 "", 114 "@ProducerModule", 115 "class ParentModule {", 116 " @Provides @ProductionScope Object parentScopedObject() { return new Object(); }", 117 "}"); 118 Source subcomponent = 119 CompilerTests.javaSource( 120 "test.Child", 121 "package test;", 122 "", 123 "import dagger.producers.ProductionSubcomponent;", 124 "", 125 "@ProductionSubcomponent(modules = ChildModule.class)", 126 "interface Child {", 127 " String productionScopedString();", 128 "}"); 129 Source childModule = 130 CompilerTests.javaSource( 131 "test.ChildModule", 132 "package test;", 133 "", 134 "import dagger.Provides;", 135 "import dagger.producers.ProducerModule;", 136 "import dagger.producers.ProductionScope;", 137 "", 138 "@ProducerModule", 139 "class ChildModule {", 140 " @Provides @ProductionScope String childScopedString() { return new String(); }", 141 "}"); 142 CompilerTests.daggerCompiler(component, subcomponent, parentModule, childModule) 143 .withProcessingOptions(compilerMode.processorOptions()) 144 .compile(subject -> subject.hasErrorCount(0)); 145 } 146 147 @Test producerModuleRepeated()148 public void producerModuleRepeated() { 149 Source component = 150 CompilerTests.javaSource( 151 "test.Parent", 152 "package test;", 153 "", 154 "import dagger.producers.ProductionComponent;", 155 "", 156 "@ProductionComponent(modules = RepeatedProducerModule.class)", 157 "interface Parent {", 158 " Child child();", 159 "}"); 160 Source repeatedModule = 161 CompilerTests.javaSource( 162 "test.RepeatedProducerModule", 163 "package test;", 164 "", 165 "import dagger.producers.ProducerModule;", 166 "", 167 "@ProducerModule", 168 "interface RepeatedProducerModule {}"); 169 Source subcomponent = 170 CompilerTests.javaSource( 171 "test.Child", 172 "package test;", 173 "", 174 "import dagger.producers.ProductionSubcomponent;", 175 "", 176 "@ProductionSubcomponent(modules = RepeatedProducerModule.class)", 177 "interface Child {}"); 178 CompilerTests.daggerCompiler(component, subcomponent, repeatedModule) 179 .withProcessingOptions(compilerMode.processorOptions()) 180 .compile( 181 subject -> { 182 subject.hasErrorCount(1); 183 subject.hasErrorContaining( 184 String.join( 185 "\n", 186 "test.Child repeats @ProducerModules:", 187 "test.Parent also installs: test.RepeatedProducerModule")) 188 .onSource(component) 189 .onLineContaining("interface Parent"); 190 }); 191 } 192 193 @Test factoryMethodForSubcomponentWithBuilder_isNotAllowed()194 public void factoryMethodForSubcomponentWithBuilder_isNotAllowed() { 195 Source module = 196 CompilerTests.javaSource( 197 "test.TestModule", 198 "package test;", 199 "", 200 "import dagger.Module;", 201 "import dagger.Provides;", 202 "", 203 "@Module(subcomponents = Sub.class)", 204 "class TestModule {", 205 "}"); 206 207 Source subcomponent = 208 CompilerTests.javaSource( 209 "test.Sub", 210 "package test;", 211 "", 212 "import dagger.Subcomponent;", 213 "", 214 "@Subcomponent", 215 "interface Sub {", 216 " @Subcomponent.Builder", 217 " interface Builder {", 218 " Sub build();", 219 " }", 220 "}"); 221 222 Source component = 223 CompilerTests.javaSource( 224 "test.C", 225 "package test;", 226 "", 227 "import dagger.Component;", 228 "", 229 "@Component(modules = TestModule.class)", 230 "interface C {", 231 " Sub newSub();", 232 "}"); 233 234 CompilerTests.daggerCompiler(module, component, subcomponent) 235 .withProcessingOptions(compilerMode.processorOptions()) 236 .compile( 237 subject -> { 238 subject.hasErrorCount(1); 239 subject.hasErrorContaining( 240 "Components may not have factory methods for subcomponents that define a " 241 + "builder."); 242 }); 243 } 244 245 @Test repeatedModulesWithScopes()246 public void repeatedModulesWithScopes() { 247 Source testScope = 248 CompilerTests.javaSource( 249 "test.TestScope", 250 "package test;", 251 "", 252 "import javax.inject.Scope;", 253 "", 254 "@Scope", 255 "@interface TestScope {}"); 256 Source moduleWithScopedProvides = 257 CompilerTests.javaSource( 258 "test.ModuleWithScopedProvides", 259 "package test;", 260 "", 261 "import dagger.Module;", 262 "import dagger.Provides;", 263 "", 264 "@Module", 265 "class ModuleWithScopedProvides {", 266 " @Provides", 267 " @TestScope", 268 " static Object o() { return new Object(); }", 269 "}"); 270 Source moduleWithScopedBinds = 271 CompilerTests.javaSource( 272 "test.ModuleWithScopedBinds", 273 "package test;", 274 "", 275 "import dagger.Binds;", 276 "import dagger.Module;", 277 "", 278 "@Module", 279 "interface ModuleWithScopedBinds {", 280 " @Binds", 281 " @TestScope", 282 " Object o(String s);", 283 "}"); 284 Source parent = 285 CompilerTests.javaSource( 286 "test.Parent", 287 "package test;", 288 "", 289 "import dagger.Component;", 290 "", 291 "@Component(modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 292 "interface Parent {", 293 " Child child();", 294 "}"); 295 Source child = 296 CompilerTests.javaSource( 297 "test.Child", 298 "package test;", 299 "", 300 "import dagger.Subcomponent;", 301 "", 302 "@Subcomponent(", 303 " modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 304 "interface Child {}"); 305 CompilerTests.daggerCompiler( 306 testScope, moduleWithScopedProvides, moduleWithScopedBinds, parent, child) 307 .withProcessingOptions(compilerMode.processorOptions()) 308 .compile( 309 subject -> { 310 subject.hasErrorCount(1); 311 subject.hasErrorContaining( 312 String.join( 313 "\n", 314 "test.Child repeats modules with scoped bindings or declarations:", 315 "- test.Parent also includes:", 316 " - test.ModuleWithScopedProvides with scopes: @test.TestScope", 317 " - test.ModuleWithScopedBinds with scopes: @test.TestScope")); 318 }); 319 } 320 321 @Test repeatedModulesWithReusableScope()322 public void repeatedModulesWithReusableScope() { 323 Source moduleWithScopedProvides = 324 CompilerTests.javaSource( 325 "test.ModuleWithScopedProvides", 326 "package test;", 327 "", 328 "import dagger.Module;", 329 "import dagger.Provides;", 330 "import dagger.Reusable;", 331 "", 332 "@Module", 333 "class ModuleWithScopedProvides {", 334 " @Provides", 335 " @Reusable", 336 " static Object o() { return new Object(); }", 337 "}"); 338 Source moduleWithScopedBinds = 339 CompilerTests.javaSource( 340 "test.ModuleWithScopedBinds", 341 "package test;", 342 "", 343 "import dagger.Binds;", 344 "import dagger.Module;", 345 "import dagger.Reusable;", 346 "", 347 "@Module", 348 "interface ModuleWithScopedBinds {", 349 " @Binds", 350 " @Reusable", 351 " Object o(String s);", 352 "}"); 353 Source parent = 354 CompilerTests.javaSource( 355 "test.Parent", 356 "package test;", 357 "", 358 "import dagger.Component;", 359 "", 360 "@Component(modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 361 "interface Parent {", 362 " Child child();", 363 "}"); 364 Source child = 365 CompilerTests.javaSource( 366 "test.Child", 367 "package test;", 368 "", 369 "import dagger.Subcomponent;", 370 "", 371 "@Subcomponent(", 372 " modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 373 "interface Child {}"); 374 CompilerTests.daggerCompiler(moduleWithScopedProvides, moduleWithScopedBinds, parent, child) 375 .withProcessingOptions(compilerMode.processorOptions()) 376 .compile( 377 subject -> { 378 subject.hasErrorCount(0); 379 subject.hasWarningCount(0); 380 }); 381 } 382 } 383