• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.view.accessibility.cts;
18 
19 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
20 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_IN_DIRECTION;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertSame;
26 import static org.junit.Assert.assertThrows;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 
30 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
31 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
32 import android.app.Activity;
33 import android.app.Instrumentation;
34 import android.app.UiAutomation;
35 import android.content.Context;
36 import android.os.Message;
37 import android.os.Parcel;
38 import android.os.SystemClock;
39 import android.platform.test.annotations.FlakyTest;
40 import android.text.SpannableString;
41 import android.text.TextUtils;
42 import android.text.style.LocaleSpan;
43 import android.view.Display;
44 import android.view.View;
45 import android.view.accessibility.AccessibilityEvent;
46 import android.view.accessibility.AccessibilityNodeInfo;
47 import android.view.accessibility.AccessibilityRecord;
48 import android.widget.LinearLayout;
49 import android.widget.TextView;
50 
51 import androidx.test.InstrumentationRegistry;
52 import androidx.test.filters.SmallTest;
53 import androidx.test.rule.ActivityTestRule;
54 import androidx.test.runner.AndroidJUnit4;
55 
56 import org.junit.Before;
57 import org.junit.Rule;
58 import org.junit.Test;
59 import org.junit.rules.RuleChain;
60 import org.junit.runner.RunWith;
61 
62 import java.util.ArrayList;
63 import java.util.List;
64 import java.util.Locale;
65 import java.util.concurrent.TimeoutException;
66 
67 /** Class for testing {@link AccessibilityEvent}. */
68 // TODO(b/263942937) Re-enable @Presubmit
69 @RunWith(AndroidJUnit4.class)
70 public class AccessibilityEventTest {
71     private static final long IDLE_TIMEOUT_MS = 500;
72     private static final long DEFAULT_TIMEOUT_MS = 2000;
73 
74     // From ViewConfiguration.SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS
75     private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 100;
76 
77     private EventReportingLinearLayout mParentView;
78     private View mChildView;
79     private TextView mTextView;
80     private String mPackageName;
81 
82     private static Instrumentation sInstrumentation;
83     private static UiAutomation sUiAutomation;
84     private final ActivityTestRule<DummyActivity> mActivityRule =
85             new ActivityTestRule<>(DummyActivity.class, false, false);
86     private final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
87             new AccessibilityDumpOnFailureRule();
88     private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService>
89             mInstrumentedAccessibilityServiceRule =
90                     new InstrumentedAccessibilityServiceTestRule<>(
91                             SpeakingAccessibilityService.class, false);
92 
93     @Rule
94     public final RuleChain mRuleChain =
95             RuleChain.outerRule(mActivityRule)
96                     .around(mInstrumentedAccessibilityServiceRule)
97                     .around(mDumpOnFailureRule);
98 
99     @Before
setUp()100     public void setUp() throws Throwable {
101         sInstrumentation = InstrumentationRegistry.getInstrumentation();
102         sUiAutomation = sInstrumentation.getUiAutomation();
103         final Activity activity = launchActivityAndWaitForItToBeOnscreen(
104                 sInstrumentation, sUiAutomation, mActivityRule);
105         mPackageName = activity.getApplicationContext().getPackageName();
106         mInstrumentedAccessibilityServiceRule.enableService();
107         sUiAutomation.executeAndWaitForEvent(() -> {
108                     try {
109                         mActivityRule.runOnUiThread(() -> {
110                             final LinearLayout grandparent = new LinearLayout(activity);
111                             activity.setContentView(grandparent);
112                             mParentView = new EventReportingLinearLayout(activity);
113                             mChildView = new View(activity);
114                             mTextView = new TextView(activity);
115                             grandparent.addView(mParentView);
116                             mParentView.addView(mChildView);
117                             mParentView.addView(mTextView);
118                         });
119                     } catch (Throwable e) {
120                         fail(e.toString());
121                     }
122                 },
123                 // There can be a race where the test Activity gets focus and we start test.
124                 // Because we don't specify flagRetrieveInteractiveWindows in this test, until
125                 // the Activity gets focus, no events will be delivered from it.
126                 // So. this waits for any event from the test activity.
127                 accessibilityEvent -> mPackageName.equals(accessibilityEvent.getPackageName()),
128                 DEFAULT_TIMEOUT_MS);
129         sUiAutomation.waitForIdle(IDLE_TIMEOUT_MS, DEFAULT_TIMEOUT_MS);
130     }
131 
132     private static class EventReportingLinearLayout extends LinearLayout {
133         public List<AccessibilityEvent> mReceivedEvents = new ArrayList<AccessibilityEvent>();
134 
EventReportingLinearLayout(Context context)135         public EventReportingLinearLayout(Context context) {
136             super(context);
137         }
138 
139         @Override
requestSendAccessibilityEvent(View child, AccessibilityEvent event)140         public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
141             mReceivedEvents.add(AccessibilityEvent.obtain(event));
142             return super.requestSendAccessibilityEvent(child, event);
143         }
144     }
145 
146     @Test
testScrollEvent()147     public void testScrollEvent() throws Exception {
148         sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
149                 () -> mChildView.scrollTo(0, 100)), new ScrollEventFilter(1), DEFAULT_TIMEOUT_MS);
150     }
151 
152     @Test
testScrollEventBurstCombined()153     public void testScrollEventBurstCombined() throws Exception {
154         sUiAutomation.executeAndWaitForEvent(
155                 () -> sInstrumentation.runOnMainSync(
156                         () -> {
157                             mChildView.scrollTo(0, 100);
158                             mChildView.scrollTo(0, 125);
159                             mChildView.scrollTo(0, 150);
160                             mChildView.scrollTo(0, 175);
161                         }),
162                 new ScrollEventFilter(1),
163                 DEFAULT_TIMEOUT_MS);
164     }
165 
166     @Test
167     @FlakyTest
testScrollEventsDeliveredInCorrectInterval()168     public void testScrollEventsDeliveredInCorrectInterval() throws Exception {
169         sUiAutomation.executeAndWaitForEvent(
170                 () -> {
171                     sInstrumentation.runOnMainSync(() -> {
172                         mChildView.scrollTo(0, 25);
173                         mChildView.scrollTo(0, 50);
174                         mChildView.scrollTo(0, 100);
175                     });
176                     SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2);
177                     sInstrumentation.runOnMainSync(() -> {
178                         mChildView.scrollTo(0, 150);
179                         mChildView.scrollTo(0, 175);
180                     });
181                     SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS / 2);
182                     sInstrumentation.runOnMainSync(() -> {
183                         mChildView.scrollTo(0, 200);
184                     });
185                 },
186                 new ScrollEventFilter(2),
187                 DEFAULT_TIMEOUT_MS);
188     }
189 
190     class ScrollEventFilter extends AccessibilityEventFilter {
191         private int mCount = 0;
192         private int mTargetCount;
193 
ScrollEventFilter(int count)194         ScrollEventFilter(int count) {
195             mTargetCount = count;
196         }
197 
accept(AccessibilityEvent event)198         public boolean accept(AccessibilityEvent event) {
199             if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
200                 mCount += 1;
201                 mEvents.add(event);
202                 return mCount >= mTargetCount;
203             }
204             return false;
205         }
206     }
207 
208     @Test
testScrollEventsClearedOnDetach()209     public void testScrollEventsClearedOnDetach() throws Throwable {
210         ScrollEventFilter scrollEventFilter = new ScrollEventFilter(1);
211         sUiAutomation.executeAndWaitForEvent(
212                 () -> sInstrumentation.runOnMainSync(
213                         () -> {
214                             mChildView.scrollTo(0, 25);
215                             mChildView.scrollTo(5, 50);
216                             mChildView.scrollTo(7, 100);
217                         }),
218                 scrollEventFilter,
219                 DEFAULT_TIMEOUT_MS);
220         mActivityRule.runOnUiThread(
221                 () -> {
222                     mParentView.removeView(mChildView);
223                     mParentView.addView(mChildView);
224                 });
225         sUiAutomation.executeAndWaitForEvent(
226                 () -> sInstrumentation.runOnMainSync(
227                         () -> {
228                             mChildView.scrollTo(0, 150);
229                         }),
230                 scrollEventFilter,
231                 DEFAULT_TIMEOUT_MS);
232         AccessibilityEvent event = scrollEventFilter.getLastEvent();
233         assertEquals(-7, event.getScrollDeltaX());
234         assertEquals(50, event.getScrollDeltaY());
235     }
236 
237     @Test
testScrollEventsCaptureTotalDelta()238     public void testScrollEventsCaptureTotalDelta() throws Throwable {
239         ScrollEventFilter scrollEventFilter = new ScrollEventFilter(1);
240         sUiAutomation.executeAndWaitForEvent(
241                 () -> sInstrumentation.runOnMainSync(
242                         () -> {
243                             mChildView.scrollTo(0, 25);
244                             mChildView.scrollTo(5, 50);
245                             mChildView.scrollTo(7, 100);
246                         }),
247                 scrollEventFilter,
248                 DEFAULT_TIMEOUT_MS);
249         AccessibilityEvent event = scrollEventFilter.getLastEvent();
250         assertEquals(7, event.getScrollDeltaX());
251         assertEquals(100, event.getScrollDeltaY());
252     }
253 
254     @Test
testScrollEventsClearDeltaAfterSending()255     public void testScrollEventsClearDeltaAfterSending() throws Throwable {
256         ScrollEventFilter scrollEventFilter = new ScrollEventFilter(2);
257         sUiAutomation.executeAndWaitForEvent(
258                 () -> {
259                     sInstrumentation.runOnMainSync(() -> {
260                         mChildView.scrollTo(0, 25);
261                         mChildView.scrollTo(5, 50);
262                         mChildView.scrollTo(7, 100);
263                     });
264                     SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2);
265                     sInstrumentation.runOnMainSync(() -> {
266                         mChildView.scrollTo(0, 25);
267                         mChildView.scrollTo(5, 50);
268                         mChildView.scrollTo(7, 100);
269                         mChildView.scrollTo(0, 150);
270                     });
271                 },
272                 scrollEventFilter,
273                 DEFAULT_TIMEOUT_MS);
274         AccessibilityEvent event = scrollEventFilter.getLastEvent();
275         assertEquals(-7, event.getScrollDeltaX());
276         assertEquals(50, event.getScrollDeltaY());
277     }
278 
279     @Test
testEventViewTargetedByScroll()280     public void testEventViewTargetedByScroll() throws Throwable {
281         final AccessibilityEvent awaitedEvent = sUiAutomation.executeAndWaitForEvent(
282                 () -> {
283                     AccessibilityEvent event = new AccessibilityEvent(
284                             AccessibilityEvent.TYPE_VIEW_TARGETED_BY_SCROLL);
285                     event.setAction(ACTION_SCROLL_IN_DIRECTION.getId());
286                     mChildView.sendAccessibilityEventUnchecked(event);
287                 },
288                 event -> event.getEventType() == AccessibilityEvent.TYPE_VIEW_TARGETED_BY_SCROLL,
289                 DEFAULT_TIMEOUT_MS);
290         assertThat(awaitedEvent.getAction()).isEqualTo(ACTION_SCROLL_IN_DIRECTION.getId());
291         assertThat(awaitedEvent.getSource()).isEqualTo(mChildView.createAccessibilityNodeInfo());
292     }
293 
294     @Test
testStateEvent()295     public void testStateEvent() throws Throwable {
296         sUiAutomation.executeAndWaitForEvent(
297                 () -> {
298                     sendStateDescriptionChangedEvent(mChildView);
299                 },
300                 new StateDescriptionEventFilter(1),
301                 DEFAULT_TIMEOUT_MS);
302     }
303 
304     @Test
testStateEventBurstCombined()305     public void testStateEventBurstCombined() throws Throwable {
306         sUiAutomation.executeAndWaitForEvent(
307                 () -> {
308                     sendStateDescriptionChangedEvent(mChildView);
309                     sendStateDescriptionChangedEvent(mChildView);
310                     sendStateDescriptionChangedEvent(mChildView);
311                     sendStateDescriptionChangedEvent(mChildView);
312                 },
313                 new StateDescriptionEventFilter(1),
314                 DEFAULT_TIMEOUT_MS);
315     }
316 
317     @Test
testStateEventsDeliveredInCorrectInterval()318     public void testStateEventsDeliveredInCorrectInterval() throws Throwable {
319         sUiAutomation.executeAndWaitForEvent(
320                 () -> {
321                     sendStateDescriptionChangedEvent(mChildView);
322                     sendStateDescriptionChangedEvent(mChildView);
323                     sendStateDescriptionChangedEvent(mChildView);
324                     SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS * 2);
325                     sendStateDescriptionChangedEvent(mChildView);
326                     sendStateDescriptionChangedEvent(mChildView);
327                     SystemClock.sleep(SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS / 2);
328                     sendStateDescriptionChangedEvent(mChildView);
329                 },
330                 new StateDescriptionEventFilter(2),
331                 DEFAULT_TIMEOUT_MS);
332     }
333 
334     @Test
testStateEventsHaveLastEventText()335     public void testStateEventsHaveLastEventText() throws Throwable {
336         StateDescriptionEventFilter stateDescriptionEventFilter =
337                 new StateDescriptionEventFilter(1);
338         String expectedState = "Second state";
339         sUiAutomation.executeAndWaitForEvent(
340                 () -> {
341                     sendStateDescriptionChangedEvent(mChildView, "First state");
342                     sendStateDescriptionChangedEvent(mChildView, expectedState);
343                 },
344                 stateDescriptionEventFilter,
345                 DEFAULT_TIMEOUT_MS);
346         AccessibilityEvent event = stateDescriptionEventFilter.getLastEvent();
347         assertEquals(expectedState, event.getText().get(0));
348     }
349 
350     class StateDescriptionEventFilter extends AccessibilityEventFilter {
351         private int mCount;
352         private int mTargetCount;
353 
StateDescriptionEventFilter(int count)354         StateDescriptionEventFilter(int count) {
355             mTargetCount = count;
356         }
357 
accept(AccessibilityEvent event)358         public boolean accept(AccessibilityEvent event) {
359             if (event.getContentChangeTypes()
360                     == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION) {
361                 mCount += 1;
362                 mEvents.add(event);
363                 return mCount >= mTargetCount;
364             }
365             return false;
366         }
367     }
368     ;
369 
370     private abstract class AccessibilityEventFilter
371             implements UiAutomation.AccessibilityEventFilter {
372         protected List<AccessibilityEvent> mEvents = new ArrayList<>();
373 
accept(AccessibilityEvent event)374         public abstract boolean accept(AccessibilityEvent event);
375 
assertReceivedEventCount(int count)376         void assertReceivedEventCount(int count) {
377             assertEquals(count, mEvents.size());
378         }
379 
getLastEvent()380         AccessibilityEvent getLastEvent() {
381             if (mEvents.size() > 0) {
382                 return mEvents.get(mEvents.size() - 1);
383             }
384             return null;
385         }
386     }
387 
sendStateDescriptionChangedEvent(View view)388     private void sendStateDescriptionChangedEvent(View view) {
389         sendStateDescriptionChangedEvent(view, null);
390     }
391 
sendStateDescriptionChangedEvent(View view, CharSequence text)392     private void sendStateDescriptionChangedEvent(View view, CharSequence text) {
393         AccessibilityEvent event =
394                 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
395         event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
396         event.getText().add(text);
397         view.sendAccessibilityEventUnchecked(event);
398     }
399 
400     @Test
401     @FlakyTest
setTextError_receiveEvent()402     public void setTextError_receiveEvent() throws Throwable {
403         sUiAutomation.executeAndWaitForEvent(
404                 () -> sInstrumentation.runOnMainSync(() -> mTextView.setError("error")),
405                 event -> isExpectedChangeType(event,
406                         AccessibilityEvent.CONTENT_CHANGE_TYPE_ERROR
407                                 | AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_INVALID)
408                         && event.getSource().getError() != null,
409                 DEFAULT_TIMEOUT_MS);
410     }
411 
412     @Test
setViewEnable_receiveEvent()413     public void setViewEnable_receiveEvent() throws Throwable {
414         sUiAutomation.executeAndWaitForEvent(
415                 () -> sInstrumentation.runOnMainSync(() -> {
416                     mChildView.setEnabled(!mChildView.isEnabled());
417                 }),
418                 event -> isExpectedChangeType(event,
419                         AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED),
420                 DEFAULT_TIMEOUT_MS);
421     }
422 
423     @Test
setText_unChanged_doNotReceiveEvent()424     public void setText_unChanged_doNotReceiveEvent() throws Throwable {
425         sUiAutomation.executeAndWaitForEvent(
426                 () -> sInstrumentation.runOnMainSync(() -> mTextView.setText("a")),
427                 event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT),
428                 DEFAULT_TIMEOUT_MS);
429 
430         assertThrows(
431                 TimeoutException.class,
432                 () ->
433                         sUiAutomation.executeAndWaitForEvent(
434                                 () -> {
435                                     sInstrumentation.runOnMainSync(
436                                             () -> {
437                                                 mTextView.setText("a");
438                                             });
439                                 },
440                                 event -> isExpectedSource(event, mTextView),
441                                 DEFAULT_TIMEOUT_MS));
442     }
443 
444     @Test
setText_textChanged_receivesTextEvent()445     public void setText_textChanged_receivesTextEvent() throws Throwable {
446         sUiAutomation.executeAndWaitForEvent(
447                 () -> sInstrumentation.runOnMainSync(() -> mTextView.setText("a")),
448                 event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT),
449                 DEFAULT_TIMEOUT_MS);
450 
451         sUiAutomation.executeAndWaitForEvent(
452                 () -> {
453                     sInstrumentation.runOnMainSync(
454                             () -> {
455                                 mTextView.setText("b");
456                             });
457                 },
458                 event ->
459                         isExpectedSource(event, mTextView)
460                                 && isExpectedChangeType(
461                                         event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT),
462                 DEFAULT_TIMEOUT_MS);
463     }
464 
465     @Test
setText_parcelableSpanChanged_receivesUndefinedEvent()466     public void setText_parcelableSpanChanged_receivesUndefinedEvent() throws Throwable {
467         String text = "a";
468         sUiAutomation.executeAndWaitForEvent(
469                 () -> sInstrumentation.runOnMainSync(() -> mTextView.setText(text)),
470                 event -> isExpectedChangeType(event, AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT),
471                 DEFAULT_TIMEOUT_MS);
472 
473         sUiAutomation.executeAndWaitForEvent(
474                 () -> {
475                     sInstrumentation.runOnMainSync(
476                             () -> {
477                                 SpannableString spannableString = new SpannableString(text);
478                                 spannableString.setSpan(new LocaleSpan(Locale.ENGLISH), 0, 1, 0);
479                                 mTextView.setText(spannableString);
480                             });
481                 },
482                 event ->
483                         isExpectedSource(event, mTextView)
484                                 && isExpectedChangeType(
485                                         event, AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED),
486                 DEFAULT_TIMEOUT_MS);
487     }
488 
isExpectedSource(AccessibilityEvent event, View view)489     private static boolean isExpectedSource(AccessibilityEvent event, View view) {
490         return TextUtils.equals(view.getContext().getPackageName(), event.getPackageName())
491                 && TextUtils.equals(view.getAccessibilityClassName(), event.getClassName());
492     }
493 
isExpectedChangeType(AccessibilityEvent event, int changeType)494     private static boolean isExpectedChangeType(AccessibilityEvent event, int changeType) {
495         return (event.getContentChangeTypes() & changeType) == changeType;
496     }
497 
498     /**
499      * Tests whether accessibility events are correctly written and read from a parcel (version 1).
500      */
501     @SmallTest
502     @Test
testMarshaling()503     public void testMarshaling() throws Exception {
504         // fully populate the event to marshal
505         AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
506         fullyPopulateAccessibilityEvent(sentEvent);
507 
508         // marshal and unmarshal the event
509         Parcel parcel = Parcel.obtain();
510         sentEvent.writeToParcel(parcel, 0);
511         parcel.setDataPosition(0);
512         AccessibilityEvent receivedEvent = AccessibilityEvent.CREATOR.createFromParcel(parcel);
513 
514         // make sure all fields properly marshaled
515         assertEqualsAccessibilityEvent(sentEvent, receivedEvent);
516 
517         parcel.recycle();
518     }
519 
520     /** Tests if {@link AccessibilityEvent} can be acquired through obtain(). */
521     @SmallTest
522     @Test
523     @FlakyTest
testRecycle()524     public void testRecycle() {
525         // evaluate that recycle() can be called on an event acquired by obtain()
526         AccessibilityEvent.obtain().recycle();
527     }
528 
529     /** Tests whether the event types are correctly converted to strings. */
530     @SmallTest
531     @Test
testEventTypeToString()532     public void testEventTypeToString() {
533         assertEquals(
534                 "TYPE_NOTIFICATION_STATE_CHANGED",
535                 AccessibilityEvent.eventTypeToString(
536                         AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED));
537         assertEquals(
538                 "TYPE_TOUCH_EXPLORATION_GESTURE_END",
539                 AccessibilityEvent.eventTypeToString(
540                         AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END));
541         assertEquals(
542                 "TYPE_TOUCH_EXPLORATION_GESTURE_START",
543                 AccessibilityEvent.eventTypeToString(
544                         AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START));
545         assertEquals(
546                 "TYPE_VIEW_CLICKED",
547                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_CLICKED));
548         assertEquals(
549                 "TYPE_VIEW_FOCUSED",
550                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_FOCUSED));
551         assertEquals(
552                 "TYPE_VIEW_HOVER_ENTER",
553                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER));
554         assertEquals(
555                 "TYPE_VIEW_HOVER_EXIT",
556                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT));
557         assertEquals(
558                 "TYPE_VIEW_LONG_CLICKED",
559                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED));
560         assertEquals(
561                 "TYPE_VIEW_CONTEXT_CLICKED",
562                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_CONTEXT_CLICKED));
563         assertEquals(
564                 "TYPE_VIEW_SCROLLED",
565                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_SCROLLED));
566         assertEquals(
567                 "TYPE_VIEW_SELECTED",
568                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_SELECTED));
569         assertEquals(
570                 "TYPE_VIEW_TEXT_CHANGED",
571                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED));
572         assertEquals(
573                 "TYPE_VIEW_TEXT_SELECTION_CHANGED",
574                 AccessibilityEvent.eventTypeToString(
575                         AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED));
576         assertEquals(
577                 "TYPE_WINDOW_CONTENT_CHANGED",
578                 AccessibilityEvent.eventTypeToString(
579                         AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED));
580         assertEquals(
581                 "TYPE_WINDOW_STATE_CHANGED",
582                 AccessibilityEvent.eventTypeToString(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
583     }
584 
585     /** Tests whether the event describes its contents consistently. */
586     @SmallTest
587     @Test
testDescribeContents()588     public void testDescribeContents() {
589         AccessibilityEvent event = AccessibilityEvent.obtain();
590         assertSame(
591                 "Accessibility events always return 0 for this method.",
592                 0,
593                 event.describeContents());
594         fullyPopulateAccessibilityEvent(event);
595         assertSame(
596                 "Accessibility events always return 0 for this method.",
597                 0,
598                 event.describeContents());
599     }
600 
601     /**
602      * Tests whether accessibility events are correctly written and read from a parcel (version 2).
603      */
604     @SmallTest
605     @Test
testMarshaling2()606     public void testMarshaling2() {
607         // fully populate the event to marshal
608         AccessibilityEvent marshaledEvent = AccessibilityEvent.obtain();
609         fullyPopulateAccessibilityEvent(marshaledEvent);
610 
611         // marshal and unmarshal the event
612         Parcel parcel = Parcel.obtain();
613         marshaledEvent.writeToParcel(parcel, 0);
614         parcel.setDataPosition(0);
615         AccessibilityEvent unmarshaledEvent = AccessibilityEvent.obtain();
616         unmarshaledEvent.initFromParcel(parcel);
617 
618         // make sure all fields properly marshaled
619         assertEqualsAccessibilityEvent(marshaledEvent, unmarshaledEvent);
620 
621         parcel.recycle();
622     }
623 
624     /**
625      * While CharSequence is immutable, some classes implementing it are mutable. Make sure they
626      * can't change the object by changing the objects backing CharSequence
627      */
628     @SmallTest
629     @Test
testChangeTextAfterSetting_shouldNotAffectEvent()630     public void testChangeTextAfterSetting_shouldNotAffectEvent() {
631         final String originalText = "Cassowary";
632         final String newText = "Hornbill";
633         AccessibilityEvent event = AccessibilityEvent.obtain();
634         StringBuffer updatingString = new StringBuffer(originalText);
635         event.setBeforeText(updatingString);
636         event.setContentDescription(updatingString);
637 
638         updatingString.delete(0, updatingString.length());
639         updatingString.append(newText);
640 
641         assertTrue(TextUtils.equals(originalText, event.getBeforeText()));
642         assertTrue(TextUtils.equals(originalText, event.getContentDescription()));
643     }
644 
645     @SmallTest
646     @Test
testConstructors()647     public void testConstructors() {
648         final AccessibilityEvent populatedEvent = new AccessibilityEvent();
649         fullyPopulateAccessibilityEvent(populatedEvent);
650         final AccessibilityEvent event = new AccessibilityEvent(populatedEvent);
651 
652         assertEqualsAccessibilityEvent(event, populatedEvent);
653 
654         final AccessibilityEvent firstEvent = new AccessibilityEvent();
655         firstEvent.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
656         final AccessibilityEvent secondEvent =
657                 new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
658 
659         assertEqualsAccessibilityEvent(firstEvent, secondEvent);
660     }
661 
662     /**
663      * Fully populates the {@link AccessibilityEvent} to marshal.
664      *
665      * @param sentEvent The event to populate.
666      */
fullyPopulateAccessibilityEvent(AccessibilityEvent sentEvent)667     private void fullyPopulateAccessibilityEvent(AccessibilityEvent sentEvent) {
668         sentEvent.setAddedCount(1);
669         sentEvent.setBeforeText("BeforeText");
670         sentEvent.setChecked(true);
671         sentEvent.setClassName("foo.bar.baz.Class");
672         sentEvent.setContentDescription("ContentDescription");
673         sentEvent.setCurrentItemIndex(1);
674         sentEvent.setEnabled(true);
675         sentEvent.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
676         sentEvent.setEventTime(1000);
677         sentEvent.setFromIndex(1);
678         sentEvent.setFullScreen(true);
679         sentEvent.setItemCount(1);
680         sentEvent.setPackageName("foo.bar.baz");
681         sentEvent.setParcelableData(Message.obtain(null, 1, 2, 3));
682         sentEvent.setPassword(true);
683         sentEvent.setRemovedCount(1);
684         sentEvent.getText().add("Foo");
685         sentEvent.setMaxScrollX(1);
686         sentEvent.setMaxScrollY(1);
687         sentEvent.setScrollX(1);
688         sentEvent.setScrollY(1);
689         sentEvent.setScrollDeltaX(3);
690         sentEvent.setScrollDeltaY(3);
691         sentEvent.setToIndex(1);
692         sentEvent.setScrollable(true);
693         sentEvent.setAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
694         sentEvent.setMovementGranularity(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
695         sentEvent.setDisplayId(Display.DEFAULT_DISPLAY);
696         sentEvent.setSpeechStateChangeTypes(AccessibilityEvent.SPEECH_STATE_SPEAKING_START);
697 
698         AccessibilityRecord record = AccessibilityRecord.obtain();
699         AccessibilityRecordTest.fullyPopulateAccessibilityRecord(record);
700         sentEvent.appendRecord(record);
701     }
702 
703     /**
704      * Compares all properties of the <code>expectedEvent</code> and the <code>receivedEvent</code>
705      * to verify that the received event is the one that is expected.
706      */
assertEqualsAccessibilityEvent( AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent)707     private static void assertEqualsAccessibilityEvent(
708             AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent) {
709         assertEquals(
710                 "addedCount has incorrect value",
711                 expectedEvent.getAddedCount(),
712                 receivedEvent.getAddedCount());
713         assertEquals(
714                 "beforeText has incorrect value",
715                 expectedEvent.getBeforeText(),
716                 receivedEvent.getBeforeText());
717         assertEquals(
718                 "checked has incorrect value",
719                 expectedEvent.isChecked(),
720                 receivedEvent.isChecked());
721         assertEquals(
722                 "className has incorrect value",
723                 expectedEvent.getClassName(),
724                 receivedEvent.getClassName());
725         assertEquals(
726                 "contentDescription has incorrect value",
727                 expectedEvent.getContentDescription(),
728                 receivedEvent.getContentDescription());
729         assertEquals(
730                 "currentItemIndex has incorrect value",
731                 expectedEvent.getCurrentItemIndex(),
732                 receivedEvent.getCurrentItemIndex());
733         assertEquals(
734                 "enabled has incorrect value",
735                 expectedEvent.isEnabled(),
736                 receivedEvent.isEnabled());
737         assertEquals(
738                 "eventType has incorrect value",
739                 expectedEvent.getEventType(),
740                 receivedEvent.getEventType());
741         assertEquals(
742                 "fromIndex has incorrect value",
743                 expectedEvent.getFromIndex(),
744                 receivedEvent.getFromIndex());
745         assertEquals(
746                 "fullScreen has incorrect value",
747                 expectedEvent.isFullScreen(),
748                 receivedEvent.isFullScreen());
749         assertEquals(
750                 "itemCount has incorrect value",
751                 expectedEvent.getItemCount(),
752                 receivedEvent.getItemCount());
753         assertEquals(
754                 "password has incorrect value",
755                 expectedEvent.isPassword(),
756                 receivedEvent.isPassword());
757         assertEquals(
758                 "removedCount has incorrect value",
759                 expectedEvent.getRemovedCount(),
760                 receivedEvent.getRemovedCount());
761         assertSame(
762                 "maxScrollX has incorrect value",
763                 expectedEvent.getMaxScrollX(),
764                 receivedEvent.getMaxScrollX());
765         assertSame(
766                 "maxScrollY has incorrect value",
767                 expectedEvent.getMaxScrollY(),
768                 receivedEvent.getMaxScrollY());
769         assertSame(
770                 "scrollX has incorrect value",
771                 expectedEvent.getScrollX(),
772                 receivedEvent.getScrollX());
773         assertSame(
774                 "scrollY has incorrect value",
775                 expectedEvent.getScrollY(),
776                 receivedEvent.getScrollY());
777         assertSame(
778                 "scrollDeltaX has incorrect value",
779                 expectedEvent.getScrollDeltaX(),
780                 receivedEvent.getScrollDeltaX());
781         assertSame(
782                 "scrollDeltaY has incorrect value",
783                 expectedEvent.getScrollDeltaY(),
784                 receivedEvent.getScrollDeltaY());
785         assertSame(
786                 "toIndex has incorrect value",
787                 expectedEvent.getToIndex(),
788                 receivedEvent.getToIndex());
789         assertSame(
790                 "scrollable has incorrect value",
791                 expectedEvent.isScrollable(),
792                 receivedEvent.isScrollable());
793         assertSame(
794                 "granularity has incorrect value",
795                 expectedEvent.getMovementGranularity(),
796                 receivedEvent.getMovementGranularity());
797         assertSame(
798                 "action has incorrect value", expectedEvent.getAction(), receivedEvent.getAction());
799         assertSame(
800                 "windowChangeTypes has incorrect value",
801                 expectedEvent.getWindowChanges(),
802                 receivedEvent.getWindowChanges());
803         assertEquals(
804                 "speechStateChangeTypes has incorrect value,",
805                 expectedEvent.getSpeechStateChangeTypes(),
806                 receivedEvent.getSpeechStateChangeTypes());
807 
808         AccessibilityRecordTest.assertEqualsText(expectedEvent.getText(), receivedEvent.getText());
809         AccessibilityRecordTest.assertEqualAccessibilityRecord(expectedEvent, receivedEvent);
810 
811         assertEqualAppendedRecord(expectedEvent, receivedEvent);
812     }
813 
assertEqualAppendedRecord( AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent)814     private static void assertEqualAppendedRecord(
815             AccessibilityEvent expectedEvent, AccessibilityEvent receivedEvent) {
816         assertEquals(
817                 "recordCount has incorrect value",
818                 expectedEvent.getRecordCount(),
819                 receivedEvent.getRecordCount());
820         if (expectedEvent.getRecordCount() != 0 && receivedEvent.getRecordCount() != 0) {
821             AccessibilityRecord expectedRecord = expectedEvent.getRecord(0);
822             AccessibilityRecord receivedRecord = receivedEvent.getRecord(0);
823             AccessibilityRecordTest.assertEqualAccessibilityRecord(expectedRecord, receivedRecord);
824         }
825     }
826 }
827