Flutter Engine
The Flutter Engine
shell_fuchsia_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// A fuchsia-specific shell test.
6//
7// This test is only supposed to be ran on Fuchsia OS, as it exercises
8// Fuchsia-specific functionality for which no equivalent exists elsewhere.
9
10#define FML_USED_ON_EMBEDDER
11
12#include <time.h>
13#include <unistd.h>
14
15#include <memory>
16
17#include <fuchsia/intl/cpp/fidl.h>
18#include <fuchsia/settings/cpp/fidl.h>
19#include <lib/sys/cpp/component_context.h>
20
21#include "flutter/fml/logging.h"
22#include "flutter/fml/synchronization/count_down_latch.h"
23#include "flutter/runtime/dart_vm.h"
24#include "flutter/shell/common/shell_test.h"
25
26namespace flutter {
27namespace testing {
28
29using fuchsia::intl::TimeZoneId;
30using fuchsia::settings::Intl_Set_Result;
31using fuchsia::settings::IntlSettings;
32
34 protected:
36 : ctx_(sys::ComponentContext::CreateAndServeOutgoingDirectory()),
37 intl_() {
38 ctx_->svc()->Connect(intl_.NewRequest());
39 }
40
42 // Restore the time zone that matches that of the test harness. This is
43 // the default.
44 // TODO(https://fxbug.dev/110019): This crashes right now.
45
46 // const std::string local_timezone = GetLocalTimezone();
47 // SetTimezone(local_timezone);
48 // AssertTimezone(local_timezone, GetSettings());
49 }
50
51 // Gets the international settings from this Fuchsia realm.
52 IntlSettings GetSettings() {
53 IntlSettings settings;
54 zx_status_t status = intl_->Watch(&settings);
55 EXPECT_EQ(status, ZX_OK);
56 return settings;
57 }
58
59 // Sets the timezone of this Fuchsia realm to `timezone_name`.
60 void SetTimezone(const std::string& timezone_name) {
61 fuchsia::settings::IntlSettings settings;
62 settings.set_time_zone_id(TimeZoneId{.id = timezone_name});
63 Intl_Set_Result result;
64 zx_status_t status = intl_->Set(std::move(settings), &result);
65 ASSERT_EQ(status, ZX_OK);
66 }
67
68 std::string GetLocalTimezone() {
69 const time_t timestamp = time(nullptr);
70 const struct tm* local_time = localtime(&timestamp);
71 EXPECT_NE(local_time, nullptr)
72 << "Could not get local time: errno=" << errno << ": "
73 << strerror(errno);
74 return std::string(local_time->tm_zone);
75 }
76
77 std::string GetLocalTime() {
78 const time_t timestamp = time(nullptr);
79 const struct tm* local_time = localtime(&timestamp);
80 EXPECT_NE(local_time, nullptr)
81 << "Could not get local time: errno=" << errno << ": "
82 << strerror(errno);
83 char buffer[sizeof("2020-08-26 14")];
84 const size_t written =
85 strftime(buffer, sizeof(buffer), "%Y-%m-%d %H", local_time);
86 EXPECT_LT(0UL, written);
87 return std::string(buffer);
88 }
89
90 // Checks that the timezone name in the `settings` matches what is `expected`.
91 void AssertTimezone(const std::string& expected,
92 const IntlSettings& settings) {
93 ASSERT_EQ(expected, settings.time_zone_id().id);
94 }
95
96 std::unique_ptr<sys::ComponentContext> ctx_;
97 fuchsia::settings::IntlSyncPtr intl_;
98
99 fuchsia::settings::IntlSettings save_settings_;
100};
101
102// These functions are used by tests that are currently disabled.
103#if false
104static bool ValidateShell(Shell* shell) {
105 if (!shell) {
106 return false;
107 }
108
109 if (!shell->IsSetup()) {
110 return false;
111 }
112
114
115 {
118 shell->GetTaskRunners().GetPlatformTaskRunner(), [shell, &latch]() {
119 shell->GetPlatformView()->NotifyDestroyed();
120 latch.Signal();
121 });
122 latch.Wait();
123 }
124
125 return true;
126}
127
128// Runs the function `f` in lock-step with a Dart isolate until the function
129// returns `true`, or until a certain fixed number of retries is exhausted.
130// Events `tick` and `tock` are used to synchronize the lock-stepping. The
131// event 'tick' is a signal to the dart isolate to advance a single iteration
132// step. 'tock' is used by the dart isolate to signal that it has completed
133// its step.
134static void RunCoroutineWithRetry(int retries,
137 std::function<bool()> f) {
138 for (; retries > 0; retries--) {
139 // Do a single coroutine step.
140 tick->Signal();
141 tock->Wait();
142 if (f()) {
143 break;
144 }
145 FML_LOG(INFO) << "Retries left: " << retries;
146 sleep(1);
147 }
148}
149#endif // false
150
151// Verifies that changing the Fuchsia settings timezone through the FIDL
152// settings interface results in a change of the reported local time in the
153// isolate.
154//
155// The test is as follows:
156//
157// - Set an initial timezone, then get a timestamp from the isolate rounded down
158// to the nearest hour. The assumption is as long as this test doesn't run
159// very near the whole hour, which should be very unlikely, the nearest hour
160// will vary depending on the time zone.
161// - Set a different timezone. Get a timestamp from the isolate again and
162// confirm that this time around the timestamps are different.
163// - Set the initial timezone again, and get the timestamp. This time, the
164// timestamp rounded down to whole hour should match the timestamp we got
165// in the initial step.
166TEST_F(FuchsiaShellTest, LocaltimesVaryOnTimezoneChanges) {
167#if defined(OS_FUCHSIA)
168 GTEST_SKIP()
169 << "This test fails after the CF V2 migration. https://fxbug.dev/110019 ";
170#else
171
172 // See fixtures/shell_test.dart, the callback NotifyLocalTime is declared
173 // there.
175 std::string dart_isolate_time_str;
176 AddNativeCallback("NotifyLocalTime", CREATE_NATIVE_ENTRY([&](auto args) {
177 dart_isolate_time_str =
180 latch.Signal();
181 }));
182
183 // As long as this is set, the isolate will keep rerunning its only task.
184 bool continue_fixture = true;
185 fml::AutoResetWaitableEvent fixture_latch;
186 AddNativeCallback("WaitFixture", CREATE_NATIVE_ENTRY([&](auto args) {
187 // Wait for the test fixture to advance.
188 fixture_latch.Wait();
190 args, continue_fixture);
191 }));
192
193 auto settings = CreateSettingsForFixture();
194 auto configuration = RunConfiguration::InferFromSettings(settings);
195 configuration.SetEntrypoint("timezonesChange");
196
197 std::unique_ptr<Shell> shell = CreateShell(settings);
198 ASSERT_NE(shell.get(), nullptr);
199 ASSERT_TRUE(ValidateShell(shell.get()));
200 RunEngine(shell.get(), std::move(configuration));
201 latch.Wait(); // After this point, the fixture is at waitFixture().
202
203 // Start with the local timezone, ensure that the isolate and the test
204 // fixture are the same.
205 SetTimezone(GetLocalTimezone());
206 AssertTimezone(GetLocalTimezone(), GetSettings());
207 std::string expected = GetLocalTime();
208 std::string actual = "undefined";
209 RunCoroutineWithRetry(10, &fixture_latch, &latch, [&]() {
210 actual = dart_isolate_time_str;
211 FML_LOG(INFO) << "reference: " << expected << ", actual: " << actual;
212 return expected == actual;
213 });
214 ASSERT_EQ(expected, actual)
215 << "The Dart isolate was expected to show the same time as the test "
216 << "fixture eventually, but that didn't happen after multiple retries.";
217
218 // Set a new timezone, which is hopefully different from the local one.
219 SetTimezone("America/New_York");
220 AssertTimezone("America/New_York", GetSettings());
221 RunCoroutineWithRetry(10, &fixture_latch, &latch, [&]() {
222 actual = dart_isolate_time_str;
223 FML_LOG(INFO) << "reference: " << expected << ", actual: " << actual;
224 return expected != actual;
225 });
226 ASSERT_NE(expected, actual)
227 << "The Dart isolate was expected to show a time different from the test "
228 << "fixture eventually, but that didn't happen after multiple retries.";
229
230 // Set a new isolate timezone, and check that the reported time is eventually
231 // different from what it used to be prior to the change.
232 SetTimezone("Europe/Amsterdam");
233 AssertTimezone("Europe/Amsterdam", GetSettings());
234 RunCoroutineWithRetry(10, &fixture_latch, &latch, [&]() {
235 actual = dart_isolate_time_str;
236 FML_LOG(INFO) << "reference: " << expected << ", actual: " << actual;
237 return expected != actual;
238 });
239 ASSERT_NE(expected, actual)
240 << "The Dart isolate was expected to show a time different from the "
241 << "prior timezone eventually, but that didn't happen after multiple "
242 << "retries.";
243
244 // Let's try to bring the timezone back to the old one.
245 expected = actual;
246 SetTimezone("America/New_York");
247 AssertTimezone("America/New_York", GetSettings());
248 RunCoroutineWithRetry(10, &fixture_latch, &latch, [&]() {
249 actual = dart_isolate_time_str;
250 FML_LOG(INFO) << "reference: " << expected << ", actual: " << actual;
251 return expected != actual;
252 });
253 ASSERT_NE(expected, actual)
254 << "The Dart isolate was expected to show a time different from the "
255 << "prior timezone eventually, but that didn't happen after multiple "
256 << "retries.";
257
258 // Tell the isolate to exit its loop.
259 ASSERT_FALSE(fixture_latch.IsSignaledForTest());
260 continue_fixture = false;
261 fixture_latch.Signal();
262 DestroyShell(std::move(shell));
263#endif // OS_FUCHSIA
264}
265
266} // namespace testing
267} // namespace flutter
static RunConfiguration InferFromSettings(const Settings &settings, const fml::RefPtr< fml::TaskRunner > &io_worker=nullptr, IsolateLaunchType launch_type=IsolateLaunchType::kNewGroup)
Attempts to infer a run configuration from the settings object. This tries to create a run configurat...
void SetTimezone(const std::string &timezone_name)
fuchsia::settings::IntlSettings save_settings_
void AssertTimezone(const std::string &expected, const IntlSettings &settings)
std::unique_ptr< sys::ComponentContext > ctx_
fuchsia::settings::IntlSyncPtr intl_
static void PlatformViewNotifyCreated(Shell *shell)
Definition: shell_test.cc:84
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
Definition: task_runner.cc:55
DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, int index)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
Dart_NativeFunction function
Definition: fuchsia.cc:51
sys::ComponentContext * ComponentContext()
TEST_F(DisplayListTest, Defaults)
static bool ValidateShell(Shell *shell)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
static double time(int loops, Benchmark *bench, Target *target)
Definition: nanobench.cpp:394
#define CREATE_NATIVE_ENTRY(native_entry)