From 22859efbfde2ad1eaf5aa12507cd12fed8b60d8d Mon Sep 17 00:00:00 2001 From: Adnan Begovic Date: Thu, 24 Mar 2016 16:26:48 -0700 Subject: [PATCH] cmsdk: Add preliminary ThemeManager test coverage. TICKET: CYNGNOS-2307 Change-Id: Ie94ae3cd55678ecb2c631dc10d2335fb5d7362a7 --- src/java/cyanogenmod/themes/ThemeManager.java | 3 +- tests/AndroidManifest.xml | 6 +- .../tests/themes/unit/ThemeManagerTest.java | 354 ++++++++++++++++++ 3 files changed, 361 insertions(+), 2 deletions(-) create mode 100644 tests/src/org/cyanogenmod/tests/themes/unit/ThemeManagerTest.java diff --git a/src/java/cyanogenmod/themes/ThemeManager.java b/src/java/cyanogenmod/themes/ThemeManager.java index b580ccd..01a0077 100644 --- a/src/java/cyanogenmod/themes/ThemeManager.java +++ b/src/java/cyanogenmod/themes/ThemeManager.java @@ -67,7 +67,8 @@ public class ThemeManager { return sInstance; } - private static IThemeService getService() { + /** @hide */ + public static IThemeService getService() { if (sService != null) { return sService; } diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 28fc932..9f241e4 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -6,11 +6,11 @@ + - @@ -21,6 +21,10 @@ + + + + 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 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 expectedAppOverlayMap = new HashMap<>(); + final CountDownLatch signal = new CountDownLatch(COUNTDOWN); + + // Get the deefault theme components + final ArrayList 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 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; + } + +}