Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03babf91 authored by Phil Weaver's avatar Phil Weaver Committed by android-build-merger
Browse files

Merge "Consolidate a11y unit tests and get them working" into oc-dev

am: 101b1515

Change-Id: Iae2179b65f758779979e15937c9a0c6b6f6efa4b
parents 9682a0de 101b1515
Loading
Loading
Loading
Loading
+22 −12
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.util.Log;
import android.view.IWindow;
import android.view.View;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IntPair;

import java.util.ArrayList;
@@ -126,6 +127,8 @@ public final class AccessibilityManager {

    final Handler mHandler;

    final Handler.Callback mCallback;

    boolean mIsEnabled;

    int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
@@ -217,12 +220,12 @@ public final class AccessibilityManager {
            // is now off an exception will be thrown. We want to have the exception
            // enforcement to guard against apps that fire unnecessary accessibility
            // events when accessibility is off.
            mHandler.obtainMessage(MyHandler.MSG_SET_STATE, state, 0).sendToTarget();
            mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget();
        }

        @Override
        public void notifyServicesStateChanged() {
            mHandler.obtainMessage(MyHandler.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget();
            mHandler.obtainMessage(MyCallback.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget();
        }

        @Override
@@ -271,7 +274,8 @@ public final class AccessibilityManager {
    public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
        // Constructor can't be chained because we can't create an instance of an inner class
        // before calling another constructor.
        mHandler = new MyHandler(context.getMainLooper());
        mCallback = new MyCallback();
        mHandler = new Handler(context.getMainLooper(), mCallback);
        mUserId = userId;
        synchronized (mLock) {
            tryConnectToServiceLocked(service);
@@ -288,6 +292,7 @@ public final class AccessibilityManager {
     * @hide
     */
    public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
        mCallback = new MyCallback();
        mHandler = handler;
        mUserId = userId;
        synchronized (mLock) {
@@ -302,6 +307,14 @@ public final class AccessibilityManager {
        return mClient;
    }

    /**
     * @hide
     */
    @VisibleForTesting
    public Handler.Callback getCallback() {
        return mCallback;
    }

    /**
     * Returns if the accessibility in the system is enabled.
     *
@@ -711,15 +724,15 @@ public final class AccessibilityManager {
        mIsHighTextContrastEnabled = highTextContrastEnabled;

        if (wasEnabled != enabled) {
            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
            mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
        }

        if (wasTouchExplorationEnabled != touchExplorationEnabled) {
            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
            mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
        }

        if (wasHighTextContrastEnabled != highTextContrastEnabled) {
            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED);
            mHandler.sendEmptyMessage(MyCallback.MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED);
        }
    }

@@ -960,19 +973,15 @@ public final class AccessibilityManager {
        }
    }

    private final class MyHandler extends Handler {
    private final class MyCallback implements Handler.Callback {
        public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1;
        public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2;
        public static final int MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED = 3;
        public static final int MSG_SET_STATE = 4;
        public static final int MSG_NOTIFY_SERVICES_STATE_CHANGED = 5;

        public MyHandler(Looper looper) {
            super(looper, null, false);
        }

        @Override
        public void handleMessage(Message message) {
        public boolean handleMessage(Message message) {
            switch (message.what) {
                case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: {
                    handleNotifyAccessibilityStateChanged();
@@ -998,6 +1007,7 @@ public final class AccessibilityManager {
                    }
                } break;
            }
            return true;
        }
    }
}
+0 −14
Original line number Diff line number Diff line
@@ -52,20 +52,6 @@
    <application>
        <uses-library android:name="android.test.runner" />

        <service android:name="com.android.server.AccessibilityManagerServiceTest$MyFirstMockAccessibilityService"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
          <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
          </intent-filter>
        </service>

        <service android:name="com.android.server.AccessibilityManagerServiceTest$MySecondMockAccessibilityService"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
          <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
          </intent-filter>
        </service>

        <service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService"
            android:exported="false">
          <intent-filter>
+0 −762

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −256
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 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;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Intent;
import android.os.Message;
import android.view.accessibility.AccessibilityEvent;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import junit.framework.TestCase;

/**
 * This is the base class for mock {@link AccessibilityService}s.
 */
public abstract class MockAccessibilityService extends AccessibilityService {

    /**
     * The event this service expects to receive.
     */
    private final Queue<AccessibilityEvent> mExpectedEvents = new LinkedList<AccessibilityEvent>();

    /**
     * Interruption call this service expects to receive.
     */
    private boolean mExpectedInterrupt;

    /**
     * Flag if the mock is currently replaying.
     */
    private boolean mReplaying;

    /**
     * Flag if the system is bound as a client to this service.
     */
    private boolean mIsSystemBoundAsClient;

    /**
     * Creates an {@link AccessibilityServiceInfo} populated with default
     * values.
     *
     * @return The default info.
     */
    public static AccessibilityServiceInfo createDefaultInfo() {
        AccessibilityServiceInfo defaultInfo = new AccessibilityServiceInfo();
        defaultInfo.eventTypes = AccessibilityEvent.TYPE_ANNOUNCEMENT;
        defaultInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
        defaultInfo.flags = 0;
        defaultInfo.notificationTimeout = 0;
        defaultInfo.packageNames = new String[] {
            "foo.bar.baz"
        };

        return defaultInfo;
    }

    /**
     * Starts replaying the mock.
     */
    public void replay() {
        mReplaying = true;
    }

    /**
     * Verifies if all expected service methods have been called.
     */
    public void verify() {
        if (!mReplaying) {
            throw new IllegalStateException("Did you forget to call replay()");
        }

        if (mExpectedInterrupt) {
            throw new IllegalStateException("Expected call to #interrupt() not received");
        }
        if (!mExpectedEvents.isEmpty()) {
            throw new IllegalStateException("Expected a call to onAccessibilityEvent() for "
                    + "events \"" + mExpectedEvents + "\" not received");
        }
    }

    /**
     * Resets this instance so it can be reused.
     */
    public void reset() {
        mExpectedEvents.clear();
        mExpectedInterrupt = false;
        mReplaying = false;
    }

    /**
     * Sets an expected call to
     * {@link #onAccessibilityEvent(AccessibilityEvent)} with given event as
     * argument.
     *
     * @param expectedEvent The expected event argument.
     */
    public void expectEvent(AccessibilityEvent expectedEvent) {
        mExpectedEvents.add(expectedEvent);
    }

    /**
     * Sets an expected call of {@link #onInterrupt()}.
     */
    public void expectInterrupt() {
        mExpectedInterrupt = true;
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent receivedEvent) {
        if (!mReplaying) {
            return;
        }

        if (mExpectedEvents.isEmpty()) {
            throw new IllegalStateException("Unexpected event: " + receivedEvent);
        }

        AccessibilityEvent expectedEvent = mExpectedEvents.poll();
        assertEqualsAccessiblityEvent(expectedEvent, receivedEvent);
    }

    @Override
    public void onInterrupt() {
        if (!mReplaying) {
            return;
        }

        if (!mExpectedInterrupt) {
            throw new IllegalStateException("Unexpected call to onInterrupt()");
        }

        mExpectedInterrupt = false;
    }

    @Override
    protected void onServiceConnected() {
        mIsSystemBoundAsClient = true;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        mIsSystemBoundAsClient = false;
        return false;
    }

    /**
     * Returns if the system is bound as client to this service.
     *
     * @return True if the system is bound, false otherwise.
     */
    public boolean isSystemBoundAsClient() {
        return mIsSystemBoundAsClient;
    }

    /**
     * Compares all properties of the <code>expectedEvent</code> and the
     * <code>receviedEvent</code> to verify that the received event is the one
     * that is expected.
     */
    private void assertEqualsAccessiblityEvent(AccessibilityEvent expectedEvent,
            AccessibilityEvent receivedEvent) {
        TestCase.assertEquals("addedCount has incorrect value", expectedEvent.getAddedCount(),
                receivedEvent.getAddedCount());
        TestCase.assertEquals("beforeText has incorrect value", expectedEvent.getBeforeText(),
                receivedEvent.getBeforeText());
        TestCase.assertEquals("checked has incorrect value", expectedEvent.isChecked(),
                receivedEvent.isChecked());
        TestCase.assertEquals("className has incorrect value", expectedEvent.getClassName(),
                receivedEvent.getClassName());
        TestCase.assertEquals("contentDescription has incorrect value", expectedEvent
                .getContentDescription(), receivedEvent.getContentDescription());
        TestCase.assertEquals("currentItemIndex has incorrect value", expectedEvent
                .getCurrentItemIndex(), receivedEvent.getCurrentItemIndex());
        TestCase.assertEquals("enabled has incorrect value", expectedEvent.isEnabled(),
                receivedEvent.isEnabled());
        TestCase.assertEquals("eventType has incorrect value", expectedEvent.getEventType(),
                receivedEvent.getEventType());
        TestCase.assertEquals("fromIndex has incorrect value", expectedEvent.getFromIndex(),
                receivedEvent.getFromIndex());
        TestCase.assertEquals("fullScreen has incorrect value", expectedEvent.isFullScreen(),
                receivedEvent.isFullScreen());
        TestCase.assertEquals("itemCount has incorrect value", expectedEvent.getItemCount(),
                receivedEvent.getItemCount());
        assertEqualsNotificationAsParcelableData(expectedEvent, receivedEvent);
        TestCase.assertEquals("password has incorrect value", expectedEvent.isPassword(),
                receivedEvent.isPassword());
        TestCase.assertEquals("removedCount has incorrect value", expectedEvent.getRemovedCount(),
                receivedEvent.getRemovedCount());
        assertEqualsText(expectedEvent, receivedEvent);
    }

    /**
     * Compares the {@link android.os.Parcelable} data of the
     * <code>expectedEvent</code> and <code>receivedEvent</code> to verify that
     * the received event is the one that is expected.
     */
    private void assertEqualsNotificationAsParcelableData(AccessibilityEvent expectedEvent,
            AccessibilityEvent receivedEvent) {
        String message = "parcelableData has incorrect value";
        Message expectedMessage = (Message) expectedEvent.getParcelableData();
        Message receivedMessage = (Message) receivedEvent.getParcelableData();

        if (expectedMessage == null) {
            if (receivedMessage == null) {
                return;
            }
        }

        TestCase.assertNotNull(message, receivedMessage);

        // we do a very simple sanity check since we do not test Message
        TestCase.assertEquals(message, expectedMessage.what, receivedMessage.what);
    }

    /**
     * Compares the text of the <code>expectedEvent</code> and
     * <code>receivedEvent</code> by comparing the string representation of the
     * corresponding {@link CharSequence}s.
     */
    private void assertEqualsText(AccessibilityEvent expectedEvent,
            AccessibilityEvent receivedEvent) {
        String message = "text has incorrect value";
        List<CharSequence> expectedText = expectedEvent.getText();
        List<CharSequence> receivedText = receivedEvent.getText();

        TestCase.assertEquals(message, expectedText.size(), receivedText.size());

        Iterator<CharSequence> expectedTextIterator = expectedText.iterator();
        Iterator<CharSequence> receivedTextIterator = receivedText.iterator();

        for (int i = 0; i < expectedText.size(); i++) {
            // compare the string representation
            TestCase.assertEquals(message, expectedTextIterator.next().toString(),
                    receivedTextIterator.next().toString());
        }
    }
}
+66 −61
Original line number Diff line number Diff line
@@ -14,18 +14,24 @@
 * limitations under the License.
 */

package com.android.server;
package com.android.server.accessibility;

import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertSame;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
import android.os.Looper;
import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -33,6 +39,10 @@ import android.view.accessibility.IAccessibilityManagerClient;

import com.android.internal.util.IntPair;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -40,44 +50,48 @@ import java.util.ArrayList;
import java.util.List;

/**
 * Tests for the AccessibilityManager which mocking the backing service.
 * Tests for the AccessibilityManager by mocking the backing service.
 */
public class AccessibilityManagerTest extends AndroidTestCase {

    /**
     * Timeout required for pending Binder calls or event processing to
     * complete.
     */
    public static final long TIMEOUT_BINDER_CALL = 50;

    @Mock
    private IAccessibilityManager mMockService;
@RunWith(AndroidJUnit4.class)
public class AccessibilityManagerTest {
    private static final boolean WITH_A11Y_ENABLED = true;
    private static final boolean WITH_A11Y_DISABLED = false;

    @Mock private IAccessibilityManager mMockService;
    private MessageCapturingHandler mHandler;
    private Instrumentation mInstrumentation;

    @BeforeClass
    public static void oneTimeInitialization() {
        if (Looper.myLooper() == null) {
            Looper.prepare();
        }
    }

    @Override
    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mHandler = new MessageCapturingHandler(null);
        mInstrumentation = InstrumentationRegistry.getInstrumentation();
    }

    private AccessibilityManager createManager(boolean enabled) throws Exception {
        if (enabled) {
        long serviceReturnValue = IntPair.of(
                (enabled) ? AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED : 0,
                AccessibilityEvent.TYPES_ALL_MASK);
        when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
                    .thenReturn(
                            IntPair.of(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
                                    AccessibilityEvent.TYPES_ALL_MASK));
        } else {
            when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
                    .thenReturn(IntPair.of(0, AccessibilityEvent.TYPES_ALL_MASK));
        }
                .thenReturn(serviceReturnValue);

        AccessibilityManager manager =
                new AccessibilityManager(mContext, mMockService, UserHandle.USER_CURRENT);
                new AccessibilityManager(mHandler, mMockService, UserHandle.USER_CURRENT);

        verify(mMockService).addClient(any(IAccessibilityManagerClient.class), anyInt());

        mHandler.setCallback(manager.getCallback());
        mHandler.sendAllMessages();
        return manager;
    }

    @MediumTest
    @Test
    public void testGetAccessibilityServiceList() throws Exception {
        // create a list of installed accessibility services the mock service returns
        List<AccessibilityServiceInfo> expectedServices = new ArrayList<>();
@@ -99,53 +113,43 @@ public class AccessibilityManagerTest extends AndroidTestCase {
        assertEquals("All expected services must be returned", expectedServices, receivedServices);
    }

    @MediumTest
    @Test
    public void testInterrupt() throws Exception {
        AccessibilityManager manager = createManager(true);
        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
        manager.interrupt();

        verify(mMockService).interrupt(UserHandle.USER_CURRENT);
    }

    @LargeTest
    @Test
    public void testIsEnabled() throws Exception {
        // invoke the method under test
        AccessibilityManager manager = createManager(true);
        boolean isEnabledServiceEnabled = manager.isEnabled();

        // check expected result
        assertTrue("Must be enabled since the mock service is enabled", isEnabledServiceEnabled);
        // Create manager with a11y enabled
        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
        assertTrue("Must be enabled since the mock service is enabled", manager.isEnabled());

        // disable accessibility
        // Disable accessibility
        manager.getClient().setState(0);

        // wait for the asynchronous IBinder call to complete
        Thread.sleep(TIMEOUT_BINDER_CALL);

        // invoke the method under test
        boolean isEnabledServcieDisabled = manager.isEnabled();

        // check expected result
        assertFalse("Must be disabled since the mock service is disabled",
                isEnabledServcieDisabled);
        mHandler.sendAllMessages();
        assertFalse("Must be disabled since the mock service is disabled", manager.isEnabled());
    }

    @MediumTest
    @Test
    public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
        AccessibilityEvent sentEvent = AccessibilityEvent.obtain(
                AccessibilityEvent.TYPE_ANNOUNCEMENT);

        AccessibilityManager manager = createManager(true);
        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
        manager.sendAccessibilityEvent(sentEvent);

        assertSame("The event should be recycled.", sentEvent, AccessibilityEvent.obtain());
    }

    @MediumTest
    @Test
    public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();

        AccessibilityManager manager = createManager(false  /* disabled */);

        AccessibilityManager manager = createManager(WITH_A11Y_DISABLED);
        mInstrumentation.runOnMainSync(() -> {
            try {
                manager.sendAccessibilityEvent(sentEvent);
                fail("No accessibility events are sent if accessibility is disabled");
@@ -153,5 +157,6 @@ public class AccessibilityManagerTest extends AndroidTestCase {
                // check expected result
                assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage());
            }
        });
    }
}
Loading