Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
fl_task_runner_test.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 "gtest/gtest.h"
6
11
12#include <vector>
13
14// MOCK_ENGINE_PROC is leaky by design.
15// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
16
17// Test fixture that provides an engine (with a real binary messenger) and a
18// main loop via the shared LinuxTest base.
20
21// Builds a FlutterTask carrying the given identifier. The runner pointer is
22// unused as the engine's RunTask is mocked out in these tests.
23static FlutterTask make_task(uint64_t id) {
24 FlutterTask task = {};
25 task.runner = nullptr;
26 task.task = id;
27 return task;
28}
29
30// The default maximum time pump_main_loop_until waits for its predicate to
31// become true before giving up.
32static constexpr gint64 kDefaultPumpTimeoutMicros = 10 * G_USEC_PER_SEC;
33
34// Iterates the default main context until either the timeout elapses or the
35// predicate returns true.
36template <typename Predicate>
37static void pump_main_loop_until(Predicate predicate,
38 gint64 timeout = kDefaultPumpTimeoutMicros) {
39 gint64 deadline = g_get_monotonic_time() + timeout;
40 while (!predicate() && g_get_monotonic_time() < deadline) {
41 g_main_context_iteration(nullptr, TRUE);
42 }
43}
44
45// A posted task is executed on the main loop and forwarded to the engine.
46TEST_F(FlTaskRunnerTest, PostTaskExecutedOnMainLoop) {
47 g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
48
49 std::vector<uint64_t> executed;
51 RunTask, ([&executed](auto engine, const FlutterTask* task) {
52 executed.push_back(task->task);
53 return kSuccess;
54 }));
55
56 // A target time in the past means the task is due immediately.
58
59 pump_main_loop_until([&executed]() { return !executed.empty(); });
60
61 ASSERT_EQ(executed.size(), 1u);
62 EXPECT_EQ(executed[0], 42u);
63}
64
65// Multiple posted tasks are all executed.
66TEST_F(FlTaskRunnerTest, MultipleTasksExecuted) {
67 g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
68
69 std::vector<uint64_t> executed;
71 RunTask, ([&executed](auto engine, const FlutterTask* task) {
72 executed.push_back(task->task);
73 return kSuccess;
74 }));
75
79
80 pump_main_loop_until([&executed]() { return executed.size() >= 3; });
81
82 ASSERT_EQ(executed.size(), 3u);
83 EXPECT_EQ(executed[0], 1u);
84 EXPECT_EQ(executed[1], 2u);
85 EXPECT_EQ(executed[2], 3u);
86}
87
88// A task whose target time has not yet arrived is still eventually executed.
89TEST_F(FlTaskRunnerTest, DelayedTaskExecuted) {
90 g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
91
92 gboolean executed = FALSE;
94 RunTask, ([&executed](auto engine, const FlutterTask* task) {
95 executed = TRUE;
96 return kSuccess;
97 }));
98
99 // Schedule roughly 20ms into the future (target time is in nanoseconds).
100 uint64_t target_time_nanos =
101 (g_get_monotonic_time() + 20 * G_TIME_SPAN_MILLISECOND) * 1000;
103 target_time_nanos);
104
105 // Not run before its time.
106 EXPECT_FALSE(executed);
107
108 pump_main_loop_until([&executed]() { return executed; });
109
110 EXPECT_TRUE(executed);
111}
112
113// fl_task_runner_wait runs a task that is already due without needing the main
114// loop.
115TEST_F(FlTaskRunnerTest, WaitRunsExpiredTask) {
116 g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
117
118 std::vector<uint64_t> executed;
120 RunTask, ([&executed](auto engine, const FlutterTask* task) {
121 executed.push_back(task->task);
122 return kSuccess;
123 }));
124
125 fl_task_runner_post_flutter_task(task_runner, make_task(99), 0);
126
127 // The task is already due, so wait processes it and returns immediately.
128 fl_task_runner_wait(task_runner, g_get_monotonic_time() + G_USEC_PER_SEC);
129
130 ASSERT_EQ(executed.size(), 1u);
131 EXPECT_EQ(executed[0], 99u);
132}
133
135 FlTaskRunner* task_runner;
136 gint done;
137};
138
139// Repeatedly interrupts fl_task_runner_wait until the main thread signals that
140// the wait has returned. Repeating guards against the interrupt arriving before
141// the wait has started blocking.
142static gpointer stop_wait_thread(gpointer user_data) {
143 StopWaitData* data = static_cast<StopWaitData*>(user_data);
144 while (g_atomic_int_get(&data->done) == 0) {
145 g_usleep(10 * G_TIME_SPAN_MILLISECOND);
146 fl_task_runner_stop_wait(data->task_runner);
147 }
148 return nullptr;
149}
150
151// fl_task_runner_stop_wait causes a blocking fl_task_runner_wait to return.
152TEST_F(FlTaskRunnerTest, StopWaitInterruptsWait) {
153 g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
154
155 StopWaitData data = {task_runner, 0};
156 GThread* thread = g_thread_new("stop-wait", stop_wait_thread, &data);
157
158 // With no tasks scheduled this would otherwise block until the far-future
159 // expiry time.
160 gint64 start = g_get_monotonic_time();
161 fl_task_runner_wait(task_runner, start + 30 * G_USEC_PER_SEC);
162 gint64 elapsed = g_get_monotonic_time() - start;
163
164 g_atomic_int_set(&data.done, 1);
165 g_thread_join(thread);
166
167 // The wait returned because of stop_wait, well before the 30s expiry.
168 EXPECT_LT(elapsed, 5 * G_USEC_PER_SEC);
169}
170
171// Calling fl_task_runner_stop_wait when no wait is in progress is a no-op: the
172// runner still works and a later wait runs a due task (the stray signal is not
173// "remembered" to interrupt the subsequent wait).
174TEST_F(FlTaskRunnerTest, StopWaitWithoutWaitIsNoOp) {
175 g_autoptr(FlTaskRunner) task_runner = fl_task_runner_new(engine);
176
177 std::vector<uint64_t> executed;
179 RunTask, ([&executed](auto engine, const FlutterTask* task) {
180 executed.push_back(task->task);
181 return kSuccess;
182 }));
183
184 // No wait is in progress, so this should do nothing.
185 fl_task_runner_stop_wait(task_runner);
186
187 // The runner is still functional: a due task is run by a subsequent wait.
188 fl_task_runner_post_flutter_task(task_runner, make_task(123), 0);
189 fl_task_runner_wait(task_runner, g_get_monotonic_time() + G_USEC_PER_SEC);
190
191 ASSERT_EQ(executed.size(), 1u);
192 EXPECT_EQ(executed[0], 123u);
193}
194
195// NOLINTEND(clang-analyzer-core.StackAddressEscape)
@ kSuccess
Definition embedder.h:73
FlutterEngine engine
Definition main.cc:84
g_autoptr(FlEngine) engine
return TRUE
fl_task_runner_stop_wait(self->task_runner)
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition fl_engine.cc:920
FlTaskRunner * fl_task_runner_new(FlEngine *engine)
void fl_task_runner_post_flutter_task(FlTaskRunner *self, FlutterTask task, uint64_t target_time_nanos)
void fl_task_runner_wait(FlTaskRunner *self, gint64 expiry_time)
static void pump_main_loop_until(Predicate predicate, gint64 timeout=kDefaultPumpTimeoutMicros)
TEST_F(FlTaskRunnerTest, PostTaskExecutedOnMainLoop)
static FlutterTask make_task(uint64_t id)
static constexpr gint64 kDefaultPumpTimeoutMicros
static gpointer stop_wait_thread(gpointer user_data)
#define MOCK_ENGINE_PROC(proc, mock_impl)
FlutterEngineRunTaskFnPtr RunTask
Definition embedder.h:3797
FlutterTaskRunner runner
Definition embedder.h:1904
uint64_t task
Definition embedder.h:1905
FlTaskRunner * task_runner
const size_t start
const uintptr_t id