blob: 85d4346d7ff26e77d530da14923d24236af0696b [file] [log] [blame]
// 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("vr/e2e_test_files/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