7 #include <UIAutomation.h>
15 #include "flutter/fml/synchronization/waitable_event.h"
17 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
21 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
22 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
23 #include "flutter/shell/platform/windows/testing/test_keyboard.h"
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
32 using ::testing::InSequence;
33 using ::testing::NiceMock;
34 using ::testing::Return;
45 struct TestResponseHandle {
50 static bool test_response =
false;
52 constexpr uint64_t kKeyEventFromChannel = 0x11;
53 constexpr uint64_t kKeyEventFromEmbedder = 0x22;
54 static std::vector<int> key_event_logs;
56 std::unique_ptr<std::vector<uint8_t>> keyHandlingResponse(
bool handled) {
57 rapidjson::Document document;
58 auto& allocator = document.GetAllocator();
60 document.AddMember(
"handled", test_response, allocator);
66 FlutterProjectBundle GetTestProject() {
68 properties.
assets_path = L
"C:\\foo\\flutter_assets";
72 return FlutterProjectBundle{properties};
78 std::unique_ptr<FlutterWindowsEngine> GetTestEngine() {
79 auto engine = std::make_unique<FlutterWindowsEngine>(GetTestProject());
81 EngineModifier modifier(engine.get());
83 auto key_response_controller = std::make_shared<MockKeyResponseController>();
84 key_response_controller->SetChannelResponse(
85 [](MockKeyResponseController::ResponseCallback
callback) {
86 key_event_logs.push_back(kKeyEventFromChannel);
89 key_response_controller->SetEmbedderResponse(
90 [](
const FlutterKeyEvent* event,
91 MockKeyResponseController::ResponseCallback
callback) {
92 key_event_logs.push_back(kKeyEventFromEmbedder);
95 modifier.embedder_api().NotifyDisplayUpdate =
96 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
97 ([engine_instance = engine.get()](
98 FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
99 const FlutterEngineDisplaysUpdateType update_type,
100 const FlutterEngineDisplay* embedder_displays,
101 size_t display_count) {
return kSuccess; }));
103 MockEmbedderApiForKeyboard(modifier, key_response_controller);
109 class MockFlutterWindowsEngine :
public FlutterWindowsEngine {
111 MockFlutterWindowsEngine() : FlutterWindowsEngine(GetTestProject()) {}
113 MOCK_METHOD(
bool, Stop, (), ());
114 MOCK_METHOD(
bool, PostRasterThreadTask, (fml::closure), ());
117 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsEngine);
120 class MockAngleSurfaceManager :
public AngleSurfaceManager {
122 MockAngleSurfaceManager() : AngleSurfaceManager(false) {}
132 MOCK_METHOD(
void, DestroySurface, (), (
override));
134 MOCK_METHOD(
void, SetVSyncEnabled, (
bool), (
override));
137 FML_DISALLOW_COPY_AND_ASSIGN(MockAngleSurfaceManager);
144 TEST(FlutterWindowsViewTest, SubMenuExpandedState) {
145 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
146 EngineModifier modifier(engine.get());
147 modifier.embedder_api().UpdateSemanticsEnabled =
152 auto window_binding_handler =
153 std::make_unique<NiceMock<MockWindowBindingHandler>>();
155 view.SetEngine(std::move(engine));
158 view.OnUpdateSemanticsEnabled(
true);
160 auto bridge = view.accessibility_bridge().lock();
163 FlutterSemanticsNode2 root{
sizeof(FlutterSemanticsNode2), 0};
168 root.increased_value =
"";
169 root.decreased_value =
"";
170 root.child_count = 0;
171 root.custom_accessibility_actions_count = 0;
172 root.flags =
static_cast<FlutterSemanticsFlag
>(
173 FlutterSemanticsFlag::kFlutterSemanticsFlagHasExpandedState |
174 FlutterSemanticsFlag::kFlutterSemanticsFlagIsExpanded);
175 bridge->AddFlutterSemanticsNodeUpdate(root);
177 bridge->CommitUpdates();
180 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
181 EXPECT_TRUE(root_node->GetData().HasState(ax::mojom::State::kExpanded));
184 IAccessible* native_view = root_node->GetNativeViewAccessible();
185 ASSERT_TRUE(native_view !=
nullptr);
188 VARIANT varchild = {};
192 varchild.lVal = CHILDID_SELF;
193 VARIANT native_state = {};
194 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
195 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_EXPANDED);
198 IRawElementProviderSimple* uia_node;
199 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
200 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
201 UIA_ExpandCollapseExpandCollapseStatePropertyId, &native_state)));
202 EXPECT_EQ(native_state.lVal, ExpandCollapseState_Expanded);
204 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
205 UIA_AriaPropertiesPropertyId, &native_state)));
206 EXPECT_NE(std::wcsstr(native_state.bstrVal, L
"expanded=true"),
nullptr);
210 root.flags =
static_cast<FlutterSemanticsFlag
>(
211 FlutterSemanticsFlag::kFlutterSemanticsFlagHasExpandedState);
212 bridge->AddFlutterSemanticsNodeUpdate(root);
213 bridge->CommitUpdates();
216 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
217 EXPECT_TRUE(root_node->GetData().HasState(ax::mojom::State::kCollapsed));
220 IAccessible* native_view = root_node->GetNativeViewAccessible();
221 ASSERT_TRUE(native_view !=
nullptr);
224 VARIANT varchild = {};
228 varchild.lVal = CHILDID_SELF;
229 VARIANT native_state = {};
230 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
231 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_COLLAPSED);
234 IRawElementProviderSimple* uia_node;
235 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
236 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
237 UIA_ExpandCollapseExpandCollapseStatePropertyId, &native_state)));
238 EXPECT_EQ(native_state.lVal, ExpandCollapseState_Collapsed);
240 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
241 UIA_AriaPropertiesPropertyId, &native_state)));
242 EXPECT_NE(std::wcsstr(native_state.bstrVal, L
"expanded=false"),
nullptr);
248 TEST(FlutterWindowsViewTest, Shutdown) {
249 std::unique_ptr<MockFlutterWindowsEngine> engine =
250 std::make_unique<MockFlutterWindowsEngine>();
251 auto window_binding_handler =
252 std::make_unique<NiceMock<MockWindowBindingHandler>>();
253 std::unique_ptr<MockAngleSurfaceManager> surface_manager =
254 std::make_unique<MockAngleSurfaceManager>();
256 EngineModifier modifier(engine.get());
261 EXPECT_CALL(*engine.get(), Stop).Times(1);
262 EXPECT_CALL(*surface_manager.get(), DestroySurface).Times(1);
264 modifier.SetSurfaceManager(surface_manager.release());
268 TEST(FlutterWindowsViewTest, KeySequence) {
269 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
271 test_response =
false;
273 auto window_binding_handler =
274 std::make_unique<NiceMock<MockWindowBindingHandler>>();
276 view.SetEngine(std::move(engine));
279 [](
bool handled) {});
281 EXPECT_EQ(key_event_logs.size(), 2);
282 EXPECT_EQ(key_event_logs[0], kKeyEventFromEmbedder);
283 EXPECT_EQ(key_event_logs[1], kKeyEventFromChannel);
285 key_event_logs.clear();
288 TEST(FlutterWindowsViewTest, EnableSemantics) {
289 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
290 EngineModifier modifier(engine.get());
292 bool semantics_enabled =
false;
293 modifier.embedder_api().UpdateSemanticsEnabled = MOCK_ENGINE_PROC(
294 UpdateSemanticsEnabled,
295 [&semantics_enabled](FLUTTER_API_SYMBOL(
FlutterEngine) engine,
297 semantics_enabled = enabled;
301 auto window_binding_handler =
302 std::make_unique<NiceMock<MockWindowBindingHandler>>();
304 view.SetEngine(std::move(engine));
306 view.OnUpdateSemanticsEnabled(
true);
307 EXPECT_TRUE(semantics_enabled);
310 TEST(FlutterWindowsViewTest, AddSemanticsNodeUpdate) {
311 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
312 EngineModifier modifier(engine.get());
313 modifier.embedder_api().UpdateSemanticsEnabled =
318 auto window_binding_handler =
319 std::make_unique<NiceMock<MockWindowBindingHandler>>();
321 view.SetEngine(std::move(engine));
324 view.OnUpdateSemanticsEnabled(
true);
326 auto bridge = view.accessibility_bridge().lock();
330 FlutterSemanticsNode2 node{
sizeof(FlutterSemanticsNode2), 0};
332 node.value =
"value";
333 node.platform_view_id = -1;
334 bridge->AddFlutterSemanticsNodeUpdate(node);
335 bridge->CommitUpdates();
338 auto node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
343 IAccessible* native_view =
node_delegate->GetNativeViewAccessible();
344 ASSERT_TRUE(native_view !=
nullptr);
349 varchild.lVal = CHILDID_SELF;
352 BSTR bname =
nullptr;
353 ASSERT_EQ(native_view->get_accName(varchild, &bname), S_OK);
354 std::string name(_com_util::ConvertBSTRToString(bname));
355 EXPECT_EQ(name,
"name");
358 BSTR bvalue =
nullptr;
359 ASSERT_EQ(native_view->get_accValue(varchild, &bvalue), S_OK);
360 std::string value(_com_util::ConvertBSTRToString(bvalue));
361 EXPECT_EQ(value,
"value");
366 ASSERT_EQ(native_view->get_accRole(varchild, &varrole), S_OK);
367 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
370 IRawElementProviderSimple* uia_view;
371 native_view->QueryInterface(IID_PPV_ARGS(&uia_view));
372 ASSERT_TRUE(uia_view !=
nullptr);
376 ASSERT_EQ(uia_view->GetPropertyValue(UIA_NamePropertyId, &varname), S_OK);
377 EXPECT_EQ(varname.vt, VT_BSTR);
378 name = _com_util::ConvertBSTRToString(varname.bstrVal);
379 EXPECT_EQ(name,
"name");
383 ASSERT_EQ(uia_view->GetPropertyValue(UIA_ValueValuePropertyId, &varvalue),
385 EXPECT_EQ(varvalue.vt, VT_BSTR);
386 value = _com_util::ConvertBSTRToString(varvalue.bstrVal);
387 EXPECT_EQ(value,
"value");
391 ASSERT_EQ(uia_view->GetPropertyValue(UIA_ControlTypePropertyId, &varrole),
393 EXPECT_EQ(varrole.vt, VT_I4);
394 EXPECT_EQ(varrole.lVal, UIA_TextControlTypeId);
409 TEST(FlutterWindowsViewTest, AddSemanticsNodeUpdateWithChildren) {
410 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
411 EngineModifier modifier(engine.get());
412 modifier.embedder_api().UpdateSemanticsEnabled =
417 auto window_binding_handler =
418 std::make_unique<NiceMock<MockWindowBindingHandler>>();
420 view.SetEngine(std::move(engine));
423 view.OnUpdateSemanticsEnabled(
true);
425 auto bridge = view.accessibility_bridge().lock();
429 FlutterSemanticsNode2 node0{
sizeof(FlutterSemanticsNode2), 0};
430 std::vector<int32_t> node0_children{1, 2};
431 node0.child_count = node0_children.size();
432 node0.children_in_traversal_order = node0_children.data();
433 node0.children_in_hit_test_order = node0_children.data();
435 FlutterSemanticsNode2 node1{
sizeof(FlutterSemanticsNode2), 1};
436 node1.label =
"prefecture";
437 node1.value =
"Kyoto";
438 FlutterSemanticsNode2 node2{
sizeof(FlutterSemanticsNode2), 2};
439 std::vector<int32_t> node2_children{3};
440 node2.child_count = node2_children.size();
441 node2.children_in_traversal_order = node2_children.data();
442 node2.children_in_hit_test_order = node2_children.data();
443 FlutterSemanticsNode2 node3{
sizeof(FlutterSemanticsNode2), 3};
444 node3.label =
"city";
447 bridge->AddFlutterSemanticsNodeUpdate(node0);
448 bridge->AddFlutterSemanticsNodeUpdate(node1);
449 bridge->AddFlutterSemanticsNodeUpdate(node2);
450 bridge->AddFlutterSemanticsNodeUpdate(node3);
451 bridge->CommitUpdates();
454 auto node_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
459 IAccessible* node0_accessible =
node_delegate->GetNativeViewAccessible();
460 ASSERT_TRUE(node0_accessible !=
nullptr);
465 varchild.lVal = CHILDID_SELF;
470 ASSERT_EQ(node0_accessible->get_accRole(varchild, &varrole), S_OK);
471 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_GROUPING);
474 long node0_child_count = 0;
475 ASSERT_EQ(node0_accessible->get_accChildCount(&node0_child_count), S_OK);
476 EXPECT_EQ(node0_child_count, 2);
481 IDispatch* node1_dispatch =
nullptr;
482 ASSERT_EQ(node0_accessible->get_accChild(varchild, &node1_dispatch), S_OK);
483 ASSERT_TRUE(node1_dispatch !=
nullptr);
484 IAccessible* node1_accessible =
nullptr;
485 ASSERT_EQ(node1_dispatch->QueryInterface(
486 IID_IAccessible,
reinterpret_cast<void**
>(&node1_accessible)),
488 ASSERT_TRUE(node1_accessible !=
nullptr);
491 varchild.lVal = CHILDID_SELF;
492 BSTR bname =
nullptr;
493 ASSERT_EQ(node1_accessible->get_accName(varchild, &bname), S_OK);
494 std::string name(_com_util::ConvertBSTRToString(bname));
495 EXPECT_EQ(name,
"prefecture");
498 BSTR bvalue =
nullptr;
499 ASSERT_EQ(node1_accessible->get_accValue(varchild, &bvalue), S_OK);
500 std::string value(_com_util::ConvertBSTRToString(bvalue));
501 EXPECT_EQ(value,
"Kyoto");
506 ASSERT_EQ(node1_accessible->get_accRole(varchild, &varrole), S_OK);
507 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
510 IDispatch* parent_dispatch;
511 node1_accessible->get_accParent(&parent_dispatch);
512 IAccessible* parent_accessible;
514 parent_dispatch->QueryInterface(
515 IID_IAccessible,
reinterpret_cast<void**
>(&parent_accessible)),
517 EXPECT_EQ(parent_accessible, node0_accessible);
522 IDispatch* node2_dispatch =
nullptr;
523 ASSERT_EQ(node0_accessible->get_accChild(varchild, &node2_dispatch), S_OK);
524 ASSERT_TRUE(node2_dispatch !=
nullptr);
525 IAccessible* node2_accessible =
nullptr;
526 ASSERT_EQ(node2_dispatch->QueryInterface(
527 IID_IAccessible,
reinterpret_cast<void**
>(&node2_accessible)),
529 ASSERT_TRUE(node2_accessible !=
nullptr);
533 long node2_child_count = 0;
534 ASSERT_EQ(node2_accessible->get_accChildCount(&node2_child_count), S_OK);
535 EXPECT_EQ(node2_child_count, 1);
538 varchild.lVal = CHILDID_SELF;
541 ASSERT_EQ(node2_accessible->get_accRole(varchild, &varrole), S_OK);
542 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_GROUPING);
545 IDispatch* parent_dispatch;
546 node2_accessible->get_accParent(&parent_dispatch);
547 IAccessible* parent_accessible;
549 parent_dispatch->QueryInterface(
550 IID_IAccessible,
reinterpret_cast<void**
>(&parent_accessible)),
552 EXPECT_EQ(parent_accessible, node0_accessible);
558 IDispatch* node3_dispatch =
nullptr;
559 ASSERT_EQ(node2_accessible->get_accChild(varchild, &node3_dispatch), S_OK);
560 ASSERT_TRUE(node3_dispatch !=
nullptr);
561 IAccessible* node3_accessible =
nullptr;
562 ASSERT_EQ(node3_dispatch->QueryInterface(
563 IID_IAccessible,
reinterpret_cast<void**
>(&node3_accessible)),
565 ASSERT_TRUE(node3_accessible !=
nullptr);
568 varchild.lVal = CHILDID_SELF;
569 BSTR bname =
nullptr;
570 ASSERT_EQ(node3_accessible->get_accName(varchild, &bname), S_OK);
571 std::string name(_com_util::ConvertBSTRToString(bname));
572 EXPECT_EQ(name,
"city");
575 BSTR bvalue =
nullptr;
576 ASSERT_EQ(node3_accessible->get_accValue(varchild, &bvalue), S_OK);
577 std::string value(_com_util::ConvertBSTRToString(bvalue));
578 EXPECT_EQ(value,
"Uji");
583 ASSERT_EQ(node3_accessible->get_accRole(varchild, &varrole), S_OK);
584 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
587 IDispatch* parent_dispatch;
588 node3_accessible->get_accParent(&parent_dispatch);
589 IAccessible* parent_accessible;
591 parent_dispatch->QueryInterface(
592 IID_IAccessible,
reinterpret_cast<void**
>(&parent_accessible)),
594 EXPECT_EQ(parent_accessible, node2_accessible);
607 TEST(FlutterWindowsViewTest, NonZeroSemanticsRoot) {
608 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
609 EngineModifier modifier(engine.get());
610 modifier.embedder_api().UpdateSemanticsEnabled =
615 auto window_binding_handler =
616 std::make_unique<NiceMock<MockWindowBindingHandler>>();
618 view.SetEngine(std::move(engine));
621 view.OnUpdateSemanticsEnabled(
true);
623 auto bridge = view.accessibility_bridge().lock();
627 FlutterSemanticsNode2 node1{
sizeof(FlutterSemanticsNode2), 1};
628 std::vector<int32_t> node1_children{2};
629 node1.child_count = node1_children.size();
630 node1.children_in_traversal_order = node1_children.data();
631 node1.children_in_hit_test_order = node1_children.data();
633 FlutterSemanticsNode2 node2{
sizeof(FlutterSemanticsNode2), 2};
634 node2.label =
"prefecture";
635 node2.value =
"Kyoto";
637 bridge->AddFlutterSemanticsNodeUpdate(node1);
638 bridge->AddFlutterSemanticsNodeUpdate(node2);
639 bridge->CommitUpdates();
642 auto root_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
643 ASSERT_TRUE(root_delegate);
644 EXPECT_EQ(root_delegate->GetChildCount(), 1);
647 auto child_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
648 ASSERT_TRUE(child_delegate);
649 EXPECT_EQ(child_delegate->GetChildCount(), 0);
652 auto fake_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
653 ASSERT_FALSE(fake_delegate);
656 IAccessible* node1_accessible = root_delegate->GetNativeViewAccessible();
657 ASSERT_TRUE(node1_accessible !=
nullptr);
662 varchild.lVal = CHILDID_SELF;
667 ASSERT_EQ(node1_accessible->get_accRole(varchild, &varrole), S_OK);
668 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_GROUPING);
671 long node1_child_count = 0;
672 ASSERT_EQ(node1_accessible->get_accChildCount(&node1_child_count), S_OK);
673 EXPECT_EQ(node1_child_count, 1);
678 IDispatch* node2_dispatch =
nullptr;
679 ASSERT_EQ(node1_accessible->get_accChild(varchild, &node2_dispatch), S_OK);
680 ASSERT_TRUE(node2_dispatch !=
nullptr);
681 IAccessible* node2_accessible =
nullptr;
682 ASSERT_EQ(node2_dispatch->QueryInterface(
683 IID_IAccessible,
reinterpret_cast<void**
>(&node2_accessible)),
685 ASSERT_TRUE(node2_accessible !=
nullptr);
688 varchild.lVal = CHILDID_SELF;
689 BSTR bname =
nullptr;
690 ASSERT_EQ(node2_accessible->get_accName(varchild, &bname), S_OK);
691 std::string name(_com_util::ConvertBSTRToString(bname));
692 EXPECT_EQ(name,
"prefecture");
695 BSTR bvalue =
nullptr;
696 ASSERT_EQ(node2_accessible->get_accValue(varchild, &bvalue), S_OK);
697 std::string value(_com_util::ConvertBSTRToString(bvalue));
698 EXPECT_EQ(value,
"Kyoto");
703 ASSERT_EQ(node2_accessible->get_accRole(varchild, &varrole), S_OK);
704 EXPECT_EQ(varrole.lVal, ROLE_SYSTEM_STATICTEXT);
707 IDispatch* parent_dispatch;
708 node2_accessible->get_accParent(&parent_dispatch);
709 IAccessible* parent_accessible;
711 parent_dispatch->QueryInterface(
712 IID_IAccessible,
reinterpret_cast<void**
>(&parent_accessible)),
714 EXPECT_EQ(parent_accessible, node1_accessible);
735 TEST(FlutterWindowsViewTest, AccessibilityHitTesting) {
736 constexpr FlutterTransformation kIdentityTransform = {1, 0, 0,
740 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
741 EngineModifier modifier(engine.get());
742 modifier.embedder_api().UpdateSemanticsEnabled =
747 auto window_binding_handler =
748 std::make_unique<NiceMock<MockWindowBindingHandler>>();
750 view.SetEngine(std::move(engine));
753 view.OnUpdateSemanticsEnabled(
true);
755 auto bridge = view.accessibility_bridge().lock();
759 FlutterSemanticsNode2 node0{
sizeof(FlutterSemanticsNode2), 0};
760 std::vector<int32_t> node0_children{1, 2};
761 node0.rect = {0, 0, 500, 500};
762 node0.transform = kIdentityTransform;
763 node0.child_count = node0_children.size();
764 node0.children_in_traversal_order = node0_children.data();
765 node0.children_in_hit_test_order = node0_children.data();
768 FlutterSemanticsNode2 node1{
sizeof(FlutterSemanticsNode2), 1};
769 node1.rect = {0, 0, 250, 500};
770 node1.transform = kIdentityTransform;
771 node1.label =
"prefecture";
772 node1.value =
"Kyoto";
775 FlutterSemanticsNode2 node2{
sizeof(FlutterSemanticsNode2), 2};
776 std::vector<int32_t> node2_children{3};
777 node2.rect = {0, 0, 250, 500};
778 node2.transform = {1, 0, 250, 0, 1, 0, 0, 0, 1};
779 node2.child_count = node2_children.size();
780 node2.children_in_traversal_order = node2_children.data();
781 node2.children_in_hit_test_order = node2_children.data();
784 FlutterSemanticsNode2 node3{
sizeof(FlutterSemanticsNode2), 3};
785 node3.rect = {0, 0, 250, 250};
786 node3.transform = {1, 0, 0, 0, 1, 250, 0, 0, 1};
787 node3.label =
"city";
790 bridge->AddFlutterSemanticsNodeUpdate(node0);
791 bridge->AddFlutterSemanticsNodeUpdate(node1);
792 bridge->AddFlutterSemanticsNodeUpdate(node2);
793 bridge->AddFlutterSemanticsNodeUpdate(node3);
794 bridge->CommitUpdates();
797 auto node0_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
798 ASSERT_TRUE(node0_delegate);
799 auto node1_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
800 ASSERT_TRUE(node1_delegate);
801 auto node2_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
802 ASSERT_TRUE(node2_delegate);
803 auto node3_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(3).lock();
804 ASSERT_TRUE(node3_delegate);
807 IAccessible* node0_accessible = node0_delegate->GetNativeViewAccessible();
808 ASSERT_TRUE(node0_accessible !=
nullptr);
812 ASSERT_TRUE(SUCCEEDED(node0_accessible->accHitTest(150, 150, &varchild)));
813 EXPECT_EQ(varchild.vt, VT_DISPATCH);
814 EXPECT_EQ(varchild.pdispVal, node1_delegate->GetNativeViewAccessible());
818 ASSERT_TRUE(SUCCEEDED(node0_accessible->accHitTest(450, 150, &varchild)));
819 EXPECT_EQ(varchild.vt, VT_DISPATCH);
820 EXPECT_EQ(varchild.pdispVal, node2_delegate->GetNativeViewAccessible());
824 ASSERT_TRUE(SUCCEEDED(node0_accessible->accHitTest(450, 450, &varchild)));
825 EXPECT_EQ(varchild.vt, VT_DISPATCH);
826 EXPECT_EQ(varchild.pdispVal, node3_delegate->GetNativeViewAccessible());
829 TEST(FlutterWindowsViewTest, WindowResizeTests) {
830 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
831 EngineModifier modifier(engine.get());
833 auto window_binding_handler =
834 std::make_unique<NiceMock<MockWindowBindingHandler>>();
835 std::unique_ptr<MockAngleSurfaceManager> surface_manager =
836 std::make_unique<MockAngleSurfaceManager>();
838 EXPECT_CALL(*window_binding_handler.get(), NeedsVSync)
839 .WillOnce(Return(
false));
841 *surface_manager.get(),
842 ResizeSurface(_, 500, 500,
false))
844 EXPECT_CALL(*surface_manager.get(), DestroySurface).Times(1);
847 modifier.SetSurfaceManager(surface_manager.release());
850 fml::AutoResetWaitableEvent metrics_sent_latch;
851 modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC(
852 SendWindowMetricsEvent,
853 ([&metrics_sent_latch](
auto engine,
854 const FlutterWindowMetricsEvent* event) {
855 metrics_sent_latch.Signal();
859 fml::AutoResetWaitableEvent resized_latch;
860 std::thread([&resized_latch, &view]() {
864 resized_latch.Signal();
868 metrics_sent_latch.Wait();
872 resized_latch.Wait();
875 TEST(FlutterWindowsViewTest, WindowRepaintTests) {
876 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
877 EngineModifier modifier(engine.get());
883 bool schedule_frame_called =
false;
884 modifier.embedder_api().ScheduleFrame =
885 MOCK_ENGINE_PROC(ScheduleFrame, ([&schedule_frame_called](
auto engine) {
886 schedule_frame_called =
true;
891 EXPECT_TRUE(schedule_frame_called);
900 TEST(FlutterWindowsViewTest, CheckboxNativeState) {
901 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
902 EngineModifier modifier(engine.get());
903 modifier.embedder_api().UpdateSemanticsEnabled =
908 auto window_binding_handler =
909 std::make_unique<NiceMock<MockWindowBindingHandler>>();
911 view.SetEngine(std::move(engine));
914 view.OnUpdateSemanticsEnabled(
true);
916 auto bridge = view.accessibility_bridge().lock();
919 FlutterSemanticsNode2 root{
sizeof(FlutterSemanticsNode2), 0};
924 root.increased_value =
"";
925 root.decreased_value =
"";
926 root.child_count = 0;
927 root.custom_accessibility_actions_count = 0;
928 root.flags =
static_cast<FlutterSemanticsFlag
>(
929 FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState |
930 FlutterSemanticsFlag::kFlutterSemanticsFlagIsChecked);
931 bridge->AddFlutterSemanticsNodeUpdate(root);
933 bridge->CommitUpdates();
936 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
937 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
938 EXPECT_EQ(root_node->GetData().GetCheckedState(),
939 ax::mojom::CheckedState::kTrue);
942 IAccessible* native_view = root_node->GetNativeViewAccessible();
943 ASSERT_TRUE(native_view !=
nullptr);
946 VARIANT varchild = {};
950 varchild.lVal = CHILDID_SELF;
951 VARIANT native_state = {};
952 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
953 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_CHECKED);
956 IRawElementProviderSimple* uia_node;
957 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
958 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
959 UIA_ToggleToggleStatePropertyId, &native_state)));
960 EXPECT_EQ(native_state.lVal, ToggleState_On);
962 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
963 UIA_AriaPropertiesPropertyId, &native_state)));
964 EXPECT_NE(std::wcsstr(native_state.bstrVal, L
"checked=true"),
nullptr);
968 root.flags =
static_cast<FlutterSemanticsFlag
>(
969 FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState);
970 bridge->AddFlutterSemanticsNodeUpdate(root);
971 bridge->CommitUpdates();
974 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
975 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
976 EXPECT_EQ(root_node->GetData().GetCheckedState(),
977 ax::mojom::CheckedState::kFalse);
980 IAccessible* native_view = root_node->GetNativeViewAccessible();
981 ASSERT_TRUE(native_view !=
nullptr);
984 VARIANT varchild = {};
988 varchild.lVal = CHILDID_SELF;
989 VARIANT native_state = {};
990 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
991 EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_CHECKED);
994 IRawElementProviderSimple* uia_node;
995 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
996 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
997 UIA_ToggleToggleStatePropertyId, &native_state)));
998 EXPECT_EQ(native_state.lVal, ToggleState_Off);
1000 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1001 UIA_AriaPropertiesPropertyId, &native_state)));
1002 EXPECT_NE(std::wcsstr(native_state.bstrVal, L
"checked=false"),
nullptr);
1006 root.flags =
static_cast<FlutterSemanticsFlag
>(
1007 FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState |
1008 FlutterSemanticsFlag::kFlutterSemanticsFlagIsCheckStateMixed);
1009 bridge->AddFlutterSemanticsNodeUpdate(root);
1010 bridge->CommitUpdates();
1013 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1014 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
1015 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1016 ax::mojom::CheckedState::kMixed);
1019 IAccessible* native_view = root_node->GetNativeViewAccessible();
1020 ASSERT_TRUE(native_view !=
nullptr);
1023 VARIANT varchild = {};
1024 varchild.vt = VT_I4;
1027 varchild.lVal = CHILDID_SELF;
1028 VARIANT native_state = {};
1029 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1030 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_MIXED);
1033 IRawElementProviderSimple* uia_node;
1034 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1035 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1036 UIA_ToggleToggleStatePropertyId, &native_state)));
1037 EXPECT_EQ(native_state.lVal, ToggleState_Indeterminate);
1039 ASSERT_TRUE(SUCCEEDED(uia_node->GetPropertyValue(
1040 UIA_AriaPropertiesPropertyId, &native_state)));
1041 EXPECT_NE(std::wcsstr(native_state.bstrVal, L
"checked=mixed"),
nullptr);
1046 TEST(FlutterWindowsViewTest, SwitchNativeState) {
1047 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1048 EngineModifier modifier(engine.get());
1049 modifier.embedder_api().UpdateSemanticsEnabled =
1050 [](FLUTTER_API_SYMBOL(
FlutterEngine) engine,
bool enabled) {
1054 auto window_binding_handler =
1055 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1057 view.SetEngine(std::move(engine));
1060 view.OnUpdateSemanticsEnabled(
true);
1062 auto bridge = view.accessibility_bridge().lock();
1063 ASSERT_TRUE(bridge);
1065 FlutterSemanticsNode2 root{
sizeof(FlutterSemanticsNode2), 0};
1067 root.label =
"root";
1070 root.increased_value =
"";
1071 root.decreased_value =
"";
1072 root.child_count = 0;
1073 root.custom_accessibility_actions_count = 0;
1074 root.flags =
static_cast<FlutterSemanticsFlag
>(
1075 FlutterSemanticsFlag::kFlutterSemanticsFlagHasToggledState |
1076 FlutterSemanticsFlag::kFlutterSemanticsFlagIsToggled);
1077 bridge->AddFlutterSemanticsNodeUpdate(root);
1079 bridge->CommitUpdates();
1082 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1083 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kToggleButton);
1084 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1085 ax::mojom::CheckedState::kTrue);
1088 IAccessible* native_view = root_node->GetNativeViewAccessible();
1089 ASSERT_TRUE(native_view !=
nullptr);
1092 VARIANT varchild = {};
1093 varchild.vt = VT_I4;
1095 varchild.lVal = CHILDID_SELF;
1096 VARIANT varrole = {};
1099 ASSERT_EQ(native_view->get_accRole(varchild, &varrole), S_OK);
1100 ASSERT_EQ(varrole.lVal, ROLE_SYSTEM_CHECKBUTTON);
1103 VARIANT native_state = {};
1104 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1105 EXPECT_TRUE(native_state.lVal & STATE_SYSTEM_PRESSED);
1108 IRawElementProviderSimple* uia_node;
1109 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1110 ASSERT_EQ(uia_node->GetPropertyValue(UIA_ControlTypePropertyId, &varrole),
1112 EXPECT_EQ(varrole.lVal, UIA_ButtonControlTypeId);
1113 ASSERT_EQ(uia_node->GetPropertyValue(UIA_ToggleToggleStatePropertyId,
1116 EXPECT_EQ(native_state.lVal, ToggleState_On);
1118 uia_node->GetPropertyValue(UIA_AriaPropertiesPropertyId, &native_state),
1120 EXPECT_NE(std::wcsstr(native_state.bstrVal, L
"pressed=true"),
nullptr);
1124 root.flags =
static_cast<FlutterSemanticsFlag
>(
1125 FlutterSemanticsFlag::kFlutterSemanticsFlagHasToggledState);
1126 bridge->AddFlutterSemanticsNodeUpdate(root);
1127 bridge->CommitUpdates();
1130 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1131 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kToggleButton);
1132 EXPECT_EQ(root_node->GetData().GetCheckedState(),
1133 ax::mojom::CheckedState::kFalse);
1136 IAccessible* native_view = root_node->GetNativeViewAccessible();
1137 ASSERT_TRUE(native_view !=
nullptr);
1140 VARIANT varchild = {};
1141 varchild.vt = VT_I4;
1144 varchild.lVal = CHILDID_SELF;
1145 VARIANT native_state = {};
1146 ASSERT_TRUE(SUCCEEDED(native_view->get_accState(varchild, &native_state)));
1147 EXPECT_FALSE(native_state.lVal & STATE_SYSTEM_PRESSED);
1150 IRawElementProviderSimple* uia_node;
1151 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1152 ASSERT_EQ(uia_node->GetPropertyValue(UIA_ToggleToggleStatePropertyId,
1155 EXPECT_EQ(native_state.lVal, ToggleState_Off);
1157 uia_node->GetPropertyValue(UIA_AriaPropertiesPropertyId, &native_state),
1159 EXPECT_NE(std::wcsstr(native_state.bstrVal, L
"pressed=false"),
nullptr);
1163 TEST(FlutterWindowsViewTest, TooltipNodeData) {
1164 std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
1165 EngineModifier modifier(engine.get());
1166 modifier.embedder_api().UpdateSemanticsEnabled =
1167 [](FLUTTER_API_SYMBOL(
FlutterEngine) engine,
bool enabled) {
1171 auto window_binding_handler =
1172 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1174 view.SetEngine(std::move(engine));
1177 view.OnUpdateSemanticsEnabled(
true);
1179 auto bridge = view.accessibility_bridge().lock();
1180 ASSERT_TRUE(bridge);
1182 FlutterSemanticsNode2 root{
sizeof(FlutterSemanticsNode2), 0};
1184 root.label =
"root";
1187 root.increased_value =
"";
1188 root.decreased_value =
"";
1189 root.tooltip =
"tooltip";
1190 root.child_count = 0;
1191 root.custom_accessibility_actions_count = 0;
1192 root.flags =
static_cast<FlutterSemanticsFlag
>(
1193 FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField);
1194 bridge->AddFlutterSemanticsNodeUpdate(root);
1196 bridge->CommitUpdates();
1197 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
1198 std::string tooltip = root_node->GetData().GetStringAttribute(
1199 ax::mojom::StringAttribute::kTooltip);
1200 EXPECT_EQ(tooltip,
"tooltip");
1203 IAccessible* native_view = bridge->GetFlutterPlatformNodeDelegateFromID(0)
1205 ->GetNativeViewAccessible();
1206 VARIANT varchild = {.vt = VT_I4, .lVal = CHILDID_SELF};
1208 ASSERT_EQ(native_view->get_accName(varchild, &bname), S_OK);
1209 EXPECT_NE(std::wcsstr(bname, L
"tooltip"),
nullptr);
1212 IRawElementProviderSimple* uia_node;
1213 native_view->QueryInterface(IID_PPV_ARGS(&uia_node));
1215 ASSERT_EQ(uia_node->GetPropertyValue(UIA_HelpTextPropertyId, &varname), S_OK);
1216 std::string uia_tooltip = _com_util::ConvertBSTRToString(varname.bstrVal);
1217 EXPECT_EQ(uia_tooltip,
"tooltip");
1221 TEST(FlutterWindowsViewTest, DisablesVSync) {
1222 std::unique_ptr<MockFlutterWindowsEngine> engine =
1223 std::make_unique<MockFlutterWindowsEngine>();
1224 auto window_binding_handler =
1225 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1226 std::unique_ptr<MockAngleSurfaceManager> surface_manager =
1227 std::make_unique<MockAngleSurfaceManager>();
1229 EXPECT_CALL(*window_binding_handler.get(), NeedsVSync)
1230 .WillOnce(Return(
false));
1232 EngineModifier modifier(engine.get());
1236 EXPECT_CALL(*surface_manager.get(),
1237 CreateSurface(_, _, _,
false))
1239 .WillOnce(Return(
true));
1241 EXPECT_CALL(*engine.get(), Stop).Times(1);
1242 EXPECT_CALL(*surface_manager.get(), DestroySurface).Times(1);
1244 modifier.SetSurfaceManager(surface_manager.release());
1251 TEST(FlutterWindowsViewTest, EnablesVSync) {
1252 std::unique_ptr<MockFlutterWindowsEngine> engine =
1253 std::make_unique<MockFlutterWindowsEngine>();
1254 auto window_binding_handler =
1255 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1256 std::unique_ptr<MockAngleSurfaceManager> surface_manager =
1257 std::make_unique<MockAngleSurfaceManager>();
1259 EXPECT_CALL(*window_binding_handler.get(), NeedsVSync).WillOnce(Return(
true));
1261 EngineModifier modifier(engine.get());
1265 EXPECT_CALL(*surface_manager.get(),
1266 CreateSurface(_, _, _,
true))
1268 .WillOnce(Return(
true));
1270 EXPECT_CALL(*engine.get(), Stop).Times(1);
1271 EXPECT_CALL(*surface_manager.get(), DestroySurface).Times(1);
1273 modifier.SetSurfaceManager(surface_manager.release());
1282 TEST(FlutterWindowsViewTest, UpdatesVSyncOnDwmUpdates) {
1283 std::unique_ptr<MockFlutterWindowsEngine> engine =
1284 std::make_unique<MockFlutterWindowsEngine>();
1285 auto window_binding_handler =
1286 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1287 std::unique_ptr<MockAngleSurfaceManager> surface_manager =
1288 std::make_unique<MockAngleSurfaceManager>();
1290 EXPECT_CALL(*engine.get(), PostRasterThreadTask)
1292 .WillRepeatedly([](fml::closure
callback) {
1297 EXPECT_CALL(*window_binding_handler.get(), NeedsVSync)
1298 .WillOnce(Return(
true))
1299 .WillOnce(Return(
false));
1301 EngineModifier modifier(engine.get());
1305 EXPECT_CALL(*surface_manager.get(), SetVSyncEnabled(
true)).Times(1);
1306 EXPECT_CALL(*surface_manager.get(), SetVSyncEnabled(
false)).Times(1);
1308 EXPECT_CALL(*engine.get(), Stop).Times(1);
1309 EXPECT_CALL(*surface_manager.get(), DestroySurface).Times(1);
1311 modifier.SetSurfaceManager(surface_manager.release());