1*f585d8a3SJacky Wang /*
2*f585d8a3SJacky Wang  * Copyright (C) 2021 The Dagger Authors.
3*f585d8a3SJacky Wang  *
4*f585d8a3SJacky Wang  * Licensed under the Apache License, Version 2.0 (the "License");
5*f585d8a3SJacky Wang  * you may not use this file except in compliance with the License.
6*f585d8a3SJacky Wang  * You may obtain a copy of the License at
7*f585d8a3SJacky Wang  *
8*f585d8a3SJacky Wang  * http://www.apache.org/licenses/LICENSE-2.0
9*f585d8a3SJacky Wang  *
10*f585d8a3SJacky Wang  * Unless required by applicable law or agreed to in writing, software
11*f585d8a3SJacky Wang  * distributed under the License is distributed on an "AS IS" BASIS,
12*f585d8a3SJacky Wang  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*f585d8a3SJacky Wang  * See the License for the specific language governing permissions and
14*f585d8a3SJacky Wang  * limitations under the License.
15*f585d8a3SJacky Wang  */
16*f585d8a3SJacky Wang 
17*f585d8a3SJacky Wang package dagger.hilt.android;
18*f585d8a3SJacky Wang 
19*f585d8a3SJacky Wang import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
20*f585d8a3SJacky Wang import static com.google.common.truth.Truth.assertThat;
21*f585d8a3SJacky Wang import static org.junit.Assert.assertThrows;
22*f585d8a3SJacky Wang 
23*f585d8a3SJacky Wang import android.os.Build;
24*f585d8a3SJacky Wang import androidx.test.ext.junit.runners.AndroidJUnit4;
25*f585d8a3SJacky Wang import dagger.hilt.EntryPoint;
26*f585d8a3SJacky Wang import dagger.hilt.EntryPoints;
27*f585d8a3SJacky Wang import dagger.hilt.InstallIn;
28*f585d8a3SJacky Wang import dagger.hilt.android.EarlyEntryPointHiltAndroidTestRuntimeClasses.BarEntryPoint;
29*f585d8a3SJacky Wang import dagger.hilt.android.EarlyEntryPointHiltAndroidTestRuntimeClasses.EarlyFooEntryPoint;
30*f585d8a3SJacky Wang import dagger.hilt.android.EarlyEntryPointHiltAndroidTestRuntimeClasses.EarlyMySubcomponentBuilderEntryPoint;
31*f585d8a3SJacky Wang import dagger.hilt.android.EarlyEntryPointHiltAndroidTestRuntimeClasses.MySubcomponent;
32*f585d8a3SJacky Wang import dagger.hilt.android.EarlyEntryPointHiltAndroidTestRuntimeClasses.MySubcomponentBuilderEntryPoint;
33*f585d8a3SJacky Wang import dagger.hilt.android.EarlyEntryPointHiltAndroidTestRuntimeClasses.MySubcomponentScoped;
34*f585d8a3SJacky Wang import dagger.hilt.android.internal.testing.TestApplicationComponentManager;
35*f585d8a3SJacky Wang import dagger.hilt.android.testing.HiltAndroidRule;
36*f585d8a3SJacky Wang import dagger.hilt.android.testing.HiltAndroidTest;
37*f585d8a3SJacky Wang import dagger.hilt.android.testing.HiltTestApplication;
38*f585d8a3SJacky Wang import dagger.hilt.components.SingletonComponent;
39*f585d8a3SJacky Wang import javax.inject.Inject;
40*f585d8a3SJacky Wang import javax.inject.Singleton;
41*f585d8a3SJacky Wang import org.junit.Rule;
42*f585d8a3SJacky Wang import org.junit.Test;
43*f585d8a3SJacky Wang import org.junit.runner.RunWith;
44*f585d8a3SJacky Wang import org.robolectric.annotation.Config;
45*f585d8a3SJacky Wang 
46*f585d8a3SJacky Wang @HiltAndroidTest
47*f585d8a3SJacky Wang @RunWith(AndroidJUnit4.class)
48*f585d8a3SJacky Wang // Robolectric requires Java9 to run API 29 and above, so use API 28 instead
49*f585d8a3SJacky Wang @Config(sdk = Build.VERSION_CODES.P, application = HiltTestApplication.class)
50*f585d8a3SJacky Wang public final class EarlyEntryPointHiltAndroidTestRuntimeTest {
51*f585d8a3SJacky Wang   @EntryPoint
52*f585d8a3SJacky Wang   @InstallIn(SingletonComponent.class)
53*f585d8a3SJacky Wang   interface FooEntryPoint {
foo()54*f585d8a3SJacky Wang     Foo foo();
55*f585d8a3SJacky Wang   }
56*f585d8a3SJacky Wang 
57*f585d8a3SJacky Wang   @Singleton
58*f585d8a3SJacky Wang   public static class Foo {
59*f585d8a3SJacky Wang     @Inject
Foo()60*f585d8a3SJacky Wang     Foo() {}
61*f585d8a3SJacky Wang   }
62*f585d8a3SJacky Wang 
63*f585d8a3SJacky Wang   @MySubcomponentScoped
64*f585d8a3SJacky Wang   public static class Bar {
65*f585d8a3SJacky Wang     final Foo foo;
66*f585d8a3SJacky Wang     final int id;
67*f585d8a3SJacky Wang 
68*f585d8a3SJacky Wang     @Inject
Bar(Foo foo, int id)69*f585d8a3SJacky Wang     Bar(Foo foo, int id) {
70*f585d8a3SJacky Wang       this.foo = foo;
71*f585d8a3SJacky Wang       this.id = id;
72*f585d8a3SJacky Wang     }
73*f585d8a3SJacky Wang   }
74*f585d8a3SJacky Wang 
75*f585d8a3SJacky Wang   @Rule public HiltAndroidRule rule = new HiltAndroidRule(this);
76*f585d8a3SJacky Wang 
77*f585d8a3SJacky Wang   @Test
testEarlyEntryPointsWrongEntryPointFails()78*f585d8a3SJacky Wang   public void testEarlyEntryPointsWrongEntryPointFails() throws Exception {
79*f585d8a3SJacky Wang     IllegalStateException exception =
80*f585d8a3SJacky Wang         assertThrows(
81*f585d8a3SJacky Wang             IllegalStateException.class,
82*f585d8a3SJacky Wang             () -> EarlyEntryPoints.get(getApplicationContext(), FooEntryPoint.class));
83*f585d8a3SJacky Wang 
84*f585d8a3SJacky Wang     assertThat(exception)
85*f585d8a3SJacky Wang         .hasMessageThat()
86*f585d8a3SJacky Wang         .contains(
87*f585d8a3SJacky Wang             "FooEntryPoint should be called with EntryPoints.get() rather than "
88*f585d8a3SJacky Wang                 + "EarlyEntryPoints.get()");
89*f585d8a3SJacky Wang   }
90*f585d8a3SJacky Wang 
91*f585d8a3SJacky Wang   @Test
testEntryPointsWrongEntryPointFails()92*f585d8a3SJacky Wang   public void testEntryPointsWrongEntryPointFails() throws Exception {
93*f585d8a3SJacky Wang     IllegalStateException exception =
94*f585d8a3SJacky Wang         assertThrows(
95*f585d8a3SJacky Wang             IllegalStateException.class,
96*f585d8a3SJacky Wang             () -> EntryPoints.get(getApplicationContext(), EarlyFooEntryPoint.class));
97*f585d8a3SJacky Wang 
98*f585d8a3SJacky Wang     assertThat(exception)
99*f585d8a3SJacky Wang         .hasMessageThat()
100*f585d8a3SJacky Wang         .contains(
101*f585d8a3SJacky Wang             "Interface, dagger.hilt.android.EarlyEntryPointHiltAndroidTestRuntimeClasses."
102*f585d8a3SJacky Wang                 + "EarlyFooEntryPoint, annotated with @EarlyEntryPoint should be called with "
103*f585d8a3SJacky Wang                 + "EarlyEntryPoints.get() rather than EntryPoints.get()");
104*f585d8a3SJacky Wang   }
105*f585d8a3SJacky Wang 
106*f585d8a3SJacky Wang   @Test
testComponentInstances()107*f585d8a3SJacky Wang   public void testComponentInstances() {
108*f585d8a3SJacky Wang     Object componentManager = ((HiltTestApplication) getApplicationContext()).componentManager();
109*f585d8a3SJacky Wang     Object component1 = ((TestApplicationComponentManager) componentManager).generatedComponent();
110*f585d8a3SJacky Wang     Object component2 = ((TestApplicationComponentManager) componentManager).generatedComponent();
111*f585d8a3SJacky Wang     assertThat(component1).isNotNull();
112*f585d8a3SJacky Wang     assertThat(component2).isNotNull();
113*f585d8a3SJacky Wang     assertThat(component1).isEqualTo(component2);
114*f585d8a3SJacky Wang 
115*f585d8a3SJacky Wang     Object earlyComponent1 =
116*f585d8a3SJacky Wang         ((TestApplicationComponentManager) componentManager).earlySingletonComponent();
117*f585d8a3SJacky Wang     Object earlyComponent2 =
118*f585d8a3SJacky Wang         ((TestApplicationComponentManager) componentManager).earlySingletonComponent();
119*f585d8a3SJacky Wang     assertThat(earlyComponent1).isNotNull();
120*f585d8a3SJacky Wang     assertThat(earlyComponent2).isNotNull();
121*f585d8a3SJacky Wang     assertThat(earlyComponent1).isEqualTo(earlyComponent2);
122*f585d8a3SJacky Wang     assertThat(earlyComponent1).isNotEqualTo(component1);
123*f585d8a3SJacky Wang   }
124*f585d8a3SJacky Wang 
125*f585d8a3SJacky Wang   // Test that the early entry point returns a different @Singleton binding instance.
126*f585d8a3SJacky Wang   @Test
testScopedEntryPointValues()127*f585d8a3SJacky Wang   public void testScopedEntryPointValues() {
128*f585d8a3SJacky Wang     Foo foo1 = EntryPoints.get(getApplicationContext(), FooEntryPoint.class).foo();
129*f585d8a3SJacky Wang     Foo foo2 = EntryPoints.get(getApplicationContext(), FooEntryPoint.class).foo();
130*f585d8a3SJacky Wang     Foo earlyFoo1 =
131*f585d8a3SJacky Wang         EarlyEntryPoints.get(getApplicationContext(), EarlyFooEntryPoint.class).foo();
132*f585d8a3SJacky Wang     Foo earlyFoo2 =
133*f585d8a3SJacky Wang         EarlyEntryPoints.get(getApplicationContext(), EarlyFooEntryPoint.class).foo();
134*f585d8a3SJacky Wang 
135*f585d8a3SJacky Wang     assertThat(foo1).isNotNull();
136*f585d8a3SJacky Wang     assertThat(foo2).isNotNull();
137*f585d8a3SJacky Wang     assertThat(earlyFoo1).isNotNull();
138*f585d8a3SJacky Wang     assertThat(earlyFoo2).isNotNull();
139*f585d8a3SJacky Wang 
140*f585d8a3SJacky Wang     assertThat(foo1).isEqualTo(foo2);
141*f585d8a3SJacky Wang     assertThat(earlyFoo1).isEqualTo(earlyFoo2);
142*f585d8a3SJacky Wang     assertThat(earlyFoo1).isNotEqualTo(foo1);
143*f585d8a3SJacky Wang   }
144*f585d8a3SJacky Wang 
145*f585d8a3SJacky Wang   // Test that the a subcomponent of the early component does not need to use EarlyEntryPoints.
146*f585d8a3SJacky Wang   @Test
testSubcomponentEntryPoints()147*f585d8a3SJacky Wang   public void testSubcomponentEntryPoints() {
148*f585d8a3SJacky Wang     MySubcomponent subcomponent =
149*f585d8a3SJacky Wang         EntryPoints.get(getApplicationContext(), MySubcomponentBuilderEntryPoint.class)
150*f585d8a3SJacky Wang             .mySubcomponentBuilder()
151*f585d8a3SJacky Wang             .id(5)
152*f585d8a3SJacky Wang             .build();
153*f585d8a3SJacky Wang 
154*f585d8a3SJacky Wang     MySubcomponent earlySubcomponent =
155*f585d8a3SJacky Wang         EarlyEntryPoints.get(
156*f585d8a3SJacky Wang                 getApplicationContext(), EarlyMySubcomponentBuilderEntryPoint.class)
157*f585d8a3SJacky Wang             .mySubcomponentBuilder()
158*f585d8a3SJacky Wang             .id(11)
159*f585d8a3SJacky Wang             .build();
160*f585d8a3SJacky Wang 
161*f585d8a3SJacky Wang     assertThat(subcomponent).isNotNull();
162*f585d8a3SJacky Wang     assertThat(earlySubcomponent).isNotNull();
163*f585d8a3SJacky Wang     assertThat(subcomponent).isNotEqualTo(earlySubcomponent);
164*f585d8a3SJacky Wang 
165*f585d8a3SJacky Wang     // Test that subcomponents can use EntryPoints
166*f585d8a3SJacky Wang     Bar bar1 = EntryPoints.get(subcomponent, BarEntryPoint.class).bar();
167*f585d8a3SJacky Wang     Bar bar2 = EntryPoints.get(subcomponent, BarEntryPoint.class).bar();
168*f585d8a3SJacky Wang 
169*f585d8a3SJacky Wang     // Test that early subcomponents can use EntryPoints or EarlyEntryPoints
170*f585d8a3SJacky Wang     Bar earlyBar1 = EntryPoints.get(earlySubcomponent, BarEntryPoint.class).bar();
171*f585d8a3SJacky Wang     Bar earlyBar2 = EntryPoints.get(earlySubcomponent, BarEntryPoint.class).bar();
172*f585d8a3SJacky Wang 
173*f585d8a3SJacky Wang     assertThat(bar1).isNotNull();
174*f585d8a3SJacky Wang     assertThat(bar2).isNotNull();
175*f585d8a3SJacky Wang     assertThat(earlyBar1).isNotNull();
176*f585d8a3SJacky Wang     assertThat(earlyBar2).isNotNull();
177*f585d8a3SJacky Wang     assertThat(bar1).isEqualTo(bar2);
178*f585d8a3SJacky Wang     assertThat(earlyBar1).isEqualTo(earlyBar2);
179*f585d8a3SJacky Wang     assertThat(bar1).isNotEqualTo(earlyBar1);
180*f585d8a3SJacky Wang   }
181*f585d8a3SJacky Wang }
182