chromium/ui/accessibility/ax_table_info_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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) {}

}  // namespace

// A macro for testing that a std::optional has both a value and that its value
// is set to a particular expectation.
#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) {
  // Simple 2 x 2 table with 2 column headers in first row, 2 cells in second
  // row.
  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);

  // We expect 3 extra Mac nodes: two column nodes, and one header node.
  EXPECT_EQ(3U, table_info->extra_mac_nodes.size());

  // The first column.
  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]);

  // The second column.
  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]);

  // The table header container.
  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) {
  // Simple 2 x 2 table with 2 column headers in first row, 2 cells in second
  // row.
  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);
  // We expect 3 extra Mac nodes: two column nodes, and one header node.
  ASSERT_EQ(3U, table_info->extra_mac_nodes.size());

  // Hide the first row. The number of extra Mac nodes should remain the same,
  // but their data should change.
  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());

  {
    // The first column.
    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]);

    // The second column.
    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]);

    // The table header container.
    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());
  }

  // Delete the first row. Again, the number of extra Mac nodes should remain
  // the same, but their data should change.
  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());

  {
    // The first column.
    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]);

    // The second column.
    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]);

    // The table header container.
    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) {}

}  // namespace ui