summaryrefslogtreecommitdiff
path: root/tests/testables/src
diff options
context:
space:
mode:
authorJason Monk <jmonk@google.com>2017-05-02 14:22:21 -0400
committerJason Monk <jmonk@google.com>2017-05-03 11:21:03 -0400
commit77f1b05fb0eb1b0f22eb91508a1a3bc0cfcda935 (patch)
treef036309e3b877c269ede9563e8f46fb9f269b7e2 /tests/testables/src
parent60e305ce8ca1e111f382c3b221fd92536e8283b5 (diff)
Add TestableResources
Makes it easy to add or change values of resources from tests. Test: runtest --path frameworks/base/tests/testables/tests Change-Id: Iaedff3d4ce9eaf9f270e7c62bc8c1634bd3519ec
Diffstat (limited to 'tests/testables/src')
-rw-r--r--tests/testables/src/android/testing/TestableContext.java31
-rw-r--r--tests/testables/src/android/testing/TestableResources.java101
2 files changed, 131 insertions, 1 deletions
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index 630a287c6f4a..d6c06e47fcdf 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -69,6 +69,7 @@ public class TestableContext extends ContextWrapper implements TestRule {
private LeakCheck.Tracker mReceiver;
private LeakCheck.Tracker mService;
private LeakCheck.Tracker mComponent;
+ private TestableResources mTestableResources;
public TestableContext(Context base) {
this(base, null);
@@ -98,9 +99,37 @@ public class TestableContext extends ContextWrapper implements TestRule {
return super.getPackageManager();
}
+ /**
+ * Makes sure the resources being returned by this TestableContext are a version of
+ * TestableResources.
+ * @see #getResources()
+ */
+ public void ensureTestableResources() {
+ if (mTestableResources == null) {
+ mTestableResources = new TestableResources(super.getResources());
+ }
+ }
+
+ /**
+ * Get (and create if necessary) {@link TestableResources} for this TestableContext.
+ */
+ public TestableResources getOrCreateTestableResources() {
+ ensureTestableResources();
+ return mTestableResources;
+ }
+
+ /**
+ * Returns a Resources instance for the test.
+ *
+ * By default this returns the same resources object that would come from the
+ * {@link ContextWrapper}, but if {@link #ensureTestableResources()} or
+ * {@link #getOrCreateTestableResources()} has been called, it will return resources gotten from
+ * {@link TestableResources}.
+ */
@Override
public Resources getResources() {
- return super.getResources();
+ return mTestableResources != null ? mTestableResources.getResources()
+ : super.getResources();
}
public <T> void addMockSystemService(Class<T> service, T mock) {
diff --git a/tests/testables/src/android/testing/TestableResources.java b/tests/testables/src/android/testing/TestableResources.java
new file mode 100644
index 000000000000..a2fa95deaa60
--- /dev/null
+++ b/tests/testables/src/android/testing/TestableResources.java
@@ -0,0 +1,101 @@
+/*
+ * 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.mockito.Mockito.mock;
+import static org.mockito.Mockito.withSettings;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+import android.util.SparseArray;
+
+import org.mockito.invocation.InvocationOnMock;
+
+/**
+ * Provides a version of Resources that defaults to all existing resources, but can have ids
+ * changed to return specific values.
+ * <p>
+ * TestableResources are lazily initialized, be sure to call
+ * {@link TestableContext#ensureTestableResources} before your tested code has an opportunity
+ * to cache {@link Context#getResources}.
+ * </p>
+ */
+public class TestableResources {
+
+ private static final String TAG = "TestableResources";
+ private final Resources mResources;
+ private final SparseArray<Object> mOverrides = new SparseArray<>();
+
+ TestableResources(Resources realResources) {
+ mResources = mock(Resources.class, withSettings()
+ .spiedInstance(realResources)
+ .defaultAnswer(this::answer));
+ }
+
+ /**
+ * Gets the implementation of Resources that will return overridden values when called.
+ */
+ public Resources getResources() {
+ return mResources;
+ }
+
+ /**
+ * Sets the return value for the specified resource id.
+ * <p>
+ * Since resource ids are unique there is a single addOverride that will override the value
+ * whenever it is gotten regardless of which method is used (i.e. getColor or getDrawable).
+ * </p>
+ * @param id The resource id to be overridden
+ * @param value The value of the resource, null to cause a {@link Resources.NotFoundException}
+ * when gotten.
+ */
+ public void addOverride(int id, Object value) {
+ mOverrides.put(id, value);
+ }
+
+ /**
+ * Removes the override for the specified id.
+ * <p>
+ * This should be called over addOverride(id, null), because specifying a null value will
+ * cause a {@link Resources.NotFoundException} whereas removing the override will actually
+ * switch back to returning the default/real value of the resource.
+ * </p>
+ * @param id
+ */
+ public void removeOverride(int id) {
+ mOverrides.remove(id);
+ }
+
+ private Object answer(InvocationOnMock invocationOnMock) throws Throwable {
+ try {
+ int id = invocationOnMock.getArgument(0);
+ int index = mOverrides.indexOfKey(id);
+ if (index >= 0) {
+ Object value = mOverrides.valueAt(index);
+ if (value == null) throw new Resources.NotFoundException();
+ return value;
+ }
+ } catch (Resources.NotFoundException e) {
+ // Let through NotFoundException.
+ throw e;
+ } catch (Throwable t) {
+ // Generic catching for the many things that can go wrong, fall back to
+ // the real implementation.
+ Log.i(TAG, "Falling back to default resources call " + t);
+ }
+ return invocationOnMock.callRealMethod();
+ }
+}