blob: 993378d8a4d04aab64193319c33d464d02f372f3 [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
Phil Weaver2f69c162018-07-17 12:28:24 -0700303 public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() {
304 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
305 long id = nodeInfo.getSourceNodeId();
306 mAccessibilityCache.add(nodeInfo);
307 nodeInfo.recycle();
308
309 AccessibilityEvent event = AccessibilityEvent
310 .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
311 event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
312 event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
313
314 mAccessibilityCache.onAccessibilityEvent(event);
315 AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id);
316 if (shouldBeNull != null) {
317 shouldBeNull.recycle();
318 }
319 assertNull(shouldBeNull);
320 }
321
322 @Test
Phil Weaverb010b122016-08-17 17:47:48 -0700323 public void scrollEvent_clearsNodeAndChild() {
324 AccessibilityEvent event = AccessibilityEvent
325 .obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
326 event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
327 try {
328 assertEventClearsParentAndChild(event);
329 } finally {
330 event.recycle();
331 }
332 }
333
334 @Test
335 public void reparentNode_clearsOldParent() {
336 AccessibilityNodeInfo parentNodeInfo = getParentNode();
337 AccessibilityNodeInfo childNodeInfo = getChildNode();
338 long parentId = parentNodeInfo.getSourceNodeId();
339 mAccessibilityCache.add(parentNodeInfo);
340 mAccessibilityCache.add(childNodeInfo);
341
342 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID + 1, WINDOW_ID_1));
343 mAccessibilityCache.add(childNodeInfo);
344
345 AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId);
346 try {
347 assertNull(parentFromCache);
348 } finally {
349 parentNodeInfo.recycle();
350 childNodeInfo.recycle();
351 if (parentFromCache != null) {
352 parentFromCache.recycle();
353 }
354 }
355 }
356
357 @Test
358 public void removeChildFromParent_clearsChild() {
359 AccessibilityNodeInfo parentNodeInfo = getParentNode();
360 AccessibilityNodeInfo childNodeInfo = getChildNode();
361 long childId = childNodeInfo.getSourceNodeId();
362 mAccessibilityCache.add(parentNodeInfo);
363 mAccessibilityCache.add(childNodeInfo);
364
365 AccessibilityNodeInfo parentNodeInfoWithNoChildren =
366 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
367 mAccessibilityCache.add(parentNodeInfoWithNoChildren);
368
369 AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId);
370 try {
371 assertNull(childFromCache);
372 } finally {
373 parentNodeInfoWithNoChildren.recycle();
374 parentNodeInfo.recycle();
375 childNodeInfo.recycle();
376 if (childFromCache != null) {
377 childFromCache.recycle();
378 }
379 }
380 }
381
382 @Test
383 public void nodeSourceOfA11yFocusEvent_getsRefreshed() {
384 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
385 nodeInfo.setAccessibilityFocused(false);
386 mAccessibilityCache.add(nodeInfo);
387 AccessibilityEvent event = AccessibilityEvent.obtain(
388 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
389 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
390 mAccessibilityCache.onAccessibilityEvent(event);
391 event.recycle();
392 try {
393 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
394 } finally {
395 nodeInfo.recycle();
396 }
397 }
398
399 @Test
400 public void nodeWithA11yFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
401 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
402 nodeInfo.setAccessibilityFocused(true);
403 mAccessibilityCache.add(nodeInfo);
404 AccessibilityEvent event = AccessibilityEvent.obtain(
405 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
406 event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1));
407 mAccessibilityCache.onAccessibilityEvent(event);
408 event.recycle();
409 try {
410 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
411 } finally {
412 nodeInfo.recycle();
413 }
414 }
415
416 @Test
417 public void nodeWithA11yFocusClearsIt_refreshes() {
418 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
419 nodeInfo.setAccessibilityFocused(true);
420 mAccessibilityCache.add(nodeInfo);
421 AccessibilityEvent event = AccessibilityEvent.obtain(
422 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
423 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
424 mAccessibilityCache.onAccessibilityEvent(event);
425 event.recycle();
426 try {
427 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
428 } finally {
429 nodeInfo.recycle();
430 }
431 }
432
433 @Test
434 public void nodeSourceOfInputFocusEvent_getsRefreshed() {
435 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
436 nodeInfo.setFocused(false);
437 mAccessibilityCache.add(nodeInfo);
438 AccessibilityEvent event = AccessibilityEvent.obtain(
439 AccessibilityEvent.TYPE_VIEW_FOCUSED);
440 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
441 mAccessibilityCache.onAccessibilityEvent(event);
442 event.recycle();
443 try {
444 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
445 } finally {
446 nodeInfo.recycle();
447 }
448 }
449
450 @Test
451 public void nodeWithInputFocusWhenAnotherNodeGetsFocus_getsRefreshed() {
452 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
453 nodeInfo.setFocused(true);
454 mAccessibilityCache.add(nodeInfo);
455 AccessibilityEvent event = AccessibilityEvent.obtain(
456 AccessibilityEvent.TYPE_VIEW_FOCUSED);
457 event.setSource(getMockViewWithA11yAndWindowIds(OTHER_VIEW_ID, WINDOW_ID_1));
458 mAccessibilityCache.onAccessibilityEvent(event);
459 event.recycle();
460 try {
461 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
462 } finally {
463 nodeInfo.recycle();
464 }
465 }
466
467 @Test
468 public void nodeEventSaysWasSelected_getsRefreshed() {
469 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_SELECTED,
470 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
471 }
472
473 @Test
474 public void nodeEventSaysHadTextChanged_getsRefreshed() {
475 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED,
476 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
477 }
478
479 @Test
480 public void nodeEventSaysWasClicked_getsRefreshed() {
481 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_CLICKED,
482 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
483 }
484
485 @Test
486 public void nodeEventSaysHadSelectionChange_getsRefreshed() {
487 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED,
488 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
489 }
490
491 @Test
492 public void nodeEventSaysHadTextContentChange_getsRefreshed() {
493 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
494 AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
495 }
496
497 @Test
498 public void nodeEventSaysHadContentDescriptionChange_getsRefreshed() {
499 assertNodeIsRefreshedWithEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,
500 AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION);
501 }
502
Eugene Susla74c6cba2016-12-28 14:42:54 -0800503 @Test
Phil Weaver61a1fab2017-05-03 13:55:51 -0700504 public void addNode_whenNodeBeingReplacedIsOwnGrandparent_doesntCrash() {
505 AccessibilityNodeInfo parentNodeInfo =
506 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
507 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1));
508 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(OTHER_CHILD_VIEW_ID, WINDOW_ID_1));
509 AccessibilityNodeInfo childNodeInfo =
510 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
511 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
512 childNodeInfo.addChild(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
513
514 AccessibilityNodeInfo replacementParentNodeInfo =
515 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
516 try {
517 mAccessibilityCache.add(parentNodeInfo);
518 mAccessibilityCache.add(childNodeInfo);
519 mAccessibilityCache.add(replacementParentNodeInfo);
520 } finally {
521 parentNodeInfo.recycle();
522 childNodeInfo.recycle();
523 replacementParentNodeInfo.recycle();
524 }
525 }
526
527 @Test
Eugene Susla74c6cba2016-12-28 14:42:54 -0800528 public void testCacheCriticalEventList_doesntLackEvents() {
529 for (int i = 0; i < 32; i++) {
530 int eventType = 1 << i;
531 if ((eventType & AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK) == 0) {
532 try {
533 assertEventTypeClearsNode(eventType, false);
534 verify(mAccessibilityNodeRefresher, never())
535 .refreshNode(anyObject(), anyBoolean());
536 } catch (Throwable e) {
537 throw new AssertionError(
538 "Failed for eventType: " + AccessibilityEvent.eventTypeToString(
539 eventType),
540 e);
541 }
542 }
543 }
544 }
545
Phil Weaverb010b122016-08-17 17:47:48 -0700546 private void assertNodeIsRefreshedWithEventType(int eventType, int contentChangeTypes) {
547 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
548 mAccessibilityCache.add(nodeInfo);
549 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
550 event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
551 event.setContentChangeTypes(contentChangeTypes);
552 mAccessibilityCache.onAccessibilityEvent(event);
553 event.recycle();
554 try {
555 verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo, true);
556 } finally {
557 nodeInfo.recycle();
558 }
559 }
560
561 private void putWindowWithIdInCache(int id) {
562 AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
563 windowInfo.setId(id);
564 mAccessibilityCache.addWindow(windowInfo);
565 windowInfo.recycle();
566 }
567
568 private AccessibilityNodeInfo getNodeWithA11yAndWindowId(int a11yId, int windowId) {
569 AccessibilityNodeInfo node =
570 AccessibilityNodeInfo.obtain(getMockViewWithA11yAndWindowIds(a11yId, windowId));
571 node.setConnectionId(MOCK_CONNECTION_ID);
572 return node;
573 }
574
575 private View getMockViewWithA11yAndWindowIds(int a11yId, int windowId) {
576 View mockView = mock(View.class);
577 when(mockView.getAccessibilityViewId()).thenReturn(a11yId);
578 when(mockView.getAccessibilityWindowId()).thenReturn(windowId);
579 doAnswer(new Answer<AccessibilityNodeInfo>() {
580 public AccessibilityNodeInfo answer(InvocationOnMock invocation) {
581 return AccessibilityNodeInfo.obtain((View) invocation.getMock());
582 }
583 }).when(mockView).createAccessibilityNodeInfo();
584 return mockView;
585 }
586
587 private void assertEventTypeClearsNode(int eventType) {
Eugene Susla74c6cba2016-12-28 14:42:54 -0800588 assertEventTypeClearsNode(eventType, true);
589 }
590
591 private void assertEventTypeClearsNode(int eventType, boolean clears) {
Phil Weaverb010b122016-08-17 17:47:48 -0700592 final int nodeId = 0xBEEF;
593 AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(nodeId, WINDOW_ID_1);
594 long id = nodeInfo.getSourceNodeId();
595 mAccessibilityCache.add(nodeInfo);
596 nodeInfo.recycle();
597 mAccessibilityCache.onAccessibilityEvent(AccessibilityEvent.obtain(eventType));
Eugene Susla74c6cba2016-12-28 14:42:54 -0800598 AccessibilityNodeInfo cachedNode = mAccessibilityCache.getNode(WINDOW_ID_1, id);
599 try {
600 if (clears) {
601 assertNull(cachedNode);
602 } else {
603 assertNotNull(cachedNode);
604 }
605 } finally {
606 if (cachedNode != null) {
607 cachedNode.recycle();
608 }
609 }
Phil Weaverb010b122016-08-17 17:47:48 -0700610 }
611
612 private AccessibilityNodeInfo getParentNode() {
613 AccessibilityNodeInfo parentNodeInfo =
614 getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
615 parentNodeInfo.addChild(getMockViewWithA11yAndWindowIds(CHILD_VIEW_ID, WINDOW_ID_1));
616 return parentNodeInfo;
617 }
618
619 private AccessibilityNodeInfo getChildNode() {
620 AccessibilityNodeInfo childNodeInfo =
621 getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
622 childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
623 return childNodeInfo;
624 }
625
626 private void assertEventClearsParentAndChild(AccessibilityEvent event) {
627 AccessibilityNodeInfo parentNodeInfo = getParentNode();
628 AccessibilityNodeInfo childNodeInfo = getChildNode();
629 long parentId = parentNodeInfo.getSourceNodeId();
630 long childId = childNodeInfo.getSourceNodeId();
631 mAccessibilityCache.add(parentNodeInfo);
632 mAccessibilityCache.add(childNodeInfo);
633
634 mAccessibilityCache.onAccessibilityEvent(event);
635 parentNodeInfo.recycle();
636 childNodeInfo.recycle();
637
638 AccessibilityNodeInfo parentFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, parentId);
639 AccessibilityNodeInfo childFromCache = mAccessibilityCache.getNode(WINDOW_ID_1, childId);
640 try {
641 assertNull(parentFromCache);
642 assertNull(childFromCache);
643 } finally {
644 if (parentFromCache != null) {
645 parentFromCache.recycle();
646 }
647 if (childFromCache != null) {
648 childFromCache.recycle();
649 }
650 }
651 }
652}