1 /* 2 * Copyright (C) 2020 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 dagger.internal.codegen; 18 19 import androidx.room.compiler.processing.util.Source; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableMap; 22 import dagger.testing.compile.CompilerTests; 23 import org.junit.Test; 24 import org.junit.runner.RunWith; 25 import org.junit.runners.Parameterized; 26 import org.junit.runners.Parameterized.Parameters; 27 28 /** 29 * Tests to make sure that delegate bindings where the impl depends on a binding in a subcomponent 30 * properly fail. These are regression tests for b/147020838. 31 */ 32 @RunWith(Parameterized.class) 33 public class BindsDependsOnSubcomponentValidationTest { 34 @Parameters(name = "{0}") parameters()35 public static ImmutableList<Object[]> parameters() { 36 return CompilerMode.TEST_PARAMETERS; 37 } 38 39 private final CompilerMode compilerMode; 40 BindsDependsOnSubcomponentValidationTest(CompilerMode compilerMode)41 public BindsDependsOnSubcomponentValidationTest(CompilerMode compilerMode) { 42 this.compilerMode = compilerMode; 43 } 44 45 @Test testBinds()46 public void testBinds() { 47 Source parentComponent = 48 CompilerTests.javaSource( 49 "test.ParentComponent", 50 "package test;", 51 "", 52 "import dagger.Component;", 53 "", 54 "@Component(modules = ParentModule.class)", 55 "interface ParentComponent {", 56 " ChildComponent getChild();", 57 "}"); 58 Source parentModule = 59 CompilerTests.javaSource( 60 "test.ParentModule", 61 "package test;", 62 "", 63 "import dagger.Binds;", 64 "import dagger.Module;", 65 "", 66 "@Module", 67 "interface ParentModule {", 68 " @Binds Foo bindFoo(FooImpl impl);", 69 "}"); 70 Source childComponent = 71 CompilerTests.javaSource( 72 "test.ChildComponent", 73 "package test;", 74 "", 75 "import dagger.Subcomponent;", 76 "", 77 "@Subcomponent(modules = ChildModule.class)", 78 "interface ChildComponent {", 79 " Foo getFoo();", 80 "}"); 81 Source childModule = 82 CompilerTests.javaSource( 83 "test.ChildModule", 84 "package test;", 85 "", 86 "import dagger.Module;", 87 "import dagger.Provides;", 88 "", 89 "@Module", 90 "interface ChildModule {", 91 " @Provides static Long providLong() {", 92 " return 0L;", 93 " }", 94 "}"); 95 Source iface = 96 CompilerTests.javaSource( 97 "test.Foo", 98 "package test;", 99 "", 100 "interface Foo {}"); 101 Source impl = 102 CompilerTests.javaSource( 103 "test.FooImpl", 104 "package test;", 105 "", 106 "import javax.inject.Inject;", 107 "", 108 "class FooImpl implements Foo {", 109 " @Inject FooImpl(Long l) {}", 110 "}"); 111 CompilerTests.daggerCompiler( 112 parentComponent, parentModule, childComponent, childModule, iface, impl) 113 .withProcessingOptions(compilerMode.processorOptions()) 114 .compile( 115 subject -> { 116 subject.hasErrorCount(1); 117 subject.hasErrorContaining("Long cannot be provided without an @Inject constructor") 118 .onSource(parentComponent) 119 .onLineContaining("interface ParentComponent"); 120 }); 121 } 122 123 @Test testSetBindings()124 public void testSetBindings() { 125 Source parentComponent = 126 CompilerTests.javaSource( 127 "test.ParentComponent", 128 "package test;", 129 "", 130 "import dagger.Component;", 131 "", 132 "@Component(modules = ParentModule.class)", 133 "interface ParentComponent {", 134 " ChildComponent getChild();", 135 "}"); 136 Source parentModule = 137 CompilerTests.javaSource( 138 "test.ParentModule", 139 "package test;", 140 "", 141 "import dagger.Binds;", 142 "import dagger.Module;", 143 "import dagger.multibindings.IntoSet;", 144 "", 145 "@Module", 146 "interface ParentModule {", 147 " @Binds @IntoSet Foo bindFoo(FooImpl impl);", 148 "}"); 149 Source childComponent = 150 CompilerTests.javaSource( 151 "test.ChildComponent", 152 "package test;", 153 "", 154 "import dagger.Subcomponent;", 155 "import java.util.Set;", 156 "", 157 "@Subcomponent(modules = ChildModule.class)", 158 "interface ChildComponent {", 159 " Set<Foo> getFooSet();", 160 "}"); 161 Source childModule = 162 CompilerTests.javaSource( 163 "test.ChildModule", 164 "package test;", 165 "", 166 "import dagger.Module;", 167 "import dagger.Provides;", 168 "", 169 "@Module", 170 "interface ChildModule {", 171 " @Provides static Long providLong() {", 172 " return 0L;", 173 " }", 174 "}"); 175 Source iface = 176 CompilerTests.javaSource( 177 "test.Foo", 178 "package test;", 179 "", 180 "interface Foo {}"); 181 Source impl = 182 CompilerTests.javaSource( 183 "test.FooImpl", 184 "package test;", 185 "", 186 "import javax.inject.Inject;", 187 "", 188 "class FooImpl implements Foo {", 189 " @Inject FooImpl(Long l) {}", 190 "}"); 191 CompilerTests.daggerCompiler( 192 parentComponent, parentModule, childComponent, childModule, iface, impl) 193 .withProcessingOptions(compilerMode.processorOptions()) 194 .compile( 195 subject -> { 196 subject.hasErrorCount(1); 197 subject.hasErrorContaining("Long cannot be provided without an @Inject constructor") 198 .onSource(parentComponent) 199 .onLineContaining("interface ParentComponent"); 200 }); 201 } 202 203 @Test testSetValueBindings()204 public void testSetValueBindings() { 205 Source parentComponent = 206 CompilerTests.javaSource( 207 "test.ParentComponent", 208 "package test;", 209 "", 210 "import dagger.Component;", 211 "", 212 "@Component(modules = ParentModule.class)", 213 "interface ParentComponent {", 214 " ChildComponent getChild();", 215 "}"); 216 Source parentModule = 217 CompilerTests.javaSource( 218 "test.ParentModule", 219 "package test;", 220 "", 221 "import dagger.Module;", 222 "import dagger.Provides;", 223 "import dagger.multibindings.ElementsIntoSet;", 224 "import java.util.Collections;", 225 "import java.util.Set;", 226 "", 227 "@Module", 228 "interface ParentModule {", 229 " @Provides @ElementsIntoSet", 230 " static Set<Foo> provideFoo(FooImpl impl) {", 231 " return Collections.singleton(impl);", 232 " }", 233 "}"); 234 Source childComponent = 235 CompilerTests.javaSource( 236 "test.ChildComponent", 237 "package test;", 238 "", 239 "import dagger.Subcomponent;", 240 "import java.util.Set;", 241 "", 242 "@Subcomponent(modules = ChildModule.class)", 243 "interface ChildComponent {", 244 " Set<Foo> getFooSet();", 245 "}"); 246 Source childModule = 247 CompilerTests.javaSource( 248 "test.ChildModule", 249 "package test;", 250 "", 251 "import dagger.Module;", 252 "import dagger.Provides;", 253 "", 254 "@Module", 255 "interface ChildModule {", 256 " @Provides static Long providLong() {", 257 " return 0L;", 258 " }", 259 "}"); 260 Source iface = 261 CompilerTests.javaSource( 262 "test.Foo", 263 "package test;", 264 "", 265 "interface Foo {}"); 266 Source impl = 267 CompilerTests.javaSource( 268 "test.FooImpl", 269 "package test;", 270 "", 271 "import javax.inject.Inject;", 272 "", 273 "class FooImpl implements Foo {", 274 " @Inject FooImpl(Long l) {}", 275 "}"); 276 CompilerTests.daggerCompiler( 277 parentComponent, parentModule, childComponent, childModule, iface, impl) 278 .withProcessingOptions(compilerMode.processorOptions()) 279 .compile( 280 subject -> { 281 subject.hasErrorCount(1); 282 subject.hasErrorContaining("Long cannot be provided without an @Inject constructor") 283 .onSource(parentComponent) 284 .onLineContaining("interface ParentComponent"); 285 }); 286 } 287 288 @Test testMapBindings()289 public void testMapBindings() { 290 Source parentComponent = 291 CompilerTests.javaSource( 292 "test.ParentComponent", 293 "package test;", 294 "", 295 "import dagger.Component;", 296 "", 297 "@Component(modules = ParentModule.class)", 298 "interface ParentComponent {", 299 " ChildComponent getChild();", 300 "}"); 301 Source parentModule = 302 CompilerTests.javaSource( 303 "test.ParentModule", 304 "package test;", 305 "", 306 "import dagger.Binds;", 307 "import dagger.Module;", 308 "import dagger.multibindings.IntoMap;", 309 "import dagger.multibindings.StringKey;", 310 "", 311 "@Module", 312 "interface ParentModule {", 313 " @Binds @IntoMap @StringKey(\"foo\") Foo bindFoo(FooImpl impl);", 314 "}"); 315 Source childComponent = 316 CompilerTests.javaSource( 317 "test.ChildComponent", 318 "package test;", 319 "", 320 "import dagger.Subcomponent;", 321 "import java.util.Map;", 322 "", 323 "@Subcomponent(modules = ChildModule.class)", 324 "interface ChildComponent {", 325 " Map<String, Foo> getFooSet();", 326 "}"); 327 Source childModule = 328 CompilerTests.javaSource( 329 "test.ChildModule", 330 "package test;", 331 "", 332 "import dagger.Module;", 333 "import dagger.Provides;", 334 "", 335 "@Module", 336 "interface ChildModule {", 337 " @Provides static Long providLong() {", 338 " return 0L;", 339 " }", 340 "}"); 341 Source iface = 342 CompilerTests.javaSource( 343 "test.Foo", 344 "package test;", 345 "", 346 "interface Foo {}"); 347 Source impl = 348 CompilerTests.javaSource( 349 "test.FooImpl", 350 "package test;", 351 "", 352 "import javax.inject.Inject;", 353 "", 354 "class FooImpl implements Foo {", 355 " @Inject FooImpl(Long l) {}", 356 "}"); 357 CompilerTests.daggerCompiler( 358 parentComponent, parentModule, childComponent, childModule, iface, impl) 359 .withProcessingOptions( 360 ImmutableMap.<String, String>builder() 361 .putAll(compilerMode.processorOptions()) 362 // TODO(erichang): make this flag the default and remove this 363 .put("dagger.strictMultibindingValidation", "enabled") 364 .build()) 365 .compile( 366 subject -> { 367 subject.hasErrorCount(1); 368 subject.hasErrorContaining("Long cannot be provided without an @Inject constructor") 369 .onSource(parentComponent) 370 .onLineContaining("interface ParentComponent"); 371 }); 372 } 373 } 374