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 TransitiveBindsQualifierTest { 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 TransitiveBindsQualifierTest(String transitiveDependencyType)47 public TransitiveBindsQualifierTest(String transitiveDependencyType) { 48 this.transitiveDependencyType = transitiveDependencyType; 49 } 50 51 @Test testQualifierOnBindsMethod()52 public void testQualifierOnBindsMethod() 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.MyQualifier' could not be resolved." 62 + "\n " 63 + "\n Dependency trace:" 64 + "\n => element (INTERFACE): library1.MyModule" 65 + "\n => element (METHOD): bindObject(java.lang.Number)" 66 + "\n => element (PARAMETER): arg0" 67 + "\n => annotation: @MyQualifier" 68 + "\n => type (ERROR annotation type): library2.MyQualifier"); 69 break; 70 case "api": 71 result = setupRunner().build(); 72 assertThat(result.task(":app:assemble").getOutcome()).isEqualTo(SUCCESS); 73 assertThat(result.getOutput()) 74 .contains("BINDINGS: [" 75 + "@library2.MyQualifier java.lang.Object, " 76 + "@library2.MyQualifier java.lang.Number, " 77 + "java.lang.Integer" 78 + "]"); 79 break; 80 } 81 } 82 setupRunner()83 private GradleRunner setupRunner() throws IOException { 84 File projectDir = folder.getRoot(); 85 GradleModule.create(projectDir) 86 .addSettingsFile( 87 "include 'app'", 88 "include 'library1'", 89 "include 'library2'", 90 "include 'spi-plugin'") 91 .addBuildFile( 92 "buildscript {", 93 " ext {", 94 String.format("dagger_version = \"%s\"", System.getProperty("dagger_version")), 95 " }", 96 "}", 97 "", 98 "allprojects {", 99 " repositories {", 100 " mavenCentral()", 101 " mavenLocal()", 102 " }", 103 "}"); 104 105 GradleModule.create(projectDir, "app") 106 .addBuildFile( 107 "plugins {", 108 " id 'java'", 109 " id 'application'", 110 "}", 111 "compileJava {", 112 " options.compilerArgs << '-Adagger.pluginsVisitFullBindingGraphs=ENABLED'", 113 "}", 114 "dependencies {", 115 " implementation project(':library1')", 116 " annotationProcessor project(':spi-plugin')", 117 " implementation \"com.google.dagger:dagger:$dagger_version\"", 118 " annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"", 119 "}") 120 .addSrcFile( 121 "MyComponent.java", 122 "package app;", 123 "", 124 "import dagger.BindsInstance;", 125 "import dagger.Component;", 126 "import library1.MyModule;", 127 "", 128 "@Component(modules = MyModule.class)", 129 "public interface MyComponent {", 130 " @Component.Factory", 131 " interface Factory {", 132 " MyComponent create(@BindsInstance int i);", 133 " }", 134 "}"); 135 136 GradleModule.create(projectDir, "library1") 137 .addBuildFile( 138 "plugins {", 139 " id 'java'", 140 " id 'java-library'", 141 "}", 142 "dependencies {", 143 transitiveDependencyType + " project(':library2')", 144 " implementation \"com.google.dagger:dagger:$dagger_version\"", 145 " annotationProcessor \"com.google.dagger:dagger-compiler:$dagger_version\"", 146 "}") 147 .addSrcFile( 148 "MyModule.java", 149 "package library1;", 150 "", 151 "import dagger.Binds;", 152 "import dagger.Module;", 153 "import dagger.Provides;", 154 "import library2.MyQualifier;", 155 "", 156 "@Module", 157 "public interface MyModule {", 158 " @Binds", 159 " @MyQualifier", 160 " Object bindObject(@MyQualifier Number number);", 161 "", 162 " @Binds", 163 " @MyQualifier", 164 " Number bindNumber(int i);", 165 "}"); 166 167 GradleModule.create(projectDir, "library2") 168 .addBuildFile( 169 "plugins {", 170 " id 'java'", 171 " id 'java-library'", 172 "}", 173 "dependencies {", 174 " implementation 'javax.inject:javax.inject:1'", 175 "}") 176 .addSrcFile( 177 "MyQualifier.java", 178 "package library2;", 179 "", 180 "import javax.inject.Qualifier;", 181 "", 182 "@Qualifier", 183 "public @interface MyQualifier {}"); 184 185 // This plugin is used to print output about bindings that we can assert on in tests. 186 GradleModule.create(projectDir, "spi-plugin") 187 .addBuildFile( 188 "plugins {", 189 " id 'java'", 190 "}", 191 "dependencies {", 192 " implementation \"com.google.dagger:dagger-spi:$dagger_version\"", 193 " implementation 'com.google.auto.service:auto-service-annotations:1.0.1'", 194 " annotationProcessor 'com.google.auto.service:auto-service:1.0.1'", 195 "}") 196 .addSrcFile( 197 "TestBindingGraphPlugin.java", 198 "package spiplugin;", 199 "", 200 "import com.google.auto.service.AutoService;", 201 "import dagger.model.Binding;", 202 "import dagger.model.BindingGraph;", 203 "import dagger.model.BindingGraph.DependencyEdge;", 204 "import dagger.model.DependencyRequest;", 205 "import dagger.spi.BindingGraphPlugin;", 206 "import dagger.spi.DiagnosticReporter;", 207 "import java.util.stream.Collectors;", 208 "", 209 "@AutoService(BindingGraphPlugin.class)", 210 "public class TestBindingGraphPlugin implements BindingGraphPlugin {", 211 " @Override", 212 " public void visitGraph(", 213 " BindingGraph bindingGraph, DiagnosticReporter diagnosticReporter) {", 214 " if (!bindingGraph.isFullBindingGraph() || bindingGraph.isModuleBindingGraph()) {", 215 " return;", 216 " }", 217 " System.out.print(", 218 " \"BINDINGS: \"", 219 " + bindingGraph.bindings().stream()", 220 " .map(Binding::key)", 221 " .collect(Collectors.toList()));", 222 " }", 223 "}"); 224 225 return GradleRunner.create() 226 .withArguments("--stacktrace", "build") 227 .withProjectDir(projectDir); 228 } 229 } 230