• 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 TransitiveProvidesScopeTest {
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 
TransitiveProvidesScopeTest(String transitiveDependencyType)47   public TransitiveProvidesScopeTest(String transitiveDependencyType) {
48     this.transitiveDependencyType = transitiveDependencyType;
49   }
50 
51   @Test
testScopeOnProvidesMethod()52   public void testScopeOnProvidesMethod() throws IOException {
53     BuildResult result;
54     switch (transitiveDependencyType) {
55       case "implementation":
56         result = setupRunner().buildAndFail();
57         assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
58         assertThat(result.getOutput())
59             .contains(
60                 "ComponentProcessingStep was unable to process 'app.MyComponent' because "
61                     + "'library2.MyScope' could not be resolved."
62                     + "\n  "
63                     + "\n  Dependency trace:"
64                     + "\n      => element (INTERFACE): library1.MyModule"
65                     + "\n      => element (METHOD): provideString()"
66                     + "\n      => annotation: @MyScope"
67                     + "\n      => type (ERROR annotation type): library2.MyScope");
68         break;
69       case "api":
70         result = setupRunner().build();
71         assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
72         assertThat(result.getOutput())
73             .contains(
74                 "@Provides @library2.MyScope String library1.MyModule.provideString(): SCOPED");
75         break;
76     }
77   }
78 
setupRunner()79   private GradleRunner setupRunner() throws IOException {
80     File projectDir = folder.getRoot();
81     GradleModule.create(projectDir)
82         .addSettingsFile(
83             "include 'app'",
84             "include 'library1'",
85             "include 'library2'",
86             "include 'spi-plugin'")
87         .addBuildFile(
88             "buildscript {",
89             "  ext {",
90             String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
91             "  }",
92             "}",
93             "",
94             "allprojects {",
95             "  repositories {",
96             "    mavenCentral()",
97             "    mavenLocal()",
98             "  }",
99             "}");
100 
101     GradleModule.create(projectDir, "app")
102         .addBuildFile(
103             "plugins {",
104             "  id 'java'",
105             "  id 'application'",
106             "}",
107             "tasks.withType(JavaCompile) {",
108             "    options.compilerArgs += '-Adagger.experimentalDaggerErrorMessages=ENABLED'",
109             "}",
110             "dependencies {",
111             "  implementation project(':library1')",
112             "  annotationProcessor project(':spi-plugin')",
113             "  implementation \"com.google.dagger:dagger:$dagger_version\"",
114             "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
115             "}")
116         .addSrcFile(
117             "MyComponent.java",
118             "package app;",
119             "",
120             "import dagger.Component;",
121             "import library1.MySubcomponent;",
122             "",
123             "@Component",
124             "public interface MyComponent {",
125             "  MySubcomponent subcomponent();",
126             "}");
127 
128     GradleModule.create(projectDir, "library1")
129         .addBuildFile(
130             "plugins {",
131             "  id 'java'",
132             "  id 'java-library'",
133             "}",
134             "dependencies {",
135             transitiveDependencyType + " project(':library2')",
136             "  implementation \"com.google.dagger:dagger:$dagger_version\"",
137             "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
138             "}")
139         // Note: In order to repro the issue we place MyScope on a subcomponent so that it can be a
140         // transitive dependency of the component. If MyScope was placed on directly on the
141         // component, it would need to be a direct dependency of the component.
142         .addSrcFile(
143             "MySubcomponent.java",
144             "package library1;",
145             "",
146             "import dagger.Subcomponent;",
147             "import library2.MyScope;",
148             "",
149             "@MyScope",
150             "@Subcomponent(modules = MyModule.class)",
151             "public interface MySubcomponent {",
152             "  String string();",
153             "}")
154         .addSrcFile(
155             "MyModule.java",
156             "package library1;",
157             "",
158             "import dagger.Module;",
159             "import dagger.Provides;",
160             "import library2.MyScope;",
161             "",
162             "@Module",
163             "public interface MyModule {",
164             "  @MyScope",
165             "  @Provides",
166             "  static String provideString() {",
167             "    return \"\";",
168             "  }",
169             "}");
170 
171     GradleModule.create(projectDir, "library2")
172         .addBuildFile(
173             "plugins {",
174             "  id 'java'",
175             "  id 'java-library'",
176             "}",
177             "dependencies {",
178             "  implementation 'javax.inject:javax.inject:1'",
179             "}")
180         .addSrcFile(
181             "MyScope.java",
182             "package library2;",
183             "",
184             "import javax.inject.Scope;",
185             "",
186             "@Scope",
187             "public @interface MyScope {}");
188 
189     // This plugin is used to print output about bindings that we can assert on in tests.
190     GradleModule.create(projectDir, "spi-plugin")
191         .addBuildFile(
192             "plugins {",
193             "  id 'java'",
194             "}",
195             "dependencies {",
196             "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
197             "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
198             "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
199             "}")
200         .addSrcFile(
201             "TestBindingGraphPlugin.java",
202             "package spiplugin;",
203             "",
204             "import com.google.auto.service.AutoService;",
205             "import dagger.model.BindingGraph;",
206             "import dagger.model.BindingGraph.DependencyEdge;",
207             "import dagger.model.DependencyRequest;",
208             "import dagger.spi.BindingGraphPlugin;",
209             "import dagger.spi.DiagnosticReporter;",
210             "",
211             "@AutoService(BindingGraphPlugin.class)",
212             "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
213             "  @Override",
214             "  public void visitGraph(",
215             "      BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {",
216             "    bindingGraph.bindings().stream()",
217             "        .filter(binding -> binding.scope().isPresent())",
218             "        .forEach(binding -> System.out.println(binding + \": SCOPED\"));",
219             "    bindingGraph.bindings().stream()",
220             "        .filter(binding -> !binding.scope().isPresent())",
221             "        .forEach(binding -> System.out.println(binding + \": UNSCOPED\"));",
222             "  }",
223             "}");
224 
225     return GradleRunner.create()
226         .withArguments("--stacktrace", "build")
227         .withProjectDir(projectDir);
228   }
229 }
230