Commit dc7c6cbb authored by bsheedy's avatar bsheedy Committed by Commit Bot

Automate WebXR controller and permission tests

Automates the manual tests from https://crbug.com/887551, which relate
to the Daydream controller while in a WebXR immersive session and
permissions, e.g. ensuring that in-use permissions are displayed when
long-pressing the app button.

As a side-effect, updates the test code for waiting on a VR UI element
to change visibility to instead wait for a specific visibility state.

Bug: 887551
Change-Id: I0f845231d2242c711a7b8d6f1465105ec8dc6383
Reviewed-on: https://chromium-review.googlesource.com/c/1318063Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605452}
parent e734bc5e
...@@ -136,6 +136,23 @@ public class VrShell extends GvrLayout ...@@ -136,6 +136,23 @@ public class VrShell extends GvrLayout
private ArrayList<Integer> mUiOperationResults; private ArrayList<Integer> mUiOperationResults;
private ArrayList<Runnable> mUiOperationResultCallbacks; private ArrayList<Runnable> mUiOperationResultCallbacks;
/**
* A struct-like object for registering UI operations during tests.
*/
@VisibleForTesting
public static class UiOperationData {
// The UiTestOperationType of this operation.
public int actionType;
// The callback to run when the operation completes.
public Runnable resultCallback;
// The timeout of the operation.
public int timeoutMs;
// The UserFriendlyElementName to perform the operation on.
public int elementName;
// The desired visibility status of the element.
public boolean visibility;
}
public VrShell( public VrShell(
ChromeActivity activity, VrShellDelegate delegate, TabModelSelector tabModelSelector) { ChromeActivity activity, VrShellDelegate delegate, TabModelSelector tabModelSelector) {
super(activity); super(activity);
...@@ -1233,8 +1250,8 @@ public class VrShell extends GvrLayout ...@@ -1233,8 +1250,8 @@ public class VrShell extends GvrLayout
}); });
} }
public void registerUiOperationCallbackForTesting( public void registerUiOperationCallbackForTesting(UiOperationData operationData) {
int actionType, Runnable resultCallback, int timeoutMs, int elementName) { int actionType = operationData.actionType;
assert actionType < UiTestOperationType.NUM_UI_TEST_OPERATION_TYPES; assert actionType < UiTestOperationType.NUM_UI_TEST_OPERATION_TYPES;
// Fill the ArrayLists if this is the first time the method has been called. // Fill the ArrayLists if this is the first time the method has been called.
if (mUiOperationResults == null) { if (mUiOperationResults == null) {
...@@ -1248,14 +1265,15 @@ public class VrShell extends GvrLayout ...@@ -1248,14 +1265,15 @@ public class VrShell extends GvrLayout
} }
} }
mUiOperationResults.set(actionType, UiTestOperationResult.UNREPORTED); mUiOperationResults.set(actionType, UiTestOperationResult.UNREPORTED);
mUiOperationResultCallbacks.set(actionType, resultCallback); mUiOperationResultCallbacks.set(actionType, operationData.resultCallback);
// In the case of the UI activity quiescence callback type, we need to let the native UI // In the case of the UI activity quiescence callback type, we need to let the native UI
// know how long to wait before timing out. // know how long to wait before timing out.
if (actionType == UiTestOperationType.UI_ACTIVITY_RESULT) { if (actionType == UiTestOperationType.UI_ACTIVITY_RESULT) {
nativeSetUiExpectingActivityForTesting(mNativeVrShell, timeoutMs); nativeSetUiExpectingActivityForTesting(mNativeVrShell, operationData.timeoutMs);
} else if (actionType == UiTestOperationType.ELEMENT_VISIBILITY_CHANGE) { } else if (actionType == UiTestOperationType.ELEMENT_VISIBILITY_STATUS) {
nativeWatchElementForVisibilityChangeForTesting(mNativeVrShell, elementName, timeoutMs); nativeWatchElementForVisibilityStatusForTesting(mNativeVrShell,
operationData.elementName, operationData.timeoutMs, operationData.visibility);
} }
} }
...@@ -1327,8 +1345,8 @@ public class VrShell extends GvrLayout ...@@ -1327,8 +1345,8 @@ public class VrShell extends GvrLayout
long nativeVrShell, int quiescenceTimeoutMs); long nativeVrShell, int quiescenceTimeoutMs);
private native void nativeSaveNextFrameBufferToDiskForTesting( private native void nativeSaveNextFrameBufferToDiskForTesting(
long nativeVrShell, String filepathBase); long nativeVrShell, String filepathBase);
private native void nativeWatchElementForVisibilityChangeForTesting( private native void nativeWatchElementForVisibilityStatusForTesting(
long nativeVrShell, int elementName, int timeoutMs); long nativeVrShell, int elementName, int timeoutMs, boolean visibility);
private native void nativeResumeContentRendering(long nativeVrShell); private native void nativeResumeContentRendering(long nativeVrShell);
private native void nativeOnOverlayTextureEmptyChanged(long nativeVrShell, boolean empty); private native void nativeOnOverlayTextureEmptyChanged(long nativeVrShell, boolean empty);
} }
...@@ -78,7 +78,7 @@ public class EmulatedVrController { ...@@ -78,7 +78,7 @@ public class EmulatedVrController {
} }
/** /**
* Either presses or releases the Daydream controller's touchpad button depending on wheter * Either presses or releases the Daydream controller's touchpad button depending on whether
* the button is currently pressed or not. * the button is currently pressed or not.
*/ */
public void sendClickButtonToggleEvent() { public void sendClickButtonToggleEvent() {
...@@ -93,6 +93,14 @@ public class EmulatedVrController { ...@@ -93,6 +93,14 @@ public class EmulatedVrController {
getApi().buttonEvent.sendClickButtonEvent(); getApi().buttonEvent.sendClickButtonEvent();
} }
/**
* Either presses or releases the Daydream controller's app button depending on whether the
* button is currently pressed or not.
*/
public void sendAppButtonToggleEvent() {
getApi().buttonEvent.sendAppButtonToggleEvent();
}
/** /**
* Presses and quickly releases the Daydream controller's app button. * Presses and quickly releases the Daydream controller's app button.
* Or, if the button is already pressed, releases and quickly presses again. * Or, if the button is already pressed, releases and quickly presses again.
......
...@@ -98,10 +98,8 @@ public class TestVrShellDelegate extends VrShellDelegate { ...@@ -98,10 +98,8 @@ public class TestVrShellDelegate extends VrShellDelegate {
getVrShell().performKeyboardInputForTesting(inputType, inputString); getVrShell().performKeyboardInputForTesting(inputType, inputString);
} }
public void registerUiOperationCallbackForTesting( public void registerUiOperationCallbackForTesting(VrShell.UiOperationData operationData) {
int actionType, Runnable resultCallback, int timeoutMs, int elementName) { getVrShell().registerUiOperationCallbackForTesting(operationData);
getVrShell().registerUiOperationCallbackForTesting(
actionType, resultCallback, timeoutMs, elementName);
} }
public void saveNextFrameBufferToDiskForTesting(String filepathBase) { public void saveNextFrameBufferToDiskForTesting(String filepathBase) {
......
...@@ -409,7 +409,7 @@ public class VrBrowserControllerInputTest { ...@@ -409,7 +409,7 @@ public class VrBrowserControllerInputTest {
NativeUiUtils.revertToRealInput(); NativeUiUtils.revertToRealInput();
// Wait for the URL bar to re-appear, which we take as a signal that we've exited omnibox // Wait for the URL bar to re-appear, which we take as a signal that we've exited omnibox
// text input mode. // text input mode.
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(UserFriendlyElementName.URL,
UserFriendlyElementName.URL, () -> { mController.pressReleaseAppButton(); }); true /* visible */, () -> { mController.pressReleaseAppButton(); });
} }
} }
...@@ -152,8 +152,8 @@ public class VrBrowserNativeUiTest { ...@@ -152,8 +152,8 @@ public class VrBrowserNativeUiTest {
NativeUiUtils.inputBackspace(); NativeUiUtils.inputBackspace();
} }
// Wait for suggestions to change so that our navigation succeeds. // Wait for suggestions to change so that our navigation succeeds.
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("version/"); }); () -> { NativeUiUtils.inputString("version/"); });
NativeUiUtils.inputEnter(); NativeUiUtils.inputEnter();
ChromeTabUtils.waitForTabPageLoaded( ChromeTabUtils.waitForTabPageLoaded(
...@@ -181,8 +181,9 @@ public class VrBrowserNativeUiTest { ...@@ -181,8 +181,9 @@ public class VrBrowserNativeUiTest {
UserFriendlyElementName.OMNIBOX_TEXT_FIELD, new PointF(-0.45f, 0.0f)); UserFriendlyElementName.OMNIBOX_TEXT_FIELD, new PointF(-0.45f, 0.0f));
// We expect this to delete an "a" instead of anything in "chrome://". Do so and wait for // We expect this to delete an "a" instead of anything in "chrome://". Do so and wait for
// the suggestions to appear. // the suggestions to appear.
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, () -> { NativeUiUtils.inputBackspace(); }); UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputBackspace(); });
NativeUiUtils.inputEnter(); NativeUiUtils.inputEnter();
// Navigating automatically appends a "/". // Navigating automatically appends a "/".
ChromeTabUtils.waitForTabPageLoaded( ChromeTabUtils.waitForTabPageLoaded(
...@@ -201,8 +202,8 @@ public class VrBrowserNativeUiTest { ...@@ -201,8 +202,8 @@ public class VrBrowserNativeUiTest {
NativeUiUtils.enableMockedKeyboard(); NativeUiUtils.enableMockedKeyboard();
NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF()); NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF());
// Input text and close the omnibox without committing. // Input text and close the omnibox without committing.
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("chrome://version/"); }); () -> { NativeUiUtils.inputString("chrome://version/"); });
NativeUiUtils.clickElementAndWaitForUiQuiescence( NativeUiUtils.clickElementAndWaitForUiQuiescence(
UserFriendlyElementName.OMNIBOX_CLOSE_BUTTON, new PointF()); UserFriendlyElementName.OMNIBOX_CLOSE_BUTTON, new PointF());
...@@ -215,8 +216,8 @@ public class VrBrowserNativeUiTest { ...@@ -215,8 +216,8 @@ public class VrBrowserNativeUiTest {
for (int i = 0; i < 11; ++i) { for (int i = 0; i < 11; ++i) {
NativeUiUtils.inputBackspace(); NativeUiUtils.inputBackspace();
} }
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visibile */,
() -> { NativeUiUtils.inputString("chrome://version/"); }); () -> { NativeUiUtils.inputString("chrome://version/"); });
NativeUiUtils.inputEnter(); NativeUiUtils.inputEnter();
// This should only succeed if the original "chrome://version/" we input is not present - // This should only succeed if the original "chrome://version/" we input is not present -
...@@ -241,8 +242,8 @@ public class VrBrowserNativeUiTest { ...@@ -241,8 +242,8 @@ public class VrBrowserNativeUiTest {
// Test that autocompletion works with only one option left. // Test that autocompletion works with only one option left.
NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF()); NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF());
// This should autocomplete to "chrome://version". // This should autocomplete to "chrome://version".
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("chrome://v"); }); () -> { NativeUiUtils.inputString("chrome://v"); });
NativeUiUtils.inputEnter(); NativeUiUtils.inputEnter();
ChromeTabUtils.waitForTabPageLoaded( ChromeTabUtils.waitForTabPageLoaded(
...@@ -252,8 +253,8 @@ public class VrBrowserNativeUiTest { ...@@ -252,8 +253,8 @@ public class VrBrowserNativeUiTest {
// Test that autocompletion updates successfully when entering more text. // Test that autocompletion updates successfully when entering more text.
NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF()); NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF());
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("chrome://v"); }); () -> { NativeUiUtils.inputString("chrome://v"); });
NativeUiUtils.inputString("e"); NativeUiUtils.inputString("e");
// Since suggestions are already visible, we need to wait for suggestions to update via // Since suggestions are already visible, we need to wait for suggestions to update via
...@@ -267,8 +268,8 @@ public class VrBrowserNativeUiTest { ...@@ -267,8 +268,8 @@ public class VrBrowserNativeUiTest {
NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF()); NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF());
// This could be either "chrome://net-export" or "chrome://net-internals", so it shouldn't // This could be either "chrome://net-export" or "chrome://net-internals", so it shouldn't
// autocomplete to anything. // autocomplete to anything.
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("chrome://net-"); }); () -> { NativeUiUtils.inputString("chrome://net-"); });
NativeUiUtils.inputEnter(); NativeUiUtils.inputEnter();
ChromeTabUtils.waitForTabPageLoaded( ChromeTabUtils.waitForTabPageLoaded(
...@@ -277,8 +278,8 @@ public class VrBrowserNativeUiTest { ...@@ -277,8 +278,8 @@ public class VrBrowserNativeUiTest {
// Test that autocompletion cancels if a non-matching character is input. // Test that autocompletion cancels if a non-matching character is input.
NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF()); NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF());
// This should autocomplete to "chrome://version". // This should autocomplete to "chrome://version".
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("chrome://v"); }); () -> { NativeUiUtils.inputString("chrome://v"); });
NativeUiUtils.inputString("a"); NativeUiUtils.inputString("a");
NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_FOR_SUGGESTION_UPDATE); NativeUiUtils.waitNumFrames(NativeUiUtils.NUM_FRAMES_FOR_SUGGESTION_UPDATE);
......
...@@ -693,8 +693,8 @@ public class VrBrowserNavigationTest { ...@@ -693,8 +693,8 @@ public class VrBrowserNavigationTest {
// having suggestions present, waiting for a number of frames before committing seemed // having suggestions present, waiting for a number of frames before committing seemed
// stable. Adding an operation to listen for suggestion changes would also work, but // stable. Adding an operation to listen for suggestion changes would also work, but
// would add some pretty niche test-only code to the native side. // would add some pretty niche test-only code to the native side.
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("chrome://version/"); }); () -> { NativeUiUtils.inputString("chrome://version/"); });
NativeUiUtils.inputEnter(); NativeUiUtils.inputEnter();
ChromeTabUtils.waitForTabPageLoaded( ChromeTabUtils.waitForTabPageLoaded(
...@@ -728,8 +728,8 @@ public class VrBrowserNavigationTest { ...@@ -728,8 +728,8 @@ public class VrBrowserNavigationTest {
private void testSuggestionClickTriggersNavigationImpl() throws InterruptedException { private void testSuggestionClickTriggersNavigationImpl() throws InterruptedException {
NativeUiUtils.enableMockedKeyboard(); NativeUiUtils.enableMockedKeyboard();
NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF()); NativeUiUtils.clickElementAndWaitForUiQuiescence(UserFriendlyElementName.URL, new PointF());
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.SUGGESTION_BOX, UserFriendlyElementName.SUGGESTION_BOX, true /* visible */,
() -> { NativeUiUtils.inputString("chrome://"); }); () -> { NativeUiUtils.inputString("chrome://"); });
// Blindly clicking in the center of the suggestion box should end up clicking the middle // Blindly clicking in the center of the suggestion box should end up clicking the middle
// suggestion, which for "chrome://" should be a valid chrome:// URL. // suggestion, which for "chrome://" should be a valid chrome:// URL.
......
...@@ -186,8 +186,8 @@ public class VrInstallUpdateInfoBarTest { ...@@ -186,8 +186,8 @@ public class VrInstallUpdateInfoBarTest {
// prompt to become visible before waiting for quiescence. // prompt to become visible before waiting for quiescence.
NativeUiUtils.performActionAndWaitForUiQuiescence(() -> { NativeUiUtils.performActionAndWaitForUiQuiescence(() -> {
try { try {
NativeUiUtils.performActionAndWaitForVisibilityChange( NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.EXIT_PROMPT, UserFriendlyElementName.EXIT_PROMPT, true /* visible */,
() -> { NativeUiUtils.clickElement(uiElementToClick, new PointF()); }); () -> { NativeUiUtils.clickElement(uiElementToClick, new PointF()); });
} catch (InterruptedException e) { } catch (InterruptedException e) {
Assert.fail("Interrupted while waiting for UI visibility change"); Assert.fail("Interrupted while waiting for UI visibility change");
......
...@@ -5,8 +5,11 @@ ...@@ -5,8 +5,11 @@
package org.chromium.chrome.browser.vr; package org.chromium.chrome.browser.vr;
import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S; import static org.chromium.chrome.browser.vr.XrTestFramework.PAGE_LOAD_TIMEOUT_S;
import static org.chromium.chrome.browser.vr.XrTestFramework.POLL_TIMEOUT_SHORT_MS;
import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_DAYDREAM_OR_STANDALONE;
import android.os.Build; import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import org.junit.Before; import org.junit.Before;
...@@ -15,16 +18,22 @@ import org.junit.Test; ...@@ -15,16 +18,22 @@ import org.junit.Test;
import org.junit.rules.RuleChain; import org.junit.rules.RuleChain;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.params.ParameterAnnotations.ClassParameter; import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate; import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
import org.chromium.base.test.params.ParameterSet; import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner; import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
import org.chromium.chrome.browser.vr.util.NativeUiUtils;
import org.chromium.chrome.browser.vr.util.PermissionUtils;
import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; import org.chromium.chrome.browser.vr.util.VrTestRuleUtils;
import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.net.test.EmbeddedTestServer;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
...@@ -93,4 +102,72 @@ public class WebXrVrTabTest { ...@@ -93,4 +102,72 @@ public class WebXrVrTabTest {
framework.executeStepAndWait("stepCheckFrameDataWhileNonFocusedTab()"); framework.executeStepAndWait("stepCheckFrameDataWhileNonFocusedTab()");
framework.endTest(); framework.endTest();
} }
/**
* Tests that permissions in use by other tabs are shown while in a WebXR for VR immersive
* session.
*/
@Test
@MediumTest
@Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM_OR_STANDALONE)
@CommandLineFlags
.Remove({"enable-webvr"})
@CommandLineFlags.Add({"enable-features=WebXR"})
public void testPermissionsInOtherTab() throws InterruptedException {
testPermissionsInOtherTabImpl(false /* incognito */);
}
@Test
@MediumTest
@Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM_OR_STANDALONE)
@CommandLineFlags
.Remove({"enable-webvr"})
@CommandLineFlags.Add({"enable-features=WebXR"})
public void testPermissionsInOtherTabIncognito() throws InterruptedException {
testPermissionsInOtherTabImpl(true /* incognito */);
}
private void testPermissionsInOtherTabImpl(boolean incognito) throws InterruptedException {
EmbeddedTestServer server = mTestRule.getTestServer();
boolean teardownServer = false;
if (server == null) {
server = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
teardownServer = true;
}
mWebXrVrTestFramework.loadUrlAndAwaitInitialization(
server.getURL(WebXrVrTestFramework.getEmbeddedServerPathForHtmlTestFile(
"generic_webxr_permission_page")),
PAGE_LOAD_TIMEOUT_S);
// Be sure to store the stream we're given so that the permission is actually in use, as
// otherwise the toast doesn't show up since another tab isn't actually using the
// permission.
WebXrVrTestFramework.runJavaScriptOrFail(
"requestPermission({audio:true}, true /* storeValue */)", POLL_TIMEOUT_SHORT_MS,
mTestRule.getWebContents());
PermissionUtils.waitForPermissionPrompt();
PermissionUtils.acceptPermissionPrompt();
WebXrVrTestFramework.waitOnJavaScriptStep(mTestRule.getWebContents());
if (incognito) {
ThreadUtils.runOnUiThreadBlocking(() -> {
mTestRule.getActivity()
.getTabCreator(true /* incognito */)
.launchUrl("about:blank", TabLaunchType.FROM_LINK);
});
} else {
mTestRule.loadUrlInNewTab("about:blank");
}
mWebXrVrTestFramework.loadUrlAndAwaitInitialization(
WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"),
PAGE_LOAD_TIMEOUT_S);
mWebXrVrTestFramework.enterSessionWithUserGestureOrFail(mTestRule.getWebContents());
NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.WEB_XR_AUDIO_INDICATOR, true /* visible */, () -> {});
if (teardownServer) {
server.stopAndDestroyServer();
}
}
} }
...@@ -43,6 +43,7 @@ import org.chromium.chrome.browser.ChromeActivity; ...@@ -43,6 +43,7 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.vr.rules.VrSettingsFile; import org.chromium.chrome.browser.vr.rules.VrSettingsFile;
import org.chromium.chrome.browser.vr.rules.XrActivityRestriction; import org.chromium.chrome.browser.vr.rules.XrActivityRestriction;
import org.chromium.chrome.browser.vr.util.NativeUiUtils;
import org.chromium.chrome.browser.vr.util.NfcSimUtils; import org.chromium.chrome.browser.vr.util.NfcSimUtils;
import org.chromium.chrome.browser.vr.util.VrSettingsServiceUtils; import org.chromium.chrome.browser.vr.util.VrSettingsServiceUtils;
import org.chromium.chrome.browser.vr.util.VrTestRuleUtils; import org.chromium.chrome.browser.vr.util.VrTestRuleUtils;
...@@ -485,4 +486,24 @@ public class WebXrVrTransitionTest { ...@@ -485,4 +486,24 @@ public class WebXrVrTransitionTest {
mWebXrVrTestFramework.executeStepAndWait("stepAfterImmersive()"); mWebXrVrTestFramework.executeStepAndWait("stepAfterImmersive()");
mWebXrVrTestFramework.endTest(); mWebXrVrTestFramework.endTest();
} }
/**
* Tests that the "Press app button to exit" toast appears when entering an immersive WebXR for
* VR session with Daydream View paired.
*/
@Test
@MediumTest
@Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM_OR_STANDALONE)
@CommandLineFlags
.Remove({"enable-webvr"})
@CommandLineFlags.Add({"enable-features=WebXR"})
@XrActivityRestriction({XrActivityRestriction.SupportedActivity.CTA})
public void testAppButtonExitToast() throws InterruptedException {
mWebXrVrTestFramework.loadUrlAndAwaitInitialization(
WebXrVrTestFramework.getFileUrlForHtmlTestFile("generic_webxr_page"),
PAGE_LOAD_TIMEOUT_S);
mWebXrVrTestFramework.enterSessionWithUserGestureOrFail();
NativeUiUtils.performActionAndWaitForVisibilityStatus(
UserFriendlyElementName.APP_BUTTON_EXIT_TOAST, true /* visible*/, () -> {});
}
} }
...@@ -223,14 +223,16 @@ public class NativeUiUtils { ...@@ -223,14 +223,16 @@ public class NativeUiUtils {
throws InterruptedException { throws InterruptedException {
final TestVrShellDelegate instance = TestVrShellDelegate.getInstance(); final TestVrShellDelegate instance = TestVrShellDelegate.getInstance();
final CountDownLatch resultLatch = new CountDownLatch(1); final CountDownLatch resultLatch = new CountDownLatch(1);
final VrShell.UiOperationData operationData = new VrShell.UiOperationData();
operationData.actionType = UiTestOperationType.UI_ACTIVITY_RESULT;
operationData.resultCallback = () -> {
resultLatch.countDown();
};
operationData.timeoutMs = DEFAULT_UI_QUIESCENCE_TIMEOUT_MS;
// Run on the UI thread to prevent issues with registering a new callback before // Run on the UI thread to prevent issues with registering a new callback before
// ReportUiOperationResultForTesting has finished. // ReportUiOperationResultForTesting has finished.
ThreadUtils.runOnUiThreadBlocking(() -> { ThreadUtils.runOnUiThreadBlocking(
instance.registerUiOperationCallbackForTesting( () -> { instance.registerUiOperationCallbackForTesting(operationData); });
UiTestOperationType.UI_ACTIVITY_RESULT, () -> {
resultLatch.countDown();
}, DEFAULT_UI_QUIESCENCE_TIMEOUT_MS, 0 /* unused */);
});
action.run(); action.run();
// Wait for any outstanding animations to finish. // Wait for any outstanding animations to finish.
...@@ -243,32 +245,38 @@ public class NativeUiUtils { ...@@ -243,32 +245,38 @@ public class NativeUiUtils {
} }
/** /**
* Runs the given Runnable and waits until the specified element changes its visibility. * Runs the given Runnable and waits until the specified element matches the requested
* visibility.
* *
* @param elementName The UserFriendlyElementName to wait on to change visibility. * @param elementName The UserFriendlyElementName to wait on to change visibility.
* @param status The visibility status to wait for.
* @param action A Runnable containing the action to perform. * @param action A Runnable containing the action to perform.
*/ */
public static void performActionAndWaitForVisibilityChange( public static void performActionAndWaitForVisibilityStatus(final int elementName,
final int elementName, Runnable action) throws InterruptedException { final boolean visible, Runnable action) throws InterruptedException {
final TestVrShellDelegate instance = TestVrShellDelegate.getInstance(); final TestVrShellDelegate instance = TestVrShellDelegate.getInstance();
final CountDownLatch resultLatch = new CountDownLatch(1); final CountDownLatch resultLatch = new CountDownLatch(1);
final VrShell.UiOperationData operationData = new VrShell.UiOperationData();
operationData.actionType = UiTestOperationType.ELEMENT_VISIBILITY_STATUS;
operationData.resultCallback = () -> {
resultLatch.countDown();
};
operationData.timeoutMs = DEFAULT_UI_QUIESCENCE_TIMEOUT_MS;
operationData.elementName = elementName;
operationData.visibility = visible;
// Run on the UI thread to prevent issues with registering a new callback before // Run on the UI thread to prevent issues with registering a new callback before
// ReportUiOperationResultForTesting has finished. // ReportUiOperationResultForTesting has finished.
ThreadUtils.runOnUiThreadBlocking(() -> { ThreadUtils.runOnUiThreadBlocking(
instance.registerUiOperationCallbackForTesting( () -> { instance.registerUiOperationCallbackForTesting(operationData); });
UiTestOperationType.ELEMENT_VISIBILITY_CHANGE, () -> {
resultLatch.countDown();
}, DEFAULT_UI_QUIESCENCE_TIMEOUT_MS, elementName);
});
action.run(); action.run();
// Wait for the result to be reported. // Wait for the result to be reported.
resultLatch.await(); resultLatch.await();
int result = instance.getLastUiOperationResultForTesting( int result = instance.getLastUiOperationResultForTesting(
UiTestOperationType.ELEMENT_VISIBILITY_CHANGE); UiTestOperationType.ELEMENT_VISIBILITY_STATUS);
Assert.assertEquals("UI reported non-visibility-changed result '" Assert.assertEquals("UI reported non-visibility-changed result '"
+ uiTestOperationResultToString(result) + "'", + uiTestOperationResultToString(result) + "'",
UiTestOperationResult.VISIBILITY_CHANGE, result); UiTestOperationResult.VISIBILITY_MATCH, result);
} }
/** /**
...@@ -310,13 +318,16 @@ public class NativeUiUtils { ...@@ -310,13 +318,16 @@ public class NativeUiUtils {
final TestVrShellDelegate instance = TestVrShellDelegate.getInstance(); final TestVrShellDelegate instance = TestVrShellDelegate.getInstance();
final CountDownLatch resultLatch = new CountDownLatch(1); final CountDownLatch resultLatch = new CountDownLatch(1);
final VrShell.UiOperationData operationData = new VrShell.UiOperationData();
operationData.actionType = UiTestOperationType.FRAME_BUFFER_DUMPED;
operationData.resultCallback = () -> {
resultLatch.countDown();
};
// Run on the UI thread to prevent issues with registering a new callback before // Run on the UI thread to prevent issues with registering a new callback before
// ReportUiOperationResultForTesting has finished. // ReportUiOperationResultForTesting has finished.
ThreadUtils.runOnUiThreadBlocking(() -> { ThreadUtils.runOnUiThreadBlocking(
instance.registerUiOperationCallbackForTesting(UiTestOperationType.FRAME_BUFFER_DUMPED, () -> { instance.registerUiOperationCallbackForTesting(operationData); });
() -> { resultLatch.countDown(); }, 0 /* unused */, 0 /* unused */);
});
instance.saveNextFrameBufferToDiskForTesting(filepathBase); instance.saveNextFrameBufferToDiskForTesting(filepathBase);
resultLatch.await(); resultLatch.await();
} }
...@@ -372,10 +383,10 @@ public class NativeUiUtils { ...@@ -372,10 +383,10 @@ public class NativeUiUtils {
return "Timeout (UI activity not started)"; return "Timeout (UI activity not started)";
case UiTestOperationResult.TIMEOUT_NO_END: case UiTestOperationResult.TIMEOUT_NO_END:
return "Timeout (UI activity not stopped)"; return "Timeout (UI activity not stopped)";
case UiTestOperationResult.VISIBILITY_CHANGE: case UiTestOperationResult.VISIBILITY_MATCH:
return "Visibility change"; return "Visibility match";
case UiTestOperationResult.TIMEOUT_NO_CHANGE: case UiTestOperationResult.TIMEOUT_NO_VISIBILITY_MATCH:
return "Timeout (Element visibility did not change)"; return "Timeout (Element visibility did not match)";
default: default:
return "Unknown result"; return "Unknown result";
} }
......
...@@ -1266,19 +1266,21 @@ void VrShell::SaveNextFrameBufferToDiskForTesting( ...@@ -1266,19 +1266,21 @@ void VrShell::SaveNextFrameBufferToDiskForTesting(
base::android::ConvertJavaStringToUTF8(env, filepath_base))); base::android::ConvertJavaStringToUTF8(env, filepath_base)));
} }
void VrShell::WatchElementForVisibilityChangeForTesting( void VrShell::WatchElementForVisibilityStatusForTesting(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
jint element_name, jint element_name,
jint timeout_ms) { jint timeout_ms,
jboolean visibility) {
VisibilityChangeExpectation visibility_expectation; VisibilityChangeExpectation visibility_expectation;
visibility_expectation.element_name = visibility_expectation.element_name =
static_cast<UserFriendlyElementName>(element_name); static_cast<UserFriendlyElementName>(element_name);
visibility_expectation.timeout_ms = timeout_ms; visibility_expectation.timeout_ms = timeout_ms;
visibility_expectation.visibility = visibility;
PostToGlThread( PostToGlThread(
FROM_HERE, FROM_HERE,
base::BindOnce( base::BindOnce(
&BrowserRenderer::WatchElementForVisibilityChangeForTesting, &BrowserRenderer::WatchElementForVisibilityStatusForTesting,
gl_thread_->GetBrowserRenderer(), visibility_expectation)); gl_thread_->GetBrowserRenderer(), visibility_expectation));
} }
......
...@@ -285,11 +285,12 @@ class VrShell : device::GvrGamepadDataProvider, ...@@ -285,11 +285,12 @@ class VrShell : device::GvrGamepadDataProvider,
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
jstring filepath_base); jstring filepath_base);
void WatchElementForVisibilityChangeForTesting( void WatchElementForVisibilityStatusForTesting(
JNIEnv* env, JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj, const base::android::JavaParamRef<jobject>& obj,
jint element_name, jint element_name,
jint timeout_ms); jint timeout_ms,
jboolean visibility);
void ReportUiOperationResultForTesting(UiTestOperationType action_type, void ReportUiOperationResultForTesting(UiTestOperationType action_type,
UiTestOperationResult result); UiTestOperationResult result);
......
...@@ -231,7 +231,7 @@ void BrowserRenderer::SaveNextFrameBufferToDiskForTesting( ...@@ -231,7 +231,7 @@ void BrowserRenderer::SaveNextFrameBufferToDiskForTesting(
frame_buffer_dump_filepath_base_ = filepath_base; frame_buffer_dump_filepath_base_ = filepath_base;
} }
void BrowserRenderer::WatchElementForVisibilityChangeForTesting( void BrowserRenderer::WatchElementForVisibilityStatusForTesting(
VisibilityChangeExpectation visibility_expectation) { VisibilityChangeExpectation visibility_expectation) {
DCHECK(ui_visibility_state_ == nullptr) << "Attempted to watch a UI element " DCHECK(ui_visibility_state_ == nullptr) << "Attempted to watch a UI element "
"for visibility changes with one " "for visibility changes with one "
...@@ -240,8 +240,7 @@ void BrowserRenderer::WatchElementForVisibilityChangeForTesting( ...@@ -240,8 +240,7 @@ void BrowserRenderer::WatchElementForVisibilityChangeForTesting(
ui_visibility_state_->timeout_ms = ui_visibility_state_->timeout_ms =
base::TimeDelta::FromMilliseconds(visibility_expectation.timeout_ms); base::TimeDelta::FromMilliseconds(visibility_expectation.timeout_ms);
ui_visibility_state_->element_to_watch = visibility_expectation.element_name; ui_visibility_state_->element_to_watch = visibility_expectation.element_name;
ui_visibility_state_->initially_visible = ui_->GetElementVisibilityForTesting( ui_visibility_state_->expected_visibile = visibility_expectation.visibility;
ui_visibility_state_->element_to_watch);
} }
void BrowserRenderer::AcceptDoffPromptForTesting() { void BrowserRenderer::AcceptDoffPromptForTesting() {
...@@ -405,13 +404,13 @@ void BrowserRenderer::ReportElementVisibilityStatusForTesting( ...@@ -405,13 +404,13 @@ void BrowserRenderer::ReportElementVisibilityStatusForTesting(
base::TimeDelta time_since_start = base::TimeDelta time_since_start =
current_time - ui_visibility_state_->start_time; current_time - ui_visibility_state_->start_time;
if (ui_->GetElementVisibilityForTesting( if (ui_->GetElementVisibilityForTesting(
ui_visibility_state_->element_to_watch) != ui_visibility_state_->element_to_watch) ==
ui_visibility_state_->initially_visible) { ui_visibility_state_->expected_visibile) {
ReportElementVisibilityResultForTesting( ReportElementVisibilityResultForTesting(
UiTestOperationResult::kVisibilityChange); UiTestOperationResult::kVisibilityMatch);
} else if (time_since_start > ui_visibility_state_->timeout_ms) { } else if (time_since_start > ui_visibility_state_->timeout_ms) {
ReportElementVisibilityResultForTesting( ReportElementVisibilityResultForTesting(
UiTestOperationResult::kTimeoutNoChange); UiTestOperationResult::kTimeoutNoVisibilityMatch);
} }
} }
...@@ -419,7 +418,7 @@ void BrowserRenderer::ReportElementVisibilityResultForTesting( ...@@ -419,7 +418,7 @@ void BrowserRenderer::ReportElementVisibilityResultForTesting(
UiTestOperationResult result) { UiTestOperationResult result) {
ui_visibility_state_ = nullptr; ui_visibility_state_ = nullptr;
browser_->ReportUiOperationResultForTesting( browser_->ReportUiOperationResultForTesting(
UiTestOperationType::kElementVisibilityChange, result); UiTestOperationType::kElementVisibilityStatus, result);
} }
} // namespace vr } // namespace vr
...@@ -74,7 +74,7 @@ class VR_EXPORT BrowserRenderer : public SchedulerBrowserRendererInterface { ...@@ -74,7 +74,7 @@ class VR_EXPORT BrowserRenderer : public SchedulerBrowserRendererInterface {
void SetUiExpectingActivityForTesting( void SetUiExpectingActivityForTesting(
UiTestActivityExpectation ui_expectation); UiTestActivityExpectation ui_expectation);
void SaveNextFrameBufferToDiskForTesting(std::string filepath_base); void SaveNextFrameBufferToDiskForTesting(std::string filepath_base);
void WatchElementForVisibilityChangeForTesting( void WatchElementForVisibilityStatusForTesting(
VisibilityChangeExpectation visibility_expectation); VisibilityChangeExpectation visibility_expectation);
void AcceptDoffPromptForTesting(); void AcceptDoffPromptForTesting();
void ConnectPresentingService( void ConnectPresentingService(
......
...@@ -77,6 +77,12 @@ UiElementName UserFriendlyElementNameToUiElementName( ...@@ -77,6 +77,12 @@ UiElementName UserFriendlyElementNameToUiElementName(
return kOmniboxTextField; return kOmniboxTextField;
case UserFriendlyElementName::kOmniboxCloseButton: case UserFriendlyElementName::kOmniboxCloseButton:
return kOmniboxCloseButton; return kOmniboxCloseButton;
case UserFriendlyElementName::kAppButtonExitToast:
return kWebVrExclusiveScreenToast;
case UserFriendlyElementName::kWebXrAudioIndicator:
return kWebVrAudioCaptureIndicator;
case UserFriendlyElementName::kWebXrHostedContent:
return kWebVrHostedUiContent;
default: default:
NOTREACHED(); NOTREACHED();
return kNone; return kNone;
......
...@@ -30,6 +30,12 @@ enum class UserFriendlyElementName : int { ...@@ -30,6 +30,12 @@ enum class UserFriendlyElementName : int {
kOmniboxTextField, // The Omnibox's text input field that shows up when the kOmniboxTextField, // The Omnibox's text input field that shows up when the
// URL bar is clicked. // URL bar is clicked.
kOmniboxCloseButton, // The button the exits the omnibox's text input mode. kOmniboxCloseButton, // The button the exits the omnibox's text input mode.
kAppButtonExitToast, // The "Press app button to exit" toast when entring
// an immersive session.
kWebXrAudioIndicator, // Toast in WebXR indicating the microphone permission
// is in use.
kWebXrHostedContent, // Hosted content in a WebXR immersive session, e.g.
// permission prompts.
}; };
// These are the types of actions that Java can request callbacks for once // These are the types of actions that Java can request callbacks for once
...@@ -38,7 +44,8 @@ enum class UserFriendlyElementName : int { ...@@ -38,7 +44,8 @@ enum class UserFriendlyElementName : int {
enum class UiTestOperationType : int { enum class UiTestOperationType : int {
kUiActivityResult = 0, // Result after being told to wait for quiescence kUiActivityResult = 0, // Result after being told to wait for quiescence
kFrameBufferDumped, // Signal that the frame buffer was dumped to disk kFrameBufferDumped, // Signal that the frame buffer was dumped to disk
kElementVisibilityChange, // Signal that a watched element changed visibility kElementVisibilityStatus, // Signal that a watched element matches the
// desired visibility.
kNumUiTestOperationTypes, // Must be last kNumUiTestOperationTypes, // Must be last
}; };
...@@ -49,10 +56,10 @@ enum class UiTestOperationResult : int { ...@@ -49,10 +56,10 @@ enum class UiTestOperationResult : int {
kQuiescent, // The UI reached quiescence (kUiActivityResult) kQuiescent, // The UI reached quiescence (kUiActivityResult)
kTimeoutNoStart, // Timed out, UI activity never started (kUiActivityResult) kTimeoutNoStart, // Timed out, UI activity never started (kUiActivityResult)
kTimeoutNoEnd, // Timed out, UI activity never finished (kUiActivityResult) kTimeoutNoEnd, // Timed out, UI activity never finished (kUiActivityResult)
kVisibilityChange, // The watched element's visibility changed kVisibilityMatch, // The watched element's visibility matches the expectation
// (kElementVisibilityChange) // (kElementVisibilityStatus)
kTimeoutNoChange, // Timed out, visibility never changed kTimeoutNoVisibilityMatch, // Timed out, visibility doesn't match expectation
// (kElementVisibilityChange) // (kElementVisibilityStatus)
}; };
// These are used to specify what type of action should be performed on a UI // These are used to specify what type of action should be performed on a UI
...@@ -100,6 +107,7 @@ struct UiTestActivityExpectation { ...@@ -100,6 +107,7 @@ struct UiTestActivityExpectation {
struct VisibilityChangeExpectation { struct VisibilityChangeExpectation {
UserFriendlyElementName element_name; UserFriendlyElementName element_name;
int timeout_ms; int timeout_ms;
bool visibility;
}; };
// Holds all the information necessary to keep track of and report whether the // Holds all the information necessary to keep track of and report whether the
...@@ -119,8 +127,8 @@ struct UiTestState { ...@@ -119,8 +127,8 @@ struct UiTestState {
struct UiVisibilityState { struct UiVisibilityState {
// The UI element being watched. // The UI element being watched.
UserFriendlyElementName element_to_watch = UserFriendlyElementName::kUrl; UserFriendlyElementName element_to_watch = UserFriendlyElementName::kUrl;
// The initial visibility state of the element. // The desired visibility state of the element.
bool initially_visible = false; bool expected_visibile = false;
// How long to wait for a visibility change before timing out. // How long to wait for a visibility change before timing out.
base::TimeDelta timeout_ms = base::TimeDelta::Min(); base::TimeDelta timeout_ms = base::TimeDelta::Min();
// The point in time that we started watching for visibility changes. // The point in time that we started watching for visibility changes.
......
<!doctype html>
<!--
WebXR page with the ability to request permissions.
-->
<html>
<head>
<link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
</head>
<body>
<canvas id="webgl-canvas"></canvas>
<script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script>
<script src="../resources/webxr_e2e.js"></script>
<script src="../resources/webxr_boilerplate.js"></script>
<script>
var lastPermissionRequestSucceeded = false;
var storedValue = null;
function requestPermission(permissionObject, storeValue) {
navigator.getUserMedia(permissionObject,
(val) => {
lastPermissionRequestSucceeded = true;
if (storeValue) {
storedValue = val;
}
finishJavaScriptStep();
},
() => {
lastPermissionRequestSucceeded = false;
finishJavaScriptStep();
});
}
</script>
</body>
</html>
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