blob: 00c9a9ec30c29d3cdc6d9e17896089a5146e6b93 [file] [log] [blame]
Sharon Yang28eac792019-10-16 00:28:231// Copyright 2019 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 "fuchsia/engine/browser/ax_tree_converter.h"
6
7#include <lib/ui/scenic/cpp/commands.h>
8#include <vector>
9
10#include "testing/gtest/include/gtest/gtest.h"
11#include "ui/gfx/transform.h"
12
13using fuchsia::accessibility::semantics::Action;
14using fuchsia::accessibility::semantics::Attributes;
Sharon Yang6f3a47b2020-01-08 21:32:2915using fuchsia::accessibility::semantics::CheckedState;
Sharon Yang28eac792019-10-16 00:28:2316using fuchsia::accessibility::semantics::Node;
17using fuchsia::accessibility::semantics::Role;
Sharon Yang6f3a47b2020-01-08 21:32:2918using fuchsia::accessibility::semantics::States;
Sharon Yang28eac792019-10-16 00:28:2319
20namespace {
21
22const char kLabel1[] = "label nodes, not people";
23const char kLabel2[] = "fancy stickers";
Sharon Yang6f3a47b2020-01-08 21:32:2924const char kDescription1[] = "this node does some stuff";
25const char kValue1[] = "user entered value";
Sharon Yang28eac792019-10-16 00:28:2326const int32_t kChildId1 = 23901;
27const int32_t kChildId2 = 484345;
28const int32_t kChildId3 = 4156877;
Tess Eisenberger77314d52020-10-12 23:30:2129const int32_t kRootId = 5;
Sharon Yang28eac792019-10-16 00:28:2330const int32_t kRectX = 1;
31const int32_t kRectY = 2;
32const int32_t kRectWidth = 7;
33const int32_t kRectHeight = 8;
Yilong Li4ada6192020-01-06 21:01:1934const std::array<float, 16> k4DIdentityMatrix = {1, 0, 0, 0, 0, 1, 0, 0,
35 0, 0, 1, 0, 0, 0, 0, 1};
Sharon Yang28eac792019-10-16 00:28:2336
Sharon Yang28eac792019-10-16 00:28:2337ui::AXNodeData CreateAXNodeData(ax::mojom::Role role,
Sharon Yang6f3a47b2020-01-08 21:32:2938 ax::mojom::Action action,
Sharon Yang28eac792019-10-16 00:28:2339 std::vector<int32_t> child_ids,
40 ui::AXRelativeBounds relative_bounds,
Sharon Yang6f3a47b2020-01-08 21:32:2941 base::StringPiece name,
42 base::StringPiece description,
43 ax::mojom::CheckedState checked_state) {
Sharon Yang28eac792019-10-16 00:28:2344 ui::AXNodeData node;
Lucas Radaelli9019dc22020-10-12 18:51:4545 // Important! ID must be set to zero here because its default value (-1), will
46 // fail when getting converted to an unsigned int (Fuchsia's ID format).
47 node.id = 0;
Sharon Yang28eac792019-10-16 00:28:2348 node.role = role;
Sharon Yang6f3a47b2020-01-08 21:32:2949 node.AddAction(action);
50 node.AddIntAttribute(ax::mojom::IntAttribute::kCheckedState,
51 static_cast<int32_t>(checked_state));
Sharon Yang28eac792019-10-16 00:28:2352 node.child_ids = child_ids;
53 node.relative_bounds = relative_bounds;
54 if (!name.empty())
55 node.AddStringAttribute(ax::mojom::StringAttribute::kName, name.data());
Sharon Yang6f3a47b2020-01-08 21:32:2956 if (!description.empty()) {
57 node.AddStringAttribute(ax::mojom::StringAttribute::kDescription,
58 description.data());
59 }
Sharon Yang28eac792019-10-16 00:28:2360 return node;
61}
62
63Node CreateSemanticNode(uint32_t id,
64 Role role,
65 Attributes attributes,
Sharon Yang6f3a47b2020-01-08 21:32:2966 States states,
Sharon Yang28eac792019-10-16 00:28:2367 std::vector<Action> actions,
68 std::vector<uint32_t> child_ids,
69 fuchsia::ui::gfx::BoundingBox location,
70 fuchsia::ui::gfx::mat4 transform) {
71 Node node;
72 node.set_node_id(id);
73 node.set_role(role);
74 node.set_attributes(std::move(attributes));
Sharon Yang6f3a47b2020-01-08 21:32:2975 node.set_states(std::move(states));
Sharon Yang28eac792019-10-16 00:28:2376 node.set_actions(actions);
77 node.set_child_ids(child_ids);
78 node.set_location(location);
79 node.set_transform(transform);
80 return node;
81}
82
Tess Eisenberger77314d52020-10-12 23:30:2183// Create an AXNodeData and a Fuchsia node that represent the same information.
84std::pair<ui::AXNodeData, Node> CreateSemanticNodeAllFieldsSet() {
Sharon Yang28eac792019-10-16 00:28:2385 ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds();
86 relative_bounds.bounds = gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight);
87 relative_bounds.transform =
88 std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization);
89 relative_bounds.transform->MakeIdentity();
Tess Eisenberger77314d52020-10-12 23:30:2190 auto ax_node_data = CreateAXNodeData(
Sharon Yang6f3a47b2020-01-08 21:32:2991 ax::mojom::Role::kButton, ax::mojom::Action::kFocus,
92 std::vector<int32_t>{kChildId1, kChildId2, kChildId3}, relative_bounds,
93 kLabel1, kDescription1, ax::mojom::CheckedState::kMixed);
Tess Eisenberger77314d52020-10-12 23:30:2194 ax_node_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, false);
95 ax_node_data.RemoveState(ax::mojom::State::kIgnored);
96 ax_node_data.id = kChildId1;
Sharon Yang28eac792019-10-16 00:28:2397
98 Attributes attributes;
99 attributes.set_label(kLabel1);
Sharon Yang6f3a47b2020-01-08 21:32:29100 attributes.set_secondary_label(kDescription1);
Sharon Yang28eac792019-10-16 00:28:23101 fuchsia::ui::gfx::BoundingBox box;
Yilong Li4ada6192020-01-06 21:01:19102 box.min = scenic::NewVector3({kRectX, kRectY + kRectHeight, 0.0f});
103 box.max = scenic::NewVector3({kRectHeight, kRectY, 0.0f});
Sharon Yang28eac792019-10-16 00:28:23104 fuchsia::ui::gfx::Matrix4Value mat =
105 scenic::NewMatrix4Value(k4DIdentityMatrix);
Sharon Yang6f3a47b2020-01-08 21:32:29106 States states;
107 states.set_checked_state(CheckedState::MIXED);
108 states.set_hidden(false);
109 states.set_selected(false);
Tess Eisenberger77314d52020-10-12 23:30:21110 auto fuchsia_node = CreateSemanticNode(
111 ConvertToFuchsiaNodeId(ax_node_data.id, kRootId), Role::BUTTON,
Lucas Radaelli9019dc22020-10-12 18:51:45112 std::move(attributes), std::move(states),
113 std::vector<Action>{Action::SET_FOCUS},
Sharon Yang28eac792019-10-16 00:28:23114 std::vector<uint32_t>{kChildId1, kChildId2, kChildId3}, box, mat.value);
115
Tess Eisenberger77314d52020-10-12 23:30:21116 return std::make_pair(std::move(ax_node_data), std::move(fuchsia_node));
117}
118
119class AXTreeConverterTest : public testing::Test {
120 public:
121 AXTreeConverterTest() = default;
122 ~AXTreeConverterTest() override = default;
123
124 DISALLOW_COPY_AND_ASSIGN(AXTreeConverterTest);
125};
126
127TEST_F(AXTreeConverterTest, AllFieldsSetAndEqual) {
128 auto nodes = CreateSemanticNodeAllFieldsSet();
129 auto& source_node_data = nodes.first;
130 auto& expected_node = nodes.second;
131
132 auto converted_node = AXNodeDataToSemanticNode(source_node_data);
133 EXPECT_EQ(ConvertToFuchsiaNodeId(source_node_data.id, kRootId),
134 converted_node.node_id());
135
Sharon Yang6f3a47b2020-01-08 21:32:29136 EXPECT_TRUE(fidl::Equals(converted_node, expected_node));
Sharon Yang28eac792019-10-16 00:28:23137}
138
139TEST_F(AXTreeConverterTest, SomeFieldsSetAndEqual) {
140 ui::AXNodeData source_node_data;
Lucas Radaelli9019dc22020-10-12 18:51:45141 source_node_data.id = 0;
Sharon Yang6f3a47b2020-01-08 21:32:29142 source_node_data.AddAction(ax::mojom::Action::kFocus);
143 source_node_data.AddAction(ax::mojom::Action::kSetValue);
Sharon Yang28eac792019-10-16 00:28:23144 source_node_data.child_ids = std::vector<int32_t>{kChildId1};
Sharon Yang6f3a47b2020-01-08 21:32:29145 source_node_data.role = ax::mojom::Role::kImage;
146 source_node_data.AddStringAttribute(ax::mojom::StringAttribute::kValue,
147 kValue1);
Sharon Yang28eac792019-10-16 00:28:23148 auto converted_node = AXNodeDataToSemanticNode(source_node_data);
149 EXPECT_EQ(static_cast<uint32_t>(source_node_data.id),
150 converted_node.node_id());
151
152 Node expected_node;
Lucas Radaelli9019dc22020-10-12 18:51:45153 expected_node.set_node_id(static_cast<uint32_t>(source_node_data.id));
Sharon Yang6f3a47b2020-01-08 21:32:29154 expected_node.set_actions(
155 std::vector<Action>{Action::SET_FOCUS, Action::SET_VALUE});
Lucas Radaelli9019dc22020-10-12 18:51:45156 expected_node.set_child_ids(
157 std::vector<uint32_t>{static_cast<uint32_t>(kChildId1)});
Sharon Yang6f3a47b2020-01-08 21:32:29158 expected_node.set_role(Role::IMAGE);
159 States states;
160 states.set_hidden(false);
161 states.set_value(kValue1);
162 expected_node.set_states(std::move(states));
163 Attributes attributes;
164 expected_node.set_attributes(std::move(attributes));
165 fuchsia::ui::gfx::BoundingBox box;
166 expected_node.set_location(std::move(box));
Sharon Yang28eac792019-10-16 00:28:23167
Sharon Yang6f3a47b2020-01-08 21:32:29168 EXPECT_TRUE(fidl::Equals(converted_node, expected_node));
Sharon Yang28eac792019-10-16 00:28:23169}
170
171TEST_F(AXTreeConverterTest, FieldMismatch) {
172 ui::AXRelativeBounds relative_bounds = ui::AXRelativeBounds();
173 relative_bounds.bounds = gfx::RectF(kRectX, kRectY, kRectWidth, kRectHeight);
174 relative_bounds.transform =
175 std::make_unique<gfx::Transform>(gfx::Transform::kSkipInitialization);
176 relative_bounds.transform->MakeIdentity();
Sharon Yang6f3a47b2020-01-08 21:32:29177 auto source_node_data = CreateAXNodeData(
178 ax::mojom::Role::kHeader, ax::mojom::Action::kSetValue,
179 std::vector<int32_t>{kChildId1, kChildId2, kChildId3}, relative_bounds,
180 kLabel1, kDescription1, ax::mojom::CheckedState::kFalse);
Sharon Yang28eac792019-10-16 00:28:23181 auto converted_node = AXNodeDataToSemanticNode(source_node_data);
182 EXPECT_EQ(static_cast<uint32_t>(source_node_data.id),
183 converted_node.node_id());
184
185 Attributes attributes;
186 attributes.set_label(kLabel1);
Sharon Yang6f3a47b2020-01-08 21:32:29187 attributes.set_secondary_label(kDescription1);
188 States states;
189 states.set_hidden(false);
190 states.set_checked_state(CheckedState::UNCHECKED);
Sharon Yang28eac792019-10-16 00:28:23191 fuchsia::ui::gfx::BoundingBox box;
Sharon Yang6f3a47b2020-01-08 21:32:29192 box.min = scenic::NewVector3({kRectX, kRectY + kRectHeight, 0.0f});
193 box.max = scenic::NewVector3({kRectHeight, kRectY, 0.0f});
Sharon Yang28eac792019-10-16 00:28:23194 fuchsia::ui::gfx::Matrix4Value mat =
195 scenic::NewMatrix4Value(k4DIdentityMatrix);
196 auto expected_node = CreateSemanticNode(
Sharon Yang6f3a47b2020-01-08 21:32:29197 source_node_data.id, Role::HEADER, std::move(attributes),
198 std::move(states), std::vector<Action>{Action::SET_VALUE},
Sharon Yang28eac792019-10-16 00:28:23199 std::vector<uint32_t>{kChildId1, kChildId2, kChildId3}, box, mat.value);
200
201 // Start with nodes being equal.
Sharon Yang6f3a47b2020-01-08 21:32:29202 EXPECT_TRUE(fidl::Equals(converted_node, expected_node));
Sharon Yang28eac792019-10-16 00:28:23203
204 // Make a copy of |source_node_data| and change the name attribute. Check that
205 // the resulting |converted_node| is different from |expected_node|.
206 auto modified_node_data = source_node_data;
207 modified_node_data.AddStringAttribute(ax::mojom::StringAttribute::kName,
208 kLabel2);
209 converted_node = AXNodeDataToSemanticNode(modified_node_data);
Sharon Yang6f3a47b2020-01-08 21:32:29210 EXPECT_FALSE(fidl::Equals(converted_node, expected_node));
Sharon Yang28eac792019-10-16 00:28:23211
212 // The same as above, this time changing |child_ids|.
213 modified_node_data = source_node_data;
214 modified_node_data.child_ids = std::vector<int32_t>{};
215 converted_node = AXNodeDataToSemanticNode(modified_node_data);
Sharon Yang6f3a47b2020-01-08 21:32:29216 EXPECT_FALSE(fidl::Equals(converted_node, expected_node));
Sharon Yang28eac792019-10-16 00:28:23217}
218
Tess Eisenberger77314d52020-10-12 23:30:21219TEST_F(AXTreeConverterTest, DefaultAction) {
220 auto nodes = CreateSemanticNodeAllFieldsSet();
221 auto& source_node_data = nodes.first;
222 auto& expected_node = nodes.second;
223
224 // Default action verb on an AXNodeData is equivalent to Action::DEFAULT on a
225 // Fuchsia semantic node.
226 source_node_data.SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kClick);
227 expected_node.mutable_actions()->insert(
228 expected_node.mutable_actions()->begin(),
229 fuchsia::accessibility::semantics::Action::DEFAULT);
230
231 auto converted_node = AXNodeDataToSemanticNode(source_node_data);
232 EXPECT_EQ(ConvertToFuchsiaNodeId(source_node_data.id, kRootId),
233 converted_node.node_id());
234
235 EXPECT_TRUE(fidl::Equals(converted_node, expected_node));
236}
237
Lucas Radaelli9019dc22020-10-12 18:51:45238TEST_F(AXTreeConverterTest, ConvertToFuchsiaNodeId) {
239 // Root AxNode is 0, Fuchsia is also 0.
240 EXPECT_EQ(0u, ConvertToFuchsiaNodeId(0, 0));
241
242 // Root AxNode is not 0, Fuchsia is still 0.
243 EXPECT_EQ(0u, ConvertToFuchsiaNodeId(2, 2));
244
245 // Regular AxNode is 0, Fuchsia can't be 0.
246 EXPECT_EQ(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) + 1,
247 ConvertToFuchsiaNodeId(0, 2));
248
249 // Regular AxNode is not 0, Fuchsia is same value.
250 EXPECT_EQ(10u, ConvertToFuchsiaNodeId(10, 0));
251}
252
Lucas Radaelli3ea1ece22020-10-13 17:10:06253TEST_F(AXTreeConverterTest, ConvertRoles) {
254 ui::AXNodeData node;
255 node.id = 0;
256 node.role = ax::mojom::Role::kButton;
257 EXPECT_EQ(fuchsia::accessibility::semantics::Role::BUTTON,
258 AXNodeDataToSemanticNode(node).role());
259
260 node.role = ax::mojom::Role::kCheckBox;
261 EXPECT_EQ(fuchsia::accessibility::semantics::Role::CHECK_BOX,
262 AXNodeDataToSemanticNode(node).role());
263
264 node.role = ax::mojom::Role::kHeader;
265 EXPECT_EQ(fuchsia::accessibility::semantics::Role::HEADER,
266 AXNodeDataToSemanticNode(node).role());
267
268 node.role = ax::mojom::Role::kImage;
269 EXPECT_EQ(fuchsia::accessibility::semantics::Role::IMAGE,
270 AXNodeDataToSemanticNode(node).role());
271
272 node.role = ax::mojom::Role::kLink;
273 EXPECT_EQ(fuchsia::accessibility::semantics::Role::LINK,
274 AXNodeDataToSemanticNode(node).role());
275
276 node.role = ax::mojom::Role::kRadioButton;
277 EXPECT_EQ(fuchsia::accessibility::semantics::Role::RADIO_BUTTON,
278 AXNodeDataToSemanticNode(node).role());
279
280 node.role = ax::mojom::Role::kSlider;
281 EXPECT_EQ(fuchsia::accessibility::semantics::Role::SLIDER,
282 AXNodeDataToSemanticNode(node).role());
283
284 node.role = ax::mojom::Role::kTextField;
285 EXPECT_EQ(fuchsia::accessibility::semantics::Role::TEXT_FIELD,
286 AXNodeDataToSemanticNode(node).role());
287
288 node.role = ax::mojom::Role::kStaticText;
289 EXPECT_EQ(fuchsia::accessibility::semantics::Role::STATIC_TEXT,
290 AXNodeDataToSemanticNode(node).role());
291}
292
Sharon Yang28eac792019-10-16 00:28:23293} // namespace