Merge "Clean-up PersisterQueue tests" into qt-dev
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
index 4e906bc..4673992 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -39,7 +39,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Predicate;
 
 /**
  * Build/Install/Run:
@@ -47,208 +46,222 @@
  */
 @MediumTest
 @Presubmit
-public class PersisterQueueTests implements PersisterQueue.Listener {
+public class PersisterQueueTests {
     private static final long INTER_WRITE_DELAY_MS = 50;
     private static final long PRE_TASK_DELAY_MS = 300;
-    // We allow at most 1s more than the expected timeout.
-    private static final long TIMEOUT_ALLOWANCE = 100;
-
-    private static final Predicate<MatchingTestItem> TEST_ITEM_PREDICATE = item -> item.mMatching;
-
-    private AtomicInteger mItemCount;
-    private CountDownLatch mSetUpLatch;
-    private volatile CountDownLatch mLatch;
-    private List<Boolean> mProbablyDoneResults;
+    // We allow at most 0.2s more than the expected timeout.
+    private static final long TIMEOUT_ALLOWANCE = 200;
 
     private final PersisterQueue mTarget =
             new PersisterQueue(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
 
+    private TestPersisterQueueListener mListener;
+    private TestWriteQueueItemFactory mFactory;
+
     @Before
     public void setUp() throws Exception {
-        mItemCount = new AtomicInteger(0);
-        mProbablyDoneResults = new ArrayList<>();
-        mSetUpLatch = new CountDownLatch(1);
+        mListener = new TestPersisterQueueListener();
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
+        mTarget.addListener(mListener);
 
-        mTarget.addListener(this);
+        mFactory = new TestWriteQueueItemFactory();
+
         mTarget.startPersisting();
 
         assertTrue("Target didn't call callback on start up.",
-                mSetUpLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+                mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
     }
 
     @After
     public void tearDown() throws Exception {
         mTarget.stopPersisting();
-        mTarget.removeListener(this);
+        mTarget.removeListener(mListener);
     }
 
     @Test
     public void testCallCallbackOnStartUp() {
         // The onPreProcessItem() must be called on start up.
-        assertEquals(1, mProbablyDoneResults.size());
+        assertEquals(1, mListener.mProbablyDoneResults.size());
         // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(0));
+        assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(0));
     }
 
     @Test
     public void testProcessOneItem() throws Exception {
-        mLatch = new CountDownLatch(1);
+        mFactory.setExpectedProcessedItemNumber(1);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
 
         final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
+        mTarget.addItem(mFactory.createItem(), false);
+        assertTrue("Target didn't process item enough times.",
+                mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
         final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
         assertTrue("Target didn't wait enough time before processing item. duration: "
                         + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS + "ms",
                 processDuration >= PRE_TASK_DELAY_MS);
 
+        assertTrue("Target didn't call callback enough times.",
+                mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
         // Once before processing this item, once after that.
-        assertEquals(2, mProbablyDoneResults.size());
+        assertEquals(2, mListener.mProbablyDoneResults.size());
         // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+        assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(1));
     }
 
     @Test
     public void testProcessOneItem_Flush() throws Exception {
-        mLatch = new CountDownLatch(1);
+        mFactory.setExpectedProcessedItemNumber(1);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
 
         final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), true);
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
+        mTarget.addItem(mFactory.createItem(), true);
+        assertTrue("Target didn't process item enough times.",
+                mFactory.waitForAllExpectedItemsProcessed(TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
         final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
         assertTrue("Target didn't process item immediately when flushing. duration: "
                         + processDuration + "ms pretask delay: "
                         + PRE_TASK_DELAY_MS + "ms",
                 processDuration < PRE_TASK_DELAY_MS);
 
+        assertTrue("Target didn't call callback enough times.",
+                mFactory.waitForAllExpectedItemsProcessed(TIMEOUT_ALLOWANCE));
         // Once before processing this item, once after that.
-        assertEquals(2, mProbablyDoneResults.size());
+        assertEquals(2, mListener.mProbablyDoneResults.size());
         // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+        assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(1));
     }
 
     @Test
     public void testProcessTwoItems() throws Exception {
-        mLatch = new CountDownLatch(2);
+        mFactory.setExpectedProcessedItemNumber(2);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(2);
 
         final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        mTarget.addItem(new TestItem(), false);
+        mTarget.addItem(mFactory.createItem(), false);
+        mTarget.addItem(mFactory.createItem(), false);
         assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
-                        TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process all items.", 2, mItemCount.get());
+                mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS
+                        + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process all items.", 2, mFactory.getTotalProcessedItemCount());
         final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
         assertTrue("Target didn't wait enough time before processing item. duration: "
                         + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS
                         + "ms inter write delay: " + INTER_WRITE_DELAY_MS + "ms",
                 processDuration >= PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS);
-
+        assertTrue("Target didn't call the onPreProcess callback enough times",
+                mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
         // Once before processing this item, once after that.
-        assertEquals(3, mProbablyDoneResults.size());
+        assertEquals(3, mListener.mProbablyDoneResults.size());
         // The first one must be called with probably done being false.
-        assertFalse("The first probablyDone must be false.", mProbablyDoneResults.get(1));
+        assertFalse("The first probablyDone must be false.", mListener.mProbablyDoneResults.get(1));
         // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+        assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(2));
     }
 
     @Test
     @FlakyTest(bugId = 128526085)
     public void testProcessTwoItems_OneAfterAnother() throws Exception {
         // First item
-        mLatch = new CountDownLatch(1);
+        mFactory.setExpectedProcessedItemNumber(1);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
         long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        mTarget.addItem(mFactory.createItem(), false);
+        assertTrue("Target didn't process item enough times.",
+                mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
         long processDuration = SystemClock.uptimeMillis() - dispatchTime;
         assertTrue("Target didn't wait enough time before processing item."
                         + processDuration + "ms pretask delay: "
                         + PRE_TASK_DELAY_MS + "ms",
                 processDuration >= PRE_TASK_DELAY_MS);
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
+        assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
+        assertTrue("Target didn't call callback enough times.",
+                mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
 
         // Second item
-        mLatch = new CountDownLatch(1);
+        mFactory.setExpectedProcessedItemNumber(1);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
         dispatchTime = SystemClock.uptimeMillis();
         // Synchronize on the instance to make sure we schedule the item after it starts to wait for
         // task indefinitely.
         synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
+            mTarget.addItem(mFactory.createItem(), false);
         }
-        assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process all items.", 2, mItemCount.get());
+        assertTrue("Target didn't process item enough times.",
+                mFactory.waitForAllExpectedItemsProcessed(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process all items.", 2, mFactory.getTotalProcessedItemCount());
         processDuration = SystemClock.uptimeMillis() - dispatchTime;
         assertTrue("Target didn't wait enough time before processing item. Process time: "
                         + processDuration + "ms pre task delay: "
                         + PRE_TASK_DELAY_MS + "ms",
                 processDuration >= PRE_TASK_DELAY_MS);
 
+        assertTrue("Target didn't call callback enough times.",
+                mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
         // Once before processing this item, once after that.
-        assertEquals(3, mProbablyDoneResults.size());
+        assertEquals(3, mListener.mProbablyDoneResults.size());
         // The last one must be called with probably done being true.
-        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+        assertTrue("The last probablyDone must be true.", mListener.mProbablyDoneResults.get(2));
     }
 
     @Test
     public void testFindLastItemNotReturnDifferentType() {
         synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
-            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+            mTarget.addItem(mFactory.createItem(), false);
+            assertNull(mTarget.findLastItem(TestItem::shouldKeepOnFilter,
+                    FilterableTestItem.class));
         }
     }
 
     @Test
     public void testFindLastItemNotReturnMismatchItem() {
         synchronized (mTarget) {
-            mTarget.addItem(new MatchingTestItem(false), false);
-            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+            mTarget.addItem(mFactory.createFilterableItem(false), false);
+            assertNull(mTarget.findLastItem(TestItem::shouldKeepOnFilter,
+                    FilterableTestItem.class));
         }
     }
 
     @Test
     public void testFindLastItemReturnMatchedItem() {
         synchronized (mTarget) {
-            final MatchingTestItem item = new MatchingTestItem(true);
+            final FilterableTestItem item = mFactory.createFilterableItem(true);
             mTarget.addItem(item, false);
-            assertSame(item, mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+            assertSame(item, mTarget.findLastItem(TestItem::shouldKeepOnFilter,
+                    FilterableTestItem.class));
         }
     }
 
     @Test
     public void testRemoveItemsNotRemoveDifferentType() throws Exception {
-        mLatch = new CountDownLatch(1);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
         synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
-            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+            mTarget.addItem(mFactory.createItem(), false);
+            mTarget.removeItems(TestItem::shouldKeepOnFilter, FilterableTestItem.class);
         }
         assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
+                mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
     }
 
     @Test
     public void testRemoveItemsNotRemoveMismatchedItem() throws Exception {
-        mLatch = new CountDownLatch(1);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
         synchronized (mTarget) {
-            mTarget.addItem(new MatchingTestItem(false), false);
-            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+            mTarget.addItem(mFactory.createFilterableItem(false), false);
+            mTarget.removeItems(TestItem::shouldKeepOnFilter, FilterableTestItem.class);
         }
         assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
+                mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
     }
 
     @Test
     public void testUpdateLastOrAddItemUpdatesMatchedItem() throws Exception {
-        mLatch = new CountDownLatch(1);
-        final MatchingTestItem scheduledItem = new MatchingTestItem(true);
-        final MatchingTestItem expected = new MatchingTestItem(true);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
+        final FilterableTestItem scheduledItem = mFactory.createFilterableItem(true);
+        final FilterableTestItem expected = mFactory.createFilterableItem(true);
         synchronized (mTarget) {
             mTarget.addItem(scheduledItem, false);
             mTarget.updateLastOrAddItem(expected, false);
@@ -256,15 +269,15 @@
 
         assertSame(expected, scheduledItem.mUpdateFromItem);
         assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
+                mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
     }
 
     @Test
     public void testUpdateLastOrAddItemUpdatesAddItemWhenNoMatch() throws Exception {
-        mLatch = new CountDownLatch(2);
-        final MatchingTestItem scheduledItem = new MatchingTestItem(false);
-        final MatchingTestItem expected = new MatchingTestItem(true);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(2);
+        final FilterableTestItem scheduledItem = mFactory.createFilterableItem(false);
+        final FilterableTestItem expected = mFactory.createFilterableItem(true);
         synchronized (mTarget) {
             mTarget.addItem(scheduledItem, false);
             mTarget.updateLastOrAddItem(expected, false);
@@ -272,73 +285,132 @@
 
         assertNull(scheduledItem.mUpdateFromItem);
         assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
-                        TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 2, mItemCount.get());
+                mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS
+                        + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process item.", 2, mFactory.getTotalProcessedItemCount());
     }
 
     @Test
     public void testRemoveItemsRemoveMatchedItem() throws Exception {
-        mLatch = new CountDownLatch(1);
+        mListener.setExpectedOnPreProcessItemCallbackTimes(1);
         synchronized (mTarget) {
-            mTarget.addItem(new TestItem(), false);
-            mTarget.addItem(new MatchingTestItem(true), false);
-            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+            mTarget.addItem(mFactory.createItem(), false);
+            mTarget.addItem(mFactory.createFilterableItem(true), false);
+            mTarget.removeItems(TestItem::shouldKeepOnFilter, FilterableTestItem.class);
         }
         assertTrue("Target didn't call callback enough times.",
-                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
-        assertEquals("Target didn't process item.", 1, mItemCount.get());
+                mListener.waitForAllExpectedCallbackDone(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE));
+        assertEquals("Target didn't process item.", 1, mFactory.getTotalProcessedItemCount());
     }
 
     @Test
     public void testFlushWaitSynchronously() {
         final long dispatchTime = SystemClock.uptimeMillis();
-        mTarget.addItem(new TestItem(), false);
-        mTarget.addItem(new TestItem(), false);
+        mTarget.addItem(mFactory.createItem(), false);
+        mTarget.addItem(mFactory.createItem(), false);
         mTarget.flush();
         assertEquals("Flush should wait until all items are processed before return.",
-                2, mItemCount.get());
+                2, mFactory.getTotalProcessedItemCount());
         final long processTime = SystemClock.uptimeMillis() - dispatchTime;
         assertWithMessage("Flush should trigger immediate flush without delays. processTime: "
                 + processTime).that(processTime).isLessThan(TIMEOUT_ALLOWANCE);
     }
 
-    @Override
-    public void onPreProcessItem(boolean queueEmpty) {
-        mProbablyDoneResults.add(queueEmpty);
+    private static class TestWriteQueueItemFactory {
+        private final AtomicInteger mItemCount = new AtomicInteger(0);;
+        private CountDownLatch mLatch;
 
-        final CountDownLatch latch = mLatch;
-        if (latch != null) {
-            latch.countDown();
+        int getTotalProcessedItemCount() {
+            return mItemCount.get();
         }
 
-        mSetUpLatch.countDown();
+        void setExpectedProcessedItemNumber(int countDown) {
+            mLatch = new CountDownLatch(countDown);
+        }
+
+        boolean waitForAllExpectedItemsProcessed(long timeoutInMilliseconds)
+                throws InterruptedException {
+            return mLatch.await(timeoutInMilliseconds, TimeUnit.MILLISECONDS);
+        }
+
+        TestItem createItem() {
+            return new TestItem(mItemCount, mLatch);
+        }
+
+        FilterableTestItem createFilterableItem(boolean shouldKeepOnFilter) {
+            return new FilterableTestItem(shouldKeepOnFilter, mItemCount, mLatch);
+        }
     }
 
-    private class TestItem<T extends TestItem<T>> implements PersisterQueue.WriteQueueItem<T> {
+    private static class TestItem<T extends TestItem<T>>
+            implements PersisterQueue.WriteQueueItem<T> {
+        private AtomicInteger mItemCount;
+        private CountDownLatch mLatch;
+
+        TestItem(AtomicInteger itemCount, CountDownLatch latch) {
+            mItemCount = itemCount;
+            mLatch = latch;
+        }
+
         @Override
         public void process() {
             mItemCount.getAndIncrement();
+            if (mLatch != null) {
+                // Count down the latch at the last step is necessary, as it's a kind of lock to the
+                // next assert in many test cases.
+                mLatch.countDown();
+            }
+        }
+
+        boolean shouldKeepOnFilter() {
+            return true;
         }
     }
 
-    private class MatchingTestItem extends TestItem<MatchingTestItem> {
-        private boolean mMatching;
+    private static class FilterableTestItem extends TestItem<FilterableTestItem> {
+        private boolean mShouldKeepOnFilter;
 
-        private MatchingTestItem mUpdateFromItem;
+        private FilterableTestItem mUpdateFromItem;
 
-        private MatchingTestItem(boolean matching) {
-            mMatching = matching;
+        private FilterableTestItem(boolean shouldKeepOnFilter, AtomicInteger mItemCount,
+                CountDownLatch mLatch) {
+            super(mItemCount, mLatch);
+            mShouldKeepOnFilter = shouldKeepOnFilter;
         }
 
         @Override
-        public boolean matches(MatchingTestItem item) {
-            return item.mMatching;
+        public boolean matches(FilterableTestItem item) {
+            return item.mShouldKeepOnFilter;
         }
 
         @Override
-        public void updateFrom(MatchingTestItem item) {
+        public void updateFrom(FilterableTestItem item) {
             mUpdateFromItem = item;
         }
+
+        @Override
+        boolean shouldKeepOnFilter() {
+            return mShouldKeepOnFilter;
+        }
+    }
+
+    private class TestPersisterQueueListener implements PersisterQueue.Listener {
+        CountDownLatch mCallbackLatch;
+        final List<Boolean> mProbablyDoneResults = new ArrayList<>();
+
+        @Override
+        public void onPreProcessItem(boolean queueEmpty) {
+            mProbablyDoneResults.add(queueEmpty);
+            mCallbackLatch.countDown();
+        }
+
+        void setExpectedOnPreProcessItemCallbackTimes(int countDown) {
+            mCallbackLatch = new CountDownLatch(countDown);
+        }
+
+        boolean waitForAllExpectedCallbackDone(long timeoutInMilliseconds)
+                throws InterruptedException {
+            return mCallbackLatch.await(timeoutInMilliseconds, TimeUnit.MILLISECONDS);
+        }
     }
 }