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