blob: 8c9fec67265041ed51c1fe75d9d6e1c665b257d2 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/events/ozone/evdev/touch_filter/false_touch_finder.h"
#include <stddef.h>
#include <algorithm>
#include <memory>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event_switches.h"
#include "ui/events/ozone/evdev/touch_evdev_types.h"
#include "ui/gfx/geometry/point_f.h"
namespace ui {
class FalseTouchFinderTest : public testing::Test {
public:
struct TouchEntry {
int time_ms;
size_t slot;
bool touching;
gfx::PointF location;
float pressure;
bool expect_noise;
bool expect_delay;
};
FalseTouchFinderTest() {}
~FalseTouchFinderTest() override {}
bool FilterAndCheck(const TouchEntry entries[], size_t count) {
std::vector<InProgressTouchEvdev> touches;
size_t start_index = 0u;
std::bitset<kNumTouchEvdevSlots> was_touching;
for (size_t i = 0; i < count; ++i) {
const TouchEntry& entry = entries[i];
InProgressTouchEvdev touch;
touch.x = entry.location.x();
touch.y = entry.location.y();
touch.tracking_id = entry.slot;
touch.slot = entry.slot;
touch.pressure = entry.pressure;
touch.was_touching = was_touching.test(touch.slot);
touch.touching = entry.touching;
touches.push_back(touch);
if (i == count - 1 || entry.time_ms != entries[i + 1].time_ms) {
false_touch_finder_->HandleTouches(touches, base::TimeTicks() +
base::TimeDelta::FromMilliseconds(entry.time_ms));
for (size_t j = 0; j < touches.size(); ++j) {
bool expect_noise = entries[j + start_index].expect_noise;
bool expect_delay = entries[j + start_index].expect_delay;
size_t slot = touches[j].slot;
if (false_touch_finder_->SlotHasNoise(slot) != expect_noise
|| false_touch_finder_->SlotShouldDelay(slot) != expect_delay) {
LOG(ERROR) << base::StringPrintf(
"Incorrect filtering at %dms for slot %zu", entry.time_ms,
slot);
return false;
}
}
start_index = i + 1;
touches.clear();
}
was_touching.set(entry.slot, entry.touching);
}
return true;
}
gfx::Size touchscreen_size = gfx::Size(4000,4000);
private:
// testing::Test:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kExtraTouchNoiseFiltering);
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEdgeTouchFiltering);
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kLowPressureTouchFiltering);
false_touch_finder_ = FalseTouchFinder::Create(touchscreen_size);
}
std::unique_ptr<FalseTouchFinder> false_touch_finder_;
DISALLOW_COPY_AND_ASSIGN(FalseTouchFinderTest);
};
// Test that taps which are far apart in quick succession are considered noise.
TEST_F(FalseTouchFinderTest, FarApartTaps) {
const TouchEntry kTestData[] = {
{10, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{20, 1, true, gfx::PointF(10, 11), 0.35, false, false},
{30, 1, true, gfx::PointF(10, 12), 0.35, false, false},
{30, 2, true, gfx::PointF(2500, 1000), 0.35, true, false},
{40, 1, true, gfx::PointF(10, 13), 0.35, true, false},
{40, 2, true, gfx::PointF(2500, 1001), 0.35, true, false},
{50, 1, true, gfx::PointF(10, 14), 0.35, true, false},
{50, 2, false, gfx::PointF(2500, 1002), 0.35, true, false},
{60, 1, false, gfx::PointF(10, 15), 0.35, true, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that taps which are far apart but do not occur in quick succession are
// not considered noise.
TEST_F(FalseTouchFinderTest, FarApartTapsSlow) {
const TouchEntry kTestData[] = {
{1000, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{1500, 1, true, gfx::PointF(10, 11), 0.35, false, false},
{2000, 1, true, gfx::PointF(10, 12), 0.35, false, false},
{2500, 1, true, gfx::PointF(10, 13), 0.35, false, false},
{2500, 2, true, gfx::PointF(2500, 1000), 0.35, false, false},
{3000, 1, true, gfx::PointF(10, 14), 0.35, false, false},
{3000, 2, false, gfx::PointF(2500, 1001), 0.35, false, false},
{3500, 1, false, gfx::PointF(10, 15), 0.35, false, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that touches which are horizontally aligned are considered noise.
TEST_F(FalseTouchFinderTest, HorizontallyAligned) {
const TouchEntry kTestData[] = {
{10, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{20, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{20, 2, true, gfx::PointF(10, 25), 0.35, true, false},
{30, 1, false, gfx::PointF(10, 10), 0.35, false, false},
{30, 2, true, gfx::PointF(10, 25), 0.35, true, false},
{40, 2, false, gfx::PointF(10, 25), 0.35, true, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that touches in the same position are considered noise.
TEST_F(FalseTouchFinderTest, SamePosition) {
const TouchEntry kTestData[] = {
{1000, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{1500, 1, false, gfx::PointF(10, 10), 0.35, false, false},
{2000, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{2500, 1, false, gfx::PointF(10, 10), 0.35, false, false},
{3000, 1, true, gfx::PointF(50, 50), 0.35, false, false},
{3500, 1, true, gfx::PointF(50, 51), 0.35, false, false},
{3500, 2, true, gfx::PointF(10, 10), 0.35, true, false},
{4000, 1, false, gfx::PointF(50, 52), 0.35, false, false},
{4000, 2, false, gfx::PointF(10, 10), 0.35, true, false},
{4500, 1, true, gfx::PointF(10, 10), 0.35, true, false},
{5000, 1, false, gfx::PointF(10, 10), 0.35, true, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that a multi-second touch is considered noise.
TEST_F(FalseTouchFinderTest, MultiSecondTouch) {
const TouchEntry kTestData[] = {
{1000, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{2000, 1, true, gfx::PointF(10, 11), 0.35, false, false},
{3000, 1, true, gfx::PointF(10, 10), 0.35, false, false},
{4000, 1, true, gfx::PointF(10, 11), 0.35, true, false},
{5000, 1, true, gfx::PointF(10, 10), 0.35, true, false},
{6000, 1, true, gfx::PointF(10, 11), 0.35, true, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that a touch on the edge which never leaves is delayed and never
// released.
TEST_F(FalseTouchFinderTest, EdgeTap) {
int ts_width = touchscreen_size.width();
int ts_height = touchscreen_size.height();
const TouchEntry kTestData[] = {
{10, 1, true, gfx::PointF(0, 100), 0.35, false, true},
{20, 1, true, gfx::PointF(0, 100), 0.35, false, true},
{30, 1, false, gfx::PointF(0, 100), 0.35, false, true},
{40, 2, true, gfx::PointF(ts_width - 1, 100), 0.35, false, true},
{50, 2, true, gfx::PointF(ts_width - 1, 100), 0.35, false, true},
{60, 2, false, gfx::PointF(ts_width - 1, 100), 0.35, false, true},
{70, 3, true, gfx::PointF(100, 0), 0.35, false, true},
{80, 3, true, gfx::PointF(100, 0), 0.35, false, true},
{90, 3, false, gfx::PointF(100, 0), 0.35, false, true},
{100, 4, true, gfx::PointF(100, ts_height - 1), 0.35, false, true},
{110, 4, true, gfx::PointF(100, ts_height - 1), 0.35, false, true},
{120, 4, false, gfx::PointF(100, ts_height - 1), 0.35, false, true}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that a touch on the edge which starts at an edge is delayed but released
// as soon as it moves.
TEST_F(FalseTouchFinderTest, MoveFromEdge) {
const TouchEntry kTestData[] = {
{10, 1, true, gfx::PointF(0, 100), 0.35, false, true},
{20, 1, true, gfx::PointF(0, 100), 0.35, false, true},
{30, 1, true, gfx::PointF(1, 100), 0.35, false, false},
{40, 1, false, gfx::PointF(1, 100), 0.35, false, false},
{50, 1, true, gfx::PointF(0, 100), 0.35, false, true},
{60, 1, true, gfx::PointF(0, 100), 0.35, false, true},
{70, 1, true, gfx::PointF(0, 101), 0.35, false, false},
{80, 1, false, gfx::PointF(0, 101), 0.35, false, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that a touch on the edge which starts away from the edge is not
// cancelled when it moves to the edge.
TEST_F(FalseTouchFinderTest, MoveToEdge) {
const TouchEntry kTestData[] = {
{10, 1, true, gfx::PointF(100, 100), 0.35, false, false},
{20, 1, true, gfx::PointF(100, 100), 0.35, false, false},
{30, 1, true, gfx::PointF(0, 100), 0.35, false, false},
{40, 1, false, gfx::PointF(0, 100), 0.35, false, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that a pencil with a wide tip should be filtered out. Based on real
// logs.
TEST_F(FalseTouchFinderTest, FatPencilPressure) {
const TouchEntry kTestData[] = {
{10, 1, true, gfx::PointF(10, 10), 0.180392, false, true},
{20, 1, true, gfx::PointF(10, 10), 0.176471, false, true},
{30, 1, true, gfx::PointF(10, 10), 0.180392, false, true},
{40, 1, true, gfx::PointF(10, 10), 0.164706, false, true},
{50, 1, true, gfx::PointF(10, 10), 0.101961, false, true}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
// Test that a pinky finger lightly pressed is not filtered out. Based on real
// logs.
TEST_F(FalseTouchFinderTest, LightPinkyPressure) {
const TouchEntry kTestData[] = {
{10, 1, true, gfx::PointF(10, 10), 0.243137, false, false},
{20, 1, true, gfx::PointF(10, 10), 0.231373, false, false},
{30, 1, true, gfx::PointF(10, 10), 0.215686, false, false},
{40, 1, true, gfx::PointF(10, 10), 0.211765, false, false},
{50, 1, true, gfx::PointF(10, 10), 0.203922, false, false}};
EXPECT_TRUE(FilterAndCheck(kTestData, base::size(kTestData)));
}
} // namespace ui