/* * Copyright (C) 2017 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen; import static com.google.testing.compile.CompilationSubject.assertThat; import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; import static dagger.internal.codegen.Compilers.CLASS_PATH_WITHOUT_GUAVA_OPTION; import static dagger.internal.codegen.Compilers.compilerWithOptions; import com.google.testing.compile.Compilation; import com.google.testing.compile.Compiler; import com.google.testing.compile.JavaFileObjects; import java.util.Collection; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class MapRequestRepresentationTest { @Parameters(name = "{0}") public static Collection parameters() { return CompilerMode.TEST_PARAMETERS; } private final CompilerMode compilerMode; public MapRequestRepresentationTest(CompilerMode compilerMode) { this.compilerMode = compilerMode; } @Test public void mapBindings() { JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("test.MapModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import dagger.multibindings.IntKey;", "import dagger.multibindings.IntoMap;", "import dagger.multibindings.LongKey;", "import dagger.multibindings.Multibinds;", "import java.util.Map;", "", "@Module", "interface MapModule {", " @Multibinds Map stringMap();", " @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }", " @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }", " @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }", " @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }", "}"); JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", "package test;", "", "import dagger.Component;", "import java.util.Map;", "import javax.inject.Provider;", "", "@Component(modules = MapModule.class)", "interface TestComponent {", " Map strings();", " Map> providerStrings();", "", " Map ints();", " Map> providerInts();", " Map longs();", " Map> providerLongs();", "}"); JavaFileObject generatedComponent = compilerMode .javaFileBuilder("test.DaggerTestComponent") .addLines( "package test;", "", GeneratedLines.generatedAnnotations(), "final class DaggerTestComponent implements TestComponent {") .addLinesIn( FAST_INIT_MODE, " private Provider provideIntProvider;", " private Provider provideLong0Provider;", " private Provider provideLong1Provider;", " private Provider provideLong2Provider;", "", " @SuppressWarnings(\"unchecked\")", " private void initialize() {", " this.provideIntProvider = new SwitchingProvider<>(testComponent, 0);", " this.provideLong0Provider = new SwitchingProvider<>(testComponent, 1);", " this.provideLong1Provider = new SwitchingProvider<>(testComponent, 2);", " this.provideLong2Provider = new SwitchingProvider<>(testComponent, 3);", " }") .addLines( " @Override", " public Map strings() {", " return Collections.emptyMap();", " }", "", " @Override", " public Map> providerStrings() {", " return Collections.>emptyMap();", " }", "") .addLinesIn( DEFAULT_MODE, " @Override", " public Map ints() {", " return Collections.", " singletonMap(0, MapModule.provideInt());", " }") .addLinesIn( FAST_INIT_MODE, " @Override", " public Map ints() {", " return Collections.singletonMap(0," + " provideIntProvider.get());", " }") .addLines( " @Override", " public Map> providerInts() {", " return Collections.>singletonMap(") .addLinesIn( DEFAULT_MODE, // " 0, MapModule_ProvideIntFactory.create());") .addLinesIn(FAST_INIT_MODE, " 0, provideIntProvider;") .addLinesIn( DEFAULT_MODE, " }", "", " @Override", " public Map longs() {", " return MapBuilder.newMapBuilder(3)", " .put(0L, MapModule.provideLong0())", " .put(1L, MapModule.provideLong1())", " .put(2L, MapModule.provideLong2())", " .build();", " }") .addLinesIn( FAST_INIT_MODE, " }", "", " @Override", " public Map longs() {", " return MapBuilder.newMapBuilder(3)", " .put(0L, provideLong0Provider.get())", " .put(1L, provideLong1Provider.get())", " .put(2L, provideLong2Provider.get())", " .build();", " }") .addLines( " @Override", " public Map> providerLongs() {", " return MapBuilder.>newMapBuilder(3)") .addLinesIn( DEFAULT_MODE, " .put(0L, MapModule_ProvideLong0Factory.create())", " .put(1L, MapModule_ProvideLong1Factory.create())", " .put(2L, MapModule_ProvideLong2Factory.create())") .addLinesIn( FAST_INIT_MODE, " .put(0L, provideLong0Provider)", " .put(1L, provideLong1Provider)", " .put(2L, provideLong2Provider)") .addLines( // " .build();", " }") .addLinesIn( FAST_INIT_MODE, " private static final class SwitchingProvider implements Provider {", " @SuppressWarnings(\"unchecked\")", " @Override", " public T get() {", " switch (id) {", " case 0: return (T) (Integer) MapModule.provideInt();", " case 1: return (T) (Long) MapModule.provideLong0();", " case 2: return (T) (Long) MapModule.provideLong1();", " case 3: return (T) (Long) MapModule.provideLong2();", " default: throw new AssertionError(id);", " }", " }", " }", "}") .build(); Compilation compilation = daggerCompilerWithoutGuava().compile(mapModuleFile, componentFile); assertThat(compilation).succeeded(); assertThat(compilation) .generatedSourceFile("test.DaggerTestComponent") .containsElementsIn(generatedComponent); } @Test public void inaccessible() { JavaFileObject inaccessible = JavaFileObjects.forSourceLines( "other.Inaccessible", "package other;", "", "class Inaccessible {}"); JavaFileObject usesInaccessible = JavaFileObjects.forSourceLines( "other.UsesInaccessible", "package other;", "", "import java.util.Map;", "import javax.inject.Inject;", "", "public class UsesInaccessible {", " @Inject UsesInaccessible(Map map) {}", "}"); JavaFileObject module = JavaFileObjects.forSourceLines( "other.TestModule", "package other;", "", "import dagger.Module;", "import dagger.multibindings.Multibinds;", "import java.util.Map;", "", "@Module", "public abstract class TestModule {", " @Multibinds abstract Map ints();", "}"); JavaFileObject componentFile = JavaFileObjects.forSourceLines( "test.TestComponent", "package test;", "", "import dagger.Component;", "import java.util.Map;", "import javax.inject.Provider;", "import other.TestModule;", "import other.UsesInaccessible;", "", "@Component(modules = TestModule.class)", "interface TestComponent {", " UsesInaccessible usesInaccessible();", "}"); JavaFileObject generatedComponent = JavaFileObjects.forSourceLines( "test.DaggerTestComponent", "package test;", "", "import other.UsesInaccessible;", "import other.UsesInaccessible_Factory;", "", GeneratedLines.generatedAnnotations(), "final class DaggerTestComponent implements TestComponent {", " @Override", " public UsesInaccessible usesInaccessible() {", " return UsesInaccessible_Factory.newInstance(", " (Map) Collections.emptyMap());", " }", "}"); Compilation compilation = daggerCompilerWithoutGuava().compile(module, inaccessible, usesInaccessible, componentFile); assertThat(compilation).succeeded(); assertThat(compilation) .generatedSourceFile("test.DaggerTestComponent") .containsElementsIn(generatedComponent); } @Test public void subcomponentOmitsInheritedBindings() { JavaFileObject parent = JavaFileObjects.forSourceLines( "test.Parent", "package test;", "", "import dagger.Component;", "", "@Component(modules = ParentModule.class)", "interface Parent {", " Child child();", "}"); JavaFileObject parentModule = JavaFileObjects.forSourceLines( "test.ParentModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import dagger.multibindings.IntoMap;", "import dagger.multibindings.StringKey;", "", "@Module", "class ParentModule {", " @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {", " return \"parent value\";", " }", "}"); JavaFileObject child = JavaFileObjects.forSourceLines( "test.Child", "package test;", "", "import dagger.Subcomponent;", "import java.util.Map;", "import java.util.Map;", "", "@Subcomponent", "interface Child {", " Map objectMap();", "}"); JavaFileObject generatedComponent = JavaFileObjects.forSourceLines( "test.DaggerParent", "package test;", "", GeneratedLines.generatedAnnotations(), "final class DaggerParent implements Parent {", " private final ParentModule parentModule;", "", " private static final class ChildImpl implements Child {", " @Override", " public Map objectMap() {", " return Collections.singletonMap(", " \"parent key\",", " ParentModule_ParentKeyObjectFactory.parentKeyObject(parent.parentModule));", " }", " }", "}"); Compilation compilation = daggerCompilerWithoutGuava().compile(parent, parentModule, child); assertThat(compilation).succeeded(); assertThat(compilation) .generatedSourceFile("test.DaggerParent") .containsElementsIn(generatedComponent); } private Compiler daggerCompilerWithoutGuava() { return compilerWithOptions(compilerMode.javacopts()) .withClasspath(CLASS_PATH_WITHOUT_GUAVA_OPTION); } }