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 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 /** Tests for {ComponentHierarchyValidator}. */ 32 @RunWith(JUnit4.class) 33 public class ComponentHierarchyValidationTest { 34 @Test singletonSubcomponent()35 public void singletonSubcomponent() { 36 JavaFileObject component = 37 JavaFileObjects.forSourceLines( 38 "test.Parent", 39 "package test;", 40 "", 41 "import dagger.Component;", 42 "import javax.inject.Singleton;", 43 "", 44 "@Singleton", 45 "@Component", 46 "interface Parent {", 47 " Child child();", 48 "}"); 49 JavaFileObject subcomponent = 50 JavaFileObjects.forSourceLines( 51 "test.Child", 52 "package test;", 53 "", 54 "import dagger.Subcomponent;", 55 "import javax.inject.Singleton;", 56 "", 57 "@Singleton", 58 "@Subcomponent", 59 "interface Child {}"); 60 61 Compilation compilation = daggerCompiler().compile(component, subcomponent); 62 assertThat(compilation).failed(); 63 assertThat(compilation).hadErrorContaining("conflicting scopes"); 64 assertThat(compilation).hadErrorContaining("test.Parent also has @Singleton"); 65 66 Compilation withoutScopeValidation = 67 compilerWithOptions("-Adagger.disableInterComponentScopeValidation=none") 68 .compile(component, subcomponent); 69 assertThat(withoutScopeValidation).succeeded(); 70 } 71 72 @Test productionComponents_productionScopeImplicitOnBoth()73 public void productionComponents_productionScopeImplicitOnBoth() { 74 JavaFileObject component = 75 JavaFileObjects.forSourceLines( 76 "test.Parent", 77 "package test;", 78 "", 79 "import dagger.producers.ProductionComponent;", 80 "", 81 "@ProductionComponent(modules = ParentModule.class)", 82 "interface Parent {", 83 " Child child();", 84 " Object productionScopedObject();", 85 "}"); 86 JavaFileObject parentModule = 87 JavaFileObjects.forSourceLines( 88 "test.ParentModule", 89 "package test;", 90 "", 91 "import dagger.Provides;", 92 "import dagger.producers.ProducerModule;", 93 "import dagger.producers.ProductionScope;", 94 "", 95 "@ProducerModule", 96 "class ParentModule {", 97 " @Provides @ProductionScope Object parentScopedObject() { return new Object(); }", 98 "}"); 99 JavaFileObject subcomponent = 100 JavaFileObjects.forSourceLines( 101 "test.Child", 102 "package test;", 103 "", 104 "import dagger.producers.ProductionSubcomponent;", 105 "", 106 "@ProductionSubcomponent(modules = ChildModule.class)", 107 "interface Child {", 108 " String productionScopedString();", 109 "}"); 110 JavaFileObject childModule = 111 JavaFileObjects.forSourceLines( 112 "test.ChildModule", 113 "package test;", 114 "", 115 "import dagger.Provides;", 116 "import dagger.producers.ProducerModule;", 117 "import dagger.producers.ProductionScope;", 118 "", 119 "@ProducerModule", 120 "class ChildModule {", 121 " @Provides @ProductionScope String childScopedString() { return new String(); }", 122 "}"); 123 Compilation compilation = 124 daggerCompiler().compile(component, subcomponent, parentModule, childModule); 125 assertThat(compilation).succeeded(); 126 } 127 128 @Test producerModuleRepeated()129 public void producerModuleRepeated() { 130 JavaFileObject component = 131 JavaFileObjects.forSourceLines( 132 "test.Parent", 133 "package test;", 134 "", 135 "import dagger.producers.ProductionComponent;", 136 "", 137 "@ProductionComponent(modules = RepeatedProducerModule.class)", 138 "interface Parent {", 139 " Child child();", 140 "}"); 141 JavaFileObject repeatedModule = 142 JavaFileObjects.forSourceLines( 143 "test.RepeatedProducerModule", 144 "package test;", 145 "", 146 "import dagger.producers.ProducerModule;", 147 "", 148 "@ProducerModule", 149 "interface RepeatedProducerModule {}"); 150 JavaFileObject subcomponent = 151 JavaFileObjects.forSourceLines( 152 "test.Child", 153 "package test;", 154 "", 155 "import dagger.producers.ProductionSubcomponent;", 156 "", 157 "@ProductionSubcomponent(modules = RepeatedProducerModule.class)", 158 "interface Child {}"); 159 Compilation compilation = daggerCompiler().compile(component, subcomponent, repeatedModule); 160 assertThat(compilation).failed(); 161 assertThat(compilation) 162 .hadErrorContaining( 163 message( 164 "test.Child repeats @ProducerModules:", 165 " test.Parent also installs: test.RepeatedProducerModule")) 166 .inFile(component) 167 .onLineContaining("interface Parent"); 168 } 169 170 @Test factoryMethodForSubcomponentWithBuilder_isNotAllowed()171 public void factoryMethodForSubcomponentWithBuilder_isNotAllowed() { 172 JavaFileObject module = 173 JavaFileObjects.forSourceLines( 174 "test.TestModule", 175 "package test;", 176 "", 177 "import dagger.Module;", 178 "import dagger.Provides;", 179 "", 180 "@Module(subcomponents = Sub.class)", 181 "class TestModule {", 182 "}"); 183 184 JavaFileObject subcomponent = 185 JavaFileObjects.forSourceLines( 186 "test.Sub", 187 "package test;", 188 "", 189 "import dagger.Subcomponent;", 190 "", 191 "@Subcomponent", 192 "interface Sub {", 193 " @Subcomponent.Builder", 194 " interface Builder {", 195 " Sub build();", 196 " }", 197 "}"); 198 199 JavaFileObject component = 200 JavaFileObjects.forSourceLines( 201 "test.Sub", 202 "package test;", 203 "", 204 "import dagger.Component;", 205 "", 206 "@Component(modules = TestModule.class)", 207 "interface C {", 208 " Sub newSub();", 209 "}"); 210 211 Compilation compilation = daggerCompiler().compile(module, component, subcomponent); 212 assertThat(compilation).failed(); 213 assertThat(compilation) 214 .hadErrorContaining( 215 "Components may not have factory methods for subcomponents that define a builder."); 216 } 217 218 @Test repeatedModulesWithScopes()219 public void repeatedModulesWithScopes() { 220 JavaFileObject testScope = 221 JavaFileObjects.forSourceLines( 222 "test.TestScope", 223 "package test;", 224 "", 225 "import javax.inject.Scope;", 226 "", 227 "@Scope", 228 "@interface TestScope {}"); 229 JavaFileObject moduleWithScopedProvides = 230 JavaFileObjects.forSourceLines( 231 "test.ModuleWithScopedProvides", 232 "package test;", 233 "", 234 "import dagger.Module;", 235 "import dagger.Provides;", 236 "", 237 "@Module", 238 "class ModuleWithScopedProvides {", 239 " @Provides", 240 " @TestScope", 241 " static Object o() { return new Object(); }", 242 "}"); 243 JavaFileObject moduleWithScopedBinds = 244 JavaFileObjects.forSourceLines( 245 "test.ModuleWithScopedBinds", 246 "package test;", 247 "", 248 "import dagger.Binds;", 249 "import dagger.Module;", 250 "", 251 "@Module", 252 "interface ModuleWithScopedBinds {", 253 " @Binds", 254 " @TestScope", 255 " Object o(String s);", 256 "}"); 257 JavaFileObject parent = 258 JavaFileObjects.forSourceLines( 259 "test.Parent", 260 "package test;", 261 "", 262 "import dagger.Component;", 263 "", 264 "@Component(modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 265 "interface Parent {", 266 " Child child();", 267 "}"); 268 JavaFileObject child = 269 JavaFileObjects.forSourceLines( 270 "test.Child", 271 "package test;", 272 "", 273 "import dagger.Subcomponent;", 274 "", 275 "@Subcomponent(", 276 " modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 277 "interface Child {}"); 278 Compilation compilation = 279 daggerCompiler() 280 .compile(testScope, moduleWithScopedProvides, moduleWithScopedBinds, parent, child); 281 assertThat(compilation).failed(); 282 assertThat(compilation) 283 .hadErrorContaining( 284 message( 285 "test.Child repeats modules with scoped bindings or declarations:", 286 " - test.Parent also includes:", 287 " - test.ModuleWithScopedProvides with scopes: @test.TestScope", 288 " - test.ModuleWithScopedBinds with scopes: @test.TestScope")); 289 } 290 291 @Test repeatedModulesWithReusableScope()292 public void repeatedModulesWithReusableScope() { 293 JavaFileObject moduleWithScopedProvides = 294 JavaFileObjects.forSourceLines( 295 "test.ModuleWithScopedProvides", 296 "package test;", 297 "", 298 "import dagger.Module;", 299 "import dagger.Provides;", 300 "import dagger.Reusable;", 301 "", 302 "@Module", 303 "class ModuleWithScopedProvides {", 304 " @Provides", 305 " @Reusable", 306 " static Object o() { return new Object(); }", 307 "}"); 308 JavaFileObject moduleWithScopedBinds = 309 JavaFileObjects.forSourceLines( 310 "test.ModuleWithScopedBinds", 311 "package test;", 312 "", 313 "import dagger.Binds;", 314 "import dagger.Module;", 315 "import dagger.Reusable;", 316 "", 317 "@Module", 318 "interface ModuleWithScopedBinds {", 319 " @Binds", 320 " @Reusable", 321 " Object o(String s);", 322 "}"); 323 JavaFileObject parent = 324 JavaFileObjects.forSourceLines( 325 "test.Parent", 326 "package test;", 327 "", 328 "import dagger.Component;", 329 "", 330 "@Component(modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 331 "interface Parent {", 332 " Child child();", 333 "}"); 334 JavaFileObject child = 335 JavaFileObjects.forSourceLines( 336 "test.Child", 337 "package test;", 338 "", 339 "import dagger.Subcomponent;", 340 "", 341 "@Subcomponent(", 342 " modules = {ModuleWithScopedProvides.class, ModuleWithScopedBinds.class})", 343 "interface Child {}"); 344 Compilation compilation = 345 daggerCompiler() 346 .compile(moduleWithScopedProvides, moduleWithScopedBinds, parent, child); 347 assertThat(compilation).succeededWithoutWarnings(); 348 } 349 } 350