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; 18 19 import static androidx.test.core.app.ApplicationProvider.getApplicationContext; 20 import static com.google.common.truth.Truth.assertThat; 21 import static org.junit.Assert.assertThrows; 22 23 import android.os.Build; 24 import androidx.activity.ComponentActivity; 25 import androidx.test.ext.junit.runners.AndroidJUnit4; 26 import dagger.Module; 27 import dagger.Provides; 28 import dagger.hilt.EntryPoint; 29 import dagger.hilt.EntryPoints; 30 import dagger.hilt.InstallIn; 31 import dagger.hilt.android.components.ActivityComponent; 32 import dagger.hilt.android.testing.BindValue; 33 import dagger.hilt.android.testing.HiltAndroidRule; 34 import dagger.hilt.android.testing.HiltAndroidTest; 35 import dagger.hilt.android.testing.HiltTestApplication; 36 import dagger.hilt.android.testing.UninstallModules; 37 import dagger.hilt.components.SingletonComponent; 38 import javax.inject.Inject; 39 import javax.inject.Named; 40 import org.junit.Rule; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 import org.robolectric.Robolectric; 44 import org.robolectric.android.controller.ActivityController; 45 import org.robolectric.annotation.Config; 46 47 // TODO(bcorso): Support transitively ignoring the @Module.includes of ignored modules? 48 // TODO(bcorso): Support including non-test @UninstallModules using @UninstallModules.includes? 49 @UninstallModules({ 50 MultiTestRootExternalModules.PkgPrivateAppModule.class, 51 MultiTestRootExternalModules.PkgPrivateActivityModule.class 52 }) 53 @HiltAndroidTest 54 @RunWith(AndroidJUnit4.class) 55 // Robolectric requires Java9 to run API 29 and above, so use API 28 instead 56 @Config(sdk = Build.VERSION_CODES.P, application = HiltTestApplication.class) 57 public final class MultiTestRoot1Test { 58 private static final int INT_VALUE = 9; 59 private static final String STR_VALUE = "MultiTestRoot1TestValue"; 60 private static final long LONG_VALUE = 11L; 61 private static final String REPLACE_EXTERNAL_STR_VALUE = "REPLACED_EXTERNAL_STR_VALUE"; 62 private static final long REPLACE_EXTERNAL_LONG_VALUE = 17L; 63 private static final String BIND_VALUE_STRING = "BIND_VALUE_STRING"; 64 private static final String TEST_QUALIFIER = "TEST_QUALIFIER"; 65 66 @AndroidEntryPoint(ComponentActivity.class) 67 public static class TestActivity extends Hilt_MultiTestRoot1Test_TestActivity { 68 @Inject Baz baz; 69 @Inject @MultiTestRootExternalModules.External Long externalLongValue; 70 } 71 72 @EntryPoint 73 @InstallIn(SingletonComponent.class) 74 public interface BindValueEntryPoint { 75 @Named(TEST_QUALIFIER) bindValueString()76 String bindValueString(); 77 } 78 79 @Module 80 @InstallIn(SingletonComponent.class) 81 public interface ReplaceExternalAppModule { 82 @Provides 83 @MultiTestRootExternalModules.External provideString()84 static String provideString() { 85 return REPLACE_EXTERNAL_STR_VALUE; 86 } 87 } 88 89 @Module 90 @InstallIn(ActivityComponent.class) 91 public interface ReplaceExternalActivityModule { 92 @Provides 93 @MultiTestRootExternalModules.External provideString()94 static Long provideString() { 95 return REPLACE_EXTERNAL_LONG_VALUE; 96 } 97 } 98 99 @Module 100 @InstallIn(ActivityComponent.class) 101 public interface TestActivityModule { 102 @Provides provideBaz()103 static Baz provideBaz() { 104 return new Baz(LONG_VALUE); 105 } 106 } 107 108 @Module 109 @InstallIn(SingletonComponent.class) 110 interface PkgPrivateTestModule { 111 @Provides provideQux()112 static Qux provideQux() { 113 return new Qux(); 114 } 115 } 116 117 @Module 118 @InstallIn(SingletonComponent.class) 119 public interface TestModule { 120 @Provides provideInt()121 static int provideInt() { 122 return INT_VALUE; 123 } 124 125 @Provides provideString()126 static String provideString() { 127 return STR_VALUE; 128 } 129 } 130 131 public static final class Outer { 132 @Module 133 @InstallIn(SingletonComponent.class) 134 public interface NestedTestModule { 135 @Provides provideLong()136 static long provideLong() { 137 return LONG_VALUE; 138 } 139 } 140 Outer()141 private Outer() {} 142 } 143 144 static class Foo { 145 final int value; 146 147 @Inject Foo(int value)148 Foo(int value) { 149 this.value = value; 150 } 151 } 152 153 // Must be public due to b/183636779 154 public static class Bar { 155 final String value; 156 Bar(String value)157 Bar(String value) { 158 this.value = value; 159 } 160 } 161 162 static class Baz { 163 final long value; 164 Baz(long value)165 Baz(long value) { 166 this.value = value; 167 } 168 } 169 170 // Must be public due to b/183636779 171 public static class Qux {} 172 173 @Module 174 @InstallIn(SingletonComponent.class) 175 public interface BarModule { 176 @Provides provideBar(String value)177 static Bar provideBar(String value) { 178 return new Bar(value); 179 } 180 } 181 182 @EntryPoint 183 @InstallIn(SingletonComponent.class) 184 public interface BarEntryPoint { getBar()185 Bar getBar(); 186 } 187 188 @EntryPoint 189 @InstallIn(SingletonComponent.class) 190 interface PkgPrivateQuxEntryPoint { getQux()191 Qux getQux(); 192 } 193 194 @Rule public HiltAndroidRule rule = new HiltAndroidRule(this); 195 196 @Inject Foo foo; 197 @Inject Qux qux; 198 @Inject Long longValue; 199 @Inject @MultiTestRootExternalModules.External String externalStrValue; 200 201 @BindValue 202 @Named(TEST_QUALIFIER) 203 String bindValueString = BIND_VALUE_STRING; 204 205 @Test testInjectFromTestModule()206 public void testInjectFromTestModule() throws Exception { 207 assertThat(foo).isNull(); 208 setupComponent(); 209 assertThat(foo).isNotNull(); 210 assertThat(foo.value).isEqualTo(INT_VALUE); 211 } 212 213 @Test testInjectFromNestedTestModule()214 public void testInjectFromNestedTestModule() throws Exception { 215 assertThat(longValue).isNull(); 216 setupComponent(); 217 assertThat(longValue).isNotNull(); 218 assertThat(longValue).isEqualTo(LONG_VALUE); 219 } 220 221 @Test testInjectFromExternalAppModule()222 public void testInjectFromExternalAppModule() throws Exception { 223 assertThat(externalStrValue).isNull(); 224 setupComponent(); 225 assertThat(externalStrValue).isNotNull(); 226 assertThat(externalStrValue).isEqualTo(REPLACE_EXTERNAL_STR_VALUE); 227 assertThat(externalStrValue).isNotEqualTo(MultiTestRootExternalModules.EXTERNAL_STR_VALUE); 228 } 229 230 @Test testInjectFromExternalActivityModule()231 public void testInjectFromExternalActivityModule() throws Exception { 232 setupComponent(); 233 ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class); 234 assertThat(ac.get().externalLongValue).isNull(); 235 ac.create(); 236 assertThat(ac.get().externalLongValue).isNotNull(); 237 assertThat(ac.get().externalLongValue).isEqualTo(REPLACE_EXTERNAL_LONG_VALUE); 238 assertThat(ac.get().externalLongValue) 239 .isNotEqualTo(MultiTestRootExternalModules.EXTERNAL_LONG_VALUE); 240 } 241 242 @Test testInjectFromPkgPrivateTestModule()243 public void testInjectFromPkgPrivateTestModule() throws Exception { 244 assertThat(qux).isNull(); 245 setupComponent(); 246 assertThat(qux).isNotNull(); 247 } 248 249 @Test testLocalEntryPoint()250 public void testLocalEntryPoint() throws Exception { 251 setupComponent(); 252 Bar bar = EntryPoints.get(getApplicationContext(), BarEntryPoint.class).getBar(); 253 assertThat(bar).isNotNull(); 254 assertThat(bar.value).isEqualTo(STR_VALUE); 255 } 256 257 @Test testLocalPkgPrivateEntryPoint()258 public void testLocalPkgPrivateEntryPoint() throws Exception { 259 setupComponent(); 260 Qux qux = EntryPoints.get(getApplicationContext(), PkgPrivateQuxEntryPoint.class).getQux(); 261 assertThat(qux).isNotNull(); 262 } 263 264 @Test testAndroidEntryPoint()265 public void testAndroidEntryPoint() throws Exception { 266 setupComponent(); 267 ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class); 268 assertThat(ac.get().baz).isNull(); 269 ac.create(); 270 assertThat(ac.get().baz).isNotNull(); 271 assertThat(ac.get().baz.value).isEqualTo(LONG_VALUE); 272 } 273 274 @Test testMissingMultiTestRoot2EntryPoint()275 public void testMissingMultiTestRoot2EntryPoint() throws Exception { 276 setupComponent(); 277 ClassCastException exception = 278 assertThrows( 279 ClassCastException.class, 280 () -> EntryPoints.get(getApplicationContext(), MultiTestRoot2Test.BarEntryPoint.class)); 281 assertThat(exception) 282 .hasMessageThat() 283 .isEqualTo( 284 "Cannot cast dagger.hilt.android.internal.testing.root." 285 + "DaggerMultiTestRoot1Test_HiltComponents_SingletonC$SingletonCImpl" 286 + " to dagger.hilt.android.MultiTestRoot2Test$BarEntryPoint"); 287 } 288 289 @Test testBindValueFieldIsProvided()290 public void testBindValueFieldIsProvided() throws Exception { 291 setupComponent(); 292 assertThat(bindValueString).isEqualTo(BIND_VALUE_STRING); 293 assertThat(getBinding()).isEqualTo(BIND_VALUE_STRING); 294 } 295 296 @Test testBindValueIsMutable()297 public void testBindValueIsMutable() throws Exception { 298 setupComponent(); 299 bindValueString = "newValue"; 300 assertThat(getBinding()).isEqualTo("newValue"); 301 } 302 setupComponent()303 void setupComponent() { 304 rule.inject(); 305 } 306 getBinding()307 private static String getBinding() { 308 return EntryPoints.get(getApplicationContext(), BindValueEntryPoint.class).bindValueString(); 309 } 310 } 311