Flutter Engine
 
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 kClipboardSetDataNullTextMessage[] =
42 "{\"method\":\"Clipboard.setData\",\"args\":{\"text\":null}}";
43static constexpr char kClipboardSetDataUnknownTypeMessage[] =
44 "{\"method\":\"Clipboard.setData\",\"args\":{\"madeuptype\":\"hello\"}}";
45static constexpr char kSystemSoundTypeAlertMessage[] =
46 "{\"method\":\"SystemSound.play\",\"args\":\"SystemSoundType.alert\"}";
47static constexpr char kSystemExitApplicationRequiredMessage[] =
48 "{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"required\","
49 "\"exitCode\":1}}";
50static constexpr char kSystemExitApplicationCancelableMessage[] =
51 "{\"method\":\"System.exitApplication\",\"args\":{\"type\":\"cancelable\","
52 "\"exitCode\":2}}";
53static constexpr char kExitResponseCancelMessage[] =
54 "[{\"response\":\"cancel\"}]";
55static constexpr char kExitResponseExitMessage[] = "[{\"response\":\"exit\"}]";
56
57static constexpr int kAccessDeniedErrorCode = 5;
58static constexpr int kErrorSuccess = 0;
59static constexpr int kArbitraryErrorCode = 1;
60
61// Test implementation of PlatformHandler to allow testing the PlatformHandler
62// logic.
63class MockPlatformHandler : public PlatformHandler {
64 public:
65 explicit MockPlatformHandler(
66 BinaryMessenger* messenger,
67 FlutterWindowsEngine* engine,
68 std::optional<std::function<std::unique_ptr<ScopedClipboardInterface>()>>
69 scoped_clipboard_provider = std::nullopt)
70 : PlatformHandler(messenger, engine, scoped_clipboard_provider) {}
71
72 virtual ~MockPlatformHandler() = default;
73
74 MOCK_METHOD(void,
75 GetPlainText,
76 (std::unique_ptr<MethodResult<rapidjson::Document>>,
77 std::string_view key),
78 (override));
79 MOCK_METHOD(void,
80 GetHasStrings,
81 (std::unique_ptr<MethodResult<rapidjson::Document>>),
82 (override));
83 MOCK_METHOD(void,
84 SetPlainText,
85 (const std::string&,
86 std::unique_ptr<MethodResult<rapidjson::Document>>),
87 (override));
88 MOCK_METHOD(void,
89 SystemSoundPlay,
90 (const std::string&,
91 std::unique_ptr<MethodResult<rapidjson::Document>>),
92 (override));
93
94 MOCK_METHOD(void,
95 QuitApplication,
96 (std::optional<HWND> hwnd,
97 std::optional<WPARAM> wparam,
98 std::optional<LPARAM> lparam,
99 UINT exit_code),
100 (override));
101
102 private:
103 FML_DISALLOW_COPY_AND_ASSIGN(MockPlatformHandler);
104};
105
106// A test version of the private ScopedClipboard.
107class MockScopedClipboard : public ScopedClipboardInterface {
108 public:
109 MockScopedClipboard() = default;
110 virtual ~MockScopedClipboard() = default;
111
112 MOCK_METHOD(int, Open, (HWND window), (override));
113 MOCK_METHOD(bool, HasString, (), (override));
114 MOCK_METHOD((std::variant<std::wstring, int>), GetString, (), (override));
115 MOCK_METHOD(int, SetString, (const std::wstring string), (override));
116
117 private:
118 FML_DISALLOW_COPY_AND_ASSIGN(MockScopedClipboard);
119};
120
121std::string SimulatePlatformMessage(TestBinaryMessenger* messenger,
122 std::string message) {
123 std::string result;
124 EXPECT_TRUE(messenger->SimulateEngineMessage(
125 kChannelName, reinterpret_cast<const uint8_t*>(message.c_str()),
126 message.size(),
127 [result = &result](const uint8_t* reply, size_t reply_size) {
128 std::string response(reinterpret_cast<const char*>(reply), reply_size);
129
130 *result = response;
131 }));
132
133 return result;
134}
135
136} // namespace
137
139 public:
141 virtual ~PlatformHandlerTest() = default;
142
143 protected:
144 FlutterWindowsEngine* engine() { return engine_.get(); }
145
148
149 engine_ = builder.Build();
150 }
151
152 private:
153 std::unique_ptr<FlutterWindowsEngine> engine_;
154 std::unique_ptr<FlutterWindowsView> view_;
155
157};
158
159TEST_F(PlatformHandlerTest, GetClipboardData) {
160 UseHeadlessEngine();
161
162 TestBinaryMessenger messenger;
163 PlatformHandler platform_handler(&messenger, engine(), []() {
164 auto clipboard = std::make_unique<MockScopedClipboard>();
165
166 EXPECT_CALL(*clipboard.get(), Open)
167 .Times(1)
168 .WillOnce(Return(kErrorSuccess));
169 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
170 EXPECT_CALL(*clipboard.get(), GetString)
171 .Times(1)
172 .WillOnce(Return(std::wstring(L"Hello world")));
173
174 return clipboard;
175 });
176
177 std::string result =
178 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
179
180 EXPECT_EQ(result, "[{\"text\":\"Hello world\"}]");
181}
182
183TEST_F(PlatformHandlerTest, GetClipboardDataRejectsUnknownContentType) {
184 UseHeadlessEngine();
185
186 TestBinaryMessenger messenger;
187 PlatformHandler platform_handler(&messenger, engine());
188
189 // Requesting an unknown content type is an error.
190 std::string result = SimulatePlatformMessage(
191 &messenger, kClipboardGetDataFakeContentTypeMessage);
192
193 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
194}
195
196TEST_F(PlatformHandlerTest, GetClipboardDataReportsOpenFailure) {
197 UseHeadlessEngine();
198
199 TestBinaryMessenger messenger;
200 PlatformHandler platform_handler(&messenger, engine(), []() {
201 auto clipboard = std::make_unique<MockScopedClipboard>();
202
203 EXPECT_CALL(*clipboard.get(), Open)
204 .Times(1)
205 .WillOnce(Return(kArbitraryErrorCode));
206
207 return clipboard;
208 });
209
210 std::string result =
211 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
212
213 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
214}
215
216TEST_F(PlatformHandlerTest, GetClipboardDataReportsGetDataFailure) {
217 UseHeadlessEngine();
218
219 TestBinaryMessenger messenger;
220 PlatformHandler platform_handler(&messenger, engine(), []() {
221 auto clipboard = std::make_unique<MockScopedClipboard>();
222
223 EXPECT_CALL(*clipboard.get(), Open)
224 .Times(1)
225 .WillOnce(Return(kErrorSuccess));
226 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
227 EXPECT_CALL(*clipboard.get(), GetString)
228 .Times(1)
229 .WillOnce(Return(kArbitraryErrorCode));
230
231 return clipboard;
232 });
233
234 std::string result =
235 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
236
237 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to get clipboard data\",1]");
238}
239
240TEST_F(PlatformHandlerTest, ClipboardHasStrings) {
241 UseHeadlessEngine();
242
243 TestBinaryMessenger messenger;
244 PlatformHandler platform_handler(&messenger, engine(), []() {
245 auto clipboard = std::make_unique<MockScopedClipboard>();
246
247 EXPECT_CALL(*clipboard.get(), Open)
248 .Times(1)
249 .WillOnce(Return(kErrorSuccess));
250 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
251
252 return clipboard;
253 });
254
255 std::string result =
256 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
257
258 EXPECT_EQ(result, "[{\"value\":true}]");
259}
260
261TEST_F(PlatformHandlerTest, ClipboardHasStringsReturnsFalse) {
262 UseHeadlessEngine();
263
264 TestBinaryMessenger messenger;
265 PlatformHandler platform_handler(&messenger, engine(), []() {
266 auto clipboard = std::make_unique<MockScopedClipboard>();
267
268 EXPECT_CALL(*clipboard.get(), Open)
269 .Times(1)
270 .WillOnce(Return(kErrorSuccess));
271 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(false));
272
273 return clipboard;
274 });
275
276 std::string result =
277 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
278
279 EXPECT_EQ(result, "[{\"value\":false}]");
280}
281
282TEST_F(PlatformHandlerTest, ClipboardHasStringsRejectsUnknownContentType) {
283 UseHeadlessEngine();
284
285 TestBinaryMessenger messenger;
286 PlatformHandler platform_handler(&messenger, engine());
287
288 std::string result = SimulatePlatformMessage(
289 &messenger, kClipboardHasStringsFakeContentTypeMessage);
290
291 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
292}
293
294// Regression test for https://github.com/flutter/flutter/issues/95817.
295TEST_F(PlatformHandlerTest, ClipboardHasStringsIgnoresPermissionErrors) {
296 UseHeadlessEngine();
297
298 TestBinaryMessenger messenger;
299 PlatformHandler platform_handler(&messenger, engine(), []() {
300 auto clipboard = std::make_unique<MockScopedClipboard>();
301
302 EXPECT_CALL(*clipboard.get(), Open)
303 .Times(1)
304 .WillOnce(Return(kAccessDeniedErrorCode));
305
306 return clipboard;
307 });
308
309 std::string result =
310 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
311
312 EXPECT_EQ(result, "[{\"value\":false}]");
313}
314
315TEST_F(PlatformHandlerTest, ClipboardHasStringsReportsErrors) {
316 UseHeadlessEngine();
317
318 TestBinaryMessenger messenger;
319 PlatformHandler platform_handler(&messenger, engine(), []() {
320 auto clipboard = std::make_unique<MockScopedClipboard>();
321
322 EXPECT_CALL(*clipboard.get(), Open)
323 .Times(1)
324 .WillOnce(Return(kArbitraryErrorCode));
325
326 return clipboard;
327 });
328
329 std::string result =
330 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
331
332 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
333}
334
335TEST_F(PlatformHandlerTest, ClipboardSetData) {
336 UseHeadlessEngine();
337
338 TestBinaryMessenger messenger;
339 PlatformHandler platform_handler(&messenger, engine(), []() {
340 auto clipboard = std::make_unique<MockScopedClipboard>();
341
342 EXPECT_CALL(*clipboard.get(), Open)
343 .Times(1)
344 .WillOnce(Return(kErrorSuccess));
345 EXPECT_CALL(*clipboard.get(), SetString)
346 .Times(1)
347 .WillOnce([](std::wstring string) {
348 EXPECT_EQ(string, L"hello");
349 return kErrorSuccess;
350 });
351
352 return clipboard;
353 });
354
355 std::string result =
356 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
357
358 EXPECT_EQ(result, "[null]");
359}
360
361// Regression test for: https://github.com/flutter/flutter/issues/121976
362TEST_F(PlatformHandlerTest, ClipboardSetDataTextMustBeString) {
363 UseHeadlessEngine();
364
365 TestBinaryMessenger messenger;
366 PlatformHandler platform_handler(&messenger, engine());
367
368 std::string result =
369 SimulatePlatformMessage(&messenger, kClipboardSetDataNullTextMessage);
370
371 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
372}
373
374TEST_F(PlatformHandlerTest, ClipboardSetDataUnknownType) {
375 UseHeadlessEngine();
376
377 TestBinaryMessenger messenger;
378 PlatformHandler platform_handler(&messenger, engine());
379
380 std::string result =
381 SimulatePlatformMessage(&messenger, kClipboardSetDataUnknownTypeMessage);
382
383 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
384}
385
386TEST_F(PlatformHandlerTest, ClipboardSetDataReportsOpenFailure) {
387 UseHeadlessEngine();
388
389 TestBinaryMessenger messenger;
390 PlatformHandler platform_handler(&messenger, engine(), []() {
391 auto clipboard = std::make_unique<MockScopedClipboard>();
392
393 EXPECT_CALL(*clipboard.get(), Open)
394 .Times(1)
395 .WillOnce(Return(kArbitraryErrorCode));
396
397 return clipboard;
398 });
399
400 std::string result =
401 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
402
403 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
404}
405
406TEST_F(PlatformHandlerTest, ClipboardSetDataReportsSetDataFailure) {
407 UseHeadlessEngine();
408
409 TestBinaryMessenger messenger;
410 PlatformHandler platform_handler(&messenger, engine(), []() {
411 auto clipboard = std::make_unique<MockScopedClipboard>();
412
413 EXPECT_CALL(*clipboard.get(), Open)
414 .Times(1)
415 .WillOnce(Return(kErrorSuccess));
416 EXPECT_CALL(*clipboard.get(), SetString)
417 .Times(1)
418 .WillOnce(Return(kArbitraryErrorCode));
419
420 return clipboard;
421 });
422
423 std::string result =
424 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
425
426 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to set clipboard data\",1]");
427}
428
429TEST_F(PlatformHandlerTest, PlaySystemSound) {
430 UseHeadlessEngine();
431
432 TestBinaryMessenger messenger;
433 MockPlatformHandler platform_handler(&messenger, engine());
434
435 EXPECT_CALL(platform_handler, SystemSoundPlay("SystemSoundType.alert", _))
436 .WillOnce([](const std::string& sound,
437 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
438 result->Success();
439 });
440
441 std::string result =
442 SimulatePlatformMessage(&messenger, kSystemSoundTypeAlertMessage);
443
444 EXPECT_EQ(result, "[null]");
445}
446
447TEST_F(PlatformHandlerTest, SystemExitApplicationRequired) {
448 UseHeadlessEngine();
449 UINT exit_code = 0;
450
451 TestBinaryMessenger messenger([](const std::string& channel,
452 const uint8_t* message, size_t size,
453 BinaryReply reply) {});
454 MockPlatformHandler platform_handler(&messenger, engine());
455
456 ON_CALL(platform_handler, QuitApplication)
457 .WillByDefault([&exit_code](std::optional<HWND> hwnd,
458 std::optional<WPARAM> wparam,
459 std::optional<LPARAM> lparam,
460 UINT ec) { exit_code = ec; });
461 EXPECT_CALL(platform_handler, QuitApplication).Times(1);
462
463 std::string result = SimulatePlatformMessage(
464 &messenger, kSystemExitApplicationRequiredMessage);
465 EXPECT_EQ(result, "[{\"response\":\"exit\"}]");
466 EXPECT_EQ(exit_code, 1);
467}
468
469TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableCancel) {
470 UseHeadlessEngine();
471 bool called_cancel = false;
472
473 TestBinaryMessenger messenger(
474 [&called_cancel](const std::string& channel, const uint8_t* message,
475 size_t size, BinaryReply reply) {
476 reply(reinterpret_cast<const uint8_t*>(kExitResponseCancelMessage),
477 sizeof(kExitResponseCancelMessage));
478 called_cancel = true;
479 });
480 MockPlatformHandler platform_handler(&messenger, engine());
481
482 EXPECT_CALL(platform_handler, QuitApplication).Times(0);
483
484 std::string result = SimulatePlatformMessage(
485 &messenger, kSystemExitApplicationCancelableMessage);
486 EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
487 EXPECT_TRUE(called_cancel);
488}
489
490TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableExit) {
491 UseHeadlessEngine();
492 bool called_cancel = false;
493 UINT exit_code = 0;
494
495 TestBinaryMessenger messenger(
496 [&called_cancel](const std::string& channel, const uint8_t* message,
497 size_t size, BinaryReply reply) {
498 reply(reinterpret_cast<const uint8_t*>(kExitResponseExitMessage),
499 sizeof(kExitResponseExitMessage));
500 called_cancel = true;
501 });
502 MockPlatformHandler platform_handler(&messenger, engine());
503
504 ON_CALL(platform_handler, QuitApplication)
505 .WillByDefault([&exit_code](std::optional<HWND> hwnd,
506 std::optional<WPARAM> wparam,
507 std::optional<LPARAM> lparam,
508 UINT ec) { exit_code = ec; });
509 EXPECT_CALL(platform_handler, QuitApplication).Times(1);
510
511 std::string result = SimulatePlatformMessage(
512 &messenger, kSystemExitApplicationCancelableMessage);
513 EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
514 EXPECT_TRUE(called_cancel);
515 EXPECT_EQ(exit_code, 2);
516}
517
518} // namespace testing
519} // namespace flutter
static NSString *const kChannelName
WindowsTestContext & GetContext()
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
const gchar * channel
G_BEGIN_DECLS GBytes * message
#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