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// FLUTTER_NOLINT
5
6#define FML_USED_ON_EMBEDDER
7
8#include <functional>
9#include <future>
10#include <memory>
11
12#include "flutter/shell/common/animator.h"
13#include "flutter/shell/common/shell_test.h"
14#include "flutter/shell/common/shell_test_platform_view.h"
15#include "flutter/testing/testing.h"
16#include "gtest/gtest.h"
17
18namespace flutter {
19namespace testing {
20
21TEST_F(ShellTest, VSyncTargetTime) {
22 // Add native callbacks to listen for window.onBeginFrame
23 int64_t target_time;
24 fml::AutoResetWaitableEvent on_target_time_latch;
25 auto nativeOnBeginFrame = [&on_target_time_latch,
26 &target_time](Dart_NativeArguments args) {
27 Dart_Handle exception = nullptr;
28 target_time =
29 tonic::DartConverter<int64_t>::FromArguments(args, 0, exception);
30 on_target_time_latch.Signal();
31 };
32 AddNativeCallback("NativeOnBeginFrame",
33 CREATE_NATIVE_ENTRY(nativeOnBeginFrame));
34
35 // Create all te prerequisites for a shell.
36 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
37 auto settings = CreateSettingsForFixture();
38
39 std::unique_ptr<Shell> shell;
40
41 TaskRunners task_runners = GetTaskRunnersForFixture();
42 // this is not used as we are not using simulated events.
43 const auto vsync_clock = std::make_shared<ShellTestVsyncClock>();
44 CreateVsyncWaiter create_vsync_waiter = [&]() {
45 return static_cast<std::unique_ptr<VsyncWaiter>>(
46 std::make_unique<ConstantFiringVsyncWaiter>(task_runners));
47 };
48
49 // create a shell with a constant firing vsync waiter.
50 auto platform_task = std::async(std::launch::async, [&]() {
51 fml::MessageLoop::EnsureInitializedForCurrentThread();
52
53 shell = Shell::Create(
54 task_runners, settings,
55 [vsync_clock, &create_vsync_waiter](Shell& shell) {
56 return ShellTestPlatformView::Create(
57 shell, shell.GetTaskRunners(), vsync_clock,
58 std::move(create_vsync_waiter),
59 ShellTestPlatformView::BackendType::kDefaultBackend, nullptr);
60 },
61 [](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
62 ASSERT_TRUE(DartVMRef::IsInstanceRunning());
63
64 auto configuration = RunConfiguration::InferFromSettings(settings);
65 ASSERT_TRUE(configuration.IsValid());
66 configuration.SetEntrypoint("onBeginFrameMain");
67
68 RunEngine(shell.get(), std::move(configuration));
69 });
70 platform_task.wait();
71
72 // schedule a frame to trigger window.onBeginFrame
73 fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetUITaskRunner(),
74 [engine = shell->GetEngine()]() {
75 if (engine) {
76 // this implies we can re-use the last
77 // frame to trigger begin frame rather
78 // than re-generating the layer tree.
79 engine->ScheduleFrame(true);
80 }
81 });
82
83 on_target_time_latch.Wait();
84 const auto vsync_waiter_target_time =
85 ConstantFiringVsyncWaiter::frame_target_time;
86 ASSERT_EQ(vsync_waiter_target_time.ToEpochDelta().ToMicroseconds(),
87 target_time);
88
89 // validate that the latest target time has also been updated.
90 ASSERT_EQ(GetLatestFrameTargetTime(shell.get()), vsync_waiter_target_time);
91
92 // teardown.
93 DestroyShell(std::move(shell), std::move(task_runners));
94 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
95}
96
97} // namespace testing
98} // namespace flutter
99