/* * 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 mExpectedEvents = new LinkedList(); /** * 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_VIEW_CLICKED; 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 expectedEvent and the * receviedEvent 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 * expectedEvent and receivedEvent 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 expectedEvent and * receivedEvent 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 expectedText = expectedEvent.getText(); List receivedText = receivedEvent.getText(); TestCase.assertEquals(message, expectedText.size(), receivedText.size()); Iterator expectedTextIterator = expectedText.iterator(); Iterator receivedTextIterator = receivedText.iterator(); for (int i = 0; i < expectedText.size(); i++) { // compare the string representation TestCase.assertEquals(message, expectedTextIterator.next().toString(), receivedTextIterator.next().toString()); } } }