XR Browser Test Details

Test Input Architecture

Overview

When a MockXRDeviceHookBase is constructed, it sets itself as the current test hook in XRServiceTestHook::SetTestHook(). This then causes the provided test hook to be set as the current hook for each runtime. What specifically each runtime does after that differs (covered in each runtime's specific section), but the important part is that the runtimes will start calling into the test via Mojo to get controller and headset data instead of attempting to use the real implementation.

OpenVR

OpenVR is handled by a mock implementation of the API found in //device/vr/openvr/test/fake_openvr_impl_api.cc. This, along with some helper files, are compiled into a standalone DLL defined in the //device/vr:openvr_mock target. This target is compiled alongside the browser tests if OpenVR support is enabled, and several Windows environment variables set during test setup. These environment variables cause OpenVR to load the fake DLL on startup instead of the real one, which both allows tests to provide data that appears to come from OpenVR (as opposed to a wrapper) and prevents the need for having the real OpenVR implementation installed.

When XRServiceTestHook::SetTestHook() is called, it in turn calls OpenVRWrapper::SetTestHook(). This both stores a reference to the provided hook, and if the runtime has already been started with the fake OpenVR DLL, calls TestHelper::SetTestHook(). If the runtime has not yet been started, TestHelper::SetTestHook() will be called during the runtime initialization with the stored hook reference.

In either case, once the hook as been set in TestHelper, the Mojo setup is complete. Anytime the fake OpenVR implementation gets a call that would require fetching data, it calls into TestHelper to retrieve the data. If the hook is set, TestHelper will then use it to call into the test and retrieve the set data. If not, it returns reasonable defaults.

Note Because the fake OpenVR implementation is in a separate DLL, it has a separate heap from the rest of Chrome. Because of this, the Mojo data types returned by the test hook cannot be used directly in the OpenVR implementation, as doing so causes them to be destructed on the wrong heap once they go out of scope. Instead, the data gets copied into duplicate, non-Mojo structs before being passed back to OpenVR.

WMR

WMR is handled by mock implementations of the wrapper classes that surround all calls to the WMR API. The real wrappers are found in //device/vr/windows_mixed_reality/wrappers/ while the mocks are in //device/vr/windows_mixed_reality/wrappers/test/.

When XRServiceTestHook::SetTestHook() is called, it in turn calls MixedRealityDeviceStatics::SetTestHook(). This stores the reference to the hook for later use.

When any of several WMR wrappers are created through the factories in //device/vr/windows_mixed_reality/wrappers/wmr_wrapper_factories.h, they check MixedRealityDeviceStatics::ShouldUseMocks(). The first time this is called, it returns true if the hook has already been set, or false otherwise. Subsequent calls will always return the same value as the first call did. This means that usage of the real and mock wrappers are impossible to accidentally mix. Note This also means that it's currently impossible to switch to or from the mock wrappers during a test after the runtime has been attempted to be started.

When the mock wrappers are in use, any calls made to them that would require data from the headset or controllers calls into the test via the test hook. Since multiple wrapper classes could potentially be using the hook at once (as opposed to OpenVR where all interaction is done through a single class), the hook is actually provided by acquiring a LockedVRTestHook via MixedRealityDeviceStatics::GetLockedTestHook(). This is effectively a reference to the currently set MockXRDeviceHookBase that automatically acquires and releases a static lock on construction and destruction, making usage of the hook thread safe.