Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
platform_handler_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
6
7#include <memory>
8
17#include "gmock/gmock.h"
18#include "gtest/gtest.h"
19#include "rapidjson/document.h"
20
21namespace flutter {
22namespace testing {
23
24namespace {
25using ::testing::_;
26using ::testing::NiceMock;
27using ::testing::Return;
28
29static constexpr char kChannelName[] = "flutter/platform";
30
31static constexpr char kClipboardGetDataMessage[] =
32 "{\"method\":\"Clipboard.getData\",\"args\":\"text/plain\"}";
33static constexpr char kClipboardGetDataFakeContentTypeMessage[] =
34 "{\"method\":\"Clipboard.getData\",\"args\":\"text/madeupcontenttype\"}";
35static constexpr char kClipboardHasStringsMessage[] =
36 "{\"method\":\"Clipboard.hasStrings\",\"args\":\"text/plain\"}";
37static constexpr char kClipboardHasStringsFakeContentTypeMessage[] =
38 "{\"method\":\"Clipboard.hasStrings\",\"args\":\"text/madeupcontenttype\"}";
39static constexpr char kClipboardSetDataMessage[] =
40 "{\"method\":\"Clipboard.setData\",\"args\":{\"text\":\"hello\"}}";
41static constexpr char kClipboardSetDataNullTerminatorMessage[] =
42 "{\"method\":\"Clipboard.setData\",\"args\":{\"text\":"
43 "\"hello\\u0000world\"}}";
44static constexpr char kClipboardSetDataNullTextMessage[] =
45 "{\"method\":\"Clipboard.setData\",\"args\":{\"text\":null}}";
46static constexpr char kClipboardSetDataUnknownTypeMessage[] =
47 "{\"method\":\"Clipboard.setData\",\"args\":{\"madeuptype\":\"hello\"}}";
48static constexpr char kSystemSoundTypeAlertMessage[] =
49 "{\"method\":\"SystemSound.play\",\"args\":\"SystemSoundType.alert\"}";
50static constexpr char kSystemExitApplicationRequiredMessage[] =
51 "{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"required\","
52 "\"exitCode\":1}}";
53static constexpr char kSystemExitApplicationCancelableMessage[] =
54 "{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"cancelable\","
55 "\"exitCode\":2}}";
56static constexpr char kExitResponseCancelMessage[] =
57 "[{\"response\":\"cancel\"}]";
58static constexpr char kExitResponseExitMessage[] = "[{\"response\":\"exit\"}]";
59
60static constexpr int kAccessDeniedErrorCode = 5;
61static constexpr int kErrorSuccess = 0;
62static constexpr int kArbitraryErrorCode = 1;
63
64// Test implementation of PlatformHandler to allow testing the PlatformHandler
65// logic.
66class MockPlatformHandler : public PlatformHandler {
67 public:
68 explicit MockPlatformHandler(
69 BinaryMessenger* messenger,
70 FlutterWindowsEngine* engine,
71 std::optional<std::function<std::unique_ptr<ScopedClipboardInterface>()>>
72 scoped_clipboard_provider = std::nullopt)
73 : PlatformHandler(messenger, engine, scoped_clipboard_provider) {}
74
75 virtual ~MockPlatformHandler() = default;
76
77 MOCK_METHOD(void,
78 GetPlainText,
79 (std::unique_ptr<MethodResult<rapidjson::Document>>,
80 std::string_view key),
81 (override));
82 MOCK_METHOD(void,
83 GetHasStrings,
84 (std::unique_ptr<MethodResult<rapidjson::Document>>),
85 (override));
86 MOCK_METHOD(void,
87 SetPlainText,
88 (std::string_view,
89 std::unique_ptr<MethodResult<rapidjson::Document>>),
90 (override));
91 MOCK_METHOD(void,
92 SystemSoundPlay,
93 (const std::string&,
94 std::unique_ptr<MethodResult<rapidjson::Document>>),
95 (override));
96
97 MOCK_METHOD(void,
98 QuitApplication,
99 (std::optional<HWND> hwnd,
100 std::optional<WPARAM> wparam,
101 std::optional<LPARAM> lparam,
102 UINT exit_code),
103 (override));
104
105 private:
106 FML_DISALLOW_COPY_AND_ASSIGN(MockPlatformHandler);
107};
108
109// A test version of the private ScopedClipboard.
110class MockScopedClipboard : public ScopedClipboardInterface {
111 public:
112 MockScopedClipboard() = default;
113 virtual ~MockScopedClipboard() = default;
114
115 MOCK_METHOD(int, Open, (HWND window), (override));
116 MOCK_METHOD(bool, HasString, (), (override));
117 MOCK_METHOD((std::variant<std::wstring, int>), GetString, (), (override));
118 MOCK_METHOD(int, SetString, (const std::wstring string), (override));
119
120 private:
121 FML_DISALLOW_COPY_AND_ASSIGN(MockScopedClipboard);
122};
123
124std::string SimulatePlatformMessage(TestBinaryMessenger* messenger,
125 std::string message) {
126 std::string result;
127 EXPECT_TRUE(messenger->SimulateEngineMessage(
128 kChannelName, reinterpret_cast<const uint8_t*>(message.c_str()),
129 message.size(),
130 [result = &result](const uint8_t* reply, size_t reply_size) {
131 std::string response(reinterpret_cast<const char*>(reply), reply_size);
132
133 *result = response;
134 }));
135
136 return result;
137}
138
139} // namespace
140
142 public:
144 virtual ~PlatformHandlerTest() = default;
145
146 protected:
147 FlutterWindowsEngine* engine() { return engine_.get(); }
148
151
152 engine_ = builder.Build();
153 }
154
155 private:
156 std::unique_ptr<FlutterWindowsEngine> engine_;
157 std::unique_ptr<FlutterWindowsView> view_;
158
160};
161
162TEST_F(PlatformHandlerTest, GetClipboardData) {
163 UseHeadlessEngine();
164
165 TestBinaryMessenger messenger;
166 PlatformHandler platform_handler(&messenger, engine(), []() {
167 auto clipboard = std::make_unique<MockScopedClipboard>();
168
169 EXPECT_CALL(*clipboard.get(), Open)
170 .Times(1)
171 .WillOnce(Return(kErrorSuccess));
172 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
173 EXPECT_CALL(*clipboard.get(), GetString)
174 .Times(1)
175 .WillOnce(Return(std::wstring(L"Hello world")));
176
177 return clipboard;
178 });
179
180 std::string result =
181 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
182
183 EXPECT_EQ(result, "[{\"text\":\"Hello world\"}]");
184}
185
186TEST_F(PlatformHandlerTest, GetClipboardDataRejectsUnknownContentType) {
187 UseHeadlessEngine();
188
189 TestBinaryMessenger messenger;
190 PlatformHandler platform_handler(&messenger, engine());
191
192 // Requesting an unknown content type is an error.
193 std::string result = SimulatePlatformMessage(
194 &messenger, kClipboardGetDataFakeContentTypeMessage);
195
196 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
197}
198
199TEST_F(PlatformHandlerTest, GetClipboardDataReportsOpenFailure) {
200 UseHeadlessEngine();
201
202 TestBinaryMessenger messenger;
203 PlatformHandler platform_handler(&messenger, engine(), []() {
204 auto clipboard = std::make_unique<MockScopedClipboard>();
205
206 EXPECT_CALL(*clipboard.get(), Open)
207 .Times(1)
208 .WillOnce(Return(kArbitraryErrorCode));
209
210 return clipboard;
211 });
212
213 std::string result =
214 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
215
216 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
217}
218
219TEST_F(PlatformHandlerTest, GetClipboardDataReportsGetDataFailure) {
220 UseHeadlessEngine();
221
222 TestBinaryMessenger messenger;
223 PlatformHandler platform_handler(&messenger, engine(), []() {
224 auto clipboard = std::make_unique<MockScopedClipboard>();
225
226 EXPECT_CALL(*clipboard.get(), Open)
227 .Times(1)
228 .WillOnce(Return(kErrorSuccess));
229 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
230 EXPECT_CALL(*clipboard.get(), GetString)
231 .Times(1)
232 .WillOnce(Return(kArbitraryErrorCode));
233
234 return clipboard;
235 });
236
237 std::string result =
238 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
239
240 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to get clipboard data\",1]");
241}
242
243TEST_F(PlatformHandlerTest, ClipboardHasStrings) {
244 UseHeadlessEngine();
245
246 TestBinaryMessenger messenger;
247 PlatformHandler platform_handler(&messenger, engine(), []() {
248 auto clipboard = std::make_unique<MockScopedClipboard>();
249
250 EXPECT_CALL(*clipboard.get(), Open)
251 .Times(1)
252 .WillOnce(Return(kErrorSuccess));
253 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
254
255 return clipboard;
256 });
257
258 std::string result =
259 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
260
261 EXPECT_EQ(result, "[{\"value\":true}]");
262}
263
264TEST_F(PlatformHandlerTest, ClipboardHasStringsReturnsFalse) {
265 UseHeadlessEngine();
266
267 TestBinaryMessenger messenger;
268 PlatformHandler platform_handler(&messenger, engine(), []() {
269 auto clipboard = std::make_unique<MockScopedClipboard>();
270
271 EXPECT_CALL(*clipboard.get(), Open)
272 .Times(1)
273 .WillOnce(Return(kErrorSuccess));
274 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(false));
275
276 return clipboard;
277 });
278
279 std::string result =
280 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
281
282 EXPECT_EQ(result, "[{\"value\":false}]");
283}
284
285TEST_F(PlatformHandlerTest, ClipboardHasStringsRejectsUnknownContentType) {
286 UseHeadlessEngine();
287
288 TestBinaryMessenger messenger;
289 PlatformHandler platform_handler(&messenger, engine());
290
291 std::string result = SimulatePlatformMessage(
292 &messenger, kClipboardHasStringsFakeContentTypeMessage);
293
294 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
295}
296
297// Regression test for https://github.com/flutter/flutter/issues/95817.
298TEST_F(PlatformHandlerTest, ClipboardHasStringsIgnoresPermissionErrors) {
299 UseHeadlessEngine();
300
301 TestBinaryMessenger messenger;
302 PlatformHandler platform_handler(&messenger, engine(), []() {
303 auto clipboard = std::make_unique<MockScopedClipboard>();
304
305 EXPECT_CALL(*clipboard.get(), Open)
306 .Times(1)
307 .WillOnce(Return(kAccessDeniedErrorCode));
308
309 return clipboard;
310 });
311
312 std::string result =
313 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
314
315 EXPECT_EQ(result, "[{\"value\":false}]");
316}
317
318TEST_F(PlatformHandlerTest, ClipboardHasStringsReportsErrors) {
319 UseHeadlessEngine();
320
321 TestBinaryMessenger messenger;
322 PlatformHandler platform_handler(&messenger, engine(), []() {
323 auto clipboard = std::make_unique<MockScopedClipboard>();
324
325 EXPECT_CALL(*clipboard.get(), Open)
326 .Times(1)
327 .WillOnce(Return(kArbitraryErrorCode));
328
329 return clipboard;
330 });
331
332 std::string result =
333 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
334
335 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
336}
337
338TEST_F(PlatformHandlerTest, ClipboardSetData) {
339 UseHeadlessEngine();
340
341 TestBinaryMessenger messenger;
342 PlatformHandler platform_handler(&messenger, engine(), []() {
343 auto clipboard = std::make_unique<MockScopedClipboard>();
344
345 EXPECT_CALL(*clipboard.get(), Open)
346 .Times(1)
347 .WillOnce(Return(kErrorSuccess));
348 EXPECT_CALL(*clipboard.get(), SetString)
349 .Times(1)
350 .WillOnce([](std::wstring string) {
351 EXPECT_EQ(string, L"hello");
352 return kErrorSuccess;
353 });
354
355 return clipboard;
356 });
357
358 std::string result =
359 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
360
361 EXPECT_EQ(result, "[null]");
362}
363
364// Regression test for https://github.com/flutter/flutter/issues/162226.
365TEST_F(PlatformHandlerTest, ClipboardSetDataReplacesNullTerminators) {
366 UseHeadlessEngine();
367
368 TestBinaryMessenger messenger;
369 PlatformHandler platform_handler(&messenger, engine(), []() {
370 auto clipboard = std::make_unique<MockScopedClipboard>();
371
372 EXPECT_CALL(*clipboard.get(), Open)
373 .Times(1)
374 .WillOnce(Return(kErrorSuccess));
375 EXPECT_CALL(*clipboard.get(), SetString)
376 .Times(1)
377 .WillOnce([](std::wstring string) {
378 EXPECT_EQ(string, L"hello\uFFFDworld");
379 return kErrorSuccess;
380 });
381
382 return clipboard;
383 });
384
385 std::string result = SimulatePlatformMessage(
386 &messenger, kClipboardSetDataNullTerminatorMessage);
387
388 EXPECT_EQ(result, "[null]");
389}
390
391// Regression test for: https://github.com/flutter/flutter/issues/121976
392TEST_F(PlatformHandlerTest, ClipboardSetDataTextMustBeString) {
393 UseHeadlessEngine();
394
395 TestBinaryMessenger messenger;
396 PlatformHandler platform_handler(&messenger, engine());
397
398 std::string result =
399 SimulatePlatformMessage(&messenger, kClipboardSetDataNullTextMessage);
400
401 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
402}
403
404TEST_F(PlatformHandlerTest, ClipboardSetDataUnknownType) {
405 UseHeadlessEngine();
406
407 TestBinaryMessenger messenger;
408 PlatformHandler platform_handler(&messenger, engine());
409
410 std::string result =
411 SimulatePlatformMessage(&messenger, kClipboardSetDataUnknownTypeMessage);
412
413 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
414}
415
416TEST_F(PlatformHandlerTest, ClipboardSetDataReportsOpenFailure) {
417 UseHeadlessEngine();
418
419 TestBinaryMessenger messenger;
420 PlatformHandler platform_handler(&messenger, engine(), []() {
421 auto clipboard = std::make_unique<MockScopedClipboard>();
422
423 EXPECT_CALL(*clipboard.get(), Open)
424 .Times(1)
425 .WillOnce(Return(kArbitraryErrorCode));
426
427 return clipboard;
428 });
429
430 std::string result =
431 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
432
433 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
434}
435
436TEST_F(PlatformHandlerTest, ClipboardSetDataReportsSetDataFailure) {
437 UseHeadlessEngine();
438
439 TestBinaryMessenger messenger;
440 PlatformHandler platform_handler(&messenger, engine(), []() {
441 auto clipboard = std::make_unique<MockScopedClipboard>();
442
443 EXPECT_CALL(*clipboard.get(), Open)
444 .Times(1)
445 .WillOnce(Return(kErrorSuccess));
446 EXPECT_CALL(*clipboard.get(), SetString)
447 .Times(1)
448 .WillOnce(Return(kArbitraryErrorCode));
449
450 return clipboard;
451 });
452
453 std::string result =
454 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
455
456 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to set clipboard data\",1]");
457}
458
459TEST_F(PlatformHandlerTest, PlaySystemSound) {
460 UseHeadlessEngine();
461
462 TestBinaryMessenger messenger;
463 MockPlatformHandler platform_handler(&messenger, engine());
464
465 EXPECT_CALL(platform_handler, SystemSoundPlay("SystemSoundType.alert", _))
466 .WillOnce([](const std::string& sound,
467 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
468 result->Success();
469 });
470
471 std::string result =
472 SimulatePlatformMessage(&messenger, kSystemSoundTypeAlertMessage);
473
474 EXPECT_EQ(result, "[null]");
475}
476
477TEST_F(PlatformHandlerTest, SystemExitApplicationRequired) {
478 UseHeadlessEngine();
479 UINT exit_code = 0;
480
481 TestBinaryMessenger messenger([](const std::string& channel,
482 const uint8_t* message, size_t size,
483 BinaryReply reply) {});
484 MockPlatformHandler platform_handler(&messenger, engine());
485
486 ON_CALL(platform_handler, QuitApplication)
487 .WillByDefault([&exit_code](std::optional<HWND> hwnd,
488 std::optional<WPARAM> wparam,
489 std::optional<LPARAM> lparam,
490 UINT ec) { exit_code = ec; });
491 EXPECT_CALL(platform_handler, QuitApplication).Times(1);
492
493 std::string result = SimulatePlatformMessage(
494 &messenger, kSystemExitApplicationRequiredMessage);
495 EXPECT_EQ(result, "[{\"response\":\"exit\"}]");
496 EXPECT_EQ(exit_code, 1);
497}
498
499TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableCancel) {
500 UseHeadlessEngine();
501 bool called_cancel = false;
502
503 TestBinaryMessenger messenger(
504 [&called_cancel](const std::string& channel, const uint8_t* message,
505 size_t size, BinaryReply reply) {
506 reply(reinterpret_cast<const uint8_t*>(kExitResponseCancelMessage),
507 sizeof(kExitResponseCancelMessage));
508 called_cancel = true;
509 });
510 MockPlatformHandler platform_handler(&messenger, engine());
511
512 EXPECT_CALL(platform_handler, QuitApplication).Times(0);
513
514 std::string result = SimulatePlatformMessage(
515 &messenger, kSystemExitApplicationCancelableMessage);
516 EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
517 EXPECT_TRUE(called_cancel);
518}
519
520TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableExit) {
521 UseHeadlessEngine();
522 bool called_cancel = false;
523 UINT exit_code = 0;
524
525 TestBinaryMessenger messenger(
526 [&called_cancel](const std::string& channel, const uint8_t* message,
527 size_t size, BinaryReply reply) {
528 reply(reinterpret_cast<const uint8_t*>(kExitResponseExitMessage),
529 sizeof(kExitResponseExitMessage));
530 called_cancel = true;
531 });
532 MockPlatformHandler platform_handler(&messenger, engine());
533
534 ON_CALL(platform_handler, QuitApplication)
535 .WillByDefault([&exit_code](std::optional<HWND> hwnd,
536 std::optional<WPARAM> wparam,
537 std::optional<LPARAM> lparam,
538 UINT ec) { exit_code = ec; });
539 EXPECT_CALL(platform_handler, QuitApplication).Times(1);
540
541 std::string result = SimulatePlatformMessage(
542 &messenger, kSystemExitApplicationCancelableMessage);
543 EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
544 EXPECT_TRUE(called_cancel);
545 EXPECT_EQ(exit_code, 2);
546}
547
548} // namespace testing
549} // namespace flutter
static NSString *const kChannelName
WindowsTestContext & GetContext()
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
const char * message
const gchar * channel
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
TEST_F(DisplayListTest, Defaults)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
static constexpr int kErrorSuccess
static constexpr int kAccessDeniedErrorCode
unsigned int UINT