/* * Copyright (C) 2014 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.Compilers.daggerCompiler; import com.google.common.collect.ImmutableList; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class MapMultibindingValidationTest { @Test public void duplicateMapKeys() { JavaFileObject module = JavaFileObjects.forSourceLines( "test.MapModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import dagger.multibindings.StringKey;", "import dagger.multibindings.IntoMap;", "", "@Module", "final class MapModule {", " @Provides @IntoMap @StringKey(\"AKey\") Object provideObjectForAKey() {", " return \"one\";", " }", "", " @Provides @IntoMap @StringKey(\"AKey\") Object provideObjectForAKeyAgain() {", " return \"one again\";", " }", "}"); // If they're all there, report only Map. Compilation compilation = daggerCompiler() .compile( module, component( "Map objects();", "Map> objectProviders();", "Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map"); assertThat(compilation).hadErrorContaining("provideObjectForAKey()"); assertThat(compilation).hadErrorContaining("provideObjectForAKeyAgain()"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler().withOptions("-Adagger.fullBindingGraphValidation=ERROR").compile(module); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map>") .inFile(module) .onLineContaining("class MapModule"); assertThat(compilation).hadErrorContaining("provideObjectForAKey()"); assertThat(compilation).hadErrorContaining("provideObjectForAKeyAgain()"); assertThat(compilation).hadErrorCount(1); // If there's Map and Map>, report only Map. compilation = daggerCompiler() .compile( module, component( "Map objects();", "Map> objectProviders();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map"); assertThat(compilation).hadErrorCount(1); // If there's Map and Map>, report only Map. compilation = daggerCompiler() .compile( module, component( "Map objects();", "Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map"); assertThat(compilation).hadErrorCount(1); // If there's Map> and Map>, report only Map>. compilation = daggerCompiler() .compile( module, component( "Map> objectProviders();", "Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map>"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler().compile(module, component("Map objects();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler() .compile(module, component("Map> objectProviders();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map>"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler() .compile( module, component("Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "The same map key is bound more than once for " + "java.util.Map>"); assertThat(compilation).hadErrorCount(1); } @Test public void inconsistentMapKeyAnnotations() { JavaFileObject module = JavaFileObjects.forSourceLines( "test.MapModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "import dagger.multibindings.StringKey;", "import dagger.multibindings.IntoMap;", "", "@Module", "final class MapModule {", " @Provides @IntoMap @StringKey(\"AKey\") Object provideObjectForAKey() {", " return \"one\";", " }", "", " @Provides @IntoMap @StringKeyTwo(\"BKey\") Object provideObjectForBKey() {", " return \"two\";", " }", "}"); JavaFileObject stringKeyTwoFile = JavaFileObjects.forSourceLines( "test.StringKeyTwo", "package test;", "", "import dagger.MapKey;", "", "@MapKey(unwrapValue = true)", "public @interface StringKeyTwo {", " String value();", "}"); // If they're all there, report only Map. Compilation compilation = daggerCompiler() .compile( module, stringKeyTwoFile, component( "Map objects();", "Map> objectProviders();", "Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map" + " uses more than one @MapKey annotation type"); assertThat(compilation).hadErrorContaining("provideObjectForAKey()"); assertThat(compilation).hadErrorContaining("provideObjectForBKey()"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler() .withOptions("-Adagger.fullBindingGraphValidation=ERROR") .compile(module, stringKeyTwoFile); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map>" + " uses more than one @MapKey annotation type") .inFile(module) .onLineContaining("class MapModule"); assertThat(compilation).hadErrorContaining("provideObjectForAKey()"); assertThat(compilation).hadErrorContaining("provideObjectForBKey()"); assertThat(compilation).hadErrorCount(1); // If there's Map and Map>, report only Map. compilation = daggerCompiler() .compile( module, stringKeyTwoFile, component( "Map objects();", "Map> objectProviders();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map" + " uses more than one @MapKey annotation type"); assertThat(compilation).hadErrorCount(1); // If there's Map and Map>, report only Map. compilation = daggerCompiler() .compile( module, stringKeyTwoFile, component( "Map objects();", "Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map" + " uses more than one @MapKey annotation type"); assertThat(compilation).hadErrorCount(1); // If there's Map> and Map>, report only Map>. compilation = daggerCompiler() .compile( module, stringKeyTwoFile, component( "Map> objectProviders();", "Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map>" + " uses more than one @MapKey annotation type"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler() .compile(module, stringKeyTwoFile, component("Map objects();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map" + " uses more than one @MapKey annotation type"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler() .compile( module, stringKeyTwoFile, component("Map> objectProviders();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map>" + " uses more than one @MapKey annotation type"); assertThat(compilation).hadErrorCount(1); compilation = daggerCompiler() .compile( module, stringKeyTwoFile, component("Producer>> objectProducers();")); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining( "java.util.Map>" + " uses more than one @MapKey annotation type"); assertThat(compilation).hadErrorCount(1); } private static JavaFileObject component(String... entryPoints) { return JavaFileObjects.forSourceLines( "test.TestComponent", ImmutableList.builder() .add( "package test;", "", "import dagger.Component;", "import dagger.producers.Producer;", "import java.util.Map;", "import javax.inject.Provider;", "", "@Component(modules = {MapModule.class})", "interface TestComponent {") .add(entryPoints) .add("}") .build()); } }