Ensure all wire child objects are destroyed before their device

Destroying a device will implicit destroy all its child objects.
Attempting to use a child object after results in a fatal error.

Bug: dawn:384
Change-Id: I43c27c92cacde759be83cca79ac890f41bac3927
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/37002
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/tests/unittests/wire/WireDestroyObjectTests.cpp b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
new file mode 100644
index 0000000..f5e16b7
--- /dev/null
+++ b/src/tests/unittests/wire/WireDestroyObjectTests.cpp
@@ -0,0 +1,45 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tests/unittests/wire/WireTest.h"
+
+using namespace testing;
+using namespace dawn_wire;
+
+class WireDestroyObjectTests : public WireTest {};
+
+// Test that destroying the device also destroys child objects.
+TEST_F(WireDestroyObjectTests, DestroyDeviceDestroysChildren) {
+    WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
+
+    WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder();
+    EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder));
+
+    FlushClient();
+
+    // Release the device. It should cause the command encoder to be destroyed.
+    wgpuDeviceRelease(device);
+
+    Sequence s1, s2;
+    // The device and child objects should be released.
+    EXPECT_CALL(api, CommandEncoderRelease(apiEncoder)).InSequence(s1);
+    EXPECT_CALL(api, QueueRelease(apiQueue)).InSequence(s2);
+    EXPECT_CALL(api, DeviceRelease(apiDevice)).InSequence(s1, s2);
+
+    FlushClient();
+
+    // Using the command encoder should be an error.
+    wgpuCommandEncoderFinish(encoder, nullptr);
+    FlushClient(false);
+}
diff --git a/src/tests/unittests/wire/WireDisconnectTests.cpp b/src/tests/unittests/wire/WireDisconnectTests.cpp
index 4c99ced..f44df13 100644
--- a/src/tests/unittests/wire/WireDisconnectTests.cpp
+++ b/src/tests/unittests/wire/WireDisconnectTests.cpp
@@ -145,9 +145,10 @@
     DeleteClient();
 
     // Expect release on all objects created by the client.
-    EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1);
-    EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1);
-    EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1);
-    EXPECT_CALL(api, SamplerRelease(apiSampler)).Times(1);
+    Sequence s1, s2, s3;
+    EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1).InSequence(s1);
+    EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1).InSequence(s2);
+    EXPECT_CALL(api, SamplerRelease(apiSampler)).Times(1).InSequence(s3);
+    EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1).InSequence(s1, s2, s3);
     FlushClient();
 }
diff --git a/src/tests/unittests/wire/WireInjectTextureTests.cpp b/src/tests/unittests/wire/WireInjectTextureTests.cpp
index 9e327cf..c6a1f2c 100644
--- a/src/tests/unittests/wire/WireInjectTextureTests.cpp
+++ b/src/tests/unittests/wire/WireInjectTextureTests.cpp
@@ -34,7 +34,8 @@
 
     WGPUTexture apiTexture = api.GetNewTexture();
     EXPECT_CALL(api, TextureReference(apiTexture));
-    ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+    ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+                                               reservation.deviceId, reservation.deviceGeneration));
 
     wgpuTextureCreateView(reservation.texture, nullptr);
     WGPUTextureView apiDummyView = api.GetNewTextureView();
@@ -57,11 +58,13 @@
 
     WGPUTexture apiTexture = api.GetNewTexture();
     EXPECT_CALL(api, TextureReference(apiTexture));
-    ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+    ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+                                               reservation.deviceId, reservation.deviceGeneration));
 
     // ID already in use, call fails.
-    ASSERT_FALSE(
-        GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+    ASSERT_FALSE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+                                                reservation.deviceId,
+                                                reservation.deviceGeneration));
 }
 
 // Test that the server only borrows the texture and does a single reference-release
@@ -71,7 +74,8 @@
     // Injecting the texture adds a reference
     WGPUTexture apiTexture = api.GetNewTexture();
     EXPECT_CALL(api, TextureReference(apiTexture));
-    ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation));
+    ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation,
+                                               reservation.deviceId, reservation.deviceGeneration));
 
     // Releasing the texture removes a single reference.
     wgpuTextureRelease(reservation.texture);