Merge "Add unit tests for SplitAttributes runtime APIs" into udc-dev am: f3710c72ae

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21473685

Change-Id: I03b9c59b5bd29d8c9345fec368814148935fd7e7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Charles Chen
2023-02-20 06:47:36 +00:00
committed by Automerger Merge Worker
2 changed files with 138 additions and 9 deletions

View File

@@ -308,7 +308,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
synchronized (mLock) { synchronized (mLock) {
final List<TaskFragmentContainer> containers = taskContainer.mContainers; final List<TaskFragmentContainer> containers = taskContainer.mContainers;
// Clean up the TaskFragmentContainers by the z-order from the lowest. // Clean up the TaskFragmentContainers by the z-order from the lowest.
for (int i = 0; i < containers.size() - 1; i++) { for (int i = 0; i < containers.size(); i++) {
final TaskFragmentContainer container = containers.get(i); final TaskFragmentContainer container = containers.get(i);
if (pendingFinishingContainers.contains(container)) { if (pendingFinishingContainers.contains(container)) {
// Don't update records here to prevent double invocation. // Don't update records here to prevent double invocation.
@@ -318,7 +318,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
} }
// Remove container records. // Remove container records.
removeContainers(taskContainer, pendingFinishingContainers); removeContainers(taskContainer, pendingFinishingContainers);
// Update the change to the client side. // Update the change to the server side.
updateContainersInTaskIfVisible(wct, taskContainer.getTaskId()); updateContainersInTaskIfVisible(wct, taskContainer.getTaskId());
} }
}); });
@@ -353,21 +353,25 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override @Override
public void updateSplitAttributes(@NonNull IBinder splitInfoToken, public void updateSplitAttributes(@NonNull IBinder splitInfoToken,
@NonNull SplitAttributes splitAttributes) { @NonNull SplitAttributes splitAttributes) {
Objects.requireNonNull(splitInfoToken);
Objects.requireNonNull(splitAttributes);
synchronized (mLock) { synchronized (mLock) {
final SplitContainer splitContainer = getSplitContainer(splitInfoToken); final SplitContainer splitContainer = getSplitContainer(splitInfoToken);
if (splitContainer == null) { if (splitContainer == null) {
Log.w(TAG, "Cannot find SplitContainer for token:" + splitInfoToken); Log.w(TAG, "Cannot find SplitContainer for token:" + splitInfoToken);
return; return;
} }
WindowContainerTransaction wct = mTransactionManager.startNewTransaction() // Override the default split Attributes so that it will be applied
.getTransaction(); // if the SplitContainer is not visible currently.
if (updateSplitContainerIfNeeded(splitContainer, wct, splitAttributes)) {
splitContainer.updateDefaultSplitAttributes(splitAttributes); splitContainer.updateDefaultSplitAttributes(splitAttributes);
mTransactionManager.getCurrentTransactionRecord()
.apply(false /* shouldApplyIndependently */); final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
final WindowContainerTransaction wct = transactionRecord.getTransaction();
if (updateSplitContainerIfNeeded(splitContainer, wct, splitAttributes)) {
transactionRecord.apply(false /* shouldApplyIndependently */);
} else { } else {
// Abort if the SplitContainer wasn't updated. // Abort if the SplitContainer wasn't updated.
mTransactionManager.getCurrentTransactionRecord().abort(); transactionRecord.abort();
} }
} }
} }
@@ -1559,8 +1563,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* *
* @return {@code true} if the update succeed. Otherwise, returns {@code false}. * @return {@code true} if the update succeed. Otherwise, returns {@code false}.
*/ */
@VisibleForTesting
@GuardedBy("mLock") @GuardedBy("mLock")
private boolean updateSplitContainerIfNeeded(@NonNull SplitContainer splitContainer, boolean updateSplitContainerIfNeeded(@NonNull SplitContainer splitContainer,
@NonNull WindowContainerTransaction wct, @Nullable SplitAttributes splitAttributes) { @NonNull WindowContainerTransaction wct, @Nullable SplitAttributes splitAttributes) {
if (!isTopMostSplit(splitContainer)) { if (!isTopMostSplit(splitContainer)) {
// Skip position update - it isn't the topmost split. // Skip position update - it isn't the topmost split.
@@ -1904,6 +1909,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null; return null;
} }
@VisibleForTesting
@Nullable @Nullable
@GuardedBy("mLock") @GuardedBy("mLock")
SplitContainer getSplitContainer(@NonNull IBinder token) { SplitContainer getSplitContainer(@NonNull IBinder token) {

View File

@@ -67,6 +67,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.Activity; import android.app.Activity;
@@ -82,6 +83,7 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.view.WindowMetrics; import android.view.WindowMetrics;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
@@ -100,12 +102,14 @@ import androidx.window.extensions.layout.WindowLayoutInfo;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -1323,6 +1327,125 @@ public class SplitControllerTest {
verify(mTransaction).startActivityInTaskFragment(any(), any(), any(), any()); verify(mTransaction).startActivityInTaskFragment(any(), any(), any(), any());
} }
@Test
public void testFinishActivityStacks_emptySet_earlyReturn() {
mSplitController.finishActivityStacks(Collections.emptySet());
verify(mSplitController, never()).updateContainersInTaskIfVisible(any(), anyInt());
}
@Test
public void testFinishActivityStacks_invalidStacks_earlyReturn() {
mSplitController.finishActivityStacks(Collections.singleton(new Binder()));
verify(mSplitController, never()).updateContainersInTaskIfVisible(any(), anyInt());
}
@Test
public void testFinishActivityStacks_finishSingleActivityStack() {
TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
tf.setInfo(mTransaction, createMockTaskFragmentInfo(tf, mActivity));
List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
.mContainers;
assertEquals(containers.get(0), tf);
mSplitController.finishActivityStacks(Collections.singleton(tf.getTaskFragmentToken()));
verify(mSplitPresenter).deleteTaskFragment(any(), eq(tf.getTaskFragmentToken()));
assertTrue(containers.isEmpty());
}
@Test
public void testFinishActivityStacks_finishActivityStacksInOrder() {
TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
TaskFragmentContainer topTf = mSplitController.newContainer(mActivity, TASK_ID);
bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity));
topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
.mContainers;
assertEquals(containers.size(), 2);
Set<IBinder> activityStackTokens = new ArraySet<>(new IBinder[]{
topTf.getTaskFragmentToken(), bottomTf.getTaskFragmentToken()});
mSplitController.finishActivityStacks(activityStackTokens);
ArgumentCaptor<IBinder> argumentCaptor = ArgumentCaptor.forClass(IBinder.class);
verify(mSplitPresenter, times(2)).deleteTaskFragment(any(), argumentCaptor.capture());
List<IBinder> fragmentTokens = argumentCaptor.getAllValues();
assertEquals("The ActivityStack must be deleted from the lowest z-order "
+ "regardless of the order in ActivityStack set",
bottomTf.getTaskFragmentToken(), fragmentTokens.get(0));
assertEquals("The ActivityStack must be deleted from the lowest z-order "
+ "regardless of the order in ActivityStack set",
topTf.getTaskFragmentToken(), fragmentTokens.get(1));
assertTrue(containers.isEmpty());
}
@Test
public void testUpdateSplitAttributes_invalidSplitContainerToken_earlyReturn() {
mSplitController.updateSplitAttributes(new Binder(), SPLIT_ATTRIBUTES);
verify(mTransactionManager, never()).startNewTransaction();
}
@Test
public void testUpdateSplitAttributes_nullParams_throwException() {
assertThrows(NullPointerException.class,
() -> mSplitController.updateSplitAttributes(null, SPLIT_ATTRIBUTES));
final SplitContainer splitContainer = mock(SplitContainer.class);
final IBinder token = new Binder();
doReturn(token).when(splitContainer).getToken();
doReturn(splitContainer).when(mSplitController).getSplitContainer(eq(token));
assertThrows(NullPointerException.class,
() -> mSplitController.updateSplitAttributes(token, null));
}
@Test
public void testUpdateSplitAttributes_doNotNeedToUpdateSplitContainer_doNotApplyTransaction() {
final SplitContainer splitContainer = mock(SplitContainer.class);
final IBinder token = new Binder();
doReturn(token).when(splitContainer).getToken();
doReturn(splitContainer).when(mSplitController).getSplitContainer(eq(token));
doReturn(false).when(mSplitController).updateSplitContainerIfNeeded(
eq(splitContainer), any(), eq(SPLIT_ATTRIBUTES));
TransactionManager.TransactionRecord testRecord =
mock(TransactionManager.TransactionRecord.class);
doReturn(testRecord).when(mTransactionManager).startNewTransaction();
mSplitController.updateSplitAttributes(token, SPLIT_ATTRIBUTES);
verify(splitContainer).updateDefaultSplitAttributes(eq(SPLIT_ATTRIBUTES));
verify(testRecord).abort();
}
@Test
public void testUpdateSplitAttributes_splitContainerUpdated_updateAttrs() {
final SplitContainer splitContainer = mock(SplitContainer.class);
final IBinder token = new Binder();
doReturn(token).when(splitContainer).getToken();
doReturn(splitContainer).when(mSplitController).getSplitContainer(eq(token));
doReturn(true).when(mSplitController).updateSplitContainerIfNeeded(
eq(splitContainer), any(), eq(SPLIT_ATTRIBUTES));
TransactionManager.TransactionRecord testRecord =
mock(TransactionManager.TransactionRecord.class);
doReturn(testRecord).when(mTransactionManager).startNewTransaction();
mSplitController.updateSplitAttributes(token, SPLIT_ATTRIBUTES);
verify(splitContainer).updateDefaultSplitAttributes(eq(SPLIT_ATTRIBUTES));
verify(testRecord).apply(eq(false));
}
/** Creates a mock activity in the organizer process. */ /** Creates a mock activity in the organizer process. */
private Activity createMockActivity() { private Activity createMockActivity() {
return createMockActivity(TASK_ID); return createMockActivity(TASK_ID);