|
4 | 4 |
|
5 | 5 | #define FML_USED_ON_EMBEDDER |
6 | 6 |
|
7 | | -#include "flutter/lib/ui/window/platform_configuration.h" |
8 | | - |
| 7 | +#include <cstddef> |
9 | 8 | #include <memory> |
10 | 9 |
|
11 | 10 | #include "flutter/common/task_runners.h" |
12 | 11 | #include "flutter/fml/synchronization/waitable_event.h" |
13 | 12 | #include "flutter/lib/ui/painting/vertices.h" |
| 13 | +#include "flutter/lib/ui/window/platform_configuration.h" |
14 | 14 | #include "flutter/runtime/dart_vm.h" |
15 | 15 | #include "flutter/shell/common/shell_test.h" |
16 | 16 | #include "flutter/shell/common/thread_host.h" |
17 | 17 | #include "flutter/testing/testing.h" |
| 18 | +#include "gmock/gmock.h" |
| 19 | +#include "googletest/googletest/include/gtest/gtest.h" |
| 20 | +#include "third_party/dart/runtime/include/dart_api.h" |
| 21 | +#include "tonic/converter/dart_converter.h" |
18 | 22 |
|
19 | 23 | namespace flutter { |
20 | 24 | namespace testing { |
@@ -332,5 +336,89 @@ TEST_F(PlatformConfigurationTest, SetDartPerformanceMode) { |
332 | 336 | DestroyShell(std::move(shell), task_runners); |
333 | 337 | } |
334 | 338 |
|
| 339 | +TEST_F(PlatformConfigurationTest, BeginFrameMonotonic) { |
| 340 | + auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>(); |
| 341 | + |
| 342 | + PlatformConfiguration* platform; |
| 343 | + |
| 344 | + // Called at the load time and will be in an Dart isolate. |
| 345 | + auto nativeValidateConfiguration = [message_latch, |
| 346 | + &platform](Dart_NativeArguments args) { |
| 347 | + platform = UIDartState::Current()->platform_configuration(); |
| 348 | + |
| 349 | + // Hijacks the `_begin_frame` in hooks.dart so we can get a callback for |
| 350 | + // validation. |
| 351 | + auto field = |
| 352 | + Dart_GetField(Dart_RootLibrary(), tonic::ToDart("_beginFrameHijack")); |
| 353 | + platform->begin_frame_.Clear(); |
| 354 | + platform->begin_frame_.Set(UIDartState::Current(), field); |
| 355 | + |
| 356 | + message_latch->Signal(); |
| 357 | + }; |
| 358 | + AddNativeCallback("ValidateConfiguration", |
| 359 | + CREATE_NATIVE_ENTRY(nativeValidateConfiguration)); |
| 360 | + |
| 361 | + std::vector<int64_t> frame_times; |
| 362 | + std::vector<uint64_t> frame_numbers; |
| 363 | + |
| 364 | + auto frame_latch = std::make_shared<fml::AutoResetWaitableEvent>(); |
| 365 | + |
| 366 | + // Called for each `_begin_frame` that is hijacked. |
| 367 | + auto nativeBeginFrame = [frame_latch, &frame_times, |
| 368 | + &frame_numbers](Dart_NativeArguments args) { |
| 369 | + int64_t microseconds; |
| 370 | + uint64_t frame_number; |
| 371 | + Dart_IntegerToInt64(Dart_GetNativeArgument(args, 0), µseconds); |
| 372 | + Dart_IntegerToUint64(Dart_GetNativeArgument(args, 1), &frame_number); |
| 373 | + |
| 374 | + frame_times.push_back(microseconds); |
| 375 | + frame_numbers.push_back(frame_number); |
| 376 | + |
| 377 | + if (frame_times.size() == 3) { |
| 378 | + frame_latch->Signal(); |
| 379 | + } |
| 380 | + }; |
| 381 | + AddNativeCallback("BeginFrame", CREATE_NATIVE_ENTRY(nativeBeginFrame)); |
| 382 | + |
| 383 | + Settings settings = CreateSettingsForFixture(); |
| 384 | + TaskRunners task_runners("test", // label |
| 385 | + GetCurrentTaskRunner(), // platform |
| 386 | + CreateNewThread(), // raster |
| 387 | + CreateNewThread(), // ui |
| 388 | + CreateNewThread() // io |
| 389 | + ); |
| 390 | + |
| 391 | + std::unique_ptr<Shell> shell = CreateShell(settings, task_runners); |
| 392 | + ASSERT_TRUE(shell->IsSetup()); |
| 393 | + |
| 394 | + auto configuration = RunConfiguration::InferFromSettings(settings); |
| 395 | + configuration.SetEntrypoint("validateConfiguration"); |
| 396 | + |
| 397 | + shell->RunEngine(std::move(configuration), [&](auto result) { |
| 398 | + ASSERT_EQ(result, Engine::RunStatus::Success); |
| 399 | + }); |
| 400 | + |
| 401 | + // Wait for `nativeValidateConfiguration` to get called. |
| 402 | + message_latch->Wait(); |
| 403 | + |
| 404 | + fml::TaskRunner::RunNowOrPostTask( |
| 405 | + shell->GetTaskRunners().GetUITaskRunner(), [platform]() { |
| 406 | + auto offset = fml::TimeDelta::FromMilliseconds(10); |
| 407 | + auto zero = fml::TimePoint(); |
| 408 | + auto one = zero + offset; |
| 409 | + auto two = one + offset; |
| 410 | + |
| 411 | + platform->BeginFrame(zero, 1); |
| 412 | + platform->BeginFrame(two, 2); |
| 413 | + platform->BeginFrame(one, 3); |
| 414 | + }); |
| 415 | + |
| 416 | + frame_latch->Wait(); |
| 417 | + |
| 418 | + ASSERT_THAT(frame_times, ::testing::ElementsAre(0, 20000, 20000)); |
| 419 | + ASSERT_THAT(frame_numbers, ::testing::ElementsAre(1, 2, 3)); |
| 420 | + DestroyShell(std::move(shell), task_runners); |
| 421 | +} |
| 422 | + |
335 | 423 | } // namespace testing |
336 | 424 | } // namespace flutter |
0 commit comments