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

Commit 0b29a587 authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

2604226 Add test cases for the internal accessibility infrastructure.

Change-Id: Iaeb2c11b6c2167632086b50d9c79e6459cc072b8
parent 650e3d31
Loading
Loading
Loading
Loading
+0 −167
Original line number Diff line number Diff line
/*
 * Copyright (C) 2009 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;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Notification;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;

import java.util.Timer;
import java.util.TimerTask;

/**
 * This class text the accessibility framework end to end.
 * <p>
 * Note: Since accessibility is provided by {@link AccessibilityService}s we create one,
 * and it generates an event and an interruption dispatching them through the
 * {@link AccessibilityManager}. We verify the received result. To trigger the test
 * go to Settings->Accessibility and select the enable accessibility check and then
 * select the check for this service (same name as the class).
 */
public class AccessibilityTestService extends AccessibilityService {

    private static final String LOG_TAG = "AccessibilityTestService";

    private static final String CLASS_NAME = "foo.bar.baz.Test";
    private static final String PACKAGE_NAME = "foo.bar.baz";
    private static final String TEXT = "Some stuff";
    private static final String BEFORE_TEXT = "Some other stuff";

    private static final String CONTENT_DESCRIPTION = "Content description";

    private static final int ITEM_COUNT = 10;
    private static final int CURRENT_ITEM_INDEX = 1;
    private static final int INTERRUPT_INVOCATION_TYPE = 0x00000200;

    private static final int FROM_INDEX = 1;
    private static final int ADDED_COUNT = 2;
    private static final int REMOVED_COUNT = 1;

    private static final int NOTIFICATION_TIMEOUT_MILLIS = 80;

    private int mReceivedResult;

    private Timer mTimer = new Timer();

    @Override
    public void onServiceConnected() {
        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
        info.notificationTimeout = NOTIFICATION_TIMEOUT_MILLIS;
        info.flags &= AccessibilityServiceInfo.DEFAULT;
        setServiceInfo(info);

        // we need to wait until the system picks our configuration
        // otherwise it will not notify us
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    testAccessibilityEventDispatching();
                    testInterrupt();
                } catch (Exception e) {
                    Log.e(LOG_TAG, "Error in testing Accessibility feature", e);
                }
            }
        }, 1000);
    }

    /**
     * Check here if the event we received is actually the one we sent.
     */
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        assert(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED == event.getEventType());
        assert(event != null);
        assert(event.getEventTime() > 0);
        assert(CLASS_NAME.equals(event.getClassName()));
        assert(PACKAGE_NAME.equals(event.getPackageName()));
        assert(1 == event.getText().size());
        assert(TEXT.equals(event.getText().get(0)));
        assert(BEFORE_TEXT.equals(event.getBeforeText()));
        assert(event.isChecked());
        assert(CONTENT_DESCRIPTION.equals(event.getContentDescription()));
        assert(ITEM_COUNT == event.getItemCount());
        assert(CURRENT_ITEM_INDEX == event.getCurrentItemIndex());
        assert(event.isEnabled());
        assert(event.isPassword());
        assert(FROM_INDEX == event.getFromIndex());
        assert(ADDED_COUNT == event.getAddedCount());
        assert(REMOVED_COUNT == event.getRemovedCount());
        assert(event.getParcelableData() != null);
        assert(1 == ((Notification) event.getParcelableData()).icon);

        // set the type of the receved request
        mReceivedResult = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
    }

    /**
     * Set a flag that we received the interrupt request.
     */
    @Override
    public void onInterrupt() {

        // set the type of the receved request
        mReceivedResult = INTERRUPT_INVOCATION_TYPE;
    }

    /**
     * If an {@link AccessibilityEvent} is sent and received correctly.
     */
   public void testAccessibilityEventDispatching() throws Exception {
       AccessibilityEvent event =
           AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);

       assert(event != null);
       event.setClassName(CLASS_NAME);
       event.setPackageName(PACKAGE_NAME);
       event.getText().add(TEXT);
       event.setBeforeText(BEFORE_TEXT);
       event.setChecked(true);
       event.setContentDescription(CONTENT_DESCRIPTION);
       event.setItemCount(ITEM_COUNT);
       event.setCurrentItemIndex(CURRENT_ITEM_INDEX);
       event.setEnabled(true);
       event.setPassword(true);
       event.setFromIndex(FROM_INDEX);
       event.setAddedCount(ADDED_COUNT);
       event.setRemovedCount(REMOVED_COUNT);
       event.setParcelableData(new Notification(1, "Foo", 1234));

       AccessibilityManager.getInstance(this).sendAccessibilityEvent(event);

       assert(mReceivedResult == event.getEventType());

       Log.i(LOG_TAG, "AccessibilityTestService#testAccessibilityEventDispatching: Success");
   }

   /**
    * If accessibility feedback interruption is triggered and received correctly.
    */
   public void testInterrupt() throws Exception {
       AccessibilityManager.getInstance(this).interrupt();

       assert(INTERRUPT_INVOCATION_TYPE == mReceivedResult);

       Log.i(LOG_TAG, "AccessibilityTestService#testInterrupt: Success");
   }
}
+2 −0
Original line number Diff line number Diff line
@@ -7,8 +7,10 @@ LOCAL_MODULE_TAGS := tests
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_STATIC_JAVA_LIBRARIES := easymocklib

LOCAL_JAVA_LIBRARIES := android.test.runner services

LOCAL_PACKAGE_NAME := FrameworksServicesTests

LOCAL_CERTIFICATE := platform
+13 −0
Original line number Diff line number Diff line
@@ -23,6 +23,19 @@
    
    <application>
        <uses-library android:name="android.test.runner" />

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

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

    </application>

    <instrumentation
+691 −0

File added.

Preview size limit exceeded, changes collapsed.

+257 −0
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 static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.reportMatcher;
import static org.easymock.EasyMock.reset;
import static org.easymock.EasyMock.verify;

import org.easymock.IArgumentMatcher;

import android.content.pm.ServiceInfo;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;

import java.util.ArrayList;
import java.util.List;

/**
 * Tests for the AccessibilityManager which 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;

    /**
     * The reusable mock {@link IAccessibilityManager}.
     */
    private final IAccessibilityManager mMockServiceInterface =
        createStrictMock(IAccessibilityManager.class);

    @Override
    public void setUp() throws Exception {
        reset(mMockServiceInterface);
    }

    @MediumTest
    public void testGetAccessibilityServiceList() throws Exception {
        // create a list of installed accessibility services the mock service returns
        List<ServiceInfo> expectedServices = new ArrayList<ServiceInfo>();
        ServiceInfo serviceInfo = new ServiceInfo();
        serviceInfo.name = "TestServiceInfoName";
        expectedServices.add(serviceInfo);

        // configure the mock service behavior
        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
        expect(mockServiceInterface.getAccessibilityServiceList()).andReturn(expectedServices);
        replay(mockServiceInterface);

        // invoke the method under test
        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
        List<ServiceInfo> receivedServices = manager.getAccessibilityServiceList();

        // check expected result (list equals() compares it contents as well)
        assertEquals("All expected services must be returned", receivedServices, expectedServices);

        // verify the mock service was properly called
        verify(mockServiceInterface);
    }

    @MediumTest
    public void testInterrupt() throws Exception {
        // configure the mock service behavior
        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
        mockServiceInterface.interrupt();
        replay(mockServiceInterface);

        // invoke the method under test
        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
        manager.interrupt();

        // verify the mock service was properly called
        verify(mockServiceInterface);
    }

    @LargeTest
    public void testIsEnabled() throws Exception {
        // configure the mock service behavior
        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
        replay(mockServiceInterface);

        // invoke the method under test
        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
        boolean isEnabledServiceEnabled = manager.isEnabled();

        // check expected result
        assertTrue("Must be enabled since the mock service is enabled", isEnabledServiceEnabled);

        // disable accessibility
        manager.getClient().setEnabled(false);

        // 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);

        // verify the mock service was properly called
        verify(mockServiceInterface);
    }

    @MediumTest
    public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
        // create an event to be dispatched
        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();

        // configure the mock service behavior
        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(true);
        expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent)))
                .andReturn(true);
        expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent)))
                .andReturn(false);
        replay(mockServiceInterface);

        // invoke the method under test (manager and service in different processes)
        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
        manager.sendAccessibilityEvent(sentEvent);

        // check expected result
        AccessibilityEvent nextEventDifferentProcesses = AccessibilityEvent.obtain();
        assertSame("The manager and the service are in different processes, so the event must be " +
                "recycled", sentEvent, nextEventDifferentProcesses);

        // invoke the method under test (manager and service in the same process)
        manager.sendAccessibilityEvent(sentEvent);

        // check expected result
        AccessibilityEvent nextEventSameProcess = AccessibilityEvent.obtain();
        assertNotSame("The manager and the service are in the same process, so the event must not" +
                "be recycled", sentEvent, nextEventSameProcess);

        // verify the mock service was properly called
        verify(mockServiceInterface);
    }

    @MediumTest
    public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
        // create an event to be dispatched
        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();

        // configure the mock service behavior
        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(false);
        replay(mockServiceInterface);

        // invoke the method under test (accessibility disabled)
        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
        try {
            manager.sendAccessibilityEvent(sentEvent);
            fail("No accessibility events are sent if accessibility is disabled");
        } catch (IllegalStateException ise) {
            // check expected result
            assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage());
        }

        // verify the mock service was properly called
        verify(mockServiceInterface);
    }

    /**
     * Determines if an {@link AccessibilityEvent} passed as a method argument
     * matches expectations.
     *
     * @param matched The event to check.
     * @return True if expectations are matched.
     */
    private static AccessibilityEvent eqAccessibilityEvent(AccessibilityEvent matched) {
        reportMatcher(new AccessibilityEventMather(matched));
        return null;
    }

    /**
     * Determines if an {@link IAccessibilityManagerClient} passed as a method argument
     * matches expectations which in this case are that any instance is accepted.
     *
     * @return <code>null</code>.
     */
    private static IAccessibilityManagerClient anyIAccessibilityManagerClient() {
        reportMatcher(new AnyIAccessibilityManagerClientMather());
        return null;
    }

    /**
     * Matcher for {@link AccessibilityEvent}s.
     */
    private static class AccessibilityEventMather implements IArgumentMatcher {
        private AccessibilityEvent mExpectedEvent;

        public AccessibilityEventMather(AccessibilityEvent expectedEvent) {
            mExpectedEvent = expectedEvent;
        }

        public boolean matches(Object matched) {
            if (!(matched instanceof AccessibilityEvent)) {
                return false;
            }
            AccessibilityEvent receivedEvent = (AccessibilityEvent) matched;
            return mExpectedEvent.getEventType() == receivedEvent.getEventType();
        }

        public void appendTo(StringBuffer buffer) {
            buffer.append("sendAccessibilityEvent()");
            buffer.append(" with event type \"");
            buffer.append(mExpectedEvent.getEventType());
            buffer.append("\"");
        }
    }

    /**
     * Matcher for {@link IAccessibilityManagerClient}s.
     */
    private static class AnyIAccessibilityManagerClientMather implements IArgumentMatcher {
        public boolean matches(Object matched) {
            if (!(matched instanceof IAccessibilityManagerClient)) {
                return false;
            }
            return true;
        }

        public void appendTo(StringBuffer buffer) {
            buffer.append("addClient() with any IAccessibilityManagerClient");
        }
    }
}
Loading