Flutter Engine
The Flutter Engine
pointer_delegate_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <fuchsia/ui/pointer/cpp/fidl.h>
6#include <gtest/gtest.h>
7#include <lib/async-loop/cpp/loop.h>
8#include <lib/async-loop/default.h>
9#include <lib/fidl/cpp/binding_set.h>
10
11#include <array>
12#include <optional>
13#include <vector>
14
15#include "flutter/fml/logging.h"
16#include "flutter/fml/macros.h"
17#include "pointer_delegate.h"
21
23
24using fup_EventPhase = fuchsia::ui::pointer::EventPhase;
25using fup_TouchEvent = fuchsia::ui::pointer::TouchEvent;
26using fup_TouchIxnId = fuchsia::ui::pointer::TouchInteractionId;
27using fup_TouchIxnResult = fuchsia::ui::pointer::TouchInteractionResult;
28using fup_TouchIxnStatus = fuchsia::ui::pointer::TouchInteractionStatus;
29using fup_TouchPointerSample = fuchsia::ui::pointer::TouchPointerSample;
30using fup_TouchResponse = fuchsia::ui::pointer::TouchResponse;
31using fup_TouchResponseType = fuchsia::ui::pointer::TouchResponseType;
32using fup_ViewParameters = fuchsia::ui::pointer::ViewParameters;
33using fup_MouseEvent = fuchsia::ui::pointer::MouseEvent;
34
35constexpr std::array<std::array<float, 2>, 2> kRect = {{{0, 0}, {20, 20}}};
36constexpr std::array<float, 9> kIdentity = {1, 0, 0, 0, 1, 0, 0, 0, 1};
37constexpr fup_TouchIxnId kIxnOne = {.device_id = 1u,
38 .pointer_id = 1u,
39 .interaction_id = 2u};
40
41constexpr uint32_t kMouseDeviceId = 123;
42constexpr std::array<int64_t, 2> kNoScrollInPhysicalPixelDelta = {0, 0};
43const bool kNotPrecisionScroll = false;
44const bool kPrecisionScroll = true;
45
46// Fixture to exercise Flutter runner's implementation for
47// fuchsia.ui.pointer.TouchSource.
48class PointerDelegateTest : public ::testing::Test {
49 protected:
50 PointerDelegateTest() : loop_(&kAsyncLoopConfigAttachToCurrentThread) {
51 touch_source_ = std::make_unique<FakeTouchSource>();
52 mouse_source_ = std::make_unique<FakeMouseSource>();
53 pointer_delegate_ = std::make_unique<flutter_runner::PointerDelegate>(
54 touch_source_bindings_.AddBinding(touch_source_.get()),
55 mouse_source_bindings_.AddBinding(mouse_source_.get()));
56 }
57
58 void RunLoopUntilIdle() { loop_.RunUntilIdle(); }
59
60 std::unique_ptr<FakeTouchSource> touch_source_;
61 std::unique_ptr<FakeMouseSource> mouse_source_;
62 std::unique_ptr<flutter_runner::PointerDelegate> pointer_delegate_;
63
64 private:
65 async::Loop loop_;
66 fidl::BindingSet<fuchsia::ui::pointer::TouchSource> touch_source_bindings_;
67 fidl::BindingSet<fuchsia::ui::pointer::MouseSource> mouse_source_bindings_;
68
69 FML_DISALLOW_COPY_AND_ASSIGN(PointerDelegateTest);
70};
71
72TEST_F(PointerDelegateTest, Data_FuchsiaTimeVersusFlutterTime) {
73 std::optional<std::vector<flutter::PointerData>> pointers;
74 pointer_delegate_->WatchLoop(
75 [&pointers](std::vector<flutter::PointerData> events) {
76 pointers = std::move(events);
77 });
78 RunLoopUntilIdle(); // Server gets watch call.
79
80 // Fuchsia ADD -> Flutter ADD+DOWN
81 std::vector<fup_TouchEvent> events =
83 .AddTime(/* in nanoseconds */ 1111789u)
86 .AddResult(
87 {.interaction = kIxnOne, .status = fup_TouchIxnStatus::GRANTED})
88 .BuildAsVector();
89 touch_source_->ScheduleCallback(std::move(events));
90 RunLoopUntilIdle();
91
92 ASSERT_TRUE(pointers.has_value());
93 ASSERT_EQ(pointers->size(), 2u);
94 EXPECT_EQ((*pointers)[0].time_stamp, /* in microseconds */ 1111u);
95 EXPECT_EQ((*pointers)[1].time_stamp, /* in microseconds */ 1111u);
96}
97
98TEST_F(PointerDelegateTest, Phase_FlutterPhasesAreSynthesized) {
99 std::optional<std::vector<flutter::PointerData>> pointers;
100 pointer_delegate_->WatchLoop(
101 [&pointers](std::vector<flutter::PointerData> events) {
102 pointers = std::move(events);
103 });
104 RunLoopUntilIdle(); // Server gets watch call.
105
106 // Fuchsia ADD -> Flutter ADD+DOWN
107 std::vector<fup_TouchEvent> events =
109 .AddTime(1111000u)
111 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
112 .AddResult(
113 {.interaction = kIxnOne, .status = fup_TouchIxnStatus::GRANTED})
114 .BuildAsVector();
115 touch_source_->ScheduleCallback(std::move(events));
116 RunLoopUntilIdle();
117
118 ASSERT_TRUE(pointers.has_value());
119 ASSERT_EQ(pointers->size(), 2u);
120 EXPECT_EQ((*pointers)[0].change, flutter::PointerData::Change::kAdd);
121 EXPECT_EQ((*pointers)[1].change, flutter::PointerData::Change::kDown);
122
123 // Fuchsia CHANGE -> Flutter MOVE
124 events = TouchEventBuilder::New()
125 .AddTime(2222000u)
126 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
127 .BuildAsVector();
128 touch_source_->ScheduleCallback(std::move(events));
129 RunLoopUntilIdle();
130
131 ASSERT_TRUE(pointers.has_value());
132 ASSERT_EQ(pointers->size(), 1u);
133 EXPECT_EQ((*pointers)[0].change, flutter::PointerData::Change::kMove);
134
135 // Fuchsia REMOVE -> Flutter UP+REMOVE
136 events = TouchEventBuilder::New()
137 .AddTime(3333000u)
138 .AddSample(kIxnOne, fup_EventPhase::REMOVE, {10.f, 10.f})
139 .BuildAsVector();
140 touch_source_->ScheduleCallback(std::move(events));
141 RunLoopUntilIdle();
142
143 ASSERT_TRUE(pointers.has_value());
144 ASSERT_EQ(pointers->size(), 2u);
145 EXPECT_EQ((*pointers)[0].change, flutter::PointerData::Change::kUp);
146 EXPECT_EQ((*pointers)[1].change, flutter::PointerData::Change::kRemove);
147}
148
149TEST_F(PointerDelegateTest, Phase_FuchsiaCancelBecomesFlutterCancel) {
150 std::optional<std::vector<flutter::PointerData>> pointers;
151 pointer_delegate_->WatchLoop(
152 [&pointers](std::vector<flutter::PointerData> events) {
153 pointers = std::move(events);
154 });
155 RunLoopUntilIdle(); // Server gets watch call.
156
157 // Fuchsia ADD -> Flutter ADD+DOWN
158 std::vector<fup_TouchEvent> events =
160 .AddTime(1111000u)
162 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
163 .AddResult(
164 {.interaction = kIxnOne, .status = fup_TouchIxnStatus::GRANTED})
165 .BuildAsVector();
166 touch_source_->ScheduleCallback(std::move(events));
167 RunLoopUntilIdle();
168
169 ASSERT_TRUE(pointers.has_value());
170 ASSERT_EQ(pointers->size(), 2u);
171 EXPECT_EQ((*pointers)[0].change, flutter::PointerData::Change::kAdd);
172 EXPECT_EQ((*pointers)[1].change, flutter::PointerData::Change::kDown);
173
174 // Fuchsia CANCEL -> Flutter CANCEL
175 events = TouchEventBuilder::New()
176 .AddTime(2222000u)
177 .AddSample(kIxnOne, fup_EventPhase::CANCEL, {10.f, 10.f})
178 .BuildAsVector();
179 touch_source_->ScheduleCallback(std::move(events));
180 RunLoopUntilIdle();
181
182 ASSERT_TRUE(pointers.has_value());
183 ASSERT_EQ(pointers->size(), 1u);
184 EXPECT_EQ((*pointers)[0].change, flutter::PointerData::Change::kCancel);
185}
186
187TEST_F(PointerDelegateTest, Coordinates_CorrectMapping) {
188 std::optional<std::vector<flutter::PointerData>> pointers;
189 pointer_delegate_->WatchLoop(
190 [&pointers](std::vector<flutter::PointerData> events) {
191 pointers = std::move(events);
192 });
193 RunLoopUntilIdle(); // Server gets watch call.
194
195 // Fuchsia ADD event, with a view parameter that maps the viewport identically
196 // to the view. Then the center point of the viewport should map to the center
197 // of the view, (10.f, 10.f).
198 std::vector<fup_TouchEvent> events =
200 .AddTime(2222000u)
201 .AddViewParameters(/*view*/ {{{0, 0}, {20, 20}}},
202 /*viewport*/ {{{0, 0}, {20, 20}}},
203 /*matrix*/ {1, 0, 0, 0, 1, 0, 0, 0, 1})
204 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
205 .AddResult(
206 {.interaction = kIxnOne, .status = fup_TouchIxnStatus::GRANTED})
207 .BuildAsVector();
208 touch_source_->ScheduleCallback(std::move(events));
209 RunLoopUntilIdle();
210
211 ASSERT_TRUE(pointers.has_value());
212 ASSERT_EQ(pointers->size(), 2u); // ADD - DOWN
213 EXPECT_FLOAT_EQ((*pointers)[0].physical_x, 10.f);
214 EXPECT_FLOAT_EQ((*pointers)[0].physical_y, 10.f);
215 pointers = {};
216
217 // Fuchsia CHANGE event, with a view parameter that translates the viewport by
218 // (10, 10) within the view. Then the minimal point in the viewport (its
219 // origin) should map to the center of the view, (10.f, 10.f).
220 events = TouchEventBuilder::New()
221 .AddTime(2222000u)
222 .AddViewParameters(/*view*/ {{{0, 0}, {20, 20}}},
223 /*viewport*/ {{{0, 0}, {20, 20}}},
224 /*matrix*/ {1, 0, 0, 0, 1, 0, 10, 10, 1})
225 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {0.f, 0.f})
226 .BuildAsVector();
227 touch_source_->ScheduleCallback(std::move(events));
228 RunLoopUntilIdle();
229
230 ASSERT_TRUE(pointers.has_value());
231 ASSERT_EQ(pointers->size(), 1u); // MOVE
232 EXPECT_FLOAT_EQ((*pointers)[0].physical_x, 10.f);
233 EXPECT_FLOAT_EQ((*pointers)[0].physical_y, 10.f);
234
235 // Fuchsia CHANGE event, with a view parameter that scales the viewport by
236 // (0.5, 0.5) within the view. Then the maximal point in the viewport should
237 // map to the center of the view, (10.f, 10.f).
238 events = TouchEventBuilder::New()
239 .AddTime(2222000u)
240 .AddViewParameters(/*view*/ {{{0, 0}, {20, 20}}},
241 /*viewport*/ {{{0, 0}, {20, 20}}},
242 /*matrix*/ {0.5f, 0, 0, 0, 0.5f, 0, 0, 0, 1})
243 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {20.f, 20.f})
244 .BuildAsVector();
245 touch_source_->ScheduleCallback(std::move(events));
246 RunLoopUntilIdle();
247
248 ASSERT_TRUE(pointers.has_value());
249 ASSERT_EQ(pointers->size(), 1u); // MOVE
250 EXPECT_FLOAT_EQ((*pointers)[0].physical_x, 10.f);
251 EXPECT_FLOAT_EQ((*pointers)[0].physical_y, 10.f);
252}
253
254TEST_F(PointerDelegateTest, Coordinates_DownEventClampedToView) {
255 const float kSmallDiscrepancy = -0.00003f;
256
257 std::optional<std::vector<flutter::PointerData>> pointers;
258 pointer_delegate_->WatchLoop(
259 [&pointers](std::vector<flutter::PointerData> events) {
260 pointers = std::move(events);
261 });
262 RunLoopUntilIdle(); // Server gets watch call.
263
264 std::vector<fup_TouchEvent> events =
266 .AddTime(1111000u)
268 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, kSmallDiscrepancy})
269 .AddResult(
270 {.interaction = kIxnOne, .status = fup_TouchIxnStatus::GRANTED})
271 .BuildAsVector();
272 touch_source_->ScheduleCallback(std::move(events));
273 RunLoopUntilIdle();
274
275 ASSERT_TRUE(pointers.has_value());
276 ASSERT_EQ(pointers->size(), 2u);
277
278 const auto& add_event = (*pointers)[0];
279 EXPECT_FLOAT_EQ(add_event.physical_x, 10.f);
280 EXPECT_FLOAT_EQ(add_event.physical_y, kSmallDiscrepancy);
281
282 const auto& down_event = (*pointers)[1];
283 EXPECT_FLOAT_EQ(down_event.physical_x, 10.f);
284 EXPECT_EQ(down_event.physical_y, 0.f);
285}
286
287TEST_F(PointerDelegateTest, Protocol_FirstResponseIsEmpty) {
288 bool called = false;
289 pointer_delegate_->WatchLoop(
290 [&called](std::vector<flutter::PointerData> events) { called = true; });
291 RunLoopUntilIdle(); // Server gets Watch call.
292
293 EXPECT_FALSE(called); // No events yet received to forward to client.
294 // Server sees an initial "response" from client, which is empty, by contract.
295 const auto responses = touch_source_->UploadedResponses();
296 ASSERT_TRUE(responses.has_value());
297 ASSERT_EQ(responses->size(), 0u);
298}
299
300TEST_F(PointerDelegateTest, Protocol_ResponseMatchesEarlierEvents) {
301 std::optional<std::vector<flutter::PointerData>> pointers;
302 pointer_delegate_->WatchLoop(
303 [&pointers](std::vector<flutter::PointerData> events) {
304 pointers = std::move(events);
305 });
306 RunLoopUntilIdle(); // Server gets watch call.
307
308 // Fuchsia view parameter only. Empty response.
309 std::vector<fup_TouchEvent> events =
311 .AddTime(1111000u)
313 .BuildAsVector();
314
315 // Fuchsia ptr 1 ADD sample. Yes response.
318 .AddTime(1111000u)
320 .AddSample({.device_id = 0u, .pointer_id = 1u, .interaction_id = 3u},
321 fup_EventPhase::ADD, {10.f, 10.f})
322 .Build();
323 events.emplace_back(std::move(e1));
324
325 // Fuchsia ptr 2 ADD sample. Yes response.
326 fup_TouchEvent e2 =
328 .AddTime(1111000u)
329 .AddSample({.device_id = 0u, .pointer_id = 2u, .interaction_id = 3u},
330 fup_EventPhase::ADD, {5.f, 5.f})
331 .Build();
332 events.emplace_back(std::move(e2));
333
334 // Fuchsia ptr 3 ADD sample. Yes response.
335 fup_TouchEvent e3 =
337 .AddTime(1111000u)
338 .AddSample({.device_id = 0u, .pointer_id = 3u, .interaction_id = 3u},
339 fup_EventPhase::ADD, {1.f, 1.f})
340 .Build();
341 events.emplace_back(std::move(e3));
342 touch_source_->ScheduleCallback(std::move(events));
343 RunLoopUntilIdle();
344
345 const auto responses = touch_source_->UploadedResponses();
346 ASSERT_TRUE(responses.has_value());
347 ASSERT_EQ(responses.value().size(), 4u);
348 // Event 0 did not carry a sample, so no response.
349 EXPECT_FALSE(responses.value()[0].has_response_type());
350 // Events 1-3 had a sample, must have a response.
351 EXPECT_TRUE(responses.value()[1].has_response_type());
352 EXPECT_EQ(responses.value()[1].response_type(), fup_TouchResponseType::YES);
353 EXPECT_TRUE(responses.value()[2].has_response_type());
354 EXPECT_EQ(responses.value()[2].response_type(), fup_TouchResponseType::YES);
355 EXPECT_TRUE(responses.value()[3].has_response_type());
356 EXPECT_EQ(responses.value()[3].response_type(), fup_TouchResponseType::YES);
357}
358
359TEST_F(PointerDelegateTest, Protocol_LateGrant) {
360 std::optional<std::vector<flutter::PointerData>> pointers;
361 pointer_delegate_->WatchLoop(
362 [&pointers](std::vector<flutter::PointerData> events) {
363 pointers = std::move(events);
364 });
365 RunLoopUntilIdle(); // Server gets watch call.
366
367 // Fuchsia ADD, no grant result - buffer it.
368 std::vector<fup_TouchEvent> events =
370 .AddTime(1111000u)
372 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
373 .BuildAsVector();
374 touch_source_->ScheduleCallback(std::move(events));
375 RunLoopUntilIdle();
376
377 ASSERT_TRUE(pointers.has_value());
378 EXPECT_EQ(pointers.value().size(), 0u);
379 pointers = {};
380
381 // Fuchsia CHANGE, no grant result - buffer it.
382 events = TouchEventBuilder::New()
383 .AddTime(2222000u)
384 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
385 .BuildAsVector();
386 touch_source_->ScheduleCallback(std::move(events));
387 RunLoopUntilIdle();
388
389 ASSERT_TRUE(pointers.has_value());
390 EXPECT_EQ(pointers.value().size(), 0u);
391 pointers = {};
392
393 // Fuchsia result: ownership granted. Buffered pointers released.
394 events = TouchEventBuilder::New()
395 .AddTime(3333000u)
396 .AddResult({kIxnOne, fup_TouchIxnStatus::GRANTED})
397 .BuildAsVector();
398 touch_source_->ScheduleCallback(std::move(events));
399 RunLoopUntilIdle();
400
401 ASSERT_TRUE(pointers.has_value());
402 ASSERT_EQ(pointers.value().size(), 3u); // ADD - DOWN - MOVE
403 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kAdd);
404 EXPECT_EQ(pointers.value()[1].change, flutter::PointerData::Change::kDown);
405 EXPECT_EQ(pointers.value()[2].change, flutter::PointerData::Change::kMove);
406 pointers = {};
407
408 // Fuchsia CHANGE, grant result - release immediately.
409 events = TouchEventBuilder::New()
410 .AddTime(/* in nanoseconds */ 4444000u)
411 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
412 .BuildAsVector();
413 touch_source_->ScheduleCallback(std::move(events));
414 RunLoopUntilIdle();
415
416 ASSERT_TRUE(pointers.has_value());
417 ASSERT_EQ(pointers.value().size(), 1u);
418 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kMove);
419 EXPECT_EQ(pointers.value()[0].time_stamp, /* in microseconds */ 4444u);
420 pointers = {};
421}
422
423TEST_F(PointerDelegateTest, Protocol_LateGrantCombo) {
424 std::optional<std::vector<flutter::PointerData>> pointers;
425 pointer_delegate_->WatchLoop(
426 [&pointers](std::vector<flutter::PointerData> events) {
427 pointers = std::move(events);
428 });
429 RunLoopUntilIdle(); // Server gets watch call.
430
431 // Fuchsia ADD, no grant result - buffer it.
432 std::vector<fup_TouchEvent> events =
434 .AddTime(1111000u)
436 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
437 .BuildAsVector();
438 touch_source_->ScheduleCallback(std::move(events));
439 RunLoopUntilIdle();
440
441 ASSERT_TRUE(pointers.has_value());
442 EXPECT_EQ(pointers.value().size(), 0u);
443 pointers = {};
444
445 // Fuchsia CHANGE, no grant result - buffer it.
446 events = TouchEventBuilder::New()
447 .AddTime(2222000u)
448 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
449 .BuildAsVector();
450 touch_source_->ScheduleCallback(std::move(events));
451 RunLoopUntilIdle();
452
453 ASSERT_TRUE(pointers.has_value());
454 EXPECT_EQ(pointers.value().size(), 0u);
455 pointers = {};
456
457 // Fuchsia CHANGE, with grant result - release buffered events.
458 events = TouchEventBuilder::New()
459 .AddTime(3333000u)
460 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
461 .AddResult({kIxnOne, fup_TouchIxnStatus::GRANTED})
462 .BuildAsVector();
463 touch_source_->ScheduleCallback(std::move(events));
464 RunLoopUntilIdle();
465
466 ASSERT_TRUE(pointers.has_value());
467 ASSERT_EQ(pointers.value().size(), 4u); // ADD - DOWN - MOVE - MOVE
468 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kAdd);
469 EXPECT_EQ(pointers.value()[0].time_stamp, 1111u);
470 EXPECT_EQ(pointers.value()[1].change, flutter::PointerData::Change::kDown);
471 EXPECT_EQ(pointers.value()[1].time_stamp, 1111u);
472 EXPECT_EQ(pointers.value()[2].change, flutter::PointerData::Change::kMove);
473 EXPECT_EQ(pointers.value()[2].time_stamp, 2222u);
474 EXPECT_EQ(pointers.value()[3].change, flutter::PointerData::Change::kMove);
475 EXPECT_EQ(pointers.value()[3].time_stamp, 3333u);
476 pointers = {};
477}
478
479TEST_F(PointerDelegateTest, Protocol_EarlyGrant) {
480 std::optional<std::vector<flutter::PointerData>> pointers;
481 pointer_delegate_->WatchLoop(
482 [&pointers](std::vector<flutter::PointerData> events) {
483 pointers = std::move(events);
484 });
485 RunLoopUntilIdle(); // Server gets watch call.
486
487 // Fuchsia ADD, with grant result - release immediately.
488 std::vector<fup_TouchEvent> events =
490 .AddTime(1111000u)
492 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
493 .AddResult({kIxnOne, fup_TouchIxnStatus::GRANTED})
494 .BuildAsVector();
495 touch_source_->ScheduleCallback(std::move(events));
496 RunLoopUntilIdle();
497
498 ASSERT_TRUE(pointers.has_value());
499 ASSERT_EQ(pointers.value().size(), 2u);
500 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kAdd);
501 EXPECT_EQ(pointers.value()[1].change, flutter::PointerData::Change::kDown);
502 pointers = {};
503
504 // Fuchsia CHANGE, after grant result - release immediately.
505 events = TouchEventBuilder::New()
506 .AddTime(2222000u)
507 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
508 .BuildAsVector();
509 touch_source_->ScheduleCallback(std::move(events));
510 RunLoopUntilIdle();
511
512 ASSERT_TRUE(pointers.has_value());
513 ASSERT_EQ(pointers.value().size(), 1u);
514 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kMove);
515 pointers = {};
516}
517
518TEST_F(PointerDelegateTest, Protocol_LateDeny) {
519 std::optional<std::vector<flutter::PointerData>> pointers;
520 pointer_delegate_->WatchLoop(
521 [&pointers](std::vector<flutter::PointerData> events) {
522 pointers = std::move(events);
523 });
524 RunLoopUntilIdle(); // Server gets watch call.
525
526 // Fuchsia ADD, no grant result - buffer it.
527 std::vector<fup_TouchEvent> events =
529 .AddTime(1111000u)
531 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
532 .BuildAsVector();
533 touch_source_->ScheduleCallback(std::move(events));
534 RunLoopUntilIdle();
535
536 ASSERT_TRUE(pointers.has_value());
537 EXPECT_EQ(pointers.value().size(), 0u);
538 pointers = {};
539
540 // Fuchsia CHANGE, no grant result - buffer it.
541 events = TouchEventBuilder::New()
542 .AddTime(2222000u)
543 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
544 .BuildAsVector();
545 touch_source_->ScheduleCallback(std::move(events));
546 RunLoopUntilIdle();
547
548 ASSERT_TRUE(pointers.has_value());
549 EXPECT_EQ(pointers.value().size(), 0u);
550 pointers = {};
551
552 // Fuchsia result: ownership denied. Buffered pointers deleted.
553 events = TouchEventBuilder::New()
554 .AddTime(3333000u)
555 .AddResult({kIxnOne, fup_TouchIxnStatus::DENIED})
556 .BuildAsVector();
557 touch_source_->ScheduleCallback(std::move(events));
558 RunLoopUntilIdle();
559
560 ASSERT_TRUE(pointers.has_value());
561 ASSERT_EQ(pointers.value().size(), 0u); // Do not release to client!
562 pointers = {};
563}
564
565TEST_F(PointerDelegateTest, Protocol_LateDenyCombo) {
566 std::optional<std::vector<flutter::PointerData>> pointers;
567 pointer_delegate_->WatchLoop(
568 [&pointers](std::vector<flutter::PointerData> events) {
569 pointers = std::move(events);
570 });
571 RunLoopUntilIdle(); // Server gets watch call.
572
573 // Fuchsia ADD, no grant result - buffer it.
574 std::vector<fup_TouchEvent> events =
576 .AddTime(1111000u)
578 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
579 .BuildAsVector();
580 touch_source_->ScheduleCallback(std::move(events));
581 RunLoopUntilIdle();
582
583 ASSERT_TRUE(pointers.has_value());
584 EXPECT_EQ(pointers.value().size(), 0u);
585 pointers = {};
586
587 // Fuchsia CHANGE, no grant result - buffer it.
588 events = TouchEventBuilder::New()
589 .AddTime(2222000u)
590 .AddSample(kIxnOne, fup_EventPhase::CHANGE, {10.f, 10.f})
591 .BuildAsVector();
592 touch_source_->ScheduleCallback(std::move(events));
593 RunLoopUntilIdle();
594
595 ASSERT_TRUE(pointers.has_value());
596 EXPECT_EQ(pointers.value().size(), 0u);
597 pointers = {};
598
599 // Fuchsia result: ownership denied. Buffered pointers deleted.
600 events = TouchEventBuilder::New()
601 .AddTime(3333000u)
602 .AddSample(kIxnOne, fup_EventPhase::CANCEL, {10.f, 10.f})
603 .AddResult({kIxnOne, fup_TouchIxnStatus::DENIED})
604 .BuildAsVector();
605 touch_source_->ScheduleCallback(std::move(events));
606 RunLoopUntilIdle();
607
608 ASSERT_TRUE(pointers.has_value());
609 ASSERT_EQ(pointers.value().size(), 0u); // Do not release to client!
610 pointers = {};
611}
612
613TEST_F(PointerDelegateTest, Protocol_PointersAreIndependent) {
614 std::optional<std::vector<flutter::PointerData>> pointers;
615 pointer_delegate_->WatchLoop(
616 [&pointers](std::vector<flutter::PointerData> events) {
617 pointers = std::move(events);
618 });
619 RunLoopUntilIdle(); // Server gets watch call.
620
621 constexpr fup_TouchIxnId kIxnTwo = {
622 .device_id = 1u, .pointer_id = 2u, .interaction_id = 2u};
623
624 // Fuchsia ptr1 ADD and ptr2 ADD, no grant result for either - buffer them.
625 std::vector<fup_TouchEvent> events =
627 .AddTime(1111000u)
629 .AddSample(kIxnOne, fup_EventPhase::ADD, {10.f, 10.f})
630 .BuildAsVector();
631 fup_TouchEvent ptr2 =
633 .AddTime(1111000u)
634 .AddSample(kIxnTwo, fup_EventPhase::ADD, {15.f, 15.f})
635 .Build();
636 events.emplace_back(std::move(ptr2));
637 touch_source_->ScheduleCallback(std::move(events));
638 RunLoopUntilIdle();
639
640 ASSERT_TRUE(pointers.has_value());
641 EXPECT_EQ(pointers.value().size(), 0u);
642 pointers = {};
643
644 // Server grants win to pointer 2.
645 events = TouchEventBuilder::New()
646 .AddTime(2222000u)
647 .AddResult({kIxnTwo, fup_TouchIxnStatus::GRANTED})
648 .BuildAsVector();
649 touch_source_->ScheduleCallback(std::move(events));
650 RunLoopUntilIdle();
651
652 // Note: Fuchsia's device and pointer IDs (both 32 bit) are coerced together
653 // to fit in Flutter's 64-bit device ID. However, Flutter's pointer_identifier
654 // is not set by platform runner code - PointerDataCaptureConverter (PDPC)
655 // sets it.
656 ASSERT_TRUE(pointers.has_value());
657 ASSERT_EQ(pointers.value().size(), 2u);
658 EXPECT_EQ(pointers.value()[0].pointer_identifier, 0); // reserved for PDPC
659 EXPECT_EQ(pointers.value()[0].device, (int64_t)((1ul << 32) | 2u));
660 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kAdd);
661 EXPECT_EQ(pointers.value()[1].pointer_identifier, 0); // reserved for PDPC
662 EXPECT_EQ(pointers.value()[1].device, (int64_t)((1ul << 32) | 2u));
663 EXPECT_EQ(pointers.value()[1].change, flutter::PointerData::Change::kDown);
664 pointers = {};
665
666 // Server grants win to pointer 1.
667 events = TouchEventBuilder::New()
668 .AddTime(3333000u)
669 .AddResult({kIxnOne, fup_TouchIxnStatus::GRANTED})
670 .BuildAsVector();
671 touch_source_->ScheduleCallback(std::move(events));
672 RunLoopUntilIdle();
673
674 ASSERT_TRUE(pointers.has_value());
675 ASSERT_EQ(pointers.value().size(), 2u);
676 EXPECT_EQ(pointers.value()[0].pointer_identifier, 0); // reserved for PDPC
677 EXPECT_EQ(pointers.value()[0].device, (int64_t)((1ul << 32) | 1u));
678 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kAdd);
679 EXPECT_EQ(pointers.value()[1].pointer_identifier, 0); // reserved for PDPC
680 EXPECT_EQ(pointers.value()[1].device, (int64_t)((1ul << 32) | 1u));
681 EXPECT_EQ(pointers.value()[1].change, flutter::PointerData::Change::kDown);
682 pointers = {};
683}
684
685TEST_F(PointerDelegateTest, MouseWheel_TickBased) {
686 std::optional<std::vector<flutter::PointerData>> pointers;
687 pointer_delegate_->WatchLoop(
688 [&pointers](std::vector<flutter::PointerData> events) {
689 pointers = std::move(events);
690 });
691 RunLoopUntilIdle(); // Server gets watch call.
692
693 std::vector<fup_MouseEvent> events =
695 .AddTime(1111789u)
697 .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {0, 1},
699 .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2})
700 .BuildAsVector();
701 mouse_source_->ScheduleCallback(std::move(events));
702 RunLoopUntilIdle();
703
704 ASSERT_TRUE(pointers.has_value());
705 ASSERT_EQ(pointers.value().size(), 1u);
706 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover);
707 EXPECT_EQ(pointers.value()[0].signal_kind,
709 EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse);
710 EXPECT_EQ(pointers.value()[0].buttons, 0);
711 EXPECT_EQ(pointers.value()[0].scroll_delta_x, 0);
712 EXPECT_EQ(pointers.value()[0].scroll_delta_y, -20);
713 pointers = {};
714
715 // receive a horizontal scroll
716 events = MouseEventBuilder()
717 .AddTime(1111789u)
719 .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {1, 0},
721 .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2})
722 .BuildAsVector();
723 mouse_source_->ScheduleCallback(std::move(events));
724 RunLoopUntilIdle();
725
726 ASSERT_TRUE(pointers.has_value());
727 ASSERT_EQ(pointers.value().size(), 1u);
728 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover);
729 EXPECT_EQ(pointers.value()[0].signal_kind,
731 EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse);
732 EXPECT_EQ(pointers.value()[0].buttons, 0);
733 EXPECT_EQ(pointers.value()[0].scroll_delta_x, 20);
734 EXPECT_EQ(pointers.value()[0].scroll_delta_y, 0);
735 pointers = {};
736}
737
738TEST_F(PointerDelegateTest, MouseWheel_PixelBased) {
739 std::optional<std::vector<flutter::PointerData>> pointers;
740 pointer_delegate_->WatchLoop(
741 [&pointers](std::vector<flutter::PointerData> events) {
742 pointers = std::move(events);
743 });
744 RunLoopUntilIdle(); // Server gets watch call.
745
746 std::vector<fup_MouseEvent> events =
748 .AddTime(1111789u)
750 .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {0, 1}, {0, 120},
752 .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2})
753 .BuildAsVector();
754 mouse_source_->ScheduleCallback(std::move(events));
755 RunLoopUntilIdle();
756
757 ASSERT_TRUE(pointers.has_value());
758 ASSERT_EQ(pointers.value().size(), 1u);
759 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover);
760 EXPECT_EQ(pointers.value()[0].signal_kind,
762 EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse);
763 EXPECT_EQ(pointers.value()[0].buttons, 0);
764 EXPECT_EQ(pointers.value()[0].scroll_delta_x, 0);
765 EXPECT_EQ(pointers.value()[0].scroll_delta_y, -120);
766 pointers = {};
767
768 // receive a horizontal scroll
769 events = MouseEventBuilder()
770 .AddTime(1111789u)
772 .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {1, 0}, {120, 0},
774 .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2})
775 .BuildAsVector();
776 mouse_source_->ScheduleCallback(std::move(events));
777 RunLoopUntilIdle();
778
779 ASSERT_TRUE(pointers.has_value());
780 ASSERT_EQ(pointers.value().size(), 1u);
781 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover);
782 EXPECT_EQ(pointers.value()[0].signal_kind,
784 EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse);
785 EXPECT_EQ(pointers.value()[0].buttons, 0);
786 EXPECT_EQ(pointers.value()[0].scroll_delta_x, 120);
787 EXPECT_EQ(pointers.value()[0].scroll_delta_y, 0);
788 pointers = {};
789}
790
791TEST_F(PointerDelegateTest, MouseWheel_TouchpadPixelBased) {
792 std::optional<std::vector<flutter::PointerData>> pointers;
793 pointer_delegate_->WatchLoop(
794 [&pointers](std::vector<flutter::PointerData> events) {
795 pointers = std::move(events);
796 });
797 RunLoopUntilIdle(); // Server gets watch call.
798
799 std::vector<fup_MouseEvent> events =
801 .AddTime(1111789u)
803 .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {0, 1}, {0, 120},
805 .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2})
806 .BuildAsVector();
807 mouse_source_->ScheduleCallback(std::move(events));
808 RunLoopUntilIdle();
809
810 ASSERT_TRUE(pointers.has_value());
811 ASSERT_EQ(pointers.value().size(), 1u);
812 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover);
813 EXPECT_EQ(pointers.value()[0].signal_kind,
815 EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse);
816 EXPECT_EQ(pointers.value()[0].buttons, 0);
817 EXPECT_EQ(pointers.value()[0].scroll_delta_x, 0);
818 EXPECT_EQ(pointers.value()[0].scroll_delta_y, -120);
819 pointers = {};
820
821 // receive a horizontal scroll
822 events = MouseEventBuilder()
823 .AddTime(1111789u)
825 .AddSample(kMouseDeviceId, {10.f, 10.f}, {}, {1, 0}, {120, 0},
827 .AddMouseDeviceInfo(kMouseDeviceId, {0, 1, 2})
828 .BuildAsVector();
829 mouse_source_->ScheduleCallback(std::move(events));
830 RunLoopUntilIdle();
831
832 ASSERT_TRUE(pointers.has_value());
833 ASSERT_EQ(pointers.value().size(), 1u);
834 EXPECT_EQ(pointers.value()[0].change, flutter::PointerData::Change::kHover);
835 EXPECT_EQ(pointers.value()[0].signal_kind,
837 EXPECT_EQ(pointers.value()[0].kind, flutter::PointerData::DeviceKind::kMouse);
838 EXPECT_EQ(pointers.value()[0].buttons, 0);
839 EXPECT_EQ(pointers.value()[0].scroll_delta_x, 120);
840 EXPECT_EQ(pointers.value()[0].scroll_delta_y, 0);
841 pointers = {};
842}
843
844} // namespace flutter_runner::testing
float e1
MouseEventBuilder & AddSample(uint32_t id, std::array< float, 2 > position, std::vector< uint8_t > pressed_buttons, std::array< int64_t, 2 > scroll, std::array< int64_t, 2 > scroll_in_physical_pixel, bool is_precision_scroll)
MouseEventBuilder & AddViewParameters(std::array< std::array< float, 2 >, 2 > view, std::array< std::array< float, 2 >, 2 > viewport, std::array< float, 9 > transform)
MouseEventBuilder & AddTime(zx_time_t time)
std::unique_ptr< flutter_runner::PointerDelegate > pointer_delegate_
std::vector< fuchsia::ui::pointer::TouchEvent > BuildAsVector()
TouchEventBuilder & AddViewParameters(std::array< std::array< float, 2 >, 2 > view, std::array< std::array< float, 2 >, 2 > viewport, std::array< float, 9 > transform)
TouchEventBuilder & AddSample(fuchsia::ui::pointer::TouchInteractionId id, fuchsia::ui::pointer::EventPhase phase, std::array< float, 2 > position)
TouchEventBuilder & AddTime(zx_time_t time)
TouchEventBuilder & AddResult(fuchsia::ui::pointer::TouchInteractionResult result)
def Build(configs, env, options)
Definition: build.py:232
fuchsia::ui::pointer::TouchEvent fup_TouchEvent
fuchsia::ui::pointer::TouchResponse fup_TouchResponse
fuchsia::ui::pointer::EventPhase fup_EventPhase
fuchsia::ui::pointer::TouchResponseType fup_TouchResponseType
fuchsia::ui::pointer::TouchPointerSample fup_TouchPointerSample
fuchsia::ui::pointer::TouchInteractionStatus fup_TouchIxnStatus
constexpr std::array< int64_t, 2 > kNoScrollInPhysicalPixelDelta
fuchsia::ui::pointer::TouchInteractionResult fup_TouchIxnResult
fuchsia::ui::pointer::MouseEvent fup_MouseEvent
constexpr std::array< std::array< float, 2 >, 2 > kRect
constexpr std::array< float, 9 > kIdentity
fuchsia::ui::pointer::ViewParameters fup_ViewParameters
TEST_F(FocusDelegateTest, WatchCallbackSeries)
constexpr fup_TouchIxnId kIxnOne
fuchsia::ui::pointer::TouchInteractionId fup_TouchIxnId
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678