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

Add VR browser test support

Adds a browser test equivalent of the framework that's
been used in instrumentation tests for VR testing and
ports over a simple WebVR presentation test as a proof
of concept. Additional tests will be ported over in
follow-up CLs.

Bug: 780668
Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: Id944f524e65056c15308a377e5c1260ce73e44ae
Reviewed-on: https://chromium-review.googlesource.com/809804Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Reviewed-by: default avatarBill Orr <billorr@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#524306}
parent 6c3194b5
// Copyright 2017 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.
#include <cstring>
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/vr/test/vr_browser_test.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "url/gurl.h"
namespace vr {
constexpr base::TimeDelta VrBrowserTest::kPollCheckIntervalShort;
constexpr base::TimeDelta VrBrowserTest::kPollCheckIntervalLong;
constexpr base::TimeDelta VrBrowserTest::kPollTimeoutShort;
constexpr base::TimeDelta VrBrowserTest::kPollTimeoutLong;
constexpr char VrBrowserTest::kVrOverrideEnvVar[];
constexpr char VrBrowserTest::kVrOverrideVal[];
constexpr char VrBrowserTest::kVrConfigPathEnvVar[];
constexpr char VrBrowserTest::kVrConfigPathVal[];
constexpr char VrBrowserTest::kVrLogPathEnvVar[];
constexpr char VrBrowserTest::kVrLogPathVal[];
VrBrowserTest::VrBrowserTest() : env_(base::Environment::Create()) {}
VrBrowserTest::~VrBrowserTest() = default;
// We need an std::string that is an absolute file path, which requires
// platform-specific logic since Windows uses std::wstring instead of
// std::string for FilePaths, but SetVar only accepts std::string
#ifdef OS_WIN
#define MAKE_ABSOLUTE(x) \
base::WideToUTF8( \
base::MakeAbsoluteFilePath(base::FilePath(base::UTF8ToWide(x))).value())
#else
#define MAKE_ABSOLUTE(x) base::MakeAbsoluteFilePath(base::FilePath(x)).value()
#endif
void VrBrowserTest::SetUp() {
// Set the environment variable to use the mock OpenVR client
EXPECT_TRUE(env_->SetVar(kVrOverrideEnvVar, MAKE_ABSOLUTE(kVrOverrideVal)));
EXPECT_TRUE(
env_->SetVar(kVrConfigPathEnvVar, MAKE_ABSOLUTE(kVrConfigPathVal)));
EXPECT_TRUE(env_->SetVar(kVrLogPathEnvVar, MAKE_ABSOLUTE(kVrLogPathVal)));
// Make sure the WebVR, Gamepad, and OpenVR features are enabled
base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kEnableWebVR);
scoped_feature_list_.InitWithFeatures(
{features::kOpenVR, features::kGamepadExtensions}, {});
InProcessBrowserTest::SetUp();
}
GURL VrBrowserTest::GetHtmlTestFile(const std::string& test_name) {
return ui_test_utils::GetTestUrl(
base::FilePath(FILE_PATH_LITERAL("android/webvr_instrumentation/html")),
#ifdef OS_WIN
base::FilePath(base::UTF8ToWide(test_name + ".html")
#else
base::FilePath(test_name + ".html")
#endif
));
}
content::WebContents* VrBrowserTest::GetFirstTabWebContents() {
return browser()->tab_strip_model()->GetWebContentsAt(0);
}
void VrBrowserTest::LoadUrlAndAwaitInitialization(const GURL& url) {
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_TRUE(PollJavaScriptBoolean(
"isInitializationComplete()", kPollTimeoutShort,
browser()->tab_strip_model()->GetActiveWebContents()))
<< "Timed out waiting for JavaScript test initialization.";
}
bool VrBrowserTest::VrDisplayFound(content::WebContents* web_contents) {
return RunJavaScriptAndExtractBoolOrFail("vrDisplay != null", web_contents);
}
VrBrowserTest::TestStatus VrBrowserTest::CheckTestStatus(
content::WebContents* web_contents) {
std::string result_string =
RunJavaScriptAndExtractStringOrFail("resultString", web_contents);
bool test_passed =
RunJavaScriptAndExtractBoolOrFail("testPassed", web_contents);
if (test_passed) {
return VrBrowserTest::TestStatus::STATUS_PASSED;
} else if (!test_passed && result_string == "") {
return VrBrowserTest::TestStatus::STATUS_RUNNING;
} else {
// !test_passed && result_string != ""
return VrBrowserTest::TestStatus::STATUS_FAILED;
}
}
void VrBrowserTest::EndTest(content::WebContents* web_contents) {
switch (CheckTestStatus(web_contents)) {
case VrBrowserTest::TestStatus::STATUS_PASSED:
break;
case VrBrowserTest::TestStatus::STATUS_FAILED:
FAIL() << "JavaScript testharness failed with result: "
<< RunJavaScriptAndExtractStringOrFail("resultString",
web_contents);
break;
case VrBrowserTest::TestStatus::STATUS_RUNNING:
FAIL() << "Attempted to end test in C++ without finishing in JavaScript";
break;
default:
FAIL() << "Received unknown test status.";
}
}
bool VrBrowserTest::PollJavaScriptBoolean(const std::string& bool_expression,
const base::TimeDelta& timeout,
content::WebContents* web_contents) {
return BlockOnConditionUnsafe(
base::BindRepeating(RunJavaScriptAndExtractBoolOrFail, bool_expression,
web_contents),
timeout);
}
void VrBrowserTest::WaitOnJavaScriptStep(content::WebContents* web_contents) {
// Make sure we aren't trying to wait on a JavaScript test step without the
// code to do so.
bool code_available = RunJavaScriptAndExtractBoolOrFail(
"typeof javascriptDone !== 'undefined'", web_contents);
EXPECT_TRUE(code_available) << "Attempted to wait on a JavaScript test step "
<< "without the code to do so. You either forgot "
<< "to import webvr_e2e.js or "
<< "are incorrectly using a C++ function.";
// Actually wait for the step to finish
bool success =
PollJavaScriptBoolean("javascriptDone", kPollTimeoutLong, web_contents);
// Check what state we're in to make sure javascriptDone wasn't called
// because the test failed.
VrBrowserTest::TestStatus test_status = CheckTestStatus(web_contents);
if (!success || test_status == VrBrowserTest::TestStatus::STATUS_FAILED) {
// Failure states: Either polling failed or polling succeeded, but because
// the test failed.
std::string reason;
if (!success) {
reason = "Polling JavaScript boolean javascriptDone timed out.";
} else {
reason =
"Polling JavaScript boolean javascriptDone succeeded, but test "
"failed.";
}
std::string result_string =
RunJavaScriptAndExtractStringOrFail("resultString", web_contents);
if (result_string == "") {
reason += " Did not obtain specific reason from testharness.";
} else {
reason += " Testharness reported failure: " + result_string;
}
FAIL() << reason;
}
// Reset the synchronization boolean
EXPECT_TRUE(content::ExecuteScript(web_contents, "javascriptDone = false"));
}
void VrBrowserTest::ExecuteStepAndWait(const std::string& step_function,
content::WebContents* web_contents) {
EXPECT_TRUE(content::ExecuteScript(web_contents, step_function));
WaitOnJavaScriptStep(web_contents);
}
bool VrBrowserTest::BlockOnConditionUnsafe(
base::RepeatingCallback<bool()> condition,
const base::TimeDelta& timeout,
const base::TimeDelta& period) {
base::Time start = base::Time::Now();
bool successful = false;
while (base::Time::Now() - start < timeout) {
successful = condition.Run();
if (successful) {
break;
}
base::PlatformThread::Sleep(period);
}
return successful;
}
bool VrBrowserTest::RunJavaScriptAndExtractBoolOrFail(
const std::string& js_expression,
content::WebContents* web_contents) {
bool result;
EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
web_contents,
"window.domAutomationController.send(" + js_expression + ")", &result));
return result;
}
std::string VrBrowserTest::RunJavaScriptAndExtractStringOrFail(
const std::string& js_expression,
content::WebContents* web_contents) {
std::string result;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
web_contents,
"window.domAutomationController.send(" + js_expression + ")", &result));
return result;
}
} // namespace vr
// Copyright 2017 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.
#ifndef CHROME_BROWSER_VR_TEST_VR_BROWSER_TEST_H_
#define CHROME_BROWSER_VR_TEST_VR_BROWSER_TEST_H_
#include "base/callback.h"
#include "base/environment.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/web_contents.h"
#include "url/gurl.h"
#define REQUIRES_GPU(x) DISABLED_##x
namespace vr {
// Base browser test class for running VR-related tests.
// This is essentially a C++ port of the way Android does similar tests in
// //chrome/android/javatests/src/.../browser/vr_shell/VrTestFramework.java
class VrBrowserTest : public InProcessBrowserTest {
public:
static constexpr base::TimeDelta kPollCheckIntervalShort =
base::TimeDelta::FromMilliseconds(50);
static constexpr base::TimeDelta kPollCheckIntervalLong =
base::TimeDelta::FromMilliseconds(100);
static constexpr base::TimeDelta kPollTimeoutShort =
base::TimeDelta::FromMilliseconds(1000);
static constexpr base::TimeDelta kPollTimeoutLong =
base::TimeDelta::FromMilliseconds(10000);
static constexpr char kVrOverrideEnvVar[] = "VR_OVERRIDE";
static constexpr char kVrOverrideVal[] = "./mock_vr_clients/";
static constexpr char kVrConfigPathEnvVar[] = "VR_CONFIG_PATH";
static constexpr char kVrConfigPathVal[] = "./";
static constexpr char kVrLogPathEnvVar[] = "VR_LOG_PATH";
static constexpr char kVrLogPathVal[] = "./";
enum class TestStatus {
STATUS_RUNNING = 0,
STATUS_PASSED = 1,
STATUS_FAILED = 2
};
VrBrowserTest();
~VrBrowserTest() override;
void SetUp() override;
// Returns a GURL to the VR test HTML file of the given name, e.g.
// GetHtmlTestFile("foo") returns a GURL for the foo.html file in the VR
// test HTML directory.
GURL GetHtmlTestFile(const std::string& test_name);
// Convenience function for accessing the WebContents belonging to the first
// tab open in the browser.
content::WebContents* GetFirstTabWebContents();
// Loads the given GURL and blocks until the JavaScript on the page has
// signalled that pre-test initialization is complete.
void LoadUrlAndAwaitInitialization(const GURL& url);
// Returns true if the JavaScript in the given WebContents has found a
// WebVR VRDisplay, false otherwise.
static bool VrDisplayFound(content::WebContents* web_contents);
// Retrieves the current status of the JavaScript test and returns an enum
// corresponding to it.
static TestStatus CheckTestStatus(content::WebContents* web_contents);
// Asserts that the JavaScript test code completed successfully.
static void EndTest(content::WebContents* web_contents);
// Blocks until the given JavaScript expression evaluates to true or the
// timeout is reached. Returns true if the expression evaluated to true or
// false on timeout.
static bool PollJavaScriptBoolean(const std::string& bool_expression,
const base::TimeDelta& timeout,
content::WebContents* web_contents);
// Blocks until the JavaScript in the given WebContents signals that it is
// finished.
static void WaitOnJavaScriptStep(content::WebContents* web_contents);
// Executes the given step/JavaScript expression and blocks until JavaScript
// signals that it is finished.
static void ExecuteStepAndWait(const std::string& step_function,
content::WebContents* web_contents);
// Blocks until the given callback returns true or the timeout is reached.
// Returns true if the condition successfully resolved or false on timeout.
// This is unsafe because it relies on the provided callback checking a result
// from a different thread. This isn't an issue when blocking on some
// JavaScript condition to be true, but could be problematic if forced into
// use elsewhere.
static bool BlockOnConditionUnsafe(
base::RepeatingCallback<bool()> condition,
const base::TimeDelta& timeout = kPollTimeoutLong,
const base::TimeDelta& period = kPollCheckIntervalLong);
// Convenience function for ensuring ExecuteScriptAndExtractBool runs
// successfully and for directly getting the result instead of needing to pass
// a pointer to be filled.
static bool RunJavaScriptAndExtractBoolOrFail(
const std::string& js_expression,
content::WebContents* web_contents);
// Convenience function for ensuring ExecuteScripteAndExtractString runs
// successfully and for directly getting the result instead of needing to pass
// a pointer to be filled.
static std::string RunJavaScriptAndExtractStringOrFail(
const std::string& js_expression,
content::WebContents* web_contents);
protected:
std::unique_ptr<base::Environment> env_;
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(VrBrowserTest);
};
} // namespace vr
#endif // CHROME_BROWSER_VR_TEST_VR_BROWSER_TEST_H_
// Copyright 2017 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.
#include "chrome/browser/vr/test/vr_transition_utils.h"
#include "chrome/browser/vr/test/vr_browser_test.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
namespace vr {
void enterPresentation(content::WebContents* web_contents) {
// ExecuteScript runs with a user gesture, so we can just directly call
// requestPresent instead of having to do the hacky workaround the
// instrumentation tests use of actually sending a click event to the canvas.
EXPECT_TRUE(content::ExecuteScript(web_contents, "onVrRequestPresent()"));
}
void enterPresentationAndWait(content::WebContents* web_contents) {
enterPresentation(web_contents);
VrBrowserTest::WaitOnJavaScriptStep(web_contents);
}
void enterPresentationOrFail(content::WebContents* web_contents) {
enterPresentation(web_contents);
EXPECT_TRUE(VrBrowserTest::PollJavaScriptBoolean(
"vrDisplay.isPresenting", VrBrowserTest::kPollTimeoutLong, web_contents));
}
} // namespace vr
// Copyright 2017 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.
#ifndef CHROME_BROWSER_VR_TEST_VR_TRANSITION_UTILS_H_
#define CHROME_BROWSER_VR_TEST_VR_TRANSITION_UTILS_H_
#include "content/public/browser/web_contents.h"
namespace vr {
void enterPresentation(content::WebContents* web_contents);
void enterPresentationAndWait(content::WebContents* web_contents);
void enterPresentationOrFail(content::WebContents* web_contents);
} // namespace vr
#endif // CHROME_BROWSER_VR_TEST_VR_TRANSITION_UTILS_H_
// Copyright 2017 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.
#include "chrome/browser/vr/test/vr_browser_test.h"
#include "chrome/browser/vr/test/vr_transition_utils.h"
#include "content/public/test/browser_test_utils.h"
// Browser test equivalent of
// chrome/android/javatests/src/.../browser/vr_shell/WebVrTransitionTest.java.
// End-to-end tests for transitioning between WebVR's magic window and
// presentation modes.
namespace vr {
// Tests that a successful requestPresent call actually enters VR
IN_PROC_BROWSER_TEST_F(VrBrowserTest, REQUIRES_GPU(TestPresentation)) {
LoadUrlAndAwaitInitialization(
GetHtmlTestFile("test_requestPresent_enters_vr"));
enterPresentationAndWait(GetFirstTabWebContents());
EXPECT_TRUE(RunJavaScriptAndExtractBoolOrFail("vrDisplay.isPresenting",
GetFirstTabWebContents()))
<< "Was not presenting after requestPresent";
EndTest(GetFirstTabWebContents());
}
} // namespace vr
......@@ -363,7 +363,12 @@ test("browser_tests") {
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
if (is_win) {
data += [ "$root_out_dir/chrome_200_percent.pak" ]
data += [
"$root_out_dir/chrome_200_percent.pak",
# Needed for VR browser tests
"//third_party/WebKit/LayoutTests/resources/testharness.js",
]
deps += [ "//chrome/app:chrome_dll_resources" ]
}
if (is_chromeos) {
......@@ -1830,7 +1835,14 @@ test("browser_tests") {
"../browser/ui/views/accessibility/invert_bubble_view_browsertest.cc",
"../browser/ui/views/settings_reset_prompt_dialog_browsertest.cc",
"../browser/ui/views/uninstall_view_browsertest.cc",
"../browser/vr/test/vr_browser_test.cc",
"../browser/vr/test/vr_browser_test.h",
"../browser/vr/test/vr_transition_utils.cc",
"../browser/vr/test/vr_transition_utils.h",
"../browser/vr/webvr_transition_browser_test.cc",
]
data_deps += [ "../../device/vr:openvr_mock" ]
}
if (is_mac || is_win) {
sources += [
......
......@@ -1677,6 +1677,42 @@ COMMON_GTESTS = {
],
'test': 'video_decode_accelerator_unittest',
},
'vr_browser_tests': {
'tester_configs': [
{
'predicate': Predicates.FYI_ONLY,
'os_types': ['win']
},
],
# We currently only want to run this on Win10 Release (NVIDIA)
'disabled_tester_configs': [
{
'names': [
'Win7 Release (NVIDIA)',
'Win7 Debug (NVIDIA)',
'Win7 dEQP Release (NVIDIA)',
'Win7 Experimental Release (NVIDIA)',
'Win10 Debug (NVIDIA)',
'Win7 Release (AMD)',
'Win7 Debug (AMD)',
'Win7 dEQP Release (AMD)',
'Win10 Release (Intel HD 630)',
'Win10 Release (NVIDIA Quadro P400)',
'Win7 x64 Release (NVIDIA)',
'Win7 x64 Debug (NVIDIA)',
'Win7 x64 dEQP Release (NVIDIA)',
],
}
],
'args': [
'--enable-gpu',
'--test-launcher-bot-mode',
'--test-launcher-jobs=1',
'--gtest_filter=VrBrowserTest.*',
'--gtest_also_run_disabled_tests',
],
'test': 'browser_tests',
}
}
# This requires a hack because the isolate's name is different than
......
......@@ -70,7 +70,9 @@ if (enable_vr) {
"//third_party/WebKit/public:blink_headers",
]
ldflags = [ "-landroid" ]
libs = [ "//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a" ]
libs = [
"//third_party/gvr-android-sdk/libgvr_shim_static_${current_cpu}.a",
]
configs += [ "//third_party/gvr-android-sdk:libgvr_config" ]
}
......
......@@ -13083,6 +13083,28 @@
"test": "browser_tests",
"use_xvfb": false
},
{
"args": [
"--enable-gpu",
"--test-launcher-bot-mode",
"--test-launcher-jobs=1",
"--gtest_filter=VrBrowserTest.*",
"--gtest_also_run_disabled_tests"
],
"name": "vr_browser_tests",
"swarming": {
"can_use_on_swarming_builders": true,
"dimension_sets": [
{
"gpu": "10de:1cb3-23.21.13.8792",
"os": "Windows-10",
"pool": "Chrome-GPU"
}
]
},
"test": "browser_tests",
"use_xvfb": false
},
{
"args": [
"--use-gpu-in-tests",
......
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