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