1*f585d8a3SJacky Wang /*
2*f585d8a3SJacky Wang  * Copyright (C) 2014 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.internal.codegen;
18*f585d8a3SJacky Wang 
19*f585d8a3SJacky Wang import androidx.room.compiler.processing.util.Source;
20*f585d8a3SJacky Wang import com.google.common.collect.ImmutableList;
21*f585d8a3SJacky Wang import dagger.testing.compile.CompilerTests;
22*f585d8a3SJacky Wang import org.junit.Test;
23*f585d8a3SJacky Wang import org.junit.runner.RunWith;
24*f585d8a3SJacky Wang import org.junit.runners.Parameterized;
25*f585d8a3SJacky Wang import org.junit.runners.Parameterized.Parameters;
26*f585d8a3SJacky Wang 
27*f585d8a3SJacky Wang /**
28*f585d8a3SJacky Wang  * Tests that errors are reported correctly when a {@code @Binds} method's delegate (the type of its
29*f585d8a3SJacky Wang  * parameter) is missing.
30*f585d8a3SJacky Wang  */
31*f585d8a3SJacky Wang @RunWith(Parameterized.class)
32*f585d8a3SJacky Wang public class BindsMissingDelegateValidationTest {
33*f585d8a3SJacky Wang   @Parameters(name = "{0}")
parameters()34*f585d8a3SJacky Wang   public static ImmutableList<Object[]> parameters() {
35*f585d8a3SJacky Wang     return CompilerMode.TEST_PARAMETERS;
36*f585d8a3SJacky Wang   }
37*f585d8a3SJacky Wang 
38*f585d8a3SJacky Wang   private final CompilerMode compilerMode;
39*f585d8a3SJacky Wang 
BindsMissingDelegateValidationTest(CompilerMode compilerMode)40*f585d8a3SJacky Wang   public BindsMissingDelegateValidationTest(CompilerMode compilerMode) {
41*f585d8a3SJacky Wang     this.compilerMode = compilerMode;
42*f585d8a3SJacky Wang   }
43*f585d8a3SJacky Wang 
44*f585d8a3SJacky Wang   @Test
bindsMissingDelegate()45*f585d8a3SJacky Wang   public void bindsMissingDelegate() {
46*f585d8a3SJacky Wang     Source component =
47*f585d8a3SJacky Wang         CompilerTests.javaSource(
48*f585d8a3SJacky Wang             "test.C",
49*f585d8a3SJacky Wang             "package test;",
50*f585d8a3SJacky Wang             "",
51*f585d8a3SJacky Wang             "import dagger.Binds;",
52*f585d8a3SJacky Wang             "import dagger.Component;",
53*f585d8a3SJacky Wang             "import dagger.Module;",
54*f585d8a3SJacky Wang             "",
55*f585d8a3SJacky Wang             "@Component(modules = C.TestModule.class)",
56*f585d8a3SJacky Wang             "interface C {",
57*f585d8a3SJacky Wang             "  Object object();",
58*f585d8a3SJacky Wang             "",
59*f585d8a3SJacky Wang             "  static class NotBound {}",
60*f585d8a3SJacky Wang             "",
61*f585d8a3SJacky Wang             "  @Module",
62*f585d8a3SJacky Wang             "  abstract static class TestModule {",
63*f585d8a3SJacky Wang             "    @Binds abstract Object bindObject(NotBound notBound);",
64*f585d8a3SJacky Wang             "  }",
65*f585d8a3SJacky Wang             "}");
66*f585d8a3SJacky Wang 
67*f585d8a3SJacky Wang     CompilerTests.daggerCompiler(component)
68*f585d8a3SJacky Wang         .withProcessingOptions(compilerMode.processorOptions())
69*f585d8a3SJacky Wang         .compile(
70*f585d8a3SJacky Wang             subject -> {
71*f585d8a3SJacky Wang               subject.hasErrorCount(1);
72*f585d8a3SJacky Wang               subject.hasErrorContaining("test.C.NotBound cannot be provided")
73*f585d8a3SJacky Wang                   .onSource(component)
74*f585d8a3SJacky Wang                   .onLineContaining("interface C");
75*f585d8a3SJacky Wang             });
76*f585d8a3SJacky Wang   }
77*f585d8a3SJacky Wang 
78*f585d8a3SJacky Wang   @Test
bindsMissingDelegate_duplicateBinding()79*f585d8a3SJacky Wang   public void bindsMissingDelegate_duplicateBinding() {
80*f585d8a3SJacky Wang     Source component =
81*f585d8a3SJacky Wang         CompilerTests.javaSource(
82*f585d8a3SJacky Wang             "test.C",
83*f585d8a3SJacky Wang             "package test;",
84*f585d8a3SJacky Wang             "",
85*f585d8a3SJacky Wang             "import dagger.Binds;",
86*f585d8a3SJacky Wang             "import dagger.Component;",
87*f585d8a3SJacky Wang             "import dagger.Module;",
88*f585d8a3SJacky Wang             "import dagger.Provides;",
89*f585d8a3SJacky Wang             "",
90*f585d8a3SJacky Wang             "@Component(modules = C.TestModule.class)",
91*f585d8a3SJacky Wang             "interface C {",
92*f585d8a3SJacky Wang             "  Object object();",
93*f585d8a3SJacky Wang             "",
94*f585d8a3SJacky Wang             "  static class NotBound {}",
95*f585d8a3SJacky Wang             "",
96*f585d8a3SJacky Wang             "  @Module",
97*f585d8a3SJacky Wang             "  abstract static class TestModule {",
98*f585d8a3SJacky Wang             "    @Binds abstract Object bindObject(NotBound notBound);",
99*f585d8a3SJacky Wang             "    @Provides static Object provideObject() { return new Object(); }",
100*f585d8a3SJacky Wang             "  }",
101*f585d8a3SJacky Wang             "}");
102*f585d8a3SJacky Wang 
103*f585d8a3SJacky Wang     CompilerTests.daggerCompiler(component)
104*f585d8a3SJacky Wang         .withProcessingOptions(compilerMode.processorOptions())
105*f585d8a3SJacky Wang         .compile(
106*f585d8a3SJacky Wang             subject -> {
107*f585d8a3SJacky Wang               subject.hasErrorCount(1);
108*f585d8a3SJacky Wang               // Some versions of javacs report only the first error for each source line so we
109*f585d8a3SJacky Wang               // allow 1 of the assertions below to fail.
110*f585d8a3SJacky Wang               // TODO(bcorso): Add CompilationResultSubject#hasErrorContainingMatch() to do this
111*f585d8a3SJacky Wang               // more elegantly (see CL/469765892).
112*f585d8a3SJacky Wang               java.util.List<Error> errors = new java.util.ArrayList<>();
113*f585d8a3SJacky Wang               try {
114*f585d8a3SJacky Wang                 subject.hasErrorContaining("test.C.NotBound cannot be provided")
115*f585d8a3SJacky Wang                     .onSource(component)
116*f585d8a3SJacky Wang                     .onLineContaining("interface C");
117*f585d8a3SJacky Wang               } catch (Error e) {
118*f585d8a3SJacky Wang                 errors.add(e);
119*f585d8a3SJacky Wang               }
120*f585d8a3SJacky Wang               try {
121*f585d8a3SJacky Wang                 subject.hasErrorContaining("Object is bound multiple times:")
122*f585d8a3SJacky Wang                     .onSource(component)
123*f585d8a3SJacky Wang                     .onLineContaining("interface C");
124*f585d8a3SJacky Wang                 subject.hasErrorContaining(
125*f585d8a3SJacky Wang                     "@Binds Object test.C.TestModule.bindObject(test.C.NotBound)");
126*f585d8a3SJacky Wang                 subject.hasErrorContaining("@Provides Object test.C.TestModule.provideObject()");
127*f585d8a3SJacky Wang               } catch (Error e) {
128*f585d8a3SJacky Wang                 errors.add(e);
129*f585d8a3SJacky Wang               }
130*f585d8a3SJacky Wang               com.google.common.truth.Truth.assertThat(errors.size()).isAtMost(1);
131*f585d8a3SJacky Wang             });
132*f585d8a3SJacky Wang   }
133*f585d8a3SJacky Wang 
134*f585d8a3SJacky Wang   @Test
bindsMissingDelegate_setBinding()135*f585d8a3SJacky Wang   public void bindsMissingDelegate_setBinding() {
136*f585d8a3SJacky Wang     Source component =
137*f585d8a3SJacky Wang         CompilerTests.javaSource(
138*f585d8a3SJacky Wang             "test.C",
139*f585d8a3SJacky Wang             "package test;",
140*f585d8a3SJacky Wang             "",
141*f585d8a3SJacky Wang             "import dagger.Binds;",
142*f585d8a3SJacky Wang             "import dagger.Component;",
143*f585d8a3SJacky Wang             "import dagger.Module;",
144*f585d8a3SJacky Wang             "import dagger.multibindings.IntoSet;",
145*f585d8a3SJacky Wang             "import java.util.Set;",
146*f585d8a3SJacky Wang             "",
147*f585d8a3SJacky Wang             "@Component(modules = C.TestModule.class)",
148*f585d8a3SJacky Wang             "interface C {",
149*f585d8a3SJacky Wang             "  Set<Object> objects();",
150*f585d8a3SJacky Wang             "",
151*f585d8a3SJacky Wang             "  static class NotBound {}",
152*f585d8a3SJacky Wang             "",
153*f585d8a3SJacky Wang             "  @Module",
154*f585d8a3SJacky Wang             "  abstract static class TestModule {",
155*f585d8a3SJacky Wang             "    @Binds @IntoSet abstract Object bindObject(NotBound notBound);",
156*f585d8a3SJacky Wang             "  }",
157*f585d8a3SJacky Wang             "}");
158*f585d8a3SJacky Wang 
159*f585d8a3SJacky Wang     CompilerTests.daggerCompiler(component)
160*f585d8a3SJacky Wang         .withProcessingOptions(compilerMode.processorOptions())
161*f585d8a3SJacky Wang         .compile(
162*f585d8a3SJacky Wang             subject -> {
163*f585d8a3SJacky Wang               subject.hasErrorCount(1);
164*f585d8a3SJacky Wang               subject.hasErrorContaining("test.C.NotBound cannot be provided")
165*f585d8a3SJacky Wang                   .onSource(component)
166*f585d8a3SJacky Wang                   .onLineContaining("interface C");
167*f585d8a3SJacky Wang             });
168*f585d8a3SJacky Wang   }
169*f585d8a3SJacky Wang 
170*f585d8a3SJacky Wang   @Test
bindsMissingDelegate_mapBinding()171*f585d8a3SJacky Wang   public void bindsMissingDelegate_mapBinding() {
172*f585d8a3SJacky Wang     Source component =
173*f585d8a3SJacky Wang         CompilerTests.javaSource(
174*f585d8a3SJacky Wang             "test.C",
175*f585d8a3SJacky Wang             "package test;",
176*f585d8a3SJacky Wang             "",
177*f585d8a3SJacky Wang             "import dagger.Binds;",
178*f585d8a3SJacky Wang             "import dagger.Component;",
179*f585d8a3SJacky Wang             "import dagger.Module;",
180*f585d8a3SJacky Wang             "import dagger.multibindings.IntoMap;",
181*f585d8a3SJacky Wang             "import dagger.multibindings.StringKey;",
182*f585d8a3SJacky Wang             "import java.util.Map;",
183*f585d8a3SJacky Wang             "",
184*f585d8a3SJacky Wang             "@Component(modules = C.TestModule.class)",
185*f585d8a3SJacky Wang             "interface C {",
186*f585d8a3SJacky Wang             "  Map<String, Object> objects();",
187*f585d8a3SJacky Wang             "",
188*f585d8a3SJacky Wang             "  static class NotBound {}",
189*f585d8a3SJacky Wang             "",
190*f585d8a3SJacky Wang             "  @Module",
191*f585d8a3SJacky Wang             "  abstract static class TestModule {",
192*f585d8a3SJacky Wang             "    @Binds @IntoMap @StringKey(\"key\")",
193*f585d8a3SJacky Wang             "    abstract Object bindObject(NotBound notBound);",
194*f585d8a3SJacky Wang             "  }",
195*f585d8a3SJacky Wang             "}");
196*f585d8a3SJacky Wang 
197*f585d8a3SJacky Wang     CompilerTests.daggerCompiler(component)
198*f585d8a3SJacky Wang         .withProcessingOptions(compilerMode.processorOptions())
199*f585d8a3SJacky Wang         .compile(
200*f585d8a3SJacky Wang             subject -> {
201*f585d8a3SJacky Wang               subject.hasErrorCount(1);
202*f585d8a3SJacky Wang               subject.hasErrorContaining("test.C.NotBound cannot be provided")
203*f585d8a3SJacky Wang                   .onSource(component)
204*f585d8a3SJacky Wang                   .onLineContaining("interface C");
205*f585d8a3SJacky Wang             });
206*f585d8a3SJacky Wang   }
207*f585d8a3SJacky Wang 
208*f585d8a3SJacky Wang   @Test
bindsMissingDelegate_mapBinding_sameKey()209*f585d8a3SJacky Wang   public void bindsMissingDelegate_mapBinding_sameKey() {
210*f585d8a3SJacky Wang     Source component =
211*f585d8a3SJacky Wang         CompilerTests.javaSource(
212*f585d8a3SJacky Wang             "test.C",
213*f585d8a3SJacky Wang             "package test;",
214*f585d8a3SJacky Wang             "",
215*f585d8a3SJacky Wang             "import dagger.Binds;",
216*f585d8a3SJacky Wang             "import dagger.Component;",
217*f585d8a3SJacky Wang             "import dagger.Module;",
218*f585d8a3SJacky Wang             "import dagger.Provides;",
219*f585d8a3SJacky Wang             "import dagger.multibindings.IntoMap;",
220*f585d8a3SJacky Wang             "import dagger.multibindings.StringKey;",
221*f585d8a3SJacky Wang             "import java.util.Map;",
222*f585d8a3SJacky Wang             "",
223*f585d8a3SJacky Wang             "@Component(modules = C.TestModule.class)",
224*f585d8a3SJacky Wang             "interface C {",
225*f585d8a3SJacky Wang             "  Map<String, Object> objects();",
226*f585d8a3SJacky Wang             "",
227*f585d8a3SJacky Wang             "  static class NotBound {}",
228*f585d8a3SJacky Wang             "",
229*f585d8a3SJacky Wang             "  @Module",
230*f585d8a3SJacky Wang             "  abstract static class TestModule {",
231*f585d8a3SJacky Wang             "    @Binds @IntoMap @StringKey(\"key\")",
232*f585d8a3SJacky Wang             "    abstract Object bindObject(NotBound notBound);",
233*f585d8a3SJacky Wang             "",
234*f585d8a3SJacky Wang             "    @Provides @IntoMap @StringKey(\"key\")",
235*f585d8a3SJacky Wang             "    static Object provideObject() { return new Object(); }",
236*f585d8a3SJacky Wang             "  }",
237*f585d8a3SJacky Wang             "}");
238*f585d8a3SJacky Wang 
239*f585d8a3SJacky Wang 
240*f585d8a3SJacky Wang     CompilerTests.daggerCompiler(component)
241*f585d8a3SJacky Wang         .withProcessingOptions(compilerMode.processorOptions())
242*f585d8a3SJacky Wang         .compile(
243*f585d8a3SJacky Wang             subject -> {
244*f585d8a3SJacky Wang               subject.hasErrorCount(1);
245*f585d8a3SJacky Wang               // Some versions of javacs report only the first error for each source line so we
246*f585d8a3SJacky Wang               // allow 1 of the assertions below to fail.
247*f585d8a3SJacky Wang               // TODO(bcorso): Add CompilationResultSubject#hasErrorContainingMatch() to do this
248*f585d8a3SJacky Wang               // more elegantly (see CL/469765892).
249*f585d8a3SJacky Wang               java.util.List<Error> errors = new java.util.ArrayList<>();
250*f585d8a3SJacky Wang               try {
251*f585d8a3SJacky Wang                 subject.hasErrorContaining("test.C.NotBound cannot be provided")
252*f585d8a3SJacky Wang                     .onSource(component)
253*f585d8a3SJacky Wang                     .onLineContaining("interface C");
254*f585d8a3SJacky Wang               } catch (Error e) {
255*f585d8a3SJacky Wang                 errors.add(e);
256*f585d8a3SJacky Wang               }
257*f585d8a3SJacky Wang               try {
258*f585d8a3SJacky Wang                 subject.hasErrorContaining("same map key is bound more than once")
259*f585d8a3SJacky Wang                     .onSource(component)
260*f585d8a3SJacky Wang                     .onLineContaining("interface C");
261*f585d8a3SJacky Wang               } catch (Error e) {
262*f585d8a3SJacky Wang                 errors.add(e);
263*f585d8a3SJacky Wang               }
264*f585d8a3SJacky Wang               com.google.common.truth.Truth.assertThat(errors.size()).isAtMost(1);
265*f585d8a3SJacky Wang             });
266*f585d8a3SJacky Wang   }
267*f585d8a3SJacky Wang }
268