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