1 /* 2 * Copyright (C) 2021 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 com.google.common.truth.Truth.assertThat; 20 import static java.lang.annotation.RetentionPolicy.CLASS; 21 22 import android.content.Context; 23 import android.os.Build; 24 import androidx.activity.ComponentActivity; 25 import androidx.test.core.app.ActivityScenario; 26 import androidx.test.ext.junit.runners.AndroidJUnit4; 27 import dagger.Module; 28 import dagger.Provides; 29 import dagger.hilt.DefineComponent; 30 import dagger.hilt.EntryPoint; 31 import dagger.hilt.EntryPoints; 32 import dagger.hilt.InstallIn; 33 import dagger.hilt.android.components.ActivityComponent; 34 import dagger.hilt.android.qualifiers.ApplicationContext; 35 import dagger.hilt.android.scopes.ActivityScoped; 36 import dagger.hilt.android.testing.HiltAndroidRule; 37 import dagger.hilt.android.testing.HiltAndroidTest; 38 import dagger.hilt.android.testing.HiltTestApplication; 39 import dagger.hilt.components.SingletonComponent; 40 import dagger.hilt.migration.AliasOf; 41 import java.lang.annotation.ElementType; 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.Target; 44 import javax.inject.Inject; 45 import javax.inject.Provider; 46 import javax.inject.Scope; 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.robolectric.annotation.Config; 52 53 @HiltAndroidTest 54 @RunWith(AndroidJUnit4.class) 55 @Config(sdk = Build.VERSION_CODES.P, application = HiltTestApplication.class) 56 public final class AliasOfMultipleScopesTest { 57 58 @Rule public final HiltAndroidRule rule = new HiltAndroidRule(this); 59 60 @Inject @ApplicationContext Context context; 61 @Inject CustomComponent.Builder customComponentBuilder; 62 63 @Scope 64 @Retention(CLASS) 65 @Target({ElementType.METHOD, ElementType.TYPE}) 66 public @interface CustomScoped {} 67 68 @DefineComponent(parent = SingletonComponent.class) 69 @CustomScoped 70 public interface CustomComponent { 71 @DefineComponent.Builder 72 public interface Builder { build()73 CustomComponent build(); 74 } 75 } 76 77 @Scope 78 @AliasOf({ActivityScoped.class, CustomScoped.class}) 79 public @interface AliasScoped {} 80 81 public interface UnscopedDep {} 82 83 public interface ActivityScopedDep {} 84 85 public interface CustomScopedDep {} 86 87 public interface AliasScopedDep {} 88 89 @Module 90 @InstallIn(SingletonComponent.class) 91 interface SingletonTestModule { 92 @Provides unscopedDep()93 static UnscopedDep unscopedDep() { 94 return new UnscopedDep() {}; 95 } 96 } 97 98 @Module 99 @InstallIn(ActivityComponent.class) 100 interface ActivityTestModule { 101 @Provides 102 @ActivityScoped activityScopedDep()103 static ActivityScopedDep activityScopedDep() { 104 return new ActivityScopedDep() {}; 105 } 106 107 @Provides 108 @AliasScoped aliasScopedDep()109 static AliasScopedDep aliasScopedDep() { 110 return new AliasScopedDep() {}; 111 } 112 } 113 114 @Module 115 @InstallIn(CustomComponent.class) 116 interface CustomTestModule { 117 @Provides 118 @CustomScoped customScopedDep()119 static CustomScopedDep customScopedDep() { 120 return new CustomScopedDep() {}; 121 } 122 123 @Provides 124 @AliasScoped aliasScopedDep()125 static AliasScopedDep aliasScopedDep() { 126 return new AliasScopedDep() {}; 127 } 128 } 129 130 /** An activity to test injection. */ 131 @AndroidEntryPoint(ComponentActivity.class) 132 public static final class TestActivity extends Hilt_AliasOfMultipleScopesTest_TestActivity { 133 @Inject Provider<UnscopedDep> unscopedDep; 134 @Inject Provider<ActivityScopedDep> activityScopedDep; 135 @Inject Provider<AliasScopedDep> aliasScopedDep; 136 } 137 138 @EntryPoint 139 @InstallIn(SingletonComponent.class) 140 interface CustomComponentBuilderEntryPoint { customComponentBuilder()141 CustomComponent.Builder customComponentBuilder(); 142 } 143 144 @EntryPoint 145 @InstallIn(CustomComponent.class) 146 interface CustomComponentEntryPoint { unscopedDep()147 Provider<UnscopedDep> unscopedDep(); 148 customScopedDep()149 Provider<CustomScopedDep> customScopedDep(); 150 aliasScopedDep()151 Provider<AliasScopedDep> aliasScopedDep(); 152 } 153 154 @Before setUp()155 public void setUp() { 156 rule.inject(); 157 } 158 159 @Test testActivityScoped()160 public void testActivityScoped() { 161 try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) { 162 scenario.onActivity( 163 activity -> { 164 assertThat(activity.unscopedDep.get()).isNotSameInstanceAs(activity.unscopedDep.get()); 165 assertThat(activity.activityScopedDep.get()) 166 .isSameInstanceAs(activity.activityScopedDep.get()); 167 assertThat(activity.aliasScopedDep.get()) 168 .isSameInstanceAs(activity.aliasScopedDep.get()); 169 }); 170 } 171 } 172 173 @Test testCustomScoped()174 public void testCustomScoped() { 175 CustomComponent customComponent = 176 EntryPoints.get(context, CustomComponentBuilderEntryPoint.class) 177 .customComponentBuilder() 178 .build(); 179 CustomComponentEntryPoint entryPoint = 180 EntryPoints.get(customComponent, CustomComponentEntryPoint.class); 181 assertThat(entryPoint.unscopedDep().get()).isNotSameInstanceAs(entryPoint.unscopedDep().get()); 182 assertThat(entryPoint.customScopedDep().get()) 183 .isSameInstanceAs(entryPoint.customScopedDep().get()); 184 assertThat(entryPoint.aliasScopedDep().get()) 185 .isSameInstanceAs(entryPoint.aliasScopedDep().get()); 186 } 187 } 188