xref: /aosp_15_r20/external/dagger2/javatests/dagger/spi/SpiPluginTest.java (revision f585d8a307d0621d6060bd7e80091fdcbf94fe27)
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