Commit edb91802 authored by Bill Orr's avatar Bill Orr Committed by Commit Bot

Add an interface to communicate between the fake openvr and tests

This change creates a way for tests to control the behavior of
the fake OpenVR, and allows devices to send information up to
tests.

BUG=801034

Change-Id: Ie0fe035ada9553114c054717f52788d375d5c62f
Reviewed-on: https://chromium-review.googlesource.com/1100157
Commit-Queue: Bill Orr <billorr@chromium.org>
Reviewed-by: default avatarBrian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567725}
parent a0190fd8
include_rules = [
"+mojo/edk/embedder",
"+device/vr/openvr/test/test_hook.h",
"+device/vr/openvr/openvr_device_provider.h"
]
\ No newline at end of file
// Copyright 2018 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/mock_openvr_device_hook_base.h"
MockOpenVRBase::MockOpenVRBase() {
device::OpenVRDeviceProvider::SetTestHook(this);
}
MockOpenVRBase::~MockOpenVRBase() {
device::OpenVRDeviceProvider::SetTestHook(nullptr);
}
void MockOpenVRBase::OnFrameSubmitted(device::SubmittedFrameData frame_data) {}
// Copyright 2018 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_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
#define CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
#include "device/vr/openvr/openvr_device_provider.h"
#include "device/vr/openvr/test/test_hook.h"
class MockOpenVRBase : public device::OpenVRTestHook {
public:
MockOpenVRBase();
virtual ~MockOpenVRBase();
// OpenVRTestHook
void OnFrameSubmitted(device::SubmittedFrameData frame_data) override;
};
#endif // CHROME_BROWSER_VR_TEST_MOCK_OPENVR_DEVICE_HOOK_BASE_H_
......@@ -54,7 +54,7 @@ VrXrBrowserTestBase::~VrXrBrowserTestBase() = default;
#endif
void VrXrBrowserTestBase::SetUp() {
// Set the environment variable to use the mock OpenVR client
// 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)));
......
......@@ -4,32 +4,53 @@
#include "base/environment.h"
#include "base/files/file.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/vr/test/mock_openvr_device_hook_base.h"
#include "chrome/browser/vr/test/vr_browser_test.h"
#include "chrome/browser/vr/test/vr_xr_browser_test.h"
#include "chrome/browser/vr/test/xr_browser_test.h"
#include "device/vr/openvr/test/fake_openvr_log.h"
#include <memory>
namespace vr {
class MyOpenVRMock : public MockOpenVRBase {
public:
void OnFrameSubmitted(device::SubmittedFrameData frame_data) final;
void WaitForFrame() {
DCHECK(!wait_loop_);
if (num_submitted_frames_ > 0)
return;
wait_loop_ = new base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed);
wait_loop_->Run();
delete wait_loop_;
wait_loop_ = nullptr;
}
device::Color last_submitted_color_ = {};
unsigned int num_submitted_frames_ = 0;
private:
base::RunLoop* wait_loop_ = nullptr;
};
void MyOpenVRMock::OnFrameSubmitted(device::SubmittedFrameData frame_data) {
last_submitted_color_ = frame_data.color;
num_submitted_frames_++;
if (wait_loop_) {
wait_loop_->Quit();
}
}
// Pixel test for WebVR/WebXR - start presentation, submit frames, get data back
// out. Validates that a pixel was rendered with the expected color.
void TestPresentationPixelsImpl(VrXrBrowserTestBase* t, std::string filename) {
// Set up environment variable to tell mock device to save pixel logs.
std::unique_ptr<base::Environment> env = base::Environment::Create();
base::ScopedTempDir temp_dir;
base::FilePath log_path;
{
base::ScopedAllowBlockingForTesting allow_files;
EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
log_path = temp_dir.GetPath().Append(FILE_PATH_LITERAL("VRPixelTest.Log"));
EXPECT_TRUE(log_path.MaybeAsASCII() != "") << "Temp dir is non-ascii";
env->SetVar(GetVrPixelLogEnvVarName(), log_path.MaybeAsASCII());
}
MyOpenVRMock my_mock;
// Load the test page, and enter presentation.
t->LoadUrlAndAwaitInitialization(t->GetHtmlTestFile(filename));
......@@ -44,38 +65,13 @@ void TestPresentationPixelsImpl(VrXrBrowserTestBase* t, std::string filename) {
t->ExecuteStepAndWait("finishTest()", t->GetFirstTabWebContents());
t->EndTest(t->GetFirstTabWebContents());
// Try to open the log file.
{
base::ScopedAllowBlockingForTesting allow_files;
std::unique_ptr<base::File> file;
base::Time start = base::Time::Now();
while (!file) {
file = std::make_unique<base::File>(
log_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_DELETE_ON_CLOSE);
if (!file->IsValid()) {
file = nullptr;
}
if (base::Time::Now() - start > t->kPollTimeoutLong)
break;
}
EXPECT_TRUE(file);
// Now parse the log to validate that we ran correctly.
VRSubmittedFrameEvent event;
int read =
file->ReadAtCurrentPos(reinterpret_cast<char*>(&event), sizeof(event));
EXPECT_EQ(read, static_cast<int>(sizeof(event)));
VRSubmittedFrameEvent::Color expected = {0, 0, 255, 255};
EXPECT_EQ(expected.r, event.color.r);
EXPECT_EQ(expected.g, event.color.g);
EXPECT_EQ(expected.b, event.color.b);
EXPECT_EQ(expected.a, event.color.a);
file = nullptr; // Make sure we destroy this before allow_files.
EXPECT_TRUE(temp_dir.Delete());
}
my_mock.WaitForFrame();
device::Color expected = {0, 0, 255, 255};
EXPECT_EQ(expected.r, my_mock.last_submitted_color_.r);
EXPECT_EQ(expected.g, my_mock.last_submitted_color_.g);
EXPECT_EQ(expected.b, my_mock.last_submitted_color_.b);
EXPECT_EQ(expected.a, my_mock.last_submitted_color_.a);
}
IN_PROC_BROWSER_TEST_F(VrBrowserTestStandard,
......
......@@ -1933,6 +1933,8 @@ 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/mock_openvr_device_hook_base.cc",
"../browser/vr/test/mock_openvr_device_hook_base.h",
"../browser/vr/test/vr_browser_test.cc",
"../browser/vr/test/vr_browser_test.h",
"../browser/vr/test/vr_xr_browser_test.cc",
......@@ -1947,6 +1949,7 @@ test("browser_tests") {
deps += [
"//chrome:other_version",
"//chrome/app:command_ids",
"//device/vr",
"//third_party/wtl",
"//ui/resources",
]
......
......@@ -100,6 +100,7 @@ if (enable_vr) {
"openvr/openvr_render_loop.h",
"openvr/openvr_type_converters.cc",
"openvr/openvr_type_converters.h",
"openvr/test/test_hook.h",
]
}
......@@ -190,7 +191,9 @@ if (enable_openvr) {
sources = [
"openvr/test/fake_openvr_impl_api.cc",
"openvr/test/fake_openvr_log.h",
"openvr/test/test_helper.cc",
"openvr/test/test_helper.h",
"openvr/test/test_hook.h",
]
libs = [
......
......@@ -8,8 +8,17 @@
#include "device/gamepad/gamepad_data_fetcher_manager.h"
#include "device/vr/openvr/openvr_device.h"
#include "device/vr/openvr/openvr_gamepad_data_fetcher.h"
#include "device/vr/openvr/test/test_hook.h"
#include "third_party/openvr/src/headers/openvr.h"
#include "base/command_line.h"
namespace {
// Use the pattern established in content_switches.h, but don't add a content
// dependency -- device shouldn't have one.
constexpr char kTestType[] = "test-type";
} // namespace
namespace device {
void OpenVRDeviceProvider::RecordRuntimeAvailability() {
......@@ -33,7 +42,13 @@ OpenVRDeviceProvider::~OpenVRDeviceProvider() {
device_ = nullptr;
}
if (test_hook_registration_s) {
DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(kTestType));
test_hook_registration_s->SetTestHook(nullptr);
}
vr::VR_Shutdown();
test_hook_registration_s = nullptr;
}
void OpenVRDeviceProvider::Initialize(
......@@ -47,6 +62,17 @@ void OpenVRDeviceProvider::Initialize(
std::move(initialization_complete).Run();
}
void OpenVRDeviceProvider::SetTestHook(OpenVRTestHook* test_hook) {
DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(kTestType));
test_hook_s = test_hook;
if (test_hook_registration_s) {
test_hook_registration_s->SetTestHook(test_hook_s);
}
}
OpenVRTestHook* OpenVRDeviceProvider::test_hook_s = nullptr;
TestHookRegistration* OpenVRDeviceProvider::test_hook_registration_s = nullptr;
void OpenVRDeviceProvider::CreateDevice() {
if (!vr::VR_IsRuntimeInstalled() || !vr::VR_IsHmdPresent())
return;
......@@ -55,6 +81,16 @@ void OpenVRDeviceProvider::CreateDevice() {
vr::IVRSystem* vr_system =
vr::VR_Init(&init_error, vr::EVRApplicationType::VRApplication_Scene);
if (base::CommandLine::ForCurrentProcess()->HasSwitch(kTestType)) {
// Allow our mock implementation of OpenVR to be controlled by tests.
vr::EVRInitError eError;
test_hook_registration_s = reinterpret_cast<TestHookRegistration*>(
vr::VR_GetGenericInterface(kChromeOpenVRTestHookAPI, &eError));
if (test_hook_registration_s) {
test_hook_registration_s->SetTestHook(test_hook_s);
}
}
if (init_error != vr::VRInitError_None) {
LOG(ERROR) << vr::VR_GetVRInitErrorAsEnglishDescription(init_error);
return;
......
......@@ -15,6 +15,8 @@
namespace device {
class OpenVRDevice;
class OpenVRTestHook;
class TestHookRegistration;
class DEVICE_VR_EXPORT OpenVRDeviceProvider : public VRDeviceProvider {
public:
......@@ -31,11 +33,15 @@ class DEVICE_VR_EXPORT OpenVRDeviceProvider : public VRDeviceProvider {
static void RecordRuntimeAvailability();
static void SetTestHook(OpenVRTestHook*);
private:
void CreateDevice();
std::unique_ptr<OpenVRDevice> device_;
bool initialized_ = false;
static OpenVRTestHook* test_hook_s;
static TestHookRegistration* test_hook_registration_s;
DISALLOW_COPY_AND_ASSIGN(OpenVRDeviceProvider);
};
......
......@@ -2,13 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/debugger.h"
#include "base/environment.h"
#include "base/files/file.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "device/vr/openvr/test/fake_openvr_log.h"
#include "device/vr/openvr/test/test_helper.h"
#include "device/vr/openvr/test/test_hook.h"
#include "third_party/openvr/src/headers/openvr.h"
#include "third_party/openvr/src/src/ivrclientcore.h"
......@@ -19,94 +16,6 @@
namespace vr {
class TestVRLogger {
public:
void Start() {
// Look for environment variable saying we should log data.
std::unique_ptr<base::Environment> env = base::Environment::Create();
std::string log_filename;
if (env->GetVar(GetVrPixelLogEnvVarName(), &log_filename)) {
base::ScopedAllowBlockingForTesting allow_files;
log_file_ = std::make_unique<base::File>(
base::FilePath::FromUTF8Unsafe(log_filename),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE);
logging_ = log_file_->IsValid();
}
}
void Stop() {
base::ScopedAllowBlockingForTesting allow_files;
if (logging_) {
log_file_->Flush();
log_file_->Close();
log_file_ = nullptr;
}
logging_ = false;
}
void OnPresentedFrame(ID3D11Texture2D* texture) {
if (!logging_)
return;
VRSubmittedFrameEvent frame;
Microsoft::WRL::ComPtr<ID3D11Device> device;
texture->GetDevice(&device);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(&context);
// We copy the submitted texture to a new texture, so we can map it, and
// read back pixel data.
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_copy;
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
desc.Width = 1;
desc.Height = 1;
desc.MiscFlags = 0;
desc.BindFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
HRESULT hr = device->CreateTexture2D(&desc, nullptr, &texture_copy);
if (FAILED(hr)) {
// We'll have invalid data in log (no frame data) so test will fail.
Stop();
return;
}
D3D11_BOX box = {0, 0, 0, 1, 1, 1}; // a 1-pixel box
context->CopySubresourceRegion(texture_copy.Get(), 0, 0, 0, 0, texture, 0,
&box);
D3D11_MAPPED_SUBRESOURCE map_data = {};
hr = context->Map(texture_copy.Get(), 0, D3D11_MAP_READ, 0, &map_data);
if (FAILED(hr)) {
// We'll have invalid data in log (no frame data) so test will fail.
Stop();
return;
}
VRSubmittedFrameEvent::Color* data =
reinterpret_cast<VRSubmittedFrameEvent::Color*>(map_data.pData);
frame.color = data[0]; // Save top-left pixel value.
context->Unmap(texture_copy.Get(), 0);
WriteVRSubmittedFrameEvent(&frame);
Stop(); // For now only validate one pixel from first frame.
}
private:
void WriteVRSubmittedFrameEvent(VRSubmittedFrameEvent* frame) {
base::ScopedAllowBlockingForTesting allow_files;
log_file_->WriteAtCurrentPos(reinterpret_cast<char*>(frame),
sizeof(*frame));
}
bool logging_ = false;
std::unique_ptr<base::File> log_file_;
};
class TestVRSystem : public IVRSystem {
public:
void GetRecommendedRenderTargetSize(uint32_t* pnWidth,
......@@ -488,18 +397,16 @@ class TestVRClientCore : public IVRClientCore {
}
};
TestVRLogger g_logger;
TestHelper g_test_helper;
TestVRSystem g_system;
TestVRCompositor g_compositor;
TestVRClientCore g_loader;
EVRInitError TestVRClientCore::Init(EVRApplicationType eApplicationType) {
g_logger.Start();
return VRInitError_None;
}
void TestVRClientCore::Cleanup() {
g_logger.Stop();
}
EVRInitError TestVRClientCore::IsInterfaceVersionValid(
......@@ -511,9 +418,11 @@ void* TestVRClientCore::GetGenericInterface(const char* pchNameAndVersion,
EVRInitError* peError) {
*peError = VRInitError_None;
if (strcmp(pchNameAndVersion, IVRSystem_Version) == 0)
return (IVRSystem*)(&g_system);
return static_cast<IVRSystem*>(&g_system);
if (strcmp(pchNameAndVersion, IVRCompositor_Version) == 0)
return (IVRCompositor*)(&g_compositor);
return static_cast<IVRCompositor*>(&g_compositor);
if (strcmp(pchNameAndVersion, device::kChromeOpenVRTestHookAPI) == 0)
return static_cast<device::TestHookRegistration*>(&g_test_helper);
*peError = VRInitError_Init_InvalidInterface;
return nullptr;
......@@ -666,7 +575,7 @@ EVRCompositorError TestVRCompositor::Submit(EVREye,
Texture_t const* texture,
VRTextureBounds_t const*,
EVRSubmitFlags) {
g_logger.OnPresentedFrame(
g_test_helper.OnPresentedFrame(
reinterpret_cast<ID3D11Texture2D*>(texture->handle));
return VRCompositorError_None;
}
......
// Copyright 2018 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 DEVICE_VR_OPENVR_TEST_FAKE_OPENVR_LOG_H_
#define DEVICE_VR_OPENVR_TEST_FAKE_OPENVR_LOG_H_
namespace vr {
// We log instances of this structure when a frame is submitted.
// Tests can walk the log to validate things ran as expected.
struct VRSubmittedFrameEvent {
struct Color {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} color;
};
inline char* GetVrPixelLogEnvVarName() {
static char ret[] = "VR_MOCK_LOG_PATH";
return ret;
}
} // namespace vr
#endif // DEVICE_VR_OPENVR_TEST_FAKE_OPENVR_LOG_H_
\ No newline at end of file
// Copyright 2018 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 "device/vr/openvr/test/test_helper.h"
#include "base/debug/debugger.h"
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "device/vr/openvr/test/test_hook.h"
#include "third_party/openvr/src/headers/openvr.h"
#include "third_party/openvr/src/src/ivrclientcore.h"
#include <D3D11_1.h>
#include <DXGI1_4.h>
#include <wrl.h>
#include <memory>
namespace vr {
void TestHelper::TestFailure() {
NOTREACHED();
}
void TestHelper::OnPresentedFrame(ID3D11Texture2D* texture) {
if (!test_hook_) {
return;
}
Microsoft::WRL::ComPtr<ID3D11Device> device;
texture->GetDevice(&device);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> context;
device->GetImmediateContext(&context);
// We copy the submitted texture to a new texture, so we can map it, and
// read back pixel data.
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture_copy;
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
desc.Width = 1;
desc.Height = 1;
desc.MiscFlags = 0;
desc.BindFlags = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
HRESULT hr = device->CreateTexture2D(&desc, nullptr, &texture_copy);
if (FAILED(hr)) {
TestFailure();
return;
}
D3D11_BOX box = {0, 0, 0, 1, 1, 1}; // a 1-pixel box
context->CopySubresourceRegion(texture_copy.Get(), 0, 0, 0, 0, texture, 0,
&box);
D3D11_MAPPED_SUBRESOURCE map_data = {};
hr = context->Map(texture_copy.Get(), 0, D3D11_MAP_READ, 0, &map_data);
if (FAILED(hr)) {
TestFailure();
return;
}
// We have a 1-pixel image. Give it to the test hook.
device::Color* color = reinterpret_cast<device::Color*>(map_data.pData);
test_hook_->OnFrameSubmitted(device::SubmittedFrameData{color[0]});
context->Unmap(texture_copy.Get(), 0);
}
void TestHelper::SetTestHook(device::OpenVRTestHook* hook) {
test_hook_ = hook;
}
} // namespace vr
\ No newline at end of file
// Copyright 2018 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 DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
#define DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
#include "device/vr/openvr/test/test_hook.h"
class ID3D11Texture2D;
namespace vr {
class TestHelper : public device::TestHookRegistration {
public:
// Methods called by mock OpenVR APIs.
void OnPresentedFrame(ID3D11Texture2D* texture);
void TestFailure();
// TestHookRegistration
void SetTestHook(device::OpenVRTestHook* hook) final;
private:
device::OpenVRTestHook* test_hook_ = nullptr;
};
} // namespace vr
#endif // DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
\ No newline at end of file
// Copyright 2018 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 DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
#define DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
namespace device {
// Update this string whenever either interface changes.
constexpr char kChromeOpenVRTestHookAPI[] = "ChromeTestHook_1";
struct Color {
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};
struct SubmittedFrameData {
Color color;
};
// Tests may implement this, and register it to control behavior of OpenVR.
class OpenVRTestHook {
public:
virtual void OnFrameSubmitted(SubmittedFrameData frame_data) = 0;
};
class TestHookRegistration {
public:
virtual void SetTestHook(OpenVRTestHook*) = 0;
};
} // namespace device
#endif // DEVICE_VR_OPENVR_TEST_TEST_HOOK_H_
\ No newline at end of file
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