AccessibilityCacheTest.java revision 74c6cbaa63dca46cfebff60bda68d6498c6c0c3b
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.accessibility; 18 19import static junit.framework.Assert.assertEquals; 20import static junit.framework.Assert.assertNotNull; 21import static junit.framework.Assert.assertNull; 22import static org.mockito.Matchers.anyBoolean; 23import static org.mockito.Matchers.anyObject; 24import static org.mockito.Mockito.doAnswer; 25import static org.mockito.Mockito.mock; 26import static org.mockito.Mockito.verify; 27import static org.mockito.Mockito.never; 28import static org.mockito.Mockito.when; 29 30import android.support.test.runner.AndroidJUnit4; 31import android.view.accessibility.AccessibilityCache; 32import android.view.accessibility.AccessibilityEvent; 33import android.view.accessibility.AccessibilityInteractionClient; 34import android.view.accessibility.AccessibilityNodeInfo; 35import android.view.accessibility.AccessibilityWindowInfo; 36import android.view.View; 37import org.junit.After; 38import org.junit.Before; 39import org.junit.Test; 40import org.junit.runner.RunWith; 41import org.mockito.invocation.InvocationOnMock; 42import org.mockito.stubbing.Answer; 43 44import java.util.Arrays; 45import java.util.List; 46import java.util.concurrent.atomic.AtomicInteger; 47 48 49@RunWith(AndroidJUnit4.class) 50public class AccessibilityCacheTest { 51 private static int WINDOW_ID_1 = 0xBEEF; 52 private static int WINDOW_ID_2 = 0xFACE; 53 private static int SINGLE_VIEW_ID = 0xCAFE; 54 private static int OTHER_VIEW_ID = 0xCAB2; 55 private static int PARENT_VIEW_ID = 0xFED4; 56 private static int CHILD_VIEW_ID = 0xFEED; 57 private static int MOCK_CONNECTION_ID = 1; 58 59 AccessibilityCache mAccessibilityCache; 60 AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher; 61 AtomicInteger numA11yNodeInfosInUse = new AtomicInteger(0); 62 AtomicInteger numA11yWinInfosInUse = new AtomicInteger(0); 63 64 @Before 65 public void setUp() { 66 mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class); 67 when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true); 68 mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher); 69 AccessibilityNodeInfo.setNumInstancesInUseCounter(numA11yNodeInfosInUse); 70 AccessibilityWindowInfo.setNumInstancesInUseCounter(numA11yWinInfosInUse); 71 } 72 73 @After 74 public void tearDown() { 75 // Make sure we're recycling all of our window and node infos 76 mAccessibilityCache.clear(); 77 AccessibilityInteractionClient.getInstance().clearCache(); 78 assertEquals(0, numA11yWinInfosInUse.get()); 79 assertEquals(0, numA11yNodeInfosInUse.get()); 80 } 81 82 @Test 83 public void testEmptyCache_returnsNull() { 84 assertNull(mAccessibilityCache.getNode(0, 0)); 85 assertNull(mAccessibilityCache.getWindows()); 86 assertNull(mAccessibilityCache.getWindow(0)); 87 } 88 89 @Test 90 public void testEmptyCache_clearDoesntCrash() { 91 mAccessibilityCache.clear(); 92 } 93 94 @Test 95 public void testEmptyCache_a11yEventsHaveNoEffect() { 96 AccessibilityEvent event = AccessibilityEvent.obtain(); 97 int[] a11yEventTypes = { 98 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, 99 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, 100 AccessibilityEvent.TYPE_VIEW_FOCUSED, 101 AccessibilityEvent.TYPE_VIEW_SELECTED, 102 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, 103 AccessibilityEvent.TYPE_VIEW_CLICKED, 104 AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED, 105 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 106 AccessibilityEvent.TYPE_VIEW_SCROLLED, 107 AccessibilityEvent.TYPE_WINDOWS_CHANGED, 108 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED}; 109 for (int i = 0; i < a11yEventTypes.length; i++) { 110 event.setEventType(a11yEventTypes[i]); 111 mAccessibilityCache.onAccessibilityEvent(event); 112 } 113 } 114 115 @Test 116 public void addThenGetWindow_returnsEquivalentButNotSameWindow() { 117 AccessibilityWindowInfo windowInfo = null, copyOfInfo = null, windowFromCache = null; 118 try { 119 windowInfo = AccessibilityWindowInfo.obtain(); 120 windowInfo.setId(WINDOW_ID_1); 121 mAccessibilityCache.addWindow(windowInfo); 122 // Make a copy 123 copyOfInfo = AccessibilityWindowInfo.obtain(windowInfo); 124 windowInfo.setId(WINDOW_ID_2); // Simulate recycling and reusing the original info 125 windowFromCache = mAccessibilityCache.getWindow(WINDOW_ID_1); 126 assertEquals(copyOfInfo, windowFromCache); 127 } finally { 128 windowFromCache.recycle(); 129 windowInfo.recycle(); 130 copyOfInfo.recycle(); 131 } 132 } 133 134 @Test 135 public void addWindowThenClear_noLongerInCache() { 136 putWindowWithIdInCache(WINDOW_ID_1); 137 mAccessibilityCache.clear(); 138 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1)); 139 } 140 141 @Test 142 public void addWindowGetOtherId_returnsNull() { 143 putWindowWithIdInCache(WINDOW_ID_1); 144 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1 + 1)); 145 } 146 147 @Test 148 public void addWindowThenGetWindows_returnsNull() { 149 putWindowWithIdInCache(WINDOW_ID_1); 150 assertNull(mAccessibilityCache.getWindows()); 151 } 152 153 @Test 154 public void setWindowsThenGetWindows_returnsInDecreasingLayerOrder() { 155 AccessibilityWindowInfo windowInfo1 = null, windowInfo2 = null; 156 AccessibilityWindowInfo window1Out = null, window2Out = null; 157 List<AccessibilityWindowInfo> windowsOut = null; 158 try { 159 windowInfo1 = AccessibilityWindowInfo.obtain(); 160 windowInfo1.setId(WINDOW_ID_1); 161 windowInfo1.setLayer(5); 162 windowInfo2 = AccessibilityWindowInfo.obtain(); 163 windowInfo2.setId(WINDOW_ID_2); 164 windowInfo2.setLayer(windowInfo1.getLayer() + 1); 165 List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2); 166 mAccessibilityCache.setWindows(windowsIn); 167 168 windowsOut = mAccessibilityCache.getWindows(); 169 window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1); 170 window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2); 171 172 assertEquals(2, windowsOut.size()); 173 assertEquals(windowInfo2, windowsOut.get(0)); 174 assertEquals(windowInfo1, windowsOut.get(1)); 175 assertEquals(windowInfo1, window1Out); 176 assertEquals(windowInfo2, window2Out); 177 } finally { 178 window1Out.recycle(); 179 window2Out.recycle(); 180 windowInfo1.recycle(); 181 windowInfo2.recycle(); 182 for (AccessibilityWindowInfo windowInfo : windowsOut) { 183 windowInfo.recycle(); 184 } 185 } 186 } 187 188 @Test 189 public void addWindowThenStateChangedEvent_noLongerInCache() { 190 putWindowWithIdInCache(WINDOW_ID_1); 191 mAccessibilityCache.onAccessibilityEvent( 192 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)); 193 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1)); 194 } 195 196 @Test 197 public void addWindowThenWindowsChangedEvent_noLongerInCache() { 198 putWindowWithIdInCache(WINDOW_ID_1); 199 mAccessibilityCache.onAccessibilityEvent( 200 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOWS_CHANGED)); 201 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1)); 202 } 203 204 @Test 205 public void addThenGetNode_returnsEquivalentNode() { 206 AccessibilityNodeInfo nodeInfo, nodeCopy = null, nodeFromCache = null; 207 try { 208 nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 209 long id = nodeInfo.getSourceNodeId(); 210 nodeCopy = AccessibilityNodeInfo.obtain(nodeInfo); 211 mAccessibilityCache.add(nodeInfo); 212 nodeInfo.recycle(); 213 nodeFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id); 214 assertEquals(nodeCopy, nodeFromCache); 215 } finally { 216 nodeFromCache.recycle(); 217 nodeCopy.recycle(); 218 } 219 } 220 221 @Test 222 public void overwriteThenGetNode_returnsNewNode() { 223 final CharSequence contentDescription1 = "foo"; 224 final CharSequence contentDescription2 = "bar"; 225 AccessibilityNodeInfo nodeInfo1 = null, nodeInfo2 = null, nodeFromCache = null; 226 try { 227 nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 228 nodeInfo1.setContentDescription(contentDescription1); 229 long id = nodeInfo1.getSourceNodeId(); 230 nodeInfo2 = AccessibilityNodeInfo.obtain(nodeInfo1); 231 nodeInfo2.setContentDescription(contentDescription2); 232 mAccessibilityCache.add(nodeInfo1); 233 mAccessibilityCache.add(nodeInfo2); 234 nodeFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id); 235 assertEquals(nodeInfo2, nodeFromCache); 236 assertEquals(contentDescription2, nodeFromCache.getContentDescription()); 237 } finally { 238 nodeFromCache.recycle(); 239 nodeInfo2.recycle(); 240 nodeInfo1.recycle(); 241 } 242 } 243 244 @Test 245 public void nodesInDifferentWindowWithSameId_areKeptSeparate() { 246 final CharSequence contentDescription1 = "foo"; 247 final CharSequence contentDescription2 = "bar"; 248 AccessibilityNodeInfo nodeInfo1 = null, nodeInfo2 = null, 249 node1FromCache = null, node2FromCache = null; 250 try { 251 nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 252 nodeInfo1.setContentDescription(contentDescription1); 253 long id = nodeInfo1.getSourceNodeId(); 254 nodeInfo2 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_2); 255 nodeInfo2.setContentDescription(contentDescription2); 256 assertEquals(id, nodeInfo2.getSourceNodeId()); 257 mAccessibilityCache.add(nodeInfo1); 258 mAccessibilityCache.add(nodeInfo2); 259 node1FromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id); 260 node2FromCache = mAccessibilityCache.getNode(WINDOW_ID_2, id); 261 assertEquals(nodeInfo1, node1FromCache); 262 assertEquals(nodeInfo2, node2FromCache); 263 assertEquals(nodeInfo1.getContentDescription(), node1FromCache.getContentDescription()); 264 assertEquals(nodeInfo2.getContentDescription(), node2FromCache.getContentDescription()); 265 } finally { 266 node1FromCache.recycle(); 267 node2FromCache.recycle(); 268 nodeInfo1.recycle(); 269 nodeInfo2.recycle(); 270 } 271 } 272 273 @Test 274 public void addNodeThenClear_nodeIsRemoved() { 275 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 276 long id = nodeInfo.getSourceNodeId(); 277 mAccessibilityCache.add(nodeInfo); 278 nodeInfo.recycle(); 279 mAccessibilityCache.clear(); 280 assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, id)); 281 } 282 283 @Test 284 public void windowStateChangeAndWindowsChangedEvents_clearsNode() { 285 assertEventTypeClearsNode(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 286 assertEventTypeClearsNode(AccessibilityEvent.TYPE_WINDOWS_CHANGED); 287 } 288 289 @Test 290 public void subTreeChangeEvent_clearsNodeAndChild() { 291 AccessibilityEvent event = AccessibilityEvent 292 .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 293 event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); 294 event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 295 296 try { 297 assertEventClearsParentAndChild(event); 298 } finally { 299 event.recycle(); 300 } 301 } 302 303 @Test 304 public void scrollEvent_clearsNodeAndChild() { 305 AccessibilityEvent event = AccessibilityEvent 306 .obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); 307 event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 308 try { 309 assertEventClearsParentAndChild(event); 310 } finally { 311 event.recycle(); 312 } 313 } 314 315 @Test 316 public void reparentNode_clearsOldParent() { 317 AccessibilityNodeInfo parentNodeInfo = getParentNode(); 318 AccessibilityNodeInfo childNodeInfo = getChildNode(); 319 long parentId = parentNodeInfo.getSourceNodeId(); 320 mAccessibilityCache.add(parentNodeInfo); 321 mAccessibilityCache.add(childNodeInfo); 322 323 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID + 1, WINDOW_ID_1)); 324 mAccessibilityCache.add(childNodeInfo); 325 326 AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId); 327 try { 328 assertNull(parentFromCache); 329 } finally { 330 parentNodeInfo.recycle(); 331 childNodeInfo.recycle(); 332 if (parentFromCache != null) { 333 parentFromCache.recycle(); 334 } 335 } 336 } 337 338 @Test 339 public void removeChildFromParent_clearsChild() { 340 AccessibilityNodeInfo parentNodeInfo = getParentNode(); 341 AccessibilityNodeInfo childNodeInfo = getChildNode(); 342 long childId = childNodeInfo.getSourceNodeId(); 343 mAccessibilityCache.add(parentNodeInfo); 344 mAccessibilityCache.add(childNodeInfo); 345 346 AccessibilityNodeInfo parentNodeInfoWithNoChildren = 347 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 348 mAccessibilityCache.add(parentNodeInfoWithNoChildren); 349 350 AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId); 351 try { 352 assertNull(childFromCache); 353 } finally { 354 parentNodeInfoWithNoChildren.recycle(); 355 parentNodeInfo.recycle(); 356 childNodeInfo.recycle(); 357 if (childFromCache != null) { 358 childFromCache.recycle(); 359 } 360 } 361 } 362 363 @Test 364 public void nodeSourceOfA11yFocusEvent_getsRefreshed() { 365 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 366 nodeInfo.setAccessibilityFocused(false); 367 mAccessibilityCache.add(nodeInfo); 368 AccessibilityEvent event = AccessibilityEvent.obtain( 369 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 370 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 371 mAccessibilityCache.onAccessibilityEvent(event); 372 event.recycle(); 373 try { 374 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 375 } finally { 376 nodeInfo.recycle(); 377 } 378 } 379 380 @Test 381 public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRefreshed() { 382 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 383 nodeInfo.setAccessibilityFocused(true); 384 mAccessibilityCache.add(nodeInfo); 385 AccessibilityEvent event = AccessibilityEvent.obtain( 386 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 387 event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1)); 388 mAccessibilityCache.onAccessibilityEvent(event); 389 event.recycle(); 390 try { 391 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 392 } finally { 393 nodeInfo.recycle(); 394 } 395 } 396 397 @Test 398 public void nodeWithA11yFocusClearsIt_refreshes() { 399 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 400 nodeInfo.setAccessibilityFocused(true); 401 mAccessibilityCache.add(nodeInfo); 402 AccessibilityEvent event = AccessibilityEvent.obtain( 403 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); 404 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 405 mAccessibilityCache.onAccessibilityEvent(event); 406 event.recycle(); 407 try { 408 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 409 } finally { 410 nodeInfo.recycle(); 411 } 412 } 413 414 @Test 415 public void nodeSourceOfInputFocusEvent_getsRefreshed() { 416 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 417 nodeInfo.setFocused(false); 418 mAccessibilityCache.add(nodeInfo); 419 AccessibilityEvent event = AccessibilityEvent.obtain( 420 AccessibilityEvent.TYPE_VIEW_FOCUSED); 421 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 422 mAccessibilityCache.onAccessibilityEvent(event); 423 event.recycle(); 424 try { 425 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 426 } finally { 427 nodeInfo.recycle(); 428 } 429 } 430 431 @Test 432 public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRefreshed() { 433 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 434 nodeInfo.setFocused(true); 435 mAccessibilityCache.add(nodeInfo); 436 AccessibilityEvent event = AccessibilityEvent.obtain( 437 AccessibilityEvent.TYPE_VIEW_FOCUSED); 438 event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1)); 439 mAccessibilityCache.onAccessibilityEvent(event); 440 event.recycle(); 441 try { 442 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 443 } finally { 444 nodeInfo.recycle(); 445 } 446 } 447 448 @Test 449 public void nodeEventSaysWasSelected_getsRefreshed() { 450 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_SELECTED, 451 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 452 } 453 454 @Test 455 public void nodeEventSaysHadTextChanged_getsRefreshed() { 456 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, 457 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 458 } 459 460 @Test 461 public void nodeEventSaysWasClicked_getsRefreshed() { 462 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_CLICKED, 463 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 464 } 465 466 @Test 467 public void nodeEventSaysHadSelectionChange_getsRefreshed() { 468 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED, 469 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 470 } 471 472 @Test 473 public void nodeEventSaysHadTextContentChange_getsRefreshed() { 474 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 475 AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT); 476 } 477 478 @Test 479 public void nodeEventSaysHadContentDescriptionChange_getsRefreshed() { 480 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 481 AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION); 482 } 483 484 @Test 485 public void testCacheCriticalEventList_doesntLackEvents() { 486 for (int i = 0; i < 32; i++) { 487 int eventType = 1 << i; 488 if ((eventType & AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK) == 0) { 489 try { 490 assertEventTypeClearsNode(eventType, false); 491 verify(mAccessibilityNodeRefresher, never()) 492 .refreshNode(anyObject(), anyBoolean()); 493 } catch (Throwable e) { 494 throw new AssertionError( 495 "Failed for eventType: " + AccessibilityEvent.eventTypeToString( 496 eventType), 497 e); 498 } 499 } 500 } 501 } 502 503 private void assertNodeIsRefreshedWithEventType(int eventType, int contentChangeTypes) { 504 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 505 mAccessibilityCache.add(nodeInfo); 506 AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 507 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 508 event.setContentChangeTypes(contentChangeTypes); 509 mAccessibilityCache.onAccessibilityEvent(event); 510 event.recycle(); 511 try { 512 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 513 } finally { 514 nodeInfo.recycle(); 515 } 516 } 517 518 private void putWindowWithIdInCache(int id) { 519 AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain(); 520 windowInfo.setId(id); 521 mAccessibilityCache.addWindow(windowInfo); 522 windowInfo.recycle(); 523 } 524 525 private AccessibilityNodeInfo getNodeWithA11yAndWindowId(int a11yId, int windowId) { 526 AccessibilityNodeInfo node = 527 AccessibilityNodeInfo.obtain(getMockViewWithA11yAndWindowIds(a11yId, windowId)); 528 node.setConnectionId(MOCK_CONNECTION_ID); 529 return node; 530 } 531 532 private View getMockViewWithA11yAndWindowIds(int a11yId, int windowId) { 533 View mockView = mock(View.class); 534 when(mockView.getAccessibilityViewId()).thenReturn(a11yId); 535 when(mockView.getAccessibilityWindowId()).thenReturn(windowId); 536 doAnswer(new Answer<AccessibilityNodeInfo>() { 537 public AccessibilityNodeInfo answer(InvocationOnMock invocation) { 538 return AccessibilityNodeInfo.obtain((View) invocation.getMock()); 539 } 540 }).when(mockView).createAccessibilityNodeInfo(); 541 return mockView; 542 } 543 544 private void assertEventTypeClearsNode(int eventType) { 545 assertEventTypeClearsNode(eventType, true); 546 } 547 548 private void assertEventTypeClearsNode(int eventType, boolean clears) { 549 final int nodeId = 0xBEEF; 550 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(nodeId, WINDOW_ID_1); 551 long id = nodeInfo.getSourceNodeId(); 552 mAccessibilityCache.add(nodeInfo); 553 nodeInfo.recycle(); 554 mAccessibilityCache.onAccessibilityEvent(AccessibilityEvent.obtain(eventType)); 555 AccessibilityNodeInfo cachedNode = mAccessibilityCache.getNode(WINDOW_ID_1, id); 556 try { 557 if (clears) { 558 assertNull(cachedNode); 559 } else { 560 assertNotNull(cachedNode); 561 } 562 } finally { 563 if (cachedNode != null) { 564 cachedNode.recycle(); 565 } 566 } 567 } 568 569 private AccessibilityNodeInfo getParentNode() { 570 AccessibilityNodeInfo parentNodeInfo = 571 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 572 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1)); 573 return parentNodeInfo; 574 } 575 576 private AccessibilityNodeInfo getChildNode() { 577 AccessibilityNodeInfo childNodeInfo = 578 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); 579 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 580 return childNodeInfo; 581 } 582 583 private void assertEventClearsParentAndChild(AccessibilityEvent event) { 584 AccessibilityNodeInfo parentNodeInfo = getParentNode(); 585 AccessibilityNodeInfo childNodeInfo = getChildNode(); 586 long parentId = parentNodeInfo.getSourceNodeId(); 587 long childId = childNodeInfo.getSourceNodeId(); 588 mAccessibilityCache.add(parentNodeInfo); 589 mAccessibilityCache.add(childNodeInfo); 590 591 mAccessibilityCache.onAccessibilityEvent(event); 592 parentNodeInfo.recycle(); 593 childNodeInfo.recycle(); 594 595 AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId); 596 AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId); 597 try { 598 assertNull(parentFromCache); 599 assertNull(childFromCache); 600 } finally { 601 if (parentFromCache != null) { 602 parentFromCache.recycle(); 603 } 604 if (childFromCache != null) { 605 childFromCache.recycle(); 606 } 607 } 608 } 609} 610