1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "gtest/gtest.h"
17 #include "gmock/gmock.h"
18
19 #include <fstream>
20 #include <random>
21
22 #define private public
23 #define protected public
24
25 #include "core/components_ng/event/touch_event.h"
26 #include "core/components_v2/inspector/inspector_constants.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 #include "frameworks/core/components_ng/pattern/root/root_pattern.h"
29
30 using namespace testing;
31 using namespace testing::ext;
32 using OHOS::Ace::TimeStamp;
33
34 namespace OHOS::Ace {
35
GetDeltaT(TimeStamp time,TimeStamp base)36 int64_t GetDeltaT(TimeStamp time, TimeStamp base)
37 {
38 auto deltaT = time - base;
39 return std::chrono::duration_cast<std::chrono::milliseconds>(deltaT).count();
40 }
41
GenerateCSV(const std::string & fileName,TimeStamp baseTime,const std::vector<TouchEvent> & tp,const std::vector<TouchEvent> & common,const std::vector<TouchEvent> & accelarate)42 void GenerateCSV(const std::string& fileName, TimeStamp baseTime,
43 const std::vector<TouchEvent>& tp,
44 const std::vector<TouchEvent>& common,
45 const std::vector<TouchEvent>& accelarate)
46 {
47 std::ofstream file(fileName);
48 if (!file.is_open()) {
49 std::cerr << "Cannot open the file: " << fileName << std::endl;
50 return;
51 }
52
53 // write head
54 file << "TpTime,TpDisplacement,"
55 << "CommonTime,CommonDisplacement,"
56 << "AccelarateTime,AccelarateDisplacement\n";
57 size_t maxSize = std::max({tp.size(), common.size(), accelarate.size()});
58
59 // write data
60 for (size_t i = 0; i < maxSize; ++i) {
61 if (i < tp.size()) {
62 double d = std::sqrt(tp[i].x * tp[i].x + tp[i].y * tp[i].y);
63 file << GetDeltaT(tp[i].time, baseTime) << "," << d;
64 } else {
65 file << ",";
66 }
67
68 if (i < common.size()) {
69 double d = std::sqrt(common[i].x * common[i].x + common[i].y * common[i].y);
70 file << "," << GetDeltaT(common[i].time, baseTime) << "," << d;
71 } else {
72 file << ",,";
73 }
74
75 if (i < accelarate.size()) {
76 double d = std::sqrt(accelarate[i].x * accelarate[i].x + accelarate[i].y * accelarate[i].y);
77 file << "," << GetDeltaT(accelarate[i].time, baseTime) << "," << d << "\n";
78 } else {
79 file << ",,\n";
80 }
81 }
82 file.close();
83 }
84
85 class MockEventManager : public EventManager {
86 DECLARE_ACE_TYPE(MockEventManager, OHOS::Ace::EventManager);
87
88 public:
89 MockEventManager() = default;
90 ~MockEventManager() override = default;
91
92 MOCK_METHOD(bool, DispatchTouchEvent, (const TouchEvent&, bool));
93 };
94
95 using NiceEventManager = NiceMock<MockEventManager>;
96
97 namespace NG {
98 enum TestTouchTrackingMode {
99 FAST_FLICK,
100 DECELARATING,
101 CONSTANT_SPEED,
102 SLOW_CONSTANT_SPEED,
103 };
104
105 class ResampleTestNg : public testing::Test {
106 public:
107 static void SetUpTestSuite();
108 static void TearDownTestSuite();
109
110 static bool TestOnTouchEvent(const TouchEvent& event, bool sendOnTouch);
111 static vector<TouchEvent> events_;
112 static vector<TouchEvent> oriEvents_;
113 static RefPtr<PipelineContext> pipeline_;
114
115 void SetUp() override;
116 void TearDown() override;
117
118 /* simulate functions */
119 void GenerateTouchEvents(double a, double b, double c, uint32_t duration, double angle);
120 void RunVsync(int32_t vsyncPeriodMs);
121 TouchEvent GetPhysicPoint(TimeStamp time);
122 TouchEvent GetOriginPoint(TimeStamp time);
123
124 /* test functions */
125 double GetTouchEventsCoordVariance();
126 double GetTouchEventsSmothDiff();
127 double CompareAccelarate(TestTouchTrackingMode mode, int32_t frameRate);
128
129 static constexpr uint32_t TP_RATE = 140;
130
131 TimeStamp nowTime_;
132 };
133
134 std::vector<TouchEvent> ResampleTestNg::events_;
135 std::vector<TouchEvent> ResampleTestNg::oriEvents_;
136 std::vector<double> coefs_;
137 RefPtr<PipelineContext> ResampleTestNg::pipeline_;
138
SetUpTestSuite()139 void ResampleTestNg::SetUpTestSuite()
140 {
141 pipeline_ = AceType::MakeRefPtr<PipelineContext>();
142 pipeline_->rootNode_ = FrameNode::CreateFrameNodeWithTree(
143 V2::ROOT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<RootPattern>());
144 auto mockEventManager = AceType::MakeRefPtr<NiceEventManager>();
145 pipeline_->eventManager_ = mockEventManager;
146 ON_CALL((*mockEventManager), DispatchTouchEvent(testing::_, testing::_)).WillByDefault(testing::Invoke([](const TouchEvent& event, bool sendOnTouch) {
147 return TestOnTouchEvent(event, sendOnTouch);
148 }));
149 }
150
TearDownTestSuite()151 void ResampleTestNg::TearDownTestSuite()
152 {
153 pipeline_ = nullptr;
154 }
155
SetUp()156 void ResampleTestNg::SetUp()
157 {
158 nowTime_ = std::chrono::high_resolution_clock::now();
159 }
160
TearDown()161 void ResampleTestNg::TearDown()
162 {
163 events_.clear();
164 oriEvents_.clear();
165 }
166
GetPhysicPoint(TimeStamp time)167 TouchEvent ResampleTestNg::GetPhysicPoint(TimeStamp time)
168 {
169 auto deltaT = time - nowTime_;
170 int64_t t = std::chrono::duration_cast<std::chrono::milliseconds>(deltaT).count();
171 double d = coefs_[0] * t * t + coefs_[1] * t + coefs_[2];
172 TouchEvent event;
173 double cosTheta = std::cos(coefs_[3]);
174 double sinTheta = std::sin(coefs_[3]);
175 event.SetX(d * cosTheta);
176 event.SetY(d * sinTheta);
177 return event;
178 }
179
GetOriginPoint(TimeStamp time)180 TouchEvent ResampleTestNg::GetOriginPoint(TimeStamp time)
181 {
182 auto deltaT = time - nowTime_;
183 int64_t t = std::chrono::duration_cast<std::chrono::milliseconds>(deltaT).count();
184 return oriEvents_[t * TP_RATE / 1000];
185 }
186
GenerateTouchEvents(double a,double b,double c,uint32_t duration,double angle)187 void ResampleTestNg::GenerateTouchEvents(double a, double b, double c, uint32_t duration, double angle) {
188 coefs_.assign({a, b, c, angle});
189 std::vector<TouchEvent> events;
190 uint32_t samplingRate = TP_RATE;
191 std::chrono::microseconds dt(1000000 / samplingRate);
192 std::chrono::microseconds deltaT(0);
193 std::chrono::milliseconds durationMs(duration);
194 double cosTheta = std::cos(angle);
195 double sinTheta = std::sin(angle);
196 std::random_device rd;
197 std::mt19937 gen(rd());
198 std::normal_distribution<double> noise(0.0, 0.7);
199
200 // touch down
201 TouchEvent event;
202 event.SetId(0.0)
203 .SetX(0.0)
204 .SetY(0.0)
205 .SetType(TouchType::DOWN)
206 .SetTime(nowTime_);
207 oriEvents_.emplace_back(event);
208
209 event.SetType(TouchType::MOVE);
210 do {
211 deltaT += dt;
212 double t = deltaT.count() / 1000;
213 double d = a * t * t + b * t + c;
214 event.SetX(d * cosTheta + noise(gen));
215 event.SetY(d * sinTheta + noise(gen));
216 event.SetTime(deltaT + nowTime_);
217 oriEvents_.emplace_back(event);
218 } while (deltaT < durationMs);
219 }
220
RunVsync(int32_t vsyncPeriodUs)221 void ResampleTestNg::RunVsync(int32_t vsyncPeriodUs)
222 {
223 std::chrono::microseconds vsyncPeriod(1000000 / vsyncPeriodUs);
224 constexpr int64_t delay = 3000000; // simulate the 3ms delay between vsync and driver time of event
225 int32_t i = 0;
226 auto vsyncTime = nowTime_;
227 while (i < oriEvents_.size()) {
228 while (i < oriEvents_.size() && oriEvents_[i].time < vsyncTime) {
229 pipeline_->touchEvents_.emplace_back(oriEvents_[i]);
230 i++;
231 }
232 int64_t vsynTimeNs = vsyncTime.time_since_epoch().count();
233 pipeline_->SetVsyncTime(vsynTimeNs + delay);
234 pipeline_->FlushTouchEvents();
235 vsyncTime += vsyncPeriod;
236 }
237 }
238
TestOnTouchEvent(const TouchEvent & event,bool sendOnTouch)239 bool ResampleTestNg::TestOnTouchEvent(const TouchEvent& event, bool sendOnTouch)
240 {
241 events_.emplace_back(event);
242 return true;
243 }
244
GetTouchEventsCoordVariance()245 double ResampleTestNg::GetTouchEventsCoordVariance()
246 {
247 double variance = 0;
248 for (int32_t i = 0; i < events_.size(); ++i) {
249 // auto base = GetPhysicPoint(events_[i].time);
250 auto base = GetOriginPoint(events_[i].time);
251 double dx = events_[i].x - base.x;
252 double dy = events_[i].y - base.y;
253 variance += dx * dx + dy * dy;
254 }
255 return variance / events_.size();
256 }
257
GetTouchEventsSmothDiff()258 double ResampleTestNg::GetTouchEventsSmothDiff()
259 {
260 return 0.0f;
261 }
262
CompareAccelarate(TestTouchTrackingMode mode,int32_t frameRate)263 double ResampleTestNg::CompareAccelarate(TestTouchTrackingMode mode, int32_t frameRate)
264 {
265 switch (mode) {
266 case FAST_FLICK:
267 GenerateTouchEvents(-0.0005, 0.8, 0, 500, 0);
268 break;
269 case DECELARATING:
270 GenerateTouchEvents(-0.0001, 0.3, 0, 800, M_PI / 6);
271 break;
272 case CONSTANT_SPEED:
273 GenerateTouchEvents(0, 0.2, 0, 1000, M_PI / 4);
274 break;
275 case SLOW_CONSTANT_SPEED:
276 GenerateTouchEvents(0, 0.05, 0, 1000, M_PI / 3);
277 break;
278 }
279 pipeline_->touchAccelarate_ = false;
280 RunVsync(frameRate);
281 auto commonEvents = events_;
282 events_.clear();
283 pipeline_->touchAccelarate_ = true;
284 RunVsync(frameRate);
285 // double accelarateVar = GetTouchEventsCoordVariance();
286 GenerateCSV("/data/log/faultlog/resample.csv", nowTime_, oriEvents_, commonEvents, events_);
287
288 return 0.0;
289 }
290
291 /**
292 * @tc.name: ResampleTest60HzCommon
293 * @tc.desc: Construct touch events and test resample sequence in 60Hz vsync.
294 * @tc.type: FUNC
295 */
296 HWTEST_F(ResampleTestNg, ResampleTest60HzFastFlick, TestSize.Level1)
297 {
298 constexpr int32_t repeatTimes = 1;
299 int32_t failTimes = 0;
300 double accumulateVar = 0;
301 for (int32_t i = 0; i < repeatTimes; ++i) {
302 accumulateVar += CompareAccelarate(FAST_FLICK, 60);
303 if (accumulateVar < 0) {
304 ++failTimes;
305 }
306 }
307 }
308 }
309 }