|
10 | 10 | #include <jsi/jsi.h> |
11 | 11 | #include <react/renderer/core/EventLogger.h> |
12 | 12 | #include <react/renderer/core/EventPipe.h> |
| 13 | +#include <react/renderer/core/EventQueue.h> |
13 | 14 | #include <react/renderer/core/EventQueueProcessor.h> |
14 | 15 | #include <react/renderer/core/EventTarget.h> |
15 | 16 | #include <react/renderer/core/InstanceHandle.h> |
16 | 17 | #include <react/renderer/core/ShadowNodeFamily.h> |
17 | 18 | #include <react/renderer/core/StatePipe.h> |
18 | 19 | #include <react/renderer/core/ValueFactoryEventPayload.h> |
| 20 | +#include <react/renderer/runtimescheduler/RuntimeScheduler.h> |
19 | 21 |
|
20 | 22 | #include <memory> |
21 | 23 | #include <stdexcept> |
@@ -65,6 +67,81 @@ class EventQueueProcessorTest : public testing::Test { |
65 | 67 | ValueFactory dummyValueFactory_; |
66 | 68 | }; |
67 | 69 |
|
| 70 | +class NoopEventBeat final : public EventBeat { |
| 71 | + public: |
| 72 | + explicit NoopEventBeat(RuntimeScheduler& runtimeScheduler) |
| 73 | + : EventBeat(std::make_shared<EventBeat::OwnerBox>(), runtimeScheduler) {} |
| 74 | + |
| 75 | + void request() const override {} |
| 76 | + void requestSynchronous(Tag /*tag*/) const override {} |
| 77 | +}; |
| 78 | + |
| 79 | +class TestEventQueue final : public EventQueue { |
| 80 | + public: |
| 81 | + using EventQueue::EventQueue; |
| 82 | + |
| 83 | + void flush(jsi::Runtime& runtime) const { |
| 84 | + flushEvents(runtime); |
| 85 | + } |
| 86 | +}; |
| 87 | + |
| 88 | +class EventQueueCoalescingTest : public testing::Test { |
| 89 | + protected: |
| 90 | + void SetUp() override { |
| 91 | + runtime_ = facebook::hermes::makeHermesRuntime(); |
| 92 | + |
| 93 | + RuntimeExecutor runtimeExecutor = |
| 94 | + [](std::function<void(facebook::jsi::Runtime&)>&& /*callback*/) {}; |
| 95 | + runtimeScheduler_ = std::make_unique<RuntimeScheduler>(runtimeExecutor); |
| 96 | + } |
| 97 | + |
| 98 | + std::unique_ptr<TestEventQueue> createEventQueue() { |
| 99 | + auto eventPipe = [this]( |
| 100 | + jsi::Runtime& /*runtime*/, |
| 101 | + const EventTarget* /*eventTarget*/, |
| 102 | + const std::string& type, |
| 103 | + ReactEventPriority /*priority*/, |
| 104 | + const EventPayload& /*payload*/, |
| 105 | + HighResTimeStamp eventTimestamp) { |
| 106 | + eventTypes_.push_back(type); |
| 107 | + eventTimestamps_.push_back(eventTimestamp); |
| 108 | + }; |
| 109 | + |
| 110 | + auto dummyEventPipeConclusion = [](jsi::Runtime&) {}; |
| 111 | + auto dummyStatePipe = [](const StateUpdate&) {}; |
| 112 | + auto mockEventLogger = std::make_shared<MockEventLogger>(); |
| 113 | + |
| 114 | + return std::make_unique<TestEventQueue>( |
| 115 | + EventQueueProcessor( |
| 116 | + eventPipe, |
| 117 | + dummyEventPipeConclusion, |
| 118 | + dummyStatePipe, |
| 119 | + mockEventLogger), |
| 120 | + std::make_unique<NoopEventBeat>(*runtimeScheduler_)); |
| 121 | + } |
| 122 | + |
| 123 | + RawEvent createRawEvent( |
| 124 | + std::string type, |
| 125 | + RawEvent::CoalescingKey coalescingKey, |
| 126 | + HighResTimeStamp eventTimestamp) { |
| 127 | + return RawEvent( |
| 128 | + std::move(type), |
| 129 | + std::make_shared<ValueFactoryEventPayload>(dummyValueFactory_), |
| 130 | + nullptr, |
| 131 | + {}, |
| 132 | + RawEvent::Category::Continuous, |
| 133 | + false, |
| 134 | + eventTimestamp, |
| 135 | + coalescingKey); |
| 136 | + } |
| 137 | + |
| 138 | + std::unique_ptr<facebook::hermes::HermesRuntime> runtime_; |
| 139 | + std::unique_ptr<RuntimeScheduler> runtimeScheduler_; |
| 140 | + std::vector<std::string> eventTypes_; |
| 141 | + std::vector<HighResTimeStamp> eventTimestamps_; |
| 142 | + ValueFactory dummyValueFactory_; |
| 143 | +}; |
| 144 | + |
68 | 145 | TEST_F(EventQueueProcessorTest, singleUnspecifiedEvent) { |
69 | 146 | eventProcessor_->flushEvents( |
70 | 147 | *runtime_, |
@@ -205,4 +282,95 @@ TEST_F(EventQueueProcessorTest, releasesEventTargetsWhenDispatchThrows) { |
205 | 282 | EXPECT_TRUE(eventTarget->getInstanceHandle(*runtime_).isNull()); |
206 | 283 | } |
207 | 284 |
|
| 285 | +TEST_F(EventQueueCoalescingTest, uniqueEventsWithSameCoalescingKeyCoalesce) { |
| 286 | + auto eventQueue = createEventQueue(); |
| 287 | + auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1); |
| 288 | + auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2); |
| 289 | + |
| 290 | + eventQueue->enqueueUniqueEvent( |
| 291 | + createRawEvent("topChange", 3, firstTimestamp)); |
| 292 | + eventQueue->enqueueUniqueEvent( |
| 293 | + createRawEvent("topChange", 3, secondTimestamp)); |
| 294 | + |
| 295 | + eventQueue->flush(*runtime_); |
| 296 | + |
| 297 | + ASSERT_EQ(eventTypes_.size(), 1); |
| 298 | + EXPECT_EQ(eventTypes_[0], "topChange"); |
| 299 | + EXPECT_EQ(eventTimestamps_[0], secondTimestamp); |
| 300 | +} |
| 301 | + |
| 302 | +TEST_F( |
| 303 | + EventQueueCoalescingTest, |
| 304 | + defaultCoalescingKeysPreserveExistingBehavior) { |
| 305 | + auto eventQueue = createEventQueue(); |
| 306 | + auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1); |
| 307 | + auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2); |
| 308 | + |
| 309 | + eventQueue->enqueueUniqueEvent(createRawEvent( |
| 310 | + "topChange", RawEvent::DefaultCoalescingKey, firstTimestamp)); |
| 311 | + eventQueue->enqueueUniqueEvent(createRawEvent( |
| 312 | + "topChange", RawEvent::DefaultCoalescingKey, secondTimestamp)); |
| 313 | + eventQueue->flush(*runtime_); |
| 314 | + |
| 315 | + ASSERT_EQ(eventTimestamps_.size(), 1); |
| 316 | + EXPECT_EQ(eventTimestamps_[0], secondTimestamp); |
| 317 | +} |
| 318 | + |
| 319 | +TEST_F(EventQueueCoalescingTest, defaultCoalescingKeyDoesNotMatchCustomKey) { |
| 320 | + auto eventQueue = createEventQueue(); |
| 321 | + auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1); |
| 322 | + auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2); |
| 323 | + |
| 324 | + eventQueue->enqueueUniqueEvent(createRawEvent( |
| 325 | + "topChange", RawEvent::DefaultCoalescingKey, firstTimestamp)); |
| 326 | + eventQueue->enqueueUniqueEvent( |
| 327 | + createRawEvent("topChange", 3, secondTimestamp)); |
| 328 | + eventQueue->flush(*runtime_); |
| 329 | + |
| 330 | + ASSERT_EQ(eventTimestamps_.size(), 2); |
| 331 | + EXPECT_EQ(eventTimestamps_[0], firstTimestamp); |
| 332 | + EXPECT_EQ(eventTimestamps_[1], secondTimestamp); |
| 333 | +} |
| 334 | + |
| 335 | +TEST_F(EventQueueCoalescingTest, differentKeysPreserveEventOrder) { |
| 336 | + auto eventQueue = createEventQueue(); |
| 337 | + auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1); |
| 338 | + auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2); |
| 339 | + auto thirdTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(3); |
| 340 | + |
| 341 | + eventQueue->enqueueUniqueEvent( |
| 342 | + createRawEvent("topChange", 3, firstTimestamp)); |
| 343 | + eventQueue->enqueueUniqueEvent( |
| 344 | + createRawEvent("topChange", 4, secondTimestamp)); |
| 345 | + eventQueue->enqueueUniqueEvent( |
| 346 | + createRawEvent("topChange", 3, thirdTimestamp)); |
| 347 | + eventQueue->flush(*runtime_); |
| 348 | + |
| 349 | + ASSERT_EQ(eventTimestamps_.size(), 3); |
| 350 | + EXPECT_EQ(eventTimestamps_[0], firstTimestamp); |
| 351 | + EXPECT_EQ(eventTimestamps_[1], secondTimestamp); |
| 352 | + EXPECT_EQ(eventTimestamps_[2], thirdTimestamp); |
| 353 | +} |
| 354 | + |
| 355 | +TEST_F( |
| 356 | + EventQueueCoalescingTest, |
| 357 | + uniqueEventsWithDifferentCoalescingKeysDoNotCoalesce) { |
| 358 | + auto eventQueue = createEventQueue(); |
| 359 | + auto firstTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(1); |
| 360 | + auto secondTimestamp = HighResTimeStamp::fromDOMHighResTimeStamp(2); |
| 361 | + |
| 362 | + eventQueue->enqueueUniqueEvent( |
| 363 | + createRawEvent("topChange", 3, firstTimestamp)); |
| 364 | + eventQueue->enqueueUniqueEvent( |
| 365 | + createRawEvent("topChange", 4, secondTimestamp)); |
| 366 | + |
| 367 | + eventQueue->flush(*runtime_); |
| 368 | + |
| 369 | + ASSERT_EQ(eventTypes_.size(), 2); |
| 370 | + EXPECT_EQ(eventTypes_[0], "topChange"); |
| 371 | + EXPECT_EQ(eventTypes_[1], "topChange"); |
| 372 | + EXPECT_EQ(eventTimestamps_[0], firstTimestamp); |
| 373 | + EXPECT_EQ(eventTimestamps_[1], secondTimestamp); |
| 374 | +} |
| 375 | + |
208 | 376 | } // namespace facebook::react |
0 commit comments