diff options
| author | 2025-01-16 10:22:57 -0800 | |
|---|---|---|
| committer | 2025-01-16 10:22:57 -0800 | |
| commit | 46225032e5126bfef6df36a10392cc15c60b7a51 (patch) | |
| tree | 1623aa7e0060ebc634f2b561bd4eda841d1a642f | |
| parent | 554eab73848a40d98fccdc152b7acec961268380 (diff) | |
| parent | fbea199bf45f90151b2d424190084f2a7a6792a6 (diff) | |
Merge "a11y: Add tests for AutoclickController" into main
3 files changed, 263 insertions, 6 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java index b94fa2f59162..8b758d29a2ac 100644 --- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java +++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java @@ -39,6 +39,8 @@ import android.view.MotionEvent.PointerProperties; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import androidx.annotation.VisibleForTesting; + /** * Implements "Automatically click on mouse stop" feature. * @@ -69,10 +71,10 @@ public class AutoclickController extends BaseEventStreamTransformation { private final int mUserId; // Lazily created on the first mouse motion event. - private ClickScheduler mClickScheduler; - private AutoclickSettingsObserver mAutoclickSettingsObserver; - private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler; - private AutoclickIndicatorView mAutoclickIndicatorView; + @VisibleForTesting ClickScheduler mClickScheduler; + @VisibleForTesting AutoclickSettingsObserver mAutoclickSettingsObserver; + @VisibleForTesting AutoclickIndicatorScheduler mAutoclickIndicatorScheduler; + @VisibleForTesting AutoclickIndicatorView mAutoclickIndicatorView; private WindowManager mWindowManager; public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) { @@ -360,7 +362,8 @@ public class AutoclickController extends BaseEventStreamTransformation { * moving. The click is first scheduled when a mouse movement is detected, and then further * delayed on every sufficient mouse movement. */ - final private class ClickScheduler implements Runnable { + @VisibleForTesting + final class ClickScheduler implements Runnable { /** * Minimal distance pointer has to move relative to anchor in order for movement not to be * discarded as noise. Anchor is the position of the last MOVE event that was not considered @@ -474,6 +477,11 @@ public class AutoclickController extends BaseEventStreamTransformation { } } + @VisibleForTesting + int getDelayForTesting() { + return mDelay; + } + /** * Updates the time at which click sequence should occur. * diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java index bf5015176f8c..f87dcdb200bb 100644 --- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java +++ b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java @@ -28,6 +28,8 @@ import android.view.View; import android.view.accessibility.AccessibilityManager; import android.view.animation.LinearInterpolator; +import androidx.annotation.VisibleForTesting; + // A visual indicator for the autoclick feature. public class AutoclickIndicatorView extends View { private static final String TAG = AutoclickIndicatorView.class.getSimpleName(); @@ -37,7 +39,7 @@ public class AutoclickIndicatorView extends View { static final int MINIMAL_ANIMATION_DURATION = 50; - private float mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT; + private int mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT; private final Paint mPaint; @@ -112,6 +114,11 @@ public class AutoclickIndicatorView extends View { mRadius = radius; } + @VisibleForTesting + int getRadiusForTesting() { + return mRadius; + } + public void redrawIndicator() { showIndicator = true; invalidate(); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java new file mode 100644 index 000000000000..acce813ff659 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java @@ -0,0 +1,242 @@ +/* + * Copyright 2025 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 com.android.server.accessibility; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.server.testutils.MockitoUtilsKt.eq; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; + +import android.content.Context; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; +import android.testing.AndroidTestingRunner; +import android.testing.TestableContext; +import android.testing.TestableLooper; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** Test cases for {@link AutoclickController}. */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class AutoclickControllerTest { + + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Rule + public TestableContext mTestableContext = + new TestableContext(getInstrumentation().getContext()); + + private TestableLooper mTestableLooper; + @Mock private AccessibilityTraceManager mMockTrace; + @Mock private WindowManager mMockWindowManager; + private AutoclickController mController; + + @Before + public void setUp() { + mTestableLooper = TestableLooper.get(this); + mTestableContext.addMockSystemService(Context.WINDOW_SERVICE, mMockWindowManager); + mController = + new AutoclickController(mTestableContext, mTestableContext.getUserId(), mMockTrace); + } + + @After + public void tearDown() { + mTestableLooper.processAllMessages(); + } + + @Test + public void onMotionEvent_lazyInitClickScheduler() { + assertNull(mController.mClickScheduler); + + injectFakeMouseActionDownEvent(); + + assertNotNull(mController.mClickScheduler); + } + + @Test + public void onMotionEvent_nonMouseSource_notInitClickScheduler() { + assertNull(mController.mClickScheduler); + + injectFakeNonMouseActionDownEvent(); + + assertNull(mController.mClickScheduler); + } + + @Test + public void onMotionEvent_lazyInitAutoclickSettingsObserver() { + assertNull(mController.mAutoclickSettingsObserver); + + injectFakeMouseActionDownEvent(); + + assertNotNull(mController.mAutoclickSettingsObserver); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorScheduler() { + assertNull(mController.mAutoclickIndicatorScheduler); + + injectFakeMouseActionDownEvent(); + + assertNotNull(mController.mAutoclickIndicatorScheduler); + } + + @Test + @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onMotionEvent_flagOff_notInitAutoclickIndicatorScheduler() { + assertNull(mController.mAutoclickIndicatorScheduler); + + injectFakeMouseActionDownEvent(); + + assertNull(mController.mAutoclickIndicatorScheduler); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorView() { + assertNull(mController.mAutoclickIndicatorView); + + injectFakeMouseActionDownEvent(); + + assertNotNull(mController.mAutoclickIndicatorView); + } + + @Test + @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onMotionEvent_flagOff_notInitAutoclickIndicatorView() { + assertNull(mController.mAutoclickIndicatorView); + + injectFakeMouseActionDownEvent(); + + assertNull(mController.mAutoclickIndicatorView); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onMotionEvent_flagOn_addAutoclickIndicatorViewToWindowManager() { + injectFakeMouseActionDownEvent(); + + verify(mMockWindowManager).addView(eq(mController.mAutoclickIndicatorView), any()); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onDestroy_flagOn_removeAutoclickIndicatorViewToWindowManager() { + injectFakeMouseActionDownEvent(); + + mController.onDestroy(); + + verify(mMockWindowManager).removeView(mController.mAutoclickIndicatorView); + } + + @Test + public void onMotionEvent_initClickSchedulerDelayFromSetting() { + injectFakeMouseActionDownEvent(); + + int delay = + Settings.Secure.getIntForUser( + mTestableContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY, + AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, + mTestableContext.getUserId()); + assertEquals(delay, mController.mClickScheduler.getDelayForTesting()); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onMotionEvent_flagOn_initCursorAreaSizeFromSetting() { + injectFakeMouseActionDownEvent(); + + int size = + Settings.Secure.getIntForUser( + mTestableContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE, + AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT, + mTestableContext.getUserId()); + assertEquals(size, mController.mAutoclickIndicatorView.getRadiusForTesting()); + } + + @Test + public void onDestroy_clearClickScheduler() { + injectFakeMouseActionDownEvent(); + + mController.onDestroy(); + + assertNull(mController.mClickScheduler); + } + + @Test + public void onDestroy_clearAutoclickSettingsObserver() { + injectFakeMouseActionDownEvent(); + + mController.onDestroy(); + + assertNull(mController.mAutoclickSettingsObserver); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void onDestroy_flagOn_clearAutoclickIndicatorScheduler() { + injectFakeMouseActionDownEvent(); + + mController.onDestroy(); + + assertNull(mController.mAutoclickIndicatorScheduler); + } + + private void injectFakeMouseActionDownEvent() { + MotionEvent event = getFakeMotionDownEvent(); + event.setSource(InputDevice.SOURCE_MOUSE); + mController.onMotionEvent(event, event, /* policyFlags= */ 0); + } + + private void injectFakeNonMouseActionDownEvent() { + MotionEvent event = getFakeMotionDownEvent(); + event.setSource(InputDevice.SOURCE_KEYBOARD); + mController.onMotionEvent(event, event, /* policyFlags= */ 0); + } + + private MotionEvent getFakeMotionDownEvent() { + return MotionEvent.obtain( + /* downTime= */ 0, + /* eventTime= */ 0, + /* action= */ MotionEvent.ACTION_DOWN, + /* x= */ 0, + /* y= */ 0, + /* metaState= */ 0); + } +} |