blob: 17173f77ada4a8b03c39aca140f1fb28d0f0a9eb [file] [log] [blame]
[email protected]5d94eb92014-01-03 17:42:461// 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
danakj25c52c32016-04-12 21:51:085#include <memory>
6
avi9c81217b2015-12-24 23:40:057#include "base/macros.h"
[email protected]5d94eb92014-01-03 17:42:468#include "base/strings/string_number_conversions.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "ui/accessibility/ax_node.h"
11#include "ui/accessibility/ax_serializable_tree.h"
12#include "ui/accessibility/ax_tree.h"
13#include "ui/accessibility/ax_tree_serializer.h"
[email protected]65f0f142014-06-30 22:51:3014#include "ui/accessibility/tree_generator.h"
[email protected]5d94eb92014-01-03 17:42:4615
16namespace ui {
17namespace {
18
19// A function to turn a tree into a string, capturing only the node ids
20// and their relationship to one another.
21//
22// The string format is kind of like an S-expression, with each expression
23// being either a node id, or a node id followed by a subexpression
24// representing its children.
25//
26// Examples:
27//
28// (1) is a tree with a single node with id 1.
29// (1 (2 3)) is a tree with 1 as the root, and 2 and 3 as its children.
30// (1 (2 (3))) has 1 as the root, 2 as its child, and then 3 as the child of 2.
31void TreeToStringHelper(const AXNode* node, std::string* out_result) {
32 *out_result += base::IntToString(node->id());
33 if (node->child_count() != 0) {
34 *out_result += " (";
35 for (int i = 0; i < node->child_count(); ++i) {
36 if (i != 0)
37 *out_result += " ";
38 TreeToStringHelper(node->ChildAtIndex(i), out_result);
39 }
40 *out_result += ")";
41 }
42}
43
44std::string TreeToString(const AXTree& tree) {
45 std::string result;
tfarina6b1c1e082015-02-20 23:47:0746 TreeToStringHelper(tree.root(), &result);
[email protected]5d94eb92014-01-03 17:42:4647 return "(" + result + ")";
48}
49
50} // anonymous namespace
51
[email protected]5d94eb92014-01-03 17:42:4652// Test the TreeGenerator class by building all possible trees with
dmazzoniee2eaca52015-03-18 18:13:0753// 3 nodes and the ids [1...3], with no permutations of ids.
54TEST(AXGeneratedTreeTest, TestTreeGeneratorNoPermutations) {
[email protected]5d94eb92014-01-03 17:42:4655 int tree_size = 3;
dmazzoniee2eaca52015-03-18 18:13:0756 TreeGenerator generator(tree_size, false);
[email protected]5d94eb92014-01-03 17:42:4657 const char* EXPECTED_TREES[] = {
dmazzoniee2eaca52015-03-18 18:13:0758 "(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>(arraysize(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.
77TEST(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))",
[email protected]5d94eb92014-01-03 17:42:4684 "(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>(arraysize(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.
118TEST(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
dmazzoniee2eaca52015-03-18 18:13:07123 int max_tree_size = 4;
[email protected]5d94eb92014-01-03 17:42:46124#else
125 LOG(WARNING) << "Debug build, only testing trees with 3 nodes and not 4.";
dmazzoniee2eaca52015-03-18 18:13:07126 int max_tree_size = 3;
[email protected]5d94eb92014-01-03 17:42:46127#endif
128
dmazzoniee2eaca52015-03-18 18:13:07129 TreeGenerator generator0(max_tree_size, false);
130 int n0 = generator0.UniqueTreeCount();
[email protected]5d94eb92014-01-03 17:42:46131
dmazzoniee2eaca52015-03-18 18:13:07132 TreeGenerator generator1(max_tree_size, true);
133 int n1 = generator1.UniqueTreeCount();
134
135 for (int i = 0; i < n0; i++) {
[email protected]5d94eb92014-01-03 17:42:46136 // Build the first tree, tree0.
137 AXSerializableTree tree0;
dmazzoniee2eaca52015-03-18 18:13:07138 generator0.BuildUniqueTree(i, &tree0);
[email protected]5d94eb92014-01-03 17:42:46139 SCOPED_TRACE("tree0 is " + TreeToString(tree0));
140
dmazzoniee2eaca52015-03-18 18:13:07141 for (int j = 0; j < n1; j++) {
[email protected]5d94eb92014-01-03 17:42:46142 // Build the second tree, tree1.
143 AXSerializableTree tree1;
dmazzoniee2eaca52015-03-18 18:13:07144 generator1.BuildUniqueTree(j, &tree1);
145 SCOPED_TRACE("tree1 is " + TreeToString(tree1));
146
147 int tree_size = tree1.size();
[email protected]5d94eb92014-01-03 17:42:46148
149 // Now iterate over which node to update first, |k|.
150 for (int k = 0; k < tree_size; k++) {
151 SCOPED_TRACE("i=" + base::IntToString(i) +
152 " j=" + base::IntToString(j) +
153 " k=" + base::IntToString(k));
154
155 // Start by serializing tree0 and unserializing it into a new
156 // empty tree |dst_tree|.
danakj25c52c32016-04-12 21:51:08157 std::unique_ptr<AXTreeSource<const AXNode*, AXNodeData, AXTreeData>>
dmazzoni329fd012015-10-22 20:05:35158 tree0_source(tree0.CreateTreeSource());
159 AXTreeSerializer<const AXNode*, AXNodeData, AXTreeData> serializer(
dmazzoniac6cdd02015-08-04 21:07:06160 tree0_source.get());
dmazzoni329fd012015-10-22 20:05:35161 AXTreeUpdate update0;
dmazzoni8f5c3342016-02-16 20:11:16162 ASSERT_TRUE(serializer.SerializeChanges(tree0.root(), &update0));
[email protected]5d94eb92014-01-03 17:42:46163
164 AXTree dst_tree;
165 ASSERT_TRUE(dst_tree.Unserialize(update0));
166
167 // At this point, |dst_tree| should now be identical to |tree0|.
168 EXPECT_EQ(TreeToString(tree0), TreeToString(dst_tree));
169
170 // Next, pretend that tree0 turned into tree1, and serialize
171 // a sequence of updates to |dst_tree| to match.
danakj25c52c32016-04-12 21:51:08172 std::unique_ptr<AXTreeSource<const AXNode*, AXNodeData, AXTreeData>>
dmazzoni329fd012015-10-22 20:05:35173 tree1_source(tree1.CreateTreeSource());
[email protected]5d94eb92014-01-03 17:42:46174 serializer.ChangeTreeSourceForTesting(tree1_source.get());
175
176 for (int k_index = 0; k_index < tree_size; ++k_index) {
177 int id = 1 + (k + k_index) % tree_size;
dmazzoni329fd012015-10-22 20:05:35178 AXTreeUpdate update;
dmazzoni8f5c3342016-02-16 20:11:16179 ASSERT_TRUE(
180 serializer.SerializeChanges(tree1.GetFromId(id), &update));
[email protected]5d94eb92014-01-03 17:42:46181 ASSERT_TRUE(dst_tree.Unserialize(update));
182 }
183
184 // After the sequence of updates, |dst_tree| should now be
185 // identical to |tree1|.
186 EXPECT_EQ(TreeToString(tree1), TreeToString(dst_tree));
187 }
188 }
189 }
190}
191
192} // namespace ui