Update the validation tests to use utils::WireHelper

This enables running the validation uses using the wire with
the command line flag --use-wire

Bug: dawn:654
Change-Id: I17a642a132c8b6321195ec6869e5f86aebdd1c51
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/38620
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
diff --git a/src/tests/UnittestsMain.cpp b/src/tests/UnittestsMain.cpp
index fb5dfa3..b98a616 100644
--- a/src/tests/UnittestsMain.cpp
+++ b/src/tests/UnittestsMain.cpp
@@ -12,9 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "unittests/validation/ValidationTest.h"
+
 #include <gtest/gtest.h>
 
 int main(int argc, char** argv) {
+    InitDawnValidationTestEnvironment(argc, argv);
     testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
 }
diff --git a/src/tests/unittests/validation/BindGroupValidationTests.cpp b/src/tests/unittests/validation/BindGroupValidationTests.cpp
index 1403d7e..3aa3763 100644
--- a/src/tests/unittests/validation/BindGroupValidationTests.cpp
+++ b/src/tests/unittests/validation/BindGroupValidationTests.cpp
@@ -85,6 +85,7 @@
 
     // Check that nextInChain != nullptr is an error.
     wgpu::ChainedStruct chainedDescriptor;
+    chainedDescriptor.sType = wgpu::SType::ShaderModuleWGSLDescriptor;
     descriptor.nextInChain = &chainedDescriptor;
     ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
 }
diff --git a/src/tests/unittests/validation/BufferValidationTests.cpp b/src/tests/unittests/validation/BufferValidationTests.cpp
index c895b1b..0aa5953 100644
--- a/src/tests/unittests/validation/BufferValidationTests.cpp
+++ b/src/tests/unittests/validation/BufferValidationTests.cpp
@@ -808,6 +808,9 @@
 
 // Test validation of the GetMappedRange parameters
 TEST_F(BufferValidationTest, GetMappedRange_OffsetSizeOOB) {
+    // TODO(crbug.com/dawn/651): Fix failures on the wire.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     // Valid case: full range is ok
     {
         wgpu::Buffer buffer = CreateMapWriteBuffer(8);
diff --git a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
index c3accb2..dd41962 100644
--- a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
+++ b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
@@ -1904,10 +1904,10 @@
 
 class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest {
   protected:
-    wgpu::Device CreateTestDevice() override {
+    WGPUDevice CreateTestDevice() override {
         dawn_native::DeviceDescriptor descriptor;
         descriptor.requiredExtensions = {"texture_compression_bc"};
-        return wgpu::Device::Acquire(adapter.CreateDevice(&descriptor));
+        return adapter.CreateDevice(&descriptor);
     }
 
     wgpu::Texture Create2DTexture(wgpu::TextureFormat format,
diff --git a/src/tests/unittests/validation/ErrorScopeValidationTests.cpp b/src/tests/unittests/validation/ErrorScopeValidationTests.cpp
index 2d83caf4a..1778565 100644
--- a/src/tests/unittests/validation/ErrorScopeValidationTests.cpp
+++ b/src/tests/unittests/validation/ErrorScopeValidationTests.cpp
@@ -51,6 +51,7 @@
 
     EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1);
     device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
+    FlushWire();
 }
 
 // Test the simple case where the error scope catches an error.
@@ -63,6 +64,7 @@
 
     EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_Validation, _, this)).Times(1);
     device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
+    FlushWire();
 }
 
 // Test that errors bubble to the parent scope if not handled by the current scope.
@@ -77,11 +79,13 @@
     // OutOfMemory does not match Validation error.
     EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1);
     device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
+    FlushWire();
 
     // Parent validation error scope captures the error.
     EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_Validation, _, this + 1))
         .Times(1);
     device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1);
+    FlushWire();
 }
 
 // Test that if an error scope matches an error, it does not bubble to the parent scope.
@@ -96,11 +100,13 @@
     // Inner scope catches the error.
     EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_Validation, _, this)).Times(1);
     device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
+    FlushWire();
 
     // Parent scope does not see the error.
     EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this + 1))
         .Times(1);
     device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1);
+    FlushWire();
 }
 
 // Test that if no error scope handles an error, it goes to the device UncapturedError callback
@@ -113,6 +119,7 @@
 
     EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1);
     device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this);
+    FlushWire();
 }
 
 // Check that push/popping error scopes must be balanced.
@@ -127,6 +134,7 @@
         EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this + 1))
             .Times(1);
         device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1);
+        FlushWire();
 
         EXPECT_FALSE(device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 2));
     }
@@ -145,6 +153,7 @@
 
     // Side effects of Queue::Submit only are seen after Tick()
     device.Tick();
+    FlushWire();
 }
 
 // Test that parent error scopes do not call their callbacks until after an enclosed Queue::Submit
@@ -164,6 +173,7 @@
 
     // Side effects of Queue::Submit only are seen after Tick()
     device.Tick();
+    FlushWire();
 }
 
 // Test a callback that returns asynchronously followed by a synchronous one
@@ -183,11 +193,15 @@
 
     // Side effects of Queue::Submit only are seen after Tick()
     device.Tick();
+    FlushWire();
 }
 
 // Test that if the device is destroyed before the callback occurs, it is called with NoError
 // because all previous operations are waited upon before the destruction returns.
 TEST_F(ErrorScopeValidationTest, DeviceDestroyedBeforeCallback) {
+    // TODO(crbug.com/dawn/652): This has different behavior on the wire and should be consistent.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::Queue queue = device.GetDefaultQueue();
 
     device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory);
diff --git a/src/tests/unittests/validation/FenceValidationTests.cpp b/src/tests/unittests/validation/FenceValidationTests.cpp
index ef6a32e..0e680b5 100644
--- a/src/tests/unittests/validation/FenceValidationTests.cpp
+++ b/src/tests/unittests/validation/FenceValidationTests.cpp
@@ -93,6 +93,10 @@
 // Test that OnCompletion handlers are called immediately for
 // already completed fence values
 TEST_F(FenceValidationTest, OnCompletionImmediate) {
+    // TODO(crbug.com/dawn/653): This has wrong different behavior on the wire, but fences will be
+    // removed soon.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::FenceDescriptor descriptor;
     descriptor.initialValue = 1;
     wgpu::Fence fence = queue.CreateFence(&descriptor);
@@ -127,6 +131,10 @@
 }
 
 TEST_F(FenceValidationTest, GetCompletedValueInsideCallback) {
+    // TODO(crbug.com/dawn/653): This has wrong different behavior on the wire, but fences will be
+    // removed soon.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::FenceDescriptor descriptor;
     descriptor.initialValue = 1;
     wgpu::Fence fence = queue.CreateFence(&descriptor);
diff --git a/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp b/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp
index dff2b7d..10ba010 100644
--- a/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp
+++ b/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp
@@ -38,6 +38,11 @@
 // Test that GetBindGroupLayout returns the same object for the same index
 // and for matching layouts.
 TEST_F(GetBindGroupLayoutTests, SameObject) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::ShaderModule vsModule = utils::CreateShaderModuleFromWGSL(device, R"(
         [[block]] struct S {
             [[offset(0)]] pos : vec4<f32>;
@@ -86,6 +91,11 @@
 // - shader stage visibility is the stage that adds the binding.
 // - dynamic offsets is false
 TEST_F(GetBindGroupLayoutTests, DefaultShaderStageAndDynamicOffsets) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
         [[block]] struct S {
             [[offset(0)]] pos : vec4<f32>;
@@ -124,6 +134,11 @@
 
 // Test GetBindGroupLayout works with a compute pipeline
 TEST_F(GetBindGroupLayoutTests, ComputePipeline) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::ShaderModule csModule = utils::CreateShaderModuleFromWGSL(device, R"(
         [[block]] struct S {
             [[offset(0)]] pos : vec4<f32>;
@@ -156,6 +171,11 @@
 
 // Test that the binding type matches the shader.
 TEST_F(GetBindGroupLayoutTests, BindingType) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::BindGroupLayoutEntry binding = {};
     binding.binding = 0;
     binding.buffer.hasDynamicOffset = false;
@@ -242,6 +262,11 @@
 
 // Test that texture view dimension matches the shader.
 TEST_F(GetBindGroupLayoutTests, ViewDimension) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::BindGroupLayoutEntry binding = {};
     binding.binding = 0;
     binding.visibility = wgpu::ShaderStage::Fragment;
@@ -314,6 +339,11 @@
 
 // Test that texture component type matches the shader.
 TEST_F(GetBindGroupLayoutTests, TextureComponentType) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::BindGroupLayoutEntry binding = {};
     binding.binding = 0;
     binding.visibility = wgpu::ShaderStage::Fragment;
@@ -355,6 +385,11 @@
 
 // Test that binding= indices match.
 TEST_F(GetBindGroupLayoutTests, BindingIndices) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::BindGroupLayoutEntry binding = {};
     binding.visibility = wgpu::ShaderStage::Fragment;
     binding.buffer.type = wgpu::BufferBindingType::Uniform;
@@ -436,6 +471,11 @@
 
 // Test that minBufferSize is set on the BGL and that the max of the min buffer sizes is used.
 TEST_F(GetBindGroupLayoutTests, MinBufferSize) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::ShaderModule vsModule4 = utils::CreateShaderModuleFromWGSL(device, R"(
         [[block]] struct S {
             [[offset(0)]] pos : f32;
@@ -517,6 +557,11 @@
 
 // Test that the visibility is correctly aggregated if two stages have the exact same binding.
 TEST_F(GetBindGroupLayoutTests, StageAggregation) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::ShaderModule vsModuleNoSampler = utils::CreateShaderModuleFromWGSL(device, R"(
         [[stage(vertex)]] fn main() -> void {
         })");
@@ -687,6 +732,11 @@
 
 // Test that unused indices return the empty bind group layout.
 TEST_F(GetBindGroupLayoutTests, UnusedIndex) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
         [[block]] struct S {
             [[offset(0)]] pos : vec4<f32>;
@@ -712,6 +762,11 @@
 // Test that after explicitly creating a pipeline with a pipeline layout, calling
 // GetBindGroupLayout reflects the same bind group layouts.
 TEST_F(GetBindGroupLayoutTests, Reflection) {
+    // This test works assuming Dawn Native's object deduplication.
+    // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
+    // Native.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     wgpu::BindGroupLayoutEntry binding = {};
     binding.binding = 0;
     binding.buffer.type = wgpu::BufferBindingType::Uniform;
diff --git a/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp b/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
index b69893c..4b09d2e5 100644
--- a/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
+++ b/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
@@ -391,6 +391,10 @@
 
 // Check two layouts with different minimum size are unequal
 TEST_F(MinBufferSizeBindGroupCreationTests, LayoutEquality) {
+    // Returning the same pointer is an implementation detail of Dawn Native.
+    // It is not the same semantic with the Wire.
+    DAWN_SKIP_TEST_IF(UsesWire());
+
     auto MakeLayout = [&](uint64_t size) {
         return utils::MakeBindGroupLayout(
             device,
diff --git a/src/tests/unittests/validation/QueryValidationTests.cpp b/src/tests/unittests/validation/QueryValidationTests.cpp
index fa0d8c7..257cc72 100644
--- a/src/tests/unittests/validation/QueryValidationTests.cpp
+++ b/src/tests/unittests/validation/QueryValidationTests.cpp
@@ -110,13 +110,18 @@
 
     // Fail to begin render pass if the occlusionQuerySet is created from other device
     {
-        wgpu::Device otherDevice = adapter.CreateDevice();
+        wgpu::Device otherDevice = RegisterDevice(adapter.CreateDevice());
         wgpu::QuerySet occlusionQuerySetOnOther =
             CreateQuerySet(otherDevice, wgpu::QueryType::Occlusion, 2);
         renderPass.occlusionQuerySet = occlusionQuerySetOnOther;
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        encoder.BeginRenderPass(&renderPass);
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
+        pass.EndPass();
         ASSERT_DEVICE_ERROR(encoder.Finish());
+
+        // Clear this out so we don't hold a reference. The query set
+        // must be destroyed before the device local to this test case.
+        renderPass.occlusionQuerySet = wgpu::QuerySet();
     }
 
     // Fail to submit occlusion query with a destroyed query set
@@ -219,10 +224,10 @@
 
 class TimestampQueryValidationTest : public QuerySetValidationTest {
   protected:
-    wgpu::Device CreateTestDevice() override {
+    WGPUDevice CreateTestDevice() override {
         dawn_native::DeviceDescriptor descriptor;
         descriptor.requiredExtensions = {"timestamp_query"};
-        return wgpu::Device::Acquire(adapter.CreateDevice(&descriptor));
+        return adapter.CreateDevice(&descriptor);
     }
 };
 
@@ -422,10 +427,10 @@
 
 class PipelineStatisticsQueryValidationTest : public QuerySetValidationTest {
   protected:
-    wgpu::Device CreateTestDevice() override {
+    WGPUDevice CreateTestDevice() override {
         dawn_native::DeviceDescriptor descriptor;
         descriptor.requiredExtensions = {"pipeline_statistics_query"};
-        return wgpu::Device::Acquire(adapter.CreateDevice(&descriptor));
+        return adapter.CreateDevice(&descriptor);
     }
 };
 
@@ -563,7 +568,7 @@
 
     // Fail to resolve query set to a buffer created from another device
     {
-        wgpu::Device otherDevice = adapter.CreateDevice();
+        wgpu::Device otherDevice = RegisterDevice(adapter.CreateDevice());
         wgpu::Buffer bufferOnOther =
             CreateBuffer(otherDevice, kBufferSize, wgpu::BufferUsage::QueryResolve);
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
diff --git a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp
index a4f1bf7..26d92e4 100644
--- a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp
+++ b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp
@@ -559,10 +559,10 @@
 
     class WriteTextureTest_CompressedTextureFormats : public QueueWriteTextureValidationTest {
       protected:
-        wgpu::Device CreateTestDevice() override {
+        WGPUDevice CreateTestDevice() override {
             dawn_native::DeviceDescriptor descriptor;
             descriptor.requiredExtensions = {"texture_compression_bc"};
-            return wgpu::Device::Acquire(adapter.CreateDevice(&descriptor));
+            return adapter.CreateDevice(&descriptor);
         }
 
         wgpu::Texture Create2DTexture(wgpu::TextureFormat format,
diff --git a/src/tests/unittests/validation/TextureValidationTests.cpp b/src/tests/unittests/validation/TextureValidationTests.cpp
index a1a3e1b..458ce00 100644
--- a/src/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/tests/unittests/validation/TextureValidationTests.cpp
@@ -393,10 +393,10 @@
     // compressed texture formats.
     class CompressedTextureFormatsValidationTests : public TextureValidationTest {
       protected:
-        wgpu::Device CreateTestDevice() override {
+        WGPUDevice CreateTestDevice() override {
             dawn_native::DeviceDescriptor descriptor;
             descriptor.requiredExtensions = {"texture_compression_bc"};
-            return wgpu::Device::Acquire(adapter.CreateDevice(&descriptor));
+            return adapter.CreateDevice(&descriptor);
         }
 
         wgpu::TextureDescriptor CreateDefaultTextureDescriptor() {
diff --git a/src/tests/unittests/validation/UnsafeAPIValidationTests.cpp b/src/tests/unittests/validation/UnsafeAPIValidationTests.cpp
index 3346722..4ba2194 100644
--- a/src/tests/unittests/validation/UnsafeAPIValidationTests.cpp
+++ b/src/tests/unittests/validation/UnsafeAPIValidationTests.cpp
@@ -20,10 +20,10 @@
 
 class UnsafeAPIValidationTest : public ValidationTest {
   protected:
-    wgpu::Device CreateTestDevice() override {
+    WGPUDevice CreateTestDevice() override {
         dawn_native::DeviceDescriptor descriptor;
         descriptor.forceEnabledToggles.push_back("disallow_unsafe_apis");
-        return wgpu::Device::Acquire(adapter.CreateDevice(&descriptor));
+        return adapter.CreateDevice(&descriptor);
     }
 };
 
diff --git a/src/tests/unittests/validation/ValidationTest.cpp b/src/tests/unittests/validation/ValidationTest.cpp
index a4695c6..6ad7c71 100644
--- a/src/tests/unittests/validation/ValidationTest.cpp
+++ b/src/tests/unittests/validation/ValidationTest.cpp
@@ -15,12 +15,41 @@
 #include "tests/unittests/validation/ValidationTest.h"
 
 #include "common/Assert.h"
+#include "common/SystemUtils.h"
 #include "dawn/dawn_proc.h"
 #include "dawn/webgpu.h"
 #include "dawn_native/NullBackend.h"
+#include "utils/WireHelper.h"
 
 #include <algorithm>
 
+namespace {
+
+    bool gUseWire = false;
+    std::string gWireTraceDir = "";
+
+}  // namespace
+
+void InitDawnValidationTestEnvironment(int argc, char** argv) {
+    for (int i = 1; i < argc; ++i) {
+        if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
+            gUseWire = true;
+            continue;
+        }
+
+        constexpr const char kWireTraceDirArg[] = "--wire-trace-dir=";
+        size_t argLen = sizeof(kWireTraceDirArg) - 1;
+        if (strncmp(argv[i], kWireTraceDirArg, argLen) == 0) {
+            gWireTraceDir = argv[i] + argLen;
+            continue;
+        }
+    }
+}
+
+ValidationTest::ValidationTest()
+    : mWireHelper(utils::CreateWireHelper(gUseWire, gWireTraceDir.c_str())) {
+}
+
 void ValidationTest::SetUp() {
     instance = std::make_unique<dawn_native::Instance>();
     instance->DiscoverDefaultAdapters();
@@ -42,25 +71,29 @@
 
     ASSERT(foundNullAdapter);
 
-    dawnProcSetProcs(&dawn_native::GetProcs());
-
-    device = CreateTestDevice();
+    std::tie(device, backendDevice) = mWireHelper->RegisterDevice(CreateTestDevice());
     device.SetUncapturedErrorCallback(ValidationTest::OnDeviceError, this);
+
+    std::string traceName =
+        std::string(::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) +
+        "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name();
+    mWireHelper->BeginWireTrace(traceName.c_str());
 }
 
 ValidationTest::~ValidationTest() {
     // We need to destroy Dawn objects before setting the procs to null otherwise the dawn*Release
     // will call a nullptr
     device = wgpu::Device();
-    dawnProcSetProcs(nullptr);
+    mWireHelper.reset();
 }
 
 void ValidationTest::TearDown() {
+    FlushWire();
     ASSERT_FALSE(mExpectError);
 
     if (device) {
         EXPECT_EQ(mLastWarningCount,
-                  dawn_native::GetDeprecationWarningCountForTesting(device.Get()));
+                  dawn_native::GetDeprecationWarningCountForTesting(backendDevice));
     }
 }
 
@@ -76,7 +109,20 @@
     return mDeviceErrorMessage;
 }
 
-void ValidationTest::WaitForAllOperations(const wgpu::Device& device) const {
+wgpu::Device ValidationTest::RegisterDevice(WGPUDevice backendDevice) {
+    return mWireHelper->RegisterDevice(backendDevice).first;
+}
+
+bool ValidationTest::UsesWire() const {
+    return gUseWire;
+}
+
+void ValidationTest::FlushWire() {
+    EXPECT_TRUE(mWireHelper->FlushClient());
+    EXPECT_TRUE(mWireHelper->FlushServer());
+}
+
+void ValidationTest::WaitForAllOperations(const wgpu::Device& device) {
     wgpu::Queue queue = device.GetDefaultQueue();
     wgpu::Fence fence = queue.CreateFence();
 
@@ -84,11 +130,13 @@
     queue.Signal(fence, 1);
     while (fence.GetCompletedValue() < 1) {
         device.Tick();
+        FlushWire();
     }
 
     // TODO(cwallez@chromium.org): It's not clear why we need this additional tick. Investigate it
     // once WebGPU has defined the ordering of callbacks firing.
     device.Tick();
+    FlushWire();
 }
 
 bool ValidationTest::HasWGSL() const {
@@ -100,14 +148,14 @@
 }
 
 bool ValidationTest::HasToggleEnabled(const char* toggle) const {
-    auto toggles = dawn_native::GetTogglesUsed(device.Get());
+    auto toggles = dawn_native::GetTogglesUsed(backendDevice);
     return std::find_if(toggles.begin(), toggles.end(), [toggle](const char* name) {
                return strcmp(toggle, name) == 0;
            }) != toggles.end();
 }
 
-wgpu::Device ValidationTest::CreateTestDevice() {
-    return wgpu::Device::Acquire(adapter.CreateDevice());
+WGPUDevice ValidationTest::CreateTestDevice() {
+    return adapter.CreateDevice();
 }
 
 // static
diff --git a/src/tests/unittests/validation/ValidationTest.h b/src/tests/unittests/validation/ValidationTest.h
index 79f9cce..edab1a6 100644
--- a/src/tests/unittests/validation/ValidationTest.h
+++ b/src/tests/unittests/validation/ValidationTest.h
@@ -21,8 +21,10 @@
 #include "gtest/gtest.h"
 
 #define ASSERT_DEVICE_ERROR(statement)                          \
+    FlushWire();                                                \
     StartExpectDeviceError();                                   \
     statement;                                                  \
+    FlushWire();                                                \
     if (!EndExpectDeviceError()) {                              \
         FAIL() << "Expected device error in:\n " << #statement; \
     }                                                           \
@@ -39,17 +41,26 @@
         }                                                       \
     } while (0)
 
-#define EXPECT_DEPRECATION_WARNING(statement)                                                    \
-    do {                                                                                         \
-        size_t warningsBefore = dawn_native::GetDeprecationWarningCountForTesting(device.Get()); \
-        statement;                                                                               \
-        size_t warningsAfter = dawn_native::GetDeprecationWarningCountForTesting(device.Get());  \
-        EXPECT_EQ(mLastWarningCount, warningsBefore);                                            \
-        mLastWarningCount = warningsAfter;                                                       \
+#define EXPECT_DEPRECATION_WARNING(statement)                                                     \
+    do {                                                                                          \
+        FlushWire();                                                                              \
+        size_t warningsBefore = dawn_native::GetDeprecationWarningCountForTesting(backendDevice); \
+        statement;                                                                                \
+        FlushWire();                                                                              \
+        size_t warningsAfter = dawn_native::GetDeprecationWarningCountForTesting(backendDevice);  \
+        EXPECT_EQ(mLastWarningCount, warningsBefore);                                             \
+        mLastWarningCount = warningsAfter;                                                        \
     } while (0)
 
+namespace utils {
+    class WireHelper;
+}  // namespace utils
+
+void InitDawnValidationTestEnvironment(int argc, char** argv);
+
 class ValidationTest : public testing::Test {
   public:
+    ValidationTest();
     ~ValidationTest() override;
 
     void SetUp() override;
@@ -59,7 +70,12 @@
     bool EndExpectDeviceError();
     std::string GetLastDeviceErrorMessage() const;
 
-    void WaitForAllOperations(const wgpu::Device& device) const;
+    wgpu::Device RegisterDevice(WGPUDevice backendDevice);
+
+    bool UsesWire() const;
+
+    void FlushWire();
+    void WaitForAllOperations(const wgpu::Device& device);
 
     // Helper functions to create objects to test validation.
 
@@ -79,15 +95,18 @@
     bool HasToggleEnabled(const char* toggle) const;
 
   protected:
-    virtual wgpu::Device CreateTestDevice();
+    virtual WGPUDevice CreateTestDevice();
 
-    wgpu::Device device;
-    dawn_native::Adapter adapter;
     std::unique_ptr<dawn_native::Instance> instance;
+    dawn_native::Adapter adapter;
+    wgpu::Device device;
+    WGPUDevice backendDevice;
 
     size_t mLastWarningCount = 0;
 
   private:
+    std::unique_ptr<utils::WireHelper> mWireHelper;
+
     static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata);
     std::string mDeviceErrorMessage;
     bool mExpectError = false;