/* * Copyright (C) 2016 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 dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass; import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod; import androidx.room.compiler.processing.util.Source; import com.google.common.collect.ImmutableList; import dagger.Module; import dagger.producers.ProducerModule; import dagger.testing.compile.CompilerTests; import java.lang.annotation.Annotation; import java.util.Collection; import javax.inject.Inject; import javax.inject.Qualifier; import javax.inject.Singleton; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** Tests {@link dagger.internal.codegen.validation.BindsOptionalOfMethodValidator}. */ @RunWith(Parameterized.class) public class BindsOptionalOfMethodValidationTest { @Parameters(name = "{0}") public static Collection data() { return ImmutableList.copyOf(new Object[][] {{Module.class}, {ProducerModule.class}}); } private final String moduleDeclaration; public BindsOptionalOfMethodValidationTest(Class moduleAnnotation) { moduleDeclaration = "@" + moduleAnnotation.getCanonicalName() + " abstract class %s { %s }"; } @Test public void nonAbstract() { assertThatMethod("@BindsOptionalOf Object concrete() { return null; }") .hasError("must be abstract"); } @Test public void hasParameters() { assertThatMethod("@BindsOptionalOf abstract Object hasParameters(String s1);") .hasError("parameters"); } @Test public void typeParameters() { assertThatMethod("@BindsOptionalOf abstract S generic();").hasError("type parameters"); } @Test public void notInModule() { assertThatMethodInUnannotatedClass("@BindsOptionalOf abstract Object notInModule();") .hasError("within a @Module or @ProducerModule"); } @Test public void throwsException() { assertThatMethod("@BindsOptionalOf abstract Object throwsException() throws RuntimeException;") .hasError("may not throw"); } @Test public void returnsVoid() { assertThatMethod("@BindsOptionalOf abstract void returnsVoid();").hasError("void"); } @Test public void returnsMembersInjector() { assertThatMethod("@BindsOptionalOf abstract MembersInjector returnsMembersInjector();") .hasError("framework"); } @Test public void tooManyQualifiers() { assertThatMethod( "@BindsOptionalOf @Qualifier1 @Qualifier2 abstract String tooManyQualifiers();") .importing(Qualifier1.class, Qualifier2.class) .hasError("more than one @Qualifier"); } @Test public void intoSet() { assertThatMethod("@BindsOptionalOf @IntoSet abstract String intoSet();") .hasError("cannot have multibinding annotations"); } @Test public void elementsIntoSet() { assertThatMethod("@BindsOptionalOf @ElementsIntoSet abstract Set elementsIntoSet();") .hasError("cannot have multibinding annotations"); } @Test public void intoMap() { assertThatMethod("@BindsOptionalOf @IntoMap abstract String intoMap();") .hasError("cannot have multibinding annotations"); } /** * Tests that @BindsOptionalOf @IntoMap actually causes module validation to fail. * * @see bug 118434447 */ @Test public void intoMapWithComponent() { Source module = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.BindsOptionalOf;", "import dagger.Module;", "import dagger.multibindings.IntoMap;", "", "@Module", "interface TestModule {", " @BindsOptionalOf @IntoMap Object object();", "}"); Source component = CompilerTests.javaSource( "test.TestComponent", "package test;", "", "import dagger.Component;", "", "@Component(modules = TestModule.class)", "interface TestComponent {}"); CompilerTests.daggerCompiler(module, component) .compile( subject -> { subject.hasErrorCount(2); subject.hasErrorContaining("test.TestModule has errors") .onSource(component) .onLineContaining("@Component(modules = TestModule.class)"); subject.hasErrorContaining("cannot have multibinding annotations") .onSource(module) .onLineContaining("object()"); }); } /** An injectable value object. */ public static final class Thing { @Inject Thing() {} } @Test public void implicitlyProvidedType() { assertThatMethod("@BindsOptionalOf abstract Thing thing();") .importing(Thing.class) .hasError("return unqualified types that have an @Inject-annotated constructor"); } @Test public void hasScope() { assertThatMethod("@BindsOptionalOf @Singleton abstract String scoped();") .importing(Singleton.class) .hasError("cannot be scoped"); } private DaggerModuleMethodSubject assertThatMethod(String method) { return assertThatModuleMethod(method).withDeclaration(moduleDeclaration); } /** A qualifier. */ @Qualifier public @interface Qualifier1 {} /** A qualifier. */ @Qualifier public @interface Qualifier2 {} }