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