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
13namespace ui {
14
15// The framework for these tests is that each test sets up |treedata0_|
16// and |treedata1_| and then calls GetTreeSerializer, which creates a
17// serializer for a tree that's initially in state |treedata0_|, but then
18// changes to state |treedata1_|. This allows each test to check the
19// updates created by AXTreeSerializer or unit-test its private
20// member functions.
21class AXTreeSerializerTest : public testing::Test {
22 public:
23  AXTreeSerializerTest() {}
24  virtual ~AXTreeSerializerTest() {}
25
26 protected:
27  void CreateTreeSerializer();
28
29  AXTreeUpdate treedata0_;
30  AXTreeUpdate treedata1_;
31  scoped_ptr<AXSerializableTree> tree0_;
32  scoped_ptr<AXSerializableTree> tree1_;
33  scoped_ptr<AXTreeSource<const AXNode*> > tree0_source_;
34  scoped_ptr<AXTreeSource<const AXNode*> > tree1_source_;
35  scoped_ptr<AXTreeSerializer<const AXNode*> > serializer_;
36
37 private:
38  DISALLOW_COPY_AND_ASSIGN(AXTreeSerializerTest);
39};
40
41void AXTreeSerializerTest::CreateTreeSerializer() {
42  if (serializer_)
43    return;
44
45  tree0_.reset(new AXSerializableTree(treedata0_));
46  tree1_.reset(new AXSerializableTree(treedata1_));
47
48  // Serialize tree0 so that AXTreeSerializer thinks that its client
49  // is totally in sync.
50  tree0_source_.reset(tree0_->CreateTreeSource());
51  serializer_.reset(new AXTreeSerializer<const AXNode*>(tree0_source_.get()));
52  AXTreeUpdate unused_update;
53  serializer_->SerializeChanges(tree0_->GetRoot(), &unused_update);
54
55  // Pretend that tree0_ turned into tree1_. The next call to
56  // AXTreeSerializer will force it to consider these changes to
57  // the tree and send them as part of the next update.
58  tree1_source_.reset(tree1_->CreateTreeSource());
59  serializer_->ChangeTreeSourceForTesting(tree1_source_.get());
60}
61
62// In this test, one child is added to the root. Only the root and
63// new child should be added.
64TEST_F(AXTreeSerializerTest, UpdateContainsOnlyChangedNodes) {
65  // (1 (2 3))
66  treedata0_.nodes.resize(3);
67  treedata0_.nodes[0].id = 1;
68  treedata0_.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
69  treedata0_.nodes[0].child_ids.push_back(2);
70  treedata0_.nodes[0].child_ids.push_back(3);
71  treedata0_.nodes[1].id = 2;
72  treedata0_.nodes[2].id = 3;
73
74  // (1 (4 2 3))
75  treedata1_.nodes.resize(4);
76  treedata1_.nodes[0].id = 1;
77  treedata1_.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
78  treedata1_.nodes[0].child_ids.push_back(4);
79  treedata1_.nodes[0].child_ids.push_back(2);
80  treedata1_.nodes[0].child_ids.push_back(3);
81  treedata1_.nodes[1].id = 2;
82  treedata1_.nodes[2].id = 3;
83  treedata1_.nodes[3].id = 4;
84
85  CreateTreeSerializer();
86  AXTreeUpdate update;
87  serializer_->SerializeChanges(tree1_->GetFromId(1), &update);
88
89  // The update should only touch nodes 1 and 4 - nodes 2 and 3 are unchanged
90  // and shouldn't be affected.
91  EXPECT_EQ(0, update.node_id_to_clear);
92  ASSERT_EQ(static_cast<size_t>(2), update.nodes.size());
93  EXPECT_EQ(1, update.nodes[0].id);
94  EXPECT_EQ(4, update.nodes[1].id);
95}
96
97// When the root changes, the whole tree is updated, even if some of it
98// is unaffected.
99TEST_F(AXTreeSerializerTest, NewRootUpdatesEntireTree) {
100  // (1 (2 (3 (4))))
101  treedata0_.nodes.resize(4);
102  treedata0_.nodes[0].id = 1;
103  treedata0_.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
104  treedata0_.nodes[0].child_ids.push_back(2);
105  treedata0_.nodes[1].id = 2;
106  treedata0_.nodes[1].child_ids.push_back(3);
107  treedata0_.nodes[2].id = 3;
108  treedata0_.nodes[2].child_ids.push_back(4);
109  treedata0_.nodes[3].id = 4;
110
111  // (5 (2 (3 (4))))
112  treedata1_.nodes.resize(4);
113  treedata1_.nodes[0].id = 5;
114  treedata1_.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
115  treedata1_.nodes[0].child_ids.push_back(2);
116  treedata1_.nodes[1].id = 2;
117  treedata1_.nodes[1].child_ids.push_back(3);
118  treedata1_.nodes[2].id = 3;
119  treedata1_.nodes[2].child_ids.push_back(4);
120  treedata1_.nodes[3].id = 4;
121
122  CreateTreeSerializer();
123  AXTreeUpdate update;
124  serializer_->SerializeChanges(tree1_->GetFromId(4), &update);
125
126  // The update should delete the subtree rooted at node id=1, and
127  // then include all four nodes in the update, even though the
128  // subtree rooted at id=2 didn't actually change.
129  EXPECT_EQ(1, update.node_id_to_clear);
130  ASSERT_EQ(static_cast<size_t>(4), update.nodes.size());
131  EXPECT_EQ(5, update.nodes[0].id);
132  EXPECT_EQ(2, update.nodes[1].id);
133  EXPECT_EQ(3, update.nodes[2].id);
134  EXPECT_EQ(4, update.nodes[3].id);
135}
136
137// When a node is reparented, the subtree including both the old parent
138// and new parent of the reparented node must be deleted and recreated.
139TEST_F(AXTreeSerializerTest, ReparentingUpdatesSubtree) {
140  // (1 (2 (3 (4) 5)))
141  treedata0_.nodes.resize(5);
142  treedata0_.nodes[0].id = 1;
143  treedata0_.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
144  treedata0_.nodes[0].child_ids.push_back(2);
145  treedata0_.nodes[1].id = 2;
146  treedata0_.nodes[1].child_ids.push_back(3);
147  treedata0_.nodes[1].child_ids.push_back(5);
148  treedata0_.nodes[2].id = 3;
149  treedata0_.nodes[2].child_ids.push_back(4);
150  treedata0_.nodes[3].id = 4;
151  treedata0_.nodes[4].id = 5;
152
153  // Node 5 has been reparented from being a child of node 2,
154  // to a child of node 4.
155  // (1 (2 (3 (4 (5)))))
156  treedata1_.nodes.resize(5);
157  treedata1_.nodes[0].id = 1;
158  treedata1_.nodes[0].role = AX_ROLE_ROOT_WEB_AREA;
159  treedata1_.nodes[0].child_ids.push_back(2);
160  treedata1_.nodes[1].id = 2;
161  treedata1_.nodes[1].child_ids.push_back(3);
162  treedata1_.nodes[2].id = 3;
163  treedata1_.nodes[2].child_ids.push_back(4);
164  treedata1_.nodes[3].id = 4;
165  treedata1_.nodes[3].child_ids.push_back(5);
166  treedata1_.nodes[4].id = 5;
167
168  CreateTreeSerializer();
169  AXTreeUpdate update;
170  serializer_->SerializeChanges(tree1_->GetFromId(4), &update);
171
172  // The update should delete the subtree rooted at node id=2, and
173  // then include nodes 2...5.
174  EXPECT_EQ(2, update.node_id_to_clear);
175  ASSERT_EQ(static_cast<size_t>(4), update.nodes.size());
176  EXPECT_EQ(2, update.nodes[0].id);
177  EXPECT_EQ(3, update.nodes[1].id);
178  EXPECT_EQ(4, update.nodes[2].id);
179  EXPECT_EQ(5, update.nodes[3].id);
180}
181
182}  // namespace ui
183