1*f585d8a3SJacky Wang /* 2*f585d8a3SJacky Wang * Copyright (C) 2018 The Dagger Authors. 3*f585d8a3SJacky Wang * 4*f585d8a3SJacky Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5*f585d8a3SJacky Wang * you may not use this file except in compliance with the License. 6*f585d8a3SJacky Wang * You may obtain a copy of the License at 7*f585d8a3SJacky Wang * 8*f585d8a3SJacky Wang * http://www.apache.org/licenses/LICENSE-2.0 9*f585d8a3SJacky Wang * 10*f585d8a3SJacky Wang * Unless required by applicable law or agreed to in writing, software 11*f585d8a3SJacky Wang * distributed under the License is distributed on an "AS IS" BASIS, 12*f585d8a3SJacky Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*f585d8a3SJacky Wang * See the License for the specific language governing permissions and 14*f585d8a3SJacky Wang * limitations under the License. 15*f585d8a3SJacky Wang */ 16*f585d8a3SJacky Wang 17*f585d8a3SJacky Wang package dagger.spi; 18*f585d8a3SJacky Wang 19*f585d8a3SJacky Wang import static com.google.testing.compile.CompilationSubject.assertThat; 20*f585d8a3SJacky Wang import static com.google.testing.compile.Compiler.javac; 21*f585d8a3SJacky Wang 22*f585d8a3SJacky Wang import com.google.common.base.Joiner; 23*f585d8a3SJacky Wang import com.google.common.collect.ImmutableList; 24*f585d8a3SJacky Wang import com.google.testing.compile.Compilation; 25*f585d8a3SJacky Wang import com.google.testing.compile.JavaFileObjects; 26*f585d8a3SJacky Wang import dagger.internal.codegen.ComponentProcessor; 27*f585d8a3SJacky Wang import javax.tools.JavaFileObject; 28*f585d8a3SJacky Wang import javax.tools.StandardLocation; 29*f585d8a3SJacky Wang import org.junit.Test; 30*f585d8a3SJacky Wang import org.junit.runner.RunWith; 31*f585d8a3SJacky Wang import org.junit.runners.JUnit4; 32*f585d8a3SJacky Wang 33*f585d8a3SJacky Wang @RunWith(JUnit4.class) 34*f585d8a3SJacky Wang public final class SpiPluginTest { 35*f585d8a3SJacky Wang @Test moduleBinding()36*f585d8a3SJacky Wang public void moduleBinding() { 37*f585d8a3SJacky Wang JavaFileObject module = 38*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 39*f585d8a3SJacky Wang "test.TestModule", 40*f585d8a3SJacky Wang "package test;", 41*f585d8a3SJacky Wang "", 42*f585d8a3SJacky Wang "import dagger.Module;", 43*f585d8a3SJacky Wang "import dagger.Provides;", 44*f585d8a3SJacky Wang "", 45*f585d8a3SJacky Wang "@Module", 46*f585d8a3SJacky Wang "interface TestModule {", 47*f585d8a3SJacky Wang " @Provides", 48*f585d8a3SJacky Wang " static int provideInt() {", 49*f585d8a3SJacky Wang " return 0;", 50*f585d8a3SJacky Wang " }", 51*f585d8a3SJacky Wang "}"); 52*f585d8a3SJacky Wang 53*f585d8a3SJacky Wang Compilation compilation = 54*f585d8a3SJacky Wang javac() 55*f585d8a3SJacky Wang .withProcessors(new ComponentProcessor()) 56*f585d8a3SJacky Wang .withOptions( 57*f585d8a3SJacky Wang "-Aerror_on_binding=java.lang.Integer", 58*f585d8a3SJacky Wang "-Adagger.fullBindingGraphValidation=ERROR", 59*f585d8a3SJacky Wang "-Adagger.pluginsVisitFullBindingGraphs=ENABLED") 60*f585d8a3SJacky Wang .compile(module); 61*f585d8a3SJacky Wang assertThat(compilation).failed(); 62*f585d8a3SJacky Wang assertThat(compilation) 63*f585d8a3SJacky Wang .hadErrorContaining( 64*f585d8a3SJacky Wang message("[FailingPlugin] Bad Binding: @Provides int test.TestModule.provideInt()")) 65*f585d8a3SJacky Wang .inFile(module) 66*f585d8a3SJacky Wang .onLineContaining("interface TestModule"); 67*f585d8a3SJacky Wang } 68*f585d8a3SJacky Wang 69*f585d8a3SJacky Wang @Test dependencyTraceAtBinding()70*f585d8a3SJacky Wang public void dependencyTraceAtBinding() { 71*f585d8a3SJacky Wang JavaFileObject foo = 72*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 73*f585d8a3SJacky Wang "test.Foo", 74*f585d8a3SJacky Wang "package test;", 75*f585d8a3SJacky Wang "", 76*f585d8a3SJacky Wang "import javax.inject.Inject;", 77*f585d8a3SJacky Wang "", 78*f585d8a3SJacky Wang "class Foo {", 79*f585d8a3SJacky Wang " @Inject Foo() {}", 80*f585d8a3SJacky Wang "}"); 81*f585d8a3SJacky Wang JavaFileObject component = 82*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 83*f585d8a3SJacky Wang "test.TestComponent", 84*f585d8a3SJacky Wang "package test;", 85*f585d8a3SJacky Wang "", 86*f585d8a3SJacky Wang "import dagger.Component;", 87*f585d8a3SJacky Wang "", 88*f585d8a3SJacky Wang "@Component", 89*f585d8a3SJacky Wang "interface TestComponent {", 90*f585d8a3SJacky Wang " Foo foo();", 91*f585d8a3SJacky Wang "}"); 92*f585d8a3SJacky Wang 93*f585d8a3SJacky Wang Compilation compilation = 94*f585d8a3SJacky Wang javac() 95*f585d8a3SJacky Wang .withProcessors(new ComponentProcessor()) 96*f585d8a3SJacky Wang .withOptions("-Aerror_on_binding=test.Foo") 97*f585d8a3SJacky Wang .compile(component, foo); 98*f585d8a3SJacky Wang assertThat(compilation).failed(); 99*f585d8a3SJacky Wang assertThat(compilation) 100*f585d8a3SJacky Wang .hadErrorContaining( 101*f585d8a3SJacky Wang message( 102*f585d8a3SJacky Wang "[FailingPlugin] Bad Binding: @Inject test.Foo()", 103*f585d8a3SJacky Wang " test.Foo is requested at", 104*f585d8a3SJacky Wang " test.TestComponent.foo()")) 105*f585d8a3SJacky Wang .inFile(component) 106*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 107*f585d8a3SJacky Wang } 108*f585d8a3SJacky Wang 109*f585d8a3SJacky Wang @Test dependencyTraceAtDependencyRequest()110*f585d8a3SJacky Wang public void dependencyTraceAtDependencyRequest() { 111*f585d8a3SJacky Wang JavaFileObject foo = 112*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 113*f585d8a3SJacky Wang "test.Foo", 114*f585d8a3SJacky Wang "package test;", 115*f585d8a3SJacky Wang "", 116*f585d8a3SJacky Wang "import javax.inject.Inject;", 117*f585d8a3SJacky Wang "", 118*f585d8a3SJacky Wang "class Foo {", 119*f585d8a3SJacky Wang " @Inject Foo(Duplicated inFooDep) {}", 120*f585d8a3SJacky Wang "}"); 121*f585d8a3SJacky Wang JavaFileObject duplicated = 122*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 123*f585d8a3SJacky Wang "test.Duplicated", 124*f585d8a3SJacky Wang "package test;", 125*f585d8a3SJacky Wang "", 126*f585d8a3SJacky Wang "import javax.inject.Inject;", 127*f585d8a3SJacky Wang "", 128*f585d8a3SJacky Wang "class Duplicated {", 129*f585d8a3SJacky Wang " @Inject Duplicated() {}", 130*f585d8a3SJacky Wang "}"); 131*f585d8a3SJacky Wang JavaFileObject entryPoint = 132*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 133*f585d8a3SJacky Wang "test.EntryPoint", 134*f585d8a3SJacky Wang "package test;", 135*f585d8a3SJacky Wang "", 136*f585d8a3SJacky Wang "import javax.inject.Inject;", 137*f585d8a3SJacky Wang "", 138*f585d8a3SJacky Wang "class EntryPoint {", 139*f585d8a3SJacky Wang " @Inject EntryPoint(Foo foo, Duplicated dup1, Duplicated dup2) {}", 140*f585d8a3SJacky Wang "}"); 141*f585d8a3SJacky Wang JavaFileObject chain1 = 142*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 143*f585d8a3SJacky Wang "test.Chain1", 144*f585d8a3SJacky Wang "package test;", 145*f585d8a3SJacky Wang "", 146*f585d8a3SJacky Wang "import javax.inject.Inject;", 147*f585d8a3SJacky Wang "", 148*f585d8a3SJacky Wang "class Chain1 {", 149*f585d8a3SJacky Wang " @Inject Chain1(Chain2 chain) {}", 150*f585d8a3SJacky Wang "}"); 151*f585d8a3SJacky Wang JavaFileObject chain2 = 152*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 153*f585d8a3SJacky Wang "test.Chain2", 154*f585d8a3SJacky Wang "package test;", 155*f585d8a3SJacky Wang "", 156*f585d8a3SJacky Wang "import javax.inject.Inject;", 157*f585d8a3SJacky Wang "", 158*f585d8a3SJacky Wang "class Chain2 {", 159*f585d8a3SJacky Wang " @Inject Chain2(Chain3 chain) {}", 160*f585d8a3SJacky Wang "}"); 161*f585d8a3SJacky Wang JavaFileObject chain3 = 162*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 163*f585d8a3SJacky Wang "test.Chain3", 164*f585d8a3SJacky Wang "package test;", 165*f585d8a3SJacky Wang "", 166*f585d8a3SJacky Wang "import javax.inject.Inject;", 167*f585d8a3SJacky Wang "", 168*f585d8a3SJacky Wang "class Chain3 {", 169*f585d8a3SJacky Wang " @Inject Chain3(Foo foo) {}", 170*f585d8a3SJacky Wang "}"); 171*f585d8a3SJacky Wang JavaFileObject component = 172*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 173*f585d8a3SJacky Wang "test.TestComponent", 174*f585d8a3SJacky Wang "package test;", 175*f585d8a3SJacky Wang "", 176*f585d8a3SJacky Wang "import dagger.Component;", 177*f585d8a3SJacky Wang "", 178*f585d8a3SJacky Wang "@Component", 179*f585d8a3SJacky Wang "interface TestComponent {", 180*f585d8a3SJacky Wang " EntryPoint entryPoint();", 181*f585d8a3SJacky Wang " Chain1 chain();", 182*f585d8a3SJacky Wang "}"); 183*f585d8a3SJacky Wang 184*f585d8a3SJacky Wang CompilationFactory compilationFactory = 185*f585d8a3SJacky Wang new CompilationFactory(component, foo, duplicated, entryPoint, chain1, chain2, chain3); 186*f585d8a3SJacky Wang 187*f585d8a3SJacky Wang assertThat(compilationFactory.compilationWithErrorOnDependency("entryPoint")) 188*f585d8a3SJacky Wang .hadErrorContaining( 189*f585d8a3SJacky Wang message( 190*f585d8a3SJacky Wang "[FailingPlugin] Bad Dependency: test.TestComponent.entryPoint() (entry point)", 191*f585d8a3SJacky Wang " test.EntryPoint is requested at", 192*f585d8a3SJacky Wang " test.TestComponent.entryPoint()")) 193*f585d8a3SJacky Wang .inFile(component) 194*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 195*f585d8a3SJacky Wang assertThat(compilationFactory.compilationWithErrorOnDependency("dup1")) 196*f585d8a3SJacky Wang .hadErrorContaining( 197*f585d8a3SJacky Wang message( 198*f585d8a3SJacky Wang "[FailingPlugin] Bad Dependency: test.EntryPoint(…, dup1, …)", 199*f585d8a3SJacky Wang " test.Duplicated is injected at", 200*f585d8a3SJacky Wang " test.EntryPoint(…, dup1, …)", 201*f585d8a3SJacky Wang " test.EntryPoint is requested at", 202*f585d8a3SJacky Wang " test.TestComponent.entryPoint()")) 203*f585d8a3SJacky Wang .inFile(component) 204*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 205*f585d8a3SJacky Wang assertThat(compilationFactory.compilationWithErrorOnDependency("dup2")) 206*f585d8a3SJacky Wang .hadErrorContaining( 207*f585d8a3SJacky Wang message( 208*f585d8a3SJacky Wang "[FailingPlugin] Bad Dependency: test.EntryPoint(…, dup2)", 209*f585d8a3SJacky Wang " test.Duplicated is injected at", 210*f585d8a3SJacky Wang " test.EntryPoint(…, dup2)", 211*f585d8a3SJacky Wang " test.EntryPoint is requested at", 212*f585d8a3SJacky Wang " test.TestComponent.entryPoint()")) 213*f585d8a3SJacky Wang .inFile(component) 214*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 215*f585d8a3SJacky Wang 216*f585d8a3SJacky Wang Compilation inFooDepCompilation = 217*f585d8a3SJacky Wang compilationFactory.compilationWithErrorOnDependency("inFooDep"); 218*f585d8a3SJacky Wang assertThat(inFooDepCompilation) 219*f585d8a3SJacky Wang .hadErrorContaining( 220*f585d8a3SJacky Wang message( 221*f585d8a3SJacky Wang "[FailingPlugin] Bad Dependency: test.Foo(inFooDep)", 222*f585d8a3SJacky Wang " test.Duplicated is injected at", 223*f585d8a3SJacky Wang " test.Foo(inFooDep)", 224*f585d8a3SJacky Wang " test.Foo is injected at", 225*f585d8a3SJacky Wang " test.EntryPoint(foo, …)", 226*f585d8a3SJacky Wang " test.EntryPoint is requested at", 227*f585d8a3SJacky Wang " test.TestComponent.entryPoint()", 228*f585d8a3SJacky Wang "The following other entry points also depend on it:", 229*f585d8a3SJacky Wang " test.TestComponent.chain()")) 230*f585d8a3SJacky Wang .inFile(component) 231*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 232*f585d8a3SJacky Wang } 233*f585d8a3SJacky Wang 234*f585d8a3SJacky Wang @Test dependencyTraceAtDependencyRequest_subcomponents()235*f585d8a3SJacky Wang public void dependencyTraceAtDependencyRequest_subcomponents() { 236*f585d8a3SJacky Wang JavaFileObject foo = 237*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 238*f585d8a3SJacky Wang "test.Foo", 239*f585d8a3SJacky Wang "package test;", 240*f585d8a3SJacky Wang "", 241*f585d8a3SJacky Wang "import javax.inject.Inject;", 242*f585d8a3SJacky Wang "", 243*f585d8a3SJacky Wang "class Foo {", 244*f585d8a3SJacky Wang " @Inject Foo() {}", 245*f585d8a3SJacky Wang "}"); 246*f585d8a3SJacky Wang JavaFileObject entryPoint = 247*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 248*f585d8a3SJacky Wang "test.EntryPoint", 249*f585d8a3SJacky Wang "package test;", 250*f585d8a3SJacky Wang "", 251*f585d8a3SJacky Wang "import javax.inject.Inject;", 252*f585d8a3SJacky Wang "", 253*f585d8a3SJacky Wang "class EntryPoint {", 254*f585d8a3SJacky Wang " @Inject EntryPoint(Foo foo) {}", 255*f585d8a3SJacky Wang "}"); 256*f585d8a3SJacky Wang JavaFileObject component = 257*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 258*f585d8a3SJacky Wang "test.TestComponent", 259*f585d8a3SJacky Wang "package test;", 260*f585d8a3SJacky Wang "", 261*f585d8a3SJacky Wang "import dagger.Component;", 262*f585d8a3SJacky Wang "", 263*f585d8a3SJacky Wang "@Component", 264*f585d8a3SJacky Wang "interface TestComponent {", 265*f585d8a3SJacky Wang " TestSubcomponent sub();", 266*f585d8a3SJacky Wang "}"); 267*f585d8a3SJacky Wang JavaFileObject subcomponent = 268*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 269*f585d8a3SJacky Wang "test.TestSubcomponent", 270*f585d8a3SJacky Wang "package test;", 271*f585d8a3SJacky Wang "", 272*f585d8a3SJacky Wang "import dagger.Subcomponent;", 273*f585d8a3SJacky Wang "", 274*f585d8a3SJacky Wang "@Subcomponent", 275*f585d8a3SJacky Wang "interface TestSubcomponent {", 276*f585d8a3SJacky Wang " EntryPoint childEntryPoint();", 277*f585d8a3SJacky Wang "}"); 278*f585d8a3SJacky Wang 279*f585d8a3SJacky Wang CompilationFactory compilationFactory = 280*f585d8a3SJacky Wang new CompilationFactory(component, subcomponent, foo, entryPoint); 281*f585d8a3SJacky Wang assertThat(compilationFactory.compilationWithErrorOnDependency("childEntryPoint")) 282*f585d8a3SJacky Wang .hadErrorContaining( 283*f585d8a3SJacky Wang message( 284*f585d8a3SJacky Wang "[FailingPlugin] Bad Dependency: " 285*f585d8a3SJacky Wang + "test.TestSubcomponent.childEntryPoint() (entry point)", 286*f585d8a3SJacky Wang " test.EntryPoint is requested at", 287*f585d8a3SJacky Wang " test.TestSubcomponent.childEntryPoint()" 288*f585d8a3SJacky Wang + " [test.TestComponent → test.TestSubcomponent]")) 289*f585d8a3SJacky Wang .inFile(component) 290*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 291*f585d8a3SJacky Wang assertThat(compilationFactory.compilationWithErrorOnDependency("foo")) 292*f585d8a3SJacky Wang .hadErrorContaining( 293*f585d8a3SJacky Wang // TODO(ronshapiro): Maybe make the component path resemble a stack trace: 294*f585d8a3SJacky Wang // test.TestSubcomponent is a child of 295*f585d8a3SJacky Wang // test.TestComponent 296*f585d8a3SJacky Wang // TODO(dpb): Or invert the order: Child → Parent 297*f585d8a3SJacky Wang message( 298*f585d8a3SJacky Wang "[FailingPlugin] Bad Dependency: test.EntryPoint(foo)", 299*f585d8a3SJacky Wang " test.Foo is injected at", 300*f585d8a3SJacky Wang " test.EntryPoint(foo)", 301*f585d8a3SJacky Wang " test.EntryPoint is requested at", 302*f585d8a3SJacky Wang " test.TestSubcomponent.childEntryPoint() " 303*f585d8a3SJacky Wang + "[test.TestComponent → test.TestSubcomponent]")) 304*f585d8a3SJacky Wang .inFile(component) 305*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 306*f585d8a3SJacky Wang } 307*f585d8a3SJacky Wang 308*f585d8a3SJacky Wang @Test errorOnComponent()309*f585d8a3SJacky Wang public void errorOnComponent() { 310*f585d8a3SJacky Wang JavaFileObject component = 311*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 312*f585d8a3SJacky Wang "test.TestComponent", 313*f585d8a3SJacky Wang "package test;", 314*f585d8a3SJacky Wang "", 315*f585d8a3SJacky Wang "import dagger.Component;", 316*f585d8a3SJacky Wang "", 317*f585d8a3SJacky Wang "@Component", 318*f585d8a3SJacky Wang "interface TestComponent {}"); 319*f585d8a3SJacky Wang 320*f585d8a3SJacky Wang Compilation compilation = 321*f585d8a3SJacky Wang javac() 322*f585d8a3SJacky Wang .withProcessors(new ComponentProcessor()) 323*f585d8a3SJacky Wang .withOptions("-Aerror_on_component") 324*f585d8a3SJacky Wang .compile(component); 325*f585d8a3SJacky Wang assertThat(compilation).failed(); 326*f585d8a3SJacky Wang assertThat(compilation) 327*f585d8a3SJacky Wang .hadErrorContaining("[FailingPlugin] Bad Component: test.TestComponent") 328*f585d8a3SJacky Wang .inFile(component) 329*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 330*f585d8a3SJacky Wang } 331*f585d8a3SJacky Wang 332*f585d8a3SJacky Wang @Test errorOnSubcomponent()333*f585d8a3SJacky Wang public void errorOnSubcomponent() { 334*f585d8a3SJacky Wang JavaFileObject subcomponent = 335*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 336*f585d8a3SJacky Wang "test.TestSubcomponent", 337*f585d8a3SJacky Wang "package test;", 338*f585d8a3SJacky Wang "", 339*f585d8a3SJacky Wang "import dagger.Subcomponent;", 340*f585d8a3SJacky Wang "", 341*f585d8a3SJacky Wang "@Subcomponent", 342*f585d8a3SJacky Wang "interface TestSubcomponent {}"); 343*f585d8a3SJacky Wang JavaFileObject component = 344*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 345*f585d8a3SJacky Wang "test.TestComponent", 346*f585d8a3SJacky Wang "package test;", 347*f585d8a3SJacky Wang "", 348*f585d8a3SJacky Wang "import dagger.Component;", 349*f585d8a3SJacky Wang "", 350*f585d8a3SJacky Wang "@Component", 351*f585d8a3SJacky Wang "interface TestComponent {", 352*f585d8a3SJacky Wang " TestSubcomponent subcomponent();", 353*f585d8a3SJacky Wang "}"); 354*f585d8a3SJacky Wang 355*f585d8a3SJacky Wang Compilation compilation = 356*f585d8a3SJacky Wang javac() 357*f585d8a3SJacky Wang .withProcessors(new ComponentProcessor()) 358*f585d8a3SJacky Wang .withOptions("-Aerror_on_subcomponents") 359*f585d8a3SJacky Wang .compile(component, subcomponent); 360*f585d8a3SJacky Wang assertThat(compilation).failed(); 361*f585d8a3SJacky Wang assertThat(compilation) 362*f585d8a3SJacky Wang .hadErrorContaining( 363*f585d8a3SJacky Wang "[FailingPlugin] Bad Subcomponent: test.TestComponent → test.TestSubcomponent " 364*f585d8a3SJacky Wang + "[test.TestComponent → test.TestSubcomponent]") 365*f585d8a3SJacky Wang .inFile(component) 366*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 367*f585d8a3SJacky Wang } 368*f585d8a3SJacky Wang 369*f585d8a3SJacky Wang // SpiDiagnosticReporter uses a shortest path algorithm to determine a dependency trace to a 370*f585d8a3SJacky Wang // binding. Without modifications, this would produce a strange error if a shorter path exists 371*f585d8a3SJacky Wang // from one entrypoint, through a @Module.subcomponents builder binding edge, and to the binding 372*f585d8a3SJacky Wang // usage within the subcomponent. Therefore, when scanning for the shortest path, we only consider 373*f585d8a3SJacky Wang // BindingNodes so we don't cross component boundaries. This test exhibits this case. 374*f585d8a3SJacky Wang @Test shortestPathToBindingExistsThroughSubcomponentBuilder()375*f585d8a3SJacky Wang public void shortestPathToBindingExistsThroughSubcomponentBuilder() { 376*f585d8a3SJacky Wang JavaFileObject chain1 = 377*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 378*f585d8a3SJacky Wang "test.Chain1", 379*f585d8a3SJacky Wang "package test;", 380*f585d8a3SJacky Wang "", 381*f585d8a3SJacky Wang "import javax.inject.Inject;", 382*f585d8a3SJacky Wang "", 383*f585d8a3SJacky Wang "class Chain1 {", 384*f585d8a3SJacky Wang " @Inject Chain1(Chain2 chain) {}", 385*f585d8a3SJacky Wang "}"); 386*f585d8a3SJacky Wang JavaFileObject chain2 = 387*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 388*f585d8a3SJacky Wang "test.Chain2", 389*f585d8a3SJacky Wang "package test;", 390*f585d8a3SJacky Wang "", 391*f585d8a3SJacky Wang "import javax.inject.Inject;", 392*f585d8a3SJacky Wang "", 393*f585d8a3SJacky Wang "class Chain2 {", 394*f585d8a3SJacky Wang " @Inject Chain2(Chain3 chain) {}", 395*f585d8a3SJacky Wang "}"); 396*f585d8a3SJacky Wang JavaFileObject chain3 = 397*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 398*f585d8a3SJacky Wang "test.Chain3", 399*f585d8a3SJacky Wang "package test;", 400*f585d8a3SJacky Wang "", 401*f585d8a3SJacky Wang "import javax.inject.Inject;", 402*f585d8a3SJacky Wang "", 403*f585d8a3SJacky Wang "class Chain3 {", 404*f585d8a3SJacky Wang " @Inject Chain3(ExposedOnSubcomponent exposedOnSubcomponent) {}", 405*f585d8a3SJacky Wang "}"); 406*f585d8a3SJacky Wang JavaFileObject exposedOnSubcomponent = 407*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 408*f585d8a3SJacky Wang "test.ExposedOnSubcomponent", 409*f585d8a3SJacky Wang "package test;", 410*f585d8a3SJacky Wang "", 411*f585d8a3SJacky Wang "import javax.inject.Inject;", 412*f585d8a3SJacky Wang "", 413*f585d8a3SJacky Wang "class ExposedOnSubcomponent {", 414*f585d8a3SJacky Wang " @Inject ExposedOnSubcomponent() {}", 415*f585d8a3SJacky Wang "}"); 416*f585d8a3SJacky Wang JavaFileObject subcomponent = 417*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 418*f585d8a3SJacky Wang "test.TestSubcomponent", 419*f585d8a3SJacky Wang "package test;", 420*f585d8a3SJacky Wang "", 421*f585d8a3SJacky Wang "import dagger.Subcomponent;", 422*f585d8a3SJacky Wang "", 423*f585d8a3SJacky Wang "@Subcomponent", 424*f585d8a3SJacky Wang "interface TestSubcomponent {", 425*f585d8a3SJacky Wang " ExposedOnSubcomponent exposedOnSubcomponent();", 426*f585d8a3SJacky Wang "", 427*f585d8a3SJacky Wang " @Subcomponent.Builder", 428*f585d8a3SJacky Wang " interface Builder {", 429*f585d8a3SJacky Wang " TestSubcomponent build();", 430*f585d8a3SJacky Wang " }", 431*f585d8a3SJacky Wang "}"); 432*f585d8a3SJacky Wang JavaFileObject subcomponentModule = 433*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 434*f585d8a3SJacky Wang "test.SubcomponentModule", 435*f585d8a3SJacky Wang "package test;", 436*f585d8a3SJacky Wang "", 437*f585d8a3SJacky Wang "import dagger.Module;", 438*f585d8a3SJacky Wang "", 439*f585d8a3SJacky Wang "@Module(subcomponents = TestSubcomponent.class)", 440*f585d8a3SJacky Wang "interface SubcomponentModule {}"); 441*f585d8a3SJacky Wang JavaFileObject component = 442*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 443*f585d8a3SJacky Wang "test.TestComponent", 444*f585d8a3SJacky Wang "package test;", 445*f585d8a3SJacky Wang "", 446*f585d8a3SJacky Wang "import dagger.Component;", 447*f585d8a3SJacky Wang "import javax.inject.Singleton;", 448*f585d8a3SJacky Wang "", 449*f585d8a3SJacky Wang "@Singleton", 450*f585d8a3SJacky Wang "@Component(modules = SubcomponentModule.class)", 451*f585d8a3SJacky Wang "interface TestComponent {", 452*f585d8a3SJacky Wang " Chain1 chain();", 453*f585d8a3SJacky Wang " TestSubcomponent.Builder subcomponent();", 454*f585d8a3SJacky Wang "}"); 455*f585d8a3SJacky Wang 456*f585d8a3SJacky Wang Compilation compilation = 457*f585d8a3SJacky Wang javac() 458*f585d8a3SJacky Wang .withProcessors(new ComponentProcessor()) 459*f585d8a3SJacky Wang .withOptions("-Aerror_on_binding=test.ExposedOnSubcomponent") 460*f585d8a3SJacky Wang .compile( 461*f585d8a3SJacky Wang component, 462*f585d8a3SJacky Wang subcomponent, 463*f585d8a3SJacky Wang chain1, 464*f585d8a3SJacky Wang chain2, 465*f585d8a3SJacky Wang chain3, 466*f585d8a3SJacky Wang exposedOnSubcomponent, 467*f585d8a3SJacky Wang subcomponentModule); 468*f585d8a3SJacky Wang assertThat(compilation) 469*f585d8a3SJacky Wang .hadErrorContaining( 470*f585d8a3SJacky Wang message( 471*f585d8a3SJacky Wang "[FailingPlugin] Bad Binding: @Inject test.ExposedOnSubcomponent()", 472*f585d8a3SJacky Wang " test.ExposedOnSubcomponent is injected at", 473*f585d8a3SJacky Wang " test.Chain3(exposedOnSubcomponent)", 474*f585d8a3SJacky Wang " test.Chain3 is injected at", 475*f585d8a3SJacky Wang " test.Chain2(chain)", 476*f585d8a3SJacky Wang " test.Chain2 is injected at", 477*f585d8a3SJacky Wang " test.Chain1(chain)", 478*f585d8a3SJacky Wang " test.Chain1 is requested at", 479*f585d8a3SJacky Wang " test.TestComponent.chain()", 480*f585d8a3SJacky Wang "The following other entry points also depend on it:", 481*f585d8a3SJacky Wang " test.TestSubcomponent.exposedOnSubcomponent() " 482*f585d8a3SJacky Wang + "[test.TestComponent → test.TestSubcomponent]")) 483*f585d8a3SJacky Wang .inFile(component) 484*f585d8a3SJacky Wang .onLineContaining("interface TestComponent"); 485*f585d8a3SJacky Wang } 486*f585d8a3SJacky Wang 487*f585d8a3SJacky Wang @Test onPluginEnd()488*f585d8a3SJacky Wang public void onPluginEnd() { 489*f585d8a3SJacky Wang JavaFileObject component = 490*f585d8a3SJacky Wang JavaFileObjects.forSourceLines( 491*f585d8a3SJacky Wang "test.TestComponent", 492*f585d8a3SJacky Wang "package test;", 493*f585d8a3SJacky Wang "", 494*f585d8a3SJacky Wang "import dagger.Component;", 495*f585d8a3SJacky Wang "", 496*f585d8a3SJacky Wang "@Component", 497*f585d8a3SJacky Wang "interface TestComponent {}"); 498*f585d8a3SJacky Wang Compilation compilation = javac().withProcessors(new ComponentProcessor()).compile(component); 499*f585d8a3SJacky Wang assertThat(compilation) 500*f585d8a3SJacky Wang .generatedFile(StandardLocation.SOURCE_OUTPUT, "", "onPluginEndTest.txt"); 501*f585d8a3SJacky Wang } 502*f585d8a3SJacky Wang 503*f585d8a3SJacky Wang // This works around an issue in the opensource compile testing where only one diagnostic is 504*f585d8a3SJacky Wang // recorded per line. When multiple validation items resolve to the same entry point, we can 505*f585d8a3SJacky Wang // only see the first. This helper class makes it easier to compile all of the files in the test 506*f585d8a3SJacky Wang // multiple times with different options to single out each error 507*f585d8a3SJacky Wang private static class CompilationFactory { 508*f585d8a3SJacky Wang private final ImmutableList<JavaFileObject> javaFileObjects; 509*f585d8a3SJacky Wang CompilationFactory(JavaFileObject... javaFileObjects)510*f585d8a3SJacky Wang CompilationFactory(JavaFileObject... javaFileObjects) { 511*f585d8a3SJacky Wang this.javaFileObjects = ImmutableList.copyOf(javaFileObjects); 512*f585d8a3SJacky Wang } 513*f585d8a3SJacky Wang compilationWithErrorOnDependency(String dependencySimpleName)514*f585d8a3SJacky Wang private Compilation compilationWithErrorOnDependency(String dependencySimpleName) { 515*f585d8a3SJacky Wang return javac() 516*f585d8a3SJacky Wang .withProcessors(new ComponentProcessor()) 517*f585d8a3SJacky Wang .withOptions("-Aerror_on_dependency=" + dependencySimpleName) 518*f585d8a3SJacky Wang .compile(javaFileObjects); 519*f585d8a3SJacky Wang } 520*f585d8a3SJacky Wang } 521*f585d8a3SJacky Wang message(String... lines)522*f585d8a3SJacky Wang private static String message(String... lines) { 523*f585d8a3SJacky Wang return Joiner.on("\n ").join(lines); 524*f585d8a3SJacky Wang } 525*f585d8a3SJacky Wang } 526