Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ax_table_info_unittest.cc
Go to the documentation of this file.
1// Copyright 2018 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 "ax_table_info.h"
6
7#include "gtest/gtest.h"
8
9#include "ax_enums.h"
10#include "ax_node.h"
11#include "ax_tree.h"
12
13namespace ui {
14
15namespace {
16
17void MakeTable(AXNodeData* table, int id, int row_count, int col_count) {
18 table->id = id;
20 table->AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, row_count);
21 table->AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, col_count);
22}
23
24void MakeRowGroup(AXNodeData* row_group, int id) {
25 row_group->id = id;
26 row_group->role = ax::mojom::Role::kRowGroup;
27}
28
29void MakeRow(AXNodeData* row, int id, int row_index) {
30 row->id = id;
31 row->role = ax::mojom::Role::kRow;
32 row->AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, row_index);
33}
34
35void MakeCell(AXNodeData* cell,
36 int id,
37 int row_index,
38 int col_index,
39 int row_span = 1,
40 int col_span = 1) {
41 cell->id = id;
42 cell->role = ax::mojom::Role::kCell;
43 cell->AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, row_index);
45 col_index);
46 if (row_span > 1)
47 cell->AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowSpan, row_span);
48 if (col_span > 1)
50 col_span);
51}
52
53void MakeColumnHeader(AXNodeData* cell,
54 int id,
55 int row_index,
56 int col_index,
57 int row_span = 1,
58 int col_span = 1) {
59 MakeCell(cell, id, row_index, col_index, row_span, col_span);
61}
62
63void MakeRowHeader(AXNodeData* cell,
64 int id,
65 int row_index,
66 int col_index,
67 int row_span = 1,
68 int col_span = 1) {
69 MakeCell(cell, id, row_index, col_index, row_span, col_span);
70 cell->role = ax::mojom::Role::kRowHeader;
71}
72
73} // namespace
74
75// A macro for testing that a std::optional has both a value and that its value
76// is set to a particular expectation.
77#define EXPECT_OPTIONAL_EQ(expected, actual) \
78 EXPECT_TRUE(actual.has_value()); \
79 if (actual) { \
80 EXPECT_EQ(expected, actual.value()); \
81 }
82
83class AXTableInfoTest : public testing::Test {
84 public:
86 ~AXTableInfoTest() override {}
87
88 protected:
90 return tree->GetTableInfo(node);
91 }
92
93 private:
95};
96
97TEST_F(AXTableInfoTest, SimpleTable) {
98 // Simple 2 x 2 table with 2 column headers in first row, 2 cells in second
99 // row. The first row is parented by a rowgroup.
100 AXTreeUpdate initial_state;
101 initial_state.root_id = 1;
102 initial_state.nodes.resize(8);
103 MakeTable(&initial_state.nodes[0], 1, 0, 0);
104 initial_state.nodes[0].child_ids = {888, 3};
105
106 MakeRowGroup(&initial_state.nodes[1], 888);
107 initial_state.nodes[1].child_ids = {2};
108
109 MakeRow(&initial_state.nodes[2], 2, 0);
110 initial_state.nodes[2].child_ids = {4, 5};
111
112 MakeRow(&initial_state.nodes[3], 3, 1);
113 initial_state.nodes[3].child_ids = {6, 7};
114 MakeColumnHeader(&initial_state.nodes[4], 4, 0, 0);
115 MakeColumnHeader(&initial_state.nodes[5], 5, 0, 1);
116 MakeCell(&initial_state.nodes[6], 6, 1, 0);
117 MakeCell(&initial_state.nodes[7], 7, 1, 1);
118 AXTree tree(initial_state);
119
120 //
121 // Low-level: test the AXTableInfo directly.
122 //
123
124 AXTableInfo* table_info = GetTableInfo(&tree, tree.root()->children()[0]);
125 EXPECT_FALSE(table_info);
126
127 table_info = GetTableInfo(&tree, tree.root());
128 EXPECT_TRUE(table_info);
129
130 EXPECT_EQ(2u, table_info->row_count);
131 EXPECT_EQ(2u, table_info->col_count);
132
133 EXPECT_EQ(2U, table_info->row_headers.size());
134 EXPECT_EQ(0U, table_info->row_headers[0].size());
135 EXPECT_EQ(0U, table_info->row_headers[1].size());
136
137 EXPECT_EQ(2U, table_info->col_headers.size());
138 EXPECT_EQ(1U, table_info->col_headers[0].size());
139 EXPECT_EQ(4, table_info->col_headers[0][0]);
140 EXPECT_EQ(1U, table_info->col_headers[1].size());
141 EXPECT_EQ(5, table_info->col_headers[1][0]);
142
143 EXPECT_EQ(4, table_info->cell_ids[0][0]);
144 EXPECT_EQ(5, table_info->cell_ids[0][1]);
145 EXPECT_EQ(6, table_info->cell_ids[1][0]);
146 EXPECT_EQ(7, table_info->cell_ids[1][1]);
147
148 EXPECT_EQ(4U, table_info->unique_cell_ids.size());
149 EXPECT_EQ(4, table_info->unique_cell_ids[0]);
150 EXPECT_EQ(5, table_info->unique_cell_ids[1]);
151 EXPECT_EQ(6, table_info->unique_cell_ids[2]);
152 EXPECT_EQ(7, table_info->unique_cell_ids[3]);
153
154 EXPECT_EQ(0u, table_info->cell_id_to_index[4]);
155 EXPECT_EQ(1u, table_info->cell_id_to_index[5]);
156 EXPECT_EQ(2u, table_info->cell_id_to_index[6]);
157 EXPECT_EQ(3u, table_info->cell_id_to_index[7]);
158
159 EXPECT_EQ(2u, table_info->row_nodes.size());
160 EXPECT_EQ(2, table_info->row_nodes[0]->data().id);
161 EXPECT_EQ(3, table_info->row_nodes[1]->data().id);
162
163 EXPECT_EQ(0U, table_info->extra_mac_nodes.size());
164
165 //
166 // High-level: Test the helper functions on AXNode.
167 //
168
169 AXNode* table = tree.root();
170 EXPECT_TRUE(table->IsTable());
171 EXPECT_FALSE(table->IsTableRow());
172 EXPECT_FALSE(table->IsTableCellOrHeader());
173 EXPECT_OPTIONAL_EQ(2, table->GetTableColCount());
174 EXPECT_OPTIONAL_EQ(2, table->GetTableRowCount());
175
176 ASSERT_TRUE(table->GetTableCellFromCoords(0, 0));
177 EXPECT_EQ(4, table->GetTableCellFromCoords(0, 0)->id());
178 EXPECT_EQ(5, table->GetTableCellFromCoords(0, 1)->id());
179 EXPECT_EQ(6, table->GetTableCellFromCoords(1, 0)->id());
180 EXPECT_EQ(7, table->GetTableCellFromCoords(1, 1)->id());
181 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(2, 1));
182 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(1, -1));
183
184 EXPECT_EQ(4, table->GetTableCellFromIndex(0)->id());
185 EXPECT_EQ(5, table->GetTableCellFromIndex(1)->id());
186 EXPECT_EQ(6, table->GetTableCellFromIndex(2)->id());
187 EXPECT_EQ(7, table->GetTableCellFromIndex(3)->id());
188 EXPECT_EQ(nullptr, table->GetTableCellFromIndex(-1));
189 EXPECT_EQ(nullptr, table->GetTableCellFromIndex(4));
190
191 AXNode* row_0 = tree.GetFromId(2);
192 EXPECT_FALSE(row_0->IsTable());
193 EXPECT_TRUE(row_0->IsTableRow());
194 EXPECT_FALSE(row_0->IsTableCellOrHeader());
196
197 AXNode* row_1 = tree.GetFromId(3);
198 EXPECT_FALSE(row_1->IsTable());
199 EXPECT_TRUE(row_1->IsTableRow());
200 EXPECT_FALSE(row_1->IsTableCellOrHeader());
202
203 AXNode* cell_0_0 = tree.GetFromId(4);
204 EXPECT_FALSE(cell_0_0->IsTable());
205 EXPECT_FALSE(cell_0_0->IsTableRow());
206 EXPECT_TRUE(cell_0_0->IsTableCellOrHeader());
212
213 AXNode* cell_1_1 = tree.GetFromId(7);
214 EXPECT_FALSE(cell_1_1->IsTable());
215 EXPECT_FALSE(cell_1_1->IsTableRow());
216 EXPECT_TRUE(cell_1_1->IsTableCellOrHeader());
221
222 std::vector<AXNode*> col_headers;
223 cell_1_1->GetTableCellColHeaders(&col_headers);
224 EXPECT_EQ(1U, col_headers.size());
225 EXPECT_EQ(5, col_headers[0]->id());
226
227 std::vector<AXNode*> row_headers;
228 cell_1_1->GetTableCellRowHeaders(&row_headers);
229 EXPECT_EQ(0U, row_headers.size());
230
231 EXPECT_EQ(2u, table->GetTableRowNodeIds().size());
232 EXPECT_EQ(2, table->GetTableRowNodeIds()[0]);
233 EXPECT_EQ(3, table->GetTableRowNodeIds()[1]);
234 EXPECT_EQ(2u, row_0->GetTableRowNodeIds().size());
235 EXPECT_EQ(2, row_0->GetTableRowNodeIds()[0]);
236 EXPECT_EQ(3, row_0->GetTableRowNodeIds()[1]);
237 EXPECT_EQ(2u, row_1->GetTableRowNodeIds().size());
238 EXPECT_EQ(2, row_1->GetTableRowNodeIds()[0]);
239 EXPECT_EQ(3, row_1->GetTableRowNodeIds()[1]);
240 EXPECT_EQ(2u, cell_0_0->GetTableRowNodeIds().size());
241 EXPECT_EQ(2, cell_0_0->GetTableRowNodeIds()[0]);
242 EXPECT_EQ(3, cell_0_0->GetTableRowNodeIds()[1]);
243 EXPECT_EQ(2u, cell_1_1->GetTableRowNodeIds().size());
244 EXPECT_EQ(2, cell_1_1->GetTableRowNodeIds()[0]);
245 EXPECT_EQ(3, cell_1_1->GetTableRowNodeIds()[1]);
246}
247
248TEST_F(AXTableInfoTest, ComputedTableSizeIncludesSpans) {
249 // Simple 2 x 2 table with 2 column headers in first row, 2 cells in second
250 // row, but two cells have spans, affecting the computed row and column count.
251 AXTreeUpdate initial_state;
252 initial_state.root_id = 1;
253 initial_state.nodes.resize(7);
254 MakeTable(&initial_state.nodes[0], 1, 0, 0);
255 initial_state.nodes[0].child_ids = {2, 3};
256 MakeRow(&initial_state.nodes[1], 2, 0);
257 initial_state.nodes[1].child_ids = {4, 5};
258 MakeRow(&initial_state.nodes[2], 3, 1);
259 initial_state.nodes[2].child_ids = {6, 7};
260 MakeCell(&initial_state.nodes[3], 4, 0, 0);
261 MakeCell(&initial_state.nodes[4], 5, 0, 1, 1, 5); // Column span of 5
262 MakeCell(&initial_state.nodes[5], 6, 1, 0);
263 MakeCell(&initial_state.nodes[6], 7, 1, 1, 3, 1); // Row span of 3
264 AXTree tree(initial_state);
265
266 AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
267 EXPECT_EQ(4u, table_info->row_count);
268 EXPECT_EQ(6u, table_info->col_count);
269
270 EXPECT_EQ(2u, table_info->row_nodes.size());
271 EXPECT_EQ(2, table_info->row_nodes[0]->data().id);
272 EXPECT_EQ(3, table_info->row_nodes[1]->data().id);
273}
274
275TEST_F(AXTableInfoTest, AuthorRowAndColumnCountsAreRespected) {
276 // Simple 1 x 1 table, but the table's authored row and column
277 // counts imply a larger table (with missing cells).
278 AXTreeUpdate initial_state;
279 initial_state.root_id = 1;
280 initial_state.nodes.resize(3);
281 MakeTable(&initial_state.nodes[0], 1, 8, 9);
282 initial_state.nodes[0].child_ids = {2};
283 MakeRow(&initial_state.nodes[1], 2, 0);
284 initial_state.nodes[1].child_ids = {3};
285 MakeCell(&initial_state.nodes[2], 3, 0, 1);
286 AXTree tree(initial_state);
287
288 AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
289 EXPECT_EQ(8u, table_info->row_count);
290 EXPECT_EQ(9u, table_info->col_count);
291
292 EXPECT_EQ(1u, table_info->row_nodes.size());
293 EXPECT_EQ(2, table_info->row_nodes[0]->data().id);
294}
295
296TEST_F(AXTableInfoTest, TableInfoRecomputedOnlyWhenTableChanges) {
297 // Simple 1 x 1 table.
298 AXTreeUpdate initial_state;
299 initial_state.root_id = 1;
300 initial_state.nodes.resize(3);
301 MakeTable(&initial_state.nodes[0], 1, 0, 0);
302 initial_state.nodes[0].child_ids = {2};
303 MakeRow(&initial_state.nodes[1], 2, 0);
304 initial_state.nodes[1].child_ids = {3};
305 MakeCell(&initial_state.nodes[2], 3, 0, 0);
306 AXTree tree(initial_state);
307
308 AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
309 EXPECT_EQ(1u, table_info->row_count);
310 EXPECT_EQ(1u, table_info->col_count);
311
312 // Table info is cached.
313 AXTableInfo* table_info_2 = GetTableInfo(&tree, tree.root());
314 EXPECT_EQ(table_info, table_info_2);
315
316 // Update the table so that the cell has a span.
317 AXTreeUpdate update = initial_state;
318 MakeCell(&update.nodes[2], 3, 0, 0, 1, 2);
320
321 AXTableInfo* table_info_3 = GetTableInfo(&tree, tree.root());
322 EXPECT_EQ(1u, table_info_3->row_count);
323 EXPECT_EQ(2u, table_info_3->col_count);
324
325 EXPECT_EQ(1u, table_info->row_nodes.size());
326 EXPECT_EQ(2, table_info->row_nodes[0]->data().id);
327}
328
329TEST_F(AXTableInfoTest, CellIdsHandlesSpansAndMissingCells) {
330 // 3 column x 2 row table with spans and missing cells:
331 //
332 // +---+---+---+
333 // | | 5 |
334 // + 4 +---+---+
335 // | | 6 |
336 // +---+---+
337 AXTreeUpdate initial_state;
338 initial_state.root_id = 1;
339 initial_state.nodes.resize(6);
340 MakeTable(&initial_state.nodes[0], 1, 0, 0);
341 initial_state.nodes[0].child_ids = {2, 3};
342 MakeRow(&initial_state.nodes[1], 2, 0);
343 initial_state.nodes[1].child_ids = {4, 5};
344 MakeRow(&initial_state.nodes[2], 3, 1);
345 initial_state.nodes[2].child_ids = {6};
346 MakeCell(&initial_state.nodes[3], 4, 0, 0, 2, 1); // Row span of 2
347 MakeCell(&initial_state.nodes[4], 5, 0, 1, 1, 5); // Column span of 2
348 MakeCell(&initial_state.nodes[5], 6, 1, 1);
349 AXTree tree(initial_state);
350
351 AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
352 EXPECT_EQ(4, table_info->cell_ids[0][0]);
353 EXPECT_EQ(5, table_info->cell_ids[0][1]);
354 EXPECT_EQ(5, table_info->cell_ids[0][1]);
355 EXPECT_EQ(4, table_info->cell_ids[1][0]);
356 EXPECT_EQ(6, table_info->cell_ids[1][1]);
357 EXPECT_EQ(0, table_info->cell_ids[1][2]);
358
359 EXPECT_EQ(3U, table_info->unique_cell_ids.size());
360 EXPECT_EQ(4, table_info->unique_cell_ids[0]);
361 EXPECT_EQ(5, table_info->unique_cell_ids[1]);
362 EXPECT_EQ(6, table_info->unique_cell_ids[2]);
363
364 EXPECT_EQ(0u, table_info->cell_id_to_index[4]);
365 EXPECT_EQ(1u, table_info->cell_id_to_index[5]);
366 EXPECT_EQ(2u, table_info->cell_id_to_index[6]);
367
368 EXPECT_EQ(2u, table_info->row_nodes.size());
369 EXPECT_EQ(2, table_info->row_nodes[0]->data().id);
370 EXPECT_EQ(3, table_info->row_nodes[1]->data().id);
371}
372
373TEST_F(AXTableInfoTest, SkipsGenericAndIgnoredNodes) {
374 // Simple 2 x 2 table with 2 cells in the first row, 2 cells in the second
375 // row, but with extra divs and ignored nodes in the tree.
376 //
377 // 1 Table
378 // 2 Row
379 // 3 Ignored
380 // 4 Generic
381 // 5 Cell
382 // 6 Cell
383 // 7 Ignored
384 // 8 Row
385 // 9 Cell
386 // 10 Cell
387
388 AXTreeUpdate initial_state;
389 initial_state.root_id = 1;
390 initial_state.nodes.resize(10);
391 MakeTable(&initial_state.nodes[0], 1, 0, 0);
392 initial_state.nodes[0].child_ids = {2, 7};
393 MakeRow(&initial_state.nodes[1], 2, 0);
394 initial_state.nodes[1].child_ids = {3};
395 initial_state.nodes[2].id = 3;
396 initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
397 initial_state.nodes[2].child_ids = {4, 6};
398 initial_state.nodes[3].id = 4;
399 initial_state.nodes[3].role = ax::mojom::Role::kGenericContainer;
400 initial_state.nodes[3].child_ids = {5};
401 MakeCell(&initial_state.nodes[4], 5, 0, 0);
402 MakeCell(&initial_state.nodes[5], 6, 0, 1);
403 initial_state.nodes[6].id = 7;
404 initial_state.nodes[6].AddState(ax::mojom::State::kIgnored);
405 initial_state.nodes[6].child_ids = {8};
406 MakeRow(&initial_state.nodes[7], 8, 1);
407 initial_state.nodes[7].child_ids = {9, 10};
408 MakeCell(&initial_state.nodes[8], 9, 1, 0);
409 MakeCell(&initial_state.nodes[9], 10, 1, 1);
410 AXTree tree(initial_state);
411
412 AXTableInfo* table_info = GetTableInfo(&tree, tree.root()->children()[0]);
413 EXPECT_FALSE(table_info);
414
415 table_info = GetTableInfo(&tree, tree.root());
416 EXPECT_TRUE(table_info);
417
418 EXPECT_EQ(2u, table_info->row_count);
419 EXPECT_EQ(2u, table_info->col_count);
420
421 EXPECT_EQ(5, table_info->cell_ids[0][0]);
422 EXPECT_EQ(6, table_info->cell_ids[0][1]);
423 EXPECT_EQ(9, table_info->cell_ids[1][0]);
424 EXPECT_EQ(10, table_info->cell_ids[1][1]);
425
426 EXPECT_EQ(2u, table_info->row_nodes.size());
427 EXPECT_EQ(2, table_info->row_nodes[0]->data().id);
428 EXPECT_EQ(8, table_info->row_nodes[1]->data().id);
429}
430
431TEST_F(AXTableInfoTest, HeadersWithSpans) {
432 // Row and column headers spanning multiple cells.
433 // In the figure below, 5 and 6 are headers.
434 //
435 // +---+---+
436 // | 5 |
437 // +---+---+---+
438 // | | 7 |
439 // + 6 +---+---+
440 // | | | 8 |
441 // +---+ +---+
442 AXTreeUpdate initial_state;
443 initial_state.root_id = 1;
444 initial_state.nodes.resize(8);
445 MakeTable(&initial_state.nodes[0], 1, 0, 0);
446 initial_state.nodes[0].child_ids = {2, 3, 4};
447 MakeRow(&initial_state.nodes[1], 2, 0);
448 initial_state.nodes[1].child_ids = {5};
449 MakeRow(&initial_state.nodes[2], 3, 1);
450 initial_state.nodes[2].child_ids = {6, 7};
451 MakeRow(&initial_state.nodes[3], 4, 2);
452 initial_state.nodes[3].child_ids = {8};
453 MakeColumnHeader(&initial_state.nodes[4], 5, 0, 1, 1, 2);
454 MakeRowHeader(&initial_state.nodes[5], 6, 1, 0, 2, 1);
455 MakeCell(&initial_state.nodes[6], 7, 1, 1);
456 MakeCell(&initial_state.nodes[7], 8, 2, 2);
457 AXTree tree(initial_state);
458
459 AXTableInfo* table_info = GetTableInfo(&tree, tree.root()->children()[0]);
460 EXPECT_FALSE(table_info);
461
462 table_info = GetTableInfo(&tree, tree.root());
463 EXPECT_TRUE(table_info);
464
465 EXPECT_EQ(3U, table_info->row_headers.size());
466 EXPECT_EQ(0U, table_info->row_headers[0].size());
467 EXPECT_EQ(1U, table_info->row_headers[1].size());
468 EXPECT_EQ(6, table_info->row_headers[1][0]);
469 EXPECT_EQ(1U, table_info->row_headers[1].size());
470 EXPECT_EQ(6, table_info->row_headers[2][0]);
471
472 EXPECT_EQ(3U, table_info->col_headers.size());
473 EXPECT_EQ(0U, table_info->col_headers[0].size());
474 EXPECT_EQ(1U, table_info->col_headers[1].size());
475 EXPECT_EQ(5, table_info->col_headers[1][0]);
476 EXPECT_EQ(1U, table_info->col_headers[2].size());
477 EXPECT_EQ(5, table_info->col_headers[2][0]);
478
479 EXPECT_EQ(0, table_info->cell_ids[0][0]);
480 EXPECT_EQ(5, table_info->cell_ids[0][1]);
481 EXPECT_EQ(5, table_info->cell_ids[0][2]);
482 EXPECT_EQ(6, table_info->cell_ids[1][0]);
483 EXPECT_EQ(7, table_info->cell_ids[1][1]);
484 EXPECT_EQ(0, table_info->cell_ids[1][2]);
485 EXPECT_EQ(6, table_info->cell_ids[2][0]);
486 EXPECT_EQ(0, table_info->cell_ids[2][1]);
487 EXPECT_EQ(8, table_info->cell_ids[2][2]);
488
489 EXPECT_EQ(3u, table_info->row_nodes.size());
490 EXPECT_EQ(2, table_info->row_nodes[0]->data().id);
491 EXPECT_EQ(3, table_info->row_nodes[1]->data().id);
492 EXPECT_EQ(4, table_info->row_nodes[2]->data().id);
493}
494
495TEST_F(AXTableInfoTest, ExtraMacNodes) {
496 // Simple 2 x 2 table with 2 column headers in first row, 2 cells in second
497 // row.
498 AXTreeUpdate initial_state;
499 initial_state.root_id = 1;
500 initial_state.nodes.resize(7);
501 MakeTable(&initial_state.nodes[0], 1, 0, 0);
502 initial_state.nodes[0].child_ids = {2, 3};
503 MakeRow(&initial_state.nodes[1], 2, 0);
504 initial_state.nodes[1].child_ids = {4, 5};
505 MakeRow(&initial_state.nodes[2], 3, 1);
506 initial_state.nodes[2].child_ids = {6, 7};
507 MakeColumnHeader(&initial_state.nodes[3], 4, 0, 0);
508 MakeColumnHeader(&initial_state.nodes[4], 5, 0, 1);
509 MakeCell(&initial_state.nodes[5], 6, 1, 0);
510 MakeCell(&initial_state.nodes[6], 7, 1, 1);
511 AXTree tree(initial_state);
512
513 tree.SetEnableExtraMacNodes(true);
514 AXTableInfo* table_info = GetTableInfo(&tree, tree.root()->children()[0]);
515 EXPECT_FALSE(table_info);
516
517 table_info = GetTableInfo(&tree, tree.root());
518 EXPECT_TRUE(table_info);
519
520 // We expect 3 extra Mac nodes: two column nodes, and one header node.
521 EXPECT_EQ(3U, table_info->extra_mac_nodes.size());
522
523 // The first column.
524 AXNodeData extra_node_0 = table_info->extra_mac_nodes[0]->data();
525 EXPECT_EQ(-1, table_info->extra_mac_nodes[0]->id());
526 EXPECT_EQ(1, table_info->extra_mac_nodes[0]->parent()->id());
527 EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0.role);
528 EXPECT_EQ(2U, table_info->extra_mac_nodes[0]->GetIndexInParent());
529 EXPECT_EQ(2U, table_info->extra_mac_nodes[0]->GetUnignoredIndexInParent());
530 EXPECT_EQ(0, extra_node_0.GetIntAttribute(
532 std::vector<int32_t> indirect_child_ids;
533 EXPECT_EQ(true, extra_node_0.GetIntListAttribute(
535 &indirect_child_ids));
536 EXPECT_EQ(2U, indirect_child_ids.size());
537 EXPECT_EQ(4, indirect_child_ids[0]);
538 EXPECT_EQ(6, indirect_child_ids[1]);
539
540 // The second column.
541 AXNodeData extra_node_1 = table_info->extra_mac_nodes[1]->data();
542 EXPECT_EQ(-2, table_info->extra_mac_nodes[1]->id());
543 EXPECT_EQ(1, table_info->extra_mac_nodes[1]->parent()->id());
544 EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1.role);
545 EXPECT_EQ(3U, table_info->extra_mac_nodes[1]->GetIndexInParent());
546 EXPECT_EQ(3U, table_info->extra_mac_nodes[1]->GetUnignoredIndexInParent());
547 EXPECT_EQ(1, extra_node_1.GetIntAttribute(
549 indirect_child_ids.clear();
550 EXPECT_EQ(true, extra_node_1.GetIntListAttribute(
552 &indirect_child_ids));
553 EXPECT_EQ(2U, indirect_child_ids.size());
554 EXPECT_EQ(5, indirect_child_ids[0]);
555 EXPECT_EQ(7, indirect_child_ids[1]);
556
557 // The table header container.
558 AXNodeData extra_node_2 = table_info->extra_mac_nodes[2]->data();
559 EXPECT_EQ(-3, table_info->extra_mac_nodes[2]->id());
560 EXPECT_EQ(1, table_info->extra_mac_nodes[2]->parent()->id());
561 EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2.role);
562 EXPECT_EQ(4U, table_info->extra_mac_nodes[2]->GetIndexInParent());
563 EXPECT_EQ(4U, table_info->extra_mac_nodes[2]->GetUnignoredIndexInParent());
564 indirect_child_ids.clear();
565 EXPECT_EQ(true, extra_node_2.GetIntListAttribute(
567 &indirect_child_ids));
568 EXPECT_EQ(2U, indirect_child_ids.size());
569 EXPECT_EQ(4, indirect_child_ids[0]);
570 EXPECT_EQ(5, indirect_child_ids[1]);
571}
572
573TEST_F(AXTableInfoTest, TableWithNoIndices) {
574 AXTreeUpdate initial_state;
575 initial_state.root_id = 1;
576 initial_state.nodes.resize(7);
577 initial_state.nodes[0].id = 1;
578 initial_state.nodes[0].role = ax::mojom::Role::kTable;
579 initial_state.nodes[0].child_ids = {2, 3};
580 initial_state.nodes[1].id = 2;
581 initial_state.nodes[1].role = ax::mojom::Role::kRow;
582 initial_state.nodes[1].child_ids = {4, 5};
583 initial_state.nodes[2].id = 3;
584 initial_state.nodes[2].role = ax::mojom::Role::kRow;
585 initial_state.nodes[2].child_ids = {6, 7};
586 initial_state.nodes[3].id = 4;
587 initial_state.nodes[3].role = ax::mojom::Role::kColumnHeader;
588 initial_state.nodes[4].id = 5;
589 initial_state.nodes[4].role = ax::mojom::Role::kColumnHeader;
590 initial_state.nodes[5].id = 6;
591 initial_state.nodes[5].role = ax::mojom::Role::kCell;
592 initial_state.nodes[6].id = 7;
593 initial_state.nodes[6].role = ax::mojom::Role::kCell;
594
595 AXTree tree(initial_state);
596 AXNode* table = tree.root();
597
598 EXPECT_TRUE(table->IsTable());
599 EXPECT_FALSE(table->IsTableRow());
600 EXPECT_FALSE(table->IsTableCellOrHeader());
601 EXPECT_EQ(2, table->GetTableColCount());
602 EXPECT_EQ(2, table->GetTableRowCount());
603
604 EXPECT_EQ(2u, table->GetTableRowNodeIds().size());
605 EXPECT_EQ(2, table->GetTableRowNodeIds()[0]);
606 EXPECT_EQ(3, table->GetTableRowNodeIds()[1]);
607
608 EXPECT_EQ(4, table->GetTableCellFromCoords(0, 0)->id());
609 EXPECT_EQ(5, table->GetTableCellFromCoords(0, 1)->id());
610 EXPECT_EQ(6, table->GetTableCellFromCoords(1, 0)->id());
611 EXPECT_EQ(7, table->GetTableCellFromCoords(1, 1)->id());
612 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(2, 1));
613 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(1, -1));
614
615 EXPECT_EQ(4, table->GetTableCellFromIndex(0)->id());
616 EXPECT_EQ(5, table->GetTableCellFromIndex(1)->id());
617 EXPECT_EQ(6, table->GetTableCellFromIndex(2)->id());
618 EXPECT_EQ(7, table->GetTableCellFromIndex(3)->id());
619 EXPECT_EQ(nullptr, table->GetTableCellFromIndex(-1));
620 EXPECT_EQ(nullptr, table->GetTableCellFromIndex(4));
621
622 AXNode* cell_0_0 = tree.GetFromId(4);
623 EXPECT_EQ(0, cell_0_0->GetTableCellRowIndex());
624 EXPECT_EQ(0, cell_0_0->GetTableCellColIndex());
625 AXNode* cell_0_1 = tree.GetFromId(5);
626 EXPECT_EQ(0, cell_0_1->GetTableCellRowIndex());
627 EXPECT_EQ(1, cell_0_1->GetTableCellColIndex());
628 AXNode* cell_1_0 = tree.GetFromId(6);
629 EXPECT_EQ(1, cell_1_0->GetTableCellRowIndex());
630 EXPECT_EQ(0, cell_1_0->GetTableCellColIndex());
631 AXNode* cell_1_1 = tree.GetFromId(7);
632 EXPECT_EQ(1, cell_1_1->GetTableCellRowIndex());
633 EXPECT_EQ(1, cell_1_1->GetTableCellColIndex());
634}
635
636TEST_F(AXTableInfoTest, TableWithPartialIndices) {
637 // Start with a table with no indices.
638 AXTreeUpdate initial_state;
639 initial_state.root_id = 1;
640 initial_state.nodes.resize(7);
641 initial_state.nodes[0].id = 1;
642 initial_state.nodes[0].role = ax::mojom::Role::kTable;
643 initial_state.nodes[0].child_ids = {2, 3};
644 initial_state.nodes[1].id = 2;
645 initial_state.nodes[1].role = ax::mojom::Role::kRow;
646 initial_state.nodes[1].child_ids = {4, 5};
647 initial_state.nodes[2].id = 3;
648 initial_state.nodes[2].role = ax::mojom::Role::kRow;
649 initial_state.nodes[2].child_ids = {6, 7};
650 initial_state.nodes[3].id = 4;
651 initial_state.nodes[3].role = ax::mojom::Role::kColumnHeader;
652 initial_state.nodes[4].id = 5;
653 initial_state.nodes[4].role = ax::mojom::Role::kColumnHeader;
654 initial_state.nodes[5].id = 6;
655 initial_state.nodes[5].role = ax::mojom::Role::kCell;
656 initial_state.nodes[6].id = 7;
657 initial_state.nodes[6].role = ax::mojom::Role::kCell;
658
659 AXTree tree(initial_state);
660 AXNode* table = tree.root();
661
662 EXPECT_EQ(2, table->GetTableColCount());
663 EXPECT_EQ(2, table->GetTableRowCount());
664
665 AXNode* cell_0_0 = tree.GetFromId(4);
666 EXPECT_EQ(0, cell_0_0->GetTableCellRowIndex());
667 EXPECT_EQ(0, cell_0_0->GetTableCellColIndex());
668 AXNode* cell_0_1 = tree.GetFromId(5);
669 EXPECT_EQ(0, cell_0_1->GetTableCellRowIndex());
670 EXPECT_EQ(1, cell_0_1->GetTableCellColIndex());
671 AXNode* cell_1_0 = tree.GetFromId(6);
672 EXPECT_EQ(1, cell_1_0->GetTableCellRowIndex());
673 EXPECT_EQ(0, cell_1_0->GetTableCellColIndex());
674 AXNode* cell_1_1 = tree.GetFromId(7);
675 EXPECT_EQ(1, cell_1_1->GetTableCellRowIndex());
676 EXPECT_EQ(1, cell_1_1->GetTableCellColIndex());
677
678 AXTreeUpdate update = initial_state;
679 update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount,
680 5);
681 update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, 2);
683 2);
684 update.nodes[5].AddIntAttribute(
687 2);
688 update.nodes[6].AddIntAttribute(
691
692 // The largest column index in the table is 2, but the
693 // table claims it has a column count of 5. That's allowed.
694 EXPECT_EQ(5, table->GetTableColCount());
695
696 // While the table claims it has a row count of 2, the
697 // last row has an index of 2, so the correct row count is 3.
698 EXPECT_EQ(3, table->GetTableRowCount());
699
700 EXPECT_EQ(2u, table->GetTableRowNodeIds().size());
701 EXPECT_EQ(2, table->GetTableRowNodeIds()[0]);
702 EXPECT_EQ(3, table->GetTableRowNodeIds()[1]);
703
704 // All of the specified row and cell indices are legal
705 // so they're respected.
706 EXPECT_EQ(0, cell_0_0->GetTableCellRowIndex());
707 EXPECT_EQ(0, cell_0_0->GetTableCellColIndex());
708 EXPECT_EQ(0, cell_0_1->GetTableCellRowIndex());
709 EXPECT_EQ(1, cell_0_1->GetTableCellColIndex());
710 EXPECT_EQ(2, cell_1_0->GetTableCellRowIndex());
711 EXPECT_EQ(0, cell_1_0->GetTableCellColIndex());
712 EXPECT_EQ(2, cell_1_1->GetTableCellRowIndex());
713 EXPECT_EQ(2, cell_1_1->GetTableCellColIndex());
714
715 // Fetching cells by coordinates works.
716 EXPECT_EQ(4, table->GetTableCellFromCoords(0, 0)->id());
717 EXPECT_EQ(5, table->GetTableCellFromCoords(0, 1)->id());
718 EXPECT_EQ(6, table->GetTableCellFromCoords(2, 0)->id());
719 EXPECT_EQ(7, table->GetTableCellFromCoords(2, 2)->id());
720 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(0, 2));
721 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(1, 0));
722 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(1, 1));
723 EXPECT_EQ(nullptr, table->GetTableCellFromCoords(2, 1));
724}
725
726TEST_F(AXTableInfoTest, BadRowIndicesIgnored) {
727 // The table claims it has two rows and two columns, but
728 // the cell indices for both the first and second rows
729 // are for row 2 (zero-based).
730 //
731 // The cell indexes for the first row should be
732 // respected, and for the second row it will get the
733 // next row index.
734 AXTreeUpdate initial_state;
735 initial_state.root_id = 1;
736 initial_state.nodes.resize(7);
737 MakeTable(&initial_state.nodes[0], 1, 2, 2);
738 initial_state.nodes[0].child_ids = {2, 3};
739 MakeRow(&initial_state.nodes[1], 2, 0);
740 initial_state.nodes[1].child_ids = {4, 5};
741 MakeRow(&initial_state.nodes[2], 3, 0);
742 initial_state.nodes[2].child_ids = {6, 7};
743 MakeCell(&initial_state.nodes[3], 4, 2, 0);
744 MakeCell(&initial_state.nodes[4], 5, 2, 1);
745 MakeCell(&initial_state.nodes[5], 6, 2, 0);
746 MakeCell(&initial_state.nodes[6], 7, 2, 1);
747 AXTree tree(initial_state);
748 AXNode* table = tree.root();
749
750 EXPECT_EQ(2, table->GetTableColCount());
751 EXPECT_EQ(4, table->GetTableRowCount());
752
753 EXPECT_EQ(2u, table->GetTableRowNodeIds().size());
754 EXPECT_EQ(2, table->GetTableRowNodeIds()[0]);
755 EXPECT_EQ(3, table->GetTableRowNodeIds()[1]);
756
757 AXNode* cell_id_4 = tree.GetFromId(4);
758 EXPECT_EQ(2, cell_id_4->GetTableCellRowIndex());
759 EXPECT_EQ(0, cell_id_4->GetTableCellColIndex());
760 AXNode* cell_id_5 = tree.GetFromId(5);
761 EXPECT_EQ(2, cell_id_5->GetTableCellRowIndex());
762 EXPECT_EQ(1, cell_id_5->GetTableCellColIndex());
763 AXNode* cell_id_6 = tree.GetFromId(6);
764 EXPECT_EQ(3, cell_id_6->GetTableCellRowIndex());
765 EXPECT_EQ(0, cell_id_6->GetTableCellColIndex());
766 AXNode* cell_id_7 = tree.GetFromId(7);
767 EXPECT_EQ(3, cell_id_7->GetTableCellRowIndex());
768 EXPECT_EQ(1, cell_id_7->GetTableCellColIndex());
769}
770
771TEST_F(AXTableInfoTest, BadColIndicesIgnored) {
772 // The table claims it has two rows and two columns, but
773 // the cell indices for the columns either repeat or
774 // go backwards.
775 AXTreeUpdate initial_state;
776 initial_state.root_id = 1;
777 initial_state.nodes.resize(7);
778 MakeTable(&initial_state.nodes[0], 1, 2, 2);
779 initial_state.nodes[0].child_ids = {2, 3};
780 MakeRow(&initial_state.nodes[1], 2, 0);
781 initial_state.nodes[1].child_ids = {4, 5};
782 MakeRow(&initial_state.nodes[2], 3, 0);
783 initial_state.nodes[2].child_ids = {6, 7};
784 MakeCell(&initial_state.nodes[3], 4, 0, 1);
785 MakeCell(&initial_state.nodes[4], 5, 0, 1);
786 MakeCell(&initial_state.nodes[5], 6, 1, 2);
787 MakeCell(&initial_state.nodes[6], 7, 1, 1);
788 AXTree tree(initial_state);
789 AXNode* table = tree.root();
790
791 EXPECT_EQ(4, table->GetTableColCount());
792 EXPECT_EQ(2, table->GetTableRowCount());
793
794 EXPECT_EQ(2u, table->GetTableRowNodeIds().size());
795 EXPECT_EQ(2, table->GetTableRowNodeIds()[0]);
796 EXPECT_EQ(3, table->GetTableRowNodeIds()[1]);
797
798 AXNode* cell_id_4 = tree.GetFromId(4);
799 EXPECT_EQ(0, cell_id_4->GetTableCellRowIndex());
800 EXPECT_EQ(1, cell_id_4->GetTableCellColIndex());
801 AXNode* cell_id_5 = tree.GetFromId(5);
802 EXPECT_EQ(0, cell_id_5->GetTableCellRowIndex());
803 EXPECT_EQ(2, cell_id_5->GetTableCellColIndex());
804 AXNode* cell_id_6 = tree.GetFromId(6);
805 EXPECT_EQ(1, cell_id_6->GetTableCellRowIndex());
806 EXPECT_EQ(2, cell_id_6->GetTableCellColIndex());
807 AXNode* cell_id_7 = tree.GetFromId(7);
808 EXPECT_EQ(1, cell_id_7->GetTableCellRowIndex());
809 EXPECT_EQ(3, cell_id_7->GetTableCellColIndex());
810}
811
812TEST_F(AXTableInfoTest, AriaIndicesInferred) {
813 // Note that ARIA indices are 1-based, whereas the rest of
814 // the table indices are zero-based.
815 AXTreeUpdate initial_state;
816 initial_state.root_id = 1;
817 initial_state.nodes.resize(13);
818 MakeTable(&initial_state.nodes[0], 1, 3, 3);
819 initial_state.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kAriaRowCount,
820 5);
821 initial_state.nodes[0].AddIntAttribute(
823 initial_state.nodes[0].child_ids = {2, 3, 4};
824 MakeRow(&initial_state.nodes[1], 2, 0);
825 initial_state.nodes[1].child_ids = {5, 6, 7};
826 MakeRow(&initial_state.nodes[2], 3, 1);
827 initial_state.nodes[2].AddIntAttribute(
829 initial_state.nodes[2].child_ids = {8, 9, 10};
830 MakeRow(&initial_state.nodes[3], 4, 2);
831 initial_state.nodes[3].AddIntAttribute(
833 initial_state.nodes[3].child_ids = {11, 12, 13};
834 MakeCell(&initial_state.nodes[4], 5, 0, 0);
835 initial_state.nodes[4].AddIntAttribute(
837 initial_state.nodes[4].AddIntAttribute(
839 MakeCell(&initial_state.nodes[5], 6, 0, 1);
840 MakeCell(&initial_state.nodes[6], 7, 0, 2);
841 MakeCell(&initial_state.nodes[7], 8, 1, 0);
842 MakeCell(&initial_state.nodes[8], 9, 1, 1);
843 MakeCell(&initial_state.nodes[9], 10, 1, 2);
844 MakeCell(&initial_state.nodes[10], 11, 2, 0);
845 initial_state.nodes[10].AddIntAttribute(
847 MakeCell(&initial_state.nodes[11], 12, 2, 1);
848 initial_state.nodes[11].AddIntAttribute(
850 MakeCell(&initial_state.nodes[12], 13, 2, 2);
851 initial_state.nodes[12].AddIntAttribute(
853 AXTree tree(initial_state);
854 AXNode* table = tree.root();
855
856 EXPECT_EQ(5, table->GetTableAriaColCount());
857 EXPECT_EQ(5, table->GetTableAriaRowCount());
858
859 EXPECT_EQ(3u, table->GetTableRowNodeIds().size());
860 EXPECT_EQ(2, table->GetTableRowNodeIds()[0]);
861 EXPECT_EQ(3, table->GetTableRowNodeIds()[1]);
862 EXPECT_EQ(4, table->GetTableRowNodeIds()[2]);
863
864 // The first row has the first cell ARIA row and column index
865 // specified as (2, 2). The rest of the row is inferred.
866
867 AXNode* cell_0_0 = tree.GetFromId(5);
868 EXPECT_EQ(2, cell_0_0->GetTableCellAriaRowIndex());
869 EXPECT_EQ(2, cell_0_0->GetTableCellAriaColIndex());
870
871 AXNode* cell_0_1 = tree.GetFromId(6);
872 EXPECT_EQ(2, cell_0_1->GetTableCellAriaRowIndex());
873 EXPECT_EQ(3, cell_0_1->GetTableCellAriaColIndex());
874
875 AXNode* cell_0_2 = tree.GetFromId(7);
876 EXPECT_EQ(2, cell_0_2->GetTableCellAriaRowIndex());
877 EXPECT_EQ(4, cell_0_2->GetTableCellAriaColIndex());
878
879 // The next row has the ARIA row index set to 4 on the row
880 // element. The rest is inferred.
881
882 AXNode* cell_1_0 = tree.GetFromId(8);
883 EXPECT_EQ(4, cell_1_0->GetTableCellAriaRowIndex());
884 EXPECT_EQ(1, cell_1_0->GetTableCellAriaColIndex());
885
886 AXNode* cell_1_1 = tree.GetFromId(9);
887 EXPECT_EQ(4, cell_1_1->GetTableCellAriaRowIndex());
888 EXPECT_EQ(2, cell_1_1->GetTableCellAriaColIndex());
889
890 AXNode* cell_1_2 = tree.GetFromId(10);
891 EXPECT_EQ(4, cell_1_2->GetTableCellAriaRowIndex());
892 EXPECT_EQ(3, cell_1_2->GetTableCellAriaColIndex());
893
894 // The last row has the ARIA row index set to 4 again, which is
895 // illegal so we should get 5. The cells have column indices of
896 // 3, 2, 1 which is illegal so we ignore the latter two and should
897 // end up with column indices of 3, 4, 5.
898
899 AXNode* cell_2_0 = tree.GetFromId(11);
900 EXPECT_EQ(5, cell_2_0->GetTableCellAriaRowIndex());
901 EXPECT_EQ(3, cell_2_0->GetTableCellAriaColIndex());
902
903 AXNode* cell_2_1 = tree.GetFromId(12);
904 EXPECT_EQ(5, cell_2_1->GetTableCellAriaRowIndex());
905 EXPECT_EQ(4, cell_2_1->GetTableCellAriaColIndex());
906
907 AXNode* cell_2_2 = tree.GetFromId(13);
908 EXPECT_EQ(5, cell_2_2->GetTableCellAriaRowIndex());
909 EXPECT_EQ(5, cell_2_2->GetTableCellAriaColIndex());
910}
911
912TEST_F(AXTableInfoTest, TableChanges) {
913 // Simple 2 col x 1 row table
914 AXTreeUpdate initial_state;
915 initial_state.root_id = 1;
916 initial_state.nodes.resize(4);
917 MakeTable(&initial_state.nodes[0], 1, 0, 0);
918 initial_state.nodes[0].child_ids = {2};
919 MakeRow(&initial_state.nodes[1], 2, 0);
920 initial_state.nodes[1].child_ids = {3, 4};
921 MakeCell(&initial_state.nodes[2], 3, 0, 0);
922 MakeCell(&initial_state.nodes[3], 4, 0, 1);
923 AXTree tree(initial_state);
924
925 AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
926 EXPECT_TRUE(table_info);
927
928 EXPECT_EQ(1u, table_info->row_count);
929 EXPECT_EQ(2u, table_info->col_count);
930
931 // Update the tree to remove the table role.
932 AXTreeUpdate update = initial_state;
933 update.nodes[0].role = ax::mojom::Role::kGroup;
934 ASSERT_TRUE(tree.Unserialize(update));
935
936 table_info = GetTableInfo(&tree, tree.root());
937 EXPECT_FALSE(table_info);
938}
939
940TEST_F(AXTableInfoTest, ExtraMacNodesChanges) {
941 // Simple 2 x 2 table with 2 column headers in first row, 2 cells in second
942 // row.
943 AXTreeUpdate initial_state;
944 initial_state.root_id = 1;
945 initial_state.nodes.resize(7);
946 MakeTable(&initial_state.nodes[0], 1, 0, 0);
947 initial_state.nodes[0].child_ids = {2, 3};
948 MakeRow(&initial_state.nodes[1], 2, 0);
949 initial_state.nodes[1].child_ids = {4, 5};
950 MakeRow(&initial_state.nodes[2], 3, 1);
951 initial_state.nodes[2].child_ids = {6, 7};
952 MakeColumnHeader(&initial_state.nodes[3], 4, 0, 0);
953 MakeColumnHeader(&initial_state.nodes[4], 5, 0, 1);
954 MakeCell(&initial_state.nodes[5], 6, 1, 0);
955 MakeCell(&initial_state.nodes[6], 7, 1, 1);
956 AXTree tree(initial_state);
957
958 tree.SetEnableExtraMacNodes(true);
959 AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
960 ASSERT_NE(nullptr, table_info);
961 // We expect 3 extra Mac nodes: two column nodes, and one header node.
962 ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
963
964 // Hide the first row. The number of extra Mac nodes should remain the same,
965 // but their data should change.
966 AXTreeUpdate update1;
967 update1.nodes.resize(1);
968 MakeRow(&update1.nodes[0], 2, 0);
969 update1.nodes[0].AddState(ax::mojom::State::kIgnored);
970 update1.nodes[0].child_ids = {4, 5};
971 ASSERT_TRUE(tree.Unserialize(update1));
972 table_info = GetTableInfo(&tree, tree.root());
973 ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
974
975 {
976 // The first column.
977 AXNodeData extra_node_0 = table_info->extra_mac_nodes[0]->data();
978 EXPECT_EQ(-4, table_info->extra_mac_nodes[0]->id());
979 EXPECT_EQ(1, table_info->extra_mac_nodes[0]->parent()->id());
980 EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0.role);
981 EXPECT_EQ(2U, table_info->extra_mac_nodes[0]->GetIndexInParent());
982 EXPECT_EQ(3U, table_info->extra_mac_nodes[0]->GetUnignoredIndexInParent());
983 EXPECT_EQ(0, extra_node_0.GetIntAttribute(
985 std::vector<int32_t> indirect_child_ids;
986 EXPECT_EQ(true, extra_node_0.GetIntListAttribute(
988 &indirect_child_ids));
989 EXPECT_EQ(1U, indirect_child_ids.size());
990 EXPECT_EQ(6, indirect_child_ids[0]);
991
992 // The second column.
993 AXNodeData extra_node_1 = table_info->extra_mac_nodes[1]->data();
994 EXPECT_EQ(-5, table_info->extra_mac_nodes[1]->id());
995 EXPECT_EQ(1, table_info->extra_mac_nodes[1]->parent()->id());
996 EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1.role);
997 EXPECT_EQ(3U, table_info->extra_mac_nodes[1]->GetIndexInParent());
998 EXPECT_EQ(4U, table_info->extra_mac_nodes[1]->GetUnignoredIndexInParent());
999 EXPECT_EQ(1, extra_node_1.GetIntAttribute(
1001 indirect_child_ids.clear();
1002 EXPECT_EQ(true, extra_node_1.GetIntListAttribute(
1004 &indirect_child_ids));
1005 EXPECT_EQ(1U, indirect_child_ids.size());
1006 EXPECT_EQ(7, indirect_child_ids[0]);
1007
1008 // The table header container.
1009 AXNodeData extra_node_2 = table_info->extra_mac_nodes[2]->data();
1010 EXPECT_EQ(-6, table_info->extra_mac_nodes[2]->id());
1011 EXPECT_EQ(1, table_info->extra_mac_nodes[2]->parent()->id());
1012 EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2.role);
1013 EXPECT_EQ(4U, table_info->extra_mac_nodes[2]->GetIndexInParent());
1014 EXPECT_EQ(5U, table_info->extra_mac_nodes[2]->GetUnignoredIndexInParent());
1015 indirect_child_ids.clear();
1016 EXPECT_EQ(true, extra_node_2.GetIntListAttribute(
1018 &indirect_child_ids));
1019 EXPECT_EQ(0U, indirect_child_ids.size());
1020 }
1021
1022 // Delete the first row. Again, the number of extra Mac nodes should remain
1023 // the same, but their data should change.
1024 AXTreeUpdate update2;
1025 update2.node_id_to_clear = 2;
1026 update2.nodes.resize(1);
1027 MakeTable(&update2.nodes[0], 1, 0, 0);
1028 update2.nodes[0].child_ids = {3};
1029 ASSERT_TRUE(tree.Unserialize(update2));
1030 table_info = GetTableInfo(&tree, tree.root());
1031 ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
1032
1033 {
1034 // The first column.
1035 AXNodeData extra_node_0 = table_info->extra_mac_nodes[0]->data();
1036 EXPECT_EQ(-7, table_info->extra_mac_nodes[0]->id());
1037 EXPECT_EQ(1, table_info->extra_mac_nodes[0]->parent()->id());
1038 EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0.role);
1039 EXPECT_EQ(1U, table_info->extra_mac_nodes[0]->GetIndexInParent());
1040 EXPECT_EQ(1U, table_info->extra_mac_nodes[0]->GetUnignoredIndexInParent());
1041 EXPECT_EQ(0, extra_node_0.GetIntAttribute(
1043 std::vector<int32_t> indirect_child_ids;
1044 EXPECT_EQ(true, extra_node_0.GetIntListAttribute(
1046 &indirect_child_ids));
1047 EXPECT_EQ(1U, indirect_child_ids.size());
1048 EXPECT_EQ(6, indirect_child_ids[0]);
1049
1050 // The second column.
1051 AXNodeData extra_node_1 = table_info->extra_mac_nodes[1]->data();
1052 EXPECT_EQ(-8, table_info->extra_mac_nodes[1]->id());
1053 EXPECT_EQ(1, table_info->extra_mac_nodes[1]->parent()->id());
1054 EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1.role);
1055 EXPECT_EQ(2U, table_info->extra_mac_nodes[1]->GetIndexInParent());
1056 EXPECT_EQ(2U, table_info->extra_mac_nodes[1]->GetUnignoredIndexInParent());
1057 EXPECT_EQ(1, extra_node_1.GetIntAttribute(
1059 indirect_child_ids.clear();
1060 EXPECT_EQ(true, extra_node_1.GetIntListAttribute(
1062 &indirect_child_ids));
1063 EXPECT_EQ(1U, indirect_child_ids.size());
1064 EXPECT_EQ(7, indirect_child_ids[0]);
1065
1066 // The table header container.
1067 AXNodeData extra_node_2 = table_info->extra_mac_nodes[2]->data();
1068 EXPECT_EQ(-9, table_info->extra_mac_nodes[2]->id());
1069 EXPECT_EQ(1, table_info->extra_mac_nodes[2]->parent()->id());
1070 EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2.role);
1071 EXPECT_EQ(3U, table_info->extra_mac_nodes[2]->GetIndexInParent());
1072 EXPECT_EQ(3U, table_info->extra_mac_nodes[2]->GetUnignoredIndexInParent());
1073 indirect_child_ids.clear();
1074 EXPECT_EQ(true, extra_node_2.GetIntListAttribute(
1076 &indirect_child_ids));
1077 EXPECT_EQ(0U, indirect_child_ids.size());
1078 }
1079}
1080
1081TEST_F(AXTableInfoTest, RowColumnSpanChanges) {
1082 // Simple 2 col x 1 row table
1084 update.root_id = 1;
1085 update.nodes.resize(4);
1086 MakeTable(&update.nodes[0], 1, 0, 0);
1087 update.nodes[0].child_ids = {2};
1088 MakeRow(&update.nodes[1], 2, 0);
1089 update.nodes[1].child_ids = {3, 10};
1090 MakeCell(&update.nodes[2], 3, 0, 0);
1091 MakeCell(&update.nodes[3], 10, 0, 1);
1092 AXTree tree(update);
1093
1094 AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
1095 ASSERT_TRUE(table_info);
1096
1097 EXPECT_EQ(1u, table_info->row_count);
1098 EXPECT_EQ(2u, table_info->col_count);
1099
1100 EXPECT_EQ("|3 |10|\n", table_info->ToString());
1101
1102 // Add a row to the table.
1103 update.nodes.resize(6);
1104 update.nodes[0].child_ids = {2, 4};
1105 MakeRow(&update.nodes[4], 4, 0);
1106 update.nodes[4].child_ids = {5};
1107 MakeCell(&update.nodes[5], 5, -1, -1);
1108
1109 tree.Unserialize(update);
1110
1111 table_info = GetTableInfo(&tree, tree.root());
1112 ASSERT_TRUE(table_info);
1113 EXPECT_EQ(2u, table_info->row_count);
1114 EXPECT_EQ(2u, table_info->col_count);
1115 EXPECT_EQ(
1116 "|3 |10|\n"
1117 "|5 |0 |\n",
1118 table_info->ToString());
1119
1120 // Add a row to the middle of the table, with a span. Intentionally omit other
1121 // rows from the update.
1122 update.nodes.resize(3);
1123 update.nodes[0].child_ids = {2, 6, 4};
1124 MakeRow(&update.nodes[1], 6, 0);
1125 update.nodes[1].child_ids = {7};
1126 MakeCell(&update.nodes[2], 7, -1, -1, 1, 2);
1127
1128 tree.Unserialize(update);
1129
1130 table_info = GetTableInfo(&tree, tree.root());
1131 ASSERT_TRUE(table_info);
1132 EXPECT_EQ(3u, table_info->row_count);
1133 EXPECT_EQ(2u, table_info->col_count);
1134 EXPECT_EQ(
1135 "|3 |10|\n"
1136 "|7 |7 |\n"
1137 "|5 |0 |\n",
1138 table_info->ToString());
1139
1140 // Add a row to the end of the table, with a span. Intentionally omit other
1141 // rows from the update.
1142 update.nodes.resize(3);
1143 update.nodes[0].child_ids = {2, 6, 4, 8};
1144 MakeRow(&update.nodes[1], 8, 0);
1145 update.nodes[1].child_ids = {9};
1146 MakeCell(&update.nodes[2], 9, -1, -1, 2, 3);
1147
1148 tree.Unserialize(update);
1149
1150 table_info = GetTableInfo(&tree, tree.root());
1151 ASSERT_TRUE(table_info);
1152 EXPECT_EQ(5u, table_info->row_count);
1153 EXPECT_EQ(3u, table_info->col_count);
1154 EXPECT_EQ(
1155 "|3 |10|0 |\n"
1156 "|7 |7 |0 |\n"
1157 "|5 |0 |0 |\n"
1158 "|9 |9 |9 |\n"
1159 "|9 |9 |9 |\n",
1160 table_info->ToString());
1161
1162 // Finally, delete a few rows.
1163 update.nodes.resize(1);
1164 update.nodes[0].child_ids = {6, 8};
1165
1166 tree.Unserialize(update);
1167
1168 table_info = GetTableInfo(&tree, tree.root());
1169 ASSERT_TRUE(table_info);
1170 EXPECT_EQ(3u, table_info->row_count);
1171 EXPECT_EQ(3u, table_info->col_count);
1172 EXPECT_EQ(
1173 "|7|7|0|\n"
1174 "|9|9|9|\n"
1175 "|9|9|9|\n",
1176 table_info->ToString());
1177}
1178
1179} // namespace ui
SI F table(const skcms_Curve *curve, F v)
#define EXPECT_OPTIONAL_EQ(expected, actual)
std::optional< int > GetTableCellIndex() const
Definition ax_node.cc:758
std::optional< int > GetTableCellRowIndex() const
Definition ax_node.cc:784
bool IsTableCellOrHeader() const
Definition ax_node.cc:754
std::optional< int > GetTableCellAriaColIndex() const
Definition ax_node.cc:822
std::optional< int > GetTableRowRowIndex() const
Definition ax_node.cc:695
void GetTableCellColHeaders(std::vector< AXNode * > *col_headers) const
Definition ax_node.cc:857
std::vector< AXNode::AXID > GetTableRowNodeIds() const
Definition ax_node.cc:709
std::optional< int > GetTableCellAriaRowIndex() const
Definition ax_node.cc:834
bool IsTableRow() const
Definition ax_node.cc:691
const std::vector< AXNode * > & children() const
Definition ax_node.h:113
void GetTableCellRowHeaders(std::vector< AXNode * > *row_headers) const
Definition ax_node.cc:875
std::optional< int > GetTableCellColSpan() const
Definition ax_node.cc:796
std::optional< int > GetTableCellRowSpan() const
Definition ax_node.cc:809
bool IsTable() const
Definition ax_node.cc:540
std::optional< int > GetTableCellColIndex() const
Definition ax_node.cc:772
AXTableInfo * GetTableInfo(AXTree *tree, AXNode *node)
std::vector< std::vector< int32_t > > col_headers
std::vector< std::vector< int32_t > > cell_ids
std::vector< AXNode * > extra_mac_nodes
std::unordered_map< int32_t, size_t > cell_id_to_index
std::vector< AXNode * > row_nodes
std::string ToString() const
std::vector< std::vector< int32_t > > row_headers
std::vector< int32_t > unique_cell_ids
virtual bool Unserialize(const AXTreeUpdate &update)
Definition ax_tree.cc:969
AXNode * GetFromId(int32_t id) const override
Definition ax_tree.cc:728
AXNode * root() const
Definition ax_tree.h:57
void SetEnableExtraMacNodes(bool enabled)
Definition ax_tree.cc:1933
AXTableInfo * GetTableInfo(const AXNode *table_node) const override
Definition ax_tree.cc:1206
TEST_F(AXPositionTest, Clone)
ax::mojom::Role role
const std::vector< int32_t > & GetIntListAttribute(ax::mojom::IntListAttribute attribute) const
int GetIntAttribute(ax::mojom::IntAttribute attribute) const
std::vector< AXNodeData > nodes
const uintptr_t id
#define BASE_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:8
#define EXPECT_TRUE(handle)
Definition unit_test.h:685