diff options
11 files changed, 698 insertions, 39 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java index e1d6a94fcf07..57e324548a34 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java @@ -32,8 +32,8 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; -import android.os.Looper; import android.provider.Settings; + import com.android.systemui.statusbar.policy.BatteryController; public class BatteryMeterDrawable extends Drawable implements diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java index fccb2a240660..f3be945d5002 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java @@ -16,26 +16,24 @@ package com.android.keyguard; -import com.android.systemui.SysuiTestCase; +import static junit.framework.Assert.assertEquals; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import static org.mockito.Mockito.mock; -import android.content.Context; import android.os.Handler; import android.os.Looper; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import static junit.framework.Assert.*; -import static org.mockito.Mockito.mock; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class KeyguardMessageAreaTest extends SysuiTestCase { - private Context mContext = InstrumentationRegistry.getTargetContext(); private Handler mHandler = new Handler(Looper.getMainLooper()); private KeyguardMessageArea mMessageArea; diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java index 5cb5e68fe6f8..cb0f7a388d01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java @@ -17,7 +17,7 @@ package com.android.systemui; import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyFloat; import static org.mockito.Mockito.anyString; @@ -28,27 +28,24 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) -public class BatteryMeterDrawableTest { +public class BatteryMeterDrawableTest extends SysuiTestCase { - private Context mContext; private Resources mResources; private BatteryMeterDrawable mBatteryMeter; @Before public void setUp() throws Exception { - mContext = InstrumentationRegistry.getTargetContext(); mResources = mContext.getResources(); mBatteryMeter = new BatteryMeterDrawable(mContext, 0); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index d943eb6a1a60..5dac8e54aa21 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -20,6 +20,10 @@ import android.support.test.InstrumentationRegistry; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; + +import com.android.systemui.utils.TestableContext; + +import org.junit.After; import org.junit.Before; /** @@ -28,11 +32,16 @@ import org.junit.Before; public class SysuiTestCase { private Handler mHandler; - protected Context mContext; + protected TestableContext mContext; @Before public void SysuiSetup() throws Exception { - mContext = InstrumentationRegistry.getTargetContext(); + mContext = new TestableContext(InstrumentationRegistry.getTargetContext()); + } + + @After + public void cleanup() throws Exception { + mContext.getSettingsProvider().clearOverrides(this); } protected Context getContext() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index 39b64129f64d..5c87fb01e23c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -17,9 +17,11 @@ package com.android.systemui.power; import static android.test.MoreAsserts.assertNotEqual; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; @@ -29,9 +31,11 @@ import static org.mockito.Mockito.verify; import android.app.Notification; import android.app.NotificationManager; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; + +import com.android.systemui.SysuiTestCase; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,7 +43,7 @@ import org.mockito.ArgumentCaptor; @SmallTest @RunWith(AndroidJUnit4.class) -public class PowerNotificationWarningsTest { +public class PowerNotificationWarningsTest extends SysuiTestCase { private final NotificationManager mMockNotificationManager = mock(NotificationManager.class); private PowerNotificationWarnings mPowerNotificationWarnings; @@ -47,7 +51,7 @@ public class PowerNotificationWarningsTest { public void setUp() throws Exception { // Test Instance. mPowerNotificationWarnings = new PowerNotificationWarnings( - InstrumentationRegistry.getTargetContext(), mMockNotificationManager, null); + mContext, mMockNotificationManager, null); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java index 8eecfcf00d46..5401c30def54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java @@ -18,6 +18,7 @@ package com.android.systemui.qs; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; @@ -26,11 +27,12 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; + import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,13 +40,13 @@ import org.mockito.ArgumentCaptor; @SmallTest @RunWith(AndroidJUnit4.class) -public class TileLayoutTest { - private Context mContext = InstrumentationRegistry.getTargetContext(); - private final TileLayout mTileLayout = new TileLayout(mContext); +public class TileLayoutTest extends SysuiTestCase { + private TileLayout mTileLayout; private int mLayoutSizeForOneTile; @Before public void setUp() throws Exception { + mTileLayout = new TileLayout(mContext); // Layout needs to leave space for the tile margins. Three times the margin size is // sufficient for any number of columns. mLayoutSizeForOneTile = diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java index d7ff04f539ab..782a4890ba55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java @@ -16,7 +16,7 @@ package com.android.systemui.qs.external; import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; @@ -25,12 +25,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Service; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageInfo; import android.content.pm.ServiceInfo; @@ -38,19 +35,16 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteException; import android.os.UserHandle; import android.service.quicksettings.IQSService; import android.service.quicksettings.IQSTileService; import android.service.quicksettings.Tile; import android.service.quicksettings.TileService; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import android.util.ArraySet; -import android.util.Log; + +import com.android.systemui.SysuiTestCase; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -61,7 +55,7 @@ import org.mockito.stubbing.Answer; @SmallTest @RunWith(AndroidJUnit4.class) -public class TileLifecycleManagerTest { +public class TileLifecycleManagerTest extends SysuiTestCase { private static final int TEST_FAIL_TIMEOUT = 5000; private final Context mMockContext = Mockito.mock(Context.class); @@ -78,8 +72,7 @@ public class TileLifecycleManagerTest { @Before public void setUp() throws Exception { setPackageEnabled(true); - mTileServiceComponentName = new ComponentName( - InstrumentationRegistry.getTargetContext(), "FakeTileService.class"); + mTileServiceComponentName = new ComponentName(mContext, "FakeTileService.class"); // Stub.asInterface will just return itself. when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java new file mode 100644 index 000000000000..34f2e019761d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 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.systemui.utils; + +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IContentProvider; +import android.database.ContentObserver; +import android.net.Uri; +import android.util.ArraySet; + +import com.google.android.collect.Maps; + +import java.util.Map; + +/** + * Alternative to a MockContentResolver that falls back to real providers. + */ +public class FakeContentResolver extends ContentResolver { + + private final Map<String, ContentProvider> mProviders = Maps.newHashMap(); + private final ContentResolver mParent; + private final ArraySet<ContentProvider> mInUse = new ArraySet<>(); + private boolean mFallbackToExisting; + + public FakeContentResolver(Context context) { + super(context); + mParent = context.getContentResolver(); + mFallbackToExisting = true; + } + + /** + * Sets whether existing providers should be returned when a mock does not exist. + * The default is true. + */ + public void setFallbackToExisting(boolean fallbackToExisting) { + mFallbackToExisting = fallbackToExisting; + } + + /** + * Adds access to a provider based on its authority + * + * @param name The authority name associated with the provider. + * @param provider An instance of {@link android.content.ContentProvider} or one of its + * subclasses, or null. + */ + public void addProvider(String name, ContentProvider provider) { + mProviders.put(name, provider); + } + + @Override + protected IContentProvider acquireProvider(Context context, String name) { + final ContentProvider provider = mProviders.get(name); + if (provider != null) { + return provider.getIContentProvider(); + } else { + return mFallbackToExisting ? mParent.acquireProvider(name) : null; + } + } + + @Override + protected IContentProvider acquireExistingProvider(Context context, String name) { + final ContentProvider provider = mProviders.get(name); + if (provider != null) { + return provider.getIContentProvider(); + } else { + return mFallbackToExisting ? mParent.acquireExistingProvider( + new Uri.Builder().authority(name).build()) : null; + } + } + + @Override + public boolean releaseProvider(IContentProvider provider) { + if (!mFallbackToExisting) return true; + if (mInUse.contains(provider)) { + mInUse.remove(provider); + return true; + } + return mParent.releaseProvider(provider); + } + + @Override + protected IContentProvider acquireUnstableProvider(Context c, String name) { + final ContentProvider provider = mProviders.get(name); + if (provider != null) { + return provider.getIContentProvider(); + } else { + return mFallbackToExisting ? mParent.acquireUnstableProvider(name) : null; + } + } + + @Override + public boolean releaseUnstableProvider(IContentProvider icp) { + if (!mFallbackToExisting) return true; + if (mInUse.contains(icp)) { + mInUse.remove(icp); + return true; + } + return mParent.releaseUnstableProvider(icp); + } + + @Override + public void unstableProviderDied(IContentProvider icp) { + if (!mFallbackToExisting) return; + if (mInUse.contains(icp)) { + return; + } + mParent.unstableProviderDied(icp); + } + + @Override + public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { + if (!mFallbackToExisting) return; + if (!mProviders.containsKey(uri.getAuthority())) { + super.notifyChange(uri, observer, syncToNetwork); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java new file mode 100644 index 000000000000..f40fe4cb2450 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2016 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.systemui.utils; + +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.os.Bundle; +import android.os.RemoteException; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; +import android.test.mock.MockContentProvider; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider.Builder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Allows calls to android.provider.Settings to be tested easier. A SettingOverride + * can be acquired and a set of specific settings can be set to a value (and not changed + * in the system when set), so that they can be tested without breaking the test device. + * <p> + * To use, in the before method acquire the override add all settings that will affect if + * your test passes or not. + * + * <pre class="prettyprint"> + * {@literal + * mSettingOverride = mTestableContext.getSettingsProvider().acquireOverridesBuilder() + * .addSetting("secure", Secure.USER_SETUP_COMPLETE, "0") + * .build(); + * } + * </pre> + * + * Then in the after free up the settings. + * + * <pre class="prettyprint"> + * {@literal + * mSettingOverride.release(); + * } + * </pre> + */ +public class FakeSettingsProvider extends MockContentProvider { + + private static final String TAG = "FakeSettingsProvider"; + private static final boolean DEBUG = false; + + // Number of times to try to acquire a setting if in use. + private static final int MAX_TRIES = 10; + // Time to wait for each setting. WAIT_TIMEOUT * MAX_TRIES will be the maximum wait time + // for a setting. + private static final long WAIT_TIMEOUT = 1000; + + private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>(); + private final Map<SysuiTestCase, List<SettingOverrider>> mOwners = new ArrayMap<>(); + + private static FakeSettingsProvider sInstance; + private final ContentProviderClient mSettings; + private final ContentResolver mResolver; + + private FakeSettingsProvider(ContentProviderClient settings, ContentResolver resolver) { + mSettings = settings; + mResolver = resolver; + } + + public Builder acquireOverridesBuilder(SysuiTestCase test) { + return new Builder(this, test); + } + + public void clearOverrides(SysuiTestCase test) { + List<SettingOverrider> overrides = mOwners.remove(test); + if (overrides != null) { + overrides.forEach(override -> override.ensureReleased()); + } + } + + public Bundle call(String method, String arg, Bundle extras) { + // Methods are "GET_system", "GET_global", "PUT_secure", etc. + final String[] commands = method.split("_", 2); + final String op = commands[0]; + final String table = commands[1]; + + synchronized (mOverrideMap) { + SettingOverrider overrider = mOverrideMap.get(key(table, arg)); + if (overrider == null) { + // Fall through to real settings. + try { + if (DEBUG) Log.d(TAG, "Falling through to real settings " + method); + // TODO: Add our own version of caching to handle this. + Bundle call = mSettings.call(method, arg, extras); + call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY); + return call; + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + String value; + Bundle out = new Bundle(); + switch (op) { + case "GET": + value = overrider.get(table, arg); + if (value != null) { + out.putString(Settings.NameValueTable.VALUE, value); + } + break; + case "PUT": + value = extras.getString(Settings.NameValueTable.VALUE, null); + if (value != null) { + overrider.put(table, arg, value); + } else { + overrider.remove(table, arg); + } + break; + default: + throw new UnsupportedOperationException("Unknown command " + method); + } + return out; + } + } + + private void acquireSettings(SettingOverrider overridder, Set<String> keys, + SysuiTestCase owner) throws AcquireTimeoutException { + synchronized (mOwners) { + List<SettingOverrider> list = mOwners.get(owner); + if (list == null) { + list = new ArrayList<>(); + mOwners.put(owner, list); + } + list.add(overridder); + } + synchronized (mOverrideMap) { + for (int i = 0; i < MAX_TRIES; i++) { + if (checkKeys(keys, false)) break; + try { + if (DEBUG) Log.d(TAG, "Waiting for contention to finish"); + mOverrideMap.wait(WAIT_TIMEOUT); + } catch (InterruptedException e) { + } + } + checkKeys(keys, true); + for (String key : keys) { + if (DEBUG) Log.d(TAG, "Acquiring " + key); + mOverrideMap.put(key, overridder); + } + } + } + + private void releaseSettings(Set<String> keys) { + synchronized (mOverrideMap) { + for (String key : keys) { + if (DEBUG) Log.d(TAG, "Releasing " + key); + mOverrideMap.remove(key); + } + if (DEBUG) Log.d(TAG, "Notifying"); + mOverrideMap.notify(); + } + } + + @VisibleForTesting + public Object getLock() { + return mOverrideMap; + } + + private boolean checkKeys(Set<String> keys, boolean shouldThrow) + throws AcquireTimeoutException { + for (String key : keys) { + if (mOverrideMap.containsKey(key)) { + if (shouldThrow) { + throw new AcquireTimeoutException("Could not acquire " + key); + } + return false; + } + } + return true; + } + + public static class SettingOverrider { + private final Set<String> mValidKeys; + private final Map<String, String> mValueMap = new ArrayMap<>(); + private final FakeSettingsProvider mProvider; + private boolean mReleased; + + private SettingOverrider(Set<String> keys, FakeSettingsProvider provider) { + mValidKeys = new ArraySet<>(keys); + mProvider = provider; + } + + private void ensureReleased() { + if (!mReleased) { + release(); + } + } + + public void release() { + mProvider.releaseSettings(mValidKeys); + mReleased = true; + } + + private void putDirect(String key, String value) { + mValueMap.put(key, value); + } + + public void put(String table, String key, String value) { + if (!mValidKeys.contains(key(table, key))) { + throw new IllegalArgumentException("Key " + table + " " + key + + " not acquired for this overrider"); + } + mValueMap.put(key(table, key), value); + } + + public void remove(String table, String key) { + if (!mValidKeys.contains(key(table, key))) { + throw new IllegalArgumentException("Key " + table + " " + key + + " not acquired for this overrider"); + } + mValueMap.remove(key(table, key)); + } + + public String get(String table, String key) { + if (!mValidKeys.contains(key(table, key))) { + throw new IllegalArgumentException("Key " + table + " " + key + + " not acquired for this overrider"); + } + Log.d(TAG, "Get " + table + " " + key + " " + mValueMap.get(key(table, key))); + return mValueMap.get(key(table, key)); + } + + public static class Builder { + private final FakeSettingsProvider mProvider; + private final SysuiTestCase mOwner; + private Set<String> mKeys = new ArraySet<>(); + private Map<String, String> mValues = new ArrayMap<>(); + + private Builder(FakeSettingsProvider provider, SysuiTestCase test) { + mProvider = provider; + mOwner = test; + } + + public Builder addSetting(String table, String key) { + mKeys.add(key(table, key)); + return this; + } + + public Builder addSetting(String table, String key, String value) { + addSetting(table, key); + mValues.put(key(table, key), value); + return this; + } + + public SettingOverrider build() throws AcquireTimeoutException { + SettingOverrider overrider = new SettingOverrider(mKeys, mProvider); + mProvider.acquireSettings(overrider, mKeys, mOwner); + mValues.forEach((key, value) -> overrider.putDirect(key, value)); + return overrider; + } + } + } + + public static class AcquireTimeoutException extends Exception { + public AcquireTimeoutException(String str) { + super(str); + } + } + + private static String key(String table, String key) { + return table + "_" + key; + } + + /** + * Since the settings provider is cached inside android.provider.Settings, this must + * be gotten statically to ensure there is only one instance referenced. + * @param settings + */ + public static FakeSettingsProvider getFakeSettingsProvider(ContentProviderClient settings, + ContentResolver resolver) { + if (sInstance == null) { + sInstance = new FakeSettingsProvider(settings, resolver); + } + return sInstance; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java new file mode 100644 index 000000000000..63bb5e7f0e4a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 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.systemui.utils; + +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.content.ContentResolver; +import android.os.Handler; +import android.os.HandlerThread; +import android.provider.Settings; +import android.provider.Settings.Global; +import android.provider.Settings.Secure; +import android.support.test.runner.AndroidJUnit4; +import android.util.Log; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.utils.FakeSettingsProvider.AcquireTimeoutException; +import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class FakeSettingsProviderTest extends SysuiTestCase { + + public static final String NONEXISTENT_SETTING = "nonexistent_setting"; + private static final String TAG = "FakeSettingsProviderTest"; + private SettingOverrider mOverrider; + private ContentResolver mContentResolver; + + @Before + public void setup() throws AcquireTimeoutException { + mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder(this) + .addSetting("secure", NONEXISTENT_SETTING) + .addSetting("global", NONEXISTENT_SETTING, "initial value") + .addSetting("global", Global.DEVICE_PROVISIONED) + .build(); + mContentResolver = mContext.getContentResolver(); + } + + @After + public void teardown() { + if (mOverrider != null) { + mOverrider.release(); + } + } + + @Test + public void testInitialValueSecure() { + String value = Secure.getString(mContentResolver, NONEXISTENT_SETTING); + assertNull(value); + } + + @Test + public void testInitialValueGlobal() { + String value = Global.getString(mContentResolver, NONEXISTENT_SETTING); + assertEquals("initial value", value); + } + + @Test + public void testSeparateTables() { + Secure.putString(mContentResolver, NONEXISTENT_SETTING, "something"); + Global.putString(mContentResolver, NONEXISTENT_SETTING, "else"); + assertEquals("something", Secure.getString(mContentResolver, NONEXISTENT_SETTING)); + assertEquals("something", mOverrider.get("secure", NONEXISTENT_SETTING)); + assertEquals("else", Global.getString(mContentResolver, NONEXISTENT_SETTING)); + assertEquals("else", mOverrider.get("global", NONEXISTENT_SETTING)); + } + + @Test + public void testPassThrough() { + // Grab the value of a setting that is not overridden. + assertTrue(Secure.getInt(mContentResolver, Secure.USER_SETUP_COMPLETE, 0) != 0); + } + + @Test + public void testOverrideExisting() { + // Grab the value of a setting that is overridden and will be different than the actual + // value. + assertNull(Global.getString(mContentResolver, Global.DEVICE_PROVISIONED)); + } + + @Test + public void testRelease() { + // Verify different value. + assertNull(Global.getString(mContentResolver, Global.DEVICE_PROVISIONED)); + mOverrider.release(); + mOverrider = null; + // Verify actual value after release. + assertEquals("1", Global.getString(mContentResolver, Global.DEVICE_PROVISIONED)); + } + + @Test + public void testAutoRelease() throws Exception { + super.cleanup(); + mContext.getSettingsProvider().acquireOverridesBuilder(this) + .addSetting("global", Global.DEVICE_PROVISIONED) + .build(); + } + + @Test + public void testContention() throws AcquireTimeoutException, InterruptedException { + SettingOverrider[] overriders = new SettingOverrider[2]; + Object lock = new Object(); + String secure = "secure"; + String key = "something shared"; + String[] result = new String[1]; + overriders[0] = mContext.getSettingsProvider().acquireOverridesBuilder(this) + .addSetting(secure, key, "Some craziness") + .build(); + synchronized (lock) { + HandlerThread t = runOnHandler(() -> { + try { + // Grab the lock that will be used for the settings ownership to ensure + // we have some contention going on. + synchronized (mContext.getSettingsProvider().getLock()) { + synchronized (lock) { + // Let the other thread know to release the settings, but it won't + // be able to until this thread waits in the build() method. + lock.notify(); + } + overriders[1] = mContext.getSettingsProvider() + .acquireOverridesBuilder(FakeSettingsProviderTest.this) + .addSetting(secure, key, "default value") + .build(); + // Ensure that the default is the one we set, and not left over from + // the other setting override. + result[0] = Settings.Secure.getString(mContentResolver, key); + synchronized (lock) { + // Let the main thread know we are done. + lock.notify(); + } + } + } catch (AcquireTimeoutException e) { + Log.e(TAG, "Couldn't acquire setting", e); + } + }); + // Wait for the thread to hold the acquire lock, then release the settings. + lock.wait(); + overriders[0].release(); + // Wait for the thread to be done getting the value. + lock.wait(); + // Quit and cleanup. + t.quitSafely(); + assertNotNull(overriders[1]); + overriders[1].release(); + } + // Verify the value was the expected one from the thread's SettingOverride. + assertEquals("default value", result[0]); + } + + private HandlerThread runOnHandler(Runnable r) { + HandlerThread t = new HandlerThread("Test Thread"); + t.start(); + new Handler(t.getLooper()).post(r); + return t; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java new file mode 100644 index 000000000000..517982361424 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 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.systemui.utils; + +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ContextWrapper; +import android.provider.Settings; + +public class TestableContext extends ContextWrapper { + + private final FakeContentResolver mFakeContentResolver; + private final FakeSettingsProvider mSettingsProvider; + + public TestableContext(Context base) { + super(base); + mFakeContentResolver = new FakeContentResolver(base); + ContentProviderClient settings = base.getContentResolver() + .acquireContentProviderClient(Settings.AUTHORITY); + mSettingsProvider = FakeSettingsProvider.getFakeSettingsProvider(settings, + mFakeContentResolver); + mFakeContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider); + } + + public FakeSettingsProvider getSettingsProvider() { + return mSettingsProvider; + } + + @Override + public FakeContentResolver getContentResolver() { + return mFakeContentResolver; + } + + @Override + public Context getApplicationContext() { + // Return this so its always a TestableContext. + return this; + } +} |