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