| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.accessibilityservice.cts; |
| |
| import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen; |
| |
| import static org.hamcrest.CoreMatchers.is; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertThrows; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; |
| import android.accessibility.cts.common.InstrumentedAccessibilityService; |
| import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule; |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.accessibilityservice.cts.activities.AccessibilityCacheActivity; |
| import android.app.Instrumentation; |
| import android.app.UiAutomation; |
| import android.platform.test.annotations.AppModeFull; |
| import android.text.TextUtils; |
| import android.view.accessibility.AccessibilityNodeInfo; |
| import android.view.accessibility.AccessibilityWindowInfo; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.rule.ActivityTestRule; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import org.junit.AfterClass; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.RuleChain; |
| import org.junit.runner.RunWith; |
| |
| import java.util.List; |
| |
| @AppModeFull |
| @RunWith(AndroidJUnit4.class) |
| public class AccessibilityCacheTest { |
| private static Instrumentation sInstrumentation; |
| private static UiAutomation sUiAutomation; |
| |
| private InstrumentedAccessibilityService mService; |
| private AccessibilityCacheActivity mActivity; |
| |
| private AccessibilityDumpOnFailureRule mDumpOnFailureRule = |
| new AccessibilityDumpOnFailureRule(); |
| |
| private final ActivityTestRule<AccessibilityCacheActivity> mActivityRule = |
| new ActivityTestRule<>(AccessibilityCacheActivity.class, false, false); |
| |
| private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService> |
| mInstrumentedAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>( |
| InstrumentedAccessibilityService.class, false); |
| |
| @Rule |
| public final RuleChain mRuleChain = RuleChain |
| .outerRule(mActivityRule) |
| .around(mInstrumentedAccessibilityServiceRule) |
| .around(mDumpOnFailureRule); |
| |
| @BeforeClass |
| public static void oneTimeSetup() throws Exception { |
| sInstrumentation = InstrumentationRegistry.getInstrumentation(); |
| sUiAutomation = sInstrumentation.getUiAutomation(); |
| } |
| |
| @AfterClass |
| public static void postTestTearDown() { |
| sUiAutomation.destroy(); |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| mService = mInstrumentedAccessibilityServiceRule.enableService(); |
| AccessibilityServiceInfo info = mService.getServiceInfo(); |
| info.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; |
| mService.setServiceInfo(info); |
| mActivity = launchActivityAndWaitForItToBeOnscreen( |
| sInstrumentation, sUiAutomation, mActivityRule); |
| } |
| |
| @Test |
| public void enable_cacheEnabled() { |
| assertTrue(mService.setCacheEnabled(false)); |
| assertFalse("Failed to disable", mService.isCacheEnabled()); |
| |
| assertTrue(mService.setCacheEnabled(true)); |
| assertTrue("Failed to enable", mService.isCacheEnabled()); |
| } |
| |
| @Test |
| public void disable_cacheDisabled() { |
| assertTrue(mService.setCacheEnabled(false)); |
| assertFalse("Failed to disable", mService.isCacheEnabled()); |
| } |
| |
| @Test |
| public void queryNode_nodeIsInCache() { |
| AccessibilityNodeInfo info = mService.getRootInActiveWindow(); |
| assertTrue("Node is not in cache", mService.isNodeInCache(info)); |
| } |
| |
| @Test |
| public void invalidateNode_nodeInCacheInvalidated() { |
| AccessibilityNodeInfo info = mService.getRootInActiveWindow(); |
| assertTrue(mService.clearCachedSubtree(info)); |
| assertFalse("Node is still in cache", mService.isNodeInCache(info)); |
| } |
| |
| @Test |
| public void invalidateNode_subtreeInCacheInvalidated() { |
| // Tree is FrameLayout with TextView and LinearLayout children. |
| // The LinearLayout has a TextView child. |
| AccessibilityNodeInfo root = mService.getRootInActiveWindow(); |
| assertThat(root.getChildCount(), is(2)); |
| AccessibilityNodeInfo child0 = root.getChild(0); |
| AccessibilityNodeInfo child1 = root.getChild(1); |
| AccessibilityNodeInfo grandChild = child1.getChild(0); |
| |
| assertTrue(mService.clearCachedSubtree(root)); |
| |
| assertFalse("Root is in cache", mService.isNodeInCache(root)); |
| assertFalse("Child0 is in cache", mService.isNodeInCache(child0)); |
| assertFalse("Child1 is in cache", mService.isNodeInCache(child1)); |
| assertFalse("Grandchild is in cache", mService.isNodeInCache(grandChild)); |
| } |
| |
| @Test |
| public void clear_cacheInvalidated() { |
| // Tree is FrameLayout with TextView and LinearLayout children. |
| // The LinearLayout has a TextView child. |
| AccessibilityNodeInfo root = mService.getRootInActiveWindow(); |
| assertThat(root.getChildCount(), is(2)); |
| AccessibilityNodeInfo child0 = root.getChild(0); |
| AccessibilityNodeInfo child1 = root.getChild(1); |
| AccessibilityNodeInfo grandChild = child1.getChild(0); |
| |
| assertTrue(mService.clearCache()); |
| |
| assertFalse("Root is in cache", mService.isNodeInCache(root)); |
| assertFalse("Child0 is in cache", mService.isNodeInCache(child0)); |
| assertFalse("Child1 is in cache", mService.isNodeInCache(child1)); |
| assertFalse("Grandchild is in cache", mService.isNodeInCache(grandChild)); |
| } |
| |
| @Test |
| public void getChild_descendantNotPrefetched() { |
| // Tree is FrameLayout with TextView and LinearLayout children. |
| // The LinearLayout has a TextView child. |
| AccessibilityNodeInfo frameRoot = mService.getRootInActiveWindow(); |
| assertThat(frameRoot.getChildCount(), is(2)); |
| AccessibilityNodeInfo textViewChild = frameRoot.getChild(0); |
| AccessibilityNodeInfo linearLayoutChild = frameRoot.getChild(1); |
| AccessibilityNodeInfo frameGrandChild = linearLayoutChild.getChild(0); |
| |
| // Clear cache. |
| assertTrue(mService.clearCachedSubtree(frameRoot)); |
| frameRoot.getChild(1, AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS |
| | AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE); |
| assertTrue("Root is not in cache", mService.isNodeInCache(frameRoot)); |
| assertTrue("TextView is not in cache", mService.isNodeInCache(textViewChild)); |
| assertTrue("LinearLayout is not in cache", mService.isNodeInCache(linearLayoutChild)); |
| // No descendant prefetching |
| assertFalse("Root grandchild is in cache", mService.isNodeInCache(frameGrandChild)); |
| } |
| |
| @Test |
| public void getChild_descendantPrefetched() { |
| // Tree is FrameLayout with TextView and LinearLayout children. |
| // The LinearLayout has a TextView child. |
| AccessibilityNodeInfo frameRoot = mService.getRootInActiveWindow(); |
| assertThat(frameRoot.getChildCount(), is(2)); |
| AccessibilityNodeInfo textViewChild = frameRoot.getChild(0); |
| AccessibilityNodeInfo linearLayoutChild = frameRoot.getChild(1); |
| AccessibilityNodeInfo frameGrandChild = linearLayoutChild.getChild(0); |
| |
| // Clear cache. |
| assertTrue(mService.clearCachedSubtree(frameRoot)); |
| |
| frameRoot.getChild(1, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_DEPTH_FIRST |
| | AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE); |
| |
| assertFalse("Root is in cache", mService.isNodeInCache(frameRoot)); |
| assertFalse("TextView is in cache", mService.isNodeInCache(textViewChild)); |
| assertTrue("LinearLayout is not in cache", mService.isNodeInCache(linearLayoutChild)); |
| // Descendant prefetching |
| assertTrue("Root grandchild is not in cache", mService.isNodeInCache(frameGrandChild)); |
| } |
| |
| @Test |
| public void getParent_ancestorsPrefetched() { |
| // Tree is FrameLayout with TextView and LinearLayout children. |
| // The LinearLayout has a TextView child. |
| AccessibilityNodeInfo frameRoot = mService.getRootInActiveWindow(); |
| assertThat(frameRoot.getChildCount(), is(2)); |
| AccessibilityNodeInfo textViewChild = frameRoot.getChild(0); |
| AccessibilityNodeInfo linearLayoutChild = frameRoot.getChild(1); |
| AccessibilityNodeInfo frameGrandChild = linearLayoutChild.getChild(0); |
| |
| // Clear cache. |
| assertTrue(mService.clearCachedSubtree(frameRoot)); |
| |
| frameGrandChild.getParent(AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS |
| | AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE); |
| |
| assertTrue("Root is not in cache", mService.isNodeInCache(frameRoot)); |
| assertFalse("TextView is in cache", mService.isNodeInCache(textViewChild)); |
| assertTrue("linearLayout is not in cache", mService.isNodeInCache(linearLayoutChild)); |
| // Grandchild itself isn't in the cache |
| assertFalse("root grandchild is in cache", mService.isNodeInCache(frameGrandChild)); |
| } |
| |
| /** |
| * Tests a request that prefetches descendants with multiple strategies. This throws an |
| * exception. |
| */ |
| @Test |
| public void testRequest_withMultiplePrefetchingStrategies_throwsException() { |
| // Tree is FrameLayout with TextView and LinearLayout children. |
| // The LinearLayout has a TextView child. |
| AccessibilityNodeInfo root = mService.getRootInActiveWindow(); |
| assertThat(root.getChildCount(), is(2)); |
| |
| // Clear cache. |
| mService.clearCachedSubtree(root); |
| |
| assertThrows(IllegalArgumentException.class, () -> { |
| root.getChild(0, |
| AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_BREADTH_FIRST |
| | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID); |
| }); |
| } |
| |
| @Test |
| public void testRequest_prefetchWithA11yWindowInfo() { |
| List<AccessibilityWindowInfo> windows = mService.getWindows(); |
| AccessibilityWindowInfo activityWindowInfo = null; |
| for (AccessibilityWindowInfo window : windows) { |
| if (window.getTitle() != null |
| && TextUtils.equals(window.getTitle(), mActivity.getTitle())) { |
| activityWindowInfo = window; |
| } |
| } |
| |
| assertNotNull(activityWindowInfo); |
| AccessibilityNodeInfo windowRoot = activityWindowInfo.getRoot(); |
| |
| assertThat(windowRoot.getChildCount(), is(2)); |
| AccessibilityNodeInfo textViewChild = windowRoot.getChild(0); |
| AccessibilityNodeInfo linearLayoutChild = windowRoot.getChild(1); |
| AccessibilityNodeInfo frameGrandChild = linearLayoutChild.getChild(0); |
| |
| // Clear cache. |
| assertTrue(mService.clearCachedSubtree(windowRoot)); |
| |
| AccessibilityNodeInfo windowRoot2 = activityWindowInfo.getRoot( |
| AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS |
| | AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE); |
| // Confirm roots are the same. |
| assertEquals(windowRoot, windowRoot2); |
| assertFalse("Root is in cache", mService.isNodeInCache(windowRoot)); |
| assertFalse("TextView is in cache", mService.isNodeInCache(textViewChild)); |
| assertFalse("LinearLayout is in cache", mService.isNodeInCache(linearLayoutChild)); |
| assertFalse("Root grandchild is in cache", mService.isNodeInCache(frameGrandChild)); |
| } |
| |
| @Test |
| public void testRequest_prefetchWithRootInActiveWindow() { |
| // Tree is FrameLayout with TextView and LinearLayout children. |
| // The LinearLayout has a TextView child. |
| AccessibilityNodeInfo frameRoot = mService.getRootInActiveWindow(); |
| assertThat(frameRoot.getChildCount(), is(2)); |
| AccessibilityNodeInfo textViewChild = frameRoot.getChild(0); |
| AccessibilityNodeInfo linearLayoutChild = frameRoot.getChild(1); |
| AccessibilityNodeInfo frameGrandChild = linearLayoutChild.getChild(0); |
| |
| // Clear cache. |
| assertTrue(mService.clearCachedSubtree(frameRoot)); |
| |
| AccessibilityNodeInfo frameRoot2 = mService.getRootInActiveWindow( |
| AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS |
| | AccessibilityNodeInfo.FLAG_PREFETCH_UNINTERRUPTIBLE); |
| // Confirm roots are the same. |
| assertEquals(frameRoot, frameRoot2); |
| assertTrue("Root is in cache", mService.isNodeInCache(frameRoot2)); |
| assertFalse("TextView is in cache", mService.isNodeInCache(textViewChild)); |
| assertFalse("LinearLayout is in cache", mService.isNodeInCache(linearLayoutChild)); |
| assertFalse("Root grandchild is in cache", mService.isNodeInCache(frameGrandChild)); |
| } |
| } |