diff options
| author | 2023-02-20 06:22:57 +0000 | |
|---|---|---|
| committer | 2023-02-20 06:22:57 +0000 | |
| commit | f3710c72aece54372bc9075da9fb35c1dc7c61ca (patch) | |
| tree | 285f1d2189c8eb64f26d975f935fa619478bfc68 | |
| parent | ad325995ab9ad1f84d54f6e6ccfb527a9e477acd (diff) | |
| parent | 22db2a304cbf9681692cc229ef96c49378b6e63e (diff) | |
Merge "Add unit tests for SplitAttributes runtime APIs" into udc-dev
2 files changed, 138 insertions, 9 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 2c1ddf7cfcbe..9f3c19b2b545 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -308,7 +308,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen synchronized (mLock) { final List<TaskFragmentContainer> containers = taskContainer.mContainers; // 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); if (pendingFinishingContainers.contains(container)) { // Don't update records here to prevent double invocation. @@ -318,7 +318,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } // Remove container records. removeContainers(taskContainer, pendingFinishingContainers); - // Update the change to the client side. + // Update the change to the server side. updateContainersInTaskIfVisible(wct, taskContainer.getTaskId()); } }); @@ -353,21 +353,25 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void updateSplitAttributes(@NonNull IBinder splitInfoToken, @NonNull SplitAttributes splitAttributes) { + Objects.requireNonNull(splitInfoToken); + Objects.requireNonNull(splitAttributes); synchronized (mLock) { final SplitContainer splitContainer = getSplitContainer(splitInfoToken); if (splitContainer == null) { Log.w(TAG, "Cannot find SplitContainer for token:" + splitInfoToken); return; } - WindowContainerTransaction wct = mTransactionManager.startNewTransaction() - .getTransaction(); + // Override the default split Attributes so that it will be applied + // if the SplitContainer is not visible currently. + splitContainer.updateDefaultSplitAttributes(splitAttributes); + + final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); if (updateSplitContainerIfNeeded(splitContainer, wct, splitAttributes)) { - splitContainer.updateDefaultSplitAttributes(splitAttributes); - mTransactionManager.getCurrentTransactionRecord() - .apply(false /* shouldApplyIndependently */); + transactionRecord.apply(false /* shouldApplyIndependently */); } else { // 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}. */ + @VisibleForTesting @GuardedBy("mLock") - private boolean updateSplitContainerIfNeeded(@NonNull SplitContainer splitContainer, + boolean updateSplitContainerIfNeeded(@NonNull SplitContainer splitContainer, @NonNull WindowContainerTransaction wct, @Nullable SplitAttributes splitAttributes) { if (!isTopMostSplit(splitContainer)) { // Skip position update - it isn't the topmost split. @@ -1904,6 +1909,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return null; } + @VisibleForTesting @Nullable @GuardedBy("mLock") SplitContainer getSplitContainer(@NonNull IBinder token) { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 17909d4a0763..61cd7485ff1d 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -67,6 +67,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import android.annotation.NonNull; import android.app.Activity; @@ -82,6 +83,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; import android.view.WindowInsets; import android.view.WindowMetrics; import android.window.TaskFragmentInfo; @@ -100,12 +102,14 @@ import androidx.window.extensions.layout.WindowLayoutInfo; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.function.Consumer; /** @@ -1323,6 +1327,125 @@ public class SplitControllerTest { 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. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); |