Add unit tests for activity client state changes

This also adds new module for tests that require Extended Mockito.

Bug: 127106719
Test: atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
Change-Id: I7ed5de72d35babbbf72c4c823d3a9e4baa50e3c4
This commit is contained in:
Andrii Kulian
2019-03-24 23:31:24 -07:00
parent d9ed811626
commit ca7f710eee
7 changed files with 526 additions and 0 deletions

View File

@@ -27,5 +27,15 @@
}
]
}
],
"postsubmit": [
{
"file_patterns": ["(/|^)ActivityThreadClientTest.java"],
"name": "FrameworksMockingCoreTests"
},
{
"file_patterns": ["(/|^)ActivityThreadTest.java"],
"name": "FrameworksCoreTests"
}
]
}

View File

@@ -0,0 +1,53 @@
//
// 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.
//
android_test {
name: "FrameworksMockingCoreTests",
srcs: [
"src/**/*.java",
],
static_libs: [
"frameworks-base-testutils",
"services.core",
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.ext.junit",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
"truth-prebuilt",
"testables",
"ub-uiautomator",
],
libs: [
"android.test.base",
"android.test.mock",
"android.test.runner",
],
// These are not normally accessible from apps so they must be explicitly included.
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
platform_apis: true,
test_suites: ["device-tests"],
certificate: "platform",
}

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="internalOnly"
package="com.android.frameworks.mockingcoretests"
android:sharedUserId="com.android.uid.test">
<application android:supportsRtl="true" android:debuggable="true">
<uses-library android:name="android.test.runner" />
<activity android:name="android.app.activity.ActivityThreadClientTest$TestActivity"
android:exported="true">
</activity>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.mockingcoretests"
android:label="Frameworks Mocking Core Tests" />
</manifest>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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
-->
<configuration description="Runs Frameworks Mocking Core Tests.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksMockingCoreTests.apk" />
</target_preparer>
<option name="test-tag" value="FrameworksMockingCoreTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.mockingcoretests" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>

View File

@@ -0,0 +1,33 @@
* 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.
INTRODUCTION
The Android platform core tests that require additional mocking capabilities and use Extended
Mockito, such as the ability to stub static methods. ExtendedMockito is not fully compatible with
regular Mockito library, so tests that use it have to be verified and adapted. For that reason a
separate module is used instead of adding to FrameworksCoreTests.
For more information about ExtendedMockito see documentation of
com.android.dx.mockito.inline.extended.ExtendedMockito class.
See ../coretests/README for more information on FrameworksCoreTests.
INSTRUCTIONS
To build, install and run:
atest FrameworksMockingCoreTests

View File

@@ -0,0 +1,362 @@
/*
* 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 android.app.activity;
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.IActivityTaskManager;
import android.app.servertransaction.PendingTransactionActions;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.view.WindowManagerGlobal;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
/**
* Test for verifying {@link android.app.ActivityThread} class.
*
* <p>Build/Install/Run:
* atest FrameworksMockingCoreTests:android.app.activity.ActivityThreadClientTest
*
* <p>This test class is a part of Window Manager Service tests and specified in
* {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ActivityThreadClientTest {
@Test
@UiThreadTest
public void testWindowVisibilityChange_OnCreate() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
clientSession.launchActivity(r);
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.changeVisibility(r, true);
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.changeVisibility(r, false);
assertEquals(ON_CREATE, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testWindowVisibilityChange_OnCreate_Finished() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
Activity activity = clientSession.launchActivity(r);
activity.finish();
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.changeVisibility(r, true);
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.changeVisibility(r, false);
assertEquals(ON_CREATE, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testWindowVisibilityChange_OnStart() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
clientSession.launchActivity(r);
clientSession.startActivity(r);
assertEquals(ON_START, r.getLifecycleState());
clientSession.changeVisibility(r, false);
assertEquals(ON_STOP, r.getLifecycleState());
clientSession.changeVisibility(r, true);
assertEquals(ON_START, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testWindowVisibilityChange_OnStart_Finished() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
Activity activity = clientSession.launchActivity(r);
clientSession.startActivity(r);
activity.finish();
assertEquals(ON_START, r.getLifecycleState());
clientSession.changeVisibility(r, false);
assertEquals(ON_STOP, r.getLifecycleState());
clientSession.changeVisibility(r, true);
assertEquals(ON_START, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testWindowVisibilityChange_OnResume() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
clientSession.launchActivity(r);
clientSession.startActivity(r);
clientSession.resumeActivity(r);
assertEquals(ON_RESUME, r.getLifecycleState());
clientSession.changeVisibility(r, false);
assertEquals(ON_STOP, r.getLifecycleState());
clientSession.changeVisibility(r, true);
assertEquals(ON_START, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testWindowVisibilityChange_OnPause() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
clientSession.launchActivity(r);
clientSession.startActivity(r);
clientSession.resumeActivity(r);
clientSession.pauseActivity(r);
assertEquals(ON_PAUSE, r.getLifecycleState());
clientSession.changeVisibility(r, false);
assertEquals(ON_STOP, r.getLifecycleState());
clientSession.changeVisibility(r, true);
assertEquals(ON_START, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testWindowVisibilityChange_OnStop() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
clientSession.launchActivity(r);
clientSession.startActivity(r);
clientSession.resumeActivity(r);
clientSession.pauseActivity(r);
clientSession.stopActivity(r);
assertEquals(ON_STOP, r.getLifecycleState());
clientSession.changeVisibility(r, true);
assertEquals(ON_START, r.getLifecycleState());
clientSession.changeVisibility(r, false);
assertEquals(ON_STOP, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testLifecycleAfterFinished_OnCreate() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
Activity activity = clientSession.launchActivity(r);
activity.finish();
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.startActivity(r);
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.resumeActivity(r);
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.pauseActivity(r);
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.stopActivity(r);
assertEquals(ON_CREATE, r.getLifecycleState());
clientSession.destroyActivity(r);
assertEquals(ON_DESTROY, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testLifecycleAfterFinished_OnStart() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
Activity activity = clientSession.launchActivity(r);
clientSession.startActivity(r);
activity.finish();
assertEquals(ON_START, r.getLifecycleState());
clientSession.resumeActivity(r);
assertEquals(ON_START, r.getLifecycleState());
clientSession.pauseActivity(r);
assertEquals(ON_START, r.getLifecycleState());
clientSession.stopActivity(r);
assertEquals(ON_STOP, r.getLifecycleState());
clientSession.destroyActivity(r);
assertEquals(ON_DESTROY, r.getLifecycleState());
}
}
@Test
@UiThreadTest
public void testLifecycleAfterFinished_OnResume() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityClientRecord r = clientSession.stubActivityRecord();
Activity activity = clientSession.launchActivity(r);
clientSession.startActivity(r);
clientSession.resumeActivity(r);
activity.finish();
assertEquals(ON_RESUME, r.getLifecycleState());
clientSession.pauseActivity(r);
assertEquals(ON_PAUSE, r.getLifecycleState());
clientSession.stopActivity(r);
assertEquals(ON_STOP, r.getLifecycleState());
clientSession.destroyActivity(r);
assertEquals(ON_DESTROY, r.getLifecycleState());
}
}
private class ClientMockSession implements AutoCloseable {
private MockitoSession mMockSession;
private ActivityThread mThread;
private ClientMockSession() throws RemoteException {
mThread = ActivityThread.currentActivityThread();
mMockSession = mockitoSession()
.strictness(Strictness.LENIENT)
.spyStatic(ActivityTaskManager.class)
.spyStatic(WindowManagerGlobal.class)
.startMocking();
doReturn(Mockito.mock(WindowManagerGlobal.class))
.when(WindowManagerGlobal::getInstance);
IActivityTaskManager mockAtm = Mockito.mock(IActivityTaskManager.class);
doReturn(mockAtm).when(ActivityTaskManager::getService);
when(mockAtm.finishActivity(any(), anyInt(), any(), anyInt())).thenReturn(true);
}
private Activity launchActivity(ActivityClientRecord r) {
return mThread.handleLaunchActivity(r, null /* pendingActions */,
null /* customIntent */);
}
private void startActivity(ActivityClientRecord r) {
mThread.handleStartActivity(r, null /* pendingActions */);
}
private void resumeActivity(ActivityClientRecord r) {
mThread.handleResumeActivity(r.token, true /* finalStateRequest */,
true /* isForward */, "test");
}
private void pauseActivity(ActivityClientRecord r) {
mThread.handlePauseActivity(r.token, false /* finished */,
false /* userLeaving */, 0 /* configChanges */, null /* pendingActions */,
"test");
}
private void stopActivity(ActivityClientRecord r) {
mThread.handleStopActivity(r.token, false /* show */, 0 /* configChanges */,
new PendingTransactionActions(), false /* finalStateRequest */, "test");
}
private void destroyActivity(ActivityClientRecord r) {
mThread.handleDestroyActivity(r.token, true /* finishing */, 0 /* configChanges */,
false /* getNonConfigInstance */, "test");
}
private void changeVisibility(ActivityClientRecord r, boolean show) {
mThread.handleWindowVisibility(r.token, show);
}
private ActivityClientRecord stubActivityRecord() {
ComponentName component = new ComponentName(
InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class);
ActivityInfo info = new ActivityInfo();
info.packageName = component.getPackageName();
info.name = component.getClassName();
info.exported = true;
info.applicationInfo = new ApplicationInfo();
info.applicationInfo.packageName = info.packageName;
info.applicationInfo.uid = UserHandle.myUserId();
return new ActivityClientRecord(new Binder(), Intent.makeMainActivity(component),
0 /* ident */, info, new Configuration(),
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */,
null /* voiceInteractor */, null /* state */, null /* persistentState */,
null /* pendingResults */, null /* pendingNewIntents */, true /* isForward */,
null /* profilerInfo */, mThread /* client */);
}
@Override
public void close() {
mMockSession.finishMocking();
}
}
// Test activity
public static class TestActivity extends Activity {
}
}

View File

@@ -34,6 +34,8 @@ import com.android.test.filters.SelectTest;
public final class FrameworksTestsFilter extends SelectTest {
private static final String[] SELECTED_TESTS = {
// Test specifications for FrameworksMockingCoreTests.
"android.app.activity.ActivityThreadClientTest",
// Test specifications for FrameworksCoreTests.
"android.app.servertransaction.", // all tests under the package.
"android.view.DisplayCutoutTest",