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 static class Bar { 154 final String value; 155 Bar(String value)156 Bar(String value) { 157 this.value = value; 158 } 159 } 160 161 static class Baz { 162 final long value; 163 Baz(long value)164 Baz(long value) { 165 this.value = value; 166 } 167 } 168 169 static class Qux {} 170 171 @Module 172 @InstallIn(SingletonComponent.class) 173 public interface BarModule { 174 @Provides provideBar(String value)175 static Bar provideBar(String value) { 176 return new Bar(value); 177 } 178 } 179 180 @EntryPoint 181 @InstallIn(SingletonComponent.class) 182 public interface BarEntryPoint { getBar()183 Bar getBar(); 184 } 185 186 @EntryPoint 187 @InstallIn(SingletonComponent.class) 188 interface PkgPrivateQuxEntryPoint { getQux()189 Qux getQux(); 190 } 191 192 @Rule public HiltAndroidRule rule = new HiltAndroidRule(this); 193 194 @Inject Foo foo; 195 @Inject Qux qux; 196 @Inject Long longValue; 197 @Inject @MultiTestRootExternalModules.External String externalStrValue; 198 199 @BindValue 200 @Named(TEST_QUALIFIER) 201 String bindValueString = BIND_VALUE_STRING; 202 203 @Test testInjectFromTestModule()204 public void testInjectFromTestModule() throws Exception { 205 assertThat(foo).isNull(); 206 setupComponent(); 207 assertThat(foo).isNotNull(); 208 assertThat(foo.value).isEqualTo(INT_VALUE); 209 } 210 211 @Test testInjectFromNestedTestModule()212 public void testInjectFromNestedTestModule() throws Exception { 213 assertThat(longValue).isNull(); 214 setupComponent(); 215 assertThat(longValue).isNotNull(); 216 assertThat(longValue).isEqualTo(LONG_VALUE); 217 } 218 219 @Test testInjectFromExternalAppModule()220 public void testInjectFromExternalAppModule() throws Exception { 221 assertThat(externalStrValue).isNull(); 222 setupComponent(); 223 assertThat(externalStrValue).isNotNull(); 224 assertThat(externalStrValue).isEqualTo(REPLACE_EXTERNAL_STR_VALUE); 225 assertThat(externalStrValue).isNotEqualTo(MultiTestRootExternalModules.EXTERNAL_STR_VALUE); 226 } 227 228 @Test testInjectFromExternalActivityModule()229 public void testInjectFromExternalActivityModule() throws Exception { 230 setupComponent(); 231 ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class); 232 assertThat(ac.get().externalLongValue).isNull(); 233 ac.create(); 234 assertThat(ac.get().externalLongValue).isNotNull(); 235 assertThat(ac.get().externalLongValue).isEqualTo(REPLACE_EXTERNAL_LONG_VALUE); 236 assertThat(ac.get().externalLongValue) 237 .isNotEqualTo(MultiTestRootExternalModules.EXTERNAL_LONG_VALUE); 238 } 239 240 @Test testInjectFromPkgPrivateTestModule()241 public void testInjectFromPkgPrivateTestModule() throws Exception { 242 assertThat(qux).isNull(); 243 setupComponent(); 244 assertThat(qux).isNotNull(); 245 } 246 247 @Test testLocalEntryPoint()248 public void testLocalEntryPoint() throws Exception { 249 setupComponent(); 250 Bar bar = EntryPoints.get(getApplicationContext(), BarEntryPoint.class).getBar(); 251 assertThat(bar).isNotNull(); 252 assertThat(bar.value).isEqualTo(STR_VALUE); 253 } 254 255 @Test testLocalPkgPrivateEntryPoint()256 public void testLocalPkgPrivateEntryPoint() throws Exception { 257 setupComponent(); 258 Qux qux = EntryPoints.get(getApplicationContext(), PkgPrivateQuxEntryPoint.class).getQux(); 259 assertThat(qux).isNotNull(); 260 } 261 262 @Test testAndroidEntryPoint()263 public void testAndroidEntryPoint() throws Exception { 264 setupComponent(); 265 ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class); 266 assertThat(ac.get().baz).isNull(); 267 ac.create(); 268 assertThat(ac.get().baz).isNotNull(); 269 assertThat(ac.get().baz.value).isEqualTo(LONG_VALUE); 270 } 271 272 @Test testMissingMultiTestRoot2EntryPoint()273 public void testMissingMultiTestRoot2EntryPoint() throws Exception { 274 setupComponent(); 275 ClassCastException exception = 276 assertThrows( 277 ClassCastException.class, 278 () -> EntryPoints.get(getApplicationContext(), MultiTestRoot2Test.BarEntryPoint.class)); 279 assertThat(exception) 280 .hasMessageThat() 281 .isEqualTo( 282 "Cannot cast dagger.hilt.android.DaggerMultiTestRoot1Test_HiltComponents_SingletonC" 283 + " to dagger.hilt.android.MultiTestRoot2Test$BarEntryPoint"); 284 } 285 286 @Test testBindValueFieldIsProvided()287 public void testBindValueFieldIsProvided() throws Exception { 288 setupComponent(); 289 assertThat(bindValueString).isEqualTo(BIND_VALUE_STRING); 290 assertThat(getBinding()).isEqualTo(BIND_VALUE_STRING); 291 } 292 293 @Test testBindValueIsMutable()294 public void testBindValueIsMutable() throws Exception { 295 setupComponent(); 296 bindValueString = "newValue"; 297 assertThat(getBinding()).isEqualTo("newValue"); 298 } 299 setupComponent()300 void setupComponent() { 301 rule.inject(); 302 } 303 getBinding()304 private static String getBinding() { 305 return EntryPoints.get(getApplicationContext(), BindValueEntryPoint.class).bindValueString(); 306 } 307 } 308