Add unit tests for the app-locales backup logic.
Bug: 204986584 Test: atest FrameworksServicesTests:LocaleManagerBackupRestoreTest Change-Id: Iac73c15f9b28aeb3bfc3f7af381cd49df443194e
This commit is contained in:
@@ -77,15 +77,15 @@ public class LocaleManagerService extends SystemService {
|
||||
@VisibleForTesting
|
||||
LocaleManagerService(Context context, ActivityTaskManagerInternal activityTaskManagerInternal,
|
||||
ActivityManagerInternal activityManagerInternal,
|
||||
PackageManagerInternal packageManagerInternal) {
|
||||
PackageManagerInternal packageManagerInternal,
|
||||
LocaleManagerBackupHelper localeManagerBackupHelper) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
mBinderService = new LocaleManagerBinderService();
|
||||
mActivityTaskManagerInternal = activityTaskManagerInternal;
|
||||
mActivityManagerInternal = activityManagerInternal;
|
||||
mPackageManagerInternal = packageManagerInternal;
|
||||
mBackupHelper = new LocaleManagerBackupHelper(this,
|
||||
mPackageManagerInternal);
|
||||
mBackupHelper = localeManagerBackupHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,11 +143,6 @@ public class LocaleManagerService extends SystemService {
|
||||
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
LocaleManagerBackupHelper getBackupHelper() {
|
||||
return mBackupHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current UI locales for a specified app.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.server.locales;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.os.Environment;
|
||||
import android.os.LocaleList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SimpleClock;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.Xml;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Clock;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link LocaleManagerInternal}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LocaleManagerBackupRestoreTest {
|
||||
private static final String DEFAULT_PACKAGE_NAME = "com.android.myapp";
|
||||
private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
|
||||
private static final String STAGE_LOCALES_XML_TAG = "locales";
|
||||
private static final int DEFAULT_USER_ID = 0;
|
||||
private static final int INVALID_UID = -1;
|
||||
private static final LocaleList DEFAULT_LOCALES =
|
||||
LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
|
||||
private static final Map<String, String> DEFAULT_PACKAGE_LOCALES_MAP = Map.of(
|
||||
DEFAULT_PACKAGE_NAME, DEFAULT_LOCALE_TAGS);
|
||||
|
||||
private LocaleManagerBackupHelper mBackupHelper;
|
||||
private long mCurrentTimeMillis;
|
||||
|
||||
@Mock
|
||||
private Context mMockContext;
|
||||
@Mock
|
||||
private PackageManagerInternal mMockPackageManagerInternal;
|
||||
@Mock
|
||||
private LocaleManagerService mMockLocaleManagerService;
|
||||
|
||||
private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
|
||||
@Override
|
||||
public long millis() {
|
||||
return currentTimeMillis();
|
||||
}
|
||||
};
|
||||
|
||||
private long currentTimeMillis() {
|
||||
return mCurrentTimeMillis;
|
||||
}
|
||||
|
||||
private void setCurrentTimeMillis(long currentTimeMillis) {
|
||||
mCurrentTimeMillis = currentTimeMillis;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mMockContext = mock(Context.class);
|
||||
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
|
||||
|
||||
mMockLocaleManagerService = mock(LocaleManagerService.class);
|
||||
|
||||
mBackupHelper = new LocaleManagerBackupHelper(mMockContext, mMockLocaleManagerService,
|
||||
mMockPackageManagerInternal,
|
||||
new File(Environment.getExternalStorageDirectory(), "lmsUnitTests"),
|
||||
mClock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupPayload_noAppsInstalled_returnsNull() throws Exception {
|
||||
doReturn(List.of()).when(mMockPackageManagerInternal)
|
||||
.getInstalledApplications(anyLong(), anyInt(), anyInt());
|
||||
|
||||
assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupPayload_noAppLocalesSet_returnsNull() throws Exception {
|
||||
setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
|
||||
setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
|
||||
|
||||
assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupPayload_appLocalesSet_returnsNonNullBlob() throws Exception {
|
||||
setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
|
||||
setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
|
||||
|
||||
byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
|
||||
verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupPayload_exceptionInGetLocalesAllPackages_returnsNull() throws Exception {
|
||||
setUpDummyAppForPackageManager(DEFAULT_PACKAGE_NAME);
|
||||
doThrow(new RemoteException("mock")).when(mMockLocaleManagerService).getApplicationLocales(
|
||||
anyString(), anyInt());
|
||||
|
||||
assertNull(mBackupHelper.getBackupPayload(DEFAULT_USER_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackupPayload_exceptionInGetLocalesSomePackages_appsWithExceptionNotBackedUp()
|
||||
throws Exception {
|
||||
// Set up two apps.
|
||||
ApplicationInfo defaultAppInfo = new ApplicationInfo();
|
||||
ApplicationInfo anotherAppInfo = new ApplicationInfo();
|
||||
defaultAppInfo.packageName = DEFAULT_PACKAGE_NAME;
|
||||
anotherAppInfo.packageName = "com.android.anotherapp";
|
||||
doReturn(List.of(defaultAppInfo, anotherAppInfo)).when(mMockPackageManagerInternal)
|
||||
.getInstalledApplications(anyLong(), anyInt(), anyInt());
|
||||
|
||||
setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
|
||||
// Exception when getting locales for anotherApp.
|
||||
doThrow(new RemoteException("mock")).when(mMockLocaleManagerService).getApplicationLocales(
|
||||
eq(anotherAppInfo.packageName), anyInt());
|
||||
|
||||
byte[] payload = mBackupHelper.getBackupPayload(DEFAULT_USER_ID);
|
||||
verifyPayloadForAppLocales(DEFAULT_PACKAGE_LOCALES_MAP, payload);
|
||||
}
|
||||
|
||||
private void setUpLocalesForPackage(String packageName, LocaleList locales) throws Exception {
|
||||
doReturn(locales).when(mMockLocaleManagerService).getApplicationLocales(
|
||||
eq(packageName), anyInt());
|
||||
}
|
||||
|
||||
private void setUpDummyAppForPackageManager(String packageName) {
|
||||
ApplicationInfo dummyApp = new ApplicationInfo();
|
||||
dummyApp.packageName = packageName;
|
||||
doReturn(List.of(dummyApp)).when(mMockPackageManagerInternal)
|
||||
.getInstalledApplications(anyLong(), anyInt(), anyInt());
|
||||
}
|
||||
|
||||
private void verifyPayloadForAppLocales(Map<String, String> expectedPkgLocalesMap,
|
||||
byte[] payload)
|
||||
throws IOException, XmlPullParserException {
|
||||
final ByteArrayInputStream stream = new ByteArrayInputStream(payload);
|
||||
final TypedXmlPullParser parser = Xml.newFastPullParser();
|
||||
parser.setInput(stream, StandardCharsets.UTF_8.name());
|
||||
|
||||
Map<String, String> backupDataMap = new HashMap<>();
|
||||
XmlUtils.beginDocument(parser, STAGE_LOCALES_XML_TAG);
|
||||
int depth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, depth)) {
|
||||
if (parser.getName().equals("package")) {
|
||||
String packageName = parser.getAttributeValue(null, "name");
|
||||
String languageTags = parser.getAttributeValue(null, "locales");
|
||||
backupDataMap.put(packageName, languageTags);
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(expectedPkgLocalesMap, backupDataMap);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.Manifest;
|
||||
@@ -60,7 +61,7 @@ public class LocaleManagerServiceTest {
|
||||
private static final int DEFAULT_USER_ID = 0;
|
||||
private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
|
||||
private static final int INVALID_UID = -1;
|
||||
private static final String DEFAULT_LOCALE_TAGS = "en-XC, ar-XB";
|
||||
private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
|
||||
private static final LocaleList DEFAULT_LOCALES =
|
||||
LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
|
||||
private static final InstallSourceInfo DEFAULT_INSTALL_SOURCE_INFO = new InstallSourceInfo(
|
||||
@@ -68,6 +69,7 @@ public class LocaleManagerServiceTest {
|
||||
/* originatingPackageName = */ null, /* installingPackageName = */ null);
|
||||
|
||||
private LocaleManagerService mLocaleManagerService;
|
||||
private LocaleManagerBackupHelper mMockBackupHelper;
|
||||
|
||||
@Mock
|
||||
private Context mMockContext;
|
||||
@@ -104,8 +106,9 @@ public class LocaleManagerServiceTest {
|
||||
.handleIncomingUser(anyInt(), anyInt(), eq(DEFAULT_USER_ID), anyBoolean(), anyInt(),
|
||||
anyString(), anyString());
|
||||
|
||||
mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
|
||||
mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager,
|
||||
mMockActivityManager, mMockPackageManagerInternal);
|
||||
mMockActivityManager, mMockPackageManagerInternal, mMockBackupHelper);
|
||||
}
|
||||
|
||||
@Test(expected = SecurityException.class)
|
||||
@@ -122,6 +125,7 @@ public class LocaleManagerServiceTest {
|
||||
verify(mMockContext).enforceCallingOrSelfPermission(
|
||||
eq(android.Manifest.permission.CHANGE_CONFIGURATION),
|
||||
anyString());
|
||||
verify(mMockBackupHelper, times(0)).notifyBackupManager();
|
||||
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
|
||||
}
|
||||
}
|
||||
@@ -133,6 +137,7 @@ public class LocaleManagerServiceTest {
|
||||
DEFAULT_USER_ID, LocaleList.getEmptyLocaleList());
|
||||
fail("Expected NullPointerException");
|
||||
} finally {
|
||||
verify(mMockBackupHelper, times(0)).notifyBackupManager();
|
||||
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
|
||||
}
|
||||
}
|
||||
@@ -146,6 +151,7 @@ public class LocaleManagerServiceTest {
|
||||
/* locales = */ null);
|
||||
fail("Expected NullPointerException");
|
||||
} finally {
|
||||
verify(mMockBackupHelper, times(0)).notifyBackupManager();
|
||||
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
|
||||
}
|
||||
}
|
||||
@@ -163,6 +169,7 @@ public class LocaleManagerServiceTest {
|
||||
DEFAULT_LOCALES);
|
||||
|
||||
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
|
||||
verify(mMockBackupHelper, times(1)).notifyBackupManager();
|
||||
|
||||
}
|
||||
|
||||
@@ -175,6 +182,7 @@ public class LocaleManagerServiceTest {
|
||||
DEFAULT_LOCALES);
|
||||
|
||||
assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales());
|
||||
verify(mMockBackupHelper, times(1)).notifyBackupManager();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@@ -187,6 +195,7 @@ public class LocaleManagerServiceTest {
|
||||
fail("Expected IllegalArgumentException");
|
||||
} finally {
|
||||
assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());
|
||||
verify(mMockBackupHelper, times(0)).notifyBackupManager();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +226,7 @@ public class LocaleManagerServiceTest {
|
||||
.when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
|
||||
|
||||
LocaleList locales = mLocaleManagerService.getApplicationLocales(
|
||||
DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
|
||||
DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
|
||||
|
||||
assertEquals(LocaleList.getEmptyLocaleList(), locales);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.server.locales;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Clock;
|
||||
|
||||
/**
|
||||
* Shadow for {@link LocaleManagerBackupHelper} to enable mocking it for tests.
|
||||
*
|
||||
* <p>{@link LocaleManagerBackupHelper} is a package private class and hence not mockable directly.
|
||||
*/
|
||||
public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper {
|
||||
ShadowLocaleManagerBackupHelper(Context context,
|
||||
LocaleManagerService localeManagerService,
|
||||
PackageManagerInternal pmInternal, File stagedLocalesDir, Clock clock) {
|
||||
super(context, localeManagerService, pmInternal, stagedLocalesDir, clock);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user