cmsdk: Add preliminary ThemeManager test coverage.

TICKET: CYNGNOS-2307
Change-Id: Ie94ae3cd55678ecb2c631dc10d2335fb5d7362a7
This commit is contained in:
Adnan Begovic 2016-03-24 16:26:48 -07:00
parent d2e7e04bc4
commit 22859efbfd
3 changed files with 361 additions and 2 deletions

View File

@ -67,7 +67,8 @@ public class ThemeManager {
return sInstance;
}
private static IThemeService getService() {
/** @hide */
public static IThemeService getService() {
if (sService != null) {
return sService;
}

View File

@ -6,11 +6,11 @@
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="cyanogenmod.permission.PUBLISH_CUSTOM_TILE" />
<uses-permission android:name="cyanogenmod.permission.WRITE_SETTINGS"/>
<uses-permission android:name="cyanogenmod.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="cyanogenmod.permission.MODIFY_NETWORK_SETTINGS" />
<uses-permission android:name="cyanogenmod.permission.MODIFY_SOUND_SETTINGS" />
<uses-permission android:name="cyanogenmod.permission.MANAGE_ALARMS" />
@ -21,6 +21,10 @@
<uses-permission android:name="cyanogenmod.permission.MODIFY_PROFILES" />
<uses-permission android:name="cyanogenmod.permission.MANAGE_PERSISTENT_STORAGE" />
<uses-permission android:name="cyanogenmod.permission.PERFORMANCE_ACCESS" />
<uses-permission android:name="cyanogenmod.permission.READ_THEMES" />
<uses-permission android:name="cyanogenmod.permission.WRITE_THEMES" />
<uses-permission android:name="cyanogenmod.permission.ACCESS_THEME_MANAGER" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<application android:name=".CyanogenModTestApplication"

View File

@ -0,0 +1,354 @@
/**
* Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.tests.themes.unit;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import android.util.Log;
import cyanogenmod.themes.IThemeService;
import cyanogenmod.themes.ThemeChangeRequest;
import cyanogenmod.themes.ThemeManager;
import cyanogenmod.themes.ThemeManager.ThemeChangeListener;
import cyanogenmod.providers.CMSettings;
import cyanogenmod.providers.ThemesContract;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
public class ThemeManagerTest extends AndroidTestCase {
private static final String TAG = ThemeManagerTest.class.getSimpleName();
private static final int COUNTDOWN = 1;
private ThemeManager mThemeManager;
private ContentResolver mContentResolver;
@Override
protected void setUp() throws Exception {
super.setUp();
mThemeManager = ThemeManager.getInstance(getContext());
mContentResolver = mContext.getContentResolver();
}
@SmallTest
public void testManagerExists() {
assertNotNull(mThemeManager);
}
@SmallTest
public void testManagerServiceIsAvailable() {
IThemeService icmStatusBarManager = mThemeManager.getService();
assertNotNull(icmStatusBarManager);
}
@SmallTest
public void testApplyDefaultTheme() {
final HashMap<String, String> componentKeyMap = new HashMap<>();
final CountDownLatch signal = new CountDownLatch(COUNTDOWN);
// Get the default theme package
final String defaultThemePkg = CMSettings.Secure.getString(mContentResolver,
CMSettings.Secure.DEFAULT_THEME_PACKAGE);
// Get the deefault theme components
final ArrayList<String> components = new ArrayList<>(
Arrays.asList(CMSettings.Secure.getString(mContentResolver,
CMSettings.Secure.DEFAULT_THEME_COMPONENTS).split("\\|")));
// Populate componentkey map since we're going to lock the thread
for (String component : components) {
String key = ThemesContract.MixnMatchColumns.componentToMixNMatchKey(component);
componentKeyMap.put(key, getPackageNameForKey(mContext, key));
}
// Register defaultThemeChangeListener
mThemeManager.registerThemeChangeListener(new ThemeChangeListener() {
public void onProgress(int progress) {
}
public void onFinish(boolean isSuccess) {
boolean assertionFailure = false;
if (isSuccess) {
for (String component : components) {
String key = ThemesContract.MixnMatchColumns.
componentToMixNMatchKey(component);
Log.d(TAG, "Verifying row " + key);
if (!verifyThemeAppliedFromPackageForRow(defaultThemePkg,
componentKeyMap.get(key), true)) {
Log.d(TAG, "Expected package " + defaultThemePkg
+ " but got package " + componentKeyMap.get(key));
assertionFailure = true;
}
}
}
mThemeManager.unregisterThemeChangeListener(this);
signal.countDown();
if (assertionFailure) throw new AssertionError("Unable to apply default theme");
}
});
// Apply the default theme
mThemeManager.applyDefaultTheme();
// Lock
try {
signal.await();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
private ThemeChangeListener dummyThemeChangeListener = new ThemeChangeListener() {
@Override
public void onProgress(int progress) {
}
@Override
public void onFinish(boolean isSuccess) {
}
};
@SmallTest
public void testRegisterAndUnregisterThemeChangeListener() {
// Exploit the illegalArgumentException thrown by registerThemeChangeListener to
// verify registration.
mThemeManager.registerThemeChangeListener(dummyThemeChangeListener);
try {
mThemeManager.registerThemeChangeListener(dummyThemeChangeListener);
throw new AssertionError("Failed to register theme change listener!");
} catch (IllegalArgumentException e) {
// EXPECTED!
}
// Inversely, exploit that the illegal argument exception isn't thrown
// if unregistering and reregistering
mThemeManager.unregisterThemeChangeListener(dummyThemeChangeListener);
try {
mThemeManager.registerThemeChangeListener(dummyThemeChangeListener);
} catch (IllegalArgumentException e) {
throw new AssertionError("Failed to unregister theme change listener!");
}
// Cleanup!
mThemeManager.unregisterThemeChangeListener(dummyThemeChangeListener);
}
private ThemeManager.ThemeProcessingListener dummyThemeProcessingListener =
new ThemeManager.ThemeProcessingListener() {
@Override
public void onFinishedProcessing(String pkgName) {
}
};
@SmallTest
public void testRegisterAndUnregisterThemeProcessingListener() {
// Exploit the illegalArgumentException thrown by registerThemeChangeListener to
// verify registration.
mThemeManager.registerProcessingListener(dummyThemeProcessingListener);
try {
mThemeManager.registerProcessingListener(dummyThemeProcessingListener);
throw new AssertionError("Failed to register theme processing listener!");
} catch (IllegalArgumentException e) {
// EXPECTED!
}
// Inversely, exploit that the illegal argument exception isn't thrown
// if unregistering and reregistering
mThemeManager.unregisterProcessingListener(dummyThemeProcessingListener);
try {
mThemeManager.registerProcessingListener(dummyThemeProcessingListener);
} catch (IllegalArgumentException e) {
throw new AssertionError("Failed to unregister theme change listener!");
}
// Cleanup!
mThemeManager.unregisterProcessingListener(dummyThemeProcessingListener);
}
boolean actualRequestThemeChangeAsMapResponse = false;
@SmallTest
public void testRequestThemeChangeAsMapAndCallback() {
Map<String, String> expectedAppOverlayMap = new HashMap<>();
final CountDownLatch signal = new CountDownLatch(COUNTDOWN);
// Get the deefault theme components
final ArrayList<String> components = new ArrayList<>(
Arrays.asList(CMSettings.Secure.getString(mContentResolver,
CMSettings.Secure.DEFAULT_THEME_COMPONENTS).split("\\|")));
// Get the default theme package
final String defaultThemePkg = CMSettings.Secure.getString(mContentResolver,
CMSettings.Secure.DEFAULT_THEME_PACKAGE);
for (String component : components) {
expectedAppOverlayMap.put(component, defaultThemePkg);
}
mThemeManager.registerThemeChangeListener(new ThemeChangeListener() {
@Override
public void onProgress(int progress) {
}
@Override
public void onFinish(boolean isSuccess) {
actualRequestThemeChangeAsMapResponse = isSuccess;
signal.countDown();
}
});
mThemeManager.requestThemeChange(expectedAppOverlayMap);
// Lock
try {
signal.await();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
assertTrue(actualRequestThemeChangeAsMapResponse);
}
boolean actualRequestThemeChangeAsStringListResponse = false;
@SmallTest
public void testRequestThemeChangeAsStringListAndCallback() {
final CountDownLatch signal = new CountDownLatch(COUNTDOWN);
// Get the deefault theme components
final ArrayList<String> components = new ArrayList<>(
Arrays.asList(CMSettings.Secure.getString(mContentResolver,
CMSettings.Secure.DEFAULT_THEME_COMPONENTS).split("\\|")));
// Get the default theme package
final String defaultThemePkg = CMSettings.Secure.getString(mContentResolver,
CMSettings.Secure.DEFAULT_THEME_PACKAGE);
mThemeManager.registerThemeChangeListener(new ThemeChangeListener() {
@Override
public void onProgress(int progress) {
}
@Override
public void onFinish(boolean isSuccess) {
actualRequestThemeChangeAsStringListResponse = isSuccess;
signal.countDown();
}
});
mThemeManager.requestThemeChange(defaultThemePkg, components);
// Lock
try {
signal.await();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
assertTrue(actualRequestThemeChangeAsStringListResponse);
}
boolean actualRequestThemeChangeAsRequestResponse = false;
@SmallTest
public void testRequestThemeChangeAsRequestAndCallback() {
final CountDownLatch signal = new CountDownLatch(COUNTDOWN);
// Get the default theme package
final String defaultThemePkg = CMSettings.Secure.getString(mContentResolver,
CMSettings.Secure.DEFAULT_THEME_PACKAGE);
ThemeChangeRequest request = new ThemeChangeRequest.Builder()
.setAlarm(defaultThemePkg)
.setNavBar(defaultThemePkg)
.setBootanimation(defaultThemePkg)
.setLockWallpaper(defaultThemePkg)
.setLiveLockScreen(defaultThemePkg)
.build();
mThemeManager.registerThemeChangeListener(new ThemeChangeListener() {
@Override
public void onProgress(int progress) {
}
@Override
public void onFinish(boolean isSuccess) {
actualRequestThemeChangeAsRequestResponse = isSuccess;
signal.countDown();
}
});
mThemeManager.requestThemeChange(request, true);
// Lock
try {
signal.await();
} catch (InterruptedException e) {
throw new AssertionError(e);
}
assertTrue(actualRequestThemeChangeAsRequestResponse);
}
private boolean verifyThemeAppliedFromPackageForRow(String packageName, String expectedPackage,
boolean systemTheme) {
boolean verified = TextUtils.isEmpty(expectedPackage) ||
TextUtils.equals(packageName, expectedPackage);
if (systemTheme && !verified) {
verified = TextUtils.equals(expectedPackage, "system");
}
return verified;
}
private String getPackageNameForKey(Context context, String key) {
final ContentResolver cr = context.getContentResolver();
String[] projection = {ThemesContract.MixnMatchColumns.COL_VALUE};
String selection = ThemesContract.MixnMatchColumns.COL_KEY + "=?";
String[] selectionArgs = {key};
Cursor c = cr.query(ThemesContract.MixnMatchColumns.CONTENT_URI, projection, selection,
selectionArgs, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
return c.getString(0);
}
} finally {
c.close();
}
}
return null;
}
}