#include "ui/accessibility/ax_table_info.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node.h"
#include "ui/accessibility/ax_tree.h"
namespace ui {
namespace {
void MakeTable(AXNodeData* table, int id, int row_count, int col_count) { … }
void MakeRowGroup(AXNodeData* row_group, int id) { … }
void MakeRow(AXNodeData* row, int id, int row_index) { … }
void MakeCell(AXNodeData* cell,
int id,
int row_index,
int col_index,
int row_span = 1,
int col_span = 1) { … }
void MakeColumnHeader(AXNodeData* cell,
int id,
int row_index,
int col_index,
int row_span = 1,
int col_span = 1) { … }
void MakeRowHeader(AXNodeData* cell,
int id,
int row_index,
int col_index,
int row_span = 1,
int col_span = 1) { … }
}
#define EXPECT_OPTIONAL_EQ(expected, actual) …
class AXTableInfoTest : public testing::Test { … };
TEST_F(AXTableInfoTest, SimpleTable) { … }
TEST_F(AXTableInfoTest, ComputedTableSizeIncludesSpans) { … }
TEST_F(AXTableInfoTest, AuthorRowAndColumnCountsAreRespected) { … }
TEST_F(AXTableInfoTest, TableInfoRecomputedOnlyWhenTableChanges) { … }
TEST_F(AXTableInfoTest, CellIdsHandlesSpansAndMissingCells) { … }
TEST_F(AXTableInfoTest, SkipsGenericAndIgnoredNodes) { … }
TEST_F(AXTableInfoTest, RowHeadersWithSpans) { … }
TEST_F(AXTableInfoTest, MultipleRowHeadersInSameRow) { … }
TEST_F(AXTableInfoTest, ColumnHeadersWithSpans) { … }
TEST_F(AXTableInfoTest, MultipleColumnHeadersInSameColumn) { … }
#if BUILDFLAG(IS_MAC)
TEST_F(AXTableInfoTest, ExtraMacNodes) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
initial_state.nodes.resize(7);
MakeTable(&initial_state.nodes[0], 1, 0, 0);
initial_state.nodes[0].child_ids = {2, 3};
MakeRow(&initial_state.nodes[1], 2, 0);
initial_state.nodes[1].child_ids = {4, 5};
MakeRow(&initial_state.nodes[2], 3, 1);
initial_state.nodes[2].child_ids = {6, 7};
MakeColumnHeader(&initial_state.nodes[3], 4, 0, 0);
MakeColumnHeader(&initial_state.nodes[4], 5, 0, 1);
MakeCell(&initial_state.nodes[5], 6, 1, 0);
MakeCell(&initial_state.nodes[6], 7, 1, 1);
AXTree tree(initial_state);
AXTableInfo* table_info = GetTableInfo(&tree, tree.root()->children()[0]);
EXPECT_FALSE(table_info);
table_info = GetTableInfo(&tree, tree.root());
EXPECT_TRUE(table_info);
EXPECT_EQ(3U, table_info->extra_mac_nodes.size());
AXNode* extra_node_0 = table_info->extra_mac_nodes[0];
EXPECT_EQ(-1, extra_node_0->id());
EXPECT_EQ(1, extra_node_0->parent()->id());
EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0->GetRole());
EXPECT_EQ(2U, extra_node_0->GetIndexInParent());
EXPECT_EQ(2U, extra_node_0->GetUnignoredIndexInParent());
EXPECT_EQ(0, extra_node_0->GetIntAttribute(
ax::mojom::IntAttribute::kTableColumnIndex));
std::vector<AXNodeID> indirect_child_ids;
EXPECT_EQ(true, extra_node_0->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(2U, indirect_child_ids.size());
EXPECT_EQ(4, indirect_child_ids[0]);
EXPECT_EQ(6, indirect_child_ids[1]);
AXNode* extra_node_1 = table_info->extra_mac_nodes[1];
EXPECT_EQ(-2, extra_node_1->id());
EXPECT_EQ(1, extra_node_1->parent()->id());
EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1->GetRole());
EXPECT_EQ(3U, extra_node_1->GetIndexInParent());
EXPECT_EQ(3U, extra_node_1->GetUnignoredIndexInParent());
EXPECT_EQ(1, extra_node_1->GetIntAttribute(
ax::mojom::IntAttribute::kTableColumnIndex));
indirect_child_ids.clear();
EXPECT_EQ(true, extra_node_1->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(2U, indirect_child_ids.size());
EXPECT_EQ(5, indirect_child_ids[0]);
EXPECT_EQ(7, indirect_child_ids[1]);
AXNode* extra_node_2 = table_info->extra_mac_nodes[2];
EXPECT_EQ(-3, extra_node_2->id());
EXPECT_EQ(1, extra_node_2->parent()->id());
EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2->GetRole());
EXPECT_EQ(4U, extra_node_2->GetIndexInParent());
EXPECT_EQ(4U, extra_node_2->GetUnignoredIndexInParent());
indirect_child_ids.clear();
EXPECT_EQ(true, extra_node_2->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(2U, indirect_child_ids.size());
EXPECT_EQ(4, indirect_child_ids[0]);
EXPECT_EQ(5, indirect_child_ids[1]);
}
#endif
TEST_F(AXTableInfoTest, TableWithNoRows) { … }
TEST_F(AXTableInfoTest, TableWithNoIndices) { … }
TEST_F(AXTableInfoTest, TableWithPartialIndices) { … }
TEST_F(AXTableInfoTest, BadRowIndicesIgnored) { … }
TEST_F(AXTableInfoTest, BadColIndicesIgnored) { … }
TEST_F(AXTableInfoTest, AriaIndicesInferred) { … }
TEST_F(AXTableInfoTest, TableChanges) { … }
#if BUILDFLAG(IS_MAC)
TEST_F(AXTableInfoTest, ExtraMacNodesChanges) {
AXTreeUpdate initial_state;
initial_state.root_id = 1;
initial_state.nodes.resize(7);
MakeTable(&initial_state.nodes[0], 1, 0, 0);
initial_state.nodes[0].child_ids = {2, 3};
MakeRow(&initial_state.nodes[1], 2, 0);
initial_state.nodes[1].child_ids = {4, 5};
MakeRow(&initial_state.nodes[2], 3, 1);
initial_state.nodes[2].child_ids = {6, 7};
MakeColumnHeader(&initial_state.nodes[3], 4, 0, 0);
MakeColumnHeader(&initial_state.nodes[4], 5, 0, 1);
MakeCell(&initial_state.nodes[5], 6, 1, 0);
MakeCell(&initial_state.nodes[6], 7, 1, 1);
AXTree tree(initial_state);
AXTableInfo* table_info = GetTableInfo(&tree, tree.root());
ASSERT_NE(nullptr, table_info);
ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
AXTreeUpdate update1;
update1.nodes.resize(1);
MakeRow(&update1.nodes[0], 2, 0);
update1.nodes[0].AddState(ax::mojom::State::kIgnored);
update1.nodes[0].child_ids = {4, 5};
ASSERT_TRUE(tree.Unserialize(update1));
table_info = GetTableInfo(&tree, tree.root());
ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
{
AXNode* extra_node_0 = table_info->extra_mac_nodes[0];
EXPECT_EQ(-4, extra_node_0->id());
EXPECT_EQ(1, extra_node_0->parent()->id());
EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0->GetRole());
EXPECT_EQ(2U, extra_node_0->GetIndexInParent());
EXPECT_EQ(3U, extra_node_0->GetUnignoredIndexInParent());
EXPECT_EQ(0, extra_node_0->GetIntAttribute(
ax::mojom::IntAttribute::kTableColumnIndex));
std::vector<AXNodeID> indirect_child_ids;
EXPECT_EQ(true, extra_node_0->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(1U, indirect_child_ids.size());
EXPECT_EQ(6, indirect_child_ids[0]);
AXNode* extra_node_1 = table_info->extra_mac_nodes[1];
EXPECT_EQ(-5, extra_node_1->id());
EXPECT_EQ(1, extra_node_1->parent()->id());
EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1->GetRole());
EXPECT_EQ(3U, extra_node_1->GetIndexInParent());
EXPECT_EQ(4U, extra_node_1->GetUnignoredIndexInParent());
EXPECT_EQ(1, extra_node_1->GetIntAttribute(
ax::mojom::IntAttribute::kTableColumnIndex));
indirect_child_ids.clear();
EXPECT_EQ(true, extra_node_1->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(1U, indirect_child_ids.size());
EXPECT_EQ(7, indirect_child_ids[0]);
AXNode* extra_node_2 = table_info->extra_mac_nodes[2];
EXPECT_EQ(-6, extra_node_2->id());
EXPECT_EQ(1, extra_node_2->parent()->id());
EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2->GetRole());
EXPECT_EQ(4U, extra_node_2->GetIndexInParent());
EXPECT_EQ(5U, extra_node_2->GetUnignoredIndexInParent());
indirect_child_ids.clear();
EXPECT_EQ(true, extra_node_2->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(0U, indirect_child_ids.size());
}
AXTreeUpdate update2;
update2.node_id_to_clear = 2;
update2.nodes.resize(1);
MakeTable(&update2.nodes[0], 1, 0, 0);
update2.nodes[0].child_ids = {3};
ASSERT_TRUE(tree.Unserialize(update2));
table_info = GetTableInfo(&tree, tree.root());
ASSERT_EQ(3U, table_info->extra_mac_nodes.size());
{
AXNode* extra_node_0 = table_info->extra_mac_nodes[0];
EXPECT_EQ(-7, extra_node_0->id());
EXPECT_EQ(1, extra_node_0->parent()->id());
EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_0->GetRole());
EXPECT_EQ(1U, extra_node_0->GetIndexInParent());
EXPECT_EQ(1U, extra_node_0->GetUnignoredIndexInParent());
EXPECT_EQ(0, extra_node_0->GetIntAttribute(
ax::mojom::IntAttribute::kTableColumnIndex));
std::vector<AXNodeID> indirect_child_ids;
EXPECT_EQ(true, extra_node_0->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(1U, indirect_child_ids.size());
EXPECT_EQ(6, indirect_child_ids[0]);
AXNode* extra_node_1 = table_info->extra_mac_nodes[1];
EXPECT_EQ(-8, extra_node_1->id());
EXPECT_EQ(1, extra_node_1->parent()->id());
EXPECT_EQ(ax::mojom::Role::kColumn, extra_node_1->GetRole());
EXPECT_EQ(2U, extra_node_1->GetIndexInParent());
EXPECT_EQ(2U, extra_node_1->GetUnignoredIndexInParent());
EXPECT_EQ(1, extra_node_1->GetIntAttribute(
ax::mojom::IntAttribute::kTableColumnIndex));
indirect_child_ids.clear();
EXPECT_EQ(true, extra_node_1->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(1U, indirect_child_ids.size());
EXPECT_EQ(7, indirect_child_ids[0]);
AXNode* extra_node_2 = table_info->extra_mac_nodes[2];
EXPECT_EQ(-9, extra_node_2->id());
EXPECT_EQ(1, extra_node_2->parent()->id());
EXPECT_EQ(ax::mojom::Role::kTableHeaderContainer, extra_node_2->GetRole());
EXPECT_EQ(3U, extra_node_2->GetIndexInParent());
EXPECT_EQ(3U, extra_node_2->GetUnignoredIndexInParent());
indirect_child_ids.clear();
EXPECT_EQ(true, extra_node_2->GetIntListAttribute(
ax::mojom::IntListAttribute::kIndirectChildIds,
&indirect_child_ids));
EXPECT_EQ(0U, indirect_child_ids.size());
}
}
#endif
TEST_F(AXTableInfoTest, RowColumnSpanChanges) { … }
}