blob: 3b5fad899f1f9fff71a1050f48868b71b2af3c32 [file] [log] [blame]
// Copyright 2014 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 "content/browser/renderer_host/input/stylus_text_selector.h"
#include <memory>
#include <string>
#include <vector>
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event_constants.h"
#include "ui/events/gesture_detection/motion_event.h"
#include "ui/events/test/motion_event_test_utils.h"
#include "ui/gfx/geometry/rect_f.h"
using ui::MotionEvent;
using ui::test::MockMotionEvent;
namespace content {
class StylusTextSelectorTest : public testing::Test,
public StylusTextSelectorClient {
public:
StylusTextSelectorTest() {}
~StylusTextSelectorTest() override {}
// Test implementation.
void SetUp() override {
selector_.reset(new StylusTextSelector(this));
event_log_.clear();
}
void TearDown() override {
selector_.reset();
event_log_.clear();
}
// StylusTextSelectorClient implementation.
void OnStylusSelectBegin(float x0, float y0, float x1, float y1) override {
std::stringstream ss;
ss << "Begin(" << x0 << ", " << y0 << ", " << x1 << ", " << y1 << ")";
event_log_.push_back(ss.str());
}
void OnStylusSelectUpdate(float x, float y) override {
std::stringstream ss;
ss << "Update(" << x << ", " << y << ")";
event_log_.push_back(ss.str());
}
void OnStylusSelectEnd(float x, float y) override {
event_log_.push_back("End");
}
void OnStylusSelectTap(base::TimeTicks time, float x, float y) override {
event_log_.push_back("Tap");
}
protected:
std::unique_ptr<StylusTextSelector> selector_;
std::vector<std::string> event_log_;
};
TEST_F(StylusTextSelectorTest, ShouldStartTextSelection) {
base::TimeTicks event_time = base::TimeTicks::Now();
{ // Touched with a finger.
MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
e.SetToolType(0, MotionEvent::ToolType::FINGER);
e.set_button_state(0);
EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
}
{ // Touched with a stylus, but no button pressed.
MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
e.SetToolType(0, MotionEvent::ToolType::STYLUS);
e.set_button_state(0);
EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
}
{ // Touched with a stylus, with first button (BUTTON_SECONDARY) pressed.
// For Android version < M, this stylus state is BUTTON_SECONDARY.
MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
e.SetToolType(0, MotionEvent::ToolType::STYLUS);
e.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->ShouldStartTextSelection(e));
}
{ // Touched with a stylus, with first button (BUTTON_STYLUS_PRIMARY)
// pressed. From Android M, this stylus state has been changed to
// BUTTON_STYLUS_PRIMARY.
MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
e.SetToolType(0, MotionEvent::ToolType::STYLUS);
e.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->ShouldStartTextSelection(e));
}
{ // Touched with a stylus, with two buttons pressed.
// For Android version < M, these states are BUTTON_SECONDARY,
// BUTTON_TERTIARY.
MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
e.SetToolType(0, MotionEvent::ToolType::STYLUS);
e.set_button_state(MotionEvent::BUTTON_SECONDARY |
MotionEvent::BUTTON_TERTIARY);
EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
}
{ // Touched with a stylus, with two buttons pressed.
// From Android M, these state are BUTTON_STYLUS_PRIMARY,
// BUTTON_STYLUS_SECONDARY.
MockMotionEvent e(MotionEvent::Action::DOWN, event_time, 50.0f, 50.0f);
e.SetToolType(0, MotionEvent::ToolType::STYLUS);
e.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY |
MotionEvent::BUTTON_STYLUS_SECONDARY);
EXPECT_FALSE(selector_->ShouldStartTextSelection(e));
}
}
TEST_F(StylusTextSelectorTest, FingerTouch) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float x = 50.0f;
const float y = 30.0f;
// 1. Touched with a finger: ignored
MockMotionEvent finger(MotionEvent::Action::DOWN, event_time, x, y);
finger.SetToolType(0, MotionEvent::ToolType::FINGER);
EXPECT_FALSE(selector_->OnTouchEvent(finger));
// We do not consume finger events.
EXPECT_TRUE(event_log_.empty());
}
// The following Tests cover BUTTON_SECONDARY case, which is the stylus button
// pressed state for Android version < M.
TEST_F(StylusTextSelectorTest, PenDraggingButtonSecondary) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float x1 = 50.0f;
const float y1 = 30.0f;
const float x2 = 100.0f;
const float y2 = 90.0f;
const float x3 = 150.0f;
const float y3 = 150.0f;
// 1. Action::DOWN with stylus + button
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_down.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_down));
EXPECT_TRUE(event_log_.empty());
// 2. Action::MOVE
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
ASSERT_EQ(1u, event_log_.size());
EXPECT_STREQ("Begin(50, 30, 100, 90)", event_log_.back().c_str());
event_time += base::TimeDelta::FromMilliseconds(10);
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x3, y3);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
ASSERT_EQ(2u, event_log_.size());
EXPECT_STREQ("Update(150, 150)", event_log_.back().c_str());
// 3. Action::UP
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x3, y3);
action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_up.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_up));
ASSERT_EQ(3u, event_log_.size()); // NO CHANGE
EXPECT_STREQ("End", event_log_.back().c_str());
}
TEST_F(StylusTextSelectorTest, PenDraggingButtonSecondaryNotPressed) {
base::TimeTicks event_time = base::TimeTicks::Now();
float x = 50.0f;
float y = 30.0f;
// 1. Action::DOWN with stylus + button
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x, y);
action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_down.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_down));
EXPECT_TRUE(event_log_.empty());
// 2. Action::MOVE
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 70
y += 20; // 50
MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
ASSERT_EQ(1u, event_log_.size());
EXPECT_STREQ("Begin(50, 30, 70, 50)", event_log_.back().c_str());
// 3. Action::MOVE with stylus + no button
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 90
y += 20; // 70
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_EQ(1u, event_log_.size()); // NO CHANGE
// 4. Action::MOVE with stylus + button pressed again
// Note that the end action is deferred until the stylus is lifted.
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 110
y += 20; // 90
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_EQ(2u, event_log_.size());
EXPECT_STREQ("Begin(90, 70, 110, 90)", event_log_.back().c_str());
// 5. Action::MOVE with stylus + no button
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 130
y += 20; // 110
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_EQ(2u, event_log_.size()); // NO CHANGE
// 5. Action::UP
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x, y);
action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_up.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_up));
EXPECT_EQ(3u, event_log_.size());
EXPECT_STREQ("End", event_log_.back().c_str());
}
TEST_F(StylusTextSelectorTest, TapTriggersLongPressSelection) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float x1 = 50.0f;
const float y1 = 30.0f;
const float x2 = 51.0f;
const float y2 = 31.0f;
// 1. Action::DOWN with stylus + button
event_time += base::TimeDelta::FromMilliseconds(1);
MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_down.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_down));
EXPECT_TRUE(event_log_.empty());
// 2. Action::MOVE
event_time += base::TimeDelta::FromMilliseconds(1);
MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_SECONDARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_TRUE(event_log_.empty());
// 3. Action::UP
event_time += base::TimeDelta::FromMilliseconds(1);
MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x2, y2);
action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_up.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_up));
ASSERT_EQ(1u, event_log_.size());
EXPECT_STREQ("Tap", event_log_.back().c_str());
}
// End of Tests for BUTTON_SECONDARY case.
// The following Tests cover BUTTON_STYLUS_PRIMARY case, which is the stylus
// button pressed state from Android M.
TEST_F(StylusTextSelectorTest, PenDraggingButtonStylusPrimary) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float x1 = 50.0f;
const float y1 = 30.0f;
const float x2 = 100.0f;
const float y2 = 90.0f;
const float x3 = 150.0f;
const float y3 = 150.0f;
// 1. Action::DOWN with stylus + button
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_down.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_down));
EXPECT_TRUE(event_log_.empty());
// 2. Action::MOVE
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
ASSERT_EQ(1u, event_log_.size());
EXPECT_STREQ("Begin(50, 30, 100, 90)", event_log_.back().c_str());
event_time += base::TimeDelta::FromMilliseconds(10);
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x3, y3);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
ASSERT_EQ(2u, event_log_.size());
EXPECT_STREQ("Update(150, 150)", event_log_.back().c_str());
// 3. Action::UP
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x3, y3);
action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_up.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_up));
ASSERT_EQ(3u, event_log_.size()); // NO CHANGE
EXPECT_STREQ("End", event_log_.back().c_str());
}
TEST_F(StylusTextSelectorTest, PenDraggingButtonStylusPrimaryNotPressed) {
base::TimeTicks event_time = base::TimeTicks::Now();
float x = 50.0f;
float y = 30.0f;
// 1. Action::DOWN with stylus + button
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x, y);
action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_down.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_down));
EXPECT_TRUE(event_log_.empty());
// 2. Action::MOVE
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 70
y += 20; // 50
MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
ASSERT_EQ(1u, event_log_.size());
EXPECT_STREQ("Begin(50, 30, 70, 50)", event_log_.back().c_str());
// 3. Action::MOVE with stylus + no button
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 90
y += 20; // 70
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_EQ(1u, event_log_.size()); // NO CHANGE
// 4. Action::MOVE with stylus + button pressed again
// Note that the end action is deferred until the stylus is lifted.
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 110
y += 20; // 90
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_EQ(2u, event_log_.size());
EXPECT_STREQ("Begin(90, 70, 110, 90)", event_log_.back().c_str());
// 5. Action::MOVE with stylus + no button
event_time += base::TimeDelta::FromMilliseconds(10);
x += 20; // 130
y += 20; // 110
action_move = MockMotionEvent(MotionEvent::Action::MOVE, event_time, x, y);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_EQ(2u, event_log_.size()); // NO CHANGE
// 5. Action::UP
event_time += base::TimeDelta::FromMilliseconds(10);
MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x, y);
action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_up.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_up));
EXPECT_EQ(3u, event_log_.size());
EXPECT_STREQ("End", event_log_.back().c_str());
}
TEST_F(StylusTextSelectorTest, TapTriggersLongPressSelection2) {
base::TimeTicks event_time = base::TimeTicks::Now();
const float x1 = 50.0f;
const float y1 = 30.0f;
const float x2 = 51.0f;
const float y2 = 31.0f;
// 1. Action::DOWN with stylus + button
event_time += base::TimeDelta::FromMilliseconds(1);
MockMotionEvent action_down(MotionEvent::Action::DOWN, event_time, x1, y1);
action_down.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_down.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_down));
EXPECT_TRUE(event_log_.empty());
// 2. Action::MOVE
event_time += base::TimeDelta::FromMilliseconds(1);
MockMotionEvent action_move(MotionEvent::Action::MOVE, event_time, x2, y2);
action_move.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_move.set_button_state(MotionEvent::BUTTON_STYLUS_PRIMARY);
EXPECT_TRUE(selector_->OnTouchEvent(action_move));
EXPECT_TRUE(event_log_.empty());
// 3. Action::UP
event_time += base::TimeDelta::FromMilliseconds(1);
MockMotionEvent action_up(MotionEvent::Action::UP, event_time, x2, y2);
action_up.SetToolType(0, MotionEvent::ToolType::STYLUS);
action_up.set_button_state(0);
EXPECT_TRUE(selector_->OnTouchEvent(action_up));
ASSERT_EQ(1u, event_log_.size());
EXPECT_STREQ("Tap", event_log_.back().c_str());
}
// End of tests for BUTTON_STLUS_PRIMARY case.
} // namespace content