Merge "android.provider.Setting test utilities"
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -16,26 +16,24 @@
|
||||
|
||||
package com.android.keyguard;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class KeyguardMessageAreaTest extends SysuiTestCase {
|
||||
private Context mContext = InstrumentationRegistry.getTargetContext();
|
||||
private Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private KeyguardMessageArea mMessageArea;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user