1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import android.accessibilityservice.AccessibilityService; 20 import android.accessibilityservice.AccessibilityServiceInfo; 21 import android.content.Intent; 22 import android.os.Message; 23 import android.view.accessibility.AccessibilityEvent; 24 25 import java.util.Iterator; 26 import java.util.LinkedList; 27 import java.util.List; 28 import java.util.Queue; 29 30 import junit.framework.TestCase; 31 32 /** 33 * This is the base class for mock {@link AccessibilityService}s. 34 */ 35 public abstract class MockAccessibilityService extends AccessibilityService { 36 37 /** 38 * The event this service expects to receive. 39 */ 40 private final Queue<AccessibilityEvent> mExpectedEvents = new LinkedList<AccessibilityEvent>(); 41 42 /** 43 * Interruption call this service expects to receive. 44 */ 45 private boolean mExpectedInterrupt; 46 47 /** 48 * Flag if the mock is currently replaying. 49 */ 50 private boolean mReplaying; 51 52 /** 53 * Flag if the system is bound as a client to this service. 54 */ 55 private boolean mIsSystemBoundAsClient; 56 57 /** 58 * Creates an {@link AccessibilityServiceInfo} populated with default 59 * values. 60 * 61 * @return The default info. 62 */ 63 public static AccessibilityServiceInfo createDefaultInfo() { 64 AccessibilityServiceInfo defaultInfo = new AccessibilityServiceInfo(); 65 defaultInfo.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED; 66 defaultInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE; 67 defaultInfo.flags = 0; 68 defaultInfo.notificationTimeout = 0; 69 defaultInfo.packageNames = new String[] { 70 "foo.bar.baz" 71 }; 72 73 return defaultInfo; 74 } 75 76 /** 77 * Starts replaying the mock. 78 */ 79 public void replay() { 80 mReplaying = true; 81 } 82 83 /** 84 * Verifies if all expected service methods have been called. 85 */ 86 public void verify() { 87 if (!mReplaying) { 88 throw new IllegalStateException("Did you forget to call replay()"); 89 } 90 91 if (mExpectedInterrupt) { 92 throw new IllegalStateException("Expected call to #interrupt() not received"); 93 } 94 if (!mExpectedEvents.isEmpty()) { 95 throw new IllegalStateException("Expected a call to onAccessibilityEvent() for " 96 + "events \"" + mExpectedEvents + "\" not received"); 97 } 98 } 99 100 /** 101 * Resets this instance so it can be reused. 102 */ 103 public void reset() { 104 mExpectedEvents.clear(); 105 mExpectedInterrupt = false; 106 mReplaying = false; 107 } 108 109 /** 110 * Sets an expected call to 111 * {@link #onAccessibilityEvent(AccessibilityEvent)} with given event as 112 * argument. 113 * 114 * @param expectedEvent The expected event argument. 115 */ 116 public void expectEvent(AccessibilityEvent expectedEvent) { 117 mExpectedEvents.add(expectedEvent); 118 } 119 120 /** 121 * Sets an expected call of {@link #onInterrupt()}. 122 */ 123 public void expectInterrupt() { 124 mExpectedInterrupt = true; 125 } 126 127 @Override 128 public void onAccessibilityEvent(AccessibilityEvent receivedEvent) { 129 if (!mReplaying) { 130 return; 131 } 132 133 if (mExpectedEvents.isEmpty()) { 134 throw new IllegalStateException("Unexpected event: " + receivedEvent); 135 } 136 137 AccessibilityEvent expectedEvent = mExpectedEvents.poll(); 138 assertEqualsAccessiblityEvent(expectedEvent, receivedEvent); 139 } 140 141 @Override 142 public void onInterrupt() { 143 if (!mReplaying) { 144 return; 145 } 146 147 if (!mExpectedInterrupt) { 148 throw new IllegalStateException("Unexpected call to onInterrupt()"); 149 } 150 151 mExpectedInterrupt = false; 152 } 153 154 @Override 155 protected void onServiceConnected() { 156 mIsSystemBoundAsClient = true; 157 } 158 159 @Override 160 public boolean onUnbind(Intent intent) { 161 mIsSystemBoundAsClient = false; 162 return false; 163 } 164 165 /** 166 * Returns if the system is bound as client to this service. 167 * 168 * @return True if the system is bound, false otherwise. 169 */ 170 public boolean isSystemBoundAsClient() { 171 return mIsSystemBoundAsClient; 172 } 173 174 /** 175 * Compares all properties of the <code>expectedEvent</code> and the 176 * <code>receviedEvent</code> to verify that the received event is the one 177 * that is expected. 178 */ 179 private void assertEqualsAccessiblityEvent(AccessibilityEvent expectedEvent, 180 AccessibilityEvent receivedEvent) { 181 TestCase.assertEquals("addedCount has incorrect value", expectedEvent.getAddedCount(), 182 receivedEvent.getAddedCount()); 183 TestCase.assertEquals("beforeText has incorrect value", expectedEvent.getBeforeText(), 184 receivedEvent.getBeforeText()); 185 TestCase.assertEquals("checked has incorrect value", expectedEvent.isChecked(), 186 receivedEvent.isChecked()); 187 TestCase.assertEquals("className has incorrect value", expectedEvent.getClassName(), 188 receivedEvent.getClassName()); 189 TestCase.assertEquals("contentDescription has incorrect value", expectedEvent 190 .getContentDescription(), receivedEvent.getContentDescription()); 191 TestCase.assertEquals("currentItemIndex has incorrect value", expectedEvent 192 .getCurrentItemIndex(), receivedEvent.getCurrentItemIndex()); 193 TestCase.assertEquals("enabled has incorrect value", expectedEvent.isEnabled(), 194 receivedEvent.isEnabled()); 195 TestCase.assertEquals("eventType has incorrect value", expectedEvent.getEventType(), 196 receivedEvent.getEventType()); 197 TestCase.assertEquals("fromIndex has incorrect value", expectedEvent.getFromIndex(), 198 receivedEvent.getFromIndex()); 199 TestCase.assertEquals("fullScreen has incorrect value", expectedEvent.isFullScreen(), 200 receivedEvent.isFullScreen()); 201 TestCase.assertEquals("itemCount has incorrect value", expectedEvent.getItemCount(), 202 receivedEvent.getItemCount()); 203 assertEqualsNotificationAsParcelableData(expectedEvent, receivedEvent); 204 TestCase.assertEquals("password has incorrect value", expectedEvent.isPassword(), 205 receivedEvent.isPassword()); 206 TestCase.assertEquals("removedCount has incorrect value", expectedEvent.getRemovedCount(), 207 receivedEvent.getRemovedCount()); 208 assertEqualsText(expectedEvent, receivedEvent); 209 } 210 211 /** 212 * Compares the {@link android.os.Parcelable} data of the 213 * <code>expectedEvent</code> and <code>receivedEvent</code> to verify that 214 * the received event is the one that is expected. 215 */ 216 private void assertEqualsNotificationAsParcelableData(AccessibilityEvent expectedEvent, 217 AccessibilityEvent receivedEvent) { 218 String message = "parcelableData has incorrect value"; 219 Message expectedMessage = (Message) expectedEvent.getParcelableData(); 220 Message receivedMessage = (Message) receivedEvent.getParcelableData(); 221 222 if (expectedMessage == null) { 223 if (receivedMessage == null) { 224 return; 225 } 226 } 227 228 TestCase.assertNotNull(message, receivedMessage); 229 230 // we do a very simple sanity check since we do not test Message 231 TestCase.assertEquals(message, expectedMessage.what, receivedMessage.what); 232 } 233 234 /** 235 * Compares the text of the <code>expectedEvent</code> and 236 * <code>receivedEvent</code> by comparing the string representation of the 237 * corresponding {@link CharSequence}s. 238 */ 239 private void assertEqualsText(AccessibilityEvent expectedEvent, 240 AccessibilityEvent receivedEvent) { 241 String message = "text has incorrect value"; 242 List<CharSequence> expectedText = expectedEvent.getText(); 243 List<CharSequence> receivedText = receivedEvent.getText(); 244 245 TestCase.assertEquals(message, expectedText.size(), receivedText.size()); 246 247 Iterator<CharSequence> expectedTextIterator = expectedText.iterator(); 248 Iterator<CharSequence> receivedTextIterator = receivedText.iterator(); 249 250 for (int i = 0; i < expectedText.size(); i++) { 251 // compare the string representation 252 TestCase.assertEquals(message, expectedTextIterator.next().toString(), 253 receivedTextIterator.next().toString()); 254 } 255 } 256 } 257