• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }