1 /* 2 * Copyright (C) 2014 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 dagger.testing.compile.CompilerTests; 22 import org.junit.Test; 23 import org.junit.runner.RunWith; 24 import org.junit.runners.Parameterized; 25 import org.junit.runners.Parameterized.Parameters; 26 27 /** 28 * Tests that errors are reported correctly when a {@code @Binds} method's delegate (the type of its 29 * parameter) is missing. 30 */ 31 @RunWith(Parameterized.class) 32 public class BindsMissingDelegateValidationTest { 33 @Parameters(name = "{0}") parameters()34 public static ImmutableList<Object[]> parameters() { 35 return CompilerMode.TEST_PARAMETERS; 36 } 37 38 private final CompilerMode compilerMode; 39 BindsMissingDelegateValidationTest(CompilerMode compilerMode)40 public BindsMissingDelegateValidationTest(CompilerMode compilerMode) { 41 this.compilerMode = compilerMode; 42 } 43 44 @Test bindsMissingDelegate()45 public void bindsMissingDelegate() { 46 Source component = 47 CompilerTests.javaSource( 48 "test.C", 49 "package test;", 50 "", 51 "import dagger.Binds;", 52 "import dagger.Component;", 53 "import dagger.Module;", 54 "", 55 "@Component(modules = C.TestModule.class)", 56 "interface C {", 57 " Object object();", 58 "", 59 " static class NotBound {}", 60 "", 61 " @Module", 62 " abstract static class TestModule {", 63 " @Binds abstract Object bindObject(NotBound notBound);", 64 " }", 65 "}"); 66 67 CompilerTests.daggerCompiler(component) 68 .withProcessingOptions(compilerMode.processorOptions()) 69 .compile( 70 subject -> { 71 subject.hasErrorCount(1); 72 subject.hasErrorContaining("test.C.NotBound cannot be provided") 73 .onSource(component) 74 .onLineContaining("interface C"); 75 }); 76 } 77 78 @Test bindsMissingDelegate_duplicateBinding()79 public void bindsMissingDelegate_duplicateBinding() { 80 Source component = 81 CompilerTests.javaSource( 82 "test.C", 83 "package test;", 84 "", 85 "import dagger.Binds;", 86 "import dagger.Component;", 87 "import dagger.Module;", 88 "import dagger.Provides;", 89 "", 90 "@Component(modules = C.TestModule.class)", 91 "interface C {", 92 " Object object();", 93 "", 94 " static class NotBound {}", 95 "", 96 " @Module", 97 " abstract static class TestModule {", 98 " @Binds abstract Object bindObject(NotBound notBound);", 99 " @Provides static Object provideObject() { return new Object(); }", 100 " }", 101 "}"); 102 103 CompilerTests.daggerCompiler(component) 104 .withProcessingOptions(compilerMode.processorOptions()) 105 .compile( 106 subject -> { 107 subject.hasErrorCount(1); 108 // Some versions of javacs report only the first error for each source line so we 109 // allow 1 of the assertions below to fail. 110 // TODO(bcorso): Add CompilationResultSubject#hasErrorContainingMatch() to do this 111 // more elegantly (see CL/469765892). 112 java.util.List<Error> errors = new java.util.ArrayList<>(); 113 try { 114 subject.hasErrorContaining("test.C.NotBound cannot be provided") 115 .onSource(component) 116 .onLineContaining("interface C"); 117 } catch (Error e) { 118 errors.add(e); 119 } 120 try { 121 subject.hasErrorContaining("Object is bound multiple times:") 122 .onSource(component) 123 .onLineContaining("interface C"); 124 subject.hasErrorContaining( 125 "@Binds Object test.C.TestModule.bindObject(test.C.NotBound)"); 126 subject.hasErrorContaining("@Provides Object test.C.TestModule.provideObject()"); 127 } catch (Error e) { 128 errors.add(e); 129 } 130 com.google.common.truth.Truth.assertThat(errors.size()).isAtMost(1); 131 }); 132 } 133 134 @Test bindsMissingDelegate_setBinding()135 public void bindsMissingDelegate_setBinding() { 136 Source component = 137 CompilerTests.javaSource( 138 "test.C", 139 "package test;", 140 "", 141 "import dagger.Binds;", 142 "import dagger.Component;", 143 "import dagger.Module;", 144 "import dagger.multibindings.IntoSet;", 145 "import java.util.Set;", 146 "", 147 "@Component(modules = C.TestModule.class)", 148 "interface C {", 149 " Set<Object> objects();", 150 "", 151 " static class NotBound {}", 152 "", 153 " @Module", 154 " abstract static class TestModule {", 155 " @Binds @IntoSet abstract Object bindObject(NotBound notBound);", 156 " }", 157 "}"); 158 159 CompilerTests.daggerCompiler(component) 160 .withProcessingOptions(compilerMode.processorOptions()) 161 .compile( 162 subject -> { 163 subject.hasErrorCount(1); 164 subject.hasErrorContaining("test.C.NotBound cannot be provided") 165 .onSource(component) 166 .onLineContaining("interface C"); 167 }); 168 } 169 170 @Test bindsMissingDelegate_mapBinding()171 public void bindsMissingDelegate_mapBinding() { 172 Source component = 173 CompilerTests.javaSource( 174 "test.C", 175 "package test;", 176 "", 177 "import dagger.Binds;", 178 "import dagger.Component;", 179 "import dagger.Module;", 180 "import dagger.multibindings.IntoMap;", 181 "import dagger.multibindings.StringKey;", 182 "import java.util.Map;", 183 "", 184 "@Component(modules = C.TestModule.class)", 185 "interface C {", 186 " Map<String, Object> objects();", 187 "", 188 " static class NotBound {}", 189 "", 190 " @Module", 191 " abstract static class TestModule {", 192 " @Binds @IntoMap @StringKey(\"key\")", 193 " abstract Object bindObject(NotBound notBound);", 194 " }", 195 "}"); 196 197 CompilerTests.daggerCompiler(component) 198 .withProcessingOptions(compilerMode.processorOptions()) 199 .compile( 200 subject -> { 201 subject.hasErrorCount(1); 202 subject.hasErrorContaining("test.C.NotBound cannot be provided") 203 .onSource(component) 204 .onLineContaining("interface C"); 205 }); 206 } 207 208 @Test bindsMissingDelegate_mapBinding_sameKey()209 public void bindsMissingDelegate_mapBinding_sameKey() { 210 Source component = 211 CompilerTests.javaSource( 212 "test.C", 213 "package test;", 214 "", 215 "import dagger.Binds;", 216 "import dagger.Component;", 217 "import dagger.Module;", 218 "import dagger.Provides;", 219 "import dagger.multibindings.IntoMap;", 220 "import dagger.multibindings.StringKey;", 221 "import java.util.Map;", 222 "", 223 "@Component(modules = C.TestModule.class)", 224 "interface C {", 225 " Map<String, Object> objects();", 226 "", 227 " static class NotBound {}", 228 "", 229 " @Module", 230 " abstract static class TestModule {", 231 " @Binds @IntoMap @StringKey(\"key\")", 232 " abstract Object bindObject(NotBound notBound);", 233 "", 234 " @Provides @IntoMap @StringKey(\"key\")", 235 " static Object provideObject() { return new Object(); }", 236 " }", 237 "}"); 238 239 240 CompilerTests.daggerCompiler(component) 241 .withProcessingOptions(compilerMode.processorOptions()) 242 .compile( 243 subject -> { 244 subject.hasErrorCount(1); 245 // Some versions of javacs report only the first error for each source line so we 246 // allow 1 of the assertions below to fail. 247 // TODO(bcorso): Add CompilationResultSubject#hasErrorContainingMatch() to do this 248 // more elegantly (see CL/469765892). 249 java.util.List<Error> errors = new java.util.ArrayList<>(); 250 try { 251 subject.hasErrorContaining("test.C.NotBound cannot be provided") 252 .onSource(component) 253 .onLineContaining("interface C"); 254 } catch (Error e) { 255 errors.add(e); 256 } 257 try { 258 subject.hasErrorContaining("same map key is bound more than once") 259 .onSource(component) 260 .onLineContaining("interface C"); 261 } catch (Error e) { 262 errors.add(e); 263 } 264 com.google.common.truth.Truth.assertThat(errors.size()).isAtMost(1); 265 }); 266 } 267 } 268