Merge "Guard against incorrect context use." into oc-dev
This commit is contained in:
@@ -47,9 +47,8 @@ public class DozeConfigurationTest extends SysuiTestCase {
|
||||
return;
|
||||
}
|
||||
|
||||
mContext.getSettingsProvider().acquireOverridesBuilder()
|
||||
.addSetting("secure", Settings.Secure.DOZE_ALWAYS_ON, null)
|
||||
.build();
|
||||
Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON,
|
||||
null);
|
||||
|
||||
assertFalse(mDozeConfig.alwaysOnEnabled(UserHandle.USER_CURRENT));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.systemui.statusbar.policy.NetworkController.IconState;
|
||||
import android.testing.TestableSettings.SettingOverrider;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -92,11 +91,10 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
|
||||
attr);
|
||||
|
||||
// Must set the Settings value before instantiating the NetworkControllerImpl due to bugs in
|
||||
// TestableSettings.
|
||||
SettingOverrider settingsOverrider =
|
||||
mContext.getSettingsProvider().acquireOverridesBuilder()
|
||||
.addSetting("global", Settings.Global.NETWORK_SCORING_UI_ENABLED, "1")
|
||||
.build();
|
||||
// TestableSettingsProvider.
|
||||
Settings.Global.putString(mContext.getContentResolver(),
|
||||
Settings.Global.NETWORK_SCORING_UI_ENABLED,
|
||||
"1");
|
||||
super.setUp(); // re-instantiate NetworkControllImpl now that setting has been updated
|
||||
setupNetworkScoreManager();
|
||||
|
||||
@@ -131,8 +129,6 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
|
||||
assertEquals("SD Badge is set",
|
||||
Utils.getWifiBadgeResource(NetworkBadging.BADGING_SD),
|
||||
iconState.iconOverlay);
|
||||
|
||||
settingsOverrider.release();
|
||||
}
|
||||
|
||||
private void setupNetworkScoreManager() {
|
||||
|
||||
@@ -43,7 +43,7 @@ import org.junit.runners.model.Statement;
|
||||
* <ul>
|
||||
* <li>System services can be mocked out with {@link #addMockSystemService}</li>
|
||||
* <li>Service binding can be mocked out with {@link #addMockService}</li>
|
||||
* <li>Settings support {@link TestableSettings}</li>
|
||||
* <li>Settings support {@link TestableSettingsProvider}</li>
|
||||
* <li>Has support for {@link LeakCheck} for services and receivers</li>
|
||||
* </ul>
|
||||
*
|
||||
@@ -59,7 +59,7 @@ import org.junit.runners.model.Statement;
|
||||
public class TestableContext extends ContextWrapper implements TestRule {
|
||||
|
||||
private final TestableContentResolver mTestableContentResolver;
|
||||
private final TestableSettings mSettingsProvider;
|
||||
private final TestableSettingsProvider mSettingsProvider;
|
||||
|
||||
private ArrayMap<String, Object> mMockSystemServices;
|
||||
private ArrayMap<ComponentName, IBinder> mMockServices;
|
||||
@@ -79,9 +79,8 @@ public class TestableContext extends ContextWrapper implements TestRule {
|
||||
mTestableContentResolver = new TestableContentResolver(base);
|
||||
ContentProviderClient settings = base.getContentResolver()
|
||||
.acquireContentProviderClient(Settings.AUTHORITY);
|
||||
mSettingsProvider = TestableSettings.getFakeSettingsProvider(settings,
|
||||
mTestableContentResolver);
|
||||
mTestableContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider.getProvider());
|
||||
mSettingsProvider = TestableSettingsProvider.getFakeSettingsProvider(settings);
|
||||
mTestableContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
|
||||
mReceiver = check != null ? check.getTracker("receiver") : null;
|
||||
mService = check != null ? check.getTracker("service") : null;
|
||||
mComponent = check != null ? check.getTracker("component") : null;
|
||||
@@ -129,7 +128,7 @@ public class TestableContext extends ContextWrapper implements TestRule {
|
||||
return super.getSystemService(name);
|
||||
}
|
||||
|
||||
public TestableSettings getSettingsProvider() {
|
||||
TestableSettingsProvider getSettingsProvider() {
|
||||
return mSettingsProvider;
|
||||
}
|
||||
|
||||
@@ -236,12 +235,12 @@ public class TestableContext extends ContextWrapper implements TestRule {
|
||||
return new TestWatcher() {
|
||||
@Override
|
||||
protected void succeeded(Description description) {
|
||||
mSettingsProvider.clearOverrides();
|
||||
mSettingsProvider.clearValuesAndCheck(TestableContext.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed(Throwable e, Description description) {
|
||||
mSettingsProvider.clearOverrides();
|
||||
mSettingsProvider.clearValuesAndCheck(TestableContext.this);
|
||||
}
|
||||
}.apply(base, description);
|
||||
}
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.testing;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
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.testing.TestableSettings.SettingOverrider.Builder;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
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 TestableSettings {
|
||||
|
||||
private static final String TAG = "TestableSettings";
|
||||
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 static TestableSettingsProvider sInstance;
|
||||
|
||||
private final TestableSettingsProvider mProvider;
|
||||
|
||||
private TestableSettings(TestableSettingsProvider provider) {
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
public Builder acquireOverridesBuilder() {
|
||||
return new Builder(this);
|
||||
}
|
||||
|
||||
public void clearOverrides() {
|
||||
List<SettingOverrider> overrides = mProvider.mOwners.remove(this);
|
||||
if (overrides != null) {
|
||||
overrides.forEach(override -> override.ensureReleased());
|
||||
}
|
||||
}
|
||||
|
||||
private void acquireSettings(SettingOverrider overridder, Set<String> keys)
|
||||
throws AcquireTimeoutException {
|
||||
mProvider.acquireSettings(overridder, keys, this);
|
||||
}
|
||||
|
||||
ContentProvider getProvider() {
|
||||
return mProvider;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Object getLock() {
|
||||
return mProvider.mOverrideMap;
|
||||
}
|
||||
|
||||
public static class SettingOverrider {
|
||||
private final Set<String> mValidKeys;
|
||||
private final Map<String, String> mValueMap = new ArrayMap<>();
|
||||
private final TestableSettings mSettings;
|
||||
private boolean mReleased;
|
||||
public Throwable mObtain;
|
||||
|
||||
private SettingOverrider(Set<String> keys, TestableSettings provider) {
|
||||
mValidKeys = new ArraySet<>(keys);
|
||||
mSettings = provider;
|
||||
}
|
||||
|
||||
private void ensureReleased() {
|
||||
if (!mReleased) {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
mSettings.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 TestableSettings mProvider;
|
||||
private Set<String> mKeys = new ArraySet<>();
|
||||
private Map<String, String> mValues = new ArrayMap<>();
|
||||
|
||||
private Builder(TestableSettings provider) {
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
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);
|
||||
mValues.forEach((key, value) -> overrider.putDirect(key, value));
|
||||
return overrider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestableSettingsProvider extends MockContentProvider {
|
||||
|
||||
private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>();
|
||||
private final Map<Object, List<SettingOverrider>> mOwners = new ArrayMap<>();
|
||||
|
||||
private final ContentProviderClient mSettings;
|
||||
private final ContentResolver mResolver;
|
||||
|
||||
public TestableSettingsProvider(ContentProviderClient settings, ContentResolver resolver) {
|
||||
mSettings = settings;
|
||||
mResolver = resolver;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkKeysLocked(Set<String> keys, boolean shouldThrow)
|
||||
throws AcquireTimeoutException {
|
||||
for (String key : keys) {
|
||||
if (mOverrideMap.containsKey(key)) {
|
||||
if (shouldThrow) {
|
||||
if (DEBUG) Log.e(TAG, "Lock obtained at",
|
||||
mOverrideMap.get(key).mObtain);
|
||||
throw new AcquireTimeoutException("Could not acquire " + key,
|
||||
mOverrideMap.get(key).mObtain);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void acquireSettings(SettingOverrider overridder, Set<String> keys,
|
||||
Object 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 (checkKeysLocked(keys, false)) break;
|
||||
try {
|
||||
if (DEBUG) Log.d(TAG, "Waiting for contention to finish");
|
||||
mOverrideMap.wait(WAIT_TIMEOUT);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
overridder.mObtain = new Throwable();
|
||||
checkKeysLocked(keys, true);
|
||||
for (String key : keys) {
|
||||
if (DEBUG) Log.d(TAG, "Acquiring " + key);
|
||||
mOverrideMap.put(key, overridder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class AcquireTimeoutException extends Exception {
|
||||
public AcquireTimeoutException(String str, Throwable cause) {
|
||||
super(str, cause);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
public static TestableSettings getFakeSettingsProvider(ContentProviderClient settings,
|
||||
ContentResolver resolver) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new TestableSettingsProvider(settings, resolver);
|
||||
}
|
||||
return new TestableSettings(sInstance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.testing;
|
||||
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.test.mock.MockContentProvider;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Allows calls to android.provider.Settings to be tested easier.
|
||||
*
|
||||
* This provides a simple copy-on-write implementation of settings that gets cleared
|
||||
* at the end of each test.
|
||||
*/
|
||||
public class TestableSettingsProvider extends MockContentProvider {
|
||||
|
||||
private static final String TAG = "TestableSettingsProvider";
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String MY_UNIQUE_KEY = "Key_" + TestableSettingsProvider.class.getName();
|
||||
private static TestableSettingsProvider sInstance;
|
||||
|
||||
private final ContentProviderClient mSettings;
|
||||
|
||||
private final HashMap<String, String> mValues = new HashMap<>();
|
||||
|
||||
private TestableSettingsProvider(ContentProviderClient settings) {
|
||||
mSettings = settings;
|
||||
}
|
||||
|
||||
void clearValuesAndCheck(Context context) {
|
||||
mValues.put(key("global", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
|
||||
mValues.put(key("secure", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
|
||||
mValues.put(key("system", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
|
||||
|
||||
// Verify that if any test is using TestableContext, they all have the correct settings
|
||||
// provider.
|
||||
assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
|
||||
Settings.Global.getString(context.getContentResolver(), MY_UNIQUE_KEY));
|
||||
assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
|
||||
Settings.Secure.getString(context.getContentResolver(), MY_UNIQUE_KEY));
|
||||
assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
|
||||
Settings.System.getString(context.getContentResolver(), MY_UNIQUE_KEY));
|
||||
|
||||
mValues.clear();
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
String k = key(table, arg);
|
||||
String value;
|
||||
Bundle out = new Bundle();
|
||||
switch (op) {
|
||||
case "GET":
|
||||
if (mValues.containsKey(k)) {
|
||||
value = mValues.get(k);
|
||||
if (value != null) {
|
||||
out.putString(Settings.NameValueTable.VALUE, value);
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "PUT":
|
||||
value = extras.getString(Settings.NameValueTable.VALUE, null);
|
||||
mValues.put(k, value);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown command " + method);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
static TestableSettingsProvider getFakeSettingsProvider(ContentProviderClient settings) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new TestableSettingsProvider(settings);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.testing;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.Global;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TestableSettingsProviderTest {
|
||||
|
||||
public static final String NONEXISTENT_SETTING = "nonexistent_setting";
|
||||
private static final String TAG = "TestableSettingsProviderTest";
|
||||
private ContentResolver mContentResolver;
|
||||
@Rule
|
||||
public final TestableContext mContext =
|
||||
new TestableContext(InstrumentationRegistry.getContext());
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
Settings.Secure.putString(mContentResolver, NONEXISTENT_SETTING, null);
|
||||
Settings.Global.putString(mContentResolver, NONEXISTENT_SETTING, "initial value");
|
||||
Settings.Global.putString(mContentResolver, Global.DEVICE_PROVISIONED, null);
|
||||
}
|
||||
|
||||
@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("else", Global.getString(mContentResolver, 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));
|
||||
mContext.getSettingsProvider().clearValuesAndCheck(mContext);
|
||||
// Verify actual value after release.
|
||||
assertEquals("1", Global.getString(mContentResolver, Global.DEVICE_PROVISIONED));
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.testing;
|
||||
|
||||
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.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.testing.TestableSettings.AcquireTimeoutException;
|
||||
import android.testing.TestableSettings.SettingOverrider;
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TestableSettingsTest {
|
||||
|
||||
public static final String NONEXISTENT_SETTING = "nonexistent_setting";
|
||||
private static final String TAG = "TestableSettingsTest";
|
||||
private SettingOverrider mOverrider;
|
||||
private ContentResolver mContentResolver;
|
||||
@Rule
|
||||
public final TestableContext mContext =
|
||||
new TestableContext(InstrumentationRegistry.getContext());
|
||||
|
||||
@Before
|
||||
public void setup() throws AcquireTimeoutException {
|
||||
mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder()
|
||||
.addSetting("secure", NONEXISTENT_SETTING)
|
||||
.addSetting("global", NONEXISTENT_SETTING, "initial value")
|
||||
.addSetting("global", Global.DEVICE_PROVISIONED)
|
||||
.build();
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
}
|
||||
|
||||
@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 {
|
||||
mOverrider.release();
|
||||
mOverrider = null;
|
||||
mContext.getSettingsProvider().acquireOverridesBuilder()
|
||||
.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()
|
||||
.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()
|
||||
.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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user