blob: 4de8155663f5b0cbcae2f0c3da59f6deeee96afc [file] [log] [blame]
Phil Weaverb010b122016-08-17 17:47:48 -07001/*
Phil Weaverc140fdc2017-11-09 15:24:17 -08002 * Copyright 2016 The Android Open Source Project
Phil Weaverb010b122016-08-17 17:47:48 -07003 *
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
Phil Weaverc140fdc2017-11-09 15:24:17 -080017package android.view.accessibility;
Phil Weaverb010b122016-08-17 17:47:48 -070018
19import static junit.framework.Assert.assertEquals;
Eugene Susla74c6cba2016-12-28 14:42:54 -080020import static junit.framework.Assert.assertNotNull;
Phil Weaverb010b122016-08-17 17:47:48 -070021import static junit.framework.Assert.assertNull;
Phil Weaverc140fdc2017-11-09 15:24:17 -080022
Phil Weaverb010b122016-08-17 17:47:48 -070023import static org.mockito.Matchers.anyBoolean;
24import static org.mockito.Matchers.anyObject;
25import static org.mockito.Mockito.doAnswer;
26import static org.mockito.Mockito.mock;
Eugene Susla74c6cba2016-12-28 14:42:54 -080027import static org.mockito.Mockito.never;
Phil Weaverc140fdc2017-11-09 15:24:17 -080028import static org.mockito.Mockito.verify;
Phil Weaverb010b122016-08-17 17:47:48 -070029import static org.mockito.Mockito.when;
30
Aurimas Liutikasbdbde552017-12-19 13:21:10 -080031import android.support.test.filters.LargeTest;
Phil Weaverb010b122016-08-17 17:47:48 -070032import android.support.test.runner.AndroidJUnit4;
Phil Weaverb010b122016-08-17 17:47:48 -070033import android.view.View;
Phil Weaverc140fdc2017-11-09 15:24:17 -080034
Phil Weaverb010b122016-08-17 17:47:48 -070035import org.junit.After;
36import org.junit.Before;
37import org.junit.Test;
38import org.junit.runner.RunWith;
39import org.mockito.invocation.InvocationOnMock;
40import org.mockito.stubbing.Answer;
41
42import java.util.Arrays;
43import java.util.List;
Phil Weaver62d20fa2016-09-15 11:05:55 -070044import java.util.concurrent.atomic.AtomicInteger;
Phil Weaverb010b122016-08-17 17:47:48 -070045
Aurimas Liutikasbdbde552017-12-19 13:21:10 -080046@LargeTest
Phil Weaverb010b122016-08-17 17:47:48 -070047@RunWith(AndroidJUnit4.class)
48public class AccessibilityCacheTest {
Phil Weaverc140fdc2017-11-09 15:24:17 -080049 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;
Phil Weaverb010b122016-08-17 17:47:48 -070057
58 AccessibilityCache mAccessibilityCache;
59 AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
Phil Weaverc140fdc2017-11-09 15:24:17 -080060 AtomicInteger mNumA11yNodeInfosInUse = new AtomicInteger(0);
61 AtomicInteger mNumA11yWinInfosInUse = new AtomicInteger(0);
Phil Weaverb010b122016-08-17 17:47:48 -070062
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);
Phil Weaverc140fdc2017-11-09 15:24:17 -080068 AccessibilityNodeInfo.setNumInstancesInUseCounter(mNumA11yNodeInfosInUse);
69 AccessibilityWindowInfo.setNumInstancesInUseCounter(mNumA11yWinInfosInUse);
Phil Weaverb010b122016-08-17 17:47:48 -070070 }
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();
Phil Weaverc140fdc2017-11-09 15:24:17 -080077 assertEquals(0, mNumA11yWinInfosInUse.get());
78 assertEquals(0, mNumA11yNodeInfosInUse.get());
Phil Weaverb010b122016-08-17 17:47:48 -070079 }
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
Eugene Susla74c6cba2016-12-28 14:42:54 -0800483 @Test
Phil Weaver61a1fab2017-05-03 13:55:51 -0700484 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
Eugene Susla74c6cba2016-12-28 14:42:54 -0800508 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
Phil Weaverb010b122016-08-17 17:47:48 -0700526 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) {
Eugene Susla74c6cba2016-12-28 14:42:54 -0800568 assertEventTypeClearsNode(eventType, true);
569 }
570
571 private void assertEventTypeClearsNode(int eventType, boolean clears) {
Phil Weaverb010b122016-08-17 17:47:48 -0700572 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));
Eugene Susla74c6cba2016-12-28 14:42:54 -0800578 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 }
Phil Weaverb010b122016-08-17 17:47:48 -0700590 }
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}