1 /* 2 * Copyright (C) 2020 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.hilt.processor.internal.aggregateddeps; 18 19 import static com.google.testing.compile.CompilationSubject.assertThat; 20 21 import com.google.common.base.Joiner; 22 import com.google.testing.compile.Compilation; 23 import com.google.testing.compile.JavaFileObjects; 24 import dagger.hilt.processor.internal.GeneratedImport; 25 import dagger.testing.compile.CompilerTests; 26 import javax.tools.JavaFileObject; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.junit.runners.JUnit4; 30 31 /** Tests for errors generated by {@link AggregatedDepsProcessor} */ 32 @RunWith(JUnit4.class) 33 public class AggregatedDepsProcessorErrorsTest { 34 private static final Joiner LINES = Joiner.on("\n"); 35 36 @Test reportMultipleAnnotationTypeKindErrors()37 public void reportMultipleAnnotationTypeKindErrors() { 38 JavaFileObject source = 39 JavaFileObjects.forSourceString( 40 "foo.bar.AnnotationsOnWrongTypeKind", 41 LINES.join( 42 "package foo.bar;", 43 "", 44 "import dagger.hilt.EntryPoint;", 45 "import dagger.hilt.InstallIn;", 46 "import dagger.Module;", 47 "import dagger.hilt.components.SingletonComponent;", 48 "import dagger.hilt.internal.ComponentEntryPoint;", 49 "import dagger.hilt.internal.GeneratedEntryPoint;", 50 "", 51 "@InstallIn(SingletonComponent.class)", 52 "@Module", 53 "enum FooModule { VALUE }", 54 "", 55 "@InstallIn(SingletonComponent.class)", 56 "@EntryPoint", 57 "final class BarEntryPoint {}", 58 "", 59 "@InstallIn(SingletonComponent.class)", 60 "@ComponentEntryPoint", 61 "final class BazComponentEntryPoint {}", 62 "", 63 "@EntryPoint", 64 "interface QuxEntryPoint {}", 65 "", 66 "@EntryPoint", 67 "@Module", 68 "interface DontMix{}", 69 "")); 70 71 Compilation compilation = 72 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source); 73 74 assertThat(compilation).failed(); 75 assertThat(compilation) 76 .hadErrorContaining("Only classes and interfaces can be annotated with @Module") 77 .inFile(source) 78 .onLine(12); 79 assertThat(compilation) 80 .hadErrorContaining("Only interfaces can be annotated with @EntryPoint") 81 .inFile(source) 82 .onLine(16); 83 assertThat(compilation) 84 .hadErrorContaining("Only interfaces can be annotated with @ComponentEntryPoint") 85 .inFile(source) 86 .onLine(20); 87 assertThat(compilation) 88 .hadErrorContaining( 89 "@EntryPoint foo.bar.QuxEntryPoint must also be annotated with @InstallIn") 90 .inFile(source) 91 .onLine(23); 92 assertThat(compilation) 93 .hadErrorContaining("@Module and @EntryPoint cannot be used on the same interface") 94 .inFile(source) 95 .onLine(27); 96 } 97 98 @Test testInvalidComponentInInstallInAnnotation()99 public void testInvalidComponentInInstallInAnnotation() { 100 JavaFileObject module = JavaFileObjects.forSourceLines( 101 "test.FooModule", 102 "package test;", 103 "", 104 "import dagger.Module;", 105 "import dagger.hilt.InstallIn;", 106 "import dagger.hilt.android.qualifiers.ApplicationContext;", 107 "", 108 "@InstallIn(ApplicationContext.class)", // Error: Not a Hilt component 109 "@Module", 110 "final class FooModule {}"); 111 112 Compilation compilation = 113 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(module); 114 115 assertThat(compilation).failed(); 116 assertThat(compilation) 117 .hadErrorContaining( 118 "@InstallIn, can only be used with @DefineComponent-annotated classes, but found: " 119 + "[dagger.hilt.android.qualifiers.ApplicationContext]") 120 .inFile(module) 121 .onLine(9); 122 } 123 124 @Test testMissingInstallInAnnotation()125 public void testMissingInstallInAnnotation() { 126 JavaFileObject source = JavaFileObjects.forSourceString( 127 "foo.bar.AnnotationsOnWrongTypeKind", 128 LINES.join( 129 "package foo.bar;", 130 "", 131 "import dagger.Module;", 132 "", 133 "@Module", // Error: Doesn't have InstallIn annotation 134 "final class FooModule {}")); 135 136 Compilation compilation = 137 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source); 138 139 assertThat(compilation).failed(); 140 assertThat(compilation) 141 .hadErrorContaining("foo.bar.FooModule is missing an @InstallIn annotation") 142 .inFile(source) 143 .onLine(6); 144 } 145 146 @Test testNoErrorOnDaggerGeneratedModules()147 public void testNoErrorOnDaggerGeneratedModules() { 148 JavaFileObject source = 149 JavaFileObjects.forSourceString( 150 "foo.bar", 151 LINES.join( 152 "package foo.bar;", 153 "", 154 GeneratedImport.IMPORT_GENERATED_ANNOTATION, 155 "import dagger.Module;", 156 "", 157 "@Module", 158 "@Generated(value = \"something\")", // Error: Isn't Dagger-generated but missing 159 // InstallIn 160 "final class FooModule {}", 161 "", 162 "@Module", 163 "@Generated(value = \"dagger\")", // No error because the module is dagger generated 164 "final class BarModule {}")); 165 166 Compilation compilation = 167 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source); 168 169 assertThat(compilation).failed(); 170 assertThat(compilation).hadErrorCount(1); 171 assertThat(compilation) 172 .hadErrorContaining("foo.bar.FooModule is missing an @InstallIn annotation") 173 .inFile(source) 174 .onLine(8); 175 } 176 177 @Test testModuleWithOnlyParamConstructor_fails()178 public void testModuleWithOnlyParamConstructor_fails() { 179 JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join( 180 "package foo.bar;", 181 "", 182 "import dagger.Module;", 183 "import dagger.Provides;", 184 "import dagger.hilt.InstallIn;", 185 "import dagger.hilt.components.SingletonComponent;", 186 "", 187 "@Module", 188 "@InstallIn(SingletonComponent.class)", 189 "final class FooModule {", 190 " FooModule(String arg) {}", 191 "", 192 " @Provides", 193 " String provideString() {", 194 " return \"\";", 195 " }", 196 "}")); 197 198 Compilation compilation = 199 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source); 200 201 assertThat(compilation).failed(); 202 assertThat(compilation).hadErrorCount(1); 203 assertThat(compilation) 204 .hadErrorContaining( 205 "Modules that need to be instantiated by Hilt must have a visible, empty constructor."); 206 } 207 208 @Test testInnerModule()209 public void testInnerModule() { 210 JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join( 211 "package foo.bar;", 212 "", 213 "import dagger.Module;", 214 "import dagger.hilt.InstallIn;", 215 "import dagger.hilt.components.SingletonComponent;", 216 "", 217 "final class Outer {", 218 " @Module", 219 " @InstallIn(SingletonComponent.class)", 220 " final class InnerModule {}", 221 "}")); 222 223 Compilation compilation = 224 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source); 225 226 assertThat(compilation).failed(); 227 assertThat(compilation).hadErrorCount(1); 228 assertThat(compilation) 229 .hadErrorContaining( 230 "Nested @InstallIn modules must be static unless they are directly nested within a " 231 + "test. Found: foo.bar.Outer.InnerModule"); 232 } 233 234 @Test testInnerModuleInTest()235 public void testInnerModuleInTest() { 236 JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join( 237 "package foo.bar;", 238 "", 239 "import dagger.Module;", 240 "import dagger.hilt.InstallIn;", 241 "import dagger.hilt.components.SingletonComponent;", 242 "import dagger.hilt.android.testing.HiltAndroidTest;", 243 "", 244 "@HiltAndroidTest", 245 "final class Outer {", 246 " static class Nested {", 247 " @Module", 248 " @InstallIn(SingletonComponent.class)", 249 " final class InnerModule {}", 250 " }", 251 "}")); 252 253 Compilation compilation = 254 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source); 255 256 assertThat(compilation).failed(); 257 assertThat(compilation).hadErrorCount(1); 258 assertThat(compilation) 259 .hadErrorContaining( 260 "Nested @InstallIn modules must be static unless they are directly nested within a " 261 + "test. Found: foo.bar.Outer.Nested.InnerModule"); 262 } 263 264 @Test testInnerModuleInTest_succeeds()265 public void testInnerModuleInTest_succeeds() { 266 JavaFileObject source = JavaFileObjects.forSourceString("foo.bar", LINES.join( 267 "package foo.bar;", 268 "", 269 "import dagger.Module;", 270 "import dagger.hilt.InstallIn;", 271 "import dagger.hilt.components.SingletonComponent;", 272 "import dagger.hilt.android.testing.HiltAndroidTest;", 273 "", 274 "@HiltAndroidTest", 275 "final class Outer {", 276 " @Module", 277 " @InstallIn(SingletonComponent.class)", 278 " final class InnerModule {}", 279 "}")); 280 281 Compilation compilation = 282 CompilerTests.compiler().withProcessors(new AggregatedDepsProcessor()).compile(source); 283 284 assertThat(compilation).succeeded(); 285 } 286 } 287