• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 buildtests;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
21 
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import org.gradle.testkit.runner.BuildResult;
27 import org.gradle.testkit.runner.GradleRunner;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.junit.rules.TemporaryFolder;
31 import org.junit.runner.RunWith;
32 import org.junit.runners.Parameterized;
33 import org.junit.runners.Parameterized.Parameters;
34 
35 // This is a regression test for https://github.com/google/dagger/issues/3136
36 @RunWith(Parameterized.class)
37 public class TransitiveSubcomponentModulesTest {
38   @Parameters(name = "{0}")
parameters()39   public static Collection<Object[]> parameters() {
40     return Arrays.asList(new Object[][] {{"implementation"}, {"api"}});
41   }
42 
43   @Rule public TemporaryFolder folder = new TemporaryFolder();
44 
45   private final String transitiveDependencyType;
46 
TransitiveSubcomponentModulesTest(String transitiveDependencyType)47   public TransitiveSubcomponentModulesTest(String transitiveDependencyType) {
48     this.transitiveDependencyType = transitiveDependencyType;
49   }
50 
51   @Test
testSubcomponentAnnotationWithTransitiveModule()52   public void testSubcomponentAnnotationWithTransitiveModule() throws IOException {
53     GradleRunner runner =
54         setupRunner(
55             GradleFile.create(
56                 "MySubcomponent.java",
57                 "package library1;",
58                 "",
59                 "import dagger.Subcomponent;",
60                 "import library2.TransitiveModule;",
61                 "",
62                 "@Subcomponent(modules = TransitiveModule.class)",
63                 "public abstract class MySubcomponent {",
64                 "  public abstract int getInt();",
65                 "}"));
66     BuildResult result;
67     switch (transitiveDependencyType) {
68       case "implementation":
69         result = runner.buildAndFail();
70         assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
71         String expectedErrorMsg =
72             "error: ComponentProcessingStep was unable to process 'app.MyComponent' because"
73                 + " 'library2.TransitiveModule' could not be resolved."
74                 + "\n  "
75                 + "\n  Dependency trace:"
76                 + "\n      => element (CLASS): library1.MySubcomponent"
77                 + "\n      => annotation type: dagger.Subcomponent"
78                 + "\n      => annotation: @dagger.Subcomponent(modules={library2.TransitiveModule})"
79                 + "\n      => annotation value (TYPE_ARRAY): modules={library2.TransitiveModule}"
80                 + "\n      => annotation value (TYPE): modules=library2.TransitiveModule";
81         assertThat(result.getOutput()).contains(expectedErrorMsg);
82         break;
83       case "api":
84         result = runner.build();
85         assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
86         break;
87     }
88   }
89 
90   @Test
testSubcomponentAnnotationWithModuleIncludesTransitiveModuleDependencies()91   public void testSubcomponentAnnotationWithModuleIncludesTransitiveModuleDependencies()
92       throws IOException {
93     GradleRunner runner =
94         setupRunner(
95             GradleFile.create(
96                 "MySubcomponent.java",
97                 "package library1;",
98                 "",
99                 "import dagger.Subcomponent;",
100                 "",
101                 "@Subcomponent(modules = IncludesTransitiveModule.class)",
102                 "public abstract class MySubcomponent {",
103                 "  public abstract int getInt();",
104                 "}"));
105     BuildResult result;
106     switch (transitiveDependencyType) {
107       case "implementation":
108         result = runner.buildAndFail();
109         assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
110         String expectedErrorMsg =
111             "error: ComponentProcessingStep was unable to process 'app.MyComponent' because"
112                 + " 'library2.TransitiveModule' could not be resolved."
113                 + "\n  "
114                 + "\n  Dependency trace:"
115                 + "\n      => element (INTERFACE): library1.IncludesTransitiveModule"
116                 + "\n      => annotation type: dagger.Module"
117                 + "\n      => annotation: "
118                 + "@dagger.Module(includes={library2.TransitiveModule}, subcomponents={})"
119                 + "\n      => annotation value (TYPE_ARRAY): includes={library2.TransitiveModule}"
120                 + "\n      => annotation value (TYPE): includes=library2.TransitiveModule";
121         assertThat(result.getOutput()).contains(expectedErrorMsg);
122         break;
123       case "api":
124         result = runner.build();
125         assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
126         break;
127     }
128   }
129 
setupRunner(GradleFile subcomponent)130   private GradleRunner setupRunner(GradleFile subcomponent) throws IOException {
131     File projectDir = folder.getRoot();
132     GradleModule.create(projectDir)
133         .addSettingsFile("include 'app'", "include 'library1'", "include 'library2'")
134         .addBuildFile(
135             "buildscript {",
136             "  ext {",
137             String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
138             "  }",
139             "}",
140             "",
141             "allprojects {",
142             "  repositories {",
143             "    mavenCentral()",
144             "    mavenLocal()",
145             "  }",
146             "}");
147 
148     GradleModule.create(projectDir, "app")
149         .addBuildFile(
150             "plugins {",
151             "  id 'java'",
152             "  id 'application'",
153             "}",
154             "tasks.withType(JavaCompile) {",
155             "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
156             "}",
157             "dependencies {",
158             "  implementation project(':library1')",
159             "  implementation \"com.google.dagger:dagger:$dagger_version\"",
160             "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
161             "}")
162         .addSrcFile(
163             "MyComponent.java",
164             "package app;",
165             "",
166             "import dagger.Component;",
167             "import library1.MySubcomponent;",
168             "",
169             "@Component",
170             "public interface MyComponent {",
171             "  MySubcomponent mySubcomponent();",
172             "}");
173 
174     GradleModule.create(projectDir, "library1")
175         .addBuildFile(
176             "plugins {",
177             "  id 'java'",
178             "  id 'java-library'",
179             "}",
180             "dependencies {",
181             transitiveDependencyType + " project(':library2')",
182             "  implementation \"com.google.dagger:dagger:$dagger_version\"",
183             "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
184             "}")
185         .addSrcFile(
186             "IncludesTransitiveModule.java",
187             "package library1;",
188             "",
189             "import dagger.Module;",
190             "import dagger.Provides;",
191             "import library2.TransitiveModule;",
192             "",
193             "@Module(includes = TransitiveModule.class)",
194             "public interface IncludesTransitiveModule {}")
195         .addSrcFile(subcomponent);
196 
197     GradleModule.create(projectDir, "library2")
198         .addBuildFile(
199             "plugins {",
200             "  id 'java'",
201             "  id 'java-library'",
202             "}",
203             "dependencies {",
204             "  implementation \"com.google.dagger:dagger:$dagger_version\"",
205             "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
206             "}")
207         .addSrcFile(
208             "TransitiveModule.java",
209             "package library2;",
210             "",
211             "import dagger.Module;",
212             "import dagger.Provides;",
213             "",
214             "@Module",
215             "public interface TransitiveModule {",
216             "  @Provides",
217             "  static int provideInt() {",
218             "    return 0;",
219             "  }",
220             "}");
221 
222     return GradleRunner.create().withArguments("--stacktrace", "build").withProjectDir(projectDir);
223   }
224 }
225