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