1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <input/VelocityControl.h>
18 
19 #include <limits>
20 
21 #include <gtest/gtest.h>
22 #include <input/AccelerationCurve.h>
23 #include <utils/Timers.h>
24 
25 namespace android {
26 
27 namespace {
28 
29 constexpr float EPSILON = 0.001;
30 constexpr float COUNTS_PER_MM = 800 / 25.4;
31 
32 } // namespace
33 
34 class CurvedVelocityControlTest : public testing::Test {
35 protected:
36     CurvedVelocityControl mCtrl;
37 
moveWithoutCheckingResult(nsecs_t eventTime,float deltaX,float deltaY)38     void moveWithoutCheckingResult(nsecs_t eventTime, float deltaX, float deltaY) {
39         mCtrl.move(eventTime, &deltaX, &deltaY);
40     }
41 
moveAndCheckRatio(nsecs_t eventTime,const float deltaX,const float deltaY,float expectedRatio)42     void moveAndCheckRatio(nsecs_t eventTime, const float deltaX, const float deltaY,
43                            float expectedRatio) {
44         float newDeltaX = deltaX, newDeltaY = deltaY;
45         mCtrl.move(eventTime, &newDeltaX, &newDeltaY);
46         ASSERT_NEAR(expectedRatio * deltaX, newDeltaX, EPSILON)
47                 << "Expected ratio of " << expectedRatio << " in X, but actual ratio was "
48                 << newDeltaX / deltaX;
49         ASSERT_NEAR(expectedRatio * deltaY, newDeltaY, EPSILON)
50                 << "Expected ratio of " << expectedRatio << " in Y, but actual ratio was "
51                 << newDeltaY / deltaY;
52     }
53 };
54 
TEST_F(CurvedVelocityControlTest,SegmentSelection)55 TEST_F(CurvedVelocityControlTest, SegmentSelection) {
56     // To make the maths simple, use a "curve" that's actually just a sequence of steps.
57     mCtrl.setCurve({
58             {10, 2, 0},
59             {20, 3, 0},
60             {30, 4, 0},
61             {std::numeric_limits<double>::infinity(), 5, 0},
62     });
63 
64     // Establish a velocity of 16 mm/s.
65     moveWithoutCheckingResult(0, 0, 0);
66     moveWithoutCheckingResult(10'000'000, 0.16 * COUNTS_PER_MM, 0);
67     moveWithoutCheckingResult(20'000'000, 0.16 * COUNTS_PER_MM, 0);
68     moveWithoutCheckingResult(30'000'000, 0.16 * COUNTS_PER_MM, 0);
69     ASSERT_NO_FATAL_FAILURE(
70             moveAndCheckRatio(40'000'000, 0.16 * COUNTS_PER_MM, 0, /*expectedRatio=*/3));
71 
72     // Establish a velocity of 50 mm/s.
73     mCtrl.reset();
74     moveWithoutCheckingResult(100'000'000, 0, 0);
75     moveWithoutCheckingResult(110'000'000, 0.50 * COUNTS_PER_MM, 0);
76     moveWithoutCheckingResult(120'000'000, 0.50 * COUNTS_PER_MM, 0);
77     moveWithoutCheckingResult(130'000'000, 0.50 * COUNTS_PER_MM, 0);
78     ASSERT_NO_FATAL_FAILURE(
79             moveAndCheckRatio(140'000'000, 0.50 * COUNTS_PER_MM, 0, /*expectedRatio=*/5));
80 }
81 
TEST_F(CurvedVelocityControlTest,RatioDefaultsToFirstSegmentWhenVelocityIsUnknown)82 TEST_F(CurvedVelocityControlTest, RatioDefaultsToFirstSegmentWhenVelocityIsUnknown) {
83     mCtrl.setCurve({
84             {10, 3, 0},
85             {20, 2, 0},
86             {std::numeric_limits<double>::infinity(), 4, 0},
87     });
88 
89     // Only send two moves, which won't be enough for VelocityTracker to calculate a velocity from.
90     moveWithoutCheckingResult(0, 0, 0);
91     ASSERT_NO_FATAL_FAILURE(
92             moveAndCheckRatio(10'000'000, 0.25 * COUNTS_PER_MM, 0, /*expectedRatio=*/3));
93 }
94 
TEST_F(CurvedVelocityControlTest,VelocityCalculatedUsingBothAxes)95 TEST_F(CurvedVelocityControlTest, VelocityCalculatedUsingBothAxes) {
96     mCtrl.setCurve({
97             {8.0, 3, 0},
98             {8.1, 2, 0},
99             {std::numeric_limits<double>::infinity(), 4, 0},
100     });
101 
102     // Establish a velocity of 8.06 (= √65 = √(7²+4²)) mm/s between the two axes.
103     moveWithoutCheckingResult(0, 0, 0);
104     moveWithoutCheckingResult(10'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM);
105     moveWithoutCheckingResult(20'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM);
106     moveWithoutCheckingResult(30'000'000, 0.07 * COUNTS_PER_MM, 0.04 * COUNTS_PER_MM);
107     ASSERT_NO_FATAL_FAILURE(moveAndCheckRatio(40'000'000, 0.07 * COUNTS_PER_MM,
108                                               0.04 * COUNTS_PER_MM,
109                                               /*expectedRatio=*/2));
110 }
111 
TEST_F(CurvedVelocityControlTest,ReciprocalTerm)112 TEST_F(CurvedVelocityControlTest, ReciprocalTerm) {
113     mCtrl.setCurve({
114             {10, 2, 0},
115             {20, 3, -10},
116             {std::numeric_limits<double>::infinity(), 3, 0},
117     });
118 
119     // Establish a velocity of 15 mm/s.
120     moveWithoutCheckingResult(0, 0, 0);
121     moveWithoutCheckingResult(10'000'000, 0, 0.15 * COUNTS_PER_MM);
122     moveWithoutCheckingResult(20'000'000, 0, 0.15 * COUNTS_PER_MM);
123     moveWithoutCheckingResult(30'000'000, 0, 0.15 * COUNTS_PER_MM);
124     // Expected ratio is 3 - 10 / 15 = 2.33333...
125     ASSERT_NO_FATAL_FAILURE(
126             moveAndCheckRatio(40'000'000, 0, 0.15 * COUNTS_PER_MM, /*expectedRatio=*/2.33333));
127 }
128 
129 } // namespace android