Attempt to reduce race of WmTests

WmTests contains both e2e and mocked unit tests. The mocked tests
are sensitive to concurrent issue. Such as if the activity is still
destroying on main thread, then when the next test starts to establish
mocked environment, it may throw ConcurrentModificationException
when iterating invocation records because there is additional access
from other thread.

This change waits until the test activities are destroyed, that avoids
overlapping with the next test.

Bug: 293549565
Bug: 254268165
Test: TaskStackChangedListenerTest
Change-Id: I1b2eb77585ba0a1ee936ee921af95e9c2113ec63
This commit is contained in:
Riddle Hsu
2023-07-28 15:31:50 +00:00
parent 89563b52ea
commit c5482b12fe
9 changed files with 85 additions and 0 deletions

View File

@@ -65,6 +65,7 @@
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInVirtualDisplay"
android:resizeableActivity="true" />
<activity android:name="com.android.server.wm.TaskStackChangedListenerTest$LandscapeActivity"
android:configChanges="screenLayout|screenSize|smallestScreenSize|orientation"
android:screenOrientation="sensorLandscape"
android:showWhenLocked="true"
android:turnScreenOn="true" />

View File

@@ -34,6 +34,8 @@ import android.os.StrictMode;
import android.os.strictmode.InstanceCountViolation;
import android.util.Log;
import com.android.server.wm.utils.CommonUtils;
import org.junit.After;
import org.junit.Test;
@@ -63,6 +65,10 @@ public class ActivityLeakTests {
activity.finish();
}
}
if (!mStartedActivityList.isEmpty()) {
CommonUtils.waitUntilActivityRemoved(
mStartedActivityList.get(mStartedActivityList.size() - 1));
}
mStartedActivityList.clear();
}

View File

@@ -54,6 +54,8 @@ import android.window.TaskOrganizer;
import androidx.test.filters.MediumTest;
import com.android.server.wm.utils.CommonUtils;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -172,6 +174,7 @@ public class ActivityOptionsTest {
instrumentation.removeMonitor(monitor);
if (mainActivity != null) {
mainActivity.finish();
CommonUtils.waitUntilActivityRemoved(mainActivity);
}
}
}

View File

@@ -56,6 +56,9 @@ import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule;
import com.android.server.wm.utils.CommonUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -90,6 +93,11 @@ public class ScreenshotTests {
mInstrumentation.waitForIdleSync();
}
@After
public void tearDown() {
CommonUtils.waitUntilActivityRemoved(mActivity);
}
@Test
public void testScreenshotSecureLayers() throws InterruptedException {
SurfaceControl secureSC = new SurfaceControl.Builder()

View File

@@ -52,6 +52,8 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.server.wm.utils.CommonUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -87,6 +89,7 @@ public class SurfaceControlViewHostTests {
@After
public void tearDown() {
mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
CommonUtils.waitUntilActivityRemoved(mActivity);
}
@Test

View File

@@ -47,6 +47,9 @@ import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import com.android.server.wm.utils.CommonUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -79,6 +82,12 @@ public class SurfaceSyncGroupTests {
mHandler = mHandlerThread.getThreadHandler();
}
@After
public void tearDown() {
mHandlerThread.quitSafely();
CommonUtils.waitUntilActivityRemoved(mActivity);
}
@Test
public void testOverlappingSyncsEnsureOrder_WhenTimeout() throws InterruptedException {
WindowManager.LayoutParams params = new WindowManager.LayoutParams();

View File

@@ -25,6 +25,9 @@ import android.view.cts.surfacevalidator.CapturedActivity;
import androidx.test.rule.ActivityTestRule;
import com.android.server.wm.utils.CommonUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -54,6 +57,11 @@ public class SurfaceViewSyncContinuousTest {
}
}
@After
public void tearDown() {
CommonUtils.waitUntilActivityRemoved(mCapturedActivity);
}
@Test
public void testSurfaceViewSyncDuringResize() throws Throwable {
mCapturedActivity.verifyTest(new SurfaceViewSyncValidatorTestCase(), mName);

View File

@@ -60,6 +60,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
@@ -76,6 +77,7 @@ public class TaskStackChangedListenerTest {
private ITaskStackListener mTaskStackListener;
private VirtualDisplay mVirtualDisplay;
private ImageReader mImageReader;
private final ArrayList<Activity> mStartedActivities = new ArrayList<>();
private static final int WAIT_TIMEOUT_MS = 5000;
private static final Object sLock = new Object();
@@ -94,6 +96,19 @@ public class TaskStackChangedListenerTest {
mVirtualDisplay.release();
mImageReader.close();
}
// Finish from bottom to top.
final int size = mStartedActivities.size();
for (int i = 0; i < size; i++) {
final Activity activity = mStartedActivities.get(i);
if (!activity.isFinishing()) {
activity.finish();
}
}
// Wait for the last launched activity to be removed.
if (size > 0) {
CommonUtils.waitUntilActivityRemoved(mStartedActivities.get(size - 1));
}
mStartedActivities.clear();
}
private VirtualDisplay createVirtualDisplay() {
@@ -381,6 +396,7 @@ public class TaskStackChangedListenerTest {
throw new RuntimeException("Timed out waiting for Activity");
}
activity.waitForResumeStateChange(true);
mStartedActivities.add(activity);
return activity;
}

View File

@@ -18,14 +18,23 @@ package com.android.server.wm.utils;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.UiAutomation;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import androidx.test.uiautomator.UiDevice;
import java.io.IOException;
/** Provides common utility functions. */
public class CommonUtils {
private static final String TAG = "CommonUtils";
private static final long REMOVAL_TIMEOUT_MS = 3000;
private static final long TIMEOUT_INTERVAL_MS = 200;
public static UiAutomation getUiAutomation() {
return getInstrumentation().getUiAutomation();
}
@@ -50,4 +59,26 @@ public class CommonUtils {
device.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
device.pressKeyCode(KeyEvent.KEYCODE_MENU);
}
public static void waitUntilActivityRemoved(Activity activity) {
if (!activity.isFinishing()) {
activity.finish();
}
final UiDevice uiDevice = UiDevice.getInstance(getInstrumentation());
final String classPattern = activity.getComponentName().flattenToShortString();
final long startTime = SystemClock.uptimeMillis();
while (SystemClock.uptimeMillis() - startTime <= REMOVAL_TIMEOUT_MS) {
SystemClock.sleep(TIMEOUT_INTERVAL_MS);
final String windowTokenDump;
try {
windowTokenDump = uiDevice.executeShellCommand("dumpsys window tokens");
} catch (IOException e) {
throw new RuntimeException(e);
}
if (!windowTokenDump.contains(classPattern)) {
return;
}
}
Log.i(TAG, "Removal timeout of " + classPattern);
}
}