diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index 14c58e744dacd..def1f457fb4ab 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -27,5 +27,15 @@ } ] } + ], + "postsubmit": [ + { + "file_patterns": ["(/|^)ActivityThreadClientTest.java"], + "name": "FrameworksMockingCoreTests" + }, + { + "file_patterns": ["(/|^)ActivityThreadTest.java"], + "name": "FrameworksCoreTests" + } ] } diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp new file mode 100644 index 0000000000000..ae3ff8612eee8 --- /dev/null +++ b/core/tests/mockingcoretests/Android.bp @@ -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", +} diff --git a/core/tests/mockingcoretests/AndroidManifest.xml b/core/tests/mockingcoretests/AndroidManifest.xml new file mode 100644 index 0000000000000..b9ee0852366de --- /dev/null +++ b/core/tests/mockingcoretests/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + diff --git a/core/tests/mockingcoretests/AndroidTest.xml b/core/tests/mockingcoretests/AndroidTest.xml new file mode 100644 index 0000000000000..47aa410033365 --- /dev/null +++ b/core/tests/mockingcoretests/AndroidTest.xml @@ -0,0 +1,31 @@ + + + + diff --git a/core/tests/mockingcoretests/README b/core/tests/mockingcoretests/README new file mode 100644 index 0000000000000..c0f65c29a8a4a --- /dev/null +++ b/core/tests/mockingcoretests/README @@ -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 diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java new file mode 100644 index 0000000000000..86d55ea4f5c25 --- /dev/null +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -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. + * + *

Build/Install/Run: + * atest FrameworksMockingCoreTests:android.app.activity.ActivityThreadClientTest + * + *

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 { + } +} diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index e0d74e0908ee3..c9e3404e0f1a0 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -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",