| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/accessibility/ax_tree_update_util.h" |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/accessibility/ax_tree_update.h" |
| |
| namespace ui { |
| |
| TEST(AXTreeUpdateUtilTest, AXTreeUpdatesCanBeMerged_NodeIDToClear) { |
| AXTreeUpdate u1; |
| AXTreeUpdate u2; |
| |
| // Both updates have an invalid node ID to clear. |
| u1.node_id_to_clear = kInvalidAXNodeID; |
| u2.node_id_to_clear = kInvalidAXNodeID; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Update 1 has a node ID to clear. This can interfere with u2's update. |
| u1.node_id_to_clear = 1; |
| u2.node_id_to_clear = kInvalidAXNodeID; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Update 2 has a node ID to clear. |
| u1.node_id_to_clear = kInvalidAXNodeID; |
| u2.node_id_to_clear = 2; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, AXTreeUpdatesCanBeMerged_TreeData) { |
| AXTreeUpdate u1; |
| AXTreeUpdate u2; |
| |
| // Check when no tree data has been set. |
| u1.has_tree_data = false; |
| u2.has_tree_data = false; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = true; |
| u2.has_tree_data = false; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = false; |
| u2.has_tree_data = true; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = true; |
| u2.has_tree_data = true; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Check when both updates have identical tree data. |
| AXTreeData d1; |
| d1.tree_id = AXTreeID::CreateNewAXTreeID(); |
| u1.tree_data = d1; |
| u2.tree_data = d1; |
| |
| u1.has_tree_data = false; |
| u2.has_tree_data = false; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = true; |
| u2.has_tree_data = false; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = false; |
| u2.has_tree_data = true; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = true; |
| u2.has_tree_data = true; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Check when the updates have different tree data. |
| AXTreeData d2; |
| d2.tree_id = AXTreeID::CreateNewAXTreeID(); |
| u1.tree_data = d1; |
| u2.tree_data = d2; |
| |
| u1.has_tree_data = false; |
| u2.has_tree_data = false; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = true; |
| u2.has_tree_data = false; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = false; |
| u2.has_tree_data = true; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| u1.has_tree_data = true; |
| u2.has_tree_data = true; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, AXTreeUpdatesCanBeMerged_RootID) { |
| AXTreeUpdate u1; |
| AXTreeUpdate u2; |
| |
| // Same root id. |
| u1.root_id = 1; |
| u2.root_id = 1; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Different root id. |
| u1.root_id = 1; |
| u2.root_id = 2; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Invalid root id. |
| u1.root_id = kInvalidAXNodeID; |
| u2.root_id = kInvalidAXNodeID; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, AXTreeUpdatesCanBeMerged_EventFrom) { |
| AXTreeUpdate u1; |
| AXTreeUpdate u2; |
| |
| // Same event from. |
| u1.event_from = ax::mojom::EventFrom::kAction; |
| u2.event_from = ax::mojom::EventFrom::kAction; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Different event from. |
| u1.event_from = ax::mojom::EventFrom::kAction; |
| u2.event_from = ax::mojom::EventFrom::kPage; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, AXTreeUpdatesCanBeMerged_EventFromAction) { |
| AXTreeUpdate u1; |
| AXTreeUpdate u2; |
| |
| // Same event from action. |
| u1.event_from_action = ax::mojom::Action::kFocus; |
| u2.event_from_action = ax::mojom::Action::kFocus; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Different event_ from action. |
| u1.event_from_action = ax::mojom::Action::kFocus; |
| u2.event_from_action = ax::mojom::Action::kDecrement; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, AXTreeUpdatesCanBeMerged_EventIntents) { |
| AXTreeUpdate u1; |
| AXTreeUpdate u2; |
| |
| // Same event intents. |
| u1.event_intents = {AXEventIntent(ax::mojom::Command::kClearSelection)}; |
| u2.event_intents = {AXEventIntent(ax::mojom::Command::kClearSelection)}; |
| EXPECT_TRUE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| |
| // Different event intents. |
| u1.event_intents = {AXEventIntent(ax::mojom::Command::kClearSelection)}; |
| u2.event_intents = {AXEventIntent(ax::mojom::Command::kDelete)}; |
| EXPECT_FALSE(AXTreeUpdatesCanBeMerged(u1, u2)); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, MergeAXTreeUpdates) { |
| std::vector<AXTreeUpdate> src; |
| for (int i = 0; i < 10; i++) { |
| AXNodeData node; |
| node.id = i; |
| AXTreeUpdate u; |
| u.nodes.push_back(node); |
| src.push_back(u); |
| } |
| |
| std::vector<AXTreeUpdate> dst; |
| EXPECT_TRUE(MergeAXTreeUpdates(src, &dst)); |
| EXPECT_EQ(1u, dst.size()); |
| EXPECT_EQ(10u, dst[0].nodes.size()); |
| for (int i = 0; i < 10; i++) { |
| EXPECT_EQ(i, dst[0].nodes[i].id); |
| } |
| } |
| |
| TEST(AXTreeUpdateUtilTest, MergeAXTreeUpdates_CannotBeMerged) { |
| std::vector<AXTreeUpdate> src; |
| for (int i = 0; i < 10; i++) { |
| AXTreeUpdate u; |
| u.root_id = i; |
| src.push_back(u); |
| } |
| |
| std::vector<AXTreeUpdate> dst; |
| EXPECT_FALSE(MergeAXTreeUpdates(src, &dst)); |
| EXPECT_TRUE(dst.empty()); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, MergeAXTreeUpdates_NoConsecutiveMergeableUpdates) { |
| // Alternate updates, where the odd ones cannot be merged, while the even ones |
| // can be merged. |
| std::vector<AXTreeUpdate> src; |
| for (int i = 0; i < 5; i++) { |
| // U1 cannot be merged. |
| AXTreeUpdate u1; |
| u1.root_id = i * 2; |
| src.push_back(u1); |
| |
| // U2 can be merged. |
| AXNodeData node; |
| node.id = i * 2 + 1; |
| AXTreeUpdate u2; |
| u2.nodes.push_back(node); |
| src.push_back(u2); |
| } |
| |
| // Since no consecutive updates can be merged, none are merged. Note that this |
| // might not be the desired behavior, but this is how the function is written |
| // now. |
| std::vector<AXTreeUpdate> dst; |
| EXPECT_FALSE(MergeAXTreeUpdates(src, &dst)); |
| EXPECT_TRUE(dst.empty()); |
| } |
| |
| TEST(AXTreeUpdateUtilTest, MergeAXTreeUpdates_SomeCanBeMerged) { |
| std::vector<AXTreeUpdate> src; |
| // The first half updates cannot be merged. |
| for (int i = 0; i < 5; i++) { |
| AXNodeData node; |
| node.id = i; |
| AXTreeUpdate u; |
| u.root_id = i; |
| u.nodes.push_back(node); |
| src.push_back(u); |
| } |
| |
| // The second half updates can be merged. |
| for (int i = 5; i < 10; i++) { |
| AXNodeData node; |
| node.id = i; |
| AXTreeUpdate u; |
| u.nodes.push_back(node); |
| src.push_back(u); |
| } |
| |
| // The first five updates are not merged and the last five are. |
| std::vector<AXTreeUpdate> dst; |
| EXPECT_TRUE(MergeAXTreeUpdates(src, &dst)); |
| EXPECT_EQ(6u, dst.size()); |
| for (int i = 0; i < 5; i++) { |
| EXPECT_EQ(1u, dst[i].nodes.size()); |
| EXPECT_EQ(i, dst[i].nodes[0].id); |
| } |
| EXPECT_EQ(5u, dst[5].nodes.size()); |
| for (int i = 0; i < 5; i++) { |
| EXPECT_EQ(i + 5, dst[5].nodes[i].id); |
| } |
| } |
| |
| TEST(AXTreeUpdateUtilTest, MergeAXTreeUpdates_AtLeastTwoMerges) { |
| std::vector<AXTreeUpdate> src; |
| for (int i = 0; i < 2; i++) { |
| AXNodeData node; |
| node.id = i; |
| AXTreeUpdate u; |
| u.nodes.push_back(node); |
| src.push_back(u); |
| } |
| |
| // If there are fewer than 2 merges, the merge fails. |
| std::vector<AXTreeUpdate> dst; |
| EXPECT_FALSE(MergeAXTreeUpdates(src, &dst)); |
| EXPECT_TRUE(dst.empty()); |
| |
| AXNodeData node; |
| node.id = 2; |
| AXTreeUpdate u; |
| u.nodes.push_back(node); |
| src.push_back(u); |
| |
| // If there are more than 2 merges, the merge succeeds. |
| EXPECT_TRUE(MergeAXTreeUpdates(src, &dst)); |
| EXPECT_EQ(1u, dst.size()); |
| EXPECT_EQ(3u, dst[0].nodes.size()); |
| for (int i = 0; i < 3; i++) { |
| EXPECT_EQ(i, dst[0].nodes[i].id); |
| } |
| } |
| |
| } // namespace ui |