blob: d7700b44970e218ba7bee4f0049587fc015c60e5 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.rotary;
import static com.google.common.truth.Truth.assertThat;
import android.view.accessibility.AccessibilityNodeInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class TreeTraverserTest {
private TreeTraverser mTreeTraverser;
private NodeBuilder mNodeBuilder;
@Before
public void setUp() {
mTreeTraverser = new TreeTraverser();
mTreeTraverser.setNodeCopier(MockNodeCopierProvider.get());
mNodeBuilder = new NodeBuilder(new ArrayList<>());
}
/**
* Tests
* {@link TreeTraverser#findNodeOrAncestor(AccessibilityNodeInfo, NodePredicate, NodePredicate)}
* in the following node tree:
* <pre>
* node0
* / \
* / \
* node1 node4
* / \ / \
* / \ / \
* node2 node3 node5 node6
* </pre>
*/
@Test
public void testFindNodeOrAncestor() {
AccessibilityNodeInfo node0 = mNodeBuilder.build();
AccessibilityNodeInfo node1 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node2 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node3 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node4 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node5 = mNodeBuilder.setParent(node4).build();
AccessibilityNodeInfo node6 = mNodeBuilder.setParent(node4).build();
// Should check the node itself.
AccessibilityNodeInfo result = mTreeTraverser.findNodeOrAncestor(node0,
/* stopPredicate= */ null, /* targetPredicate= */ node -> node == node0);
assertThat(result).isSameInstanceAs(node0);
// Parent.
result = mTreeTraverser.findNodeOrAncestor(node1, /* stopPredicate= */ null,
/* targetPredicate= */ node -> node == node0);
assertThat(result).isSameInstanceAs(node0);
// Grandparent.
result = mTreeTraverser.findNodeOrAncestor(node2, /* stopPredicate= */ null,
/* targetPredicate= */ node -> node == node0);
assertThat(result).isSameInstanceAs(node0);
// No ancestor found.
result = mTreeTraverser.findNodeOrAncestor(node2, /* stopPredicate= */ null,
/* targetPredicate= */ node -> node == node6);
assertThat(result).isNull();
// Stop before target.
result = mTreeTraverser.findNodeOrAncestor(node2, /* stopPredicate= */
node -> node == node1,
/* targetPredicate= */ node -> node == node0);
assertThat(result).isNull();
// Stop at target.
result = mTreeTraverser.findNodeOrAncestor(node2, /* stopPredicate= */
node -> node == node0,
/* targetPredicate= */ node -> node == node0);
assertThat(result).isNull();
}
/**
* Tests {@link TreeTraverser#depthFirstSearch(AccessibilityNodeInfo, NodePredicate,
* NodePredicate)}
* in the following node tree:
* <pre>
* node0
* / \
* / \
* node1 node4
* / \ / \
* / \ / \
* node2 node3 node5 node6
* </pre>
*/
@Test
public void testDepthFirstSearch() {
AccessibilityNodeInfo node0 = mNodeBuilder.build();
AccessibilityNodeInfo node1 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node2 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node3 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node4 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node5 = mNodeBuilder.setParent(node4).build();
AccessibilityNodeInfo node6 = mNodeBuilder.setParent(node4).build();
// Iterate in depth-first order, finding nothing.
List<AccessibilityNodeInfo> targetPredicateCalledWithNodes = new ArrayList<>();
AccessibilityNodeInfo result = mTreeTraverser.depthFirstSearch(
node0,
/* skipPredicate= */ null,
node -> {
targetPredicateCalledWithNodes.add(node);
return false;
});
assertThat(result).isNull();
assertThat(targetPredicateCalledWithNodes).containsExactly(
node0, node1, node2, node3, node4, node5, node6);
// Find root.
result = mTreeTraverser.depthFirstSearch(node0, /* skipPredicate= */ null,
/* targetPredicate= */ node -> node == node0);
assertThat(result).isSameInstanceAs(node0);
// Find child.
result = mTreeTraverser.depthFirstSearch(node0, /* skipPredicate= */ null,
/* targetPredicate= */ node -> node == node4);
assertThat(result).isSameInstanceAs(node4);
// Find grandchild.
result = mTreeTraverser.depthFirstSearch(node0, /* skipPredicate= */ null,
/* targetPredicate= */ node -> node == node6);
assertThat(result).isSameInstanceAs(node6);
// Iterate in depth-first order, skipping a subtree containing the target
List<AccessibilityNodeInfo> skipPredicateCalledWithNodes = new ArrayList<>();
targetPredicateCalledWithNodes.clear();
result = mTreeTraverser.depthFirstSearch(node0,
node -> {
skipPredicateCalledWithNodes.add(node);
return node == node1;
},
node -> {
targetPredicateCalledWithNodes.add(node);
return node == node2;
});
assertThat(result).isNull();
assertThat(skipPredicateCalledWithNodes).containsExactly(node0, node1, node4, node5, node6);
assertThat(targetPredicateCalledWithNodes).containsExactly(node0, node4, node5, node6);
// Skip subtree whose root is the target.
result = mTreeTraverser.depthFirstSearch(node0,
/* skipPredicate= */ node -> node == node1,
/* skipPredicate= */ node -> node == node1);
assertThat(result).isNull();
}
/**
* Tests {@link TreeTraverser#reverseDepthFirstSearch} in the following node tree:
* <pre>
* node0
* / \
* / \
* node1 node4
* / \ / \
* / \ / \
* node2 node3 node5 node6
* </pre>
*/
@Test
public void testReverseDepthFirstSearch() {
AccessibilityNodeInfo node0 = mNodeBuilder.build();
AccessibilityNodeInfo node1 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node2 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node3 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node4 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node5 = mNodeBuilder.setParent(node4).build();
AccessibilityNodeInfo node6 = mNodeBuilder.setParent(node4).build();
// Iterate in reverse depth-first order, finding nothing.
List<AccessibilityNodeInfo> predicateCalledWithNodes = new ArrayList<>();
AccessibilityNodeInfo result = mTreeTraverser.reverseDepthFirstSearch(
node0,
node -> {
predicateCalledWithNodes.add(node);
return false;
});
assertThat(result).isNull();
assertThat(predicateCalledWithNodes).containsExactly(
node6, node5, node4, node3, node2, node1, node0);
// Find root.
result = mTreeTraverser.reverseDepthFirstSearch(node0, node -> node == node0);
assertThat(result).isSameInstanceAs(node0);
// Find child.
result = mTreeTraverser.reverseDepthFirstSearch(node0, node -> node == node1);
assertThat(result).isSameInstanceAs(node1);
// Find grandchild.
result = mTreeTraverser.reverseDepthFirstSearch(node0, node -> node == node2);
assertThat(result).isSameInstanceAs(node2);
}
/**
* Tests {@link TreeTraverser#depthFirstSelect} in the following node tree:
* <pre>
* node0
* / \
* / \
* node1 node4
* / \ / \
* / \ / \
* node2 node3 node5 node6
* </pre>
*/
@Test
public void testDepthFirstSelect() {
AccessibilityNodeInfo node0 = mNodeBuilder.build();
AccessibilityNodeInfo node1 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node2 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node3 = mNodeBuilder.setParent(node1).build();
AccessibilityNodeInfo node4 = mNodeBuilder.setParent(node0).build();
AccessibilityNodeInfo node5 = mNodeBuilder.setParent(node4).build();
AccessibilityNodeInfo node6 = mNodeBuilder.setParent(node4).build();
// Iterate in depth-first order, selecting no nodes.
List<AccessibilityNodeInfo> predicateCalledWithNodes = new ArrayList<>();
List<AccessibilityNodeInfo> selectedNodes = new ArrayList<>();
mTreeTraverser.depthFirstSelect(node0, node -> {
predicateCalledWithNodes.add(node);
return false;
}, selectedNodes);
assertThat(predicateCalledWithNodes).containsExactly(
node0, node1, node2, node3, node4, node5, node6);
assertThat(selectedNodes).isEmpty();
// Find any node. Selects root and skips descendents.
predicateCalledWithNodes.clear();
selectedNodes = new ArrayList<>();
mTreeTraverser.depthFirstSelect(node0, node -> {
predicateCalledWithNodes.add(node);
return true;
}, selectedNodes);
assertThat(predicateCalledWithNodes).containsExactly(node0);
assertThat(selectedNodes).containsExactly(node0);
// Find children of root node. Skips grandchildren.
predicateCalledWithNodes.clear();
selectedNodes = new ArrayList<>();
mTreeTraverser.depthFirstSelect(node0, node -> {
predicateCalledWithNodes.add(node);
return node == node1 || node == node4;
}, selectedNodes);
assertThat(predicateCalledWithNodes).containsExactly(node0, node1, node4);
assertThat(selectedNodes).containsExactly(node1, node4);
// Find grandchildren of root node.
selectedNodes = new ArrayList<>();
mTreeTraverser.depthFirstSelect(node0,
node -> node == node2 || node == node3 || node == node5 || node == node6,
selectedNodes);
assertThat(selectedNodes).containsExactly(node2, node3, node5, node6);
}
}