Skip to content

Commit

Permalink
Add TestInstallIn to Hilt.
Browse files Browse the repository at this point in the history
This feature allows users to replace @Installin modules with a @TestInstallIn module. For example, you can replace FooModule with FakeFooModule with the following:

```
  @module
  @TestInstallIn(
      components = SingletonComponent.class,
      replaces = FooModule.class)
  interface FakeFooModule {
    ...
  }
```

Note that @TestInstallIn only replaces @Installin when using @HiltAndroidTest, it does not apply in @HiltAndroidApp.

RELNOTES=Adds @TestInstallIn feature to Hilt
PiperOrigin-RevId: 351213437
  • Loading branch information
bcorso authored and Dagger Team committed Jan 11, 2021
1 parent 494a578 commit d828472
Show file tree
Hide file tree
Showing 23 changed files with 1,165 additions and 270 deletions.
8 changes: 8 additions & 0 deletions java/dagger/hilt/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ filegroup(
":hilt_android_filegroup",
":hilt_android_testing_filegroup",
":hilt_filegroup",
":hilt_testing_filegroup",
],
)

Expand All @@ -119,6 +120,13 @@ filegroup(
],
)

filegroup(
name = "hilt_testing_filegroup",
srcs = [
"//java/dagger/hilt/testing:srcs_filegroup",
],
)

filegroup(
name = "hilt_android_filegroup",
srcs = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private static ViewModelProvider.Factory getFactoryFromSet(Set<ViewModelProvider
/** The activity module to declare the optional factories. */
@Module
@InstallIn(ActivityComponent.class)
public interface ActivityModule {
interface ActivityModule {
@Multibinds
@HiltViewModelMap.KeySet
abstract Set<String> viewModelKeys();
Expand All @@ -152,14 +152,14 @@ public interface ActivityModule {
/** The activity entry point to retrieve the factory. */
@EntryPoint
@InstallIn(ActivityComponent.class)
public interface ActivityEntryPoint {
interface ActivityEntryPoint {
InternalFactoryFactory getHiltInternalFactoryFactory();
}

/** The fragment entry point to retrieve the factory. */
@EntryPoint
@InstallIn(FragmentComponent.class)
public interface FragmentEntryPoint {
interface FragmentEntryPoint {
InternalFactoryFactory getHiltInternalFactoryFactory();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ interface ViewModelFactoriesEntryPoint {
/** Hilt module for providing the empty multi-binding map of ViewModels. */
@Module
@InstallIn(ViewModelComponent.class)
public abstract static class ViewModelModule {
interface ViewModelModule {
@Multibinds
@HiltViewModelMap
abstract Map<String, ViewModel> hiltViewModelMap();
Map<String, ViewModel> hiltViewModelMap();
}

private final Set<String> viewModelInjectKeys;
Expand Down
5 changes: 1 addition & 4 deletions java/dagger/hilt/android/processor/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,9 @@ gen_maven_artifact(
"//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib",
"//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib",
"//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib",
"//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
"//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib",
"//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
"//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
"//java/dagger/hilt/codegen:originating_element",
"//java/dagger/hilt/codegen:package_info",
"//java/dagger/hilt/processor/internal:base_processor",
"//java/dagger/hilt/processor/internal:classnames",
"//java/dagger/hilt/processor/internal:component_descriptor",
Expand All @@ -56,7 +54,6 @@ gen_maven_artifact(
"//java/dagger/hilt/processor/internal:kotlin",
"//java/dagger/hilt/processor/internal:processor_errors",
"//java/dagger/hilt/processor/internal:processors",
"//java/dagger/hilt/processor/internal/aggregateddeps:annotation",
"//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
"//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
"//java/dagger/hilt/processor/internal/aliasof:alias_ofs",
Expand Down
4 changes: 4 additions & 0 deletions java/dagger/hilt/android/testing/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ android_library(
":package_info",
":uninstall_modules",
"//java/dagger/hilt/android:artifact-lib",
"//java/dagger/hilt/testing:test_install_in",
],
)

Expand All @@ -193,6 +194,8 @@ gen_maven_artifact(
"//java/dagger/hilt/android/testing:hilt_test_application",
"//java/dagger/hilt/android/testing:on_component_ready_runner",
"//java/dagger/hilt/android/testing:package_info",
"//java/dagger/hilt/testing:test_install_in",
"//java/dagger/hilt/testing:package_info",
"//java/dagger/hilt/android/testing:uninstall_modules",
],
artifact_target_maven_deps = [
Expand Down Expand Up @@ -223,6 +226,7 @@ gen_maven_artifact(
],
javadoc_srcs = [
"//java/dagger/hilt:hilt_android_testing_filegroup",
"//java/dagger/hilt:hilt_testing_filegroup",
],
manifest = "AndroidManifest.xml",
packaging = "aar",
Expand Down
5 changes: 1 addition & 4 deletions java/dagger/hilt/processor/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,9 @@ gen_maven_artifact(
"//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib",
"//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib",
"//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib",
"//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
"//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib",
"//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib",
"//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib",
"//java/dagger/hilt/codegen:originating_element",
"//java/dagger/hilt/codegen:package_info",
"//java/dagger/hilt/processor/internal:base_processor",
"//java/dagger/hilt/processor/internal:classnames",
"//java/dagger/hilt/processor/internal:component_descriptor",
Expand All @@ -76,7 +74,6 @@ gen_maven_artifact(
"//java/dagger/hilt/processor/internal:kotlin",
"//java/dagger/hilt/processor/internal:processor_errors",
"//java/dagger/hilt/processor/internal:processors",
"//java/dagger/hilt/processor/internal/aggregateddeps:annotation",
"//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies",
"//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
"//java/dagger/hilt/processor/internal/aliasof:alias_ofs",
Expand Down
6 changes: 3 additions & 3 deletions java/dagger/hilt/processor/internal/AnnotationValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,14 @@ public static Optional<String> getOptionalStringValue(

/** Returns the int array value of an annotation */
public static int[] getIntArrayValue(AnnotationMirror annotation, String valueName) {
return asAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
return getAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
.mapToInt(it -> (int) it.getValue())
.toArray();
}

/** Returns the String array value of an annotation */
public static String[] getStringArrayValue(AnnotationMirror annotation, String valueName) {
return asAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
return getAnnotationValues(getAnnotationValue(annotation, valueName)).stream()
.map(it -> (String) it.getValue())
.toArray(String[]::new);
}
Expand All @@ -164,7 +164,7 @@ private static boolean isValuePresent(AnnotationMirror annotation, String valueN
*
* @throws IllegalArgumentException unless {@code annotationValue} represents an array
*/
private static ImmutableList<AnnotationValue> asAnnotationValues(
public static ImmutableList<AnnotationValue> getAnnotationValues(
AnnotationValue annotationValue) {
return annotationValue.accept(AS_ANNOTATION_VALUES, null);
}
Expand Down
3 changes: 3 additions & 0 deletions java/dagger/hilt/processor/internal/ClassNames.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
public final class ClassNames {
public static final ClassName ORIGINATING_ELEMENT =
get("dagger.hilt.codegen", "OriginatingElement");
public static final ClassName AGGREGATED_DEPS =
get("dagger.hilt.processor.internal.aggregateddeps", "AggregatedDeps");
public static final ClassName GENERATED_COMPONENT =
get("dagger.hilt.internal", "GeneratedComponent");
public static final ClassName GENERATED_COMPONENT_MANAGER =
Expand Down Expand Up @@ -91,6 +93,7 @@ public final class ClassNames {

public static final ClassName INSTALL_IN =
get("dagger.hilt", "InstallIn");
public static final ClassName TEST_INSTALL_IN = get("dagger.hilt.testing", "TestInstallIn");
public static final ClassName ENTRY_POINT =
get("dagger.hilt", "EntryPoint");
public static final ClassName ENTRY_POINTS = get("dagger.hilt", "EntryPoints");
Expand Down
25 changes: 19 additions & 6 deletions java/dagger/hilt/processor/internal/Components.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.definecomponent.DefineComponents;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
Expand All @@ -49,7 +48,8 @@ public static ImmutableSet<ComponentDescriptor> getComponentDescriptors(
/** Returns the {@link dagger.hilt.InstallIn} components for a given element. */
public static ImmutableSet<ClassName> getComponents(Elements elements, Element element) {
ImmutableSet<ClassName> components;
if (Processors.hasAnnotation(element, ClassNames.INSTALL_IN)) {
if (Processors.hasAnnotation(element, ClassNames.INSTALL_IN)
|| Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN)) {
components = getHiltInstallInComponents(elements, element);
} else {
// Check the enclosing element in case it passed in module is a companion object. This helps
Expand Down Expand Up @@ -88,20 +88,33 @@ public static AnnotationSpec getInstallInAnnotationSpec(ImmutableSet<ClassName>

private static ImmutableSet<ClassName> getHiltInstallInComponents(
Elements elements, Element element) {
AnnotationMirror hiltInstallIn =
Processors.getAnnotationMirror(element, ClassNames.INSTALL_IN);
Preconditions.checkArgument(
Processors.hasAnnotation(element, ClassNames.INSTALL_IN)
|| Processors.hasAnnotation(element, ClassNames.TEST_INSTALL_IN));

ImmutableSet<TypeElement> components =
Processors.getAnnotationClassValues(elements, hiltInstallIn, "value").stream()
.collect(toImmutableSet());
ImmutableSet.copyOf(
Processors.hasAnnotation(element, ClassNames.INSTALL_IN)
? Processors.getAnnotationClassValues(
elements,
Processors.getAnnotationMirror(element, ClassNames.INSTALL_IN),
"value")
: Processors.getAnnotationClassValues(
elements,
Processors.getAnnotationMirror(element, ClassNames.TEST_INSTALL_IN),
"components"));

ImmutableSet<TypeElement> undefinedComponents =
components.stream()
.filter(component -> !Processors.hasAnnotation(component, ClassNames.DEFINE_COMPONENT))
.collect(toImmutableSet());

ProcessorErrors.checkState(
undefinedComponents.isEmpty(),
element,
"@InstallIn, can only be used with @DefineComponent-annotated classes, but found: %s",
undefinedComponents);

return components.stream().map(ClassName::get).collect(toImmutableSet());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
/** Returns the test this dependency is associated with, otherwise an empty string. */
String test() default "";

/** Returns the deps that this dep replaces. */
String[] replaces() default {};

String[] modules() default {};

String[] entryPoints() default {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,15 @@

package dagger.hilt.processor.internal.aggregateddeps;

import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.Processors;
import java.io.IOException;
import java.util.Optional;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;

/**
Expand All @@ -41,17 +38,23 @@ final class AggregatedDepsGenerator {

private final String dependencyType;
private final TypeElement dependency;
private final Optional<ClassName> testName;
private final ImmutableSet<ClassName> components;
private final ImmutableSet<ClassName> replacedDependencies;
private final ProcessingEnvironment processingEnv;

AggregatedDepsGenerator(
String dependencyType,
TypeElement dependency,
Optional<ClassName> testName,
ImmutableSet<ClassName> components,
ImmutableSet<ClassName> replacedDependencies,
ProcessingEnvironment processingEnv) {
this.dependencyType = dependencyType;
this.dependency = dependency;
this.testName = testName;
this.components = components;
this.replacedDependencies = replacedDependencies;
this.processingEnv = processingEnv;
}

Expand All @@ -75,28 +78,9 @@ void generate() throws IOException {
private AnnotationSpec aggregatedDepsAnnotation() {
AnnotationSpec.Builder annotationBuilder = AnnotationSpec.builder(AGGREGATED_DEPS);
components.forEach(component -> annotationBuilder.addMember("components", "$S", component));
getEnclosingTestName(dependency)
.ifPresent(test -> annotationBuilder.addMember("test", "$S", test));
replacedDependencies.forEach(dep -> annotationBuilder.addMember("replaces", "$S", dep));
testName.ifPresent(test -> annotationBuilder.addMember("test", "$S", test));
annotationBuilder.addMember(dependencyType, "$S", dependency.getQualifiedName());
return annotationBuilder.build();
}

private Optional<ClassName> getEnclosingTestName(Element element) {
TypeElement topLevelType = getOriginatingTopLevelType(element);
return Processors.hasAnnotation(topLevelType, ClassNames.HILT_ANDROID_TEST)
? Optional.of(ClassName.get(MoreElements.asType(topLevelType)))
: Optional.empty();
}

private TypeElement getOriginatingTopLevelType(Element element) {
TypeElement topLevelType = Processors.getTopLevelType(element);
if (Processors.hasAnnotation(topLevelType, ClassNames.ORIGINATING_ELEMENT)) {
return getOriginatingTopLevelType(
Processors.getAnnotationClassValue(
processingEnv.getElementUtils(),
Processors.getAnnotationMirror(topLevelType, ClassNames.ORIGINATING_ELEMENT),
"topLevelClass"));
}
return topLevelType;
}
}
Loading

1 comment on commit d828472

@Chang-Eric
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes #1923

Please sign in to comment.