1 /* 2 * Copyright (C) 2017 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.CompilerMode.DEFAULT_MODE; 21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; 22 import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION; 23 import static dagger.internal.codegen.Compilers.compilerWithOptions; 24 25 import com.google.testing.compile.Compilation; 26 import com.google.testing.compile.Compiler; 27 import com.google.testing.compile.JavaFileObjects; 28 import java.util.Collection; 29 import javax.tools.JavaFileObject; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.Parameterized; 33 import org.junit.runners.Parameterized.Parameters; 34 35 @RunWith(Parameterized.class) 36 public class MapRequestRepresentationTest { 37 @Parameters(name = "{0}") parameters()38 public static Collection<Object[]> parameters() { 39 return CompilerMode.TEST_PARAMETERS; 40 } 41 42 private final CompilerMode compilerMode; 43 MapRequestRepresentationTest(CompilerMode compilerMode)44 public MapRequestRepresentationTest(CompilerMode compilerMode) { 45 this.compilerMode = compilerMode; 46 } 47 48 @Test mapBindings()49 public void mapBindings() { 50 JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("test.MapModule", 51 "package test;", 52 "", 53 "import dagger.Module;", 54 "import dagger.Provides;", 55 "import dagger.multibindings.IntKey;", 56 "import dagger.multibindings.IntoMap;", 57 "import dagger.multibindings.LongKey;", 58 "import dagger.multibindings.Multibinds;", 59 "import java.util.Map;", 60 "", 61 "@Module", 62 "interface MapModule {", 63 " @Multibinds Map<String, String> stringMap();", 64 " @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }", 65 " @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }", 66 " @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }", 67 " @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }", 68 "}"); 69 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 70 "package test;", 71 "", 72 "import dagger.Component;", 73 "import java.util.Map;", 74 "import javax.inject.Provider;", 75 "", 76 "@Component(modules = MapModule.class)", 77 "interface TestComponent {", 78 " Map<String, String> strings();", 79 " Map<String, Provider<String>> providerStrings();", 80 "", 81 " Map<Integer, Integer> ints();", 82 " Map<Integer, Provider<Integer>> providerInts();", 83 " Map<Long, Long> longs();", 84 " Map<Long, Provider<Long>> providerLongs();", 85 "}"); 86 JavaFileObject generatedComponent = 87 compilerMode 88 .javaFileBuilder("test.DaggerTestComponent") 89 .addLines( 90 "package test;", 91 "", 92 GeneratedLines.generatedAnnotations(), 93 "final class DaggerTestComponent implements TestComponent {") 94 .addLinesIn( 95 FAST_INIT_MODE, 96 " private Provider<Integer> provideIntProvider;", 97 " private Provider<Long> provideLong0Provider;", 98 " private Provider<Long> provideLong1Provider;", 99 " private Provider<Long> provideLong2Provider;", 100 "", 101 " @SuppressWarnings(\"unchecked\")", 102 " private void initialize() {", 103 " this.provideIntProvider = new SwitchingProvider<>(testComponent, 0);", 104 " this.provideLong0Provider = new SwitchingProvider<>(testComponent, 1);", 105 " this.provideLong1Provider = new SwitchingProvider<>(testComponent, 2);", 106 " this.provideLong2Provider = new SwitchingProvider<>(testComponent, 3);", 107 " }") 108 .addLines( 109 " @Override", 110 " public Map<String, String> strings() {", 111 " return Collections.<String, String>emptyMap();", 112 " }", 113 "", 114 " @Override", 115 " public Map<String, Provider<String>> providerStrings() {", 116 " return Collections.<String, Provider<String>>emptyMap();", 117 " }", 118 "") 119 .addLinesIn( 120 DEFAULT_MODE, 121 " @Override", 122 " public Map<Integer, Integer> ints() {", 123 " return Collections.<Integer, Integer>", 124 " singletonMap(0, MapModule.provideInt());", 125 " }") 126 .addLinesIn( 127 FAST_INIT_MODE, 128 " @Override", 129 " public Map<Integer, Integer> ints() {", 130 " return Collections.<Integer, Integer>singletonMap(0," 131 + " provideIntProvider.get());", 132 " }") 133 .addLines( 134 " @Override", 135 " public Map<Integer, Provider<Integer>> providerInts() {", 136 " return Collections.<Integer, Provider<Integer>>singletonMap(") 137 .addLinesIn( 138 DEFAULT_MODE, // 139 " 0, MapModule_ProvideIntFactory.create());") 140 .addLinesIn(FAST_INIT_MODE, " 0, provideIntProvider;") 141 .addLinesIn( 142 DEFAULT_MODE, 143 " }", 144 "", 145 " @Override", 146 " public Map<Long, Long> longs() {", 147 " return MapBuilder.<Long, Long>newMapBuilder(3)", 148 " .put(0L, MapModule.provideLong0())", 149 " .put(1L, MapModule.provideLong1())", 150 " .put(2L, MapModule.provideLong2())", 151 " .build();", 152 " }") 153 .addLinesIn( 154 FAST_INIT_MODE, 155 " }", 156 "", 157 " @Override", 158 " public Map<Long, Long> longs() {", 159 " return MapBuilder.<Long, Long>newMapBuilder(3)", 160 " .put(0L, provideLong0Provider.get())", 161 " .put(1L, provideLong1Provider.get())", 162 " .put(2L, provideLong2Provider.get())", 163 " .build();", 164 " }") 165 .addLines( 166 " @Override", 167 " public Map<Long, Provider<Long>> providerLongs() {", 168 " return MapBuilder.<Long, Provider<Long>>newMapBuilder(3)") 169 .addLinesIn( 170 DEFAULT_MODE, 171 " .put(0L, MapModule_ProvideLong0Factory.create())", 172 " .put(1L, MapModule_ProvideLong1Factory.create())", 173 " .put(2L, MapModule_ProvideLong2Factory.create())") 174 .addLinesIn( 175 FAST_INIT_MODE, 176 " .put(0L, provideLong0Provider)", 177 " .put(1L, provideLong1Provider)", 178 " .put(2L, provideLong2Provider)") 179 .addLines( // 180 " .build();", " }") 181 .addLinesIn( 182 FAST_INIT_MODE, 183 " private static final class SwitchingProvider<T> implements Provider<T> {", 184 " @SuppressWarnings(\"unchecked\")", 185 " @Override", 186 " public T get() {", 187 " switch (id) {", 188 " case 0: return (T) (Integer) MapModule.provideInt();", 189 " case 1: return (T) (Long) MapModule.provideLong0();", 190 " case 2: return (T) (Long) MapModule.provideLong1();", 191 " case 3: return (T) (Long) MapModule.provideLong2();", 192 " default: throw new AssertionError(id);", 193 " }", 194 " }", 195 " }", 196 "}") 197 .build(); 198 Compilation compilation = daggerCompilerWithoutGuava().compile(mapModuleFile, componentFile); 199 assertThat(compilation).succeeded(); 200 assertThat(compilation) 201 .generatedSourceFile("test.DaggerTestComponent") 202 .containsElementsIn(generatedComponent); 203 } 204 205 @Test inaccessible()206 public void inaccessible() { 207 JavaFileObject inaccessible = 208 JavaFileObjects.forSourceLines( 209 "other.Inaccessible", 210 "package other;", 211 "", 212 "class Inaccessible {}"); 213 JavaFileObject usesInaccessible = 214 JavaFileObjects.forSourceLines( 215 "other.UsesInaccessible", 216 "package other;", 217 "", 218 "import java.util.Map;", 219 "import javax.inject.Inject;", 220 "", 221 "public class UsesInaccessible {", 222 " @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}", 223 "}"); 224 225 JavaFileObject module = 226 JavaFileObjects.forSourceLines( 227 "other.TestModule", 228 "package other;", 229 "", 230 "import dagger.Module;", 231 "import dagger.multibindings.Multibinds;", 232 "import java.util.Map;", 233 "", 234 "@Module", 235 "public abstract class TestModule {", 236 " @Multibinds abstract Map<Integer, Inaccessible> ints();", 237 "}"); 238 JavaFileObject componentFile = 239 JavaFileObjects.forSourceLines( 240 "test.TestComponent", 241 "package test;", 242 "", 243 "import dagger.Component;", 244 "import java.util.Map;", 245 "import javax.inject.Provider;", 246 "import other.TestModule;", 247 "import other.UsesInaccessible;", 248 "", 249 "@Component(modules = TestModule.class)", 250 "interface TestComponent {", 251 " UsesInaccessible usesInaccessible();", 252 "}"); 253 JavaFileObject generatedComponent = 254 JavaFileObjects.forSourceLines( 255 "test.DaggerTestComponent", 256 "package test;", 257 "", 258 "import other.UsesInaccessible;", 259 "import other.UsesInaccessible_Factory;", 260 "", 261 GeneratedLines.generatedAnnotations(), 262 "final class DaggerTestComponent implements TestComponent {", 263 " @Override", 264 " public UsesInaccessible usesInaccessible() {", 265 " return UsesInaccessible_Factory.newInstance(", 266 " (Map) Collections.emptyMap());", 267 " }", 268 "}"); 269 Compilation compilation = 270 daggerCompilerWithoutGuava().compile(module, inaccessible, usesInaccessible, componentFile); 271 assertThat(compilation).succeeded(); 272 assertThat(compilation) 273 .generatedSourceFile("test.DaggerTestComponent") 274 .containsElementsIn(generatedComponent); 275 } 276 277 @Test subcomponentOmitsInheritedBindings()278 public void subcomponentOmitsInheritedBindings() { 279 JavaFileObject parent = 280 JavaFileObjects.forSourceLines( 281 "test.Parent", 282 "package test;", 283 "", 284 "import dagger.Component;", 285 "", 286 "@Component(modules = ParentModule.class)", 287 "interface Parent {", 288 " Child child();", 289 "}"); 290 JavaFileObject parentModule = 291 JavaFileObjects.forSourceLines( 292 "test.ParentModule", 293 "package test;", 294 "", 295 "import dagger.Module;", 296 "import dagger.Provides;", 297 "import dagger.multibindings.IntoMap;", 298 "import dagger.multibindings.StringKey;", 299 "", 300 "@Module", 301 "class ParentModule {", 302 " @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {", 303 " return \"parent value\";", 304 " }", 305 "}"); 306 JavaFileObject child = 307 JavaFileObjects.forSourceLines( 308 "test.Child", 309 "package test;", 310 "", 311 "import dagger.Subcomponent;", 312 "import java.util.Map;", 313 "import java.util.Map;", 314 "", 315 "@Subcomponent", 316 "interface Child {", 317 " Map<String, Object> objectMap();", 318 "}"); 319 JavaFileObject generatedComponent = 320 JavaFileObjects.forSourceLines( 321 "test.DaggerParent", 322 "package test;", 323 "", 324 GeneratedLines.generatedAnnotations(), 325 "final class DaggerParent implements Parent {", 326 " private final ParentModule parentModule;", 327 "", 328 " private static final class ChildImpl implements Child {", 329 " @Override", 330 " public Map<String, Object> objectMap() {", 331 " return Collections.<String, Object>singletonMap(", 332 " \"parent key\",", 333 " ParentModule_ParentKeyObjectFactory.parentKeyObject(parent.parentModule));", 334 " }", 335 " }", 336 "}"); 337 338 Compilation compilation = daggerCompilerWithoutGuava().compile(parent, parentModule, child); 339 assertThat(compilation).succeeded(); 340 assertThat(compilation) 341 .generatedSourceFile("test.DaggerParent") 342 .containsElementsIn(generatedComponent); 343 } 344 daggerCompilerWithoutGuava()345 private Compiler daggerCompilerWithoutGuava() { 346 return compilerWithOptions(compilerMode.javacopts()) 347 .withClasspath(CLASS_PATH_WITHOUT_GUAVA_OPTION); 348 } 349 } 350