OSDN Git Service

Refactor BrightLineClassifier tests to use significantly less memory.
authorDave Mankoff <mankoff@google.com>
Tue, 25 Jun 2019 16:23:05 +0000 (12:23 -0400)
committerDave Mankoff <mankoff@google.com>
Tue, 25 Jun 2019 16:25:41 +0000 (12:25 -0400)
Stop relying so heavily on mockito when it's not necessary. This also
makes tests run significantly faster. Memory usage is now basically flat.

Bug: 135715570
Test: atest SystemUITests
Change-Id: Ifd71e092b19068817600631b5b98a4a8e80a0126

packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java [new file with mode: 0644]
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java

index 1521889..3cc8ec9 100644 (file)
@@ -96,7 +96,7 @@ public class FalsingManagerProxy implements FalsingManager {
             mInternalFalsingManager = new FalsingManagerImpl(context);
         } else {
             mInternalFalsingManager = new BrightLineFalsingManager(
-                    new FalsingDataProvider(context),
+                    new FalsingDataProvider(context.getResources().getDisplayMetrics()),
                     Dependency.get(AsyncSensorManager.class)
             );
         }
index 4975e63..8b11ceb 100644 (file)
@@ -16,7 +16,6 @@
 
 package com.android.systemui.classifier.brightline;
 
-import android.content.Context;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
@@ -51,8 +50,7 @@ public class FalsingDataProvider {
     private MotionEvent mFirstRecentMotionEvent;
     private MotionEvent mLastMotionEvent;
 
-    public FalsingDataProvider(Context context) {
-        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+    public FalsingDataProvider(DisplayMetrics displayMetrics) {
         mXdpi = displayMetrics.xdpi;
         mYdpi = displayMetrics.ydpi;
         mWidthPixels = displayMetrics.widthPixels;
@@ -145,12 +143,20 @@ public class FalsingDataProvider {
 
     boolean isHorizontal() {
         recalculateData();
+        if (mRecentMotionEvents.isEmpty()) {
+            return false;
+        }
+
         return Math.abs(mFirstRecentMotionEvent.getX() - mLastMotionEvent.getX()) > Math
                 .abs(mFirstRecentMotionEvent.getY() - mLastMotionEvent.getY());
     }
 
     boolean isRight() {
         recalculateData();
+        if (mRecentMotionEvents.isEmpty()) {
+            return false;
+        }
+
         return mLastMotionEvent.getX() > mFirstRecentMotionEvent.getX();
     }
 
@@ -160,6 +166,10 @@ public class FalsingDataProvider {
 
     boolean isUp() {
         recalculateData();
+        if (mRecentMotionEvents.isEmpty()) {
+            return false;
+        }
+
         return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY();
     }
 
@@ -168,8 +178,13 @@ public class FalsingDataProvider {
             return;
         }
 
-        mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
-        mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+        if (mRecentMotionEvents.isEmpty()) {
+            mFirstRecentMotionEvent = null;
+            mLastMotionEvent = null;
+        } else {
+            mFirstRecentMotionEvent = mRecentMotionEvents.get(0);
+            mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1);
+        }
 
         calculateAngleInternal();
 
@@ -245,5 +260,7 @@ public class FalsingDataProvider {
         }
 
         mRecentMotionEvents.clear();
+
+        mDirty = true;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java
new file mode 100644 (file)
index 0000000..d011e48
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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 com.android.systemui.classifier.brightline;
+
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClassifierTest extends SysuiTestCase {
+
+    private FalsingDataProvider mDataProvider;
+    private List<MotionEvent> mMotionEvents = new ArrayList<>();
+    private float mOffsetX = 0;
+    private float mOffsetY = 0;
+
+    @Before
+    public void setup() {
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        displayMetrics.xdpi = 100;
+        displayMetrics.ydpi = 100;
+        displayMetrics.widthPixels = 1000;
+        displayMetrics.heightPixels = 1000;
+        mDataProvider = new FalsingDataProvider(displayMetrics);
+    }
+
+    @After
+    public void tearDown() {
+        resetDataProvider();
+    }
+
+    FalsingDataProvider getDataProvider() {
+        return mDataProvider;
+    }
+
+    void setOffsetX(float offsetX) {
+        mOffsetX = offsetX;
+    }
+
+    void setOffsetY(float offsetY) {
+        mOffsetY = offsetY;
+    }
+
+    void resetDataProvider() {
+        for (MotionEvent motionEvent : mMotionEvents) {
+            motionEvent.recycle();
+        }
+
+        mMotionEvents.clear();
+
+        mDataProvider.onSessionEnd();
+    }
+
+    MotionEvent appendDownEvent(float x, float y) {
+        return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y);
+    }
+
+    MotionEvent appendDownEvent(float x, float y, long eventTime) {
+        return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y, eventTime);
+    }
+
+    MotionEvent appendMoveEvent(float x, float y) {
+        return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y);
+    }
+
+    MotionEvent appendMoveEvent(float x, float y, long eventTime) {
+        return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y, eventTime);
+    }
+
+
+    MotionEvent appendUpEvent(float x, float y) {
+        return appendMotionEvent(MotionEvent.ACTION_UP, x, y);
+    }
+
+    MotionEvent appendUpEvent(float x, float y, long eventTime) {
+        return appendMotionEvent(MotionEvent.ACTION_UP, x, y, eventTime);
+    }
+
+    private MotionEvent appendMotionEvent(int actionType, float x, float y) {
+
+        long eventTime = mMotionEvents.isEmpty() ? 1 : mMotionEvents.get(
+                mMotionEvents.size() - 1).getEventTime() + 1;
+        return appendMotionEvent(actionType, x, y, eventTime);
+    }
+
+    private MotionEvent appendMotionEvent(int actionType, float x, float y, long eventTime) {
+        x += mOffsetX;
+        y += mOffsetY;
+
+        MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, actionType, x, y,
+                0);
+        mMotionEvents.add(motionEvent);
+
+        mDataProvider.onMotionEvent(motionEvent);
+
+        return motionEvent;
+    }
+}
index f37290a..b45d3f2 100644 (file)
@@ -28,10 +28,8 @@ import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -40,7 +38,7 @@ import org.mockito.MockitoAnnotations;
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class DiagonalClassifierTest extends SysuiTestCase {
+public class DiagonalClassifierTest extends ClassifierTest {
 
     // Next variable is not actually five, but is very close. 5 degrees is currently the value
     // used in the diagonal classifier, so we want slightly less than that to deal with
@@ -57,21 +55,24 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     private FalsingClassifier mClassifier;
 
     @Before
-    @Ignore("Memory Leak?")
     public void setup() {
+        super.setup();
         MockitoAnnotations.initMocks(this);
         mClassifier = new DiagonalClassifier(mDataProvider);
     }
 
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_UnknownAngle() {
         when(mDataProvider.getAngle()).thenReturn(Float.MAX_VALUE);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_VerticalSwipe() {
         when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS);
         assertThat(mClassifier.isFalseTouch(), is(false));
@@ -81,7 +82,6 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_MostlyVerticalSwipe() {
         when(mDataProvider.getAngle()).thenReturn(UP_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
         assertThat(mClassifier.isFalseTouch(), is(false));
@@ -97,7 +97,6 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_BarelyVerticalSwipe() {
         when(mDataProvider.getAngle()).thenReturn(
                 UP_IN_RADIANS - FORTY_FIVE_DEG_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
@@ -117,7 +116,6 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_HorizontalSwipe() {
         when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS);
         assertThat(mClassifier.isFalseTouch(), is(false));
@@ -127,7 +125,6 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_MostlyHorizontalSwipe() {
         when(mDataProvider.getAngle()).thenReturn(RIGHT_IN_RADIANS + 2 * FIVE_DEG_IN_RADIANS);
         assertThat(mClassifier.isFalseTouch(), is(false));
@@ -143,7 +140,6 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_BarelyHorizontalSwipe() {
         when(mDataProvider.getAngle()).thenReturn(
                 RIGHT_IN_RADIANS + FORTY_FIVE_DEG_IN_RADIANS - 2 * FIVE_DEG_IN_RADIANS);
@@ -163,7 +159,6 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_AffordanceSwipe() {
         when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE);
         when(mDataProvider.getAngle()).thenReturn(
@@ -182,7 +177,6 @@ public class DiagonalClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_DiagonalSwipe() {
         // Horizontal Swipes
         when(mDataProvider.isVertical()).thenReturn(false);
index ac30475..805bb91 100644 (file)
@@ -18,159 +18,92 @@ package com.android.systemui.classifier.brightline;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class DistanceClassifierTest extends SysuiTestCase {
+public class DistanceClassifierTest extends ClassifierTest {
 
-    @Mock
     private FalsingDataProvider mDataProvider;
     private FalsingClassifier mClassifier;
-    private List<MotionEvent> mMotionEvents = new ArrayList<>();
-
-    private static final float DPI = 100;
-    private static final int SCREEN_SIZE = (int) (DPI * 10);
 
     @Before
     public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mDataProvider.getHeightPixels()).thenReturn(SCREEN_SIZE);
-        when(mDataProvider.getWidthPixels()).thenReturn(SCREEN_SIZE);
-        when(mDataProvider.getXdpi()).thenReturn(DPI);
-        when(mDataProvider.getYdpi()).thenReturn(DPI);
+        super.setup();
+        mDataProvider = getDataProvider();
         mClassifier = new DistanceClassifier(mDataProvider);
     }
 
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_noPointer() {
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_fling() {
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 40, 0);
 
-        appendMotionEvent(motionEventA);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventB);
+        mClassifier.onTouchEvent(appendMoveEvent(1, 2));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventC);
+        mClassifier.onTouchEvent(appendUpEvent(1, 40));
         assertThat(mClassifier.isFalseTouch(), is(false));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_flingShort() {
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 10, 0);
-
-        appendMotionEvent(motionEventA);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventB);
+        mClassifier.onTouchEvent(appendMoveEvent(1, 2));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventC);
+        mClassifier.onTouchEvent(appendUpEvent(1, 10));
         assertThat(mClassifier.isFalseTouch(), is(true));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_flingSlowly() {
         // These events, in testing, result in a fling that falls just short of the threshold.
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 15, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, 16, 0);
-        MotionEvent motionEventD = MotionEvent.obtain(1, 300, MotionEvent.ACTION_MOVE, 1, 17, 0);
-        MotionEvent motionEventE = MotionEvent.obtain(1, 301, MotionEvent.ACTION_MOVE, 1, 18, 0);
-        MotionEvent motionEventF = MotionEvent.obtain(1, 500, MotionEvent.ACTION_UP, 1, 19, 0);
-
-        appendMotionEvent(motionEventA);
-        assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventB);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        appendMotionEvent(motionEventC);
-        appendMotionEvent(motionEventD);
-        appendMotionEvent(motionEventE);
-        appendMotionEvent(motionEventF);
+        mClassifier.onTouchEvent(appendMoveEvent(1, 15, 2));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
-        motionEventD.recycle();
-        motionEventE.recycle();
-        motionEventF.recycle();
+        mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3));
+        mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300));
+        mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301));
+        mClassifier.onTouchEvent(appendUpEvent(1, 19, 501));
+        assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_swipe() {
-        MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        MotionEvent motionEventB = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, DPI * 3, 0);
-        MotionEvent motionEventC = MotionEvent.obtain(1, 1000, MotionEvent.ACTION_UP, 1, DPI * 3,
-                0);
 
-        appendMotionEvent(motionEventA);
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(true));
 
-
-        appendMotionEvent(motionEventB);
-        appendMotionEvent(motionEventC);
+        mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3));
+        mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300));
         assertThat(mClassifier.isFalseTouch(), is(false));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
-    }
-
-    private void appendMotionEvent(MotionEvent motionEvent) {
-        if (mMotionEvents.isEmpty()) {
-            when(mDataProvider.getFirstRecentMotionEvent()).thenReturn(motionEvent);
-        }
-
-        mMotionEvents.add(motionEvent);
-        when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
-
-        when(mDataProvider.getLastMotionEvent()).thenReturn(motionEvent);
-
-        mClassifier.onTouchEvent(motionEvent);
     }
 }
index 86e0046..748c137 100644 (file)
@@ -22,14 +22,13 @@ import static org.junit.Assert.assertThat;
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -38,25 +37,32 @@ import java.util.List;
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class FalsingDataProviderTest extends SysuiTestCase {
+public class FalsingDataProviderTest extends ClassifierTest {
 
     private FalsingDataProvider mDataProvider;
 
     @Before
     public void setup() {
-        mDataProvider = new FalsingDataProvider(getContext());
+        super.setup();
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        displayMetrics.xdpi = 100;
+        displayMetrics.ydpi = 100;
+        displayMetrics.widthPixels = 1000;
+        displayMetrics.heightPixels = 1000;
+        mDataProvider = new FalsingDataProvider(displayMetrics);
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
+        mDataProvider.onSessionEnd();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_trackMotionEvents() {
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 3, 6, 5);
-
-        mDataProvider.onMotionEvent(motionEventA);
-        mDataProvider.onMotionEvent(motionEventB);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendDownEvent(2, 9));
+        mDataProvider.onMotionEvent(appendMoveEvent(4, 7));
+        mDataProvider.onMotionEvent(appendUpEvent(6, 5));
         List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
 
         assertThat(motionEventList.size(), is(3));
@@ -72,21 +78,12 @@ public class FalsingDataProviderTest extends SysuiTestCase {
         assertThat(motionEventList.get(0).getY(), is(9f));
         assertThat(motionEventList.get(1).getY(), is(7f));
         assertThat(motionEventList.get(2).getY(), is(5f));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_trackRecentMotionEvents() {
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9);
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 800, 4, 7);
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 1200, 6, 5);
-
-        mDataProvider.onMotionEvent(motionEventA);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendDownEvent(2, 9, 1));
+        mDataProvider.onMotionEvent(appendMoveEvent(4, 7, 800));
         List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents();
 
         assertThat(motionEventList.size(), is(2));
@@ -99,7 +96,7 @@ public class FalsingDataProviderTest extends SysuiTestCase {
         assertThat(motionEventList.get(0).getY(), is(9f));
         assertThat(motionEventList.get(1).getY(), is(7f));
 
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendUpEvent(6, 5, 1200));
 
         // Still two events, but event a is gone.
         assertThat(motionEventList.size(), is(2));
@@ -118,19 +115,14 @@ public class FalsingDataProviderTest extends SysuiTestCase {
         assertThat(firstRealMotionEvent.getEventTime(), is(1L));
         assertThat(firstRealMotionEvent.getX(), is(2f));
         assertThat(firstRealMotionEvent.getY(), is(9f));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_unpackMotionEvents() {
         // Batching only works for motion events of the same type.
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 1, 2, 9);
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7);
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 3, 6, 5);
+        MotionEvent motionEventA = appendMoveEvent(2, 9);
+        MotionEvent motionEventB = appendMoveEvent(4, 7);
+        MotionEvent motionEventC = appendMoveEvent(6, 5);
         motionEventA.addBatch(motionEventB);
         motionEventA.addBatch(motionEventC);
         // Note that calling addBatch changes properties on the original event, not just it's
@@ -152,151 +144,108 @@ public class FalsingDataProviderTest extends SysuiTestCase {
         assertThat(motionEventList.get(0).getY(), is(9f));
         assertThat(motionEventList.get(1).getY(), is(7f));
         assertThat(motionEventList.get(2).getY(), is(5f));
-
-        motionEventA.recycle();
-        motionEventB.recycle();
-        motionEventC.recycle();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_getAngle() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
         assertThat((double) mDataProvider.getAngle(), closeTo(Math.PI / 4, .001));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -1, -1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(-1, -1));
         assertThat((double) mDataProvider.getAngle(), closeTo(5 * Math.PI / 4, .001));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 0);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(2, 0));
         assertThat((double) mDataProvider.getAngle(), closeTo(0, .001));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_isHorizontal() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
         assertThat(mDataProvider.isHorizontal(), is(false));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(2, 1));
         assertThat(mDataProvider.isHorizontal(), is(true));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, -1));
         assertThat(mDataProvider.isHorizontal(), is(true));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_isVertical() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 0);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 0));
         assertThat(mDataProvider.isVertical(), is(false));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(0, 1));
         assertThat(mDataProvider.isVertical(), is(true));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, -10));
         assertThat(mDataProvider.isVertical(), is(true));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_isRight() {
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, 1));
         assertThat(mDataProvider.isRight(), is(true));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(0, 1));
         assertThat(mDataProvider.isRight(), is(false));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, -10));
         assertThat(mDataProvider.isRight(), is(false));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_isUp() {
         // Remember that our y axis is flipped.
 
-        MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0);
+        MotionEvent motionEventOrigin = appendDownEvent(0, 0);
 
-        MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, -1);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventA);
+        mDataProvider.onMotionEvent(appendMoveEvent(1, -1));
         assertThat(mDataProvider.isUp(), is(true));
-        motionEventA.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 0);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventB);
+        mDataProvider.onMotionEvent(appendMoveEvent(0, 0));
         assertThat(mDataProvider.isUp(), is(false));
-        motionEventB.recycle();
         mDataProvider.onSessionEnd();
 
-        MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, 10);
         mDataProvider.onMotionEvent(motionEventOrigin);
-        mDataProvider.onMotionEvent(motionEventC);
+        mDataProvider.onMotionEvent(appendMoveEvent(-3, 10));
         assertThat(mDataProvider.isUp(), is(false));
-        motionEventC.recycle();
         mDataProvider.onSessionEnd();
     }
-
-    private MotionEvent obtainMotionEvent(int action, long eventTimeMs, float x, float y) {
-        return MotionEvent.obtain(1, eventTimeMs, action, x, y, 0);
-    }
 }
index f87e850..341b74b 100644 (file)
@@ -25,45 +25,41 @@ import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class PointerCountClassifierTest extends SysuiTestCase {
+public class PointerCountClassifierTest extends ClassifierTest {
 
-    @Mock
-    private FalsingDataProvider mDataProvider;
     private FalsingClassifier mClassifier;
 
     @Before
     public void setup() {
-        mClassifier = new PointerCountClassifier(mDataProvider);
+        super.setup();
+        mClassifier = new PointerCountClassifier(getDataProvider());
+    }
+
+    @After
+    public void tearDown() {
+        super.tearDown();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_noPointer() {
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_singlePointer() {
-        MotionEvent motionEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0);
-        mClassifier.onTouchEvent(motionEvent);
-        motionEvent.recycle();
+        mClassifier.onTouchEvent(appendDownEvent(1, 1));
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_multiPointer() {
         MotionEvent.PointerProperties[] pointerProperties =
                 MotionEvent.PointerProperties.createArray(2);
index f42a8d1..a6cabbf 100644 (file)
@@ -31,10 +31,8 @@ import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
+import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -46,7 +44,7 @@ import java.lang.reflect.Field;
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class ProximityClassifierTest extends SysuiTestCase {
+public class ProximityClassifierTest extends ClassifierTest {
 
     private static final long NS_PER_MS = 1000000;
 
@@ -58,14 +56,19 @@ public class ProximityClassifierTest extends SysuiTestCase {
 
     @Before
     public void setup() {
+        super.setup();
         MockitoAnnotations.initMocks(this);
         when(mDataProvider.getInteractionType()).thenReturn(GENERIC);
         when(mDistanceClassifier.isLongSwipe()).thenReturn(false);
         mClassifier = new ProximityClassifier(mDistanceClassifier, mDataProvider);
     }
 
+    @After
+    public void tearDown() {
+        super.tearDown();
+    }
+
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_uncovered() {
         touchDown();
         touchUp(10);
@@ -73,7 +76,6 @@ public class ProximityClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_mostlyUncovered() {
         touchDown();
         mClassifier.onSensorEvent(createSensorEvent(true, 1));
@@ -83,7 +85,6 @@ public class ProximityClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_quickSettings() {
         touchDown();
         when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
@@ -94,7 +95,6 @@ public class ProximityClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_covered() {
         touchDown();
         mClassifier.onSensorEvent(createSensorEvent(true, 1));
@@ -104,7 +104,6 @@ public class ProximityClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_mostlyCovered() {
         touchDown();
         mClassifier.onSensorEvent(createSensorEvent(true, 1));
@@ -116,7 +115,6 @@ public class ProximityClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_coveredWithLongSwipe() {
         touchDown();
         mClassifier.onSensorEvent(createSensorEvent(true, 1));
index fb6d410..0355dc3 100644 (file)
@@ -34,10 +34,7 @@ import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -46,21 +43,20 @@ import org.mockito.MockitoAnnotations;
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class TypeClassifierTest extends SysuiTestCase {
+public class TypeClassifierTest extends ClassifierTest {
 
     @Mock
     private FalsingDataProvider mDataProvider;
-
     private FalsingClassifier mClassifier;
 
     @Before
     public void setup() {
+        super.setup();
         MockitoAnnotations.initMocks(this);
         mClassifier = new TypeClassifier(mDataProvider);
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_QuickSettings() {
         when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
         when(mDataProvider.isVertical()).thenReturn(true);
@@ -74,7 +70,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_QuickSettings() {
         when(mDataProvider.getInteractionType()).thenReturn(QUICK_SETTINGS);
 
@@ -88,7 +83,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_PulseExpand() {
         when(mDataProvider.getInteractionType()).thenReturn(PULSE_EXPAND);
         when(mDataProvider.isVertical()).thenReturn(true);
@@ -102,7 +96,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_PulseExpand() {
         when(mDataProvider.getInteractionType()).thenReturn(PULSE_EXPAND);
 
@@ -116,7 +109,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_NotificationDragDown() {
         when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DRAG_DOWN);
         when(mDataProvider.isVertical()).thenReturn(true);
@@ -130,7 +122,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_NotificationDragDown() {
         when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DRAG_DOWN);
 
@@ -144,7 +135,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_NotificationDismiss() {
         when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DISMISS);
         when(mDataProvider.isVertical()).thenReturn(false);
@@ -167,7 +157,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_NotificationDismiss() {
         when(mDataProvider.getInteractionType()).thenReturn(NOTIFICATION_DISMISS);
         when(mDataProvider.isVertical()).thenReturn(true);
@@ -191,7 +180,6 @@ public class TypeClassifierTest extends SysuiTestCase {
 
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_Unlock() {
         when(mDataProvider.getInteractionType()).thenReturn(UNLOCK);
         when(mDataProvider.isVertical()).thenReturn(true);
@@ -206,7 +194,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_Unlock() {
         when(mDataProvider.getInteractionType()).thenReturn(UNLOCK);
 
@@ -224,7 +211,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_BouncerUnlock() {
         when(mDataProvider.getInteractionType()).thenReturn(BOUNCER_UNLOCK);
         when(mDataProvider.isVertical()).thenReturn(true);
@@ -239,7 +225,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_BouncerUnlock() {
         when(mDataProvider.getInteractionType()).thenReturn(BOUNCER_UNLOCK);
 
@@ -257,7 +242,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_LeftAffordance() {
         when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE);
         when(mDataProvider.isUp()).thenReturn(true);
@@ -272,7 +256,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_LeftAffordance() {
         when(mDataProvider.getInteractionType()).thenReturn(LEFT_AFFORDANCE);
 
@@ -290,7 +273,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_RightAffordance() {
         when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE);
         when(mDataProvider.isUp()).thenReturn(true);
@@ -305,7 +287,6 @@ public class TypeClassifierTest extends SysuiTestCase {
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFalse_RightAffordance() {
         when(mDataProvider.getInteractionType()).thenReturn(RIGHT_AFFORDANCE);
 
index 8ab8202..25a1a75 100644 (file)
@@ -18,474 +18,394 @@ package com.android.systemui.classifier.brightline;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.SysuiTestCase;
-
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Random;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public class ZigZagClassifierTest extends SysuiTestCase {
-
-    private static final long NS_PER_MS = 1000000;
+public class ZigZagClassifierTest extends ClassifierTest {
 
-    @Mock
-    private FalsingDataProvider mDataProvider;
     private FalsingClassifier mClassifier;
-    private List<MotionEvent> mMotionEvents = new ArrayList<>();
-    private float mOffsetX = 0;
-    private float mOffsetY = 0;
-    private float mDx;
-    private float mDy;
 
     @Before
     public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mDataProvider.getXdpi()).thenReturn(100f);
-        when(mDataProvider.getYdpi()).thenReturn(100f);
-        when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents);
-        mClassifier = new ZigZagClassifier(mDataProvider);
-
-
-        // Calculate the response to these calls on the fly, otherwise Mockito gets bogged down
-        // everytime we call appendMotionEvent.
-        when(mDataProvider.getFirstRecentMotionEvent()).thenAnswer(
-                (Answer<MotionEvent>) invocation -> mMotionEvents.get(0));
-        when(mDataProvider.getLastMotionEvent()).thenAnswer(
-                (Answer<MotionEvent>) invocation -> mMotionEvents.get(mMotionEvents.size() - 1));
-        when(mDataProvider.isHorizontal()).thenAnswer(
-                (Answer<Boolean>) invocation -> Math.abs(mDy) < Math.abs(mDx));
-        when(mDataProvider.isVertical()).thenAnswer(
-                (Answer<Boolean>) invocation -> Math.abs(mDy) > Math.abs(mDx));
-        when(mDataProvider.isRight()).thenAnswer((Answer<Boolean>) invocation -> mDx > 0);
-        when(mDataProvider.isUp()).thenAnswer((Answer<Boolean>) invocation -> mDy < 0);
+        super.setup();
+        mClassifier = new ZigZagClassifier(getDataProvider());
     }
 
     @After
     public void tearDown() {
-        clearMotionEvents();
+        super.tearDown();
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_fewTouchesVertical() {
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(0, 0);
+        appendMoveEvent(0, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(0, 100);
+        appendMoveEvent(0, 100);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_vertical() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(0, 100);
-        appendMotionEvent(0, 200);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(0, 100);
+        appendMoveEvent(0, 200);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_fewTouchesHorizontal() {
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(0, 0);
+        appendMoveEvent(0, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
-        appendMotionEvent(100, 0);
+        appendMoveEvent(100, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_horizontal() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 0);
-        appendMotionEvent(200, 0);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 0);
+        appendMoveEvent(200, 0);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_minimumTouchesVertical() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(0, 100);
-        appendMotionEvent(0, 1);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(0, 100);
+        appendMoveEvent(0, 1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_minimumTouchesHorizontal() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 0);
-        appendMotionEvent(1, 0);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 0);
+        appendMoveEvent(1, 0);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_fortyFiveDegreesStraight() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(10, 10);
-        appendMotionEvent(20, 20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(10, 10);
+        appendMoveEvent(20, 20);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_horizontalZigZagVerticalStraight() {
         // This test looks just like testFail_horizontalZigZagVerticalStraight but with
         // a longer y range, making it look straighter.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(5, 100);
-        appendMotionEvent(-5, 200);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(5, 100);
+        appendMoveEvent(-5, 200);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testPass_horizontalStraightVerticalZigZag() {
         // This test looks just like testFail_horizontalStraightVerticalZigZag but with
         // a longer x range, making it look straighter.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 5);
-        appendMotionEvent(200, -5);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 5);
+        appendMoveEvent(200, -5);
         assertThat(mClassifier.isFalseTouch(), is(false));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_horizontalZigZagVerticalStraight() {
         // This test looks just like testPass_horizontalZigZagVerticalStraight but with
         // a shorter y range, making it look more crooked.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(5, 10);
-        appendMotionEvent(-5, 20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(5, 10);
+        appendMoveEvent(-5, 20);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void testFail_horizontalStraightVerticalZigZag() {
         // This test looks just like testPass_horizontalStraightVerticalZigZag but with
         // a shorter x range, making it look more crooked.
-        appendMotionEvent(0, 0);
-        appendMotionEvent(10, 5);
-        appendMotionEvent(20, -5);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(10, 5);
+        appendMoveEvent(20, -5);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between0And45() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 5);
-        appendMotionEvent(200, 10);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 5);
+        appendMoveEvent(200, 10);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 0);
-        appendMotionEvent(200, 10);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 0);
+        appendMoveEvent(200, 10);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, -10);
-        appendMotionEvent(200, 10);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, -10);
+        appendMoveEvent(200, 10);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, -10);
-        appendMotionEvent(200, 50);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, -10);
+        appendMoveEvent(200, 50);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between45And90() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(10, 50);
-        appendMotionEvent(8, 100);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(10, 50);
+        appendMoveEvent(8, 100);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(1, 800);
-        appendMotionEvent(2, 900);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(1, 800);
+        appendMoveEvent(2, 900);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-10, 600);
-        appendMotionEvent(30, 700);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-10, 600);
+        appendMoveEvent(30, 700);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(40, 100);
-        appendMotionEvent(0, 101);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(40, 100);
+        appendMoveEvent(0, 101);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between90And135() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-10, 50);
-        appendMotionEvent(-24, 100);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-10, 50);
+        appendMoveEvent(-24, 100);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, 800);
-        appendMotionEvent(-20, 900);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, 800);
+        appendMoveEvent(-20, 900);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(30, 600);
-        appendMotionEvent(-10, 700);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(30, 600);
+        appendMoveEvent(-10, 700);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, 100);
-        appendMotionEvent(-10, 101);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, 100);
+        appendMoveEvent(-10, 101);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between135And180() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-120, 10);
-        appendMotionEvent(-200, 20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-120, 10);
+        appendMoveEvent(-200, 20);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, 8);
-        appendMotionEvent(-40, 2);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, 8);
+        appendMoveEvent(-40, 2);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-500, -2);
-        appendMotionEvent(-600, 70);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-500, -2);
+        appendMoveEvent(-600, 70);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, 100);
-        appendMotionEvent(-100, 1);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, 100);
+        appendMoveEvent(-100, 1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between180And225() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-120, -10);
-        appendMotionEvent(-200, -20);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-120, -10);
+        appendMoveEvent(-200, -20);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, -8);
-        appendMotionEvent(-40, -2);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, -8);
+        appendMoveEvent(-40, -2);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-500, 2);
-        appendMotionEvent(-600, -70);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-500, 2);
+        appendMoveEvent(-600, -70);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, -100);
-        appendMotionEvent(-100, -1);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, -100);
+        appendMoveEvent(-100, -1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between225And270() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-12, -20);
-        appendMotionEvent(-20, -40);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-12, -20);
+        appendMoveEvent(-20, -40);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-20, -130);
-        appendMotionEvent(-40, -260);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-20, -130);
+        appendMoveEvent(-40, -260);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(1, -100);
-        appendMotionEvent(-6, -200);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(1, -100);
+        appendMoveEvent(-6, -200);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-80, -100);
-        appendMotionEvent(-10, -110);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-80, -100);
+        appendMoveEvent(-10, -110);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between270And315() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(12, -20);
-        appendMotionEvent(20, -40);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(12, -20);
+        appendMoveEvent(20, -40);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(20, -130);
-        appendMotionEvent(40, -260);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(20, -130);
+        appendMoveEvent(40, -260);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(-1, -100);
-        appendMotionEvent(6, -200);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(-1, -100);
+        appendMoveEvent(6, -200);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(80, -100);
-        appendMotionEvent(10, -110);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(80, -100);
+        appendMoveEvent(10, -110);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_between315And360() {
-        appendMotionEvent(0, 0);
-        appendMotionEvent(120, -20);
-        appendMotionEvent(200, -40);
+        appendMoveEvent(0, 0);
+        appendMoveEvent(120, -20);
+        appendMoveEvent(200, -40);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(200, -13);
-        appendMotionEvent(400, -30);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(200, -13);
+        appendMoveEvent(400, -30);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(100, 10);
-        appendMotionEvent(600, -20);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(100, 10);
+        appendMoveEvent(600, -20);
         assertThat(mClassifier.isFalseTouch(), is(false));
 
-        mMotionEvents.clear();
-        appendMotionEvent(0, 0);
-        appendMotionEvent(80, -100);
-        appendMotionEvent(100, -1);
+        resetDataProvider();
+        appendMoveEvent(0, 0);
+        appendMoveEvent(80, -100);
+        appendMoveEvent(100, -1);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
     @Test
-    @Ignore("Memory Leak?")
     public void test_randomOrigins() {
         // The purpose of this test is to try all the other tests from different starting points.
         // We use a pre-determined seed to make this test repeatable.
         Random rand = new Random(23);
         for (int i = 0; i < 100; i++) {
-            mOffsetX = rand.nextInt(2000) - 1000;
-            mOffsetY = rand.nextInt(2000) - 1000;
+            setOffsetX(rand.nextInt(2000) - 1000);
+            setOffsetY(rand.nextInt(2000) - 1000);
             try {
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_fewTouchesVertical();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_vertical();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_horizontalStraightVerticalZigZag();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_horizontalZigZagVerticalStraight();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_minimumTouchesHorizontal();
-                clearMotionEvents();
+                resetDataProvider();
                 testFail_minimumTouchesVertical();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_fewTouchesHorizontal();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_fortyFiveDegreesStraight();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_horizontal();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_horizontalStraightVerticalZigZag();
-                clearMotionEvents();
+                resetDataProvider();
                 testPass_horizontalZigZagVerticalStraight();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between0And45();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between45And90();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between90And135();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between135And180();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between180And225();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between225And270();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between270And315();
-                clearMotionEvents();
+                resetDataProvider();
                 test_between315And360();
             } catch (AssertionError e) {
                 throw new AssertionError("Random origin failure in iteration " + i, e);
             }
         }
     }
-
-    private void clearMotionEvents() {
-        for (MotionEvent motionEvent : mMotionEvents) {
-            motionEvent.recycle();
-        }
-        mMotionEvents.clear();
-    }
-
-    private void appendMotionEvent(float x, float y) {
-        x += mOffsetX;
-        y += mOffsetY;
-
-        long eventTime = mMotionEvents.size() + 1;
-        MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, MotionEvent.ACTION_DOWN, x, y,
-                0);
-        mMotionEvents.add(motionEvent);
-
-        mDx = mDataProvider.getFirstRecentMotionEvent().getX()
-                - mDataProvider.getLastMotionEvent().getX();
-        mDy = mDataProvider.getFirstRecentMotionEvent().getY()
-                - mDataProvider.getLastMotionEvent().getY();
-
-        mClassifier.onTouchEvent(motionEvent);
-    }
 }