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