diff options
author | Jason Monk <jmonk@google.com> | 2017-05-02 14:22:21 -0400 |
---|---|---|
committer | Jason Monk <jmonk@google.com> | 2017-05-03 11:21:03 -0400 |
commit | 77f1b05fb0eb1b0f22eb91508a1a3bc0cfcda935 (patch) | |
tree | f036309e3b877c269ede9563e8f46fb9f269b7e2 /tests/testables/src | |
parent | 60e305ce8ca1e111f382c3b221fd92536e8283b5 (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.java | 31 | ||||
-rw-r--r-- | tests/testables/src/android/testing/TestableResources.java | 101 |
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(); + } +} |