Commit 4335a96e authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

Improve PosInSet and SetSize functionality

Build on PosInSet and SetSize functions in ax_node.cc to handle
invalid or erroneous assignment. SetSize should not allow decreasing
assignments, while PosInSet should not allow decreasing nor duplicate
assignments among nodes in the same container.

Change-Id: I07b0e9c493439aca9ef50ecfe3c3f5bbf7a7ce7a
Reviewed-on: https://chromium-review.googlesource.com/c/1343374
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#610920}
parent 184fd278
...@@ -473,7 +473,8 @@ void AXNode::IdVectorToNodeVector(std::vector<int32_t>& ids, ...@@ -473,7 +473,8 @@ void AXNode::IdVectorToNodeVector(std::vector<int32_t>& ids,
} }
} }
// Determines the roles in which PosInSet and SetSize are used // Returns true if the node's role uses PosInSet and SetSize
// Returns false otherwise.
bool AXNode::IsSetSizePosInSetUsedInRole() const { bool AXNode::IsSetSizePosInSetUsedInRole() const {
switch (data().role) { switch (data().role) {
case ax::mojom::Role::kArticle: case ax::mojom::Role::kArticle:
...@@ -492,111 +493,198 @@ bool AXNode::IsSetSizePosInSetUsedInRole() const { ...@@ -492,111 +493,198 @@ bool AXNode::IsSetSizePosInSetUsedInRole() const {
} }
} }
// Finds the 0-based index of first element in the container (with same role as // Returns true if a node's role matches with the role of its container.
// node) where kPosInSet is assigned. Returns -1 if there are no elements with // Returns false otherwise.
// kPosInSet assigned bool AXNode::ContainerRoleMatches(AXNode* container) const {
int32_t AXNode::FirstAssignedPosInSet() const { ax::mojom::Role container_role = container->data().role;
AXNode* parent = GetUnignoredParent(); switch (data().role) {
for (int i = 0; i < parent->GetUnignoredChildCount(); ++i) { case ax::mojom::Role::kArticle:
AXNode* candidate = parent->GetUnignoredChildAtIndex(i); return container_role == ax::mojom::Role::kFeed;
if (!(candidate->data().role == data().role))
continue;
if (candidate->HasIntAttribute(ax::mojom::IntAttribute::kPosInSet))
return i;
}
return -1;
}
// Calculates node's position relative to first PosInSet-assigned element. case ax::mojom::Role::kListItem:
// Returns node's 0-based index within container (relative to nodes with the return container_role == ax::mojom::Role::kList ||
// same role) if no element assigned PosInSet container_role == ax::mojom::Role::kGroup;
int32_t AXNode::RelativePosFromFirstAssigned(
int32_t first_assigned_index) const {
AXNode* parent = GetUnignoredParent();
// Find nodes index in container
int nodes_index = 0;
for (int i = 0; i < parent->GetUnignoredChildCount(); ++i) {
AXNode* candidate = parent->GetUnignoredChildAtIndex(i);
if (candidate == this)
break;
if (candidate->data().role == data().role)
++nodes_index;
}
if (first_assigned_index == -1) case ax::mojom::Role::kMenuItem:
return nodes_index; return container_role == ax::mojom::Role::kMenu ||
container_role == ax::mojom::Role::kGroup ||
container_role == ax::mojom::Role::kMenuBar;
// Calculate relative position case ax::mojom::Role::kMenuItemRadio:
return nodes_index - first_assigned_index; return container_role == ax::mojom::Role::kGroup ||
} container_role == ax::mojom::Role::kMenu ||
container_role == ax::mojom::Role::kMenuBar;
// Finds the position of this node within its container, relative to other case ax::mojom::Role::kTab:
// nodes with the same role. return container_role == ax::mojom::Role::kTabList;
// Returns 1-based position if present in container, and 0 if not.
int32_t AXNode::PosInSet() const {
// Error checks
if (!IsSetSizePosInSetUsedInRole())
return 0;
AXNode* parent = GetUnignoredParent();
if (!parent)
return 0;
if (parent->data().role != ax::mojom::Role::kList)
return 0;
// Check if kPosInSet assigned and return if provided case ax::mojom::Role::kMenuItemCheckBox:
if (HasIntAttribute(ax::mojom::IntAttribute::kPosInSet)) { return container_role == ax::mojom::Role::kMenu ||
return GetIntAttribute(ax::mojom::IntAttribute::kPosInSet); container_role == ax::mojom::Role::kMenuBar;
}
// Caluclate PosInSet case ax::mojom::Role::kTreeItem:
// 1. Find index of first element (with same role) that has kPosInSet Assigned return container_role == ax::mojom::Role::kTree ||
// 2. Find relative position of this node compared to first assigned container_role == ax::mojom::Role::kGroup;
// 3. The PosInSet of this node = PosInSet(first assigned) + relative position
case ax::mojom::Role::kListBoxOption:
return container_role == ax::mojom::Role::kListBox;
case ax::mojom::Role::kRadioButton:
return container_role == ax::mojom::Role::kRadioGroup;
default:
return false;
}
}
int32_t first_assigned_index = FirstAssignedPosInSet(); int32_t AXNode::GetPosInSet() {
int32_t relative_position = int32_t pos = -1;
RelativePosFromFirstAssigned(first_assigned_index); int32_t size = -1;
ComputeSetSizePosInSet(&pos, &size);
return pos;
}
int32_t AXNode::GetSetSize() {
int32_t pos = -1;
int32_t size = -1;
ComputeSetSizePosInSet(&pos, &size);
return size;
}
// If no element assigned PosInSet, return this node's 1-based position in the // Finds and returns a pointer to node's container.
// container // Is not required to have a role that matches node's role.
if (first_assigned_index == -1) // Returns nullptr if node is not contained within container.
return relative_position + 1; AXNode* AXNode::GetContainer() const {
AXNode* result = parent();
// Continue walking up while parent is invalid, ignored, or is a generic
// container.
while ((result && result->data().HasState(ax::mojom::State::kIgnored)) ||
result->data().role == ax::mojom::Role::kGenericContainer ||
result->data().role == ax::mojom::Role::kIgnored) {
result = result->parent();
}
return result;
}
return parent->GetUnignoredChildAtIndex(first_assigned_index) // Populates items vector with all nodes within container whose roles match.
->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet) + void AXNode::PopulateContainerItems(AXNode* container,
relative_position; AXNode* local_parent,
std::vector<AXNode*>& items) const {
// Stop searching current path if roles of local_parent and container match.
// Don't compare the container to itself.
if (!(container == local_parent))
if (local_parent->data().role == container->data().role)
return;
for (int i = 0; i < local_parent->child_count(); ++i) {
AXNode* child = local_parent->children_[i];
// Add child to items if role matches with root container's role.
if (child->ContainerRoleMatches(container))
items.push_back(child);
// Recurse if there is a generic container or is ignored.
if (child->data().role == ax::mojom::Role::kGenericContainer ||
child->data().role == ax::mojom::Role::kIgnored) {
PopulateContainerItems(container, child, items);
}
}
} }
// Calculates the number of elements within node's container that have the // Computes pos_in_set and set_size values for this node.
// same role as node. void AXNode::ComputeSetSizePosInSet(int32_t* out_pos_in_set,
int32_t AXNode::SetSize() const { int32_t* out_set_size) {
// Error checks // Error checks
if (!IsSetSizePosInSetUsedInRole()) AXNode* container = GetContainer();
return 0; if (!(container && IsSetSizePosInSetUsedInRole() &&
AXNode* parent = GetUnignoredParent(); ContainerRoleMatches(container))) {
if (!parent) *out_pos_in_set = 0;
return 0; *out_set_size = 0;
if (parent->data().role != ax::mojom::Role::kList) return;
return 0; }
// TODO (akihiroota): List objects should report SetSize // Find all items within parent container and add to vector.
std::vector<AXNode*> items;
PopulateContainerItems(container, container, items);
// Necessary for calculating set_size. Keeps track of largest assigned
// kSetSize for each role.
std::unordered_map<ax::mojom::Role, int> largest_assigned_set_size;
// Iterate over vector of items and calculate pos_in_set and set_size for
// each. Items is not guaranteed to be populated with items of the same role.
// Use dictionary that maps role to frequency to calculate pos_in_set.
std::unordered_map<ax::mojom::Role, int> role_counts;
AXNode* node;
ax::mojom::Role node_role;
// Compute pos_in_set values.
for (unsigned int i = 0; i < items.size(); ++i) {
node = items[i];
node_role = node->data().role;
int32_t pos_in_set_value = 0;
if (role_counts.find(node_role) == role_counts.end())
// This is the first node with its role.
pos_in_set_value = 1;
else {
// This is the next node with its role.
pos_in_set_value = role_counts[node_role] + 1;
}
// Check if node has kPosInSet assigned. This assignment takes precedence
// over previous assignment.
if (node->HasIntAttribute(ax::mojom::IntAttribute::kPosInSet)) {
pos_in_set_value =
node->GetIntAttribute(ax::mojom::IntAttribute::kPosInSet);
// If invalid assignment (decrease or duplicate), adjust value.
if (pos_in_set_value <= role_counts[node_role]) {
pos_in_set_value = role_counts[node_role] + 1;
}
}
// Check if kSetSize assigned and return if provided // Assign pos_in_set and update role counts.
if (HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) if (node == this) {
return GetIntAttribute(ax::mojom::IntAttribute::kSetSize); *out_pos_in_set = pos_in_set_value;
}
role_counts[node_role] = pos_in_set_value;
// Check if kSetSize is assigned and update if it's the largest assigned
// kSetSize.
if (node->HasIntAttribute(ax::mojom::IntAttribute::kSetSize))
largest_assigned_set_size[node_role] =
std::max(largest_assigned_set_size[node_role],
node->GetIntAttribute(ax::mojom::IntAttribute::kSetSize));
}
// Check if kSetSize assigned in container and return if provided // Compute set_size values.
if (parent->HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) for (unsigned int j = 0; j < items.size(); ++j) {
return parent->GetIntAttribute(ax::mojom::IntAttribute::kSetSize); node = items[j];
node_role = node->data().role;
// TODO (akihiroota): List objects should report SetSize
// The SetSize of a node is the maximum of the following candidate values:
// 1. The PosInSet of the last value in the container (with same role as
// node's)
// 2. The Largest assigned SetSize in the container
// 3. The SetSize assigned within the node's container
int32_t pos_candidate = role_counts[node_role];
int32_t largest_set_size_candidate = 0;
if (largest_assigned_set_size.find(node_role) !=
largest_assigned_set_size.end()) {
largest_set_size_candidate = largest_assigned_set_size[node_role];
}
int32_t container_candidate = 0;
if (container->HasIntAttribute(ax::mojom::IntAttribute::kSetSize)) {
container_candidate =
container->GetIntAttribute(ax::mojom::IntAttribute::kSetSize);
}
// Calculate SetSize // Assign set_size
int count = 0; if (node == this) {
for (int i = 0; i < parent->GetUnignoredChildCount(); ++i) { *out_set_size =
AXNode* child = parent->GetUnignoredChildAtIndex(i); std::max(std::max(pos_candidate, largest_set_size_candidate),
if (child->data().role == data().role) container_candidate);
++count; }
} }
return count;
} }
} // namespace ui } // namespace ui
...@@ -181,13 +181,9 @@ class AX_EXPORT AXNode final { ...@@ -181,13 +181,9 @@ class AX_EXPORT AXNode final {
return data().GetHtmlAttribute(attribute, value); return data().GetHtmlAttribute(attribute, value);
} }
// Finds the position of this node within its container, relative to others // PosInSet and SetSize public methods
// with the same role. int32_t GetPosInSet();
// Returns 1-based index if present in container, and 0 if not. int32_t GetSetSize();
int32_t PosInSet() const;
// Calculates the number of elements within node's container that have the
// same role as node. Returns 0 if the node is not present wihtin a container.
int32_t SetSize() const;
const std::string& GetInheritedStringAttribute( const std::string& GetInheritedStringAttribute(
ax::mojom::StringAttribute attribute) const; ax::mojom::StringAttribute attribute) const;
...@@ -255,16 +251,21 @@ class AX_EXPORT AXNode final { ...@@ -255,16 +251,21 @@ class AX_EXPORT AXNode final {
void IdVectorToNodeVector(std::vector<int32_t>& ids, void IdVectorToNodeVector(std::vector<int32_t>& ids,
std::vector<AXNode*>* nodes) const; std::vector<AXNode*>* nodes) const;
// Helpers for PosInset and SetSize // Helpers for GetPosInSet and GetSetSize.
// Returns true if the PosInSet or SetSize attributes are used in node's role // Returns true if the role of parent container matches the role of node.
// Returns false otherwise.
bool ContainerRoleMatches(AXNode* parent) const;
// Returns true if the node's role uses PosInSet and SetSize
// Returns false otherwise.
bool IsSetSizePosInSetUsedInRole() const; bool IsSetSizePosInSetUsedInRole() const;
// Calculates 0-based index of first element with PosInSet assigned. // Finds and returns a pointer to node's container.
// Returns -1 if no elements assign PosInSet AXNode* GetContainer() const;
int32_t FirstAssignedPosInSet() const; // Populates items vector with all nodes within container whose roles match.
// Calculates node's position relative to first PosInSet-assigned element. void PopulateContainerItems(AXNode* container,
// Returns node's 0-based index within container (relative to nodes with the AXNode* local_parent,
// same role) if no element assigned PosInSet std::vector<AXNode*>& items) const;
int32_t RelativePosFromFirstAssigned(int32_t earliest_index) const; // Computes pos_in_set and set_size values for this node.
void ComputeSetSizePosInSet(int32_t* out_pos_in_set, int32_t* out_set_size);
OwnerTree* tree_; // Owns this. OwnerTree* tree_; // Owns this.
int index_in_parent_; int index_in_parent_;
......
...@@ -1472,7 +1472,7 @@ TEST(AXTreeTest, ChildTreeIds) { ...@@ -1472,7 +1472,7 @@ TEST(AXTreeTest, ChildTreeIds) {
EXPECT_EQ(0U, child_tree_93_nodes.size()); EXPECT_EQ(0U, child_tree_93_nodes.size());
} }
// Tests PosInSet and SetSize int attributes work if assigned // Tests PosInSet and SetSize int attributes work if assigned.
TEST(AXTreeTest, TestSetSizePosInSetAssigned) { TEST(AXTreeTest, TestSetSizePosInSetAssigned) {
AXTreeUpdate tree_update; AXTreeUpdate tree_update;
tree_update.root_id = 1; tree_update.root_id = 1;
...@@ -1495,14 +1495,14 @@ TEST(AXTreeTest, TestSetSizePosInSetAssigned) { ...@@ -1495,14 +1495,14 @@ TEST(AXTreeTest, TestSetSizePosInSetAssigned) {
AXTree tree(tree_update); AXTree tree(tree_update);
AXNode* item1 = tree.GetFromId(2); AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->PosInSet(), 2); EXPECT_EQ(item1->GetPosInSet(), 2);
EXPECT_EQ(item1->SetSize(), 12); EXPECT_EQ(item1->GetSetSize(), 12);
AXNode* item2 = tree.GetFromId(3); AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->PosInSet(), 5); EXPECT_EQ(item2->GetPosInSet(), 5);
EXPECT_EQ(item2->SetSize(), 12); EXPECT_EQ(item2->GetSetSize(), 12);
AXNode* item3 = tree.GetFromId(4); AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->PosInSet(), 9); EXPECT_EQ(item3->GetPosInSet(), 9);
EXPECT_EQ(item3->SetSize(), 12); EXPECT_EQ(item3->GetSetSize(), 12);
} }
// Tests that PosInSet and SetSize can be calculated if not assigned. // Tests that PosInSet and SetSize can be calculated if not assigned.
...@@ -1522,17 +1522,17 @@ TEST(AXTreeTest, TestSetSizePosInSetUnassigned) { ...@@ -1522,17 +1522,17 @@ TEST(AXTreeTest, TestSetSizePosInSetUnassigned) {
AXTree tree(tree_update); AXTree tree(tree_update);
AXNode* item1 = tree.GetFromId(2); AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->PosInSet(), 1); EXPECT_EQ(item1->GetPosInSet(), 1);
EXPECT_EQ(item1->SetSize(), 3); EXPECT_EQ(item1->GetSetSize(), 3);
AXNode* item2 = tree.GetFromId(3); AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->PosInSet(), 2); EXPECT_EQ(item2->GetPosInSet(), 2);
EXPECT_EQ(item2->SetSize(), 3); EXPECT_EQ(item2->GetSetSize(), 3);
AXNode* item3 = tree.GetFromId(4); AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->PosInSet(), 3); EXPECT_EQ(item3->GetPosInSet(), 3);
EXPECT_EQ(item3->SetSize(), 3); EXPECT_EQ(item3->GetSetSize(), 3);
} }
// Tests PosInSet unassigned, while SetSize assigned in container // Tests PosInSet unassigned, while SetSize assigned in container.
TEST(AXTreeTest, TestSetSizeAssignedInContainer) { TEST(AXTreeTest, TestSetSizeAssignedInContainer) {
AXTreeUpdate tree_update; AXTreeUpdate tree_update;
tree_update.root_id = 1; tree_update.root_id = 1;
...@@ -1549,16 +1549,17 @@ TEST(AXTreeTest, TestSetSizeAssignedInContainer) { ...@@ -1549,16 +1549,17 @@ TEST(AXTreeTest, TestSetSizeAssignedInContainer) {
tree_update.nodes[3].role = ax::mojom::Role::kListItem; tree_update.nodes[3].role = ax::mojom::Role::kListItem;
AXTree tree(tree_update); AXTree tree(tree_update);
// Items should inherit SetSize from container if not specified // Items should inherit SetSize from container if not specified.
AXNode* item1 = tree.GetFromId(2); AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->SetSize(), 7); EXPECT_EQ(item1->GetSetSize(), 7);
AXNode* item2 = tree.GetFromId(3); AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->SetSize(), 7); EXPECT_EQ(item2->GetSetSize(), 7);
AXNode* item3 = tree.GetFromId(4); AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->SetSize(), 7); EXPECT_EQ(item3->GetSetSize(), 7);
} }
// Tests PosInSet and SetSize on a list containing various roles. // Tests PosInSet and SetSize on a list containing various roles.
// Roles for items and associated container should match up.
TEST(AXTreeTest, TestSetSizePosInSetDiverseList) { TEST(AXTreeTest, TestSetSizePosInSetDiverseList) {
AXTreeUpdate tree_update; AXTreeUpdate tree_update;
tree_update.root_id = 1; tree_update.root_id = 1;
...@@ -1569,15 +1570,15 @@ TEST(AXTreeTest, TestSetSizePosInSetDiverseList) { ...@@ -1569,15 +1570,15 @@ TEST(AXTreeTest, TestSetSizePosInSetDiverseList) {
tree_update.nodes[1].id = 2; tree_update.nodes[1].id = 2;
tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3 tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 3
tree_update.nodes[2].id = 3; tree_update.nodes[2].id = 3;
tree_update.nodes[2].role = ax::mojom::Role::kMenuItem; // 1 of 2 tree_update.nodes[2].role = ax::mojom::Role::kMenuItem; // 0 of 0
tree_update.nodes[3].id = 4; tree_update.nodes[3].id = 4;
tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 2 of 3 tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 2 of 3
tree_update.nodes[4].id = 5; tree_update.nodes[4].id = 5;
tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 2 of 2 tree_update.nodes[4].role = ax::mojom::Role::kMenuItem; // 0 of 0
tree_update.nodes[5].id = 6; tree_update.nodes[5].id = 6;
tree_update.nodes[5].role = ax::mojom::Role::kArticle; // 1 of 2 tree_update.nodes[5].role = ax::mojom::Role::kArticle; // 0 of 0
tree_update.nodes[6].id = 7; tree_update.nodes[6].id = 7;
tree_update.nodes[6].role = ax::mojom::Role::kArticle; // 2 of 2 tree_update.nodes[6].role = ax::mojom::Role::kArticle; // 0 of 0
tree_update.nodes[7].id = 8; tree_update.nodes[7].id = 8;
tree_update.nodes[7].role = ax::mojom::Role::kListItem; // 3 of 3 tree_update.nodes[7].role = ax::mojom::Role::kListItem; // 3 of 3
tree_update.nodes[8].id = 9; tree_update.nodes[8].id = 9;
...@@ -1585,33 +1586,32 @@ TEST(AXTreeTest, TestSetSizePosInSetDiverseList) { ...@@ -1585,33 +1586,32 @@ TEST(AXTreeTest, TestSetSizePosInSetDiverseList) {
AXTree tree(tree_update); AXTree tree(tree_update);
AXNode* listitem1 = tree.GetFromId(2); AXNode* listitem1 = tree.GetFromId(2);
EXPECT_EQ(listitem1->PosInSet(), 1); EXPECT_EQ(listitem1->GetPosInSet(), 1);
EXPECT_EQ(listitem1->SetSize(), 3); EXPECT_EQ(listitem1->GetSetSize(), 3);
AXNode* listitem2 = tree.GetFromId(3); AXNode* menuitem1 = tree.GetFromId(3);
EXPECT_EQ(listitem2->PosInSet(), 1); EXPECT_EQ(menuitem1->GetPosInSet(), 0);
EXPECT_EQ(listitem2->SetSize(), 2); EXPECT_EQ(menuitem1->GetSetSize(), 0);
AXNode* menuitem1 = tree.GetFromId(4); AXNode* listitem2 = tree.GetFromId(4);
EXPECT_EQ(menuitem1->PosInSet(), 2); EXPECT_EQ(listitem2->GetPosInSet(), 2);
EXPECT_EQ(menuitem1->SetSize(), 3); EXPECT_EQ(listitem2->GetSetSize(), 3);
AXNode* menuitem2 = tree.GetFromId(5); AXNode* menuitem2 = tree.GetFromId(5);
EXPECT_EQ(menuitem2->PosInSet(), 2); EXPECT_EQ(menuitem2->GetPosInSet(), 0);
EXPECT_EQ(menuitem2->SetSize(), 2); EXPECT_EQ(menuitem2->GetSetSize(), 0);
AXNode* article1 = tree.GetFromId(6); AXNode* article1 = tree.GetFromId(6);
EXPECT_EQ(article1->PosInSet(), 1); EXPECT_EQ(article1->GetPosInSet(), 0);
EXPECT_EQ(article1->SetSize(), 2); EXPECT_EQ(article1->GetSetSize(), 0);
AXNode* article2 = tree.GetFromId(7); AXNode* article2 = tree.GetFromId(7);
EXPECT_EQ(article2->PosInSet(), 2); EXPECT_EQ(article2->GetPosInSet(), 0);
EXPECT_EQ(article2->SetSize(), 2); EXPECT_EQ(article2->GetSetSize(), 0);
AXNode* listitem3 = tree.GetFromId(8); AXNode* listitem3 = tree.GetFromId(8);
EXPECT_EQ(listitem3->PosInSet(), 3); EXPECT_EQ(listitem3->GetPosInSet(), 3);
EXPECT_EQ(listitem3->SetSize(), 3); EXPECT_EQ(listitem3->GetSetSize(), 3);
AXNode* image1 = tree.GetFromId(9); AXNode* image = tree.GetFromId(9);
// Roles that do not use PosInSet or SetSize should return 0 EXPECT_EQ(image->GetPosInSet(), 0);
EXPECT_EQ(image1->PosInSet(), 0); EXPECT_EQ(image->GetSetSize(), 0);
EXPECT_EQ(image1->SetSize(), 0);
} }
// Tests PosInSet and SetSize on a nested list // Tests PosInSet and SetSize on a nested list.
TEST(AXTreeTest, TestSetSizePosInSetNestedList) { TEST(AXTreeTest, TestSetSizePosInSetNestedList) {
AXTreeUpdate tree_update; AXTreeUpdate tree_update;
tree_update.root_id = 1; tree_update.root_id = 1;
...@@ -1635,28 +1635,28 @@ TEST(AXTreeTest, TestSetSizePosInSetNestedList) { ...@@ -1635,28 +1635,28 @@ TEST(AXTreeTest, TestSetSizePosInSetNestedList) {
AXTree tree(tree_update); AXTree tree(tree_update);
AXNode* outer_item1 = tree.GetFromId(2); AXNode* outer_item1 = tree.GetFromId(2);
EXPECT_EQ(outer_item1->PosInSet(), 1); EXPECT_EQ(outer_item1->GetPosInSet(), 1);
EXPECT_EQ(outer_item1->SetSize(), 3); EXPECT_EQ(outer_item1->GetSetSize(), 3);
AXNode* outer_item2 = tree.GetFromId(3); AXNode* outer_item2 = tree.GetFromId(3);
EXPECT_EQ(outer_item2->PosInSet(), 2); EXPECT_EQ(outer_item2->GetPosInSet(), 2);
EXPECT_EQ(outer_item2->SetSize(), 3); EXPECT_EQ(outer_item2->GetSetSize(), 3);
// List object itself should not report posinset or setsize // List object itself should not report posinset or setsize.
// TODO (akihiroota): Lists should report setsize in the future // TODO (akihiroota): Lists should report setsize in the future.
AXNode* inner_list = tree.GetFromId(4); AXNode* inner_list = tree.GetFromId(4);
EXPECT_EQ(inner_list->PosInSet(), 0); EXPECT_EQ(inner_list->GetPosInSet(), 0);
EXPECT_EQ(inner_list->SetSize(), 0); EXPECT_EQ(inner_list->GetSetSize(), 0);
AXNode* inner_item1 = tree.GetFromId(5); AXNode* inner_item1 = tree.GetFromId(5);
EXPECT_EQ(inner_item1->PosInSet(), 1); EXPECT_EQ(inner_item1->GetPosInSet(), 1);
EXPECT_EQ(inner_item1->SetSize(), 2); EXPECT_EQ(inner_item1->GetSetSize(), 2);
AXNode* inner_item2 = tree.GetFromId(6); AXNode* inner_item2 = tree.GetFromId(6);
EXPECT_EQ(inner_item2->PosInSet(), 2); EXPECT_EQ(inner_item2->GetPosInSet(), 2);
EXPECT_EQ(inner_item2->SetSize(), 2); EXPECT_EQ(inner_item2->GetSetSize(), 2);
AXNode* outer_item3 = tree.GetFromId(7); AXNode* outer_item3 = tree.GetFromId(7);
EXPECT_EQ(outer_item3->PosInSet(), 3); EXPECT_EQ(outer_item3->GetPosInSet(), 3);
EXPECT_EQ(outer_item3->SetSize(), 3); EXPECT_EQ(outer_item3->GetSetSize(), 3);
} }
// Tests PosInSet can be calculated if one item specifies PosInSet, but others // Tests PosInSet can be calculated if one item specifies PosInSet, but others
...@@ -1678,17 +1678,194 @@ TEST(AXTreeTest, TestPosInSetMissing) { ...@@ -1678,17 +1678,194 @@ TEST(AXTreeTest, TestPosInSetMissing) {
tree_update.nodes[3].role = ax::mojom::Role::kListItem; tree_update.nodes[3].role = ax::mojom::Role::kListItem;
AXTree tree(tree_update); AXTree tree(tree_update);
// Item1 should have pos of 12, since item2 is assigned a pos of 13 // Item1 should have pos of 12, since item2 is assigned a pos of 13.
AXNode* item1 = tree.GetFromId(2); AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->PosInSet(), 12); EXPECT_EQ(item1->GetPosInSet(), 1);
EXPECT_EQ(item1->SetSize(), 20); EXPECT_EQ(item1->GetSetSize(), 20);
AXNode* item2 = tree.GetFromId(3); AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->PosInSet(), 13); EXPECT_EQ(item2->GetPosInSet(), 13);
EXPECT_EQ(item2->SetSize(), 20); EXPECT_EQ(item2->GetSetSize(), 20);
// Item2 should have pos of 14, since item2 is assigned a pos of 13 // Item2 should have pos of 14, since item2 is assigned a pos of 13.
AXNode* item3 = tree.GetFromId(4); AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->PosInSet(), 14); EXPECT_EQ(item3->GetPosInSet(), 14);
EXPECT_EQ(item3->SetSize(), 20); EXPECT_EQ(item3->GetSetSize(), 20);
}
// A more difficult test that invovles missing PosInSet and SetSize values.
TEST(AXTreeTest, TestSetSizePosInSetMissingDifficult) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
tree_update.nodes.resize(6);
tree_update.nodes[0].id = 1;
tree_update.nodes[0].role = ax::mojom::Role::kList;
tree_update.nodes[0].child_ids = {2, 3, 4, 5, 6};
tree_update.nodes[1].id = 2;
tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 11
tree_update.nodes[2].id = 3;
tree_update.nodes[2].role = ax::mojom::Role::kListItem;
tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
5); // 5 of 11
tree_update.nodes[3].id = 4;
tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 6 of 11
tree_update.nodes[4].id = 5;
tree_update.nodes[4].role = ax::mojom::Role::kListItem;
tree_update.nodes[4].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet,
10); // 10 of 11
tree_update.nodes[5].id = 6;
tree_update.nodes[5].role = ax::mojom::Role::kListItem; // 11 of 11
AXTree tree(tree_update);
AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->GetPosInSet(), 1);
EXPECT_EQ(item1->GetSetSize(), 11);
AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->GetPosInSet(), 5);
EXPECT_EQ(item2->GetSetSize(), 11);
AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->GetPosInSet(), 6);
EXPECT_EQ(item3->GetSetSize(), 11);
AXNode* item4 = tree.GetFromId(5);
EXPECT_EQ(item4->GetPosInSet(), 10);
EXPECT_EQ(item4->GetSetSize(), 11);
AXNode* item5 = tree.GetFromId(6);
EXPECT_EQ(item5->GetPosInSet(), 11);
EXPECT_EQ(item5->GetSetSize(), 11);
}
// Tests that code overwrites decreasing SetSize assignments to largest of
// assigned values.
TEST(AXTreeTest, TestSetSizeDecreasing) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
tree_update.nodes.resize(4);
tree_update.nodes[0].id = 1;
tree_update.nodes[0].role = ax::mojom::Role::kList;
tree_update.nodes[0].child_ids = {2, 3, 4};
tree_update.nodes[1].id = 2;
tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 5
tree_update.nodes[2].id = 3;
tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 2 of 5
tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 5);
tree_update.nodes[3].id = 4;
tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 3 of 5
tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 4);
AXTree tree(tree_update);
AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->GetPosInSet(), 1);
EXPECT_EQ(item1->GetSetSize(), 5);
AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->GetPosInSet(), 2);
EXPECT_EQ(item2->GetSetSize(), 5);
AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->GetPosInSet(), 3);
EXPECT_EQ(item3->GetSetSize(), 5);
}
// Tests that code overwrites decreasing PosInSet values.
TEST(AXTreeTest, TestPosInSetDecreasing) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
tree_update.nodes.resize(4);
tree_update.nodes[0].id = 1;
tree_update.nodes[0].role = ax::mojom::Role::kList;
tree_update.nodes[0].child_ids = {2, 3, 4};
tree_update.nodes[1].id = 2;
tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 8
tree_update.nodes[2].id = 3;
tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 7 of 8
tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 7);
tree_update.nodes[3].id = 4;
tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 8 of 8
tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 3);
AXTree tree(tree_update);
AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->GetPosInSet(), 1);
EXPECT_EQ(item1->GetSetSize(), 8);
AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->GetPosInSet(), 7);
EXPECT_EQ(item2->GetSetSize(), 8);
AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->GetPosInSet(), 8);
EXPECT_EQ(item3->GetSetSize(), 8);
}
// Tests that code overwrites duplicate PosInSet values. Note this case is
// tricky; an update to the second element causes an update to the third
// element.
TEST(AXTreeTest, TestPosInSetDuplicates) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
tree_update.nodes.resize(4);
tree_update.nodes[0].id = 1;
tree_update.nodes[0].role = ax::mojom::Role::kList;
tree_update.nodes[0].child_ids = {2, 3, 4};
tree_update.nodes[1].id = 2;
tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 6 of 8
tree_update.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 6);
tree_update.nodes[2].id = 3;
tree_update.nodes[2].role = ax::mojom::Role::kListItem; // 7 of 8
tree_update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 6);
tree_update.nodes[3].id = 4;
tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 8 of 8
tree_update.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 7);
AXTree tree(tree_update);
AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->GetPosInSet(), 6);
EXPECT_EQ(item1->GetSetSize(), 8);
AXNode* item2 = tree.GetFromId(3);
EXPECT_EQ(item2->GetPosInSet(), 7);
EXPECT_EQ(item2->GetSetSize(), 8);
AXNode* item3 = tree.GetFromId(4);
EXPECT_EQ(item3->GetPosInSet(), 8);
EXPECT_EQ(item3->GetSetSize(), 8);
}
// Tests PosInSet and SetSize when some list items are nested in a generic
// container.
TEST(AXTreeTest, TestSetSizePosInSetNestedContainer) {
AXTreeUpdate tree_update;
tree_update.root_id = 1;
tree_update.nodes.resize(7);
tree_update.nodes[0].id = 1;
tree_update.nodes[0].role = ax::mojom::Role::kList;
tree_update.nodes[0].child_ids = {2, 3, 7};
tree_update.nodes[1].id = 2;
tree_update.nodes[1].role = ax::mojom::Role::kListItem; // 1 of 4
tree_update.nodes[2].id = 3;
tree_update.nodes[2].role = ax::mojom::Role::kGenericContainer;
tree_update.nodes[2].child_ids = {4, 5};
tree_update.nodes[3].id = 4;
tree_update.nodes[3].role = ax::mojom::Role::kListItem; // 2 of 4
tree_update.nodes[4].id = 5;
tree_update.nodes[4].role = ax::mojom::Role::kIgnored;
tree_update.nodes[4].child_ids = {6};
tree_update.nodes[5].id = 6;
tree_update.nodes[5].role = ax::mojom::Role::kListItem; // 3 of 4
tree_update.nodes[6].id = 7;
tree_update.nodes[6].role = ax::mojom::Role::kListItem; // 4 of 4
AXTree tree(tree_update);
AXNode* item1 = tree.GetFromId(2);
EXPECT_EQ(item1->GetPosInSet(), 1);
EXPECT_EQ(item1->GetSetSize(), 4);
AXNode* g_container = tree.GetFromId(3);
EXPECT_EQ(g_container->GetPosInSet(), 0);
EXPECT_EQ(g_container->GetSetSize(), 0);
AXNode* item2 = tree.GetFromId(4);
EXPECT_EQ(item2->GetPosInSet(), 2);
EXPECT_EQ(item2->GetSetSize(), 4);
AXNode* ignored = tree.GetFromId(5);
EXPECT_EQ(ignored->GetPosInSet(), 0);
EXPECT_EQ(ignored->GetSetSize(), 0);
AXNode* item3 = tree.GetFromId(6);
EXPECT_EQ(item3->GetPosInSet(), 3);
EXPECT_EQ(item3->GetSetSize(), 4);
AXNode* item4 = tree.GetFromId(7);
EXPECT_EQ(item4->GetPosInSet(), 4);
EXPECT_EQ(item4->GetSetSize(), 4);
} }
} // namespace ui } // namespace ui
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment