• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 org.gradle.testkit.runner.BuildResult;
25 import org.gradle.testkit.runner.GradleRunner;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.junit.rules.TemporaryFolder;
29 import org.junit.runner.RunWith;
30 import org.junit.runners.JUnit4;
31 
32 // This is a regression test for https://github.com/google/dagger/issues/3136
33 @RunWith(JUnit4.class)
34 public class TransitiveScopeTest {
35   @Rule public TemporaryFolder folder = new TemporaryFolder();
36 
37   @Test
testTransitiveScope_WithImplementation()38   public void testTransitiveScope_WithImplementation() throws IOException {
39     BuildResult result = setupRunnerWith("implementation").buildAndFail();
40     assertThat(result.getOutput()).contains("Task :app:compileJava FAILED");
41     assertThat(result.getOutput())
42         .contains(
43             "ComponentProcessingStep was unable to process 'app.MyComponent' because "
44                 + "'library2.MyScope' could not be resolved."
45                 + "\n  "
46                 + "\n  Dependency trace:"
47                 // Note: this fails on the subcomponent rather than Foo because the subcomponent is
48                 // validated before any of its dependencies.
49                 + "\n      => element (INTERFACE): library1.MySubcomponent"
50                 + "\n      => annotation: @MyScope"
51                 + "\n      => type (ERROR annotation type): library2.MyScope");
52   }
53 
54   @Test
testTransitiveScope_WithApi()55   public void testTransitiveScope_WithApi() throws IOException {
56     BuildResult result = setupRunnerWith("api").build();
57     assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS);
58     assertThat(result.getOutput()).contains("@Inject library1.Foo(): SCOPED");
59   }
60 
setupRunnerWith(String dependencyType)61   private GradleRunner setupRunnerWith(String dependencyType) throws IOException {
62     File projectDir = folder.getRoot();
63     GradleModule.create(projectDir)
64         .addSettingsFile(
65             "include 'app'",
66             "include 'library1'",
67             "include 'library2'",
68             "include 'spi-plugin'")
69         .addBuildFile(
70             "buildscript {",
71             "  ext {",
72             String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")),
73             "  }",
74             "}",
75             "",
76             "allprojects {",
77             "  repositories {",
78             "    mavenCentral()",
79             "    mavenLocal()",
80             "  }",
81             "}");
82 
83     GradleModule.create(projectDir, "app")
84         .addBuildFile(
85             "plugins {",
86             "  id 'java'",
87             "  id 'application'",
88             "}",
89             "dependencies {",
90             "  implementation project(':library1')",
91             "  annotationProcessor project(':spi-plugin')",
92             "  implementation \"com.google.dagger:dagger:$dagger_version\"",
93             "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
94             "}")
95         .addSrcFile(
96             "MyComponent.java",
97             "package app;",
98             "",
99             "import dagger.Component;",
100             "import library1.MySubcomponent;",
101             "",
102             "@Component",
103             "public interface MyComponent {",
104             "  MySubcomponent subcomponent();",
105             "}");
106 
107     GradleModule.create(projectDir, "library1")
108         .addBuildFile(
109             "plugins {",
110             "  id 'java'",
111             "  id 'java-library'",
112             "}",
113             "dependencies {",
114             dependencyType + " project(':library2')",
115             "  implementation \"com.google.dagger:dagger:$dagger_version\"",
116             "  annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"",
117             "}")
118         .addSrcFile(
119             "Foo.java",
120             "package library1;",
121             "",
122             "import javax.inject.Inject;",
123             "import library2.MyScope;",
124             "",
125             "@MyScope",
126             "public class Foo {",
127             "  @Inject Foo() {}",
128             "}")
129         // Note: In order to repro the issue we place MyScope on a subcomponent so that it can be a
130         // transitive dependency of the component. If MyScope was placed on directly on the
131         // component, it would need to be a direct dependency of the component.
132         .addSrcFile(
133             "MySubcomponent.java",
134             "package library1;",
135             "",
136             "import dagger.Subcomponent;",
137             "import library2.MyScope;",
138             "",
139             "@MyScope",
140             "@Subcomponent",
141             "public interface MySubcomponent {",
142             "  Foo foo();",
143             "}");
144 
145     GradleModule.create(projectDir, "library2")
146         .addBuildFile(
147             "plugins {",
148             "  id 'java'",
149             "  id 'java-library'",
150             "}",
151             "dependencies {",
152             "  implementation 'javax.inject:javax.inject:1'",
153             "}")
154         .addSrcFile(
155             "MyScope.java",
156             "package library2;",
157             "",
158             "import javax.inject.Scope;",
159             "",
160             "@Scope",
161             "public @interface MyScope {}");
162 
163     // This plugin is used to print output about bindings that we can assert on in tests.
164     GradleModule.create(projectDir, "spi-plugin")
165         .addBuildFile(
166             "plugins {",
167             "  id 'java'",
168             "}",
169             "dependencies {",
170             "  implementation \"com.google.dagger:dagger-spi:$dagger_version\"",
171             "  implementation 'com.google.auto.service:auto-service-annotations:1.0.1'",
172             "  annotationProcessor 'com.google.auto.service:auto-service:1.0.1'",
173             "}")
174         .addSrcFile(
175             "TestBindingGraphPlugin.java",
176             "package spiplugin;",
177             "",
178             "import com.google.auto.service.AutoService;",
179             "import dagger.model.BindingGraph;",
180             "import dagger.spi.BindingGraphPlugin;",
181             "import dagger.spi.DiagnosticReporter;",
182             "",
183             "@AutoService(BindingGraphPlugin.class)",
184             "public class TestBindingGraphPlugin implements BindingGraphPlugin {",
185             "  @Override",
186             "  public void visitGraph(BindingGraph bindingGraph, DiagnosticReporter"
187                 + " diagnosticReporter) {",
188             "    bindingGraph.bindings().stream()",
189             "        .filter(binding -> binding.scope().isPresent())",
190             "        .forEach(binding -> System.out.println(binding + \": SCOPED\"));",
191             "    bindingGraph.bindings().stream()",
192             "        .filter(binding -> !binding.scope().isPresent())",
193             "        .forEach(binding -> System.out.println(binding + \": UNSCOPED\"));",
194             "  }",
195             "}");
196 
197     return GradleRunner.create()
198         .withArguments("--stacktrace", "build")
199         .withProjectDir(projectDir);
200   }
201 }
202