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