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