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.android.testing.compile; 18 19 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 20 import static java.util.stream.Collectors.toMap; 21 22 import androidx.room.compiler.processing.XProcessingEnv; 23 import androidx.room.compiler.processing.util.CompilationResultSubject; 24 import androidx.room.compiler.processing.util.ProcessorTestExtKt; 25 import androidx.room.compiler.processing.util.Source; 26 import androidx.room.compiler.processing.util.compiler.TestCompilationArguments; 27 import androidx.room.compiler.processing.util.compiler.TestCompilationResult; 28 import androidx.room.compiler.processing.util.compiler.TestKotlinCompilerKt; 29 import com.google.auto.value.AutoValue; 30 import com.google.common.collect.ImmutableCollection; 31 import com.google.common.collect.ImmutableList; 32 import com.google.common.collect.ImmutableMap; 33 import com.google.devtools.ksp.processing.SymbolProcessorProvider; 34 import com.google.testing.compile.Compiler; 35 import dagger.hilt.android.processor.internal.androidentrypoint.AndroidEntryPointProcessor; 36 import dagger.hilt.android.processor.internal.androidentrypoint.KspAndroidEntryPointProcessor; 37 import dagger.hilt.android.processor.internal.customtestapplication.CustomTestApplicationProcessor; 38 import dagger.hilt.android.processor.internal.customtestapplication.KspCustomTestApplicationProcessor; 39 import dagger.hilt.processor.internal.BaseProcessingStep; 40 import dagger.hilt.processor.internal.HiltProcessingEnvConfigs; 41 import dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsProcessor; 42 import dagger.hilt.processor.internal.aggregateddeps.KspAggregatedDepsProcessor; 43 import dagger.hilt.processor.internal.aliasof.AliasOfProcessor; 44 import dagger.hilt.processor.internal.aliasof.KspAliasOfProcessor; 45 import dagger.hilt.processor.internal.definecomponent.DefineComponentProcessor; 46 import dagger.hilt.processor.internal.definecomponent.KspDefineComponentProcessor; 47 import dagger.hilt.processor.internal.earlyentrypoint.EarlyEntryPointProcessor; 48 import dagger.hilt.processor.internal.earlyentrypoint.KspEarlyEntryPointProcessor; 49 import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputProcessor; 50 import dagger.hilt.processor.internal.generatesrootinput.KspGeneratesRootInputProcessor; 51 import dagger.hilt.processor.internal.originatingelement.KspOriginatingElementProcessor; 52 import dagger.hilt.processor.internal.originatingelement.OriginatingElementProcessor; 53 import dagger.hilt.processor.internal.root.ComponentTreeDepsProcessor; 54 import dagger.hilt.processor.internal.root.KspComponentTreeDepsProcessor; 55 import dagger.hilt.processor.internal.root.KspRootProcessor; 56 import dagger.hilt.processor.internal.root.RootProcessor; 57 import dagger.hilt.processor.internal.uninstallmodules.KspUninstallModulesProcessor; 58 import dagger.hilt.processor.internal.uninstallmodules.UninstallModulesProcessor; 59 import dagger.internal.codegen.ComponentProcessor; 60 import dagger.internal.codegen.KspComponentProcessor; 61 import dagger.testing.compile.CompilerTests; 62 import java.util.Arrays; 63 import java.util.Collection; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.function.Consumer; 67 import java.util.function.Function; 68 import javax.annotation.processing.Processor; 69 import org.junit.rules.TemporaryFolder; 70 71 /** {@link Compiler} instances for testing Android Hilt. */ 72 public final class HiltCompilerTests { 73 /** Returns the {@link XProcessingEnv.Backend} for the given {@link CompilationResultSubject}. */ backend(CompilationResultSubject subject)74 public static XProcessingEnv.Backend backend(CompilationResultSubject subject) { 75 return CompilerTests.backend(subject); 76 } 77 78 /** Returns a {@link Source.KotlinSource} with the given file name and content. */ kotlinSource( String fileName, ImmutableCollection<String> srcLines)79 public static Source.KotlinSource kotlinSource( 80 String fileName, ImmutableCollection<String> srcLines) { 81 return CompilerTests.kotlinSource(fileName, srcLines); 82 } 83 84 /** Returns a {@link Source.KotlinSource} with the given file name and content. */ kotlinSource(String fileName, String... srcLines)85 public static Source.KotlinSource kotlinSource(String fileName, String... srcLines) { 86 return CompilerTests.kotlinSource(fileName, srcLines); 87 } 88 89 /** Returns a {@link Source.JavaSource} with the given file name and content. */ javaSource( String fileName, ImmutableCollection<String> srcLines)90 public static Source.JavaSource javaSource( 91 String fileName, ImmutableCollection<String> srcLines) { 92 return CompilerTests.javaSource(fileName, srcLines); 93 } 94 95 /** Returns a {@link Source.JavaSource} with the given file name and content. */ javaSource(String fileName, String... srcLines)96 public static Source.JavaSource javaSource(String fileName, String... srcLines) { 97 return CompilerTests.javaSource(fileName, srcLines); 98 } 99 100 /** Returns a {@link Compiler} instance with the given sources. */ hiltCompiler(Source... sources)101 public static HiltCompiler hiltCompiler(Source... sources) { 102 return hiltCompiler(ImmutableList.copyOf(sources)); 103 } 104 105 /** Returns a {@link Compiler} instance with the given sources. */ hiltCompiler(ImmutableCollection<Source> sources)106 public static HiltCompiler hiltCompiler(ImmutableCollection<Source> sources) { 107 return HiltCompiler.builder().sources(sources).build(); 108 } 109 compiler(Processor... extraProcessors)110 public static Compiler compiler(Processor... extraProcessors) { 111 return compiler(Arrays.asList(extraProcessors)); 112 } 113 compiler(Collection<? extends Processor> extraProcessors)114 public static Compiler compiler(Collection<? extends Processor> extraProcessors) { 115 Map<Class<?>, Processor> processors = 116 defaultProcessors().stream() 117 .collect(toMap((Processor e) -> e.getClass(), (Processor e) -> e)); 118 119 // Adds extra processors, and allows overriding any processors of the same class. 120 extraProcessors.forEach(processor -> processors.put(processor.getClass(), processor)); 121 122 return CompilerTests.compiler().withProcessors(processors.values()); 123 } 124 compileWithKapt( List<Source> sources, TemporaryFolder tempFolder, Consumer<TestCompilationResult> onCompilationResult)125 public static void compileWithKapt( 126 List<Source> sources, 127 TemporaryFolder tempFolder, 128 Consumer<TestCompilationResult> onCompilationResult) { 129 compileWithKapt( 130 sources, ImmutableMap.of(), ImmutableList.of(), tempFolder, onCompilationResult); 131 } 132 compileWithKapt( List<Source> sources, Map<String, String> processorOptions, TemporaryFolder tempFolder, Consumer<TestCompilationResult> onCompilationResult)133 public static void compileWithKapt( 134 List<Source> sources, 135 Map<String, String> processorOptions, 136 TemporaryFolder tempFolder, 137 Consumer<TestCompilationResult> onCompilationResult) { 138 compileWithKapt( 139 sources, processorOptions, ImmutableList.of(), tempFolder, onCompilationResult); 140 } 141 compileWithKapt( List<Source> sources, List<Processor> additionalProcessors, TemporaryFolder tempFolder, Consumer<TestCompilationResult> onCompilationResult)142 public static void compileWithKapt( 143 List<Source> sources, 144 List<Processor> additionalProcessors, 145 TemporaryFolder tempFolder, 146 Consumer<TestCompilationResult> onCompilationResult) { 147 compileWithKapt( 148 sources, ImmutableMap.of(), additionalProcessors, tempFolder, onCompilationResult); 149 } 150 compileWithKapt( List<Source> sources, Map<String, String> processorOptions, List<Processor> additionalProcessors, TemporaryFolder tempFolder, Consumer<TestCompilationResult> onCompilationResult)151 public static void compileWithKapt( 152 List<Source> sources, 153 Map<String, String> processorOptions, 154 List<Processor> additionalProcessors, 155 TemporaryFolder tempFolder, 156 Consumer<TestCompilationResult> onCompilationResult) { 157 TestCompilationResult result = 158 TestKotlinCompilerKt.compile( 159 tempFolder.getRoot(), 160 new TestCompilationArguments( 161 sources, 162 /* classpath= */ ImmutableList.of(CompilerTests.compilerDepsJar()), 163 /* inheritClasspath= */ false, 164 /* javacArguments= */ ImmutableList.of(), 165 /* kotlincArguments= */ ImmutableList.of(), 166 /* kaptProcessors= */ ImmutableList.<Processor>builder() 167 .addAll(defaultProcessors()) 168 .addAll(additionalProcessors) 169 .build(), 170 /* symbolProcessorProviders= */ ImmutableList.of(), 171 /* processorOptions= */ processorOptions)); 172 onCompilationResult.accept(result); 173 } 174 defaultProcessors()175 static ImmutableList<Processor> defaultProcessors() { 176 return ImmutableList.of( 177 new AggregatedDepsProcessor(), 178 new AliasOfProcessor(), 179 new AndroidEntryPointProcessor(), 180 new ComponentProcessor(), 181 new ComponentTreeDepsProcessor(), 182 new CustomTestApplicationProcessor(), 183 new DefineComponentProcessor(), 184 new EarlyEntryPointProcessor(), 185 new GeneratesRootInputProcessor(), 186 new OriginatingElementProcessor(), 187 new RootProcessor(), 188 new UninstallModulesProcessor()); 189 } 190 kspDefaultProcessors()191 private static ImmutableList<SymbolProcessorProvider> kspDefaultProcessors() { 192 // TODO(bcorso): Add the rest of the KSP processors here. 193 return ImmutableList.of( 194 new KspAggregatedDepsProcessor.Provider(), 195 new KspAliasOfProcessor.Provider(), 196 new KspAndroidEntryPointProcessor.Provider(), 197 new KspComponentProcessor.Provider(), 198 new KspComponentTreeDepsProcessor.Provider(), 199 new KspCustomTestApplicationProcessor.Provider(), 200 new KspDefineComponentProcessor.Provider(), 201 new KspEarlyEntryPointProcessor.Provider(), 202 new KspGeneratesRootInputProcessor.Provider(), 203 new KspOriginatingElementProcessor.Provider(), 204 new KspRootProcessor.Provider(), 205 new KspUninstallModulesProcessor.Provider()); 206 } 207 208 /** Used to compile Hilt sources and inspect the compiled results. */ 209 @AutoValue 210 public abstract static class HiltCompiler { builder()211 static Builder builder() { 212 return new AutoValue_HiltCompilerTests_HiltCompiler.Builder() 213 // Set the builder defaults. 214 .processorOptions(ImmutableMap.of()) 215 .additionalJavacProcessors(ImmutableList.of()) 216 .additionalKspProcessors(ImmutableList.of()) 217 .processingSteps(ImmutableList.of()) 218 .javacArguments(ImmutableList.of()); 219 } 220 221 /** Returns the sources being compiled */ sources()222 abstract ImmutableCollection<Source> sources(); 223 224 /** Returns the annotation processors options. */ processorOptions()225 abstract ImmutableMap<String, String> processorOptions(); 226 227 /** Returns the extra Javac processors. */ additionalJavacProcessors()228 abstract ImmutableCollection<Processor> additionalJavacProcessors(); 229 230 /** Returns the extra KSP processors. */ additionalKspProcessors()231 abstract ImmutableCollection<SymbolProcessorProvider> additionalKspProcessors(); 232 233 /** Returns the command-line options */ javacArguments()234 abstract ImmutableCollection<String> javacArguments(); 235 236 /** Returns a new {@link HiltCompiler} instance with the annotation processors options. */ withProcessorOptions(ImmutableMap<String, String> processorOptions)237 public HiltCompiler withProcessorOptions(ImmutableMap<String, String> processorOptions) { 238 return toBuilder().processorOptions(processorOptions).build(); 239 } 240 241 /** Returns the processing steps suppliers. */ processingSteps()242 abstract ImmutableCollection<Function<XProcessingEnv, BaseProcessingStep>> processingSteps(); 243 withProcessingSteps( Function<XProcessingEnv, BaseProcessingStep>.... mapping)244 public HiltCompiler withProcessingSteps( 245 Function<XProcessingEnv, BaseProcessingStep>... mapping) { 246 return toBuilder().processingSteps(ImmutableList.copyOf(mapping)).build(); 247 } 248 249 /** Returns a new {@link HiltCompiler} instance with the additional Javac processors. */ withAdditionalJavacProcessors(Processor... processors)250 public HiltCompiler withAdditionalJavacProcessors(Processor... processors) { 251 return toBuilder().additionalJavacProcessors(ImmutableList.copyOf(processors)).build(); 252 } 253 254 /** Returns a new {@link HiltCompiler} instance with the additional KSP processors. */ withAdditionalKspProcessors(SymbolProcessorProvider... processors)255 public HiltCompiler withAdditionalKspProcessors(SymbolProcessorProvider... processors) { 256 return toBuilder().additionalKspProcessors(ImmutableList.copyOf(processors)).build(); 257 } 258 259 /** Returns a new {@link HiltCompiler} instance with command-line options. */ withJavacArguments(String... arguments)260 public HiltCompiler withJavacArguments(String... arguments) { 261 return toBuilder().javacArguments(ImmutableList.copyOf(arguments)).build(); 262 } 263 264 /** Returns a new {@link HiltCompiler} instance with command-line options. */ withJavacArguments(ImmutableCollection<String> arguments)265 public HiltCompiler withJavacArguments(ImmutableCollection<String> arguments) { 266 return toBuilder().javacArguments(arguments).build(); 267 } 268 269 /** Returns a builder with the current values of this {@link Compiler} as default. */ toBuilder()270 abstract Builder toBuilder(); 271 compile(Consumer<CompilationResultSubject> onCompilationResult)272 public void compile(Consumer<CompilationResultSubject> onCompilationResult) { 273 ProcessorTestExtKt.runProcessorTest( 274 sources().asList(), 275 /* classpath= */ ImmutableList.of(CompilerTests.compilerDepsJar()), 276 /* options= */ processorOptions(), 277 /* javacArguments= */ javacArguments().asList(), 278 /* kotlincArguments= */ ImmutableList.of( 279 "-P", "plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true"), 280 /* config= */ HiltProcessingEnvConfigs.CONFIGS, 281 /* javacProcessors= */ ImmutableList.<Processor>builder() 282 .addAll(mergeProcessors(defaultProcessors(), additionalJavacProcessors())) 283 .addAll( 284 processingSteps().stream() 285 .map(HiltCompilerProcessors.JavacProcessor::new) 286 .collect(toImmutableList())) 287 .build(), 288 /* symbolProcessorProviders= */ ImmutableList.<SymbolProcessorProvider>builder() 289 .addAll(mergeProcessors(kspDefaultProcessors(), additionalKspProcessors())) 290 .addAll( 291 processingSteps().stream() 292 .map(HiltCompilerProcessors.KspProcessor.Provider::new) 293 .collect(toImmutableList())) 294 .build(), 295 result -> { 296 onCompilationResult.accept(result); 297 return null; 298 }); 299 } 300 mergeProcessors( Collection<T> defaultProcessors, Collection<T> extraProcessors)301 private static <T> ImmutableList<T> mergeProcessors( 302 Collection<T> defaultProcessors, Collection<T> extraProcessors) { 303 Map<Class<?>, T> processors = 304 defaultProcessors.stream().collect(toMap((T e) -> e.getClass(), (T e) -> e)); 305 // Adds extra processors, and allows overriding any processors of the same class. 306 extraProcessors.forEach(processor -> processors.put(processor.getClass(), processor)); 307 return ImmutableList.copyOf(processors.values()); 308 } 309 310 /** Used to build a {@link HiltCompiler}. */ 311 @AutoValue.Builder 312 public abstract static class Builder { sources(ImmutableCollection<Source> sources)313 abstract Builder sources(ImmutableCollection<Source> sources); processorOptions(ImmutableMap<String, String> processorOptions)314 abstract Builder processorOptions(ImmutableMap<String, String> processorOptions); additionalJavacProcessors(ImmutableCollection<Processor> processors)315 abstract Builder additionalJavacProcessors(ImmutableCollection<Processor> processors); additionalKspProcessors( ImmutableCollection<SymbolProcessorProvider> processors)316 abstract Builder additionalKspProcessors( 317 ImmutableCollection<SymbolProcessorProvider> processors); javacArguments(ImmutableCollection<String> arguments)318 abstract Builder javacArguments(ImmutableCollection<String> arguments); 319 processingSteps( ImmutableCollection<Function<XProcessingEnv, BaseProcessingStep>> processingSteps)320 abstract Builder processingSteps( 321 ImmutableCollection<Function<XProcessingEnv, BaseProcessingStep>> processingSteps); 322 build()323 abstract HiltCompiler build(); 324 } 325 } 326 HiltCompilerTests()327 private HiltCompilerTests() {} 328 } 329