• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.testing;
18 
19 import static dagger.hilt.internal.Preconditions.checkNotNull;
20 import static dagger.hilt.internal.Preconditions.checkState;
21 
22 import android.content.Context;
23 import androidx.test.core.app.ApplicationProvider;
24 import dagger.hilt.internal.GeneratedComponentManager;
25 import java.lang.annotation.Annotation;
26 import java.util.concurrent.atomic.AtomicBoolean;
27 import org.junit.rules.TestRule;
28 import org.junit.runner.Description;
29 import org.junit.runners.model.Statement;
30 
31 /**
32  * A Junit {@code TestRule} that's installed in all Hilt tests.
33  *
34  * <p>This rule enforces that a Hilt TestRule has run. The Dagger component will not be created
35  * without this test rule.
36  */
37 public final class MarkThatRulesRanRule implements TestRule {
38   private static final String HILT_ANDROID_APP = "dagger.hilt.android.HiltAndroidApp";
39   private static final String HILT_ANDROID_TEST = "dagger.hilt.android.testing.HiltAndroidTest";
40 
41   private final Context context = ApplicationProvider.getApplicationContext();
42   private final Object testInstance;
43   private final boolean autoAddModule;
44 
45   private final AtomicBoolean started = new AtomicBoolean(false);
46 
MarkThatRulesRanRule(Object testInstance)47   public MarkThatRulesRanRule(Object testInstance) {
48     this.autoAddModule = true;
49     this.testInstance = checkNotNull(testInstance);
50     checkState(
51         hasAnnotation(testInstance, HILT_ANDROID_TEST),
52         "Expected %s to be annotated with @HiltAndroidTest.",
53         testInstance.getClass().getName());
54     checkState(
55         context instanceof GeneratedComponentManager,
56         "Hilt test, %s, must use a Hilt test application but found %s. To fix, configure the test "
57             + "to use HiltTestApplication or a custom Hilt test application generated with "
58             + "@CustomTestApplication.",
59         testInstance.getClass().getName(),
60         context.getClass().getName());
61     checkState(
62         !hasAnnotation(context, HILT_ANDROID_APP),
63         "Hilt test, %s, cannot use a @HiltAndroidApp application but found %s. To fix, configure "
64             + "the test to use HiltTestApplication or a custom Hilt test application generated "
65             +  "with @CustomTestApplication.",
66         testInstance.getClass().getName(),
67         context.getClass().getName());
68   }
69 
delayComponentReady()70   public void delayComponentReady() {
71     checkState(!started.get(), "Called delayComponentReady after test execution started");
72     getTestApplicationComponentManager().delayComponentReady();
73   }
74 
componentReady()75   public void componentReady() {
76     checkState(started.get(), "Called componentReady before test execution started");
77     getTestApplicationComponentManager().componentReady();
78   }
79 
inject()80   public void inject() {
81     getTestApplicationComponentManager().inject();
82   }
83 
84   @Override
apply(final Statement base, Description description)85   public Statement apply(final Statement base, Description description) {
86     started.set(true);
87     checkState(
88         description.getTestClass().isInstance(testInstance),
89         "HiltAndroidRule was constructed with an argument that was not an instance of the test"
90             + " class");
91     return new Statement() {
92       @Override
93       public void evaluate() throws Throwable {
94 
95         TestApplicationComponentManager componentManager = getTestApplicationComponentManager();
96         try {
97           // This check is required to check that state hasn't been set before this rule runs. This
98           // prevents cases like setting state in Application.onCreate for Gradle emulator tests
99           // that will get cleared after running the first test case.
100           componentManager.checkStateIsCleared();
101           componentManager.setAutoAddModule(autoAddModule);
102           if (testInstance != null) {
103             componentManager.setTestInstance(testInstance);
104           }
105           componentManager.setHasHiltTestRule(description);
106           base.evaluate();
107           componentManager.verifyDelayedComponentWasMadeReady();
108         } finally {
109           componentManager.clearState();
110         }
111       }
112     };
113   }
114 
115   private TestApplicationComponentManager getTestApplicationComponentManager() {
116     checkState(
117         context instanceof TestApplicationComponentManagerHolder,
118         "The context is not an instance of TestApplicationComponentManagerHolder: %s",
119         context);
120     Object componentManager = ((TestApplicationComponentManagerHolder) context).componentManager();
121     checkState(
122         componentManager instanceof TestApplicationComponentManager,
123         "Expected TestApplicationComponentManagerHolder to return an instance of"
124             + "TestApplicationComponentManager");
125     return (TestApplicationComponentManager) componentManager;
126   }
127 
128   private static boolean hasAnnotation(Object obj, String annotationName) {
129     for (Annotation annotation : obj.getClass().getAnnotations()) {
130       if (annotation.annotationType().getName().contentEquals(annotationName)) {
131         return true;
132       }
133     }
134     return false;
135   }
136 }
137