blob: 9d09a7e6f01f9d666f3b73106262e5cdff1b3130 [file] [log] [blame]
/*
* 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 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.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;
@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);
}
@After
public void tearDown() {
clearMotionEvents();
}
@Test
public void testPass_fewTouchesVertical() {
assertThat(mClassifier.isFalseTouch(), is(false));
appendMotionEvent(0, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
appendMotionEvent(0, 100);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testPass_vertical() {
appendMotionEvent(0, 0);
appendMotionEvent(0, 100);
appendMotionEvent(0, 200);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testPass_fewTouchesHorizontal() {
assertThat(mClassifier.isFalseTouch(), is(false));
appendMotionEvent(0, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
appendMotionEvent(100, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testPass_horizontal() {
appendMotionEvent(0, 0);
appendMotionEvent(100, 0);
appendMotionEvent(200, 0);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
public void testFail_minimumTouchesVertical() {
appendMotionEvent(0, 0);
appendMotionEvent(0, 100);
appendMotionEvent(0, 1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void testFail_minimumTouchesHorizontal() {
appendMotionEvent(0, 0);
appendMotionEvent(100, 0);
appendMotionEvent(1, 0);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void testPass_fortyFiveDegreesStraight() {
appendMotionEvent(0, 0);
appendMotionEvent(10, 10);
appendMotionEvent(20, 20);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
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);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
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);
assertThat(mClassifier.isFalseTouch(), is(false));
}
@Test
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);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
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);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between0And45() {
appendMotionEvent(0, 0);
appendMotionEvent(100, 5);
appendMotionEvent(200, 10);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(100, 0);
appendMotionEvent(200, 10);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(100, -10);
appendMotionEvent(200, 10);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(100, -10);
appendMotionEvent(200, 50);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between45And90() {
appendMotionEvent(0, 0);
appendMotionEvent(10, 50);
appendMotionEvent(8, 100);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(1, 800);
appendMotionEvent(2, 900);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-10, 600);
appendMotionEvent(30, 700);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(40, 100);
appendMotionEvent(0, 101);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between90And135() {
appendMotionEvent(0, 0);
appendMotionEvent(-10, 50);
appendMotionEvent(-24, 100);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-20, 800);
appendMotionEvent(-20, 900);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(30, 600);
appendMotionEvent(-10, 700);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-80, 100);
appendMotionEvent(-10, 101);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between135And180() {
appendMotionEvent(0, 0);
appendMotionEvent(-120, 10);
appendMotionEvent(-200, 20);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-20, 8);
appendMotionEvent(-40, 2);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-500, -2);
appendMotionEvent(-600, 70);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-80, 100);
appendMotionEvent(-100, 1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between180And225() {
appendMotionEvent(0, 0);
appendMotionEvent(-120, -10);
appendMotionEvent(-200, -20);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-20, -8);
appendMotionEvent(-40, -2);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-500, 2);
appendMotionEvent(-600, -70);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-80, -100);
appendMotionEvent(-100, -1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between225And270() {
appendMotionEvent(0, 0);
appendMotionEvent(-12, -20);
appendMotionEvent(-20, -40);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-20, -130);
appendMotionEvent(-40, -260);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(1, -100);
appendMotionEvent(-6, -200);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-80, -100);
appendMotionEvent(-10, -110);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between270And315() {
appendMotionEvent(0, 0);
appendMotionEvent(12, -20);
appendMotionEvent(20, -40);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(20, -130);
appendMotionEvent(40, -260);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(-1, -100);
appendMotionEvent(6, -200);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(80, -100);
appendMotionEvent(10, -110);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
public void test_between315And360() {
appendMotionEvent(0, 0);
appendMotionEvent(120, -20);
appendMotionEvent(200, -40);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(200, -13);
appendMotionEvent(400, -30);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(100, 10);
appendMotionEvent(600, -20);
assertThat(mClassifier.isFalseTouch(), is(false));
mMotionEvents.clear();
appendMotionEvent(0, 0);
appendMotionEvent(80, -100);
appendMotionEvent(100, -1);
assertThat(mClassifier.isFalseTouch(), is(true));
}
@Test
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;
try {
clearMotionEvents();
testPass_fewTouchesVertical();
clearMotionEvents();
testPass_vertical();
clearMotionEvents();
testFail_horizontalStraightVerticalZigZag();
clearMotionEvents();
testFail_horizontalZigZagVerticalStraight();
clearMotionEvents();
testFail_minimumTouchesHorizontal();
clearMotionEvents();
testFail_minimumTouchesVertical();
clearMotionEvents();
testPass_fewTouchesHorizontal();
clearMotionEvents();
testPass_fortyFiveDegreesStraight();
clearMotionEvents();
testPass_horizontal();
clearMotionEvents();
testPass_horizontalStraightVerticalZigZag();
clearMotionEvents();
testPass_horizontalZigZagVerticalStraight();
clearMotionEvents();
test_between0And45();
clearMotionEvents();
test_between45And90();
clearMotionEvents();
test_between90And135();
clearMotionEvents();
test_between135And180();
clearMotionEvents();
test_between180And225();
clearMotionEvents();
test_between225And270();
clearMotionEvents();
test_between270And315();
clearMotionEvents();
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);
}
}