blob: 1a22a70eed365a64f104fb2633852415107bb1fb [file] [log] [blame]
/*
* Copyright 2016 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 android.view.accessibility;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.view.View;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.google.common.base.Throwables;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.Arrays;
import java.util.List;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class AccessibilityCacheTest {
private static final int WINDOW_ID_1 = 0xBEEF;
private static final int WINDOW_ID_2 = 0xFACE;
private static final int SINGLE_VIEW_ID = 0xCAFE;
private static final int OTHER_VIEW_ID = 0xCAB2;
private static final int PARENT_VIEW_ID = 0xFED4;
private static final int CHILD_VIEW_ID = 0xFEED;
private static final int OTHER_CHILD_VIEW_ID = 0xACE2;
private static final int MOCK_CONNECTION_ID = 1;
AccessibilityCache mAccessibilityCache;
AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
@Before
public void setUp() {
mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class);
when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true);
mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
}
@After
public void tearDown() {
// Make sure we're recycling all of our window and node infos
mAccessibilityCache.clear();
AccessibilityInteractionClient.getInstance().clearCache();
}
@Test
public void testEmptyCache_returnsNull() {
assertNull(mAccessibilityCache.getNode(0, 0));
assertNull(mAccessibilityCache.getWindows());
assertNull(mAccessibilityCache.getWindow(0));
}
@Test
public void testEmptyCache_clearDoesntCrash() {
mAccessibilityCache.clear();
}
@Test
public void testEmptyCache_a11yEventsHaveNoEffect() {
AccessibilityEvent event = AccessibilityEvent.obtain();
int[] a11yEventTypes = {
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
AccessibilityEvent.TYPE_VIEW_FOCUSED,
AccessibilityEvent.TYPE_VIEW_SELECTED,
AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED,
AccessibilityEvent.TYPE_VIEW_CLICKED,
AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED,
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
AccessibilityEvent.TYPE_VIEW_SCROLLED,
AccessibilityEvent.TYPE_WINDOWS_CHANGED,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED};
for (int i = 0; i < a11yEventTypes.length; i++) {
event.setEventType(a11yEventTypes[i]);
mAccessibilityCache.onAccessibilityEvent(event);
}
}
@Test
public void addThenGetWindow_returnsEquivalentButNotSameWindow() {
AccessibilityWindowInfo windowInfo = null, copyOfInfo = null, windowFromCache = null;
try {
windowInfo = AccessibilityWindowInfo.obtain();
windowInfo.setId(WINDOW_ID_1);
mAccessibilityCache.addWindow(windowInfo);
// Make a copy
copyOfInfo = AccessibilityWindowInfo.obtain(windowInfo);
windowInfo.setId(WINDOW_ID_2); // Simulate recycling and reusing the original info
windowFromCache = mAccessibilityCache.getWindow(WINDOW_ID_1);
assertEquals(copyOfInfo, windowFromCache);
} finally {
windowFromCache.recycle();
windowInfo.recycle();
copyOfInfo.recycle();
}
}
@Test
public void addWindowThenClear_noLongerInCache() {
putWindowWithIdInCache(WINDOW_ID_1);
mAccessibilityCache.clear();
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
}
@Test
public void addWindowGetOtherId_returnsNull() {
putWindowWithIdInCache(WINDOW_ID_1);
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1 + 1));
}
@Test
public void addWindowThenGetWindows_returnsNull() {
putWindowWithIdInCache(WINDOW_ID_1);
assertNull(mAccessibilityCache.getWindows());
}
@Test
public void setWindowsThenGetWindows_returnsInDecreasingLayerOrder() {
AccessibilityWindowInfo windowInfo1 = null, windowInfo2 = null;
AccessibilityWindowInfo window1Out = null, window2Out = null;
List<AccessibilityWindowInfo> windowsOut = null;
try {
windowInfo1 = AccessibilityWindowInfo.obtain();
windowInfo1.setId(WINDOW_ID_1);
windowInfo1.setLayer(5);
windowInfo2 = AccessibilityWindowInfo.obtain();
windowInfo2.setId(WINDOW_ID_2);
windowInfo2.setLayer(windowInfo1.getLayer() + 1);
List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2);
mAccessibilityCache.setWindows(windowsIn);
windowsOut = mAccessibilityCache.getWindows();
window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
assertEquals(2, windowsOut.size());
assertEquals(windowInfo2, windowsOut.get(0));
assertEquals(windowInfo1, windowsOut.get(1));
assertEquals(windowInfo1, window1Out);
assertEquals(windowInfo2, window2Out);
} finally {
window1Out.recycle();
window2Out.recycle();
windowInfo1.recycle();
windowInfo2.recycle();
for (AccessibilityWindowInfo windowInfo : windowsOut) {
windowInfo.recycle();
}
}
}
@Test
public void addWindowThenStateChangedEvent_noLongerInCache() {
putWindowWithIdInCache(WINDOW_ID_1);
mAccessibilityCache.onAccessibilityEvent(
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
}
@Test
public void addWindowThenWindowsChangedEvent_noLongerInCache() {
putWindowWithIdInCache(WINDOW_ID_1);
mAccessibilityCache.onAccessibilityEvent(
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOWS_CHANGED));
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
}
@Test
public void addThenGetNode_returnsEquivalentNode() {
AccessibilityNodeInfo nodeInfo, nodeCopy = null, nodeFromCache = null;
try {
nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
long id = nodeInfo.getSourceNodeId();
nodeCopy = AccessibilityNodeInfo.obtain(nodeInfo);
mAccessibilityCache.add(nodeInfo);
nodeInfo.recycle();
nodeFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id);
assertEquals(nodeCopy, nodeFromCache);
} finally {
nodeFromCache.recycle();
nodeCopy.recycle();
}
}
@Test
public void overwriteThenGetNode_returnsNewNode() {
final CharSequence contentDescription1 = "foo";
final CharSequence contentDescription2 = "bar";
AccessibilityNodeInfo nodeInfo1 = null, nodeInfo2 = null, nodeFromCache = null;
try {
nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo1.setContentDescription(contentDescription1);
long id = nodeInfo1.getSourceNodeId();
nodeInfo2 = AccessibilityNodeInfo.obtain(nodeInfo1);
nodeInfo2.setContentDescription(contentDescription2);
mAccessibilityCache.add(nodeInfo1);
mAccessibilityCache.add(nodeInfo2);
nodeFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id);
assertEquals(nodeInfo2, nodeFromCache);
assertEquals(contentDescription2, nodeFromCache.getContentDescription());
} finally {
nodeFromCache.recycle();
nodeInfo2.recycle();
nodeInfo1.recycle();
}
}
@Test
public void nodesInDifferentWindowWithSameId_areKeptSeparate() {
final CharSequence contentDescription1 = "foo";
final CharSequence contentDescription2 = "bar";
AccessibilityNodeInfo nodeInfo1 = null, nodeInfo2 = null,
node1FromCache = null, node2FromCache = null;
try {
nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo1.setContentDescription(contentDescription1);
long id = nodeInfo1.getSourceNodeId();
nodeInfo2 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_2);
nodeInfo2.setContentDescription(contentDescription2);
assertEquals(id, nodeInfo2.getSourceNodeId());
mAccessibilityCache.add(nodeInfo1);
mAccessibilityCache.add(nodeInfo2);
node1FromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id);
node2FromCache = mAccessibilityCache.getNode(WINDOW_ID_2, id);
assertEquals(nodeInfo1, node1FromCache);
assertEquals(nodeInfo2, node2FromCache);
assertEquals(nodeInfo1.getContentDescription(), node1FromCache.getContentDescription());
assertEquals(nodeInfo2.getContentDescription(), node2FromCache.getContentDescription());
} finally {
node1FromCache.recycle();
node2FromCache.recycle();
nodeInfo1.recycle();
nodeInfo2.recycle();
}
}
@Test
public void addNodeThenClear_nodeIsRemoved() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
long id = nodeInfo.getSourceNodeId();
mAccessibilityCache.add(nodeInfo);
nodeInfo.recycle();
mAccessibilityCache.clear();
assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, id));
}
@Test
public void windowStateChangeAndWindowsChangedEvents_clearsNode() {
assertEventTypeClearsNode(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
assertEventTypeClearsNode(AccessibilityEvent.TYPE_WINDOWS_CHANGED);
}
@Test
public void subTreeChangeEvent_clearsNodeAndChild() {
AccessibilityEvent event = AccessibilityEvent
.obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
try {
assertEventClearsParentAndChild(event);
} finally {
event.recycle();
}
}
@Test
public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
long id = nodeInfo.getSourceNodeId();
mAccessibilityCache.add(nodeInfo);
nodeInfo.recycle();
AccessibilityEvent event = AccessibilityEvent
.obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
mAccessibilityCache.onAccessibilityEvent(event);
AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id);
if (shouldBeNull != null) {
shouldBeNull.recycle();
}
assertNull(shouldBeNull);
}
@Test
public void scrollEvent_clearsNodeAndChild() {
AccessibilityEvent event = AccessibilityEvent
.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
try {
assertEventClearsParentAndChild(event);
} finally {
event.recycle();
}
}
@Test
public void reparentNode_clearsOldParent() {
AccessibilityNodeInfo parentNodeInfo = getParentNode();
AccessibilityNodeInfo childNodeInfo = getChildNode();
long parentId = parentNodeInfo.getSourceNodeId();
mAccessibilityCache.add(parentNodeInfo);
mAccessibilityCache.add(childNodeInfo);
childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID + 1, WINDOW_ID_1));
mAccessibilityCache.add(childNodeInfo);
AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId);
try {
assertNull(parentFromCache);
} finally {
parentNodeInfo.recycle();
childNodeInfo.recycle();
if (parentFromCache != null) {
parentFromCache.recycle();
}
}
}
@Test
public void removeChildFromParent_clearsChild() {
AccessibilityNodeInfo parentNodeInfo = getParentNode();
AccessibilityNodeInfo childNodeInfo = getChildNode();
long childId = childNodeInfo.getSourceNodeId();
mAccessibilityCache.add(parentNodeInfo);
mAccessibilityCache.add(childNodeInfo);
AccessibilityNodeInfo parentNodeInfoWithNoChildren =
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
mAccessibilityCache.add(parentNodeInfoWithNoChildren);
AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId);
try {
assertNull(childFromCache);
} finally {
parentNodeInfoWithNoChildren.recycle();
parentNodeInfo.recycle();
childNodeInfo.recycle();
if (childFromCache != null) {
childFromCache.recycle();
}
}
}
@Test
public void nodeSourceOfA11yFocusEvent_getsRefreshed() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo.setAccessibilityFocused(false);
mAccessibilityCache.add(nodeInfo);
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
mAccessibilityCache.onAccessibilityEvent(event);
event.recycle();
try {
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
} finally {
nodeInfo.recycle();
}
}
@Test
public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo.setAccessibilityFocused(true);
mAccessibilityCache.add(nodeInfo);
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1));
mAccessibilityCache.onAccessibilityEvent(event);
event.recycle();
try {
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
} finally {
nodeInfo.recycle();
}
}
@Test
public void nodeWithA11yFocusClearsIt_refreshes() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo.setAccessibilityFocused(true);
mAccessibilityCache.add(nodeInfo);
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
mAccessibilityCache.onAccessibilityEvent(event);
event.recycle();
try {
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
} finally {
nodeInfo.recycle();
}
}
@Test
public void nodeSourceOfInputFocusEvent_getsRefreshed() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo.setFocused(false);
mAccessibilityCache.add(nodeInfo);
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_FOCUSED);
event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
mAccessibilityCache.onAccessibilityEvent(event);
event.recycle();
try {
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
} finally {
nodeInfo.recycle();
}
}
@Test
public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo.setFocused(true);
mAccessibilityCache.add(nodeInfo);
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_FOCUSED);
event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1));
mAccessibilityCache.onAccessibilityEvent(event);
event.recycle();
try {
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
} finally {
nodeInfo.recycle();
}
}
@Test
public void nodeEventSaysWasSelected_getsRefreshed() {
assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_SELECTED,
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@Test
public void nodeEventSaysHadTextChanged_getsRefreshed() {
assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED,
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@Test
public void nodeEventSaysWasClicked_getsRefreshed() {
assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_CLICKED,
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@Test
public void nodeEventSaysHadSelectionChange_getsRefreshed() {
assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED,
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
@Test
public void nodeEventSaysHadTextContentChange_getsRefreshed() {
assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
}
@Test
public void nodeEventSaysHadContentDescriptionChange_getsRefreshed() {
assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
}
@Test
public void addNode_whenNodeBeingReplacedIsOwnGrandparentWithTwoChildren_doesntCrash() {
AccessibilityNodeInfo parentNodeInfo =
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1));
parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(OTHER_CHILD_VIEW_ID, WINDOW_ID_1));
AccessibilityNodeInfo childNodeInfo =
getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
AccessibilityNodeInfo replacementParentNodeInfo =
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
try {
mAccessibilityCache.add(parentNodeInfo);
mAccessibilityCache.add(childNodeInfo);
mAccessibilityCache.add(replacementParentNodeInfo);
} finally {
parentNodeInfo.recycle();
childNodeInfo.recycle();
replacementParentNodeInfo.recycle();
}
}
@Test
public void addNode_whenNodeBeingReplacedIsOwnGrandparentWithOneChild_doesntCrash() {
AccessibilityNodeInfo parentNodeInfo =
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1));
AccessibilityNodeInfo childNodeInfo =
getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
AccessibilityNodeInfo replacementParentNodeInfo =
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
try {
mAccessibilityCache.add(parentNodeInfo);
mAccessibilityCache.add(childNodeInfo);
mAccessibilityCache.add(replacementParentNodeInfo);
} finally {
parentNodeInfo.recycle();
childNodeInfo.recycle();
replacementParentNodeInfo.recycle();
}
}
@Test
public void testCacheCriticalEventList_doesntLackEvents() {
for (int i = 0; i < 32; i++) {
int eventType = 1 << i;
if ((eventType & AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK) == 0) {
try {
assertEventTypeClearsNode(eventType, false);
verify(mAccessibilityNodeRefresher, never())
.refreshNode(anyObject(), anyBoolean());
} catch (Throwable e) {
throw new AssertionError(
"Failed for eventType: " + AccessibilityEvent.eventTypeToString(
eventType),
e);
}
}
}
}
@Test
public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRefreshed() {
AccessibilityNodeInfo nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
nodeInfo1.setAccessibilityFocused(true);
mAccessibilityCache.add(nodeInfo1);
AccessibilityNodeInfo nodeInfo2 = getNodeWithA11yAndWindowId(OTHER_VIEW_ID, WINDOW_ID_1);
nodeInfo2.setAccessibilityFocused(true);
mAccessibilityCache.add(nodeInfo2);
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
mAccessibilityCache.onAccessibilityEvent(event);
event.recycle();
try {
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo1, true);
} finally {
nodeInfo1.recycle();
nodeInfo2.recycle();
}
}
@Test
public void addSameParentNodeWithDifferentChildNode_whenOriginalChildHasChild_doesntCrash() {
AccessibilityNodeInfo parentNodeInfo = getParentNode();
AccessibilityNodeInfo childNodeInfo = getChildNode();
childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID + 1, WINDOW_ID_1));
AccessibilityNodeInfo replacementParentNodeInfo =
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
replacementParentNodeInfo.addChild(
getMockViewWithA11yAndWindowIds(OTHER_CHILD_VIEW_ID, WINDOW_ID_1));
try {
mAccessibilityCache.add(parentNodeInfo);
mAccessibilityCache.add(childNodeInfo);
mAccessibilityCache.add(replacementParentNodeInfo);
} catch (IllegalStateException e) {
fail("recycle A11yNodeInfo twice" + Throwables.getStackTraceAsString(e));
} finally {
parentNodeInfo.recycle();
childNodeInfo.recycle();
replacementParentNodeInfo.recycle();
}
}
private void assertNodeIsRefreshedWithEventType(int eventType, int contentChangeTypes) {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
mAccessibilityCache.add(nodeInfo);
AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
event.setContentChangeTypes(contentChangeTypes);
mAccessibilityCache.onAccessibilityEvent(event);
event.recycle();
try {
verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
} finally {
nodeInfo.recycle();
}
}
private void putWindowWithIdInCache(int id) {
AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
windowInfo.setId(id);
mAccessibilityCache.addWindow(windowInfo);
windowInfo.recycle();
}
private AccessibilityNodeInfo getNodeWithA11yAndWindowId(int a11yId, int windowId) {
AccessibilityNodeInfo node =
AccessibilityNodeInfo.obtain(getMockViewWithA11yAndWindowIds(a11yId, windowId));
node.setConnectionId(MOCK_CONNECTION_ID);
return node;
}
private View getMockViewWithA11yAndWindowIds(int a11yId, int windowId) {
View mockView = mock(View.class);
when(mockView.getAccessibilityViewId()).thenReturn(a11yId);
when(mockView.getAccessibilityWindowId()).thenReturn(windowId);
doAnswer(new Answer<AccessibilityNodeInfo>() {
public AccessibilityNodeInfo answer(InvocationOnMock invocation) {
return AccessibilityNodeInfo.obtain((View) invocation.getMock());
}
}).when(mockView).createAccessibilityNodeInfo();
return mockView;
}
private void assertEventTypeClearsNode(int eventType) {
assertEventTypeClearsNode(eventType, true);
}
private void assertEventTypeClearsNode(int eventType, boolean clears) {
final int nodeId = 0xBEEF;
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(nodeId, WINDOW_ID_1);
long id = nodeInfo.getSourceNodeId();
mAccessibilityCache.add(nodeInfo);
nodeInfo.recycle();
mAccessibilityCache.onAccessibilityEvent(AccessibilityEvent.obtain(eventType));
AccessibilityNodeInfo cachedNode = mAccessibilityCache.getNode(WINDOW_ID_1, id);
try {
if (clears) {
assertNull(cachedNode);
} else {
assertNotNull(cachedNode);
}
} finally {
if (cachedNode != null) {
cachedNode.recycle();
}
}
}
private AccessibilityNodeInfo getParentNode() {
AccessibilityNodeInfo parentNodeInfo =
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1));
return parentNodeInfo;
}
private AccessibilityNodeInfo getChildNode() {
AccessibilityNodeInfo childNodeInfo =
getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
return childNodeInfo;
}
private void assertEventClearsParentAndChild(AccessibilityEvent event) {
AccessibilityNodeInfo parentNodeInfo = getParentNode();
AccessibilityNodeInfo childNodeInfo = getChildNode();
long parentId = parentNodeInfo.getSourceNodeId();
long childId = childNodeInfo.getSourceNodeId();
mAccessibilityCache.add(parentNodeInfo);
mAccessibilityCache.add(childNodeInfo);
mAccessibilityCache.onAccessibilityEvent(event);
parentNodeInfo.recycle();
childNodeInfo.recycle();
AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId);
AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId);
try {
assertNull(parentFromCache);
assertNull(childFromCache);
} finally {
if (parentFromCache != null) {
parentFromCache.recycle();
}
if (childFromCache != null) {
childFromCache.recycle();
}
}
}
}