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