1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.accessibility; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.mockito.Mockito.verifyZeroInteractions; 24 import static org.mockito.MockitoAnnotations.initMocks; 25 26 import android.os.Bundle; 27 import android.os.RemoteException; 28 29 import androidx.test.ext.junit.runners.AndroidJUnit4; 30 import androidx.test.filters.LargeTest; 31 32 import libcore.util.EmptyArray; 33 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.mockito.Mock; 38 39 /** 40 * Tests for AccessibilityInteractionClient 41 */ 42 @LargeTest 43 @RunWith(AndroidJUnit4.class) 44 public class AccessibilityInteractionClientTest { 45 private static final int MOCK_CONNECTION_ID = 0xabcd; 46 private static final int MOCK_CONNECTION_OTHER_ID = 0xabce; 47 48 49 private MockConnection mMockConnection = new MockConnection(); 50 @Mock private AccessibilityCache mMockCache; 51 52 @Before setUp()53 public void setUp() { 54 initMocks(this); 55 AccessibilityInteractionClient.addConnection( 56 MOCK_CONNECTION_ID, mMockConnection, /*initializeCache=*/true); 57 } 58 59 /** 60 * When the AccessibilityCache refreshes the nodes it contains, it gets very confused if 61 * it is called to update itself during the refresh. It tries to update the node that it's 62 * in the process of refreshing, which leads to AccessibilityNodeInfos in inconsistent states. 63 */ 64 @Test findA11yNodeInfoByA11yId_whenBypassingCache_doesntTouchCache()65 public void findA11yNodeInfoByA11yId_whenBypassingCache_doesntTouchCache() { 66 AccessibilityInteractionClient.setCache(MOCK_CONNECTION_ID, mMockCache); 67 final int windowId = 0x1234; 68 final long accessibilityNodeId = 0x4321L; 69 AccessibilityNodeInfo nodeFromConnection = AccessibilityNodeInfo.obtain(); 70 nodeFromConnection.setSourceNodeId(accessibilityNodeId, windowId); 71 mMockConnection.mInfoToReturn = nodeFromConnection; 72 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 73 AccessibilityNodeInfo node = client.findAccessibilityNodeInfoByAccessibilityId( 74 MOCK_CONNECTION_ID, windowId, accessibilityNodeId, true, 0, null); 75 assertEquals("Node got lost along the way", nodeFromConnection, node); 76 77 verifyZeroInteractions(mMockCache); 78 } 79 80 @Test getCache_differentConnections_returnsDifferentCaches()81 public void getCache_differentConnections_returnsDifferentCaches() { 82 MockConnection mOtherMockConnection = new MockConnection(); 83 AccessibilityInteractionClient.addConnection( 84 MOCK_CONNECTION_OTHER_ID, mOtherMockConnection, /*initializeCache=*/true); 85 86 AccessibilityCache firstCache = AccessibilityInteractionClient.getCache(MOCK_CONNECTION_ID); 87 AccessibilityCache secondCache = AccessibilityInteractionClient.getCache( 88 MOCK_CONNECTION_OTHER_ID); 89 assertNotEquals(firstCache, secondCache); 90 } 91 92 @Test getCache_addConnectionWithoutCache_returnsNullCache()93 public void getCache_addConnectionWithoutCache_returnsNullCache() { 94 // Need to first remove from process cache 95 AccessibilityInteractionClient.removeConnection(MOCK_CONNECTION_OTHER_ID); 96 97 MockConnection mOtherMockConnection = new MockConnection(); 98 AccessibilityInteractionClient.addConnection( 99 MOCK_CONNECTION_OTHER_ID, mOtherMockConnection, /*initializeCache=*/false); 100 101 AccessibilityCache cache = AccessibilityInteractionClient.getCache( 102 MOCK_CONNECTION_OTHER_ID); 103 assertNull(cache); 104 } 105 106 @Test getCache_removeConnection_returnsNull()107 public void getCache_removeConnection_returnsNull() { 108 AccessibilityCache cache = AccessibilityInteractionClient.getCache(MOCK_CONNECTION_ID); 109 assertNotNull(cache); 110 111 AccessibilityInteractionClient.removeConnection(MOCK_CONNECTION_ID); 112 cache = AccessibilityInteractionClient.getCache(MOCK_CONNECTION_ID); 113 assertNull(cache); 114 } 115 116 private static class MockConnection extends AccessibilityServiceConnectionImpl { 117 AccessibilityNodeInfo mInfoToReturn; 118 119 @Override findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long threadId, Bundle arguments)120 public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId, 121 long accessibilityNodeId, int interactionId, 122 IAccessibilityInteractionConnectionCallback callback, int flags, long threadId, 123 Bundle arguments) { 124 try { 125 callback.setFindAccessibilityNodeInfoResult(mInfoToReturn, interactionId); 126 } catch (RemoteException e) { 127 throw new RuntimeException(e); 128 } 129 return EmptyArray.STRING; 130 } 131 } 132 } 133