diff --git a/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java new file mode 100644 index 0000000000000..808f4dd7b84ef --- /dev/null +++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProvider.java @@ -0,0 +1,130 @@ +/* + * 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.internal.util; + +import android.net.Uri; +import android.os.Bundle; +import android.provider.Settings; +import android.test.mock.MockContentProvider; +import android.util.Log; + +import java.util.HashMap; + +/** + * Fake for system settings. + * + * To use, ensure that the Context used by the test code returns a ContentResolver that uses this + * provider for the Settings authority: + * + * class MyTestContext extends MockContext { + * ... + * private final MockContentResolver mContentResolver; + * public MyTestContext(...) { + * ... + * mContentResolver = new MockContentResolver(); + * mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + * } + * ... + * @Override + * public ContentResolver getContentResolver() { + * return mContentResolver; + * } + * + * As long as the code under test is using the test Context, the actual code under test does not + * need to be modified, and can access Settings using the normal static methods: + * + * Settings.Global.getInt(cr, "my_setting", 0); // Returns 0. + * Settings.Global.putInt(cr, "my_setting", 5); + * Settings.Global.getInt(cr, "my_setting", 0); // Returns 5. + * + * Note that this class cannot be used in the same process as real settings. This is because it + * works by passing an alternate ContentResolver to Settings operations. Unfortunately, the Settings + * class only fetches the content provider from the passed-in ContentResolver the first time it's + * used, and after that stores it in a per-process static. + * + * TODO: evaluate implementing settings change notifications. This would require: + * + * 1. Making ContentResolver#registerContentObserver non-final and overriding it in + * MockContentResolver. + * 2. Making FakeSettingsProvider take a ContentResolver argument. + * 3. Calling ContentResolver#notifyChange(getUriFor(table, arg), ...) on every settings change. + */ +public class FakeSettingsProvider extends MockContentProvider { + + private static final String TAG = FakeSettingsProvider.class.getSimpleName(); + private static final boolean DBG = false; + private static final String[] TABLES = { "system", "secure", "global" }; + + private final HashMap> mTables = new HashMap<>(); + + public FakeSettingsProvider() { + for (int i = 0; i < TABLES.length; i++) { + mTables.put(TABLES[i], new HashMap()); + } + } + + private Uri getUriFor(String table, String key) { + switch (table) { + case "system": + return Settings.System.getUriFor(key); + case "secure": + return Settings.Secure.getUriFor(key); + case "global": + return Settings.Global.getUriFor(key); + default: + throw new UnsupportedOperationException("Unknown settings table " + table); + } + } + + public Bundle call(String method, String arg, Bundle extras) { + // Methods are "GET_system", "GET_global", "PUT_secure", etc. + String[] commands = method.split("_", 2); + String op = commands[0]; + String table = commands[1]; + + Bundle out = new Bundle(); + String value; + switch (op) { + case "GET": + value = mTables.get(table).get(arg); + if (value != null) { + if (DBG) { + Log.d(TAG, String.format("Returning fake setting %s.%s = %s", + table, arg, value)); + } + out.putString(Settings.NameValueTable.VALUE, value); + } + break; + case "PUT": + value = extras.getString(Settings.NameValueTable.VALUE, null); + if (DBG) { + Log.d(TAG, String.format("Inserting fake setting %s.%s = %s", + table, arg, value)); + } + if (value != null) { + mTables.get(table).put(arg, value); + } else { + mTables.get(table).remove(arg); + } + break; + default: + throw new UnsupportedOperationException("Unknown command " + method); + } + + return out; + } +} diff --git a/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java new file mode 100644 index 0000000000000..a18737c42e529 --- /dev/null +++ b/services/tests/servicestests/src/com/android/internal/util/FakeSettingsProviderTest.java @@ -0,0 +1,58 @@ +/* + * 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.internal.util; + +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.net.Uri; +import android.provider.Settings; +import android.test.AndroidTestCase; +import android.test.mock.MockContentResolver; +import android.test.mock.MockContext; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Unit tests for FakeSettingsProvider. + */ +public class FakeSettingsProviderTest extends AndroidTestCase { + + private MockContentResolver mCr; + + @Override + public void setUp() throws Exception { + mCr = new MockContentResolver(); + mCr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + } + + @SmallTest + public void testBasicOperation() throws Exception { + String settingName = Settings.System.SCREEN_BRIGHTNESS; + + try { + Settings.System.getInt(mCr, settingName); + fail("FakeSettingsProvider should start off empty."); + } catch (Settings.SettingNotFoundException expected) {} + + // Check that fake settings can be written and read back. + Settings.System.putInt(mCr, settingName, 123); + assertEquals(123, Settings.System.getInt(mCr, settingName)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index 4fae4a732aad3..00ba06988d869 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; @@ -61,12 +62,15 @@ import android.os.Messenger; import android.os.MessageQueue.IdleHandler; import android.os.Process; import android.os.SystemClock; +import android.provider.Settings; import android.test.AndroidTestCase; +import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import android.util.LogPrinter; +import com.android.internal.util.FakeSettingsProvider; import com.android.internal.util.WakeupMessage; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; @@ -118,8 +122,12 @@ public class ConnectivityServiceTest extends AndroidTestCase { } private class MockContext extends BroadcastInterceptingContext { + private final MockContentResolver mContentResolver; + MockContext(Context base) { super(base); + mContentResolver = new MockContentResolver(); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); } @Override @@ -135,10 +143,15 @@ public class ConnectivityServiceTest extends AndroidTestCase { } @Override - public Object getSystemService (String name) { + public Object getSystemService(String name) { if (name == Context.CONNECTIVITY_SERVICE) return mCm; return super.getSystemService(name); } + + @Override + public ContentResolver getContentResolver() { + return mContentResolver; + } } /**