#include "ash/fast_ink/fast_ink_points.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/test/event_generator.h"
namespace ash {
namespace {
const int kTestPointsLifetimeSeconds = 5;
class FastInkPointsTest : public testing::Test {
public:
FastInkPointsTest()
: points_(base::Seconds(kTestPointsLifetimeSeconds)),
predicted_(base::Seconds(kTestPointsLifetimeSeconds)),
event_time_(base::TimeTicks()),
screen_size_(1000, 1000) {}
FastInkPointsTest(const FastInkPointsTest&) = delete;
FastInkPointsTest& operator=(const FastInkPointsTest&) = delete;
~FastInkPointsTest() override = default;
protected:
FastInkPoints points_;
FastInkPoints predicted_;
base::TimeTicks event_time_;
const gfx::Size screen_size_;
base::TimeDelta prediction_duration_;
void AddPoint(const gfx::PointF& point, base::TimeDelta interval) {
event_time_ += interval;
points_.AddPoint(point, event_time_);
predicted_.Predict(points_, event_time_, prediction_duration_,
screen_size_);
const base::TimeTicks presentation_time =
event_time_ + prediction_duration_;
points_.MoveForwardToTime(presentation_time);
predicted_.MoveForwardToTime(presentation_time);
}
void AddStroke(int points,
base::TimeDelta interval,
const gfx::PointF& position,
const gfx::Vector2dF& velocity,
const gfx::Vector2dF& acceleration) {
points_.Clear();
gfx::PointF p = position;
gfx::Vector2dF v = velocity;
for (int i = 0; i < points; ++i) {
AddPoint(p, interval);
p += v;
v += acceleration;
}
}
void Diff(std::vector<gfx::Vector2dF>& dst,
const std::vector<gfx::Vector2dF>& src) {
dst.clear();
if (src.size() < 2)
return;
for (size_t i = 1; i < src.size(); ++i)
dst.push_back(src[i] - src[i - 1]);
}
void ComputeDeltas(std::vector<gfx::Vector2dF>& velocity,
std::vector<gfx::Vector2dF>& acceleration) {
std::vector<gfx::Vector2dF> position;
for (auto p : points_.points())
position.push_back(p.location.OffsetFromOrigin());
for (auto p : predicted_.points())
position.push_back(p.location.OffsetFromOrigin());
Diff(velocity, position);
Diff(acceleration, velocity);
}
};
}
TEST_F(FastInkPointsTest, FastInkPointsInternalCollection) {
EXPECT_TRUE(points_.IsEmpty());
EXPECT_EQ(gfx::Rect(), points_.GetBoundingBox());
const gfx::PointF left(1, 1);
const gfx::PointF bottom(1, 9);
const gfx::PointF top_right(30, 0);
const gfx::PointF last(2, 2);
points_.AddPoint(left, base::TimeTicks());
EXPECT_EQ(gfx::Rect(1, 1, 0, 0), points_.GetBoundingBox());
points_.AddPoint(bottom, base::TimeTicks());
EXPECT_EQ(gfx::Rect(1, 1, 0, bottom.y() - 1), points_.GetBoundingBox());
points_.AddPoint(top_right, base::TimeTicks());
EXPECT_EQ(3, points_.GetNumberOfPoints());
EXPECT_FALSE(points_.IsEmpty());
EXPECT_EQ(gfx::Rect(left.x(), top_right.y(), top_right.x() - left.x(),
bottom.y() - top_right.y()),
points_.GetBoundingBox());
points_.AddPoint(last, base::TimeTicks());
EXPECT_EQ(gfx::Rect(left.x(), top_right.y(), top_right.x() - left.x(),
bottom.y() - top_right.y()),
points_.GetBoundingBox());
EXPECT_EQ(left, points_.GetOldest().location);
EXPECT_EQ(last, points_.GetNewest().location);
gfx::PointF new_left_bottom(0, 40);
points_.AddPoint(new_left_bottom, base::TimeTicks());
EXPECT_EQ(5, points_.GetNumberOfPoints());
EXPECT_EQ(gfx::Rect(new_left_bottom.x(), top_right.y(),
top_right.x() - new_left_bottom.x(),
new_left_bottom.y() - top_right.y()),
points_.GetBoundingBox());
points_.Clear();
EXPECT_TRUE(points_.IsEmpty());
}
TEST_F(FastInkPointsTest, FastInkPointsInternalCollectionDeletion) {
EXPECT_EQ(1, prediction_duration_.is_zero());
AddPoint(gfx::PointF(), base::Seconds(1));
EXPECT_EQ(1, points_.GetNumberOfPoints());
EXPECT_FLOAT_EQ(0.0, points_.GetFadeoutFactor(0));
AddPoint(gfx::PointF(), base::Seconds(1));
EXPECT_EQ(2, points_.GetNumberOfPoints());
EXPECT_FLOAT_EQ(0.2, points_.GetFadeoutFactor(0));
EXPECT_FLOAT_EQ(0.0, points_.GetFadeoutFactor(1));
AddPoint(gfx::PointF(), base::Seconds(10));
EXPECT_EQ(1, points_.GetNumberOfPoints());
AddPoint(gfx::PointF(), base::Seconds(1));
AddPoint(gfx::PointF(), base::Seconds(1));
AddPoint(gfx::PointF(), base::Seconds(1));
EXPECT_EQ(4, points_.GetNumberOfPoints());
EXPECT_FLOAT_EQ(0.6, points_.GetFadeoutFactor(0));
EXPECT_FLOAT_EQ(0.4, points_.GetFadeoutFactor(1));
EXPECT_FLOAT_EQ(0.2, points_.GetFadeoutFactor(2));
EXPECT_FLOAT_EQ(0.0, points_.GetFadeoutFactor(3));
AddPoint(gfx::PointF(), base::Seconds(3));
EXPECT_EQ(3, points_.GetNumberOfPoints());
}
TEST_F(FastInkPointsTest, FastInkPointsPrediction) {
prediction_duration_ = base::Milliseconds(18);
const base::TimeDelta kTraceInterval = base::Milliseconds(5);
const int kExpectedPredictionDepth = 3;
const float kMaxPredictionError = 1e-4;
std::vector<gfx::Vector2dF> computed_velocity;
std::vector<gfx::Vector2dF> computed_acceleration;
const gfx::Vector2dF zero;
const gfx::PointF position(0, 0);
AddStroke(0, kTraceInterval, position, zero, zero);
EXPECT_EQ(0, predicted_.GetNumberOfPoints());
AddStroke(1, kTraceInterval, position, zero, zero);
EXPECT_EQ(0, predicted_.GetNumberOfPoints());
for (int points = 2; points <= 4; ++points) {
SCOPED_TRACE(points);
AddStroke(points, kTraceInterval, position, zero, zero);
EXPECT_EQ(0, predicted_.GetNumberOfPoints());
}
const gfx::Vector2dF velocity(10, 5);
for (int points = 2; points <= 4; ++points) {
SCOPED_TRACE(points);
AddStroke(points, kTraceInterval, position, velocity, zero);
EXPECT_EQ(kExpectedPredictionDepth, predicted_.GetNumberOfPoints());
ComputeDeltas(computed_velocity, computed_acceleration);
for (auto v : computed_velocity) {
EXPECT_GT(kMaxPredictionError, (velocity - v).Length());
}
}
const gfx::Vector2dF acceleration(4, 2);
for (int points = 3; points <= 4; ++points) {
SCOPED_TRACE(points);
AddStroke(points, kTraceInterval, position, velocity, acceleration);
EXPECT_EQ(kExpectedPredictionDepth, predicted_.GetNumberOfPoints());
ComputeDeltas(computed_velocity, computed_acceleration);
for (auto a : computed_acceleration) {
EXPECT_GT(kMaxPredictionError, (acceleration - a).Length());
}
}
}
TEST_F(FastInkPointsTest, AddGap) {
points_.AddPoint(gfx::PointF(0, 0), base::TimeTicks());
points_.AddPoint(gfx::PointF(1, 1), base::TimeTicks());
points_.AddGap();
points_.AddPoint(gfx::PointF(2, 2), base::TimeTicks());
points_.AddPoint(gfx::PointF(3, 3), base::TimeTicks());
points_.AddPoint(gfx::PointF(4, 4), base::TimeTicks());
points_.AddGap();
points_.AddPoint(gfx::PointF(5, 5), base::TimeTicks());
auto points = points_.points();
EXPECT_FALSE(points[0].gap_after);
EXPECT_TRUE(points[1].gap_after);
EXPECT_FALSE(points[2].gap_after);
EXPECT_FALSE(points[3].gap_after);
EXPECT_TRUE(points[4].gap_after);
EXPECT_FALSE(points[5].gap_after);
}
TEST_F(FastInkPointsTest, UndoLastStroke) {
gfx::Rect bounding_box = points_.UndoLastStroke();
EXPECT_EQ(bounding_box, gfx::Rect());
points_.AddPoint(gfx::PointF(0, 0), base::TimeTicks());
points_.AddPoint(gfx::PointF(1, 1), base::TimeTicks());
points_.AddGap();
bounding_box = points_.UndoLastStroke();
EXPECT_TRUE(points_.IsEmpty());
EXPECT_EQ(bounding_box, gfx::Rect(0, 0, 1, 1));
points_.AddPoint(gfx::PointF(0, 0), base::TimeTicks());
points_.AddPoint(gfx::PointF(1, 1), base::TimeTicks());
points_.AddGap();
points_.AddPoint(gfx::PointF(2, 2), base::TimeTicks());
points_.AddPoint(gfx::PointF(3, 3), base::TimeTicks());
points_.AddPoint(gfx::PointF(4, 4), base::TimeTicks());
points_.AddGap();
bounding_box = points_.UndoLastStroke();
EXPECT_EQ(points_.GetNumberOfPoints(), 2);
EXPECT_TRUE(points_.GetNewest().gap_after);
EXPECT_EQ(bounding_box, gfx::Rect(2, 2, 2, 2));
points_.AddPoint(gfx::PointF(0, 0), base::TimeTicks());
points_.AddPoint(gfx::PointF(1, 1), base::TimeTicks());
points_.AddGap();
points_.AddPoint(gfx::PointF(2, 2), base::TimeTicks());
points_.AddPoint(gfx::PointF(3, 3), base::TimeTicks());
points_.AddPoint(gfx::PointF(4, 4), base::TimeTicks());
points_.AddGap();
points_.AddPoint(gfx::PointF(5, 5), base::TimeTicks());
bounding_box = points_.UndoLastStroke();
EXPECT_EQ(bounding_box, gfx::Rect(5, 5, 0, 0));
bounding_box = points_.UndoLastStroke();
EXPECT_EQ(bounding_box, gfx::Rect(2, 2, 2, 2));
EXPECT_EQ(points_.GetNumberOfPoints(), 4);
EXPECT_TRUE(points_.GetNewest().gap_after);
}
}