1 /*
2  * Copyright (C) 2014 The Android Open Source Project
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 com.android.cts.isolatedsplitapp;
18 
19 import static org.hamcrest.CoreMatchers.*;
20 import static org.junit.Assert.*;
21 
22 import android.app.Activity;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.res.Configuration;
28 import android.content.res.Resources;
29 import android.os.Bundle;
30 
31 import androidx.test.InstrumentationRegistry;
32 import androidx.test.rule.ActivityTestRule;
33 import androidx.test.runner.AndroidJUnit4;
34 
35 import org.junit.Rule;
36 import org.junit.Test;
37 import org.junit.rules.TestRule;
38 import org.junit.runner.Description;
39 import org.junit.runner.RunWith;
40 import org.junit.runners.model.Statement;
41 
42 import java.util.Locale;
43 import java.util.concurrent.CompletableFuture;
44 import java.util.concurrent.TimeUnit;
45 
46 @RunWith(AndroidJUnit4.class)
47 public class SplitAppTest {
48     private static final String PACKAGE = "com.android.cts.isolatedsplitapp";
49 
50     private static final ComponentName FEATURE_A_ACTIVITY =
51             ComponentName.createRelative(PACKAGE, ".feature_a.FeatureAActivity");
52     private static final ComponentName FEATURE_B_ACTIVITY =
53             ComponentName.createRelative(PACKAGE, ".feature_b.FeatureBActivity");
54     private static final ComponentName FEATURE_C_ACTIVITY =
55             ComponentName.createRelative(PACKAGE, ".feature_c.FeatureCActivity");
56 
57     private static final String FEATURE_A_STRING =
58             "com.android.cts.isolatedsplitapp.feature_a:string/feature_a_string";
59     private static final String FEATURE_B_STRING =
60             "com.android.cts.isolatedsplitapp.feature_b:string/feature_b_string";
61     private static final String FEATURE_C_STRING =
62             "com.android.cts.isolatedsplitapp.feature_c:string/feature_c_string";
63 
64     private static final Configuration PL = new Configuration();
65     static {
66         PL.setLocale(Locale.forLanguageTag("pl"));
67     }
68 
69     @Rule
70     public ActivityTestRule<BaseActivity> mBaseActivityRule =
71             new ActivityTestRule<>(BaseActivity.class);
72 
73     // Do not launch this activity lazily. We use this rule to launch all feature Activities,
74     // so we use #launchActivity() with the correct Intent.
75     @Rule
76     public ActivityTestRule<Activity> mFeatureActivityRule =
77             new ActivityTestRule<>(Activity.class, true /*initialTouchMode*/,
78                     false /*launchActivity*/);
79 
80     @Rule
81     public AppContextTestRule mAppContextTestRule = new AppContextTestRule();
82 
83     @Test
shouldLoadDefault()84     public void shouldLoadDefault() throws Exception {
85         final Context context = mBaseActivityRule.getActivity();
86         final Resources resources = context.getResources();
87         assertThat(resources, notNullValue());
88 
89         assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
90 
91         // The base does not depend on any splits so no splits should be accessible.
92         assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY,
93                 FEATURE_C_ACTIVITY);
94         assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING, FEATURE_C_STRING);
95     }
96 
97     @Test
shouldLoadPolishLocale()98     public void shouldLoadPolishLocale() throws Exception {
99         final Context context = mBaseActivityRule.getActivity().createConfigurationContext(PL);
100         final Resources resources = context.getResources();
101         assertThat(resources, notNullValue());
102 
103         assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
104 
105         // The base does not depend on any splits so no splits should be accessible.
106         assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY,
107                 FEATURE_C_ACTIVITY);
108         assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING, FEATURE_C_STRING);
109     }
110 
111     @Test
shouldLoadFeatureADefault()112     public void shouldLoadFeatureADefault() throws Exception {
113         final Context context = mFeatureActivityRule.launchActivity(
114                 new Intent().setComponent(FEATURE_A_ACTIVITY));
115         final Resources resources = context.getResources();
116         assertThat(resources, notNullValue());
117 
118         assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
119 
120         int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
121         assertThat(resources.getString(resourceId), equalTo("Feature A String Default"));
122 
123         assertActivitiesDoNotExist(context, FEATURE_B_ACTIVITY, FEATURE_C_ACTIVITY);
124         assertResourcesDoNotExist(context, FEATURE_B_STRING, FEATURE_C_STRING);
125     }
126 
127     @Test
shouldLoadFeatureAPolishLocale()128     public void shouldLoadFeatureAPolishLocale() throws Exception {
129         final Context context = mFeatureActivityRule.launchActivity(
130                 new Intent().setComponent(FEATURE_A_ACTIVITY)).createConfigurationContext(PL);
131         final Resources resources = context.getResources();
132         assertThat(resources, notNullValue());
133 
134         assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
135 
136         int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
137         assertThat(resources.getString(resourceId), equalTo("Feature A String Polish"));
138 
139         assertActivitiesDoNotExist(context, FEATURE_B_ACTIVITY, FEATURE_C_ACTIVITY);
140         assertResourcesDoNotExist(context, FEATURE_B_STRING, FEATURE_C_STRING);
141     }
142 
143     @Test
shouldLoadFeatureAReceivers()144     public void shouldLoadFeatureAReceivers() throws Exception {
145         final Context context = mAppContextTestRule.getContext();
146         final ExtrasResultReceiver receiver = sendOrderedBroadcast(context);
147         final Bundle results = receiver.get();
148         assertThat(results.getString("base"), equalTo("Base String Default"));
149         assertThat(results.getString("feature_a"), equalTo("Feature A String Default"));
150         assertThat(results.getString("feature_b"), nullValue());
151         assertThat(results.getString("feature_c"), nullValue());
152     }
153 
154     @Test
shouldLoadFeatureBDefault()155     public void shouldLoadFeatureBDefault() throws Exception {
156         // Feature B depends on A, so we expect both to be available.
157         final Context context = mFeatureActivityRule.launchActivity(
158                 new Intent().setComponent(FEATURE_B_ACTIVITY));
159         final Resources resources = context.getResources();
160         assertThat(resources, notNullValue());
161 
162         assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
163 
164         int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
165         assertThat(resources.getString(resourceId), equalTo("Feature A String Default"));
166 
167         resourceId = resources.getIdentifier(FEATURE_B_STRING, null, null);
168         assertThat(resources.getString(resourceId), equalTo("Feature B String Default"));
169 
170         assertActivitiesDoNotExist(context, FEATURE_C_ACTIVITY);
171         assertResourcesDoNotExist(context, FEATURE_C_STRING);
172     }
173 
174     @Test
shouldLoadFeatureBPolishLocale()175     public void shouldLoadFeatureBPolishLocale() throws Exception {
176         final Context context = mFeatureActivityRule.launchActivity(
177                 new Intent().setComponent(FEATURE_B_ACTIVITY)).createConfigurationContext(PL);
178         final Resources resources = context.getResources();
179         assertThat(resources, notNullValue());
180 
181         assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
182 
183         int resourceId = resources.getIdentifier(FEATURE_A_STRING, null, null);
184         assertThat(resources.getString(resourceId), equalTo("Feature A String Polish"));
185 
186         resourceId = resources.getIdentifier(FEATURE_B_STRING, null, null);
187         assertThat(resources.getString(resourceId), equalTo("Feature B String Polish"));
188 
189         assertActivitiesDoNotExist(context, FEATURE_C_ACTIVITY);
190         assertResourcesDoNotExist(context, FEATURE_C_STRING);
191     }
192 
193     @Test
shouldLoadFeatureAAndBReceivers()194     public void shouldLoadFeatureAAndBReceivers() throws Exception {
195         final Context context = mAppContextTestRule.getContext();
196         final ExtrasResultReceiver receiver = sendOrderedBroadcast(context);
197         final Bundle results = receiver.get();
198         assertThat(results.getString("base"), equalTo("Base String Default"));
199         assertThat(results.getString("feature_a"), equalTo("Feature A String Default"));
200         assertThat(results.getString("feature_b"), equalTo("Feature B String Default"));
201         assertThat(results.getString("feature_c"), nullValue());
202     }
203 
204     @Test
shouldLoadFeatureCDefault()205     public void shouldLoadFeatureCDefault() throws Exception {
206         final Context context = mFeatureActivityRule.launchActivity(
207                 new Intent().setComponent(FEATURE_C_ACTIVITY));
208         final Resources resources = context.getResources();
209         assertThat(resources, notNullValue());
210 
211         assertThat(resources.getString(R.string.base_string), equalTo("Base String Default"));
212 
213         int resourceId = resources.getIdentifier(FEATURE_C_STRING, null, null);
214         assertThat(resources.getString(resourceId), equalTo("Feature C String Default"));
215 
216         assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY);
217         assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING);
218     }
219 
220     @Test
shouldLoadFeatureCPolishLocale()221     public void shouldLoadFeatureCPolishLocale() throws Exception {
222         final Context context = mFeatureActivityRule.launchActivity(
223                 new Intent().setComponent(FEATURE_C_ACTIVITY)).createConfigurationContext(PL);
224         final Resources resources = context.getResources();
225         assertThat(resources, notNullValue());
226 
227         assertThat(resources.getString(R.string.base_string), equalTo("Base String Polish"));
228 
229         int resourceId = resources.getIdentifier(FEATURE_C_STRING, null, null);
230         assertThat(resources.getString(resourceId), equalTo("Feature C String Polish"));
231 
232         assertActivitiesDoNotExist(context, FEATURE_A_ACTIVITY, FEATURE_B_ACTIVITY);
233         assertResourcesDoNotExist(context, FEATURE_A_STRING, FEATURE_B_STRING);
234     }
235 
236     @Test
shouldLoadFeatureAAndBAndCReceivers()237     public void shouldLoadFeatureAAndBAndCReceivers() throws Exception {
238         final Context context = mAppContextTestRule.getContext();
239         final ExtrasResultReceiver receiver = sendOrderedBroadcast(context);
240         final Bundle results = receiver.get();
241         assertThat(results.getString("base"), equalTo("Base String Default"));
242         assertThat(results.getString("feature_a"), equalTo("Feature A String Default"));
243         assertThat(results.getString("feature_b"), equalTo("Feature B String Default"));
244         assertThat(results.getString("feature_c"), equalTo("Feature C String Default"));
245     }
246 
assertActivitiesDoNotExist(Context context, ComponentName... activities)247     private static void assertActivitiesDoNotExist(Context context, ComponentName... activities) {
248         for (ComponentName activity : activities) {
249             try {
250                 Class.forName(activity.getClassName(), true, context.getClassLoader());
251                 fail("Class " + activity.getClassName() + " is accessible");
252             } catch (ClassNotFoundException e) {
253                 // Pass.
254             }
255         }
256     }
257 
assertResourcesDoNotExist(Context context, String... resourceNames)258     private static void assertResourcesDoNotExist(Context context, String... resourceNames) {
259         final Resources resources = context.getResources();
260         for (String resourceName : resourceNames) {
261             final int resid = resources.getIdentifier(resourceName, null, null);
262             if (resid != 0) {
263                 fail("Found resource '" + resourceName + "' with ID " + Integer.toHexString(resid));
264             }
265         }
266     }
267 
268     private static class ExtrasResultReceiver extends BroadcastReceiver {
269         private final CompletableFuture<Bundle> mResult = new CompletableFuture<>();
270 
271         @Override
onReceive(Context context, Intent intent)272         public void onReceive(Context context, Intent intent) {
273             mResult.complete(getResultExtras(true));
274         }
275 
get()276         public Bundle get() throws Exception {
277             return mResult.get(5000, TimeUnit.SECONDS);
278         }
279     }
280 
sendOrderedBroadcast(Context context)281     private static ExtrasResultReceiver sendOrderedBroadcast(Context context) {
282         final ExtrasResultReceiver resultReceiver = new ExtrasResultReceiver();
283         context.sendOrderedBroadcast(new Intent(PACKAGE + ".ACTION").setPackage(PACKAGE), null,
284                 resultReceiver, null, 0, null, null);
285         return resultReceiver;
286     }
287 
288     private static class AppContextTestRule implements TestRule {
289         private Context mContext;
290 
291         @Override
apply(final Statement base, Description description)292         public Statement apply(final Statement base, Description description) {
293             return new Statement() {
294                 @Override
295                 public void evaluate() throws Throwable {
296                     mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
297                     base.evaluate();
298                 }
299             };
300         }
301 
getContext()302         public Context getContext() {
303             return mContext;
304         }
305     }
306 }
307