1 /* 2 * Copyright (C) 2016 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 dagger.testing.compile.CompilerTests; 21 import org.junit.Test; 22 import org.junit.runner.RunWith; 23 import org.junit.runners.JUnit4; 24 25 @RunWith(JUnit4.class) 26 public class MultibindingTest { 27 28 @Test providesWithTwoMultibindingAnnotations_failsToCompile()29 public void providesWithTwoMultibindingAnnotations_failsToCompile() { 30 Source module = 31 CompilerTests.javaSource( 32 "test.MultibindingModule", 33 "package test;", 34 "", 35 "import dagger.Module;", 36 "import dagger.Provides;", 37 "import dagger.multibindings.IntoSet;", 38 "import dagger.multibindings.IntoMap;", 39 "", 40 "@Module", 41 "class MultibindingModule {", 42 " @Provides", 43 " @IntoSet", 44 " @IntoMap", 45 " Integer provideInt() { ", 46 " return 1;", 47 " }", 48 "}"); 49 50 CompilerTests.daggerCompiler(module) 51 .compile( 52 subject -> { 53 subject.hasErrorCount(3); 54 subject.hasErrorContaining( 55 "@Provides methods cannot have more than one multibinding annotation") 56 .onSource(module) 57 .onLine(11); 58 subject.hasErrorContaining( 59 "@Provides methods cannot have more than one multibinding annotation") 60 .onSource(module) 61 .onLine(12); 62 subject.hasErrorContaining("@Provides methods of type map must declare a map key") 63 .onSource(module) 64 .onLine(13); 65 }); 66 } 67 68 @Test appliedOnInvalidMethods_failsToCompile()69 public void appliedOnInvalidMethods_failsToCompile() { 70 Source someType = 71 CompilerTests.javaSource( 72 "test.SomeType", 73 "package test;", 74 "", 75 "import java.util.Set;", 76 "import java.util.Map;", 77 "import dagger.Component;", 78 "import dagger.multibindings.IntoSet;", 79 "import dagger.multibindings.ElementsIntoSet;", 80 "import dagger.multibindings.IntoMap;", 81 "", 82 "interface SomeType {", 83 " @IntoSet Set<Integer> ints();", 84 " @ElementsIntoSet Set<Double> doubles();", 85 " @IntoMap Map<Integer, Double> map();", 86 "}"); 87 88 CompilerTests.daggerCompiler(someType) 89 .compile( 90 subject -> { 91 subject.hasErrorCount(3); 92 String error = 93 "Multibinding annotations may only be on @Provides, @Produces, or @Binds methods"; 94 subject.hasErrorContaining(error).onSource(someType).onLineContaining("ints();"); 95 subject.hasErrorContaining(error).onSource(someType).onLineContaining("doubles();"); 96 subject.hasErrorContaining(error).onSource(someType).onLineContaining("map();"); 97 }); 98 } 99 100 @Test concreteBindingForMultibindingAlias()101 public void concreteBindingForMultibindingAlias() { 102 Source module = 103 CompilerTests.javaSource( 104 "test.TestModule", 105 "package test;", 106 "", 107 "import dagger.Module;", 108 "import dagger.Provides;", 109 "import java.util.Collections;", 110 "import java.util.Map;", 111 "import javax.inject.Provider;", 112 "", 113 "@Module", 114 "class TestModule {", 115 " @Provides", 116 " Map<String, Provider<String>> mapOfStringToProviderOfString() {", 117 " return Collections.emptyMap();", 118 " }", 119 "}"); 120 Source component = 121 CompilerTests.javaSource( 122 "test.TestComponent", 123 "package test;", 124 "", 125 "import dagger.Component;", 126 "import java.util.Map;", 127 "", 128 "@Component(modules = TestModule.class)", 129 "interface TestComponent {", 130 " Map<String, String> mapOfStringToString();", 131 "}"); 132 CompilerTests.daggerCompiler(module, component) 133 .compile( 134 subject -> { 135 subject.hasErrorCount(1); 136 subject.hasErrorContaining( 137 "Map<String,String> cannot be provided without an @Provides-annotated method") 138 .onSource(component) 139 .onLineContaining("interface TestComponent"); 140 }); 141 } 142 143 @Test produceConcreteSet_andRequestSetOfProduced()144 public void produceConcreteSet_andRequestSetOfProduced() { 145 Source module = 146 CompilerTests.javaSource( 147 "test.TestModule", 148 "package test;", 149 "", 150 "import dagger.producers.ProducerModule;", 151 "import dagger.producers.Produces;", 152 "import java.util.Collections;", 153 "import java.util.Set;", 154 "", 155 "@ProducerModule", 156 "class TestModule {", 157 " @Produces", 158 " Set<String> setOfString() {", 159 " return Collections.emptySet();", 160 " }", 161 "}"); 162 Source component = 163 CompilerTests.javaSource( 164 "test.TestComponent", 165 "package test;", 166 "", 167 "import com.google.common.util.concurrent.ListenableFuture;", 168 "import dagger.BindsInstance;", 169 "import dagger.producers.Produced;", 170 "import dagger.producers.Production;", 171 "import dagger.producers.ProductionComponent;", 172 "import java.util.concurrent.Executor;", 173 "import java.util.Set;", 174 "", 175 "@ProductionComponent(modules = TestModule.class)", 176 "interface TestComponent {", 177 " ListenableFuture<Set<Produced<String>>> setOfProduced();", 178 "", 179 " @ProductionComponent.Builder", 180 " interface Builder {", 181 " @BindsInstance Builder executor(@Production Executor executor);", 182 " TestComponent build();", 183 " }", 184 "}"); 185 CompilerTests.daggerCompiler(module, component) 186 .compile( 187 subject -> { 188 subject.hasErrorCount(1); 189 subject.hasErrorContaining( 190 "Set<Produced<String>> cannot be provided without an @Provides- or " 191 + "@Produces-annotated method") 192 .onSource(component) 193 .onLineContaining("interface TestComponent"); 194 }); 195 } 196 197 @Test provideExplicitSetInParent_AndMultibindingContributionInChild()198 public void provideExplicitSetInParent_AndMultibindingContributionInChild() { 199 Source parent = 200 CompilerTests.javaSource( 201 "test.Parent", 202 "package test;", 203 "", 204 "import dagger.Component;", 205 "import java.util.Set;", 206 "", 207 "@Component(modules = ParentModule.class)", 208 "interface Parent {", 209 " Set<String> set();", 210 " Child child();", 211 "}"); 212 Source parentModule = 213 CompilerTests.javaSource( 214 "test.ParentModule", 215 "package test;", 216 "", 217 "import dagger.Module;", 218 "import dagger.Provides;", 219 "import java.util.HashSet;", 220 "import java.util.Set;", 221 "", 222 "@Module", 223 "class ParentModule {", 224 " @Provides", 225 " Set<String> set() {", 226 " return new HashSet();", 227 " }", 228 "}"); 229 230 Source child = 231 CompilerTests.javaSource( 232 "test.Child", 233 "package test;", 234 "", 235 "import dagger.Subcomponent;", 236 "import java.util.Set;", 237 "", 238 "@Subcomponent(modules = ChildModule.class)", 239 "interface Child {", 240 " Set<String> set();", 241 "}"); 242 Source childModule = 243 CompilerTests.javaSource( 244 "test.ChildModule", 245 "package test;", 246 "", 247 "import dagger.Module;", 248 "import dagger.multibindings.IntoSet;", 249 "import dagger.Provides;", 250 "", 251 "@Module", 252 "class ChildModule {", 253 " @Provides", 254 " @IntoSet", 255 " String setContribution() {", 256 " return new String();", 257 " }", 258 "}"); 259 260 CompilerTests.daggerCompiler(parent, parentModule, child, childModule) 261 .compile( 262 subject -> { 263 subject.hasErrorCount(1); 264 subject.hasErrorContaining("incompatible bindings or declarations") 265 .onSource(parent) 266 .onLineContaining("interface Parent"); 267 }); 268 } 269 } 270