Commit b911edcf authored by Mark Schillaci's avatar Mark Schillaci Committed by Commit Bot

Added unit tests for AccessibilityEventDispatcher on Android

This CL expands on the work to include an AccessibilityEventDispatcher
on Android, relevant CL:
https://chromium-review.googlesource.com/c/chromium/src/+/2139905

Here we include a simple set of tests to check our queueing logic
and throttling of specific event types. We test for both events that
are and are not throttled, with cases of the first event, an event
after some time with no events, and events in quick succession.

We also slightly modified the AccessibilityEventDispatcher with this
CL to fix a dependency injection issue to make the testing easier.

Change-Id: I13aed5541eba0feaf1b250bf15cae9809efe54b7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2151851Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Reviewed-by: default avatarMark Schillaci <mschillaci@google.com>
Commit-Queue: Mark Schillaci <mschillaci@google.com>
Cr-Commit-Position: refs/heads/master@{#759831}
parent ba2af339
......@@ -509,6 +509,7 @@ android_library("content_javatests") {
"javatests/src/org/chromium/content/browser/ViewportTest.java",
"javatests/src/org/chromium/content/browser/WakeLockTest.java",
"javatests/src/org/chromium/content/browser/WebContentsObserverAndroidTest.java",
"javatests/src/org/chromium/content/browser/accessibility/AccessibilityEventDispatcherTest.java",
"javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTest.java",
"javatests/src/org/chromium/content/browser/accessibility/captioning/CaptioningChangeDelegateTest.java",
"javatests/src/org/chromium/content/browser/androidoverlay/DialogOverlayImplPixelTest.java",
......
......@@ -4,8 +4,6 @@
package org.chromium.content.browser.accessibility;
import android.view.accessibility.AccessibilityEvent;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
......@@ -16,12 +14,9 @@ import java.util.Map;
* not overload the system and create lag by sending superfluous events.
*/
public class AccessibilityEventDispatcher {
// Default delay for throttling of successive AccessibilityEvents in milliseconds.
private static final int ACCESSIBILITY_EVENT_DEFAULT_DELAY = 100;
// Maps an AccessibilityEvent type to a throttle delay in milliseconds. This is populated once
// in the constructor.
private Map<Integer, Integer> mEventThrottleDelays = new HashMap<Integer, Integer>();
private Map<Integer, Integer> mEventThrottleDelays;
// For events being throttled (see: |mEventsToThrottle|), this array will map the eventType
// to the last time (long in milliseconds) such an event has been sent.
......@@ -71,14 +66,9 @@ public class AccessibilityEventDispatcher {
/**
* Create an AccessibilityEventDispatcher and define the delays for event types.
*/
public AccessibilityEventDispatcher(Client mClient) {
public AccessibilityEventDispatcher(Client mClient, Map<Integer, Integer> eventThrottleDelays) {
this.mClient = mClient;
// Define our delays on a per event type basis.
mEventThrottleDelays.put(
AccessibilityEvent.TYPE_VIEW_SCROLLED, ACCESSIBILITY_EVENT_DEFAULT_DELAY);
mEventThrottleDelays.put(
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, ACCESSIBILITY_EVENT_DEFAULT_DELAY);
this.mEventThrottleDelays = eventThrottleDelays;
}
/**
......
......@@ -50,8 +50,10 @@ import org.chromium.content_public.browser.WebContentsAccessibility;
import org.chromium.ui.base.WindowAndroid;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Implementation of {@link WebContentsAccessibility} interface.
......@@ -162,6 +164,9 @@ public class WebContentsAccessibilityImpl extends AccessibilityNodeProvider
// this to update a node quickly rather than building from one scratch each time.
private SparseArray<AccessibilityNodeInfo> mNodeInfoCache = new SparseArray<>();
// Default delay for throttling of successive AccessibilityEvents in milliseconds.
private static final int ACCESSIBILITY_EVENT_DEFAULT_DELAY = 100;
// This handles the dispatching of accessibility events. It acts as an intermediary where we can
// apply throttling rules, delay event construction, etc.
private AccessibilityEventDispatcher mEventDispatcher;
......@@ -206,6 +211,13 @@ public class WebContentsAccessibilityImpl extends AccessibilityNodeProvider
mCaptioningController = new CaptioningController(mWebContents);
WindowEventObserverManager.from(mWebContents).addObserver(this);
// Define our delays on a per event type basis.
Map<Integer, Integer> eventThrottleDelays = new HashMap<Integer, Integer>();
eventThrottleDelays.put(
AccessibilityEvent.TYPE_VIEW_SCROLLED, ACCESSIBILITY_EVENT_DEFAULT_DELAY);
eventThrottleDelays.put(
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, ACCESSIBILITY_EVENT_DEFAULT_DELAY);
mEventDispatcher =
new AccessibilityEventDispatcher(new AccessibilityEventDispatcher.Client() {
@Override
......@@ -227,7 +239,7 @@ public class WebContentsAccessibilityImpl extends AccessibilityNodeProvider
mView.requestSendAccessibilityEvent(mView, event);
return true;
}
});
}, eventThrottleDelays);
// Native is initialized lazily, when node provider is actually requested.
}
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser.accessibility;
import android.support.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.RetryOnFailure;
import java.util.HashMap;
/**
* Test suite to ensure that |AccessibilityEventDispatcher| behaves appropriately.
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class AccessibilityEventDispatcherTest {
private AccessibilityEventDispatcher mDispatcher;
private HashMap<Integer, Integer> mEventDelays = new HashMap<Integer, Integer>();
// Helper member variables for testing.
private boolean mRunnablePosted;
private boolean mRunnableRemoved;
private boolean mEventDispatched;
/**
* Test setup, run before each test. Creates a HashMap of eventType's to delay (fake values),
* and creates an |AccessibilityEventDispatcher| with a Client that tracks method calls.
*/
@Before
public void setUp() {
// Mock two eventTypes to throttle
mEventDelays.put(2, 10);
mEventDelays.put(3, 5000);
// Create a dispatcher, and track which callback methods have been called with booleans
mDispatcher = new AccessibilityEventDispatcher(new AccessibilityEventDispatcher.Client() {
@Override
public void postRunnable(Runnable toPost, long delayInMilliseconds) {
mRunnablePosted = true;
}
@Override
public void removeRunnable(Runnable toRemove) {
mRunnableRemoved = true;
}
@Override
public boolean dispatchEvent(int virtualViewId, int eventType) {
mEventDispatched = true;
return true;
}
}, mEventDelays);
mRunnablePosted = false;
mRunnableRemoved = false;
mEventDispatched = false;
}
/**
* Test enqueue properly ignores events not being throttled and acts like a pass-through.
*/
@Test
@SmallTest
@RetryOnFailure
public void testEnqueue_notThrottle() {
mDispatcher.enqueueEvent(1, 1);
Assert.assertTrue(mEventDispatched);
Assert.assertFalse(mRunnablePosted);
Assert.assertFalse(mRunnableRemoved);
}
/**
* Test enqueue sends first event of a given type when no previous events of that type have
* been sent, i.e. we do not throttle the first event.
*/
@Test
@SmallTest
@RetryOnFailure
public void testEnqueue_noPreviousEvents() {
mDispatcher.enqueueEvent(1, 2);
Assert.assertTrue(mEventDispatched);
Assert.assertFalse(mRunnablePosted);
Assert.assertTrue(mRunnableRemoved);
}
/**
* Test enqueue sends events of a given type when the previous event of that type has been
* sent longer ago than our delay time.
*
* @throws InterruptedException Thread.sleep()
*/
@Test
@SmallTest
@RetryOnFailure
public void testEnqueue_noRecentPreviousEvents() throws InterruptedException {
// Send first event through as normal
mDispatcher.enqueueEvent(1, 2);
Assert.assertTrue(mEventDispatched);
Assert.assertFalse(mRunnablePosted);
Assert.assertTrue(mRunnableRemoved);
// Reset trackers
mRunnablePosted = false;
mRunnableRemoved = false;
mEventDispatched = false;
// Wait 5 seconds so next event won't be throttled
Thread.sleep(5000);
// Send another event and it should pass through as normal (we waited longer than delay)
mDispatcher.enqueueEvent(1, 2);
Assert.assertTrue(mEventDispatched);
Assert.assertFalse(mRunnablePosted);
Assert.assertTrue(mRunnableRemoved);
}
/**
* Test enqueue posts a |Runnable| for events of a given type that come in quick succession,
* i.e. we are throttling events and queueing them for later sending.
*/
@Test
@SmallTest
@RetryOnFailure
public void testEnqueue_recentEventsInQueue() {
// Send first event through as normal
mDispatcher.enqueueEvent(1, 3);
Assert.assertTrue(mEventDispatched);
Assert.assertFalse(mRunnablePosted);
Assert.assertTrue(mRunnableRemoved);
// Send a series of more events rapidly
mDispatcher.enqueueEvent(1, 3);
mDispatcher.enqueueEvent(1, 3);
mDispatcher.enqueueEvent(1, 3);
Assert.assertTrue(mRunnablePosted);
// Reset trackers
mRunnablePosted = false;
mRunnableRemoved = false;
mEventDispatched = false;
// Send final event, ensure runnable is posted but nothing was dispatched
mDispatcher.enqueueEvent(1, 3);
Assert.assertFalse(mEventDispatched);
Assert.assertTrue(mRunnablePosted);
Assert.assertTrue(mRunnableRemoved);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment