Merge "Add unit tests for slice purchase app"

This commit is contained in:
Sarah Chin
2022-11-14 21:07:14 +00:00
committed by Android (Google) Code Review
6 changed files with 412 additions and 6 deletions

View File

@@ -34,7 +34,8 @@
android:label="@string/app_name"
android:directBootAware="true"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher_android">
android:icon="@mipmap/ic_launcher_android"
android:debuggable="true">
<receiver android:name="com.android.carrierdefaultapp.CarrierDefaultBroadcastReceiver"
android:exported="true">
<intent-filter>

View File

@@ -58,8 +58,8 @@ import java.util.concurrent.TimeUnit;
public class SlicePurchaseActivity extends Activity {
private static final String TAG = "SlicePurchaseActivity";
private @NonNull WebView mWebView;
private @NonNull Context mApplicationContext;
@NonNull private WebView mWebView;
@NonNull private Context mApplicationContext;
private int mSubId;
@TelephonyManager.PremiumCapability protected int mCapability;

View File

@@ -33,6 +33,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.webkit.WebView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.phone.slice.SlicePurchaseController;
import java.lang.ref.WeakReference;
@@ -291,7 +292,8 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
*
* @return The intent to start {@link SlicePurchaseActivity}.
*/
@NonNull private PendingIntent createContentIntent(@NonNull Context context,
@VisibleForTesting
@NonNull public PendingIntent createContentIntent(@NonNull Context context,
@NonNull Intent intent, int requestCode) {
Intent i = new Intent(context, SlicePurchaseActivity.class);
i.setComponent(ComponentName.unflattenFromString(
@@ -314,7 +316,8 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
*
* @return The canceled intent.
*/
@NonNull private PendingIntent createCanceledIntent(@NonNull Context context,
@VisibleForTesting
@NonNull public PendingIntent createCanceledIntent(@NonNull Context context,
@NonNull Intent intent) {
Intent i = new Intent(ACTION_NOTIFICATION_CANCELED);
i.setComponent(ComponentName.unflattenFromString(

View File

@@ -27,11 +27,13 @@ android_test {
libs: [
"android.test.runner",
"android.test.base",
"SlicePurchaseController",
],
static_libs: [
"androidx.test.rules",
"mockito-target-minus-junit4",
"mockito-target-inline-minus-junit4",
],
jni_libs: ["libdexmakerjvmtiagent"],
// Include all test java files.
srcs: ["src/**/*.java"],
platform_apis: true,

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.carrierdefaultapp;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.test.ActivityUnitTestCase;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.phone.slice.SlicePurchaseController;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchaseActivity> {
private static final String TAG = "SlicePurchaseActivityTest";
private static final String URL = "file:///android_asset/slice_purchase_test.html";
private static final int PHONE_ID = 0;
@Mock PendingIntent mPendingIntent;
@Mock PendingIntent mCanceledIntent;
@Mock CarrierConfigManager mCarrierConfigManager;
@Mock NotificationManager mNotificationManager;
@Mock PersistableBundle mPersistableBundle;
private SlicePurchaseActivity mSlicePurchaseActivity;
private Context mContext;
public SlicePurchaseActivityTest() {
super(SlicePurchaseActivity.class);
}
@Before
public void setUp() throws Exception {
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
if (Looper.myLooper() == null) {
Looper.prepare();
}
super.setUp();
MockitoAnnotations.initMocks(this);
// setup context
mContext = spy(getInstrumentation().getTargetContext());
doReturn(mCarrierConfigManager).when(mContext)
.getSystemService(eq(CarrierConfigManager.class));
doReturn(URL).when(mPersistableBundle).getString(
CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING);
doReturn(mPersistableBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
doReturn(mNotificationManager).when(mContext)
.getSystemService(eq(NotificationManager.class));
doReturn(mContext).when(mContext).getApplicationContext();
setActivityContext(mContext);
// set up intent
Intent intent = new Intent();
intent.putExtra(SlicePurchaseController.EXTRA_PHONE_ID, PHONE_ID);
intent.putExtra(SlicePurchaseController.EXTRA_SUB_ID,
SubscriptionManager.getDefaultDataSubscriptionId());
intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
intent.putExtra(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME, TAG);
Intent spiedIntent = spy(intent);
// set up pending intents
doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mPendingIntent).getCreatorPackage();
doReturn(true).when(mPendingIntent).isBroadcast();
doReturn(mPendingIntent).when(spiedIntent).getParcelableExtra(
anyString(), eq(PendingIntent.class));
doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mCanceledIntent).getCreatorPackage();
doReturn(true).when(mCanceledIntent).isBroadcast();
doReturn(mCanceledIntent).when(spiedIntent).getParcelableExtra(
eq(SlicePurchaseController.EXTRA_INTENT_CANCELED), eq(PendingIntent.class));
mSlicePurchaseActivity = startActivity(spiedIntent, null, null);
}
@After
public void tearDown() throws Exception {
mSlicePurchaseActivity.onDestroy();
super.tearDown();
}
@Test
public void testOnPurchaseSuccessful() throws Exception {
int duration = 5 * 60 * 1000; // 5 minutes
int invalidDuration = -1;
mSlicePurchaseActivity.onPurchaseSuccessful(duration);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mPendingIntent).send(eq(mContext), eq(0), intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertEquals(duration, intent.getLongExtra(
SlicePurchaseController.EXTRA_PURCHASE_DURATION, invalidDuration));
}
@Test
public void testOnPurchaseFailed() throws Exception {
int failureCode = SlicePurchaseController.FAILURE_CODE_SERVER_UNREACHABLE;
String failureReason = "Server unreachable";
mSlicePurchaseActivity.onPurchaseFailed(failureCode, failureReason);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mPendingIntent).send(eq(mContext), eq(0), intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertEquals(failureCode, intent.getIntExtra(
SlicePurchaseController.EXTRA_FAILURE_CODE, failureCode));
assertEquals(failureReason, intent.getStringExtra(
SlicePurchaseController.EXTRA_FAILURE_REASON));
}
@Test
public void testOnUserCanceled() throws Exception {
mSlicePurchaseActivity.onDestroy();
verify(mCanceledIntent).send();
}
}

View File

@@ -0,0 +1,250 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.carrierdefaultapp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import androidx.test.runner.AndroidJUnit4;
import com.android.phone.slice.SlicePurchaseController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class SlicePurchaseBroadcastReceiverTest {
private static final int PHONE_ID = 0;
private static final String TAG = "SlicePurchaseBroadcastReceiverTest";
private static final String EXTRA = "EXTRA";
@Mock Intent mIntent;
@Mock Intent mDataIntent;
@Mock PendingIntent mPendingIntent;
@Mock PendingIntent mCanceledIntent;
@Mock PendingIntent mContentIntent1;
@Mock PendingIntent mContentIntent2;
@Mock Context mContext;
@Mock Resources mResources;
@Mock NotificationManager mNotificationManager;
@Mock ApplicationInfo mApplicationInfo;
@Mock PackageManager mPackageManager;
@Mock DisplayMetrics mDisplayMetrics;
@Mock SlicePurchaseActivity mSlicePurchaseActivity;
private SlicePurchaseBroadcastReceiver mSlicePurchaseBroadcastReceiver;
private ArgumentCaptor<Intent> mIntentCaptor;
private ArgumentCaptor<Notification> mNotificationCaptor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(mNotificationManager).when(mContext)
.getSystemService(eq(NotificationManager.class));
mIntentCaptor = ArgumentCaptor.forClass(Intent.class);
mNotificationCaptor = ArgumentCaptor.forClass(Notification.class);
mSlicePurchaseBroadcastReceiver = spy(new SlicePurchaseBroadcastReceiver());
}
@Test
public void testSendSlicePurchaseAppResponse() throws Exception {
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(mIntent, EXTRA);
verify(mPendingIntent, never()).send();
doReturn(mPendingIntent).when(mIntent).getParcelableExtra(
eq(EXTRA), eq(PendingIntent.class));
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(mIntent, EXTRA);
verify(mPendingIntent).send();
}
@Test
public void testSendSlicePurchaseAppResponseWithData() throws Exception {
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(
mContext, mIntent, EXTRA, mDataIntent);
verify(mPendingIntent, never()).send(eq(mContext), eq(0), any(Intent.class));
doReturn(mPendingIntent).when(mIntent).getParcelableExtra(
eq(EXTRA), eq(PendingIntent.class));
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(
mContext, mIntent, EXTRA, mDataIntent);
verify(mPendingIntent).send(eq(mContext), eq(0), mIntentCaptor.capture());
assertEquals(mDataIntent, mIntentCaptor.getValue());
}
@Test
public void testIsIntentValid() {
assertFalse(SlicePurchaseBroadcastReceiver.isIntentValid(mIntent));
// set up intent
doReturn(PHONE_ID).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt());
doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt());
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
doReturn(TAG).when(mIntent).getStringExtra(
eq(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME));
assertFalse(SlicePurchaseBroadcastReceiver.isIntentValid(mIntent));
// set up pending intent
doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mPendingIntent).getCreatorPackage();
doReturn(true).when(mPendingIntent).isBroadcast();
doReturn(mPendingIntent).when(mIntent).getParcelableExtra(
anyString(), eq(PendingIntent.class));
assertTrue(SlicePurchaseBroadcastReceiver.isIntentValid(mIntent));
}
@Test
public void testDisplayBoosterNotification() {
// set up intent
doReturn(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP).when(mIntent).getAction();
doReturn(PHONE_ID).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt());
doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt());
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
doReturn(TAG).when(mIntent).getStringExtra(
eq(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME));
// set up pending intent
doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mPendingIntent).getCreatorPackage();
doReturn(true).when(mPendingIntent).isBroadcast();
doReturn(mPendingIntent).when(mIntent).getParcelableExtra(
anyString(), eq(PendingIntent.class));
// set up notification
doReturn(mResources).when(mContext).getResources();
doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
doReturn("").when(mResources).getString(anyInt());
doReturn(mApplicationInfo).when(mContext).getApplicationInfo();
doReturn(mPackageManager).when(mContext).getPackageManager();
// set up intents created by broadcast receiver
doReturn(mContentIntent1).when(mSlicePurchaseBroadcastReceiver).createContentIntent(
eq(mContext), eq(mIntent), eq(1));
doReturn(mContentIntent2).when(mSlicePurchaseBroadcastReceiver).createContentIntent(
eq(mContext), eq(mIntent), eq(2));
doReturn(mCanceledIntent).when(mSlicePurchaseBroadcastReceiver).createCanceledIntent(
eq(mContext), eq(mIntent));
// send ACTION_START_SLICE_PURCHASE_APP
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify network boost notification was shown
verify(mNotificationManager).notifyAsUser(
eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG),
eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
mNotificationCaptor.capture(),
eq(UserHandle.ALL));
Notification notification = mNotificationCaptor.getValue();
assertEquals(mContentIntent1, notification.contentIntent);
assertEquals(mPendingIntent, notification.deleteIntent);
assertEquals(2, notification.actions.length);
assertEquals(mCanceledIntent, notification.actions[0].actionIntent);
assertEquals(mContentIntent2, notification.actions[1].actionIntent);
}
@Test
public void testNotificationCanceled() {
// set up intent
doReturn("com.android.phone.slice.action.NOTIFICATION_CANCELED").when(mIntent).getAction();
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
// send ACTION_NOTIFICATION_CANCELED
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify notification was canceled
verify(mNotificationManager).cancelAsUser(
eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG),
eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
eq(UserHandle.ALL));
}
@Test
public void testNotificationTimeout() {
// set up intent
doReturn(SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT).when(mIntent)
.getAction();
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
// send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify notification was canceled
verify(mNotificationManager).cancelAsUser(
eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG),
eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
eq(UserHandle.ALL));
}
@Test
// TODO: WebView/Activity should not close on timeout.
// This test should be removed once implementation is fixed.
public void testActivityTimeout() {
// create and track activity
SlicePurchaseBroadcastReceiver.updateSlicePurchaseActivity(
TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mSlicePurchaseActivity);
// set up intent
doReturn(SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT).when(mIntent)
.getAction();
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
// send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify activity was canceled
verify(mSlicePurchaseActivity).finishAndRemoveTask();
// untrack activity
SlicePurchaseBroadcastReceiver.removeSlicePurchaseActivity(
TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
}
}