Flutter Engine
The Flutter Engine
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
5#include "flutter/shell/platform/windows/platform_handler.h"
6
7#include <memory>
8
9#include "flutter/fml/macros.h"
10#include "flutter/shell/platform/common/json_method_codec.h"
11#include "flutter/shell/platform/windows/flutter_windows_view.h"
12#include "flutter/shell/platform/windows/testing/engine_modifier.h"
13#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
14#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
15#include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
16#include "flutter/shell/platform/windows/testing/windows_test.h"
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
154
155 auto window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
156
157 engine_ = builder.Build();
158 view_ = std::make_unique<FlutterWindowsView>(kImplicitViewId, engine_.get(),
159 std::move(window));
160
161 EngineModifier modifier{engine_.get()};
162 modifier.SetImplicitView(view_.get());
163 }
164
165 private:
166 std::unique_ptr<FlutterWindowsEngine> engine_;
167 std::unique_ptr<FlutterWindowsView> view_;
168
169 FML_DISALLOW_COPY_AND_ASSIGN(PlatformHandlerTest);
170};
171
172TEST_F(PlatformHandlerTest, GetClipboardData) {
173 UseEngineWithView();
174
175 TestBinaryMessenger messenger;
176 PlatformHandler platform_handler(&messenger, engine(), []() {
177 auto clipboard = std::make_unique<MockScopedClipboard>();
178
179 EXPECT_CALL(*clipboard.get(), Open)
180 .Times(1)
181 .WillOnce(Return(kErrorSuccess));
182 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
183 EXPECT_CALL(*clipboard.get(), GetString)
184 .Times(1)
185 .WillOnce(Return(std::wstring(L"Hello world")));
186
187 return clipboard;
188 });
189
190 std::string result =
191 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
192
193 EXPECT_EQ(result, "[{\"text\":\"Hello world\"}]");
194}
195
196TEST_F(PlatformHandlerTest, GetClipboardDataRejectsUnknownContentType) {
197 UseEngineWithView();
198
199 TestBinaryMessenger messenger;
200 PlatformHandler platform_handler(&messenger, engine());
201
202 // Requesting an unknown content type is an error.
203 std::string result = SimulatePlatformMessage(
204 &messenger, kClipboardGetDataFakeContentTypeMessage);
205
206 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
207}
208
209TEST_F(PlatformHandlerTest, GetClipboardDataRequiresView) {
210 UseHeadlessEngine();
211
212 TestBinaryMessenger messenger;
213 PlatformHandler platform_handler(&messenger, engine());
214
215 std::string result =
216 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
217
218 EXPECT_EQ(result,
219 "[\"Clipboard error\",\"Clipboard is not available in "
220 "Windows headless mode\",null]");
221}
222
223TEST_F(PlatformHandlerTest, GetClipboardDataReportsOpenFailure) {
224 UseEngineWithView();
225
226 TestBinaryMessenger messenger;
227 PlatformHandler platform_handler(&messenger, engine(), []() {
228 auto clipboard = std::make_unique<MockScopedClipboard>();
229
230 EXPECT_CALL(*clipboard.get(), Open)
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 open clipboard\",1]");
241}
242
243TEST_F(PlatformHandlerTest, GetClipboardDataReportsGetDataFailure) {
244 UseEngineWithView();
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 EXPECT_CALL(*clipboard.get(), GetString)
255 .Times(1)
256 .WillOnce(Return(kArbitraryErrorCode));
257
258 return clipboard;
259 });
260
261 std::string result =
262 SimulatePlatformMessage(&messenger, kClipboardGetDataMessage);
263
264 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to get clipboard data\",1]");
265}
266
267TEST_F(PlatformHandlerTest, ClipboardHasStrings) {
268 UseEngineWithView();
269
270 TestBinaryMessenger messenger;
271 PlatformHandler platform_handler(&messenger, engine(), []() {
272 auto clipboard = std::make_unique<MockScopedClipboard>();
273
274 EXPECT_CALL(*clipboard.get(), Open)
275 .Times(1)
276 .WillOnce(Return(kErrorSuccess));
277 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(true));
278
279 return clipboard;
280 });
281
282 std::string result =
283 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
284
285 EXPECT_EQ(result, "[{\"value\":true}]");
286}
287
288TEST_F(PlatformHandlerTest, ClipboardHasStringsReturnsFalse) {
289 UseEngineWithView();
290
291 TestBinaryMessenger messenger;
292 PlatformHandler platform_handler(&messenger, engine(), []() {
293 auto clipboard = std::make_unique<MockScopedClipboard>();
294
295 EXPECT_CALL(*clipboard.get(), Open)
296 .Times(1)
297 .WillOnce(Return(kErrorSuccess));
298 EXPECT_CALL(*clipboard.get(), HasString).Times(1).WillOnce(Return(false));
299
300 return clipboard;
301 });
302
303 std::string result =
304 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
305
306 EXPECT_EQ(result, "[{\"value\":false}]");
307}
308
309TEST_F(PlatformHandlerTest, ClipboardHasStringsRejectsUnknownContentType) {
310 UseEngineWithView();
311
312 TestBinaryMessenger messenger;
313 PlatformHandler platform_handler(&messenger, engine());
314
315 std::string result = SimulatePlatformMessage(
316 &messenger, kClipboardHasStringsFakeContentTypeMessage);
317
318 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
319}
320
321TEST_F(PlatformHandlerTest, ClipboardHasStringsRequiresView) {
322 UseHeadlessEngine();
323
324 TestBinaryMessenger messenger;
325 PlatformHandler platform_handler(&messenger, engine());
326
327 std::string result =
328 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
329
330 EXPECT_EQ(result,
331 "[\"Clipboard error\",\"Clipboard is not available in Windows "
332 "headless mode\",null]");
333}
334
335// Regression test for https://github.com/flutter/flutter/issues/95817.
336TEST_F(PlatformHandlerTest, ClipboardHasStringsIgnoresPermissionErrors) {
337 UseEngineWithView();
338
339 TestBinaryMessenger messenger;
340 PlatformHandler platform_handler(&messenger, engine(), []() {
341 auto clipboard = std::make_unique<MockScopedClipboard>();
342
343 EXPECT_CALL(*clipboard.get(), Open)
344 .Times(1)
345 .WillOnce(Return(kAccessDeniedErrorCode));
346
347 return clipboard;
348 });
349
350 std::string result =
351 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
352
353 EXPECT_EQ(result, "[{\"value\":false}]");
354}
355
356TEST_F(PlatformHandlerTest, ClipboardHasStringsReportsErrors) {
357 UseEngineWithView();
358
359 TestBinaryMessenger messenger;
360 PlatformHandler platform_handler(&messenger, engine(), []() {
361 auto clipboard = std::make_unique<MockScopedClipboard>();
362
363 EXPECT_CALL(*clipboard.get(), Open)
364 .Times(1)
365 .WillOnce(Return(kArbitraryErrorCode));
366
367 return clipboard;
368 });
369
370 std::string result =
371 SimulatePlatformMessage(&messenger, kClipboardHasStringsMessage);
372
373 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
374}
375
376TEST_F(PlatformHandlerTest, ClipboardSetData) {
377 UseEngineWithView();
378
379 TestBinaryMessenger messenger;
380 PlatformHandler platform_handler(&messenger, engine(), []() {
381 auto clipboard = std::make_unique<MockScopedClipboard>();
382
383 EXPECT_CALL(*clipboard.get(), Open)
384 .Times(1)
385 .WillOnce(Return(kErrorSuccess));
386 EXPECT_CALL(*clipboard.get(), SetString)
387 .Times(1)
388 .WillOnce([](std::wstring string) {
389 EXPECT_EQ(string, L"hello");
390 return kErrorSuccess;
391 });
392
393 return clipboard;
394 });
395
396 std::string result =
397 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
398
399 EXPECT_EQ(result, "[null]");
400}
401
402// Regression test for: https://github.com/flutter/flutter/issues/121976
403TEST_F(PlatformHandlerTest, ClipboardSetDataTextMustBeString) {
404 UseEngineWithView();
405
406 TestBinaryMessenger messenger;
407 PlatformHandler platform_handler(&messenger, engine());
408
409 std::string result =
410 SimulatePlatformMessage(&messenger, kClipboardSetDataNullTextMessage);
411
412 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
413}
414
415TEST_F(PlatformHandlerTest, ClipboardSetDataUnknownType) {
416 UseEngineWithView();
417
418 TestBinaryMessenger messenger;
419 PlatformHandler platform_handler(&messenger, engine());
420
421 std::string result =
422 SimulatePlatformMessage(&messenger, kClipboardSetDataUnknownTypeMessage);
423
424 EXPECT_EQ(result, "[\"Clipboard error\",\"Unknown clipboard format\",null]");
425}
426
427TEST_F(PlatformHandlerTest, ClipboardSetDataRequiresView) {
428 UseHeadlessEngine();
429
430 TestBinaryMessenger messenger;
431 PlatformHandler platform_handler(&messenger, engine());
432
433 std::string result =
434 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
435
436 EXPECT_EQ(result,
437 "[\"Clipboard error\",\"Clipboard is not available in Windows "
438 "headless mode\",null]");
439}
440
441TEST_F(PlatformHandlerTest, ClipboardSetDataReportsOpenFailure) {
442 UseEngineWithView();
443
444 TestBinaryMessenger messenger;
445 PlatformHandler platform_handler(&messenger, engine(), []() {
446 auto clipboard = std::make_unique<MockScopedClipboard>();
447
448 EXPECT_CALL(*clipboard.get(), Open)
449 .Times(1)
450 .WillOnce(Return(kArbitraryErrorCode));
451
452 return clipboard;
453 });
454
455 std::string result =
456 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
457
458 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to open clipboard\",1]");
459}
460
461TEST_F(PlatformHandlerTest, ClipboardSetDataReportsSetDataFailure) {
462 UseEngineWithView();
463
464 TestBinaryMessenger messenger;
465 PlatformHandler platform_handler(&messenger, engine(), []() {
466 auto clipboard = std::make_unique<MockScopedClipboard>();
467
468 EXPECT_CALL(*clipboard.get(), Open)
469 .Times(1)
470 .WillOnce(Return(kErrorSuccess));
471 EXPECT_CALL(*clipboard.get(), SetString)
472 .Times(1)
473 .WillOnce(Return(kArbitraryErrorCode));
474
475 return clipboard;
476 });
477
478 std::string result =
479 SimulatePlatformMessage(&messenger, kClipboardSetDataMessage);
480
481 EXPECT_EQ(result, "[\"Clipboard error\",\"Unable to set clipboard data\",1]");
482}
483
484TEST_F(PlatformHandlerTest, PlaySystemSound) {
485 UseHeadlessEngine();
486
487 TestBinaryMessenger messenger;
488 MockPlatformHandler platform_handler(&messenger, engine());
489
490 EXPECT_CALL(platform_handler, SystemSoundPlay("SystemSoundType.alert", _))
491 .WillOnce([](const std::string& sound,
492 std::unique_ptr<MethodResult<rapidjson::Document>> result) {
493 result->Success();
494 });
495
496 std::string result =
497 SimulatePlatformMessage(&messenger, kSystemSoundTypeAlertMessage);
498
499 EXPECT_EQ(result, "[null]");
500}
501
502TEST_F(PlatformHandlerTest, SystemExitApplicationRequired) {
503 UseHeadlessEngine();
504 UINT exit_code = 0;
505
506 TestBinaryMessenger messenger([](const std::string& channel,
507 const uint8_t* message, size_t size,
508 BinaryReply reply) {});
509 MockPlatformHandler platform_handler(&messenger, engine());
510
511 ON_CALL(platform_handler, QuitApplication)
512 .WillByDefault([&exit_code](std::optional<HWND> hwnd,
513 std::optional<WPARAM> wparam,
514 std::optional<LPARAM> lparam,
515 UINT ec) { exit_code = ec; });
516 EXPECT_CALL(platform_handler, QuitApplication).Times(1);
517
518 std::string result = SimulatePlatformMessage(
519 &messenger, kSystemExitApplicationRequiredMessage);
520 EXPECT_EQ(result, "[{\"response\":\"exit\"}]");
521 EXPECT_EQ(exit_code, 1);
522}
523
524TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableCancel) {
525 UseHeadlessEngine();
526 bool called_cancel = false;
527
528 TestBinaryMessenger messenger(
529 [&called_cancel](const std::string& channel, const uint8_t* message,
530 size_t size, BinaryReply reply) {
531 reply(reinterpret_cast<const uint8_t*>(kExitResponseCancelMessage),
532 sizeof(kExitResponseCancelMessage));
533 called_cancel = true;
534 });
535 MockPlatformHandler platform_handler(&messenger, engine());
536
537 EXPECT_CALL(platform_handler, QuitApplication).Times(0);
538
539 std::string result = SimulatePlatformMessage(
540 &messenger, kSystemExitApplicationCancelableMessage);
541 EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
542 EXPECT_TRUE(called_cancel);
543}
544
545TEST_F(PlatformHandlerTest, SystemExitApplicationCancelableExit) {
546 UseHeadlessEngine();
547 bool called_cancel = false;
548 UINT exit_code = 0;
549
550 TestBinaryMessenger messenger(
551 [&called_cancel](const std::string& channel, const uint8_t* message,
552 size_t size, BinaryReply reply) {
553 reply(reinterpret_cast<const uint8_t*>(kExitResponseExitMessage),
554 sizeof(kExitResponseExitMessage));
555 called_cancel = true;
556 });
557 MockPlatformHandler platform_handler(&messenger, engine());
558
559 ON_CALL(platform_handler, QuitApplication)
560 .WillByDefault([&exit_code](std::optional<HWND> hwnd,
561 std::optional<WPARAM> wparam,
562 std::optional<LPARAM> lparam,
563 UINT ec) { exit_code = ec; });
564 EXPECT_CALL(platform_handler, QuitApplication).Times(1);
565
566 std::string result = SimulatePlatformMessage(
567 &messenger, kSystemExitApplicationCancelableMessage);
568 EXPECT_EQ(result, "[{\"response\":\"cancel\"}]");
569 EXPECT_TRUE(called_cancel);
570 EXPECT_EQ(exit_code, 2);
571}
572
573} // namespace testing
574} // namespace flutter
static NSString *const kChannelName
WindowsTestContext & GetContext()
Definition: windows_test.cc:21
GLFWwindow * window
Definition: main.cc:45
FlutterEngine engine
Definition: main.cc:68
GAsyncResult * result
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:27
Dart_NativeFunction function
Definition: fuchsia.cc:51
Win32Message message
TEST_F(DisplayListTest, Defaults)
constexpr int64_t kImplicitViewId
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
it will be possible to load the file into Perfetto s trace viewer 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
Definition: switches.h:259
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678
static constexpr int kErrorSuccess
static constexpr int kAccessDeniedErrorCode
unsigned int UINT
Definition: windows_types.h:32