Merge "OMS: add internal tests" into qt-dev
This commit is contained in:
@@ -451,7 +451,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
public String toString() {
|
||||
return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName
|
||||
+ ((targetOverlayableName == null) ? ""
|
||||
: ", targetOverlyabale=" + targetOverlayableName)
|
||||
: ", targetOverlayable=" + targetOverlayableName)
|
||||
+ ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* . . . . . . . . . . . . . . . . . . . . . .
|
||||
* </pre>
|
||||
*
|
||||
* <p>To test the OMS, execute:
|
||||
* <code>
|
||||
* atest FrameworksServicesTests:com.android.server.om # internal tests
|
||||
* atest OverlayDeviceTests OverlayHostTests # public API tests
|
||||
* </code>
|
||||
* </p>
|
||||
*
|
||||
* <p>Finally, here is a list of keywords used in the OMS context.</p>
|
||||
*
|
||||
* <ul>
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
@@ -327,7 +328,8 @@ final class OverlayManagerSettings {
|
||||
Serializer.persist(mItems, os);
|
||||
}
|
||||
|
||||
private static final class Serializer {
|
||||
@VisibleForTesting
|
||||
static final class Serializer {
|
||||
private static final String TAG_OVERLAYS = "overlays";
|
||||
private static final String TAG_ITEM = "item";
|
||||
|
||||
@@ -343,7 +345,8 @@ final class OverlayManagerSettings {
|
||||
private static final String ATTR_USER_ID = "userId";
|
||||
private static final String ATTR_VERSION = "version";
|
||||
|
||||
private static final int CURRENT_VERSION = 3;
|
||||
@VisibleForTesting
|
||||
static final int CURRENT_VERSION = 3;
|
||||
|
||||
public static void restore(@NonNull final ArrayList<SettingsItem> table,
|
||||
@NonNull final InputStream is) throws IOException, XmlPullParserException {
|
||||
|
||||
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.om;
|
||||
|
||||
import static android.content.om.OverlayInfo.STATE_DISABLED;
|
||||
import static android.content.om.OverlayInfo.STATE_ENABLED;
|
||||
import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
|
||||
import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OverlayManagerServiceImplTests {
|
||||
private OverlayManagerServiceImpl mImpl;
|
||||
private DummyDeviceState mState;
|
||||
private DummyListener mListener;
|
||||
|
||||
private static final String OVERLAY = "com.dummy.overlay";
|
||||
private static final String TARGET = "com.dummy.target";
|
||||
private static final int USER = 0;
|
||||
|
||||
private static final String OVERLAY2 = OVERLAY + "2";
|
||||
private static final String TARGET2 = TARGET + "2";
|
||||
private static final int USER2 = USER + 1;
|
||||
|
||||
private static final String OVERLAY3 = OVERLAY + "3";
|
||||
private static final int USER3 = USER2 + 1;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mState = new DummyDeviceState();
|
||||
mListener = new DummyListener();
|
||||
DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState);
|
||||
mImpl = new OverlayManagerServiceImpl(pmh,
|
||||
new DummyIdmapManager(mState, pmh),
|
||||
new OverlayManagerSettings(),
|
||||
new String[0],
|
||||
mListener);
|
||||
}
|
||||
|
||||
// tests: basics
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfo() throws Exception {
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, false);
|
||||
final OverlayInfo oi = mImpl.getOverlayInfo(OVERLAY, USER);
|
||||
assertNotNull(oi);
|
||||
assertEquals(oi.packageName, OVERLAY);
|
||||
assertEquals(oi.targetPackageName, TARGET);
|
||||
assertEquals(oi.userId, USER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfosForTarget() throws Exception {
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, false);
|
||||
installOverlayPackage(OVERLAY2, TARGET, USER, false);
|
||||
|
||||
installOverlayPackage(OVERLAY3, TARGET, USER2, false);
|
||||
|
||||
final List<OverlayInfo> ois = mImpl.getOverlayInfosForTarget(TARGET, USER);
|
||||
assertEquals(ois.size(), 2);
|
||||
assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER)));
|
||||
assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER)));
|
||||
|
||||
final List<OverlayInfo> ois2 = mImpl.getOverlayInfosForTarget(TARGET, USER2);
|
||||
assertEquals(ois2.size(), 1);
|
||||
assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER2)));
|
||||
|
||||
final List<OverlayInfo> ois3 = mImpl.getOverlayInfosForTarget(TARGET, USER3);
|
||||
assertNotNull(ois3);
|
||||
assertEquals(ois3.size(), 0);
|
||||
|
||||
final List<OverlayInfo> ois4 = mImpl.getOverlayInfosForTarget("no.such.overlay", USER);
|
||||
assertNotNull(ois4);
|
||||
assertEquals(ois4.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlayInfosForUser() throws Exception {
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, false);
|
||||
installOverlayPackage(OVERLAY2, TARGET, USER, false);
|
||||
installOverlayPackage(OVERLAY3, TARGET2, USER, false);
|
||||
|
||||
final Map<String, List<OverlayInfo>> everything = mImpl.getOverlaysForUser(USER);
|
||||
assertEquals(everything.size(), 2);
|
||||
|
||||
final List<OverlayInfo> ois = everything.get(TARGET);
|
||||
assertNotNull(ois);
|
||||
assertEquals(ois.size(), 2);
|
||||
assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER)));
|
||||
assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER)));
|
||||
|
||||
final List<OverlayInfo> ois2 = everything.get(TARGET2);
|
||||
assertNotNull(ois2);
|
||||
assertEquals(ois2.size(), 1);
|
||||
assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER)));
|
||||
|
||||
final Map<String, List<OverlayInfo>> everything2 = mImpl.getOverlaysForUser(USER2);
|
||||
assertNotNull(everything2);
|
||||
assertEquals(everything2.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPriority() throws Exception {
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, false);
|
||||
installOverlayPackage(OVERLAY2, TARGET, USER, false);
|
||||
installOverlayPackage(OVERLAY3, TARGET, USER, false);
|
||||
|
||||
final OverlayInfo o1 = mImpl.getOverlayInfo(OVERLAY, USER);
|
||||
final OverlayInfo o2 = mImpl.getOverlayInfo(OVERLAY2, USER);
|
||||
final OverlayInfo o3 = mImpl.getOverlayInfo(OVERLAY3, USER);
|
||||
|
||||
assertOverlayInfoList(TARGET, USER, o1, o2, o3);
|
||||
|
||||
assertTrue(mImpl.setLowestPriority(OVERLAY3, USER));
|
||||
assertOverlayInfoList(TARGET, USER, o3, o1, o2);
|
||||
|
||||
assertTrue(mImpl.setHighestPriority(OVERLAY3, USER));
|
||||
assertOverlayInfoList(TARGET, USER, o1, o2, o3);
|
||||
|
||||
assertTrue(mImpl.setPriority(OVERLAY, OVERLAY2, USER));
|
||||
assertOverlayInfoList(TARGET, USER, o2, o1, o3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlayInfoStateTransitions() throws Exception {
|
||||
assertNull(mImpl.getOverlayInfo(OVERLAY, USER));
|
||||
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, true);
|
||||
assertState(STATE_MISSING_TARGET, OVERLAY, USER);
|
||||
|
||||
installTargetPackage(TARGET, USER);
|
||||
assertState(STATE_DISABLED, OVERLAY, USER);
|
||||
|
||||
mImpl.setEnabled(OVERLAY, true, USER);
|
||||
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||
|
||||
beginUpgradeTargetPackage(TARGET, USER);
|
||||
assertState(STATE_TARGET_IS_BEING_REPLACED, OVERLAY, USER);
|
||||
|
||||
endUpgradeTargetPackage(TARGET, USER);
|
||||
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||
|
||||
uninstallTargetPackage(TARGET, USER);
|
||||
assertState(STATE_MISSING_TARGET, OVERLAY, USER);
|
||||
|
||||
installTargetPackage(TARGET, USER);
|
||||
assertState(STATE_ENABLED, OVERLAY, USER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateOverlaysForUser() throws Exception {
|
||||
installTargetPackage(TARGET, USER);
|
||||
installTargetPackage("some.other.target", USER);
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, true);
|
||||
|
||||
// do nothing, expect no change
|
||||
List<String> a = mImpl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, a.size());
|
||||
assertTrue(a.contains(TARGET));
|
||||
|
||||
// upgrade overlay, keep target
|
||||
upgradeOverlayPackage(OVERLAY, TARGET, USER, true);
|
||||
List<String> b = mImpl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, b.size());
|
||||
assertTrue(b.contains(TARGET));
|
||||
|
||||
// do nothing, expect no change
|
||||
List<String> c = mImpl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, c.size());
|
||||
assertTrue(c.contains(TARGET));
|
||||
|
||||
// upgrade overlay, switch to new target
|
||||
upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
|
||||
List<String> d = mImpl.updateOverlaysForUser(USER);
|
||||
assertEquals(2, d.size());
|
||||
assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
|
||||
|
||||
// do nothing, expect no change
|
||||
List<String> e = mImpl.updateOverlaysForUser(USER);
|
||||
assertEquals(1, e.size());
|
||||
assertTrue(e.contains("some.other.target"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnOverlayPackageUpgraded() throws Exception {
|
||||
installTargetPackage(TARGET, USER);
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, true);
|
||||
mImpl.onOverlayPackageReplacing(OVERLAY, USER);
|
||||
mListener.count = 0;
|
||||
mImpl.onOverlayPackageReplaced(OVERLAY, USER);
|
||||
assertEquals(1, mListener.count);
|
||||
|
||||
// upgrade to a version where the overlay has changed its target
|
||||
upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
|
||||
mImpl.onOverlayPackageReplacing(OVERLAY, USER);
|
||||
mListener.count = 0;
|
||||
mImpl.onOverlayPackageReplaced(OVERLAY, USER);
|
||||
// expect once for the old target package, once for the new target package
|
||||
assertEquals(2, mListener.count);
|
||||
|
||||
upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
|
||||
mImpl.onOverlayPackageReplacing(OVERLAY, USER);
|
||||
mListener.count = 0;
|
||||
mImpl.onOverlayPackageReplaced(OVERLAY, USER);
|
||||
assertEquals(1, mListener.count);
|
||||
}
|
||||
|
||||
// tests: listener interface
|
||||
|
||||
@Test
|
||||
public void testListener() throws Exception {
|
||||
installOverlayPackage(OVERLAY, TARGET, USER, true);
|
||||
assertEquals(1, mListener.count);
|
||||
mListener.count = 0;
|
||||
|
||||
installTargetPackage(TARGET, USER);
|
||||
assertEquals(1, mListener.count);
|
||||
mListener.count = 0;
|
||||
|
||||
mImpl.setEnabled(OVERLAY, true, USER);
|
||||
assertEquals(1, mListener.count);
|
||||
mListener.count = 0;
|
||||
|
||||
mImpl.setEnabled(OVERLAY, true, USER);
|
||||
assertEquals(0, mListener.count);
|
||||
}
|
||||
|
||||
// helper methods
|
||||
|
||||
private void assertState(int expected, final String overlayPackageName, int userId) {
|
||||
int actual = mImpl.getOverlayInfo(OVERLAY, USER).state;
|
||||
String msg = String.format("expected %s but was %s:",
|
||||
OverlayInfo.stateToString(expected), OverlayInfo.stateToString(actual));
|
||||
assertEquals(msg, expected, actual);
|
||||
}
|
||||
|
||||
private void assertOverlayInfoList(final String targetPackageName, int userId,
|
||||
OverlayInfo... overlayInfos) {
|
||||
final List<OverlayInfo> expected =
|
||||
mImpl.getOverlayInfosForTarget(targetPackageName, userId);
|
||||
final List<OverlayInfo> actual = Arrays.asList(overlayInfos);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private void installTargetPackage(String packageName, int userId) {
|
||||
if (mState.select(packageName, userId) != null) {
|
||||
throw new IllegalStateException("package already installed");
|
||||
}
|
||||
mState.add(packageName, null, userId, false);
|
||||
mImpl.onTargetPackageAdded(packageName, userId);
|
||||
}
|
||||
|
||||
private void beginUpgradeTargetPackage(String packageName, int userId) {
|
||||
if (mState.select(packageName, userId) == null) {
|
||||
throw new IllegalStateException("package not installed");
|
||||
}
|
||||
mState.add(packageName, null, userId, false);
|
||||
mImpl.onTargetPackageReplacing(packageName, userId);
|
||||
}
|
||||
|
||||
private void endUpgradeTargetPackage(String packageName, int userId) {
|
||||
if (mState.select(packageName, userId) == null) {
|
||||
throw new IllegalStateException("package not installed");
|
||||
}
|
||||
mState.add(packageName, null, userId, false);
|
||||
mImpl.onTargetPackageReplaced(packageName, userId);
|
||||
}
|
||||
|
||||
private void uninstallTargetPackage(String packageName, int userId) {
|
||||
if (mState.select(packageName, userId) == null) {
|
||||
throw new IllegalStateException("package not installed");
|
||||
}
|
||||
mState.remove(packageName, userId);
|
||||
mImpl.onTargetPackageRemoved(packageName, userId);
|
||||
}
|
||||
|
||||
private void installOverlayPackage(String packageName, String targetPackageName, int userId,
|
||||
boolean canCreateIdmap) {
|
||||
if (mState.select(packageName, userId) != null) {
|
||||
throw new IllegalStateException("package already installed");
|
||||
}
|
||||
mState.add(packageName, targetPackageName, userId, canCreateIdmap);
|
||||
mImpl.onOverlayPackageAdded(packageName, userId);
|
||||
}
|
||||
|
||||
private void upgradeOverlayPackage(String packageName, String targetPackageName, int userId,
|
||||
boolean canCreateIdmap) {
|
||||
DummyDeviceState.Package pkg = mState.select(packageName, userId);
|
||||
if (pkg == null) {
|
||||
throw new IllegalStateException("package not installed, cannot upgrade");
|
||||
}
|
||||
pkg.targetPackageName = targetPackageName;
|
||||
pkg.canCreateIdmap = canCreateIdmap;
|
||||
}
|
||||
|
||||
private void uninstallOverlayPackage(String packageName, int userId) {
|
||||
// implement this when adding support for downloadable overlays
|
||||
throw new IllegalArgumentException("not implemented");
|
||||
}
|
||||
|
||||
private static final class DummyDeviceState {
|
||||
private List<Package> mPackages = new ArrayList<>();
|
||||
|
||||
public void add(String packageName, String targetPackageName, int userId,
|
||||
boolean canCreateIdmap) {
|
||||
remove(packageName, userId);
|
||||
Package pkg = new Package();
|
||||
pkg.packageName = packageName;
|
||||
pkg.targetPackageName = targetPackageName;
|
||||
pkg.userId = userId;
|
||||
pkg.canCreateIdmap = canCreateIdmap;
|
||||
mPackages.add(pkg);
|
||||
}
|
||||
|
||||
public void remove(String packageName, int userId) {
|
||||
final Iterator<Package> iter = mPackages.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final Package pkg = iter.next();
|
||||
if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
|
||||
iter.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Package> select(int userId) {
|
||||
List<Package> out = new ArrayList<>();
|
||||
final int packageCount = mPackages.size();
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
final Package pkg = mPackages.get(i);
|
||||
if (pkg.userId == userId) {
|
||||
out.add(pkg);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public Package select(String packageName, int userId) {
|
||||
final int packageCount = mPackages.size();
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
final Package pkg = mPackages.get(i);
|
||||
if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
|
||||
return pkg;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final class Package {
|
||||
public String packageName;
|
||||
public int userId;
|
||||
public String targetPackageName;
|
||||
public boolean canCreateIdmap;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DummyPackageManagerHelper implements
|
||||
OverlayManagerServiceImpl.PackageManagerHelper {
|
||||
private final DummyDeviceState mState;
|
||||
|
||||
DummyPackageManagerHelper(DummyDeviceState state) {
|
||||
mState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
|
||||
final DummyDeviceState.Package pkg = mState.select(packageName, userId);
|
||||
if (pkg == null) {
|
||||
return null;
|
||||
}
|
||||
ApplicationInfo ai = new ApplicationInfo();
|
||||
ai.sourceDir = String.format("%s/%s/base.apk",
|
||||
pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/",
|
||||
pkg.packageName);
|
||||
PackageInfo pi = new PackageInfo();
|
||||
pi.applicationInfo = ai;
|
||||
pi.packageName = pkg.packageName;
|
||||
pi.overlayTarget = pkg.targetPackageName;
|
||||
pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
|
||||
return pi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean signaturesMatching(@NonNull String packageName1,
|
||||
@NonNull String packageName2, int userId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PackageInfo> getOverlayPackages(int userId) {
|
||||
List<PackageInfo> out = new ArrayList<>();
|
||||
final List<DummyDeviceState.Package> packages = mState.select(userId);
|
||||
final int packageCount = packages.size();
|
||||
for (int i = 0; i < packageCount; i++) {
|
||||
final DummyDeviceState.Package pkg = packages.get(i);
|
||||
if (pkg.targetPackageName != null) {
|
||||
out.add(getPackageInfo(pkg.packageName, pkg.userId));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyIdmapManager extends IdmapManager {
|
||||
private final DummyDeviceState mState;
|
||||
private Set<String> mIdmapFiles = new ArraySet<>();
|
||||
|
||||
DummyIdmapManager(DummyDeviceState state, DummyPackageManagerHelper packageManagerHelper) {
|
||||
super(null, packageManagerHelper);
|
||||
mState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean createIdmap(@NonNull final PackageInfo targetPackage,
|
||||
@NonNull final PackageInfo overlayPackage, int userId) {
|
||||
final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId);
|
||||
if (t == null) {
|
||||
return false;
|
||||
}
|
||||
final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId);
|
||||
if (o == null) {
|
||||
return false;
|
||||
}
|
||||
if (!o.canCreateIdmap) {
|
||||
return false;
|
||||
}
|
||||
final String key = createKey(overlayPackage.packageName, userId);
|
||||
mIdmapFiles.add(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
|
||||
final String key = createKey(oi.packageName, oi.userId);
|
||||
if (!mIdmapFiles.contains(key)) {
|
||||
return false;
|
||||
}
|
||||
mIdmapFiles.remove(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean idmapExists(@NonNull final OverlayInfo oi) {
|
||||
final String key = createKey(oi.packageName, oi.userId);
|
||||
return mIdmapFiles.contains(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
|
||||
final String key = createKey(overlayPackage.packageName, userId);
|
||||
return mIdmapFiles.contains(key);
|
||||
}
|
||||
|
||||
private String createKey(@NonNull final String packageName, final int userId) {
|
||||
return String.format("%s:%d", packageName, userId);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener {
|
||||
public int count;
|
||||
|
||||
public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.om;
|
||||
|
||||
import static android.content.om.OverlayInfo.STATE_DISABLED;
|
||||
import static android.content.om.OverlayInfo.STATE_ENABLED;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.om.OverlayInfo;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Xml;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OverlayManagerSettingsTests {
|
||||
private OverlayManagerSettings mSettings;
|
||||
|
||||
private static final OverlayInfo OVERLAY_A0 = new OverlayInfo(
|
||||
"com.dummy.overlay_a",
|
||||
"com.dummy.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.dummy.overlay_a-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
false);
|
||||
|
||||
private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
|
||||
"com.dummy.overlay_b",
|
||||
"com.dummy.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.dummy.overlay_b-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
false);
|
||||
|
||||
private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
|
||||
"com.dummy.overlay_c",
|
||||
"com.dummy.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.dummy.overlay_c-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
false);
|
||||
|
||||
private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
|
||||
"com.dummy.overlay_a",
|
||||
"com.dummy.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.dummy.overlay_a-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
1,
|
||||
0,
|
||||
false);
|
||||
|
||||
private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
|
||||
"com.dummy.overlay_b",
|
||||
"com.dummy.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.dummy.overlay_b-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
1,
|
||||
0,
|
||||
false);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mSettings = new OverlayManagerSettings();
|
||||
}
|
||||
|
||||
// tests: generic functionality
|
||||
|
||||
@Test
|
||||
public void testSettingsInitiallyEmpty() throws Exception {
|
||||
final int userId = 0;
|
||||
Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(userId);
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicSetAndGet() throws Exception {
|
||||
assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
|
||||
insert(OVERLAY_A0);
|
||||
assertContains(mSettings, OVERLAY_A0);
|
||||
OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
assertEquals(OVERLAY_A0, oi);
|
||||
|
||||
assertTrue(mSettings.remove(OVERLAY_A0.packageName, OVERLAY_A0.userId));
|
||||
assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUsers() throws Exception {
|
||||
int[] users = mSettings.getUsers();
|
||||
assertEquals(0, users.length);
|
||||
|
||||
insert(OVERLAY_A0);
|
||||
users = mSettings.getUsers();
|
||||
assertEquals(1, users.length);
|
||||
assertContains(users, OVERLAY_A0.userId);
|
||||
|
||||
insert(OVERLAY_A1);
|
||||
insert(OVERLAY_B1);
|
||||
users = mSettings.getUsers();
|
||||
assertEquals(2, users.length);
|
||||
assertContains(users, OVERLAY_A0.userId);
|
||||
assertContains(users, OVERLAY_A1.userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOverlaysForUser() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_A1);
|
||||
insert(OVERLAY_B1);
|
||||
|
||||
Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(OVERLAY_A0.userId);
|
||||
assertEquals(1, map.keySet().size());
|
||||
assertTrue(map.keySet().contains(OVERLAY_A0.targetPackageName));
|
||||
|
||||
List<OverlayInfo> list = map.get(OVERLAY_A0.targetPackageName);
|
||||
assertEquals(2, list.size());
|
||||
assertTrue(list.contains(OVERLAY_A0));
|
||||
assertTrue(list.contains(OVERLAY_B0));
|
||||
|
||||
// getOverlaysForUser should never return null
|
||||
map = mSettings.getOverlaysForUser(-1);
|
||||
assertNotNull(map);
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveUser() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_A1);
|
||||
|
||||
assertContains(mSettings, OVERLAY_A0);
|
||||
assertContains(mSettings, OVERLAY_B0);
|
||||
assertContains(mSettings, OVERLAY_A1);
|
||||
|
||||
mSettings.removeUser(OVERLAY_A0.userId);
|
||||
|
||||
assertDoesNotContain(mSettings, OVERLAY_A0);
|
||||
assertDoesNotContain(mSettings, OVERLAY_B0);
|
||||
assertContains(mSettings, OVERLAY_A1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderOfNewlyAddedItems() throws Exception {
|
||||
// new items are appended to the list
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
|
||||
// overlays keep their positions when updated
|
||||
mSettings.setState(OVERLAY_B0.packageName, OVERLAY_B0.userId, STATE_ENABLED);
|
||||
OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B0.packageName, OVERLAY_B0.userId);
|
||||
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, oi, OVERLAY_C0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPriority() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
|
||||
boolean changed = mSettings.setPriority(OVERLAY_B0.packageName, OVERLAY_C0.packageName,
|
||||
OVERLAY_B0.userId);
|
||||
assertTrue(changed);
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
|
||||
|
||||
changed =
|
||||
mSettings.setPriority(OVERLAY_B0.packageName, "does.not.exist", OVERLAY_B0.userId);
|
||||
assertFalse(changed);
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
|
||||
|
||||
OverlayInfo otherTarget = new OverlayInfo(
|
||||
"com.dummy.overlay_other",
|
||||
"com.dummy.some.other.target",
|
||||
null,
|
||||
"some-category",
|
||||
"/data/app/com.dummy.overlay_other-1/base.apk",
|
||||
STATE_DISABLED,
|
||||
0,
|
||||
0,
|
||||
false);
|
||||
insert(otherTarget);
|
||||
changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName,
|
||||
OVERLAY_A0.userId);
|
||||
assertFalse(changed);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetLowestPriority() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
|
||||
boolean changed = mSettings.setLowestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
|
||||
assertTrue(changed);
|
||||
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_B0, OVERLAY_A0, OVERLAY_C0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetHighestPriority() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
insert(OVERLAY_C0);
|
||||
|
||||
List<OverlayInfo> list =
|
||||
mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
|
||||
|
||||
boolean changed = mSettings.setHighestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
|
||||
assertTrue(changed);
|
||||
|
||||
list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
|
||||
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
|
||||
}
|
||||
|
||||
// tests: persist and restore
|
||||
|
||||
@Test
|
||||
public void testPersistEmpty() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
String xml = new String(os.toByteArray(), "utf-8");
|
||||
|
||||
assertEquals(1, countXmlTags(xml, "overlays"));
|
||||
assertEquals(0, countXmlTags(xml, "item"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistDifferentOverlaysSameUser() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B0);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
final String xml = new String(os.toByteArray(), "utf-8");
|
||||
|
||||
assertEquals(1, countXmlTags(xml, "overlays"));
|
||||
assertEquals(2, countXmlTags(xml, "item"));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
|
||||
OVERLAY_A0.packageName));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
|
||||
OVERLAY_B0.packageName));
|
||||
assertEquals(2, countXmlAttributesWhere(xml, "item", "userId",
|
||||
Integer.toString(OVERLAY_A0.userId)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistSameOverlayDifferentUsers() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_A1);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
String xml = new String(os.toByteArray(), "utf-8");
|
||||
|
||||
assertEquals(1, countXmlTags(xml, "overlays"));
|
||||
assertEquals(2, countXmlTags(xml, "item"));
|
||||
assertEquals(2, countXmlAttributesWhere(xml, "item", "packageName",
|
||||
OVERLAY_A0.packageName));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
|
||||
Integer.toString(OVERLAY_A0.userId)));
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
|
||||
Integer.toString(OVERLAY_A1.userId)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistEnabled() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
mSettings.setEnabled(OVERLAY_A0.packageName, OVERLAY_A0.userId, true);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
String xml = new String(os.toByteArray(), "utf-8");
|
||||
|
||||
assertEquals(1, countXmlAttributesWhere(xml, "item", "isEnabled", "true"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestoreEmpty() throws Exception {
|
||||
final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION;
|
||||
final String xml =
|
||||
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
|
||||
+ "<overlays version=\"" + version + "\" />\n";
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
|
||||
|
||||
mSettings.restore(is);
|
||||
assertDoesNotContain(mSettings, "com.dummy.overlay", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestoreSingleUserSingleOverlay() throws Exception {
|
||||
final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION;
|
||||
final String xml =
|
||||
"<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
|
||||
+ "<overlays version='" + version + "'>\n"
|
||||
+ "<item packageName='com.dummy.overlay'\n"
|
||||
+ " userId='1234'\n"
|
||||
+ " targetPackageName='com.dummy.target'\n"
|
||||
+ " baseCodePath='/data/app/com.dummy.overlay-1/base.apk'\n"
|
||||
+ " state='" + STATE_DISABLED + "'\n"
|
||||
+ " isEnabled='false'\n"
|
||||
+ " category='dummy-category'\n"
|
||||
+ " isStatic='false'\n"
|
||||
+ " priority='0' />\n"
|
||||
+ "</overlays>\n";
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
|
||||
|
||||
mSettings.restore(is);
|
||||
OverlayInfo oi = mSettings.getOverlayInfo("com.dummy.overlay", 1234);
|
||||
assertNotNull(oi);
|
||||
assertEquals("com.dummy.overlay", oi.packageName);
|
||||
assertEquals("com.dummy.target", oi.targetPackageName);
|
||||
assertEquals("/data/app/com.dummy.overlay-1/base.apk", oi.baseCodePath);
|
||||
assertEquals(1234, oi.userId);
|
||||
assertEquals(STATE_DISABLED, oi.state);
|
||||
assertFalse(mSettings.getEnabled("com.dummy.overlay", 1234));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistAndRestore() throws Exception {
|
||||
insert(OVERLAY_A0);
|
||||
insert(OVERLAY_B1);
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
mSettings.persist(os);
|
||||
String xml = new String(os.toByteArray(), "utf-8");
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
|
||||
OverlayManagerSettings newSettings = new OverlayManagerSettings();
|
||||
newSettings.restore(is);
|
||||
|
||||
OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
|
||||
assertEquals(OVERLAY_A0, a);
|
||||
|
||||
OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B1.packageName, OVERLAY_B1.userId);
|
||||
assertEquals(OVERLAY_B1, b);
|
||||
}
|
||||
|
||||
private int countXmlTags(String xml, String tagToLookFor) throws Exception {
|
||||
int count = 0;
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(new StringReader(xml));
|
||||
int event = parser.getEventType();
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
if (event == XmlPullParser.START_TAG && tagToLookFor.equals(parser.getName())) {
|
||||
count++;
|
||||
}
|
||||
event = parser.next();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int countXmlAttributesWhere(String xml, String tag, String attr, String value)
|
||||
throws Exception {
|
||||
int count = 0;
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(new StringReader(xml));
|
||||
int event = parser.getEventType();
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
if (event == XmlPullParser.START_TAG && tag.equals(parser.getName())) {
|
||||
String v = parser.getAttributeValue(null, attr);
|
||||
if (value.equals(v)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
event = parser.next();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void insert(OverlayInfo oi) throws Exception {
|
||||
mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath,
|
||||
false, 0, oi.category);
|
||||
mSettings.setState(oi.packageName, oi.userId, oi.state);
|
||||
mSettings.setEnabled(oi.packageName, oi.userId, false);
|
||||
}
|
||||
|
||||
private static void assertContains(final OverlayManagerSettings settings,
|
||||
final OverlayInfo oi) {
|
||||
assertContains(settings, oi.packageName, oi.userId);
|
||||
}
|
||||
|
||||
private static void assertContains(final OverlayManagerSettings settings,
|
||||
final String packageName, int userId) {
|
||||
try {
|
||||
settings.getOverlayInfo(packageName, userId);
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
fail(String.format("settings does not contain packageName=%s userId=%d",
|
||||
packageName, userId));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertDoesNotContain(final OverlayManagerSettings settings,
|
||||
final OverlayInfo oi) {
|
||||
assertDoesNotContain(settings, oi.packageName, oi.userId);
|
||||
}
|
||||
|
||||
private static void assertDoesNotContain(final OverlayManagerSettings settings,
|
||||
final String packageName, int userId) {
|
||||
try {
|
||||
settings.getOverlayInfo(packageName, userId);
|
||||
fail(String.format("settings contains packageName=%s userId=%d", packageName, userId));
|
||||
} catch (OverlayManagerSettings.BadKeyException e) {
|
||||
// do nothing: we expect to end up here
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertContains(int[] haystack, int needle) {
|
||||
List<Integer> list = IntStream.of(haystack)
|
||||
.boxed()
|
||||
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
|
||||
if (!list.contains(needle)) {
|
||||
fail(String.format("integer array [%s] does not contain value %s",
|
||||
TextUtils.join(",", list), needle));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertDoesNotContain(int[] haystack, int needle) {
|
||||
List<Integer> list = IntStream.of(haystack)
|
||||
.boxed()
|
||||
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
|
||||
if (list.contains(needle)) {
|
||||
fail(String.format("integer array [%s] contains value %s",
|
||||
TextUtils.join(",", list), needle));
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertListsAreEqual(List<OverlayInfo> list, OverlayInfo... array) {
|
||||
List<OverlayInfo> other = Stream.of(array)
|
||||
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
|
||||
assertListsAreEqual(list, other);
|
||||
}
|
||||
|
||||
private static void assertListsAreEqual(List<OverlayInfo> list, List<OverlayInfo> other) {
|
||||
if (!list.equals(other)) {
|
||||
fail(String.format("lists [%s] and [%s] differ",
|
||||
TextUtils.join(",", list), TextUtils.join(",", other)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user