1 // Copyright 2014 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 <memory>
6 #include <numeric>
7
8 #include "base/stl_util.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "ui/accessibility/ax_node.h"
12 #include "ui/accessibility/ax_serializable_tree.h"
13 #include "ui/accessibility/ax_tree.h"
14 #include "ui/accessibility/ax_tree_serializer.h"
15 #include "ui/accessibility/tree_generator.h"
16
17 namespace ui {
18 namespace {
19
20 // A function to turn a tree into a string, capturing only the node ids
21 // and their relationship to one another.
22 //
23 // The string format is kind of like an S-expression, with each expression
24 // being either a node id, or a node id followed by a subexpression
25 // representing its children.
26 //
27 // Examples:
28 //
29 // (1) is a tree with a single node with id 1.
30 // (1 (2 3)) is a tree with 1 as the root, and 2 and 3 as its children.
31 // (1 (2 (3))) has 1 as the root, 2 as its child, and then 3 as the child of 2.
TreeToStringHelper(const AXNode * node)32 std::string TreeToStringHelper(const AXNode* node) {
33 std::string result = base::NumberToString(node->id());
34 if (node->children().empty())
35 return result;
36 const auto add_children = [](const std::string& str, const auto* node) {
37 return str + " " + TreeToStringHelper(node);
38 };
39 return result + " (" +
40 std::accumulate(node->children().cbegin() + 1, node->children().cend(),
41 TreeToStringHelper(node->children().front()),
42 add_children) +
43 ")";
44 }
45
TreeToString(const AXTree & tree)46 std::string TreeToString(const AXTree& tree) {
47 return "(" + TreeToStringHelper(tree.root()) + ")";
48 }
49
50 } // anonymous namespace
51
52 // Test the TreeGenerator class by building all possible trees with
53 // 3 nodes and the ids [1...3], with no permutations of ids.
TEST(AXGeneratedTreeTest,TestTreeGeneratorNoPermutations)54 TEST(AXGeneratedTreeTest, TestTreeGeneratorNoPermutations) {
55 int tree_size = 3;
56 TreeGenerator generator(tree_size, false);
57 const char* EXPECTED_TREES[] = {
58 "(1)",
59 "(1 (2))",
60 "(1 (2 3))",
61 "(1 (2 (3)))",
62 };
63
64 int n = generator.UniqueTreeCount();
65 ASSERT_EQ(static_cast<int>(base::size(EXPECTED_TREES)), n);
66
67 for (int i = 0; i < n; ++i) {
68 AXTree tree;
69 generator.BuildUniqueTree(i, &tree);
70 std::string str = TreeToString(tree);
71 EXPECT_EQ(EXPECTED_TREES[i], str);
72 }
73 }
74
75 // Test the TreeGenerator class by building all possible trees with
76 // 3 nodes and the ids [1...3] permuted in any order.
TEST(AXGeneratedTreeTest,TestTreeGeneratorWithPermutations)77 TEST(AXGeneratedTreeTest, TestTreeGeneratorWithPermutations) {
78 int tree_size = 3;
79 TreeGenerator generator(tree_size, true);
80 const char* EXPECTED_TREES[] = {
81 "(1)",
82 "(1 (2))",
83 "(2 (1))",
84 "(1 (2 3))",
85 "(2 (1 3))",
86 "(3 (1 2))",
87 "(1 (3 2))",
88 "(2 (3 1))",
89 "(3 (2 1))",
90 "(1 (2 (3)))",
91 "(2 (1 (3)))",
92 "(3 (1 (2)))",
93 "(1 (3 (2)))",
94 "(2 (3 (1)))",
95 "(3 (2 (1)))",
96 };
97
98 int n = generator.UniqueTreeCount();
99 ASSERT_EQ(static_cast<int>(base::size(EXPECTED_TREES)), n);
100
101 for (int i = 0; i < n; i++) {
102 AXTree tree;
103 generator.BuildUniqueTree(i, &tree);
104 std::string str = TreeToString(tree);
105 EXPECT_EQ(EXPECTED_TREES[i], str);
106 }
107 }
108
109 // Test mutating every possible tree with <n> nodes to every other possible
110 // tree with <n> nodes, where <n> is 4 in release mode and 3 in debug mode
111 // (for speed). For each possible combination of trees, we also vary which
112 // node we serialize first.
113 //
114 // For every possible scenario, we check that the AXTreeUpdate is valid,
115 // that the destination tree can unserialize it and create a valid tree,
116 // and that after updating all nodes the resulting tree now matches the
117 // intended tree.
TEST(AXGeneratedTreeTest,SerializeGeneratedTrees)118 TEST(AXGeneratedTreeTest, SerializeGeneratedTrees) {
119 // Do a more exhaustive test in release mode. If you're modifying
120 // the algorithm you may want to try even larger tree sizes if you
121 // can afford the time.
122 #ifdef NDEBUG
123 int max_tree_size = 4;
124 #else
125 LOG(WARNING) << "Debug build, only testing trees with 3 nodes and not 4.";
126 int max_tree_size = 3;
127 #endif
128
129 TreeGenerator generator0(max_tree_size, false);
130 int n0 = generator0.UniqueTreeCount();
131
132 TreeGenerator generator1(max_tree_size, true);
133 int n1 = generator1.UniqueTreeCount();
134
135 for (int i = 0; i < n0; i++) {
136 // Build the first tree, tree0.
137 AXSerializableTree tree0;
138 generator0.BuildUniqueTree(i, &tree0);
139 SCOPED_TRACE("tree0 is " + TreeToString(tree0));
140
141 for (int j = 0; j < n1; j++) {
142 // Build the second tree, tree1.
143 AXSerializableTree tree1;
144 generator1.BuildUniqueTree(j, &tree1);
145 SCOPED_TRACE("tree1 is " + TreeToString(tree1));
146
147 int tree_size = tree1.size();
148
149 // Now iterate over which node to update first, |k|.
150 for (int k = 0; k < tree_size; k++) {
151 // Iterate over a node to invalidate, |l| (zero means no invalidation).
152 for (int l = 0; l <= tree_size; l++) {
153 SCOPED_TRACE("i=" + base::NumberToString(i) +
154 " j=" + base::NumberToString(j) +
155 " k=" + base::NumberToString(k) +
156 " l=" + base::NumberToString(l));
157
158 // Start by serializing tree0 and unserializing it into a new
159 // empty tree |dst_tree|.
160 std::unique_ptr<AXTreeSource<const AXNode*, AXNodeData, AXTreeData>>
161 tree0_source(tree0.CreateTreeSource());
162 AXTreeSerializer<const AXNode*, AXNodeData, AXTreeData> serializer(
163 tree0_source.get());
164 AXTreeUpdate update0;
165 ASSERT_TRUE(serializer.SerializeChanges(tree0.root(), &update0));
166
167 AXTree dst_tree;
168 ASSERT_TRUE(dst_tree.Unserialize(update0));
169
170 // At this point, |dst_tree| should now be identical to |tree0|.
171 EXPECT_EQ(TreeToString(tree0), TreeToString(dst_tree));
172
173 // Next, pretend that tree0 turned into tree1.
174 std::unique_ptr<AXTreeSource<const AXNode*, AXNodeData, AXTreeData>>
175 tree1_source(tree1.CreateTreeSource());
176 serializer.ChangeTreeSourceForTesting(tree1_source.get());
177
178 // Invalidate a subtree rooted at one of the nodes.
179 if (l > 0)
180 serializer.InvalidateSubtree(tree1.GetFromId(l));
181
182 // Serialize a sequence of updates to |dst_tree| to match.
183 for (int k_index = 0; k_index < tree_size; ++k_index) {
184 int id = 1 + (k + k_index) % tree_size;
185 AXTreeUpdate update;
186 ASSERT_TRUE(
187 serializer.SerializeChanges(tree1.GetFromId(id), &update));
188 ASSERT_TRUE(dst_tree.Unserialize(update));
189 }
190
191 // After the sequence of updates, |dst_tree| should now be
192 // identical to |tree1|.
193 EXPECT_EQ(TreeToString(tree1), TreeToString(dst_tree));
194 }
195 }
196 }
197 }
198 }
199
200 } // namespace ui
201