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 dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass; 20 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod; 21 22 import androidx.room.compiler.processing.util.Source; 23 import com.google.common.collect.ImmutableList; 24 import dagger.Module; 25 import dagger.producers.ProducerModule; 26 import dagger.testing.compile.CompilerTests; 27 import java.lang.annotation.Annotation; 28 import java.util.Collection; 29 import javax.inject.Inject; 30 import javax.inject.Qualifier; 31 import javax.inject.Singleton; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 import org.junit.runners.Parameterized; 35 import org.junit.runners.Parameterized.Parameters; 36 37 /** Tests {@link dagger.internal.codegen.validation.BindsOptionalOfMethodValidator}. */ 38 @RunWith(Parameterized.class) 39 public class BindsOptionalOfMethodValidationTest { 40 @Parameters(name = "{0}") data()41 public static Collection<Object[]> data() { 42 return ImmutableList.copyOf(new Object[][] {{Module.class}, {ProducerModule.class}}); 43 } 44 45 private final String moduleDeclaration; 46 BindsOptionalOfMethodValidationTest(Class<? extends Annotation> moduleAnnotation)47 public BindsOptionalOfMethodValidationTest(Class<? extends Annotation> moduleAnnotation) { 48 moduleDeclaration = "@" + moduleAnnotation.getCanonicalName() + " abstract class %s { %s }"; 49 } 50 51 @Test nonAbstract()52 public void nonAbstract() { 53 assertThatMethod("@BindsOptionalOf Object concrete() { return null; }") 54 .hasError("must be abstract"); 55 } 56 57 @Test hasParameters()58 public void hasParameters() { 59 assertThatMethod("@BindsOptionalOf abstract Object hasParameters(String s1);") 60 .hasError("parameters"); 61 } 62 63 @Test typeParameters()64 public void typeParameters() { 65 assertThatMethod("@BindsOptionalOf abstract <S> S generic();").hasError("type parameters"); 66 } 67 68 @Test notInModule()69 public void notInModule() { 70 assertThatMethodInUnannotatedClass("@BindsOptionalOf abstract Object notInModule();") 71 .hasError("within a @Module or @ProducerModule"); 72 } 73 74 @Test throwsException()75 public void throwsException() { 76 assertThatMethod("@BindsOptionalOf abstract Object throwsException() throws RuntimeException;") 77 .hasError("may not throw"); 78 } 79 80 @Test returnsVoid()81 public void returnsVoid() { 82 assertThatMethod("@BindsOptionalOf abstract void returnsVoid();").hasError("void"); 83 } 84 85 @Test returnsMembersInjector()86 public void returnsMembersInjector() { 87 assertThatMethod("@BindsOptionalOf abstract MembersInjector<Object> returnsMembersInjector();") 88 .hasError("framework"); 89 } 90 91 @Test tooManyQualifiers()92 public void tooManyQualifiers() { 93 assertThatMethod( 94 "@BindsOptionalOf @Qualifier1 @Qualifier2 abstract String tooManyQualifiers();") 95 .importing(Qualifier1.class, Qualifier2.class) 96 .hasError("more than one @Qualifier"); 97 } 98 99 @Test intoSet()100 public void intoSet() { 101 assertThatMethod("@BindsOptionalOf @IntoSet abstract String intoSet();") 102 .hasError("cannot have multibinding annotations"); 103 } 104 105 @Test elementsIntoSet()106 public void elementsIntoSet() { 107 assertThatMethod("@BindsOptionalOf @ElementsIntoSet abstract Set<String> elementsIntoSet();") 108 .hasError("cannot have multibinding annotations"); 109 } 110 111 @Test intoMap()112 public void intoMap() { 113 assertThatMethod("@BindsOptionalOf @IntoMap abstract String intoMap();") 114 .hasError("cannot have multibinding annotations"); 115 } 116 117 /** 118 * Tests that @BindsOptionalOf @IntoMap actually causes module validation to fail. 119 * 120 * @see <a href="http://b/118434447">bug 118434447</a> 121 */ 122 @Test intoMapWithComponent()123 public void intoMapWithComponent() { 124 Source module = 125 CompilerTests.javaSource( 126 "test.TestModule", 127 "package test;", 128 "", 129 "import dagger.BindsOptionalOf;", 130 "import dagger.Module;", 131 "import dagger.multibindings.IntoMap;", 132 "", 133 "@Module", 134 "interface TestModule {", 135 " @BindsOptionalOf @IntoMap Object object();", 136 "}"); 137 Source component = 138 CompilerTests.javaSource( 139 "test.TestComponent", 140 "package test;", 141 "", 142 "import dagger.Component;", 143 "", 144 "@Component(modules = TestModule.class)", 145 "interface TestComponent {}"); 146 147 CompilerTests.daggerCompiler(module, component) 148 .compile( 149 subject -> { 150 subject.hasErrorCount(2); 151 subject.hasErrorContaining("test.TestModule has errors") 152 .onSource(component) 153 .onLineContaining("@Component(modules = TestModule.class)"); 154 subject.hasErrorContaining("cannot have multibinding annotations") 155 .onSource(module) 156 .onLineContaining("object()"); 157 }); 158 } 159 160 /** An injectable value object. */ 161 public static final class Thing { 162 @Inject Thing()163 Thing() {} 164 } 165 166 @Test implicitlyProvidedType()167 public void implicitlyProvidedType() { 168 assertThatMethod("@BindsOptionalOf abstract Thing thing();") 169 .importing(Thing.class) 170 .hasError("return unqualified types that have an @Inject-annotated constructor"); 171 } 172 173 @Test hasScope()174 public void hasScope() { 175 assertThatMethod("@BindsOptionalOf @Singleton abstract String scoped();") 176 .importing(Singleton.class) 177 .hasError("cannot be scoped"); 178 } 179 assertThatMethod(String method)180 private DaggerModuleMethodSubject assertThatMethod(String method) { 181 return assertThatModuleMethod(method).withDeclaration(moduleDeclaration); 182 } 183 184 /** A qualifier. */ 185 @Qualifier 186 public @interface Qualifier1 {} 187 188 /** A qualifier. */ 189 @Qualifier 190 public @interface Qualifier2 {} 191 } 192