From 9d501759d8b68e86ddb6cca1fd3a486b82918af1 Mon Sep 17 00:00:00 2001 From: Linus Tufvesson Date: Tue, 12 Feb 2019 11:33:22 +0000 Subject: [PATCH] Add TestableDeviceConfig Allow for better unittesting when using DeviceConfig. This allows unit test to run without having to interact with the real values on the device. Ported AppCompactorTest as an example. Forked DeviceConfigTest into TestableDeviceConfigTest. Bug: 123035013 Test: atest AppCompactorTest TestableDeviceConfigTest Test: atest android.provider.DeviceConfigTest Change-Id: Ibc54cffd8e009c55ee95b87995843c9938c67292 --- .../android/provider/DeviceConfigTest.java | 83 ++--- .../tests/mockingservicestests/Android.bp | 1 + .../android/server/am/AppCompactorTest.java | 286 ++++++++---------- .../testables/TestableDeviceConfig.java | 131 ++++++++ .../testables/TestableDeviceConfigTest.java | 115 +++++++ 5 files changed, 400 insertions(+), 216 deletions(-) rename services/tests/{servicestests => mockingservicestests}/src/com/android/server/am/AppCompactorTest.java (55%) create mode 100644 services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java create mode 100644 services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java index 17e9654b651a3..5d5754bc41beb 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -18,15 +18,11 @@ package android.provider; import static android.provider.DeviceConfig.OnPropertyChangedListener; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.fail; +import static com.google.common.truth.Truth.assertThat; import android.app.ActivityThread; import android.content.ContentResolver; import android.os.Bundle; -import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -37,8 +33,8 @@ import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** Tests that ensure appropriate settings are backed up. */ @Presubmit @@ -51,8 +47,6 @@ public class DeviceConfigTest { private static final String sValue = "value1"; private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec - private final Object mLock = new Object(); - @After public void cleanUp() { deleteViaContentProvider(sNamespace, sKey); @@ -61,14 +55,14 @@ public class DeviceConfigTest { @Test public void getProperty_empty() { String result = DeviceConfig.getProperty(sNamespace, sKey); - assertNull(result); + assertThat(result).isNull(); } @Test public void setAndGetProperty_sameNamespace() { DeviceConfig.setProperty(sNamespace, sKey, sValue, false); String result = DeviceConfig.getProperty(sNamespace, sKey); - assertEquals(sValue, result); + assertThat(result).isEqualTo(sValue); } @Test @@ -76,7 +70,7 @@ public class DeviceConfigTest { String newNamespace = "namespace2"; DeviceConfig.setProperty(sNamespace, sKey, sValue, false); String result = DeviceConfig.getProperty(newNamespace, sKey); - assertNull(result); + assertThat(result).isNull(); } @Test @@ -86,9 +80,9 @@ public class DeviceConfigTest { DeviceConfig.setProperty(sNamespace, sKey, sValue, false); DeviceConfig.setProperty(newNamespace, sKey, newValue, false); String result = DeviceConfig.getProperty(sNamespace, sKey); - assertEquals(sValue, result); + assertThat(result).isEqualTo(sValue); result = DeviceConfig.getProperty(newNamespace, sKey); - assertEquals(newValue, result); + assertThat(result).isEqualTo(newValue); // clean up deleteViaContentProvider(newNamespace, sKey); @@ -100,59 +94,30 @@ public class DeviceConfigTest { DeviceConfig.setProperty(sNamespace, sKey, sValue, false); DeviceConfig.setProperty(sNamespace, sKey, newValue, false); String result = DeviceConfig.getProperty(sNamespace, sKey); - assertEquals(newValue, result); + assertThat(result).isEqualTo(newValue); } @Test - public void testListener() { - setPropertyAndAssertSuccessfulChange(sNamespace, sKey, sValue); - } + public void testListener() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); - private void setPropertyAndAssertSuccessfulChange(String setNamespace, String setName, - String setValue) { - final AtomicBoolean success = new AtomicBoolean(); + OnPropertyChangedListener changeListener = (namespace, name, value) -> { + assertThat(namespace).isEqualTo(sNamespace); + assertThat(name).isEqualTo(sKey); + assertThat(value).isEqualTo(sValue); + countDownLatch.countDown(); + }; - OnPropertyChangedListener changeListener = new OnPropertyChangedListener() { - @Override - public void onPropertyChanged(String namespace, String name, String value) { - assertEquals(setNamespace, namespace); - assertEquals(setName, name); - assertEquals(setValue, value); - success.set(true); - - synchronized (mLock) { - mLock.notifyAll(); - } - } - }; - Executor executor = ActivityThread.currentApplication().getMainExecutor(); - DeviceConfig.addOnPropertyChangedListener(setNamespace, executor, changeListener); try { - DeviceConfig.setProperty(setNamespace, setName, setValue, false); - - final long startTimeMillis = SystemClock.uptimeMillis(); - synchronized (mLock) { - while (true) { - if (success.get()) { - return; - } - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - if (elapsedTimeMillis >= WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS) { - fail("Could not change setting for " - + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS + " ms"); - } - final long remainingTimeMillis = WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS - - elapsedTimeMillis; - try { - mLock.wait(remainingTimeMillis); - } catch (InterruptedException ie) { - /* ignore */ - } - } - } + DeviceConfig.addOnPropertyChangedListener(sNamespace, + ActivityThread.currentApplication().getMainExecutor(), changeListener); + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); } finally { DeviceConfig.removeOnPropertyChangedListener(changeListener); } + } private static boolean deleteViaContentProvider(String namespace, String key) { @@ -160,7 +125,7 @@ public class DeviceConfigTest { String compositeName = namespace + "/" + key; Bundle result = resolver.call( DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null); - assertNotNull(result); + assertThat(result).isNotNull(); return compositeName.equals(result.getString(Settings.NameValueTable.VALUE)); } diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index ebc816dc45dac..782196dc00485 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -23,6 +23,7 @@ android_test { "androidx.test.runner", "mockito-target-extended-minus-junit4", "platform-test-annotations", + "truth-prebuilt", ], libs: [ diff --git a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java similarity index 55% rename from services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java rename to services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java index 63015be6ef3f6..d32f1f77b88f8 100644 --- a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java @@ -28,28 +28,24 @@ import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION; import static com.android.server.am.ActivityManagerService.Injector; import static com.android.server.am.AppCompactor.compactActionIntToString; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static com.google.common.truth.Truth.assertThat; import android.os.Handler; import android.os.HandlerThread; import android.provider.DeviceConfig; -import android.support.test.uiautomator.UiDevice; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; import com.android.server.appop.AppOpsService; +import com.android.server.testables.TestableDeviceConfig; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; import java.io.File; -import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -59,42 +55,21 @@ import java.util.concurrent.TimeUnit; * Build/Install/Run: * atest FrameworksServicesTests:AppCompactorTest */ -@RunWith(AndroidJUnit4.class) +@RunWith(MockitoJUnitRunner.class) public final class AppCompactorTest { - private static final String CLEAR_DEVICE_CONFIG_KEY_CMD = - "device_config delete activity_manager"; - - @Mock private AppOpsService mAppOpsService; + @Mock + private AppOpsService mAppOpsService; private AppCompactor mCompactorUnderTest; private HandlerThread mHandlerThread; private Handler mHandler; private CountDownLatch mCountDown; - private static void clearDeviceConfig() throws IOException { - UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_USE_COMPACTION); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_1); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_2); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_1); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_2); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_3); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_4); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_STATSD_SAMPLE_RATE); - } + @Rule + public TestableDeviceConfig mDeviceConfig = new TestableDeviceConfig(); @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - clearDeviceConfig(); + public void setUp() { mHandlerThread = new HandlerThread(""); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); @@ -111,38 +86,37 @@ public final class AppCompactorTest { } @After - public void tearDown() throws IOException { + public void tearDown() { mHandlerThread.quit(); mCountDown = null; - clearDeviceConfig(); } @Test public void init_setsDefaults() { mCompactorUnderTest.init(); - assertThat(mCompactorUnderTest.useCompaction(), - is(mCompactorUnderTest.DEFAULT_USE_COMPACTION)); - assertThat(mCompactorUnderTest.mCompactActionSome, is( - compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1))); - assertThat(mCompactorUnderTest.mCompactActionFull, is( - compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2))); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE)); + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE); } @Test public void init_withDeviceConfigSetsParameters() { // When the DeviceConfig already has a flag value stored (note this test will need to // change if the default value changes from false). - assertThat(mCompactorUnderTest.DEFAULT_USE_COMPACTION, is(false)); + assertThat(AppCompactor.DEFAULT_USE_COMPACTION).isFalse(); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_USE_COMPACTION, "true", false); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, @@ -169,62 +143,63 @@ public final class AppCompactorTest { // Then calling init will read and set that flag. mCompactorUnderTest.init(); - assertThat(mCompactorUnderTest.useCompaction(), is(true)); - assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true)); + assertThat(mCompactorUnderTest.useCompaction()).isTrue(); + assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue(); - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1))); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1)); - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f)); + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1)); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); } @Test public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException { - assertThat(mCompactorUnderTest.useCompaction(), - is(mCompactorUnderTest.DEFAULT_USE_COMPACTION)); + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); // When we call init and change some the flag value... mCompactorUnderTest.init(); mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_USE_COMPACTION, "true", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then that new flag value is updated in the implementation. - assertThat(mCompactorUnderTest.useCompaction(), is(true)); - assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true)); + assertThat(mCompactorUnderTest.useCompaction()).isTrue(); + assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue(); // And again, setting the flag the other way. mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_USE_COMPACTION, "false", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.useCompaction(), is(false)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.useCompaction()).isFalse(); } @Test public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException { - assertThat(mCompactorUnderTest.useCompaction(), - is(mCompactorUnderTest.DEFAULT_USE_COMPACTION)); + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); mCompactorUnderTest.init(); // When we push an invalid flag value... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_USE_COMPACTION, "foobar", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then we set the default. - assertThat(mCompactorUnderTest.useCompaction(), is(AppCompactor.DEFAULT_USE_COMPACTION)); + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); } @Test @@ -236,19 +211,19 @@ public final class AppCompactorTest { // There are four possible values for compactAction[Some|Full]. for (int i = 1; i < 5; i++) { mCountDown = new CountDownLatch(2); - int expectedSome = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1; + int expectedSome = (AppCompactor.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1; DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false); - int expectedFull = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1; + int expectedFull = (AppCompactor.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1; DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then the updates are reflected in the flags. - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString(expectedSome))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString(expectedFull))); + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(expectedSome)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(expectedFull)); } } @@ -262,25 +237,25 @@ public final class AppCompactorTest { KEY_COMPACT_ACTION_1, "foo", false); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_2, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then the default values are reflected in the flag - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2))); + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); mCountDown = new CountDownLatch(2); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_1, "", false); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_ACTION_2, "", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2))); + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); } @Test @@ -301,22 +276,22 @@ public final class AppCompactorTest { DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_THROTTLE_4, Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then those flags values are reflected in the compactor. - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1)); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1); } @Test public void compactThrottle_listensToDeviceConfigChangesBadValues() - throws IOException, InterruptedException { + throws InterruptedException { mCompactorUnderTest.init(); // When one of the throttles is overridden with a bad value... @@ -324,58 +299,55 @@ public final class AppCompactorTest { DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_THROTTLE_1, "foo", false); // Then all the throttles have the defaults set. - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - clearDeviceConfig(); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); // Repeat for each of the throttle keys. mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_THROTTLE_2, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - clearDeviceConfig(); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_THROTTLE_3, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - clearDeviceConfig(); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_THROTTLE_4, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); } @Test @@ -387,11 +359,11 @@ public final class AppCompactorTest { DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_STATSD_SAMPLE_RATE, Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f)); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); } @Test @@ -403,11 +375,11 @@ public final class AppCompactorTest { mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE)); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE); } @Test @@ -420,19 +392,19 @@ public final class AppCompactorTest { DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_STATSD_SAMPLE_RATE, Float.toString(-1.0f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then the values is capped in the range. - assertThat(mCompactorUnderTest.mStatsdSampleRate, is(0.0f)); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(0.0f); mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, KEY_COMPACT_STATSD_SAMPLE_RATE, Float.toString(1.01f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); // Then the values is capped in the range. - assertThat(mCompactorUnderTest.mStatsdSampleRate, is(1.0f)); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(1.0f); } private class TestInjector extends Injector { diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java new file mode 100644 index 0000000000000..b76682269c2e2 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 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.server.testables; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; + +import android.provider.DeviceConfig; +import android.util.Pair; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; + +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; + +/** + * TestableDeviceConfig uses ExtendedMockito to replace the real implementation of DeviceConfig + * with essentially a local HashMap in the callers process. This allows for unit testing that do not + * modify the real DeviceConfig on the device at all. + * + *

TestableDeviceConfig should be defined as a rule on your test so it can clean up after itself. + * Like the following:

+ *
+ * @Rule
+ * public final TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig();
+ * 
+ */ +public final class TestableDeviceConfig implements TestRule { + + private StaticMockitoSession mMockitoSession; + private Map> + mOnPropertyChangedListenerMap = new HashMap<>(); + private Map mKeyValueMap = new ConcurrentHashMap<>(); + + /** + * Clears out all local overrides. + */ + public void clearDeviceConfig() { + mKeyValueMap.clear(); + } + + @Override + public Statement apply(Statement base, Description description) { + mMockitoSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .spyStatic(DeviceConfig.class) + .startMocking(); + + doAnswer((Answer) invocationOnMock -> { + String namespace = invocationOnMock.getArgument(0); + Executor executor = invocationOnMock.getArgument(1); + DeviceConfig.OnPropertyChangedListener onPropertyChangedListener = + invocationOnMock.getArgument(2); + mOnPropertyChangedListenerMap.put( + onPropertyChangedListener, new Pair<>(namespace, executor)); + return null; + }).when(() -> DeviceConfig.addOnPropertyChangedListener( + anyString(), any(Executor.class), + any(DeviceConfig.OnPropertyChangedListener.class))); + + doAnswer((Answer) invocationOnMock -> { + String namespace = invocationOnMock.getArgument(0); + String name = invocationOnMock.getArgument(1); + String value = invocationOnMock.getArgument(2); + mKeyValueMap.put(getKey(namespace, name), value); + for (DeviceConfig.OnPropertyChangedListener listener : + mOnPropertyChangedListenerMap.keySet()) { + if (namespace.equals(mOnPropertyChangedListenerMap.get(listener).first)) { + mOnPropertyChangedListenerMap.get(listener).second.execute( + () -> listener.onPropertyChanged(namespace, name, value)); + } + } + return true; + } + ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean())); + + doAnswer((Answer) invocationOnMock -> { + String namespace = invocationOnMock.getArgument(0); + String name = invocationOnMock.getArgument(1); + return mKeyValueMap.get(getKey(namespace, name)); + }).when(() -> DeviceConfig.getProperty(anyString(), anyString())); + + + return new TestWatcher() { + @Override + protected void succeeded(Description description) { + mMockitoSession.finishMocking(); + mOnPropertyChangedListenerMap.clear(); + } + + @Override + protected void failed(Throwable e, Description description) { + mMockitoSession.finishMocking(e); + mOnPropertyChangedListenerMap.clear(); + } + }.apply(base, description); + } + + private static String getKey(String namespace, String name) { + return namespace + "/" + name; + } + +} diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java new file mode 100644 index 0000000000000..39b5840f12d88 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 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.server.testables; + +import static android.provider.DeviceConfig.OnPropertyChangedListener; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.ActivityThread; +import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** Tests that ensure appropriate settings are backed up. */ +@Presubmit +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TestableDeviceConfigTest { + private static final String sNamespace = "namespace1"; + private static final String sKey = "key1"; + private static final String sValue = "value1"; + private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec + + @Rule + public TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig(); + + @Test + public void getProperty_empty() { + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isNull(); + } + + @Test + public void setAndGetProperty_sameNamespace() { + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isEqualTo(sValue); + } + + @Test + public void setAndGetProperty_differentNamespace() { + String newNamespace = "namespace2"; + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + String result = DeviceConfig.getProperty(newNamespace, sKey); + assertThat(result).isNull(); + } + + @Test + public void setAndGetProperty_multipleNamespaces() { + String newNamespace = "namespace2"; + String newValue = "value2"; + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + DeviceConfig.setProperty(newNamespace, sKey, newValue, false); + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isEqualTo(sValue); + result = DeviceConfig.getProperty(newNamespace, sKey); + assertThat(result).isEqualTo(newValue); + } + + @Test + public void setAndGetProperty_overrideValue() { + String newValue = "value2"; + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + DeviceConfig.setProperty(sNamespace, sKey, newValue, false); + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isEqualTo(newValue); + } + + @Test + public void testListener() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + OnPropertyChangedListener changeListener = (namespace, name, value) -> { + assertThat(namespace).isEqualTo(sNamespace); + assertThat(name).isEqualTo(sKey); + assertThat(value).isEqualTo(sValue); + countDownLatch.countDown(); + }; + try { + DeviceConfig.addOnPropertyChangedListener(sNamespace, + ActivityThread.currentApplication().getMainExecutor(), changeListener); + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + } finally { + DeviceConfig.removeOnPropertyChangedListener(changeListener); + } + } + +} + +