/* * Copyright (C) 2015 Google Inc. * * 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.utils; /** * Tests for AccessibilityNodeInfoUtils */ import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.view.accessibility.AccessibilityNodeInfo; import com.android.switchaccess.test.ShadowAccessibilityNodeInfo; import com.android.talkback.BuildConfig; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.internal.ShadowExtractor; import static org.junit.Assert.*; @Config( constants = BuildConfig.class, sdk = 21, shadows = { ShadowAccessibilityNodeInfo.class}) @RunWith(RobolectricGradleTestRunner.class) public class AccessibilityNodeInfoUtilsTest { private NodeFilter focusableFilter = new NodeFilter() { @Override public boolean accept(AccessibilityNodeInfoCompat node) { return node.isFocusable(); } }; @Before public void setUp() { ShadowAccessibilityNodeInfo.resetObtainedInstances(); } @After public void tearDown() { assertFalse(ShadowAccessibilityNodeInfo.areThereUnrecycledNodes(true)); ShadowAccessibilityNodeInfo.resetObtainedInstances(); } @Test public void searchFromBfsFromNullNode_shouldReturnNull() { assertNull(AccessibilityNodeInfoUtils.searchFromBfs(null, focusableFilter)); } @Test public void searchFromBfsNodeMatchesSelf_shouldReturnSelf() { AccessibilityNodeInfoCompat compat = new AccessibilityNodeInfoCompat(AccessibilityNodeInfo.obtain()); compat.setFocusable(true); AccessibilityNodeInfoCompat returned = AccessibilityNodeInfoUtils.searchFromBfs(compat, focusableFilter); assertEquals(compat, returned); compat.recycle(); returned.recycle(); } @Test public void searchFromBfsNodeMatchesChild_shouldReturnChild() { AccessibilityNodeInfo parent = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo child = AccessibilityNodeInfo.obtain(); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(parent)).addChild(child); AccessibilityNodeInfoCompat parentCompat = new AccessibilityNodeInfoCompat(parent); AccessibilityNodeInfoCompat childCompat = new AccessibilityNodeInfoCompat(child); parent.setFocusable(false); child.setFocusable(true); AccessibilityNodeInfoCompat returned = AccessibilityNodeInfoUtils.searchFromBfs(parentCompat, focusableFilter); assertEquals(childCompat, returned); parentCompat.recycle(); childCompat.recycle(); returned.recycle(); } @Test public void searchFromBfsNodeMatchesNothing_shouldReturnNull() { AccessibilityNodeInfoCompat compat = new AccessibilityNodeInfoCompat(AccessibilityNodeInfo.obtain()); compat.setFocusable(false); assertNull(AccessibilityNodeInfoUtils.searchFromBfs(compat, focusableFilter)); compat.recycle(); } @Test public void getRootForNull_shouldReturnNull() { AccessibilityNodeInfoCompat root = AccessibilityNodeInfoUtils.getRoot(null); assertNull(root); } @Test public void getRootForRootNode_shouldReturnSelf() { AccessibilityNodeInfoCompat compat = new AccessibilityNodeInfoCompat(AccessibilityNodeInfo.obtain()); AccessibilityNodeInfoCompat root = AccessibilityNodeInfoUtils.getRoot(compat); assertEquals(root, compat); compat.recycle(); root.recycle(); } @Test public void getRootForNonRootNode_shouldReturnRootNode() { AccessibilityNodeInfo level1Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level2Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level3Node = AccessibilityNodeInfo.obtain(); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level1Node)).addChild(level2Node); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level2Node)).addChild(level3Node); AccessibilityNodeInfoCompat startNode = new AccessibilityNodeInfoCompat(level3Node); AccessibilityNodeInfoCompat targetNode = new AccessibilityNodeInfoCompat(level1Node); AccessibilityNodeInfoCompat rootNode = AccessibilityNodeInfoUtils.getRoot(startNode); assertEquals(targetNode, rootNode); level1Node.recycle(); level2Node.recycle(); level3Node.recycle(); rootNode.recycle(); } @Test public void getRootForNodeWithLoop_shouldReturnNull() { AccessibilityNodeInfo level1Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level2Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level3Node = AccessibilityNodeInfo.obtain(); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level1Node)).addChild(level2Node); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level2Node)).addChild(level3Node); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level3Node)).addChild(level1Node); AccessibilityNodeInfoCompat startNode = new AccessibilityNodeInfoCompat(level3Node); AccessibilityNodeInfoCompat rootNode = AccessibilityNodeInfoUtils.getRoot(startNode); assertNull(rootNode); level1Node.recycle(); level2Node.recycle(); level3Node.recycle(); } @Test(timeout=100) public void isFocusableWithLoop_shouldNotHang() { AccessibilityNodeInfo level1Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level2Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level3Node = AccessibilityNodeInfo.obtain(); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level1Node)).addChild(level2Node); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level2Node)).addChild(level3Node); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level3Node)).addChild(level1Node); AccessibilityNodeInfoCompat startNode = new AccessibilityNodeInfoCompat(level1Node); AccessibilityNodeInfoUtils.isAccessibilityFocusable(startNode); level1Node.recycle(); level2Node.recycle(); level3Node.recycle(); } @Test(timeout=100) public void shouldFocusNodeWithLoop_shouldNotHang() { AccessibilityNodeInfo level1Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level2Node = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo level3Node = AccessibilityNodeInfo.obtain(); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level1Node)).addChild(level2Node); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level2Node)).addChild(level3Node); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(level3Node)).addChild(level1Node); AccessibilityNodeInfoCompat startNode = new AccessibilityNodeInfoCompat(level1Node); AccessibilityNodeInfoUtils.shouldFocusNode(startNode); level1Node.recycle(); level2Node.recycle(); level3Node.recycle(); } @Test public void hasAncestorWithSameAncestor_shouldReturnTrue() { AccessibilityNodeInfo targetInfo = AccessibilityNodeInfo.obtain(); AccessibilityNodeInfo childInfo = AccessibilityNodeInfo.obtain(); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(targetInfo)).addChild(childInfo); AccessibilityNodeInfoCompat targetAncestor = new AccessibilityNodeInfoCompat(targetInfo); AccessibilityNodeInfoCompat childNode = new AccessibilityNodeInfoCompat(childInfo); assertTrue(AccessibilityNodeInfoUtils.hasAncestor(childNode, targetAncestor)); targetAncestor.recycle(); childNode.recycle(); } @Test public void hasAncestorWithoutSameAncestor_shouldReturnFalse() { AccessibilityNodeInfoCompat targetAncestor = AccessibilityNodeInfoCompat.obtain(); AccessibilityNodeInfoCompat childNode = AccessibilityNodeInfoCompat.obtain(); assertFalse(AccessibilityNodeInfoUtils.hasAncestor(childNode, targetAncestor)); targetAncestor.recycle(); childNode.recycle(); } private void addChild(AccessibilityNodeInfoCompat parent, AccessibilityNodeInfoCompat child) { AccessibilityNodeInfo parentInfo = (AccessibilityNodeInfo) parent.getInfo(); AccessibilityNodeInfo childInfo = (AccessibilityNodeInfo) child.getInfo(); ((ShadowAccessibilityNodeInfo) ShadowExtractor.extract(parentInfo)).addChild(childInfo); } private AccessibilityNodeInfoCompat obtainTestNode(String contentDescription, boolean focusable) { AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(); node.setContentDescription(contentDescription); node.setFocusable(focusable); return new AccessibilityNodeInfoCompat(node); } @Test(timeout=100) public void hasMatchingDescendant_withLoop_shouldNotHang() { // (a)---->(b)----->(c)---->(d)---->(e)---->(f*) // | ^ ^ | // +----- | ------+ | // +----------------+ AccessibilityNodeInfoCompat a = obtainTestNode("a", false); AccessibilityNodeInfoCompat b = obtainTestNode("b", false); AccessibilityNodeInfoCompat c = obtainTestNode("c", false); AccessibilityNodeInfoCompat d = obtainTestNode("d", false); AccessibilityNodeInfoCompat e = obtainTestNode("e", false); AccessibilityNodeInfoCompat f = obtainTestNode("f", true); addChild(a, b); addChild(a, c); addChild(b, c); addChild(c, d); addChild(d, a); addChild(d, e); addChild(e, f); assertTrue(AccessibilityNodeInfoUtils.hasMatchingDescendant(a, focusableFilter)); a.recycle(); b.recycle(); c.recycle(); d.recycle(); e.recycle(); f.recycle(); } @Test(timeout=100) public void hasMatchingDescendant_withSelfLoop_shouldNotHang() { // (a)---->(b)---->(c)---->(d*) // ^ \ ^ \ // \_/ \_/ AccessibilityNodeInfoCompat a = obtainTestNode("a", false); AccessibilityNodeInfoCompat b = obtainTestNode("b", false); AccessibilityNodeInfoCompat c = obtainTestNode("c", false); AccessibilityNodeInfoCompat d = obtainTestNode("d", true); addChild(a, b); addChild(b, b); addChild(b, c); addChild(c, d); addChild(d, d); assertTrue(AccessibilityNodeInfoUtils.hasMatchingDescendant(a, focusableFilter)); a.recycle(); b.recycle(); c.recycle(); d.recycle(); } @Test public void countMatchingAncestors() { // (a)---->(b)----->(c*)---->(d)---->(e*)---->(f*) AccessibilityNodeInfoCompat a = obtainTestNode("a", false); AccessibilityNodeInfoCompat b = obtainTestNode("b", false); AccessibilityNodeInfoCompat c = obtainTestNode("c", true); AccessibilityNodeInfoCompat d = obtainTestNode("d", false); AccessibilityNodeInfoCompat e = obtainTestNode("e", true); AccessibilityNodeInfoCompat f = obtainTestNode("f", true); addChild(a, b); addChild(b, c); addChild(c, d); addChild(d, e); addChild(e, f); assertEquals(2, AccessibilityNodeInfoUtils.countMatchingAncestors(f, focusableFilter)); a.recycle(); b.recycle(); c.recycle(); d.recycle(); e.recycle(); f.recycle(); } @Test(timeout=100) public void countMatchingAncestors_withLoop_shouldNotHang() { // (a)---->(b)----->(c*)---->(d)---->(e*)---->(f*) // ^ | // | | // +<------------------------+ AccessibilityNodeInfoCompat a = obtainTestNode("a", false); AccessibilityNodeInfoCompat b = obtainTestNode("b", false); AccessibilityNodeInfoCompat c = obtainTestNode("c", true); AccessibilityNodeInfoCompat d = obtainTestNode("d", false); AccessibilityNodeInfoCompat e = obtainTestNode("e", true); AccessibilityNodeInfoCompat f = obtainTestNode("f", true); addChild(a, b); addChild(b, c); addChild(c, d); addChild(d, a); addChild(d, e); addChild(e, f); assertEquals(0, AccessibilityNodeInfoUtils.countMatchingAncestors(f, focusableFilter)); a.recycle(); b.recycle(); c.recycle(); d.recycle(); e.recycle(); f.recycle(); } }