Commit 06b8660d authored by bsheedy's avatar bsheedy Committed by Commit Bot

Use mocked VR controller for scrolling

Switches all VR instrumentation tests for scrolling using the Daydream
controller to use the Chrome-side controller mock instead of the
VrCore-side emulation via intents. The latter has always been somewhat
flaky, so this should help reduce VR test flakiness.

Bug: 902938
Change-Id: I560cda2129357c49bdfa3d095836e6ebd1b23da8
Reviewed-on: https://chromium-review.googlesource.com/c/1341061Reviewed-by: default avatarChristopher Grant <cjgrant@chromium.org>
Reviewed-by: default avatarIan Vollick <vollick@chromium.org>
Commit-Queue: Ian Vollick <vollick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#613107}
parent 6c0f52e9
......@@ -55,7 +55,6 @@ public class VrBrowserControllerInputTest {
public ChromeTabbedActivityVrTestRule mVrTestRule = new ChromeTabbedActivityVrTestRule();
private VrBrowserTestFramework mVrBrowserTestFramework;
private EmulatedVrController mController;
@Before
public void setUp() throws Exception {
......@@ -65,8 +64,6 @@ public class VrBrowserControllerInputTest {
mVrBrowserTestFramework = new VrBrowserTestFramework(mVrTestRule);
VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
mController = new EmulatedVrController(mVrTestRule.getActivity());
mController.recenterView();
}
private void waitForPageToBeScrollable(final RenderCoordinates coord) {
......@@ -136,6 +133,16 @@ public class VrBrowserControllerInputTest {
testControllerScrollingImpl(url, waitScrollable, getYCoord, getXCoord);
}
private void waitForScrollQuiescence(final Callable<Integer> getCoord) {
final AtomicInteger lastCoord = new AtomicInteger(-1);
CriteriaHelper.pollInstrumentationThread(() -> {
Integer curCoord = getCoord.call();
if (curCoord.equals(lastCoord.get())) return true;
lastCoord.set(curCoord);
return false;
}, "Did not reach scroll quiescence", POLL_TIMEOUT_LONG_MS, POLL_CHECK_INTERVAL_LONG_MS);
}
private void testControllerScrollingImpl(String url, Runnable waitScrollable,
Callable<Integer> getYCoord, Callable<Integer> getXCoord)
throws InterruptedException, Exception {
......@@ -144,33 +151,33 @@ public class VrBrowserControllerInputTest {
// Test that scrolling down works.
int startScrollPoint = getYCoord.call().intValue();
// Arbitrary, but valid values to scroll smoothly.
int scrollSteps = 20;
int scrollSpeed = 60;
mController.scroll(EmulatedVrController.ScrollDirection.DOWN, scrollSteps, scrollSpeed);
// We need this second scroll down, otherwise the horizontal scrolling becomes flaky
// This actually seems to not be an issue in this test case anymore, but still occurs in
// the fling scroll test, so keep around here as an extra precaution.
// TODO(bsheedy): Figure out why this is the case.
mController.scroll(EmulatedVrController.ScrollDirection.DOWN, scrollSteps, scrollSpeed);
NativeUiUtils.scrollNonFling(NativeUiUtils.ScrollDirection.DOWN);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_NON_FLING_SCROLL);
waitForScrollQuiescence(getYCoord);
int endScrollPoint = getYCoord.call().intValue();
Assert.assertTrue("Controller failed to scroll down", startScrollPoint < endScrollPoint);
// Test that scrolling up works.
startScrollPoint = endScrollPoint;
mController.scroll(EmulatedVrController.ScrollDirection.UP, scrollSteps, scrollSpeed);
startScrollPoint = getYCoord.call().intValue();
NativeUiUtils.scrollNonFling(NativeUiUtils.ScrollDirection.UP);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_NON_FLING_SCROLL);
waitForScrollQuiescence(getYCoord);
endScrollPoint = getYCoord.call().intValue();
Assert.assertTrue("Controller failed to scroll up", startScrollPoint > endScrollPoint);
// Test that scrolling right works.
startScrollPoint = getXCoord.call().intValue();
mController.scroll(EmulatedVrController.ScrollDirection.RIGHT, scrollSteps, scrollSpeed);
NativeUiUtils.scrollNonFling(NativeUiUtils.ScrollDirection.RIGHT);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_NON_FLING_SCROLL);
waitForScrollQuiescence(getXCoord);
endScrollPoint = getXCoord.call().intValue();
Assert.assertTrue("Controller failed to scroll right", startScrollPoint < endScrollPoint);
// Test that scrolling left works.
startScrollPoint = endScrollPoint;
mController.scroll(EmulatedVrController.ScrollDirection.LEFT, scrollSteps, scrollSpeed);
NativeUiUtils.scrollNonFling(NativeUiUtils.ScrollDirection.LEFT);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_NON_FLING_SCROLL);
waitForScrollQuiescence(getXCoord);
endScrollPoint = getXCoord.call().intValue();
Assert.assertTrue("Controller failed to scroll left", startScrollPoint > endScrollPoint);
}
......@@ -189,46 +196,41 @@ public class VrBrowserControllerInputTest {
RenderCoordinates.fromWebContents(mVrTestRule.getWebContents());
waitForPageToBeScrollable(coord);
// Arbitrary, but valid values to trigger fling scrolling.
int scrollSteps = 10;
int scrollSpeed = 10;
// Test fling scrolling down.
mController.scroll(EmulatedVrController.ScrollDirection.DOWN, scrollSteps, scrollSpeed);
NativeUiUtils.scrollFling(NativeUiUtils.ScrollDirection.DOWN);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_FLING_SCROLL);
final AtomicInteger endScrollPoint = new AtomicInteger(coord.getScrollYPixInt());
// Check that we continue to scroll past wherever we were when we let go of the touchpad.
// Check that we continue to scroll past wherever we were even though we aren't touching
// the touchpad anymore.
CriteriaHelper.pollInstrumentationThread(
()
-> { return coord.getScrollYPixInt() > endScrollPoint.get(); },
"Controller failed to fling scroll down", POLL_TIMEOUT_SHORT_MS,
POLL_CHECK_INTERVAL_LONG_MS);
mController.cancelFlingScroll();
// Test fling scrolling up.
mController.scroll(EmulatedVrController.ScrollDirection.UP, scrollSteps, scrollSpeed);
NativeUiUtils.scrollFling(NativeUiUtils.ScrollDirection.UP);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_FLING_SCROLL);
endScrollPoint.set(coord.getScrollYPixInt());
CriteriaHelper.pollInstrumentationThread(
()
-> { return coord.getScrollYPixInt() < endScrollPoint.get(); },
"Controller failed to fling scroll up", POLL_TIMEOUT_SHORT_MS,
POLL_CHECK_INTERVAL_LONG_MS);
mController.cancelFlingScroll();
// Horizontal scrolling becomes flaky if the scroll bar is at the top when we try to scroll
// horizontally, so scroll down a bit to ensure that isn't the case.
mController.scroll(EmulatedVrController.ScrollDirection.DOWN, 10, 60);
// Test fling scrolling right.
mController.scroll(EmulatedVrController.ScrollDirection.RIGHT, scrollSteps, scrollSpeed);
NativeUiUtils.scrollFling(NativeUiUtils.ScrollDirection.RIGHT);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_FLING_SCROLL);
endScrollPoint.set(coord.getScrollXPixInt());
CriteriaHelper.pollInstrumentationThread(
()
-> { return coord.getScrollXPixInt() > endScrollPoint.get(); },
"Controller failed to fling scroll right", POLL_TIMEOUT_SHORT_MS,
POLL_CHECK_INTERVAL_LONG_MS);
mController.cancelFlingScroll();
// Test fling scrolling left.
mController.scroll(EmulatedVrController.ScrollDirection.LEFT, scrollSteps, scrollSpeed);
NativeUiUtils.scrollFling(NativeUiUtils.ScrollDirection.LEFT);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_FLING_SCROLL);
endScrollPoint.set(coord.getScrollXPixInt());
CriteriaHelper.pollInstrumentationThread(
()
......@@ -248,7 +250,7 @@ public class VrBrowserControllerInputTest {
"test_controller_clicks_register_on_webpage"),
PAGE_LOAD_TIMEOUT_S);
mController.performControllerClick();
NativeUiUtils.clickElement(UserFriendlyElementName.CONTENT_QUAD, new PointF());
ChromeTabUtils.waitForTabPageLoaded(mVrTestRule.getActivity().getActivityTab(),
VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"));
}
......@@ -264,7 +266,7 @@ public class VrBrowserControllerInputTest {
public void testControllerClicksRegisterOnIframe() throws InterruptedException {
mVrTestRule.loadUrl(
VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_iframe_clicks_outer"));
mController.performControllerClick();
NativeUiUtils.clickElement(UserFriendlyElementName.CONTENT_QUAD, new PointF());
// Wait until the iframe's current location matches the URL of the page that gets navigated
// to on click.
mVrBrowserTestFramework.pollJavaScriptBooleanInFrameOrFail("window.location.href == '"
......@@ -311,16 +313,15 @@ public class VrBrowserControllerInputTest {
// Test that scrolling down works
int startScrollPoint = recyclerView.computeVerticalScrollOffset();
// Arbitrary, but valid values to scroll smoothly
int scrollSteps = 20;
int scrollSpeed = 60;
mController.scroll(EmulatedVrController.ScrollDirection.DOWN, scrollSteps, scrollSpeed);
NativeUiUtils.scrollNonFling(NativeUiUtils.ScrollDirection.DOWN);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_NON_FLING_SCROLL);
int endScrollPoint = recyclerView.computeVerticalScrollOffset();
Assert.assertTrue("Controller failed to scroll down", startScrollPoint < endScrollPoint);
// Test that scrolling up works
startScrollPoint = endScrollPoint;
mController.scroll(EmulatedVrController.ScrollDirection.UP, scrollSteps, scrollSpeed);
NativeUiUtils.scrollNonFling(NativeUiUtils.ScrollDirection.UP);
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_NON_FLING_SCROLL);
endScrollPoint = recyclerView.computeVerticalScrollOffset();
Assert.assertTrue("Controller failed to scroll up", startScrollPoint > endScrollPoint);
}
......
......@@ -8,6 +8,7 @@ import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_
import android.graphics.PointF;
import android.graphics.Rect;
import android.support.annotation.IntDef;
import android.view.Choreographer;
import android.view.View;
import android.view.ViewGroup;
......@@ -31,6 +32,8 @@ import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.DOMUtils;
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;
......@@ -39,11 +42,33 @@ import java.util.concurrent.TimeoutException;
* omnibox or back button.
*/
public class NativeUiUtils {
@IntDef({ScrollDirection.UP, ScrollDirection.DOWN, ScrollDirection.LEFT, ScrollDirection.RIGHT})
@Retention(RetentionPolicy.SOURCE)
public @interface ScrollDirection {
int UP = 0;
int DOWN = 1;
int LEFT = 2;
int RIGHT = 3;
}
// How many frames to wait after entering text in the omnibox before we can assume that
// suggestions are updated. This should only be used if the workaround of inputting text and
// waiting for the suggestion box to appear doesn't work, e.g. if you need to input text, wait
// for autocomplete, then input more text before committing. 20 is arbitrary, but stable.
public static final int NUM_FRAMES_FOR_SUGGESTION_UPDATE = 20;
// Arbitrary number of interpolated steps to perform within a scroll to consistently trigger
// either fling or non-fling scrolling.
public static final int NUM_STEPS_NON_FLING_SCROLL = 60;
public static final int NUM_STEPS_FLING_SCROLL = 6;
// Number of frames to wait after queueing a non-fling scroll before we can be sure that all the
// scroll actions have been processed. The +2 comes from scrolls always having a touch down and
// up action with NUM_STEPS_*_SCROLL additional actions in between.
public static final int NUM_FRAMES_NON_FLING_SCROLL = NUM_STEPS_NON_FLING_SCROLL + 2;
// The number of frames after queueing a fling scroll before we can be sure that all the scroll
// actions have been processed AND we should still be scrolling due to the fling. The 10 is
// arbitrary, but 1/6 of a second is a reasonable amount of time to wait and still expect to be
// flinging, and is stable.
public static final int NUM_FRAMES_FLING_SCROLL = NUM_STEPS_FLING_SCROLL + 2 + 10;
public static final String FRAME_BUFFER_SUFFIX_WEB_XR_OVERLAY = "_WebXrOverlay";
public static final String FRAME_BUFFER_SUFFIX_WEB_XR_CONTENT = "_WebXrContent";
public static final String FRAME_BUFFER_SUFFIX_BROWSER_UI = "_BrowserUi";
......@@ -161,6 +186,51 @@ public class NativeUiUtils {
});
}
/**
* Helper function for performing a non-fling scroll.
*
* @param direction the ScrollDirection to scroll in.
*/
public static void scrollNonFling(@ScrollDirection int direction) throws InterruptedException {
scroll(directionToStartPoint(direction), directionToEndPoint(direction),
NUM_STEPS_NON_FLING_SCROLL);
}
/**
* Helper function for performing a fling scroll.
*
* @param direction the ScrollDirection to scroll in.
*/
public static void scrollFling(@ScrollDirection int direction) throws InterruptedException {
scroll(directionToStartPoint(direction), directionToEndPoint(direction),
NUM_STEPS_FLING_SCROLL);
}
/**
* Perform a touchpad drag to scroll.
*
* @param start the position on the touchpad to start the drag.
* @param end the position on the touchpad to end the drag.
* @param numSteps the number of steps to interpolate between the two points, one step per
* frame.
*/
public static void scroll(PointF start, PointF end, int numSteps) throws InterruptedException {
PointF stepIncrement =
new PointF((end.x - start.x) / numSteps, (end.y - start.y) / numSteps);
PointF currentPosition = new PointF(start.x, start.y);
TestVrShellDelegate.getInstance().performControllerActionForTesting(
UserFriendlyElementName.NONE /* unused */, VrControllerTestAction.TOUCH_DOWN,
currentPosition);
for (int i = 0; i < numSteps; ++i) {
currentPosition.offset(stepIncrement.x, stepIncrement.y);
TestVrShellDelegate.getInstance().performControllerActionForTesting(
UserFriendlyElementName.NONE /* unused */, VrControllerTestAction.TOUCH_DOWN,
currentPosition);
}
TestVrShellDelegate.getInstance().performControllerActionForTesting(
UserFriendlyElementName.NONE /* unused */, VrControllerTestAction.TOUCH_UP, end);
}
/**
* Inputs the given text as if done via the VR keyboard.
*
......@@ -479,4 +549,30 @@ public class NativeUiUtils {
return "Unknown result";
}
}
private static PointF directionToStartPoint(@ScrollDirection int direction) {
switch (direction) {
case ScrollDirection.UP:
return new PointF(0.5f, 0.05f);
case ScrollDirection.DOWN:
return new PointF(0.5f, 0.95f);
case ScrollDirection.LEFT:
return new PointF(0.05f, 0.5f);
default:
return new PointF(0.95f, 0.5f);
}
}
private static PointF directionToEndPoint(@ScrollDirection int direction) {
switch (direction) {
case ScrollDirection.UP:
return new PointF(0.5f, 0.95f);
case ScrollDirection.DOWN:
return new PointF(0.5f, 0.05f);
case ScrollDirection.LEFT:
return new PointF(0.95f, 0.5f);
default:
return new PointF(0.05f, 0.5f);
}
}
}
......@@ -43,8 +43,8 @@ class AndroidUiGestureTarget {
int64_t time_ms,
int delay_ms);
int scroll_x_ = 0;
int scroll_y_ = 0;
float scroll_x_ = 0.0f;
float scroll_y_ = 0.0f;
float scale_factor_;
float scroll_ratio_;
int touch_slop_;
......
......@@ -81,13 +81,8 @@ void InputDelegateForTesting::QueueControllerActionForTesting(
break;
case VrControllerTestAction::kMove:
// Use whatever the last button state is.
if (!IsQueueEmpty()) {
controller_model.touchpad_button_state =
controller_model_queue_.back().touchpad_button_state;
} else {
controller_model.touchpad_button_state =
cached_controller_model_.touchpad_button_state;
}
controller_model.touchpad_button_state =
GetMostRecentModel().touchpad_button_state;
controller_model_queue_.push(controller_model);
break;
case VrControllerTestAction::kAppDown:
......@@ -98,6 +93,22 @@ void InputDelegateForTesting::QueueControllerActionForTesting(
controller_model.app_button_state = ControllerModel::ButtonState::kUp;
controller_model_queue_.push(controller_model);
break;
case VrControllerTestAction::kTouchDown:
// Use whatever the most recent direction is and interpret the provided
// point as the point on the touchpad.
controller_model.laser_direction = GetMostRecentModel().laser_direction;
SetOriginAndTransform(&controller_model);
controller_model.touching_touchpad = true;
controller_model.touchpad_touch_position = controller_input.position;
controller_model_queue_.push(controller_model);
break;
case VrControllerTestAction::kTouchUp:
controller_model.laser_direction = GetMostRecentModel().laser_direction;
SetOriginAndTransform(&controller_model);
controller_model.touching_touchpad = false;
controller_model.touchpad_touch_position = controller_input.position;
controller_model_queue_.push(controller_model);
break;
default:
NOTREACHED() << "Given unsupported controller action";
}
......@@ -147,4 +158,10 @@ void InputDelegateForTesting::OnResume() {}
void InputDelegateForTesting::OnPause() {}
ControllerModel InputDelegateForTesting::GetMostRecentModel() {
if (!IsQueueEmpty())
return controller_model_queue_.back();
return cached_controller_model_;
}
} // namespace vr
......@@ -38,6 +38,8 @@ class InputDelegateForTesting : public InputDelegate {
void OnPause() override;
private:
ControllerModel GetMostRecentModel();
UiInterface* ui_;
std::queue<ControllerModel> controller_model_queue_;
ControllerModel cached_controller_model_;
......
......@@ -8,17 +8,7 @@ namespace vr {
ControllerModel::ControllerModel() = default;
ControllerModel::ControllerModel(const ControllerModel& other)
: transform(other.transform),
laser_direction(other.laser_direction),
laser_origin(other.laser_origin),
touchpad_button_state(other.touchpad_button_state),
app_button_state(other.app_button_state),
home_button_state(other.home_button_state),
opacity(other.opacity),
resting_in_viewport(other.resting_in_viewport),
handedness(other.handedness),
battery_level(other.battery_level) {}
ControllerModel::ControllerModel(const ControllerModel& other) = default;
ControllerModel::~ControllerModel() = default;
......
......@@ -63,11 +63,11 @@ bool PlatformControllerForTesting::ButtonDownHappened(
}
bool PlatformControllerForTesting::IsTouchingTrackpad() const {
return false;
return cur_model_->touching_touchpad;
}
gfx::PointF PlatformControllerForTesting::GetPositionInTrackpad() const {
return gfx::PointF();
return cur_model_->touchpad_touch_position;
}
base::TimeTicks PlatformControllerForTesting::GetLastOrientationTimestamp()
......
......@@ -78,6 +78,8 @@ enum class VrControllerTestAction : int {
kMove,
kAppDown,
kAppUp,
kTouchDown,
kTouchUp,
};
// These are used to specify what type of keyboard input should be performed
......
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