/*
* 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_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 <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());
}
}
}