Merge "Move TestableDeviceConfig to modules-utils."
This commit is contained in:
committed by
Android (Google) Code Review
commit
0ad7cbab11
@@ -29,7 +29,10 @@ package {
|
||||
|
||||
android_test {
|
||||
name: "FrameworksMockingServicesTests",
|
||||
defaults: ["FrameworkMockingServicesTests-jni-defaults"],
|
||||
defaults: [
|
||||
"FrameworkMockingServicesTests-jni-defaults",
|
||||
"modules-utils-testable-device-config-defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
@@ -67,9 +70,7 @@ android_test {
|
||||
],
|
||||
|
||||
jni_libs: [
|
||||
"libdexmakerjvmtiagent",
|
||||
"libpsi",
|
||||
"libstaticjvmtiagent",
|
||||
],
|
||||
|
||||
certificate: "platform",
|
||||
|
||||
@@ -38,10 +38,10 @@ import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.modules.utils.testing.TestableDeviceConfig;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.ServiceThread;
|
||||
import com.android.server.appop.AppOpsService;
|
||||
import com.android.server.testables.TestableDeviceConfig;
|
||||
import com.android.server.wm.ActivityTaskManagerService;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
@@ -40,10 +40,10 @@ import android.text.TextUtils;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.modules.utils.testing.TestableDeviceConfig;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.ServiceThread;
|
||||
import com.android.server.appop.AppOpsService;
|
||||
import com.android.server.testables.TestableDeviceConfig;
|
||||
import com.android.server.wm.ActivityTaskManagerService;
|
||||
|
||||
import org.junit.After;
|
||||
|
||||
@@ -29,7 +29,7 @@ import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.testables.TestableDeviceConfig;
|
||||
import com.android.modules.utils.testing.TestableDeviceConfig;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
|
||||
@@ -59,7 +59,7 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.internal.compat.CompatibilityOverrideConfig;
|
||||
import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
|
||||
import com.android.internal.compat.IPlatformCompat;
|
||||
import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
|
||||
import com.android.modules.utils.testing.TestableDeviceConfig.TestableDeviceConfigRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
|
||||
@@ -34,7 +34,7 @@ import android.testing.TestableContext;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.server.testables.TestableDeviceConfig;
|
||||
import com.android.modules.utils.testing.TestableDeviceConfig;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
|
||||
@@ -36,7 +36,7 @@ import android.testing.TestableContext;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.server.testables.TestableDeviceConfig;
|
||||
import com.android.modules.utils.testing.TestableDeviceConfig;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* 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.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class MultipleStaticMocksTest {
|
||||
@Rule
|
||||
public StaticMockFixtureRule mStaticMockFixtureRule =
|
||||
new StaticMockFixtureRule(AB::new, CD::new);
|
||||
|
||||
private List<String> mCollected;
|
||||
|
||||
@Test
|
||||
public void testMultipleStaticMocks() throws Exception {
|
||||
mCollected = new ArrayList<>();
|
||||
int n = 0;
|
||||
|
||||
A.a();
|
||||
n = verifyCollected(n, "A.a");
|
||||
|
||||
D.b();
|
||||
n = verifyCollected(n, "D.b");
|
||||
|
||||
C.b();
|
||||
n = verifyCollected(n, "C.b");
|
||||
|
||||
B.a();
|
||||
n = verifyCollected(n, "B.a");
|
||||
}
|
||||
|
||||
private int verifyCollected(int n, String... last) {
|
||||
assertThat(mCollected).hasSize(n + last.length);
|
||||
assertThat(mCollected.subList(n, mCollected.size()))
|
||||
.containsExactlyElementsIn(last).inOrder();
|
||||
return n + last.length;
|
||||
}
|
||||
|
||||
private static class A {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
private static class B {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
private static class C {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
private static class D {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
|
||||
*/
|
||||
private class AB implements StaticMockFixture {
|
||||
@Override
|
||||
public StaticMockitoSessionBuilder setUpMockedClasses(
|
||||
StaticMockitoSessionBuilder sessionBuilder) {
|
||||
sessionBuilder.spyStatic(A.class);
|
||||
sessionBuilder.spyStatic(B.class);
|
||||
return sessionBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpMockBehaviors() {
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("A.a");
|
||||
return null;
|
||||
}).when(A::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("A.b");
|
||||
return null;
|
||||
}).when(A::b);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("B.a");
|
||||
return null;
|
||||
}).when(B::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("B.b");
|
||||
return null;
|
||||
}).when(B::b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
|
||||
*/
|
||||
private class CD implements StaticMockFixture {
|
||||
@Override
|
||||
public StaticMockitoSessionBuilder setUpMockedClasses(
|
||||
StaticMockitoSessionBuilder sessionBuilder) {
|
||||
sessionBuilder.spyStatic(C.class);
|
||||
sessionBuilder.spyStatic(D.class);
|
||||
return sessionBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpMockBehaviors() {
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("C.a");
|
||||
return null;
|
||||
}).when(C::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("C.b");
|
||||
return null;
|
||||
}).when(C::b);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("D.a");
|
||||
return null;
|
||||
}).when(D::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("D.b");
|
||||
return null;
|
||||
}).when(D::b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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 com.android.dx.mockito.inline.extended.StaticMockitoSession;
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
|
||||
/**
|
||||
* Provides support for a set of static mocks for use within a single shared
|
||||
* {@link StaticMockitoSession}.
|
||||
*/
|
||||
public interface StaticMockFixture {
|
||||
/**
|
||||
* Adds any required mock or spy classes managed by this {@link StaticMockFixture} to the
|
||||
* {@link StaticMockitoSessionBuilder} provided.
|
||||
*
|
||||
* Call this to set up the classes that this expects to be mocked, by adding them to the
|
||||
* {@link StaticMockitoSessionBuilder} using
|
||||
* {@link StaticMockitoSessionBuilder#mockStatic(Class)},
|
||||
* {@link StaticMockitoSessionBuilder#spyStatic(Class)} or similar as appropriate.
|
||||
*
|
||||
* @param sessionBuilder the {@link StaticMockitoSessionBuilder} to which the classes should be
|
||||
* added to mock, spy, or otherwise as required
|
||||
* @return sessionBuilder, to allow for fluent programming
|
||||
*/
|
||||
StaticMockitoSessionBuilder setUpMockedClasses(StaticMockitoSessionBuilder sessionBuilder);
|
||||
|
||||
/**
|
||||
* Configures the behaviours of any mock or spy classes managed by this
|
||||
* {@link StaticMockFixture}.
|
||||
*
|
||||
* Call this after {@link StaticMockitoSessionBuilder#startMocking()} has been called.
|
||||
* This sets up any default behaviors for the mocks, spys, etc.
|
||||
*/
|
||||
void setUpMockBehaviors();
|
||||
|
||||
/**
|
||||
* Tear everything down.
|
||||
*/
|
||||
void tearDown();
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* 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.mockitoSession;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
|
||||
import org.junit.AssumptionViolatedException;
|
||||
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 java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* <p>StaticMockFixtureRule is a {@link TestRule} that wraps one or more {@link StaticMockFixture}s
|
||||
* to set them up and tear it down automatically. This works well when you have no other static
|
||||
* mocks than the ones supported by their respective {@link StaticMockFixture}s.</p>
|
||||
*
|
||||
* <p>StaticMockFixtureRule should be defined as a rule on your test so it can clean up after
|
||||
* itself. Like the following:</p>
|
||||
* <pre class="prettyprint">
|
||||
* public final StaticMockFixture mStaticMockFixtures = ...;
|
||||
* @Rule
|
||||
* public final StaticMockFixtureRule mStaticMockFixtureRule =
|
||||
* new StaticMockFixtureRule(mStaticMockFixtures);
|
||||
* </pre>
|
||||
*/
|
||||
public class StaticMockFixtureRule implements TestRule {
|
||||
private StaticMockitoSession mMockitoSession;
|
||||
private StaticMockFixture[] mStaticMockFixtures;
|
||||
private Supplier<? extends StaticMockFixture>[] mSupplier;
|
||||
|
||||
/**
|
||||
* Constructs a StaticMockFixtureRule that always uses the same {@link StaticMockFixture}
|
||||
* instance(s).
|
||||
*
|
||||
* @param staticMockFixtures the {@link StaticMockFixture}(s) to use.
|
||||
*/
|
||||
public StaticMockFixtureRule(StaticMockFixture... staticMockFixtures) {
|
||||
mStaticMockFixtures = staticMockFixtures;
|
||||
mSupplier = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a StaticMockFixtureRule that retrieves a new {@link StaticMockFixture} instance
|
||||
* from one or more {@link Supplier<? extends StaticMockFixture >}s for each test invocation.
|
||||
*
|
||||
* @param supplier the {@link Supplier<? extends StaticMockFixture >}(s) that will supply the
|
||||
* {@link StaticMockFixture}(s).
|
||||
*/
|
||||
@SafeVarargs
|
||||
public StaticMockFixtureRule(Supplier<? extends StaticMockFixture>... supplier) {
|
||||
mStaticMockFixtures = null;
|
||||
mSupplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
StaticMockitoSessionBuilder sessionBuilder = getSessionBuilder();
|
||||
|
||||
if (mSupplier != null) {
|
||||
mStaticMockFixtures = new StaticMockFixture[mSupplier.length];
|
||||
for (int i = 0; i < mSupplier.length; i++) {
|
||||
mStaticMockFixtures[i] = mSupplier[i].get();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mStaticMockFixtures.length; i++) {
|
||||
sessionBuilder = mStaticMockFixtures[i].setUpMockedClasses(sessionBuilder);
|
||||
}
|
||||
|
||||
mMockitoSession = sessionBuilder.startMocking();
|
||||
|
||||
for (int i = 0; i < mStaticMockFixtures.length; i++) {
|
||||
mStaticMockFixtures[i].setUpMockBehaviors();
|
||||
}
|
||||
|
||||
return new TestWatcher() {
|
||||
@Override
|
||||
protected void succeeded(Description description) {
|
||||
tearDown(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void skipped(AssumptionViolatedException e, Description description) {
|
||||
tearDown(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failed(Throwable e, Description description) {
|
||||
tearDown(e);
|
||||
}
|
||||
}.apply(base, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* This allows overriding the creation of the builder for a new {@link StaticMockitoSession}.
|
||||
* Mainly for testing, but also useful if you have other requirements for the session.
|
||||
*
|
||||
* @return a new {@link StaticMockitoSessionBuilder}.
|
||||
*/
|
||||
public StaticMockitoSessionBuilder getSessionBuilder() {
|
||||
return mockitoSession().strictness(Strictness.LENIENT);
|
||||
}
|
||||
|
||||
private void tearDown(Throwable e) {
|
||||
mMockitoSession.finishMocking(e);
|
||||
|
||||
for (int i = mStaticMockFixtures.length - 1; i >= 0; i--) {
|
||||
mStaticMockFixtures[i].tearDown();
|
||||
if (mSupplier != null) {
|
||||
mStaticMockFixtures[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSupplier != null) {
|
||||
mStaticMockFixtures = null;
|
||||
}
|
||||
|
||||
mMockitoSession = null;
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* 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 org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.quality.Strictness.LENIENT;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AssumptionViolatedException;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoSession;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** Tests that StaticMockFixture manages fixtures and suppliers correctly. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class StaticMockFixtureRuleTest {
|
||||
private MockitoSession mMockitoSession;
|
||||
|
||||
@Mock private StaticMockitoSessionBuilder mSessionBuilder;
|
||||
@Mock private StaticMockitoSession mSession;
|
||||
@Mock private StaticMockFixture mA1;
|
||||
@Mock private StaticMockFixture mB1;
|
||||
@Mock private StaticMockFixture mA2;
|
||||
@Mock private StaticMockFixture mB2;
|
||||
@Mock private Supplier<StaticMockFixture> mSupplyA;
|
||||
@Mock private Supplier<StaticMockFixture> mSupplyB;
|
||||
@Mock private Statement mStatement;
|
||||
@Mock private Statement mSkipStatement;
|
||||
@Mock private Statement mThrowStatement;
|
||||
@Mock private Description mDescription;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Throwable {
|
||||
mMockitoSession = Mockito.mockitoSession()
|
||||
.strictness(LENIENT)
|
||||
.initMocks(this)
|
||||
.startMocking();
|
||||
prepareMockBehaviours();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mMockitoSession.finishMocking();
|
||||
}
|
||||
|
||||
private void prepareFixtureMocks(StaticMockFixture... mocks) {
|
||||
for (StaticMockFixture mock : mocks) {
|
||||
when(mock.setUpMockedClasses(any())).thenAnswer(
|
||||
invocation -> invocation.getArgument(0));
|
||||
doNothing().when(mock).setUpMockBehaviors();
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareMockBehaviours() throws Throwable {
|
||||
when(mSessionBuilder.startMocking()).thenReturn(mSession);
|
||||
when(mSupplyA.get()).thenReturn(mA1, mA2);
|
||||
when(mSupplyB.get()).thenReturn(mB1, mB2);
|
||||
prepareFixtureMocks(mA1, mA2, mB1, mB2);
|
||||
when(mA1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
doNothing().when(mA1).setUpMockBehaviors();
|
||||
when(mB1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
doNothing().when(mB1).setUpMockBehaviors();
|
||||
doNothing().when(mStatement).evaluate();
|
||||
doThrow(new AssumptionViolatedException("bad assumption, test should be skipped"))
|
||||
.when(mSkipStatement).evaluate();
|
||||
doThrow(new IllegalArgumentException("bad argument, test should be failed"))
|
||||
.when(mThrowStatement).evaluate();
|
||||
doNothing().when(mA1).tearDown();
|
||||
doNothing().when(mB1).tearDown();
|
||||
}
|
||||
|
||||
private InOrder mocksInOrder() {
|
||||
return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB, mA1, mA2, mB1, mB2,
|
||||
mStatement, mSkipStatement, mThrowStatement, mDescription);
|
||||
}
|
||||
|
||||
private void verifyNoMoreImportantMockInteractions() {
|
||||
verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement,
|
||||
mSkipStatement, mThrowStatement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleWorksWithExplicitFixtures() throws Throwable {
|
||||
InOrder inOrder = mocksInOrder();
|
||||
|
||||
StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
|
||||
@Override public StaticMockitoSessionBuilder getSessionBuilder() {
|
||||
return mSessionBuilder;
|
||||
}
|
||||
};
|
||||
Statement runMe = rule.apply(mStatement, mDescription);
|
||||
|
||||
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mA1).setUpMockBehaviors();
|
||||
inOrder.verify(mB1).setUpMockBehaviors();
|
||||
|
||||
runMe.evaluate();
|
||||
|
||||
inOrder.verify(mStatement).evaluate();
|
||||
// note: tearDown in reverse order
|
||||
inOrder.verify(mB1).tearDown();
|
||||
inOrder.verify(mA1).tearDown();
|
||||
|
||||
// Round two: use the same fixtures again.
|
||||
rule.apply(mStatement, mDescription).evaluate();
|
||||
|
||||
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mA1).setUpMockBehaviors();
|
||||
inOrder.verify(mB1).setUpMockBehaviors();
|
||||
inOrder.verify(mStatement).evaluate();
|
||||
// note: tearDown in reverse order
|
||||
inOrder.verify(mB1).tearDown();
|
||||
inOrder.verify(mA1).tearDown();
|
||||
|
||||
verifyNoMoreImportantMockInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuleWorksWithFixtureSuppliers() throws Throwable {
|
||||
InOrder inOrder = mocksInOrder();
|
||||
|
||||
StaticMockFixtureRule rule = new StaticMockFixtureRule(mSupplyA, mSupplyB) {
|
||||
@Override public StaticMockitoSessionBuilder getSessionBuilder() {
|
||||
return mSessionBuilder;
|
||||
}
|
||||
};
|
||||
Statement runMe = rule.apply(mStatement, mDescription);
|
||||
|
||||
inOrder.verify(mSupplyA).get();
|
||||
inOrder.verify(mSupplyB).get();
|
||||
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mA1).setUpMockBehaviors();
|
||||
inOrder.verify(mB1).setUpMockBehaviors();
|
||||
|
||||
runMe.evaluate();
|
||||
|
||||
inOrder.verify(mStatement).evaluate();
|
||||
// note: tearDown in reverse order
|
||||
inOrder.verify(mB1).tearDown();
|
||||
inOrder.verify(mA1).tearDown();
|
||||
|
||||
// Round two: use the same suppliers again to retrieve different fixtures: mA2 and mB2
|
||||
rule.apply(mStatement, mDescription).evaluate();
|
||||
|
||||
inOrder.verify(mSupplyA).get();
|
||||
inOrder.verify(mSupplyB).get();
|
||||
inOrder.verify(mA2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mB2).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mA2).setUpMockBehaviors();
|
||||
inOrder.verify(mB2).setUpMockBehaviors();
|
||||
inOrder.verify(mStatement).evaluate();
|
||||
// note: tearDown in reverse order
|
||||
inOrder.verify(mB2).tearDown();
|
||||
inOrder.verify(mA2).tearDown();
|
||||
|
||||
verifyNoMoreImportantMockInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTearDownOnSkippedTests() throws Throwable {
|
||||
InOrder inOrder = mocksInOrder();
|
||||
|
||||
StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
|
||||
@Override public StaticMockitoSessionBuilder getSessionBuilder() {
|
||||
return mSessionBuilder;
|
||||
}
|
||||
};
|
||||
Statement skipStatement = rule.apply(mSkipStatement, mDescription);
|
||||
|
||||
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mA1).setUpMockBehaviors();
|
||||
inOrder.verify(mB1).setUpMockBehaviors();
|
||||
|
||||
try {
|
||||
skipStatement.evaluate();
|
||||
fail("AssumptionViolatedException should have been thrown");
|
||||
} catch (AssumptionViolatedException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
inOrder.verify(mSkipStatement).evaluate();
|
||||
// note: tearDown in reverse order
|
||||
inOrder.verify(mB1).tearDown();
|
||||
inOrder.verify(mA1).tearDown();
|
||||
|
||||
verifyNoMoreImportantMockInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTearDownOnFailedTests() throws Throwable {
|
||||
InOrder inOrder = mocksInOrder();
|
||||
|
||||
StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
|
||||
@Override public StaticMockitoSessionBuilder getSessionBuilder() {
|
||||
return mSessionBuilder;
|
||||
}
|
||||
};
|
||||
Statement failStatement = rule.apply(mThrowStatement, mDescription);
|
||||
|
||||
inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
|
||||
inOrder.verify(mA1).setUpMockBehaviors();
|
||||
inOrder.verify(mB1).setUpMockBehaviors();
|
||||
|
||||
try {
|
||||
failStatement.evaluate();
|
||||
fail("IllegalArgumentException should have been thrown");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
inOrder.verify(mThrowStatement).evaluate();
|
||||
// note: tearDown in reverse order
|
||||
inOrder.verify(mB1).tearDown();
|
||||
inOrder.verify(mA1).tearDown();
|
||||
|
||||
verifyNoMoreImportantMockInteractions();
|
||||
}
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* 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 org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.DeviceConfig.Properties;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* TestableDeviceConfig is a {@link StaticMockFixture} that 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.
|
||||
*/
|
||||
public final class TestableDeviceConfig implements StaticMockFixture {
|
||||
|
||||
private Map<DeviceConfig.OnPropertiesChangedListener, Pair<String, Executor>>
|
||||
mOnPropertiesChangedListenerMap = new HashMap<>();
|
||||
private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Clears out all local overrides.
|
||||
*/
|
||||
public void clearDeviceConfig() {
|
||||
mKeyValueMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public StaticMockitoSessionBuilder setUpMockedClasses(
|
||||
StaticMockitoSessionBuilder sessionBuilder) {
|
||||
sessionBuilder.spyStatic(DeviceConfig.class);
|
||||
return sessionBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setUpMockBehaviors() {
|
||||
doAnswer((Answer<Void>) invocationOnMock -> {
|
||||
String namespace = invocationOnMock.getArgument(0);
|
||||
Executor executor = invocationOnMock.getArgument(1);
|
||||
DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener =
|
||||
invocationOnMock.getArgument(2);
|
||||
mOnPropertiesChangedListenerMap.put(
|
||||
onPropertiesChangedListener, new Pair<>(namespace, executor));
|
||||
return null;
|
||||
}).when(() -> DeviceConfig.addOnPropertiesChangedListener(
|
||||
anyString(), any(Executor.class),
|
||||
any(DeviceConfig.OnPropertiesChangedListener.class)));
|
||||
|
||||
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
||||
String namespace = invocationOnMock.getArgument(0);
|
||||
String name = invocationOnMock.getArgument(1);
|
||||
String value = invocationOnMock.getArgument(2);
|
||||
mKeyValueMap.put(getKey(namespace, name), value);
|
||||
invokeListeners(namespace, getProperties(namespace, name, value));
|
||||
return true;
|
||||
}
|
||||
).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
|
||||
|
||||
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
||||
String namespace = invocationOnMock.getArgument(0);
|
||||
String name = invocationOnMock.getArgument(1);
|
||||
mKeyValueMap.remove(getKey(namespace, name));
|
||||
invokeListeners(namespace, getProperties(namespace, name, null));
|
||||
return true;
|
||||
}
|
||||
).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
|
||||
|
||||
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
||||
Properties properties = invocationOnMock.getArgument(0);
|
||||
String namespace = properties.getNamespace();
|
||||
Map<String, String> keyValues = new ArrayMap<>();
|
||||
for (String name : properties.getKeyset()) {
|
||||
String value = properties.getString(name, /* defaultValue= */ "");
|
||||
mKeyValueMap.put(getKey(namespace, name), value);
|
||||
keyValues.put(name.toLowerCase(), value);
|
||||
}
|
||||
invokeListeners(namespace, getProperties(namespace, keyValues));
|
||||
return true;
|
||||
}
|
||||
).when(() -> DeviceConfig.setProperties(any(Properties.class)));
|
||||
|
||||
doAnswer((Answer<String>) invocationOnMock -> {
|
||||
String namespace = invocationOnMock.getArgument(0);
|
||||
String name = invocationOnMock.getArgument(1);
|
||||
return mKeyValueMap.get(getKey(namespace, name));
|
||||
}).when(() -> DeviceConfig.getProperty(anyString(), anyString()));
|
||||
|
||||
doAnswer((Answer<Properties>) invocationOnMock -> {
|
||||
String namespace = invocationOnMock.getArgument(0);
|
||||
final int varargStartIdx = 1;
|
||||
Map<String, String> keyValues = new ArrayMap<>();
|
||||
if (invocationOnMock.getArguments().length == varargStartIdx) {
|
||||
mKeyValueMap.entrySet().forEach(entry -> {
|
||||
Pair<String, String> nameSpaceAndName = getNameSpaceAndName(entry.getKey());
|
||||
if (!nameSpaceAndName.first.equals(namespace)) {
|
||||
return;
|
||||
}
|
||||
keyValues.put(nameSpaceAndName.second.toLowerCase(), entry.getValue());
|
||||
});
|
||||
} else {
|
||||
for (int i = varargStartIdx; i < invocationOnMock.getArguments().length; ++i) {
|
||||
String name = invocationOnMock.getArgument(i);
|
||||
keyValues.put(name.toLowerCase(), mKeyValueMap.get(getKey(namespace, name)));
|
||||
}
|
||||
}
|
||||
return getProperties(namespace, keyValues);
|
||||
}).when(() -> DeviceConfig.getProperties(anyString(), ArgumentMatchers.<String>any()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void tearDown() {
|
||||
clearDeviceConfig();
|
||||
mOnPropertiesChangedListenerMap.clear();
|
||||
}
|
||||
|
||||
private static String getKey(String namespace, String name) {
|
||||
return namespace + "/" + name;
|
||||
}
|
||||
|
||||
private Pair<String, String> getNameSpaceAndName(String key) {
|
||||
final String[] values = key.split("/");
|
||||
return Pair.create(values[0], values[1]);
|
||||
}
|
||||
|
||||
private void invokeListeners(String namespace, Properties properties) {
|
||||
for (DeviceConfig.OnPropertiesChangedListener listener :
|
||||
mOnPropertiesChangedListenerMap.keySet()) {
|
||||
if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
|
||||
mOnPropertiesChangedListenerMap.get(listener).second.execute(
|
||||
() -> listener.onPropertiesChanged(properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Properties getProperties(String namespace, String name, String value) {
|
||||
return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value));
|
||||
}
|
||||
|
||||
private Properties getProperties(String namespace, Map<String, String> keyValues) {
|
||||
Properties properties = Mockito.mock(Properties.class);
|
||||
when(properties.getNamespace()).thenReturn(namespace);
|
||||
when(properties.getKeyset()).thenReturn(keyValues.keySet());
|
||||
when(properties.getBoolean(anyString(), anyBoolean())).thenAnswer(
|
||||
invocation -> {
|
||||
String key = invocation.getArgument(0);
|
||||
boolean defaultValue = invocation.getArgument(1);
|
||||
final String value = keyValues.get(key.toLowerCase());
|
||||
if (value != null) {
|
||||
return Boolean.parseBoolean(value);
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
when(properties.getFloat(anyString(), anyFloat())).thenAnswer(
|
||||
invocation -> {
|
||||
String key = invocation.getArgument(0);
|
||||
float defaultValue = invocation.getArgument(1);
|
||||
final String value = keyValues.get(key.toLowerCase());
|
||||
if (value != null) {
|
||||
try {
|
||||
return Float.parseFloat(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
when(properties.getInt(anyString(), anyInt())).thenAnswer(
|
||||
invocation -> {
|
||||
String key = invocation.getArgument(0);
|
||||
int defaultValue = invocation.getArgument(1);
|
||||
final String value = keyValues.get(key.toLowerCase());
|
||||
if (value != null) {
|
||||
try {
|
||||
return Integer.parseInt(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
when(properties.getLong(anyString(), anyLong())).thenAnswer(
|
||||
invocation -> {
|
||||
String key = invocation.getArgument(0);
|
||||
long defaultValue = invocation.getArgument(1);
|
||||
final String value = keyValues.get(key.toLowerCase());
|
||||
if (value != null) {
|
||||
try {
|
||||
return Long.parseLong(value);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
when(properties.getString(anyString(), nullable(String.class))).thenAnswer(
|
||||
invocation -> {
|
||||
String key = invocation.getArgument(0);
|
||||
String defaultValue = invocation.getArgument(1);
|
||||
final String value = keyValues.get(key.toLowerCase());
|
||||
if (value != null) {
|
||||
return value;
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>TestableDeviceConfigRule is a {@link TestRule} that wraps a {@link TestableDeviceConfig}
|
||||
* to set it up and tear it down automatically. This works well when you have no other static
|
||||
* mocks.</p>
|
||||
*
|
||||
* <p>TestableDeviceConfigRule should be defined as a rule on your test so it can clean up after
|
||||
* itself. Like the following:</p>
|
||||
* <pre class="prettyprint">
|
||||
* @Rule
|
||||
* public final TestableDeviceConfigRule mTestableDeviceConfigRule =
|
||||
* new TestableDeviceConfigRule();
|
||||
* </pre>
|
||||
*/
|
||||
public static class TestableDeviceConfigRule extends StaticMockFixtureRule {
|
||||
public TestableDeviceConfigRule() {
|
||||
super(TestableDeviceConfig::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* 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 android.provider.DeviceConfig;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.dx.mockito.inline.extended.ExtendedMockito;
|
||||
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class TestableDeviceConfigAndOtherStaticMocksTest {
|
||||
@Rule
|
||||
public StaticMockFixtureRule mStaticMockFixtureRule =
|
||||
new StaticMockFixtureRule(TestableDeviceConfig::new, AB::new, CD::new);
|
||||
|
||||
private List<String> mCollected;
|
||||
|
||||
@Test
|
||||
public void testDeviceConfigAndOtherStaticMocks() throws Exception {
|
||||
mCollected = new ArrayList<>();
|
||||
int n = 0;
|
||||
|
||||
String namespace = "foo";
|
||||
String flag = "bar";
|
||||
String flagValue = "new value";
|
||||
|
||||
Assert.assertNull(DeviceConfig.getProperty(namespace, flag));
|
||||
|
||||
A.a();
|
||||
verifyCollected(++n, "A.a");
|
||||
|
||||
DeviceConfig.setProperty(namespace, flag, flagValue, false);
|
||||
|
||||
D.b();
|
||||
verifyCollected(++n, "D.b");
|
||||
|
||||
Assert.assertEquals(flagValue, DeviceConfig.getProperty(namespace, flag));
|
||||
|
||||
C.b();
|
||||
verifyCollected(++n, "C.b");
|
||||
|
||||
B.a();
|
||||
verifyCollected(++n, "B.a");
|
||||
}
|
||||
|
||||
private void verifyCollected(int n, String last) {
|
||||
Assert.assertEquals(n, mCollected.size());
|
||||
Assert.assertEquals(last, mCollected.get(n - 1));
|
||||
}
|
||||
|
||||
private static class A {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
private static class B {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
private static class C {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
private static class D {
|
||||
/* package */ static void a() {}
|
||||
/* package */ static void b() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* AB StaticMockFixture class that handles two mocked classes, {@link A} and {@link B}.
|
||||
*/
|
||||
private class AB implements StaticMockFixture {
|
||||
@Override
|
||||
public StaticMockitoSessionBuilder setUpMockedClasses(
|
||||
StaticMockitoSessionBuilder sessionBuilder) {
|
||||
sessionBuilder.spyStatic(A.class);
|
||||
sessionBuilder.spyStatic(B.class);
|
||||
return sessionBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpMockBehaviors() {
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("A.a");
|
||||
return null;
|
||||
}).when(A::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("A.b");
|
||||
return null;
|
||||
}).when(A::b);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("B.a");
|
||||
return null;
|
||||
}).when(B::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("B.b");
|
||||
return null;
|
||||
}).when(B::b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AB StaticMockFixture class that handles two mocked classes, {@link C} and {@link D}.
|
||||
*/
|
||||
private class CD implements StaticMockFixture {
|
||||
@Override
|
||||
public StaticMockitoSessionBuilder setUpMockedClasses(
|
||||
StaticMockitoSessionBuilder sessionBuilder) {
|
||||
sessionBuilder.spyStatic(C.class);
|
||||
sessionBuilder.spyStatic(D.class);
|
||||
return sessionBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpMockBehaviors() {
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("C.a");
|
||||
return null;
|
||||
}).when(C::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("C.b");
|
||||
return null;
|
||||
}).when(C::b);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("D.a");
|
||||
return null;
|
||||
}).when(D::a);
|
||||
ExtendedMockito.doAnswer(invocation -> {
|
||||
mCollected.add("D.b");
|
||||
return null;
|
||||
}).when(D::b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* 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.OnPropertiesChangedListener;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.ActivityThread;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.DeviceConfig.BadConfigException;
|
||||
import android.provider.DeviceConfig.Properties;
|
||||
|
||||
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.TestableDeviceConfigRule
|
||||
mTestableDeviceConfig = new TestableDeviceConfig.TestableDeviceConfigRule();
|
||||
|
||||
@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 setProperties() throws BadConfigException {
|
||||
String newKey = "key2";
|
||||
String newValue = "value2";
|
||||
DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
|
||||
sValue).setString(newKey, newValue).build());
|
||||
assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
|
||||
assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteProperty() {
|
||||
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
|
||||
assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
|
||||
DeviceConfig.deleteProperty(sNamespace, sKey);
|
||||
assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
|
||||
String newNamespace = "namespace2";
|
||||
String newValue = "value2";
|
||||
DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
|
||||
assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
|
||||
DeviceConfig.deleteProperty(newNamespace, sKey);
|
||||
assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProperties_empty() {
|
||||
String newKey = "key2";
|
||||
String newValue = "value2";
|
||||
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
|
||||
Properties properties = DeviceConfig.getProperties(sNamespace);
|
||||
assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
|
||||
assertThat(properties.getString(newKey, null)).isNull();
|
||||
|
||||
DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
|
||||
properties = DeviceConfig.getProperties(sNamespace);
|
||||
assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
|
||||
assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProperties() {
|
||||
Properties properties = DeviceConfig.getProperties(sNamespace, sKey);
|
||||
assertThat(properties.getString(sKey, null)).isNull();
|
||||
|
||||
DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
|
||||
properties = DeviceConfig.getProperties(sNamespace, sKey);
|
||||
assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
|
||||
|
||||
String newKey = "key2";
|
||||
String newValue = "value2";
|
||||
DeviceConfig.setProperty(sNamespace, newKey, newValue, false);
|
||||
properties = DeviceConfig.getProperties(sNamespace, sKey, newKey);
|
||||
assertThat(properties.getString(sKey, null)).isEqualTo(sValue);
|
||||
assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
|
||||
|
||||
String unsetKey = "key3";
|
||||
properties = DeviceConfig.getProperties(sNamespace, newKey, unsetKey);
|
||||
assertThat(properties.getKeyset()).containsExactly(newKey, unsetKey);
|
||||
assertThat(properties.getString(newKey, null)).isEqualTo(newValue);
|
||||
assertThat(properties.getString(unsetKey, null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListener_setProperty() throws InterruptedException {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
|
||||
OnPropertiesChangedListener changeListener = (properties) -> {
|
||||
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
|
||||
assertThat(properties.getKeyset()).containsExactly(sKey);
|
||||
assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
|
||||
assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
|
||||
countDownLatch.countDown();
|
||||
};
|
||||
try {
|
||||
DeviceConfig.addOnPropertiesChangedListener(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.removeOnPropertiesChangedListener(changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListener_setProperties() throws BadConfigException, InterruptedException {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
String newKey = "key2";
|
||||
String newValue = "value2";
|
||||
|
||||
OnPropertiesChangedListener changeListener = (properties) -> {
|
||||
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
|
||||
assertThat(properties.getKeyset()).containsExactly(sKey, newKey);
|
||||
assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
|
||||
assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue);
|
||||
assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
|
||||
countDownLatch.countDown();
|
||||
};
|
||||
try {
|
||||
DeviceConfig.addOnPropertiesChangedListener(sNamespace,
|
||||
ActivityThread.currentApplication().getMainExecutor(), changeListener);
|
||||
DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
|
||||
sValue).setString(newKey, newValue).build());
|
||||
assertThat(countDownLatch.await(
|
||||
WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
|
||||
} finally {
|
||||
DeviceConfig.removeOnPropertiesChangedListener(changeListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListener_deleteProperty() throws InterruptedException {
|
||||
CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
|
||||
OnPropertiesChangedListener changeListener = (properties) -> {
|
||||
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
|
||||
assertThat(properties.getKeyset()).containsExactly(sKey);
|
||||
assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
|
||||
assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
|
||||
countDownLatch.countDown();
|
||||
};
|
||||
try {
|
||||
DeviceConfig.addOnPropertiesChangedListener(sNamespace,
|
||||
ActivityThread.currentApplication().getMainExecutor(), changeListener);
|
||||
DeviceConfig.deleteProperty(sNamespace, sKey);
|
||||
assertThat(countDownLatch.await(
|
||||
WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
|
||||
} finally {
|
||||
DeviceConfig.removeOnPropertiesChangedListener(changeListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user