1 // Copyright 2013 The Chromium 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 "base/memory/scoped_ptr.h" 6 #include "base/strings/string_number_conversions.h" 7 #include "testing/gtest/include/gtest/gtest.h" 8 #include "ui/accessibility/ax_node.h" 9 #include "ui/accessibility/ax_serializable_tree.h" 10 #include "ui/accessibility/ax_tree.h" 11 #include "ui/accessibility/ax_tree_serializer.h" 12 13 namespace ui { 14 15 namespace { 16 17 class FakeAXTreeDelegate : public AXTreeDelegate { 18 public: 19 virtual void OnNodeWillBeDeleted(AXNode* node) OVERRIDE { 20 deleted_ids_.push_back(node->id()); 21 } 22 23 virtual void OnNodeCreated(AXNode* node) OVERRIDE { 24 created_ids_.push_back(node->id()); 25 } 26 27 virtual void OnNodeChanged(AXNode* node) OVERRIDE { 28 changed_ids_.push_back(node->id()); 29 } 30 31 virtual void OnNodeCreationFinished(AXNode* node) OVERRIDE { 32 creation_finished_ids_.push_back(node->id()); 33 } 34 35 virtual void OnNodeChangeFinished(AXNode* node) OVERRIDE { 36 change_finished_ids_.push_back(node->id()); 37 } 38 39 virtual void OnRootChanged(AXNode* new_root) OVERRIDE { 40 new_root_ids_.push_back(new_root->id()); 41 } 42 43 const std::vector<int32>& deleted_ids() { return deleted_ids_; } 44 const std::vector<int32>& created_ids() { return created_ids_; } 45 const std::vector<int32>& creation_finished_ids() { 46 return creation_finished_ids_; 47 } 48 const std::vector<int32>& new_root_ids() { return new_root_ids_; } 49 50 private: 51 std::vector<int32> deleted_ids_; 52 std::vector<int32> created_ids_; 53 std::vector<int32> creation_finished_ids_; 54 std::vector<int32> changed_ids_; 55 std::vector<int32> change_finished_ids_; 56 std::vector<int32> new_root_ids_; 57 }; 58 59 } // namespace 60 61 TEST(AXTreeTest, SerializeSimpleAXTree) { 62 AXNodeData root; 63 root.id = 1; 64 root.role = AX_ROLE_ROOT_WEB_AREA; 65 root.state = (1 << AX_STATE_FOCUSABLE) | (1 << AX_STATE_FOCUSED); 66 root.location = gfx::Rect(0, 0, 800, 600); 67 root.child_ids.push_back(2); 68 root.child_ids.push_back(3); 69 70 AXNodeData button; 71 button.id = 2; 72 button.role = AX_ROLE_BUTTON; 73 button.state = 0; 74 button.location = gfx::Rect(20, 20, 200, 30); 75 76 AXNodeData checkbox; 77 checkbox.id = 3; 78 checkbox.role = AX_ROLE_CHECK_BOX; 79 checkbox.state = 0; 80 checkbox.location = gfx::Rect(20, 50, 200, 30); 81 82 AXTreeUpdate initial_state; 83 initial_state.nodes.push_back(root); 84 initial_state.nodes.push_back(button); 85 initial_state.nodes.push_back(checkbox); 86 AXSerializableTree src_tree(initial_state); 87 88 scoped_ptr<AXTreeSource<const AXNode*> > tree_source( 89 src_tree.CreateTreeSource()); 90 AXTreeSerializer<const AXNode*> serializer(tree_source.get()); 91 AXTreeUpdate update; 92 serializer.SerializeChanges(src_tree.GetRoot(), &update); 93 94 AXTree dst_tree; 95 ASSERT_TRUE(dst_tree.Unserialize(update)); 96 97 const AXNode* root_node = dst_tree.GetRoot(); 98 ASSERT_TRUE(root_node != NULL); 99 EXPECT_EQ(root.id, root_node->id()); 100 EXPECT_EQ(root.role, root_node->data().role); 101 102 ASSERT_EQ(2, root_node->child_count()); 103 104 const AXNode* button_node = root_node->ChildAtIndex(0); 105 EXPECT_EQ(button.id, button_node->id()); 106 EXPECT_EQ(button.role, button_node->data().role); 107 108 const AXNode* checkbox_node = root_node->ChildAtIndex(1); 109 EXPECT_EQ(checkbox.id, checkbox_node->id()); 110 EXPECT_EQ(checkbox.role, checkbox_node->data().role); 111 112 EXPECT_EQ( 113 "id=1 rootWebArea FOCUSABLE FOCUSED (0, 0)-(800, 600) child_ids=2,3\n" 114 " id=2 button (20, 20)-(200, 30)\n" 115 " id=3 checkBox (20, 50)-(200, 30)\n", 116 dst_tree.ToString()); 117 } 118 119 TEST(AXTreeTest, SerializeAXTreeUpdate) { 120 AXNodeData list; 121 list.id = 3; 122 list.role = AX_ROLE_LIST; 123 list.state = 0; 124 list.child_ids.push_back(4); 125 list.child_ids.push_back(5); 126 list.child_ids.push_back(6); 127 128 AXNodeData list_item_2; 129 list_item_2.id = 5; 130 list_item_2.role = AX_ROLE_LIST_ITEM; 131 list_item_2.state = 0; 132 133 AXNodeData list_item_3; 134 list_item_3.id = 6; 135 list_item_3.role = AX_ROLE_LIST_ITEM; 136 list_item_3.state = 0; 137 138 AXNodeData button; 139 button.id = 7; 140 button.role = AX_ROLE_BUTTON; 141 button.state = 0; 142 143 AXTreeUpdate update; 144 update.nodes.push_back(list); 145 update.nodes.push_back(list_item_2); 146 update.nodes.push_back(list_item_3); 147 update.nodes.push_back(button); 148 149 EXPECT_EQ( 150 "id=3 list (0, 0)-(0, 0) child_ids=4,5,6\n" 151 " id=5 listItem (0, 0)-(0, 0)\n" 152 " id=6 listItem (0, 0)-(0, 0)\n" 153 "id=7 button (0, 0)-(0, 0)\n", 154 update.ToString()); 155 } 156 157 TEST(AXTreeTest, DeleteUnknownSubtreeFails) { 158 AXNodeData root; 159 root.id = 1; 160 root.role = AX_ROLE_ROOT_WEB_AREA; 161 162 AXTreeUpdate initial_state; 163 initial_state.nodes.push_back(root); 164 AXTree tree(initial_state); 165 166 // This should fail because we're asking it to delete 167 // a subtree rooted at id=2, which doesn't exist. 168 AXTreeUpdate update; 169 update.node_id_to_clear = 2; 170 update.nodes.resize(1); 171 update.nodes[0].id = 1; 172 update.nodes[0].id = AX_ROLE_ROOT_WEB_AREA; 173 EXPECT_FALSE(tree.Unserialize(update)); 174 ASSERT_EQ("Bad node_id_to_clear: 2", tree.error()); 175 } 176 177 TEST(AXTreeTest, LeaveOrphanedDeletedSubtreeFails) { 178 AXTreeUpdate initial_state; 179 initial_state.nodes.resize(3); 180 initial_state.nodes[0].id = 1; 181 initial_state.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 182 initial_state.nodes[0].child_ids.push_back(2); 183 initial_state.nodes[0].child_ids.push_back(3); 184 initial_state.nodes[1].id = 2; 185 initial_state.nodes[2].id = 3; 186 AXTree tree(initial_state); 187 188 // This should fail because we delete a subtree rooted at id=2 189 // but never update it. 190 AXTreeUpdate update; 191 update.node_id_to_clear = 2; 192 update.nodes.resize(1); 193 update.nodes[0].id = 3; 194 EXPECT_FALSE(tree.Unserialize(update)); 195 ASSERT_EQ("Nodes left pending by the update: 2", tree.error()); 196 } 197 198 TEST(AXTreeTest, LeaveOrphanedNewChildFails) { 199 AXTreeUpdate initial_state; 200 initial_state.nodes.resize(1); 201 initial_state.nodes[0].id = 1; 202 initial_state.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 203 AXTree tree(initial_state); 204 205 // This should fail because we add a new child to the root node 206 // but never update it. 207 AXTreeUpdate update; 208 update.nodes.resize(1); 209 update.nodes[0].id = 1; 210 update.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 211 update.nodes[0].child_ids.push_back(2); 212 EXPECT_FALSE(tree.Unserialize(update)); 213 ASSERT_EQ("Nodes left pending by the update: 2", tree.error()); 214 } 215 216 TEST(AXTreeTest, DuplicateChildIdFails) { 217 AXTreeUpdate initial_state; 218 initial_state.nodes.resize(1); 219 initial_state.nodes[0].id = 1; 220 initial_state.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 221 AXTree tree(initial_state); 222 223 // This should fail because a child id appears twice. 224 AXTreeUpdate update; 225 update.nodes.resize(2); 226 update.nodes[0].id = 1; 227 update.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 228 update.nodes[0].child_ids.push_back(2); 229 update.nodes[0].child_ids.push_back(2); 230 update.nodes[1].id = 2; 231 EXPECT_FALSE(tree.Unserialize(update)); 232 ASSERT_EQ("Node 1 has duplicate child id 2", tree.error()); 233 } 234 235 TEST(AXTreeTest, InvalidReparentingFails) { 236 AXTreeUpdate initial_state; 237 initial_state.nodes.resize(3); 238 initial_state.nodes[0].id = 1; 239 initial_state.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 240 initial_state.nodes[0].child_ids.push_back(2); 241 initial_state.nodes[1].id = 2; 242 initial_state.nodes[1].child_ids.push_back(3); 243 initial_state.nodes[2].id = 3; 244 245 AXTree tree(initial_state); 246 247 // This should fail because node 3 is reparented from node 2 to node 1 248 // without deleting node 1's subtree first. 249 AXTreeUpdate update; 250 update.nodes.resize(3); 251 update.nodes[0].id = 1; 252 update.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 253 update.nodes[0].child_ids.push_back(3); 254 update.nodes[0].child_ids.push_back(2); 255 update.nodes[1].id = 2; 256 update.nodes[2].id = 3; 257 EXPECT_FALSE(tree.Unserialize(update)); 258 ASSERT_EQ("Node 3 reparented from 2 to 1", tree.error()); 259 } 260 261 TEST(AXTreeTest, TreeDelegateIsCalled) { 262 AXTreeUpdate initial_state; 263 initial_state.nodes.resize(1); 264 initial_state.nodes[0].id = 1; 265 initial_state.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 266 267 AXTree tree(initial_state); 268 AXTreeUpdate update; 269 update.node_id_to_clear = 1; 270 update.nodes.resize(2); 271 update.nodes[0].id = 2; 272 update.nodes[0].role = AX_ROLE_ROOT_WEB_AREA; 273 update.nodes[0].child_ids.push_back(3); 274 update.nodes[1].id = 3; 275 276 FakeAXTreeDelegate fake_delegate; 277 tree.SetDelegate(&fake_delegate); 278 279 EXPECT_TRUE(tree.Unserialize(update)); 280 281 ASSERT_EQ(1U, fake_delegate.deleted_ids().size()); 282 EXPECT_EQ(1, fake_delegate.deleted_ids()[0]); 283 284 ASSERT_EQ(2U, fake_delegate.created_ids().size()); 285 EXPECT_EQ(2, fake_delegate.created_ids()[0]); 286 EXPECT_EQ(3, fake_delegate.created_ids()[1]); 287 288 ASSERT_EQ(2U, fake_delegate.creation_finished_ids().size()); 289 EXPECT_EQ(2, fake_delegate.creation_finished_ids()[0]); 290 EXPECT_EQ(3, fake_delegate.creation_finished_ids()[1]); 291 292 ASSERT_EQ(1U, fake_delegate.new_root_ids().size()); 293 EXPECT_EQ(2, fake_delegate.new_root_ids()[0]); 294 295 tree.SetDelegate(NULL); 296 } 297 298 } // namespace ui 299