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 android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY; 20 import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_INPUT; 21 import static android.view.accessibility.AccessibilityNodeInfo.ROOT_ITEM_ID; 22 import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID; 23 import static android.view.accessibility.AccessibilityWindowInfo.ANY_WINDOW_ID; 24 25 import static junit.framework.Assert.assertEquals; 26 import static junit.framework.Assert.assertNotNull; 27 import static junit.framework.Assert.assertNull; 28 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertTrue; 31 import static org.junit.Assert.fail; 32 import static org.mockito.Matchers.anyBoolean; 33 import static org.mockito.Matchers.anyObject; 34 import static org.mockito.Mockito.doAnswer; 35 import static org.mockito.Mockito.mock; 36 import static org.mockito.Mockito.never; 37 import static org.mockito.Mockito.verify; 38 import static org.mockito.Mockito.when; 39 40 import android.os.SystemClock; 41 import android.util.SparseArray; 42 import android.view.Display; 43 import android.view.View; 44 45 import androidx.test.filters.LargeTest; 46 import androidx.test.runner.AndroidJUnit4; 47 48 import com.google.common.base.Throwables; 49 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.invocation.InvocationOnMock; 54 import org.mockito.stubbing.Answer; 55 56 import java.util.Arrays; 57 import java.util.List; 58 59 @LargeTest 60 @RunWith(AndroidJUnit4.class) 61 public class AccessibilityCacheTest { 62 private static final int WINDOW_ID_1 = 0xBEEF; 63 private static final int WINDOW_ID_2 = 0xFACE; 64 private static final int WINDOW_ID_3 = 0xABCD; 65 private static final int WINDOW_ID_4 = 0xDCBA; 66 private static final int SINGLE_VIEW_ID = 0xCAFE; 67 private static final int OTHER_VIEW_ID = 0xCAB2; 68 private static final int PARENT_VIEW_ID = 0xFED4; 69 private static final int CHILD_VIEW_ID = 0xFEED; 70 private static final int OTHER_CHILD_VIEW_ID = 0xACE2; 71 private static final int MOCK_CONNECTION_ID = 1; 72 private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; 73 private static final int DEFAULT_WINDOW_LAYER = 0; 74 private static final int SPECIFIC_WINDOW_LAYER = 5; 75 76 AccessibilityCache mAccessibilityCache; 77 AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher; 78 79 @Before setUp()80 public void setUp() { 81 mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class); 82 when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true); 83 mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher); 84 } 85 86 @Test testEmptyCache_returnsNull()87 public void testEmptyCache_returnsNull() { 88 assertNull(mAccessibilityCache.getNode(0, 0)); 89 assertNull(mAccessibilityCache.getWindowsOnAllDisplays()); 90 assertNull(mAccessibilityCache.getWindow(0)); 91 } 92 93 @Test testEmptyCache_clearDoesntCrash()94 public void testEmptyCache_clearDoesntCrash() { 95 mAccessibilityCache.clear(); 96 } 97 98 @Test testEmptyCache_a11yEventsHaveNoEffect()99 public void testEmptyCache_a11yEventsHaveNoEffect() { 100 AccessibilityEvent event = AccessibilityEvent.obtain(); 101 int[] a11yEventTypes = { 102 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, 103 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, 104 AccessibilityEvent.TYPE_VIEW_FOCUSED, 105 AccessibilityEvent.TYPE_VIEW_SELECTED, 106 AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, 107 AccessibilityEvent.TYPE_VIEW_CLICKED, 108 AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED, 109 AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 110 AccessibilityEvent.TYPE_VIEW_SCROLLED, 111 AccessibilityEvent.TYPE_WINDOWS_CHANGED, 112 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED}; 113 for (int i = 0; i < a11yEventTypes.length; i++) { 114 event.setEventType(a11yEventTypes[i]); 115 mAccessibilityCache.onAccessibilityEvent(event); 116 } 117 } 118 119 @Test addThenGetWindow_returnsEquivalentButNotSameWindow()120 public void addThenGetWindow_returnsEquivalentButNotSameWindow() { 121 AccessibilityWindowInfo windowInfo = null, copyOfInfo = null, windowFromCache = null; 122 try { 123 windowInfo = AccessibilityWindowInfo.obtain(); 124 windowInfo.setId(WINDOW_ID_1); 125 windowInfo.setDisplayId(Display.DEFAULT_DISPLAY); 126 mAccessibilityCache.addWindow(windowInfo); 127 // Make a copy 128 copyOfInfo = AccessibilityWindowInfo.obtain(windowInfo); 129 windowInfo.setId(WINDOW_ID_2); // Simulate recycling and reusing the original info. 130 windowFromCache = mAccessibilityCache.getWindow(WINDOW_ID_1); 131 assertEquals(copyOfInfo, windowFromCache); 132 } finally { 133 windowFromCache.recycle(); 134 windowInfo.recycle(); 135 copyOfInfo.recycle(); 136 } 137 } 138 139 @Test addWindowThenClear_noLongerInCache()140 public void addWindowThenClear_noLongerInCache() { 141 putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY, 142 DEFAULT_WINDOW_LAYER); 143 mAccessibilityCache.clear(); 144 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1)); 145 } 146 147 @Test addWindowGetOtherId_returnsNull()148 public void addWindowGetOtherId_returnsNull() { 149 putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY, 150 DEFAULT_WINDOW_LAYER); 151 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1 + 1)); 152 } 153 154 @Test addWindowThenGetWindows_returnsNull()155 public void addWindowThenGetWindows_returnsNull() { 156 putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY, 157 DEFAULT_WINDOW_LAYER); 158 assertNull(mAccessibilityCache.getWindowsOnAllDisplays()); 159 } 160 161 @Test setWindowsThenGetWindows_returnsInDecreasingLayerOrder()162 public void setWindowsThenGetWindows_returnsInDecreasingLayerOrder() { 163 AccessibilityWindowInfo windowInfo1 = null; 164 AccessibilityWindowInfo windowInfo2 = null; 165 AccessibilityWindowInfo window1Out = null; 166 AccessibilityWindowInfo window2Out = null; 167 List<AccessibilityWindowInfo> windowsOut = null; 168 try { 169 windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER); 170 windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 1); 171 List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2); 172 setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn); 173 174 windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY); 175 window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1); 176 window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2); 177 178 assertEquals(2, windowsOut.size()); 179 assertEquals(windowInfo2, windowsOut.get(0)); 180 assertEquals(windowInfo1, windowsOut.get(1)); 181 assertEquals(windowInfo1, window1Out); 182 assertEquals(windowInfo2, window2Out); 183 } finally { 184 window1Out.recycle(); 185 window2Out.recycle(); 186 windowInfo1.recycle(); 187 windowInfo2.recycle(); 188 for (AccessibilityWindowInfo windowInfo : windowsOut) { 189 windowInfo.recycle(); 190 } 191 } 192 } 193 194 @Test setWindowsAndAddWindow_thenGetWindows_returnsInDecreasingLayerOrder()195 public void setWindowsAndAddWindow_thenGetWindows_returnsInDecreasingLayerOrder() { 196 AccessibilityWindowInfo windowInfo1 = null; 197 AccessibilityWindowInfo windowInfo2 = null; 198 AccessibilityWindowInfo window1Out = null; 199 AccessibilityWindowInfo window2Out = null; 200 AccessibilityWindowInfo window3Out = null; 201 List<AccessibilityWindowInfo> windowsOut = null; 202 try { 203 windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER); 204 windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 2); 205 List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2); 206 setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn); 207 208 putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_3, Display.DEFAULT_DISPLAY, 209 windowInfo1.getLayer() + 1); 210 211 windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY); 212 window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1); 213 window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2); 214 window3Out = mAccessibilityCache.getWindow(WINDOW_ID_3); 215 216 assertEquals(3, windowsOut.size()); 217 assertEquals(windowInfo2, windowsOut.get(0)); 218 assertEquals(windowInfo1, windowsOut.get(2)); 219 assertEquals(windowInfo1, window1Out); 220 assertEquals(windowInfo2, window2Out); 221 assertEquals(window3Out, windowsOut.get(1)); 222 } finally { 223 window1Out.recycle(); 224 window2Out.recycle(); 225 window3Out.recycle(); 226 windowInfo1.recycle(); 227 windowInfo2.recycle(); 228 for (AccessibilityWindowInfo windowInfo : windowsOut) { 229 windowInfo.recycle(); 230 } 231 } 232 } 233 234 @Test 235 public void setWindowsAtFirstDisplay_thenAddWindowAtSecondDisplay_returnWindowLayerOrderUnchange()236 setWindowsAtFirstDisplay_thenAddWindowAtSecondDisplay_returnWindowLayerOrderUnchange() { 237 AccessibilityWindowInfo windowInfo1 = null; 238 AccessibilityWindowInfo windowInfo2 = null; 239 AccessibilityWindowInfo window1Out = null; 240 AccessibilityWindowInfo window2Out = null; 241 List<AccessibilityWindowInfo> windowsOut = null; 242 try { 243 // Sets windows to default display. 244 windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER); 245 windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 2); 246 List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2); 247 setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn); 248 // Adds one window to second display. 249 putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_3, SECONDARY_DISPLAY_ID, 250 windowInfo1.getLayer() + 1); 251 252 windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY); 253 window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1); 254 window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2); 255 256 assertEquals(2, windowsOut.size()); 257 assertEquals(windowInfo2, windowsOut.get(0)); 258 assertEquals(windowInfo1, windowsOut.get(1)); 259 assertEquals(windowInfo1, window1Out); 260 assertEquals(windowInfo2, window2Out); 261 } finally { 262 window1Out.recycle(); 263 window2Out.recycle(); 264 windowInfo1.recycle(); 265 windowInfo2.recycle(); 266 for (AccessibilityWindowInfo windowInfo : windowsOut) { 267 windowInfo.recycle(); 268 } 269 } 270 } 271 272 @Test setWindowsAtTwoDisplays_thenGetWindows_returnsInDecreasingLayerOrder()273 public void setWindowsAtTwoDisplays_thenGetWindows_returnsInDecreasingLayerOrder() { 274 AccessibilityWindowInfo windowInfo1 = null; 275 AccessibilityWindowInfo windowInfo2 = null; 276 AccessibilityWindowInfo window1Out = null; 277 AccessibilityWindowInfo window2Out = null; 278 AccessibilityWindowInfo windowInfo3 = null; 279 AccessibilityWindowInfo windowInfo4 = null; 280 AccessibilityWindowInfo window3Out = null; 281 AccessibilityWindowInfo window4Out = null; 282 List<AccessibilityWindowInfo> windowsOut1 = null; 283 List<AccessibilityWindowInfo> windowsOut2 = null; 284 try { 285 // Prepares all windows for default display. 286 windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER); 287 windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 1); 288 List<AccessibilityWindowInfo> windowsIn1 = Arrays.asList(windowInfo1, windowInfo2); 289 // Prepares all windows for second display. 290 windowInfo3 = obtainAccessibilityWindowInfo(WINDOW_ID_3, windowInfo1.getLayer() + 2); 291 windowInfo4 = obtainAccessibilityWindowInfo(WINDOW_ID_4, windowInfo1.getLayer() + 3); 292 List<AccessibilityWindowInfo> windowsIn2 = Arrays.asList(windowInfo3, windowInfo4); 293 // Sets all windows of all displays into A11y cache. 294 SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>(); 295 allWindows.put(Display.DEFAULT_DISPLAY, windowsIn1); 296 allWindows.put(SECONDARY_DISPLAY_ID, windowsIn2); 297 final long populationTimeStamp = SystemClock.uptimeMillis(); 298 mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp); 299 // Gets windows at default display. 300 windowsOut1 = getWindowsByDisplay(Display.DEFAULT_DISPLAY); 301 window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1); 302 window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2); 303 304 assertEquals(2, windowsOut1.size()); 305 assertEquals(windowInfo2, windowsOut1.get(0)); 306 assertEquals(windowInfo1, windowsOut1.get(1)); 307 assertEquals(windowInfo1, window1Out); 308 assertEquals(windowInfo2, window2Out); 309 // Gets windows at seocnd display. 310 windowsOut2 = getWindowsByDisplay(SECONDARY_DISPLAY_ID); 311 window3Out = mAccessibilityCache.getWindow(WINDOW_ID_3); 312 window4Out = mAccessibilityCache.getWindow(WINDOW_ID_4); 313 314 assertEquals(2, windowsOut2.size()); 315 assertEquals(windowInfo4, windowsOut2.get(0)); 316 assertEquals(windowInfo3, windowsOut2.get(1)); 317 assertEquals(windowInfo3, window3Out); 318 assertEquals(windowInfo4, window4Out); 319 } finally { 320 window1Out.recycle(); 321 window2Out.recycle(); 322 windowInfo1.recycle(); 323 windowInfo2.recycle(); 324 window3Out.recycle(); 325 window4Out.recycle(); 326 windowInfo3.recycle(); 327 windowInfo4.recycle(); 328 for (AccessibilityWindowInfo windowInfo : windowsOut1) { 329 windowInfo.recycle(); 330 } 331 for (AccessibilityWindowInfo windowInfo : windowsOut2) { 332 windowInfo.recycle(); 333 } 334 } 335 } 336 337 @Test setInvalidWindowsAfterWindowsChangedEvent_notInCache()338 public void setInvalidWindowsAfterWindowsChangedEvent_notInCache() { 339 final AccessibilityEvent event = new AccessibilityEvent( 340 AccessibilityEvent.TYPE_WINDOWS_CHANGED); 341 final long eventTime = 1000L; 342 event.setEventTime(eventTime); 343 mAccessibilityCache.onAccessibilityEvent(event); 344 345 final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, 346 SPECIFIC_WINDOW_LAYER); 347 List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1); 348 setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10); 349 350 try { 351 assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY)); 352 } finally { 353 windowInfo1.recycle(); 354 } 355 } 356 357 @Test setInvalidWindowsAfterStateChangedEvent_notInCache()358 public void setInvalidWindowsAfterStateChangedEvent_notInCache() { 359 final AccessibilityEvent event = new AccessibilityEvent( 360 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 361 final long eventTime = 1000L; 362 event.setEventTime(eventTime); 363 mAccessibilityCache.onAccessibilityEvent(event); 364 365 final AccessibilityWindowInfo windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, 366 SPECIFIC_WINDOW_LAYER); 367 List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1); 368 setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn, eventTime - 10); 369 370 try { 371 assertNull(getWindowsByDisplay(Display.DEFAULT_DISPLAY)); 372 } finally { 373 windowInfo1.recycle(); 374 } 375 } 376 377 @Test addWindowThenStateChangedEvent_noLongerInCache()378 public void addWindowThenStateChangedEvent_noLongerInCache() { 379 putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY, 380 DEFAULT_WINDOW_LAYER); 381 mAccessibilityCache.onAccessibilityEvent( 382 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)); 383 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1)); 384 } 385 386 @Test addWindowThenWindowsChangedEvent_noLongerInCache()387 public void addWindowThenWindowsChangedEvent_noLongerInCache() { 388 putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY, 389 DEFAULT_WINDOW_LAYER); 390 mAccessibilityCache.onAccessibilityEvent( 391 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOWS_CHANGED)); 392 assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1)); 393 } 394 395 @Test addThenGetNode_returnsEquivalentNode()396 public void addThenGetNode_returnsEquivalentNode() { 397 AccessibilityNodeInfo nodeInfo, nodeCopy = null, nodeFromCache = null; 398 try { 399 nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 400 long id = nodeInfo.getSourceNodeId(); 401 nodeCopy = AccessibilityNodeInfo.obtain(nodeInfo); 402 mAccessibilityCache.add(nodeInfo); 403 nodeInfo.recycle(); 404 nodeFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id); 405 assertEquals(nodeCopy, nodeFromCache); 406 } finally { 407 nodeFromCache.recycle(); 408 nodeCopy.recycle(); 409 } 410 } 411 412 @Test overwriteThenGetNode_returnsNewNode()413 public void overwriteThenGetNode_returnsNewNode() { 414 final CharSequence contentDescription1 = "foo"; 415 final CharSequence contentDescription2 = "bar"; 416 AccessibilityNodeInfo nodeInfo1 = null, nodeInfo2 = null, nodeFromCache = null; 417 try { 418 nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 419 nodeInfo1.setContentDescription(contentDescription1); 420 long id = nodeInfo1.getSourceNodeId(); 421 nodeInfo2 = AccessibilityNodeInfo.obtain(nodeInfo1); 422 nodeInfo2.setContentDescription(contentDescription2); 423 mAccessibilityCache.add(nodeInfo1); 424 mAccessibilityCache.add(nodeInfo2); 425 nodeFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id); 426 assertEquals(nodeInfo2, nodeFromCache); 427 assertEquals(contentDescription2, nodeFromCache.getContentDescription()); 428 } finally { 429 nodeFromCache.recycle(); 430 nodeInfo2.recycle(); 431 nodeInfo1.recycle(); 432 } 433 } 434 435 @Test nodesInDifferentWindowWithSameId_areKeptSeparate()436 public void nodesInDifferentWindowWithSameId_areKeptSeparate() { 437 final CharSequence contentDescription1 = "foo"; 438 final CharSequence contentDescription2 = "bar"; 439 AccessibilityNodeInfo nodeInfo1 = null, nodeInfo2 = null, 440 node1FromCache = null, node2FromCache = null; 441 try { 442 nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 443 nodeInfo1.setContentDescription(contentDescription1); 444 long id = nodeInfo1.getSourceNodeId(); 445 nodeInfo2 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_2); 446 nodeInfo2.setContentDescription(contentDescription2); 447 assertEquals(id, nodeInfo2.getSourceNodeId()); 448 mAccessibilityCache.add(nodeInfo1); 449 mAccessibilityCache.add(nodeInfo2); 450 node1FromCache = mAccessibilityCache.getNode(WINDOW_ID_1, id); 451 node2FromCache = mAccessibilityCache.getNode(WINDOW_ID_2, id); 452 assertEquals(nodeInfo1, node1FromCache); 453 assertEquals(nodeInfo2, node2FromCache); 454 assertEquals(nodeInfo1.getContentDescription(), node1FromCache.getContentDescription()); 455 assertEquals(nodeInfo2.getContentDescription(), node2FromCache.getContentDescription()); 456 } finally { 457 node1FromCache.recycle(); 458 node2FromCache.recycle(); 459 nodeInfo1.recycle(); 460 nodeInfo2.recycle(); 461 } 462 } 463 464 @Test addNodeThenClear_nodeIsRemoved()465 public void addNodeThenClear_nodeIsRemoved() { 466 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 467 long id = nodeInfo.getSourceNodeId(); 468 mAccessibilityCache.add(nodeInfo); 469 nodeInfo.recycle(); 470 mAccessibilityCache.clear(); 471 assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, id)); 472 } 473 474 @Test windowStateChangeAndWindowsChangedEvents_clearsNode()475 public void windowStateChangeAndWindowsChangedEvents_clearsNode() { 476 assertEventTypeClearsNode(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 477 assertEventTypeClearsNode(AccessibilityEvent.TYPE_WINDOWS_CHANGED); 478 } 479 480 @Test windowsChangedWithWindowsChangeA11yFocusedEvent_dontClearCache()481 public void windowsChangedWithWindowsChangeA11yFocusedEvent_dontClearCache() { 482 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 483 mAccessibilityCache.add(nodeInfo); 484 AccessibilityEvent event = new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOWS_CHANGED); 485 event.setWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED); 486 mAccessibilityCache.onAccessibilityEvent(event); 487 AccessibilityNodeInfo cachedNode = mAccessibilityCache.getNode(WINDOW_ID_1, 488 nodeInfo.getSourceNodeId()); 489 try { 490 assertNotNull(cachedNode); 491 } finally { 492 nodeInfo.recycle(); 493 } 494 } 495 496 @Test subTreeChangeEvent_clearsNodeAndChild()497 public void subTreeChangeEvent_clearsNodeAndChild() { 498 AccessibilityEvent event = AccessibilityEvent 499 .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 500 event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); 501 event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 502 503 try { 504 assertEventClearsParentAndChild(event); 505 } finally { 506 event.recycle(); 507 } 508 } 509 510 @Test subTreeChangeEventFromUncachedNode_clearsNodeInCache()511 public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() { 512 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); 513 long id = nodeInfo.getSourceNodeId(); 514 mAccessibilityCache.add(nodeInfo); 515 nodeInfo.recycle(); 516 517 AccessibilityEvent event = AccessibilityEvent 518 .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 519 event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); 520 event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 521 522 mAccessibilityCache.onAccessibilityEvent(event); 523 AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id); 524 if (shouldBeNull != null) { 525 shouldBeNull.recycle(); 526 } 527 assertNull(shouldBeNull); 528 } 529 530 @Test scrollEvent_clearsNodeAndChild()531 public void scrollEvent_clearsNodeAndChild() { 532 AccessibilityEvent event = AccessibilityEvent 533 .obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); 534 event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 535 try { 536 assertEventClearsParentAndChild(event); 537 } finally { 538 event.recycle(); 539 } 540 } 541 542 @Test reparentNode_clearsOldParent()543 public void reparentNode_clearsOldParent() { 544 AccessibilityNodeInfo parentNodeInfo = getParentNode(); 545 AccessibilityNodeInfo childNodeInfo = getChildNode(); 546 long parentId = parentNodeInfo.getSourceNodeId(); 547 mAccessibilityCache.add(parentNodeInfo); 548 mAccessibilityCache.add(childNodeInfo); 549 550 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID + 1, WINDOW_ID_1)); 551 mAccessibilityCache.add(childNodeInfo); 552 553 AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId); 554 try { 555 assertNull(parentFromCache); 556 } finally { 557 parentNodeInfo.recycle(); 558 childNodeInfo.recycle(); 559 if (parentFromCache != null) { 560 parentFromCache.recycle(); 561 } 562 } 563 } 564 565 @Test removeChildFromParent_clearsChild()566 public void removeChildFromParent_clearsChild() { 567 AccessibilityNodeInfo parentNodeInfo = getParentNode(); 568 AccessibilityNodeInfo childNodeInfo = getChildNode(); 569 long childId = childNodeInfo.getSourceNodeId(); 570 mAccessibilityCache.add(parentNodeInfo); 571 mAccessibilityCache.add(childNodeInfo); 572 573 AccessibilityNodeInfo parentNodeInfoWithNoChildren = 574 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 575 mAccessibilityCache.add(parentNodeInfoWithNoChildren); 576 577 AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId); 578 try { 579 assertNull(childFromCache); 580 } finally { 581 parentNodeInfoWithNoChildren.recycle(); 582 parentNodeInfo.recycle(); 583 childNodeInfo.recycle(); 584 if (childFromCache != null) { 585 childFromCache.recycle(); 586 } 587 } 588 } 589 590 @Test nodeSourceOfA11yFocusEvent_getsRefreshed()591 public void nodeSourceOfA11yFocusEvent_getsRefreshed() { 592 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 593 nodeInfo.setAccessibilityFocused(false); 594 mAccessibilityCache.add(nodeInfo); 595 AccessibilityEvent event = AccessibilityEvent.obtain( 596 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 597 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 598 mAccessibilityCache.onAccessibilityEvent(event); 599 event.recycle(); 600 try { 601 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 602 } finally { 603 nodeInfo.recycle(); 604 } 605 } 606 607 @Test nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRemoved()608 public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRemoved() { 609 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 610 nodeInfo.setAccessibilityFocused(true); 611 mAccessibilityCache.add(nodeInfo); 612 AccessibilityEvent event = AccessibilityEvent.obtain( 613 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); 614 event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1)); 615 mAccessibilityCache.onAccessibilityEvent(event); 616 event.recycle(); 617 try { 618 assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID)); 619 } finally { 620 nodeInfo.recycle(); 621 } 622 } 623 624 @Test nodeWithA11yFocusClearsIt_refreshes()625 public void nodeWithA11yFocusClearsIt_refreshes() { 626 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 627 nodeInfo.setAccessibilityFocused(true); 628 mAccessibilityCache.add(nodeInfo); 629 AccessibilityEvent event = AccessibilityEvent.obtain( 630 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); 631 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 632 mAccessibilityCache.onAccessibilityEvent(event); 633 event.recycle(); 634 try { 635 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 636 } finally { 637 nodeInfo.recycle(); 638 } 639 } 640 641 @Test getFocus_focusedNodeAsInitialNode_returnsNodeWithA11yFocus()642 public void getFocus_focusedNodeAsInitialNode_returnsNodeWithA11yFocus() { 643 AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY); 644 assertFocus(nodeInfo, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(), 645 nodeInfo.getWindowId()); 646 } 647 648 @Test getFocus_focusedNodeAsInitialNode_returnsNodeWithInputFocus()649 public void getFocus_focusedNodeAsInitialNode_returnsNodeWithInputFocus() { 650 AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT); 651 assertFocus(nodeInfo, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId()); 652 } 653 654 655 @Test getFocus_fromAnyWindow_returnsNodeWithA11yFocus()656 public void getFocus_fromAnyWindow_returnsNodeWithA11yFocus() { 657 AccessibilityNodeInfo parentNodeInfo = 658 getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1); 659 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 660 nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1)); 661 setFocus(nodeInfo, FOCUS_ACCESSIBILITY); 662 mAccessibilityCache.add(parentNodeInfo); 663 mAccessibilityCache.add(nodeInfo); 664 665 AccessibilityNodeInfo focusedNodeInfo = 666 mAccessibilityCache.getFocus(FOCUS_ACCESSIBILITY, ROOT_NODE_ID, ANY_WINDOW_ID); 667 try { 668 assertEquals(focusedNodeInfo, nodeInfo); 669 } finally { 670 nodeInfo.recycle(); 671 parentNodeInfo.recycle(); 672 } 673 } 674 675 @Test getFocus_fromAnyWindow_returnsNodeWithInputFocus()676 public void getFocus_fromAnyWindow_returnsNodeWithInputFocus() { 677 AccessibilityNodeInfo parentNodeInfo = 678 getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1); 679 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 680 nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1)); 681 setFocus(nodeInfo, FOCUS_INPUT); 682 mAccessibilityCache.add(parentNodeInfo); 683 mAccessibilityCache.add(nodeInfo); 684 685 AccessibilityNodeInfo focusedNodeInfo = 686 mAccessibilityCache.getFocus(FOCUS_INPUT, ROOT_NODE_ID, ANY_WINDOW_ID); 687 try { 688 assertEquals(focusedNodeInfo, nodeInfo); 689 } finally { 690 nodeInfo.recycle(); 691 parentNodeInfo.recycle(); 692 } 693 } 694 695 696 @Test getFocus_parentNodeAsInitialNode_returnsNodeWithA11yFocus()697 public void getFocus_parentNodeAsInitialNode_returnsNodeWithA11yFocus() { 698 findFocusReturnsFocus_initialNodeIsParent(FOCUS_ACCESSIBILITY); 699 } 700 701 @Test getFocus_parentNodeAsInitialNode_returnsNodeWithInputFocus()702 public void getFocus_parentNodeAsInitialNode_returnsNodeWithInputFocus() { 703 findFocusReturnsFocus_initialNodeIsParent(FOCUS_INPUT); 704 705 } 706 707 @Test getFocus_nonFocusedChildNodeAsInitialNode_returnsNullA11yFocus()708 public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullA11yFocus() { 709 findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_ACCESSIBILITY); 710 } 711 712 @Test getFocus_nonFocusedChildNodeAsInitialNode_returnsNullInputFocus()713 public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullInputFocus() { 714 findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_INPUT); 715 } 716 717 @Test getFocus_cacheCleared_returnsNullA11yFocus()718 public void getFocus_cacheCleared_returnsNullA11yFocus() { 719 AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY); 720 mAccessibilityCache.clear(); 721 assertFocus(null, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(), 722 nodeInfo.getWindowId()); 723 } 724 725 @Test getFocus_cacheCleared_returnsNullInputFocus()726 public void getFocus_cacheCleared_returnsNullInputFocus() { 727 AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT); 728 mAccessibilityCache.clear(); 729 assertFocus(null, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId()); 730 } 731 732 @Test nodeSourceOfInputFocusEvent_getsRefreshed()733 public void nodeSourceOfInputFocusEvent_getsRefreshed() { 734 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 735 nodeInfo.setFocused(false); 736 mAccessibilityCache.add(nodeInfo); 737 AccessibilityEvent event = AccessibilityEvent.obtain( 738 AccessibilityEvent.TYPE_VIEW_FOCUSED); 739 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 740 mAccessibilityCache.onAccessibilityEvent(event); 741 event.recycle(); 742 try { 743 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 744 } finally { 745 nodeInfo.recycle(); 746 } 747 } 748 749 @Test nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRemoved()750 public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRemoved() { 751 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 752 nodeInfo.setFocused(true); 753 mAccessibilityCache.add(nodeInfo); 754 AccessibilityEvent event = AccessibilityEvent.obtain( 755 AccessibilityEvent.TYPE_VIEW_FOCUSED); 756 event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1)); 757 mAccessibilityCache.onAccessibilityEvent(event); 758 event.recycle(); 759 try { 760 assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID)); 761 } finally { 762 nodeInfo.recycle(); 763 } 764 } 765 766 @Test nodeEventSaysWasSelected_getsRefreshed()767 public void nodeEventSaysWasSelected_getsRefreshed() { 768 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_SELECTED, 769 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 770 } 771 772 @Test nodeEventSaysHadTextChanged_getsRefreshed()773 public void nodeEventSaysHadTextChanged_getsRefreshed() { 774 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED, 775 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 776 } 777 778 @Test nodeEventSaysWasClicked_getsRefreshed()779 public void nodeEventSaysWasClicked_getsRefreshed() { 780 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_CLICKED, 781 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 782 } 783 784 @Test nodeEventSaysHadSelectionChange_getsRefreshed()785 public void nodeEventSaysHadSelectionChange_getsRefreshed() { 786 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED, 787 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); 788 } 789 790 @Test nodeEventSaysHadTextContentChange_getsRefreshed()791 public void nodeEventSaysHadTextContentChange_getsRefreshed() { 792 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 793 AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT); 794 } 795 796 @Test nodeEventSaysHadContentDescriptionChange_getsRefreshed()797 public void nodeEventSaysHadContentDescriptionChange_getsRefreshed() { 798 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 799 AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION); 800 } 801 802 @Test addNode_whenNodeBeingReplacedIsOwnGrandparentWithTwoChildren_doesntCrash()803 public void addNode_whenNodeBeingReplacedIsOwnGrandparentWithTwoChildren_doesntCrash() { 804 AccessibilityNodeInfo parentNodeInfo = 805 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 806 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1)); 807 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(OTHER_CHILD_VIEW_ID, WINDOW_ID_1)); 808 AccessibilityNodeInfo childNodeInfo = 809 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); 810 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 811 childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 812 813 AccessibilityNodeInfo replacementParentNodeInfo = 814 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 815 try { 816 mAccessibilityCache.add(parentNodeInfo); 817 mAccessibilityCache.add(childNodeInfo); 818 mAccessibilityCache.add(replacementParentNodeInfo); 819 } finally { 820 parentNodeInfo.recycle(); 821 childNodeInfo.recycle(); 822 replacementParentNodeInfo.recycle(); 823 } 824 } 825 826 @Test addNode_whenNodeBeingReplacedIsOwnGrandparentWithOneChild_doesntCrash()827 public void addNode_whenNodeBeingReplacedIsOwnGrandparentWithOneChild_doesntCrash() { 828 AccessibilityNodeInfo parentNodeInfo = 829 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 830 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1)); 831 AccessibilityNodeInfo childNodeInfo = 832 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); 833 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 834 childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 835 836 AccessibilityNodeInfo replacementParentNodeInfo = 837 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 838 try { 839 mAccessibilityCache.add(parentNodeInfo); 840 mAccessibilityCache.add(childNodeInfo); 841 mAccessibilityCache.add(replacementParentNodeInfo); 842 } finally { 843 parentNodeInfo.recycle(); 844 childNodeInfo.recycle(); 845 replacementParentNodeInfo.recycle(); 846 } 847 } 848 849 @Test testCacheCriticalEventList_doesntLackEvents()850 public void testCacheCriticalEventList_doesntLackEvents() { 851 for (int i = 0; i < 32; i++) { 852 int eventType = 1 << i; 853 if ((eventType & AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK) == 0) { 854 try { 855 assertEventTypeClearsNode(eventType, false); 856 verify(mAccessibilityNodeRefresher, never()) 857 .refreshNode(anyObject(), anyBoolean()); 858 } catch (Throwable e) { 859 throw new AssertionError( 860 "Failed for eventType: " + AccessibilityEvent.eventTypeToString( 861 eventType), 862 e); 863 } 864 } 865 } 866 } 867 868 @Test addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRemoved()869 public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRemoved() { 870 AccessibilityNodeInfo nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 871 nodeInfo1.setAccessibilityFocused(true); 872 mAccessibilityCache.add(nodeInfo1); 873 AccessibilityNodeInfo nodeInfo2 = getNodeWithA11yAndWindowId(OTHER_VIEW_ID, WINDOW_ID_1); 874 nodeInfo2.setAccessibilityFocused(true); 875 mAccessibilityCache.add(nodeInfo2); 876 try { 877 assertNull(mAccessibilityCache.getNode(WINDOW_ID_1, SINGLE_VIEW_ID)); 878 } finally { 879 nodeInfo1.recycle(); 880 nodeInfo2.recycle(); 881 } 882 } 883 884 @Test addSameParentNodeWithDifferentChildNode_whenOriginalChildHasChild_doesntCrash()885 public void addSameParentNodeWithDifferentChildNode_whenOriginalChildHasChild_doesntCrash() { 886 AccessibilityNodeInfo parentNodeInfo = getParentNode(); 887 AccessibilityNodeInfo childNodeInfo = getChildNode(); 888 childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID + 1, WINDOW_ID_1)); 889 890 AccessibilityNodeInfo replacementParentNodeInfo = 891 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 892 replacementParentNodeInfo.addChild( 893 getMockViewWithA11yAndWindowIds(OTHER_CHILD_VIEW_ID, WINDOW_ID_1)); 894 try { 895 mAccessibilityCache.add(parentNodeInfo); 896 mAccessibilityCache.add(childNodeInfo); 897 mAccessibilityCache.add(replacementParentNodeInfo); 898 } catch (IllegalStateException e) { 899 fail("recycle A11yNodeInfo twice" + Throwables.getStackTraceAsString(e)); 900 } finally { 901 parentNodeInfo.recycle(); 902 childNodeInfo.recycle(); 903 replacementParentNodeInfo.recycle(); 904 } 905 } 906 assertNodeIsRefreshedWithEventType(int eventType, int contentChangeTypes)907 private void assertNodeIsRefreshedWithEventType(int eventType, int contentChangeTypes) { 908 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 909 mAccessibilityCache.add(nodeInfo); 910 AccessibilityEvent event = AccessibilityEvent.obtain(eventType); 911 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1)); 912 event.setContentChangeTypes(contentChangeTypes); 913 mAccessibilityCache.onAccessibilityEvent(event); 914 event.recycle(); 915 try { 916 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true); 917 } finally { 918 nodeInfo.recycle(); 919 } 920 } 921 addFocusedNode(int focusType)922 private AccessibilityNodeInfo addFocusedNode(int focusType) { 923 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1); 924 setFocus(nodeInfo, focusType); 925 mAccessibilityCache.add(nodeInfo); 926 return nodeInfo; 927 } 928 assertFocus(AccessibilityNodeInfo focus, int focusType, long nodeId, int windowId)929 private void assertFocus(AccessibilityNodeInfo focus, int focusType, long nodeId, 930 int windowId) { 931 AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus( 932 focusType, nodeId, windowId); 933 try { 934 assertEquals(focusedNodeInfo, focus); 935 } finally { 936 if (focus != null) { 937 focus.recycle(); 938 } 939 if (focusedNodeInfo != null) { 940 focusedNodeInfo.recycle(); 941 } 942 } 943 } 944 945 findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(int focusType)946 private void findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(int focusType) { 947 AccessibilityNodeInfo parentNodeInfo = 948 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 949 AccessibilityNodeInfo childNodeInfo = 950 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); 951 AccessibilityNodeInfo otherChildNodeInfo = 952 getNodeWithA11yAndWindowId(OTHER_CHILD_VIEW_ID, WINDOW_ID_1); 953 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 954 otherChildNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 955 setFocus(childNodeInfo, focusType); 956 mAccessibilityCache.add(parentNodeInfo); 957 mAccessibilityCache.add(childNodeInfo); 958 mAccessibilityCache.add(otherChildNodeInfo); 959 960 AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus( 961 focusType, otherChildNodeInfo.getSourceNodeId(), WINDOW_ID_1); 962 963 try { 964 assertNull(focusedNodeInfo); 965 966 } finally { 967 parentNodeInfo.recycle(); 968 childNodeInfo.recycle(); 969 } 970 } 971 findFocusReturnsFocus_initialNodeIsParent(int focusType)972 private void findFocusReturnsFocus_initialNodeIsParent(int focusType) { 973 AccessibilityNodeInfo parentNodeInfo = 974 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 975 AccessibilityNodeInfo childNodeInfo = 976 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); 977 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 978 setFocus(childNodeInfo, focusType); 979 mAccessibilityCache.add(parentNodeInfo); 980 mAccessibilityCache.add(childNodeInfo); 981 982 AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus( 983 focusType, parentNodeInfo.getSourceNodeId(), WINDOW_ID_1); 984 985 try { 986 assertEquals(focusedNodeInfo, childNodeInfo); 987 988 } finally { 989 parentNodeInfo.recycle(); 990 childNodeInfo.recycle(); 991 } 992 } 993 setFocus(AccessibilityNodeInfo info, int focusType)994 private void setFocus(AccessibilityNodeInfo info, int focusType) { 995 if (focusType == FOCUS_ACCESSIBILITY) { 996 info.setAccessibilityFocused(true); 997 } else { 998 info.setFocused(true); 999 } 1000 } 1001 1002 @Test enable_cacheEnabled()1003 public void enable_cacheEnabled() { 1004 mAccessibilityCache.setEnabled(false); 1005 assertFalse(mAccessibilityCache.isEnabled()); 1006 1007 mAccessibilityCache.setEnabled(true); 1008 assertTrue(mAccessibilityCache.isEnabled()); 1009 } 1010 1011 @Test disable_cacheDisabled()1012 public void disable_cacheDisabled() { 1013 mAccessibilityCache.setEnabled(false); 1014 assertFalse(mAccessibilityCache.isEnabled()); 1015 } 1016 1017 @Test queryNode_nodeIsInCache()1018 public void queryNode_nodeIsInCache() { 1019 AccessibilityNodeInfo info = new AccessibilityNodeInfo(); 1020 mAccessibilityCache.add(info); 1021 1022 assertTrue(mAccessibilityCache.isNodeInCache(info)); 1023 } 1024 1025 @Test clearSubtreeWithNode_nodeInCacheInvalidated()1026 public void clearSubtreeWithNode_nodeInCacheInvalidated() { 1027 AccessibilityNodeInfo info = new AccessibilityNodeInfo(); 1028 info.setSource(getMockViewWithA11yAndWindowIds(1, 1)); 1029 mAccessibilityCache.add(info); 1030 1031 mAccessibilityCache.clearSubTree(info); 1032 assertFalse(mAccessibilityCache.isNodeInCache(info)); 1033 } 1034 1035 @Test clearSubtreeWithNode_subtreeInCacheInvalidated()1036 public void clearSubtreeWithNode_subtreeInCacheInvalidated() { 1037 AccessibilityNodeInfo info = new AccessibilityNodeInfo(); 1038 View parentView = getMockViewWithA11yAndWindowIds(1, 1); 1039 info.setSource(parentView); 1040 1041 AccessibilityNodeInfo childInfo = new AccessibilityNodeInfo(); 1042 View childView = getMockViewWithA11yAndWindowIds(2, 1); 1043 childInfo.setSource(childView); 1044 1045 childInfo.setParent(parentView); 1046 info.addChild(childView); 1047 mAccessibilityCache.add(info); 1048 mAccessibilityCache.add(childInfo); 1049 1050 mAccessibilityCache.clearSubTree(info); 1051 1052 assertFalse(mAccessibilityCache.isNodeInCache(info)); 1053 assertFalse(mAccessibilityCache.isNodeInCache(childInfo)); 1054 } 1055 obtainAccessibilityWindowInfo(int windowId, int layer)1056 private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) { 1057 AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain(); 1058 windowInfo.setId(windowId); 1059 windowInfo.setLayer(layer); 1060 return windowInfo; 1061 } 1062 putWindowWithWindowIdAndDisplayIdInCache(int windowId, int displayId, int layer)1063 private void putWindowWithWindowIdAndDisplayIdInCache(int windowId, int displayId, int layer) { 1064 AccessibilityWindowInfo windowInfo = obtainAccessibilityWindowInfo(windowId, layer); 1065 windowInfo.setDisplayId(displayId); 1066 mAccessibilityCache.addWindow(windowInfo); 1067 windowInfo.recycle(); 1068 } 1069 getNodeWithA11yAndWindowId(int a11yId, int windowId)1070 private AccessibilityNodeInfo getNodeWithA11yAndWindowId(int a11yId, int windowId) { 1071 AccessibilityNodeInfo node = 1072 AccessibilityNodeInfo.obtain(getMockViewWithA11yAndWindowIds(a11yId, windowId)); 1073 node.setConnectionId(MOCK_CONNECTION_ID); 1074 return node; 1075 } 1076 getMockViewWithA11yAndWindowIds(int a11yId, int windowId)1077 private View getMockViewWithA11yAndWindowIds(int a11yId, int windowId) { 1078 View mockView = mock(View.class); 1079 when(mockView.getAccessibilityViewId()).thenReturn(a11yId); 1080 when(mockView.getAccessibilityWindowId()).thenReturn(windowId); 1081 doAnswer(new Answer<AccessibilityNodeInfo>() { 1082 public AccessibilityNodeInfo answer(InvocationOnMock invocation) { 1083 return AccessibilityNodeInfo.obtain((View) invocation.getMock()); 1084 } 1085 }).when(mockView).createAccessibilityNodeInfo(); 1086 return mockView; 1087 } 1088 assertEventTypeClearsNode(int eventType)1089 private void assertEventTypeClearsNode(int eventType) { 1090 assertEventTypeClearsNode(eventType, true); 1091 } 1092 assertEventTypeClearsNode(int eventType, boolean clears)1093 private void assertEventTypeClearsNode(int eventType, boolean clears) { 1094 final int nodeId = 0xBEEF; 1095 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(nodeId, WINDOW_ID_1); 1096 long id = nodeInfo.getSourceNodeId(); 1097 mAccessibilityCache.add(nodeInfo); 1098 nodeInfo.recycle(); 1099 mAccessibilityCache.onAccessibilityEvent(AccessibilityEvent.obtain(eventType)); 1100 AccessibilityNodeInfo cachedNode = mAccessibilityCache.getNode(WINDOW_ID_1, id); 1101 try { 1102 if (clears) { 1103 assertNull(cachedNode); 1104 } else { 1105 assertNotNull(cachedNode); 1106 } 1107 } finally { 1108 if (cachedNode != null) { 1109 cachedNode.recycle(); 1110 } 1111 } 1112 } 1113 getParentNode()1114 private AccessibilityNodeInfo getParentNode() { 1115 AccessibilityNodeInfo parentNodeInfo = 1116 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1); 1117 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1)); 1118 return parentNodeInfo; 1119 } 1120 getChildNode()1121 private AccessibilityNodeInfo getChildNode() { 1122 AccessibilityNodeInfo childNodeInfo = 1123 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); 1124 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); 1125 return childNodeInfo; 1126 } 1127 assertEventClearsParentAndChild(AccessibilityEvent event)1128 private void assertEventClearsParentAndChild(AccessibilityEvent event) { 1129 AccessibilityNodeInfo parentNodeInfo = getParentNode(); 1130 AccessibilityNodeInfo childNodeInfo = getChildNode(); 1131 long parentId = parentNodeInfo.getSourceNodeId(); 1132 long childId = childNodeInfo.getSourceNodeId(); 1133 mAccessibilityCache.add(parentNodeInfo); 1134 mAccessibilityCache.add(childNodeInfo); 1135 1136 mAccessibilityCache.onAccessibilityEvent(event); 1137 parentNodeInfo.recycle(); 1138 childNodeInfo.recycle(); 1139 1140 AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId); 1141 AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId); 1142 try { 1143 assertNull(parentFromCache); 1144 assertNull(childFromCache); 1145 } finally { 1146 if (parentFromCache != null) { 1147 parentFromCache.recycle(); 1148 } 1149 if (childFromCache != null) { 1150 childFromCache.recycle(); 1151 } 1152 } 1153 } 1154 setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows)1155 private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows) { 1156 setWindowsByDisplay(displayId, windows, SystemClock.uptimeMillis()); 1157 } 1158 setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows, long populationTimeStamp)1159 private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows, 1160 long populationTimeStamp) { 1161 SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>(); 1162 allWindows.put(displayId, windows); 1163 mAccessibilityCache.setWindowsOnAllDisplays(allWindows, populationTimeStamp); 1164 } 1165 getWindowsByDisplay(int displayId)1166 private List<AccessibilityWindowInfo> getWindowsByDisplay(int displayId) { 1167 final SparseArray<List<AccessibilityWindowInfo>> allWindows = 1168 mAccessibilityCache.getWindowsOnAllDisplays(); 1169 1170 if (allWindows != null && allWindows.size() > 0) { 1171 return allWindows.get(displayId); 1172 } 1173 return null; 1174 } 1175 } 1176