137 lines
5.4 KiB
Java
137 lines
5.4 KiB
Java
/*
|
|
* Copyright (C) 2020 The Dagger Authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package dagger.hilt.android.internal.testing;
|
|
|
|
import static dagger.hilt.internal.Preconditions.checkNotNull;
|
|
import static dagger.hilt.internal.Preconditions.checkState;
|
|
|
|
import android.content.Context;
|
|
import androidx.test.core.app.ApplicationProvider;
|
|
import dagger.hilt.internal.GeneratedComponentManager;
|
|
import java.lang.annotation.Annotation;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import org.junit.rules.TestRule;
|
|
import org.junit.runner.Description;
|
|
import org.junit.runners.model.Statement;
|
|
|
|
/**
|
|
* A Junit {@code TestRule} that's installed in all Hilt tests.
|
|
*
|
|
* <p>This rule enforces that a Hilt TestRule has run. The Dagger component will not be created
|
|
* without this test rule.
|
|
*/
|
|
public final class MarkThatRulesRanRule implements TestRule {
|
|
private static final String HILT_ANDROID_APP = "dagger.hilt.android.HiltAndroidApp";
|
|
private static final String HILT_ANDROID_TEST = "dagger.hilt.android.testing.HiltAndroidTest";
|
|
|
|
private final Context context = ApplicationProvider.getApplicationContext();
|
|
private final Object testInstance;
|
|
private final boolean autoAddModule;
|
|
|
|
private final AtomicBoolean started = new AtomicBoolean(false);
|
|
|
|
public MarkThatRulesRanRule(Object testInstance) {
|
|
this.autoAddModule = true;
|
|
this.testInstance = checkNotNull(testInstance);
|
|
checkState(
|
|
hasAnnotation(testInstance, HILT_ANDROID_TEST),
|
|
"Expected %s to be annotated with @HiltAndroidTest.",
|
|
testInstance.getClass().getName());
|
|
checkState(
|
|
context instanceof GeneratedComponentManager,
|
|
"Hilt test, %s, must use a Hilt test application but found %s. To fix, configure the test "
|
|
+ "to use HiltTestApplication or a custom Hilt test application generated with "
|
|
+ "@CustomTestApplication.",
|
|
testInstance.getClass().getName(),
|
|
context.getClass().getName());
|
|
checkState(
|
|
!hasAnnotation(context, HILT_ANDROID_APP),
|
|
"Hilt test, %s, cannot use a @HiltAndroidApp application but found %s. To fix, configure "
|
|
+ "the test to use HiltTestApplication or a custom Hilt test application generated "
|
|
+ "with @CustomTestApplication.",
|
|
testInstance.getClass().getName(),
|
|
context.getClass().getName());
|
|
}
|
|
|
|
public void delayComponentReady() {
|
|
checkState(!started.get(), "Called delayComponentReady after test execution started");
|
|
getTestApplicationComponentManager().delayComponentReady();
|
|
}
|
|
|
|
public void componentReady() {
|
|
checkState(started.get(), "Called componentReady before test execution started");
|
|
getTestApplicationComponentManager().componentReady();
|
|
}
|
|
|
|
public void inject() {
|
|
getTestApplicationComponentManager().inject();
|
|
}
|
|
|
|
@Override
|
|
public Statement apply(final Statement base, Description description) {
|
|
started.set(true);
|
|
checkState(
|
|
description.getTestClass().isInstance(testInstance),
|
|
"HiltAndroidRule was constructed with an argument that was not an instance of the test"
|
|
+ " class");
|
|
return new Statement() {
|
|
@Override
|
|
public void evaluate() throws Throwable {
|
|
|
|
TestApplicationComponentManager componentManager = getTestApplicationComponentManager();
|
|
try {
|
|
// This check is required to check that state hasn't been set before this rule runs. This
|
|
// prevents cases like setting state in Application.onCreate for Gradle emulator tests
|
|
// that will get cleared after running the first test case.
|
|
componentManager.checkStateIsCleared();
|
|
componentManager.setAutoAddModule(autoAddModule);
|
|
if (testInstance != null) {
|
|
componentManager.setTestInstance(testInstance);
|
|
}
|
|
componentManager.setHasHiltTestRule(description);
|
|
base.evaluate();
|
|
componentManager.verifyDelayedComponentWasMadeReady();
|
|
} finally {
|
|
componentManager.clearState();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private TestApplicationComponentManager getTestApplicationComponentManager() {
|
|
checkState(
|
|
context instanceof TestApplicationComponentManagerHolder,
|
|
"The context is not an instance of TestApplicationComponentManagerHolder: %s",
|
|
context);
|
|
Object componentManager = ((TestApplicationComponentManagerHolder) context).componentManager();
|
|
checkState(
|
|
componentManager instanceof TestApplicationComponentManager,
|
|
"Expected TestApplicationComponentManagerHolder to return an instance of"
|
|
+ "TestApplicationComponentManager");
|
|
return (TestApplicationComponentManager) componentManager;
|
|
}
|
|
|
|
private static boolean hasAnnotation(Object obj, String annotationName) {
|
|
for (Annotation annotation : obj.getClass().getAnnotations()) {
|
|
if (annotation.annotationType().getName().contentEquals(annotationName)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|