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      */
createDefaultInfo()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      */
replay()79     public void replay() {
80         mReplaying = true;
81     }
82 
83     /**
84      * Verifies if all expected service methods have been called.
85      */
verify()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      */
reset()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      */
expectEvent(AccessibilityEvent expectedEvent)116     public void expectEvent(AccessibilityEvent expectedEvent) {
117         mExpectedEvents.add(expectedEvent);
118     }
119 
120     /**
121      * Sets an expected call of {@link #onInterrupt()}.
122      */
expectInterrupt()123     public void expectInterrupt() {
124         mExpectedInterrupt = true;
125     }
126 
127     @Override
onAccessibilityEvent(AccessibilityEvent receivedEvent)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
onInterrupt()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
onServiceConnected()155     protected void onServiceConnected() {
156         mIsSystemBoundAsClient = true;
157     }
158 
159     @Override
onUnbind(Intent intent)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      */
isSystemBoundAsClient()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      */
assertEqualsAccessiblityEvent(AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent)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      */
assertEqualsNotificationAsParcelableData(AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent)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      */
assertEqualsText(AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent)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