Browse Source

Use new XCTest async/await support (#1336)

Motivation:

Swift 5.5.2 includes XCTest support for async/await. Currently we are
working around the lack of support with XCTest expectations.

Modifications:

- Remove the helper
- Make async tests `async throws`

Result:

Fewer workarounds.
George Barnett 4 years ago
parent
commit
8fcf4f3765

+ 96 - 114
Tests/GRPCTests/AsyncAwaitSupport/AsyncIntegrationTests.swift

@@ -55,156 +55,138 @@ final class AsyncIntegrationTests: GRPCTestCase {
     super.tearDown()
   }
 
-  func testUnary() {
-    XCTAsyncTest {
-      let get = self.echo.makeGetCall(.with { $0.text = "hello" })
+  func testUnary() async throws {
+    let get = self.echo.makeGetCall(.with { $0.text = "hello" })
 
-      let initialMetadata = try await get.initialMetadata
-      initialMetadata.assertFirst("200", forName: ":status")
+    let initialMetadata = try await get.initialMetadata
+    initialMetadata.assertFirst("200", forName: ":status")
 
-      let response = try await get.response
-      XCTAssertEqual(response.text, "Swift echo get: hello")
+    let response = try await get.response
+    XCTAssertEqual(response.text, "Swift echo get: hello")
 
-      let trailingMetadata = try await get.trailingMetadata
-      trailingMetadata.assertFirst("0", forName: "grpc-status")
+    let trailingMetadata = try await get.trailingMetadata
+    trailingMetadata.assertFirst("0", forName: "grpc-status")
 
-      let status = await get.status
-      XCTAssertTrue(status.isOk)
-    }
+    let status = await get.status
+    XCTAssertTrue(status.isOk)
   }
 
-  func testUnaryWrapper() {
-    XCTAsyncTest {
-      let response = try await self.echo.get(.with { $0.text = "hello" })
-      XCTAssertEqual(response.text, "Swift echo get: hello")
-    }
+  func testUnaryWrapper() async throws {
+    let response = try await self.echo.get(.with { $0.text = "hello" })
+    XCTAssertEqual(response.text, "Swift echo get: hello")
   }
 
-  func testClientStreaming() {
-    XCTAsyncTest {
-      let collect = self.echo.makeCollectCall()
+  func testClientStreaming() async throws {
+    let collect = self.echo.makeCollectCall()
 
-      try await collect.requestStream.send(.with { $0.text = "boyle" })
-      try await collect.requestStream.send(.with { $0.text = "jeffers" })
-      try await collect.requestStream.send(.with { $0.text = "holt" })
-      try await collect.requestStream.finish()
+    try await collect.requestStream.send(.with { $0.text = "boyle" })
+    try await collect.requestStream.send(.with { $0.text = "jeffers" })
+    try await collect.requestStream.send(.with { $0.text = "holt" })
+    try await collect.requestStream.finish()
 
-      let initialMetadata = try await collect.initialMetadata
-      initialMetadata.assertFirst("200", forName: ":status")
+    let initialMetadata = try await collect.initialMetadata
+    initialMetadata.assertFirst("200", forName: ":status")
 
-      let response = try await collect.response
-      XCTAssertEqual(response.text, "Swift echo collect: boyle jeffers holt")
+    let response = try await collect.response
+    XCTAssertEqual(response.text, "Swift echo collect: boyle jeffers holt")
 
-      let trailingMetadata = try await collect.trailingMetadata
-      trailingMetadata.assertFirst("0", forName: "grpc-status")
+    let trailingMetadata = try await collect.trailingMetadata
+    trailingMetadata.assertFirst("0", forName: "grpc-status")
 
-      let status = await collect.status
-      XCTAssertTrue(status.isOk)
-    }
+    let status = await collect.status
+    XCTAssertTrue(status.isOk)
   }
 
-  func testClientStreamingWrapper() {
-    XCTAsyncTest {
-      let requests: [Echo_EchoRequest] = [
-        .with { $0.text = "boyle" },
-        .with { $0.text = "jeffers" },
-        .with { $0.text = "holt" },
-      ]
+  func testClientStreamingWrapper() async throws {
+    let requests: [Echo_EchoRequest] = [
+      .with { $0.text = "boyle" },
+      .with { $0.text = "jeffers" },
+      .with { $0.text = "holt" },
+    ]
 
-      let response = try await self.echo.collect(requests)
-      XCTAssertEqual(response.text, "Swift echo collect: boyle jeffers holt")
-    }
+    let response = try await self.echo.collect(requests)
+    XCTAssertEqual(response.text, "Swift echo collect: boyle jeffers holt")
   }
 
-  func testServerStreaming() {
-    XCTAsyncTest {
-      let expand = self.echo.makeExpandCall(.with { $0.text = "boyle jeffers holt" })
+  func testServerStreaming() async throws {
+    let expand = self.echo.makeExpandCall(.with { $0.text = "boyle jeffers holt" })
 
-      let initialMetadata = try await expand.initialMetadata
-      initialMetadata.assertFirst("200", forName: ":status")
+    let initialMetadata = try await expand.initialMetadata
+    initialMetadata.assertFirst("200", forName: ":status")
 
-      let responses = try await expand.responseStream.map { $0.text }.collect()
-      XCTAssertEqual(responses, [
-        "Swift echo expand (0): boyle",
-        "Swift echo expand (1): jeffers",
-        "Swift echo expand (2): holt",
-      ])
+    let responses = try await expand.responseStream.map { $0.text }.collect()
+    XCTAssertEqual(responses, [
+      "Swift echo expand (0): boyle",
+      "Swift echo expand (1): jeffers",
+      "Swift echo expand (2): holt",
+    ])
 
-      let trailingMetadata = try await expand.trailingMetadata
-      trailingMetadata.assertFirst("0", forName: "grpc-status")
+    let trailingMetadata = try await expand.trailingMetadata
+    trailingMetadata.assertFirst("0", forName: "grpc-status")
 
-      let status = await expand.status
-      XCTAssertTrue(status.isOk)
-    }
+    let status = await expand.status
+    XCTAssertTrue(status.isOk)
   }
 
-  func testServerStreamingWrapper() {
-    XCTAsyncTest {
-      let responseStream = self.echo.expand(.with { $0.text = "boyle jeffers holt" })
-      let responses = try await responseStream.map { $0.text }.collect()
-      XCTAssertEqual(responses, [
-        "Swift echo expand (0): boyle",
-        "Swift echo expand (1): jeffers",
-        "Swift echo expand (2): holt",
-      ])
-    }
+  func testServerStreamingWrapper() async throws {
+    let responseStream = self.echo.expand(.with { $0.text = "boyle jeffers holt" })
+    let responses = try await responseStream.map { $0.text }.collect()
+    XCTAssertEqual(responses, [
+      "Swift echo expand (0): boyle",
+      "Swift echo expand (1): jeffers",
+      "Swift echo expand (2): holt",
+    ])
   }
 
-  func testBidirectionalStreaming() {
-    XCTAsyncTest {
-      let update = self.echo.makeUpdateCall()
+  func testBidirectionalStreaming() async throws {
+    let update = self.echo.makeUpdateCall()
 
-      var responseIterator = update.responseStream.map { $0.text }.makeAsyncIterator()
+    var responseIterator = update.responseStream.map { $0.text }.makeAsyncIterator()
 
-      for (i, name) in ["boyle", "jeffers", "holt"].enumerated() {
-        try await update.requestStream.send(.with { $0.text = name })
-        let response = try await responseIterator.next()
-        XCTAssertEqual(response, "Swift echo update (\(i)): \(name)")
-      }
+    for (i, name) in ["boyle", "jeffers", "holt"].enumerated() {
+      try await update.requestStream.send(.with { $0.text = name })
+      let response = try await responseIterator.next()
+      XCTAssertEqual(response, "Swift echo update (\(i)): \(name)")
+    }
 
-      try await update.requestStream.finish()
+    try await update.requestStream.finish()
 
-      // This isn't right after we make the call as servers are not guaranteed to send metadata back
-      // immediately. Concretely, we don't send initial metadata back until the first response
-      // message is sent by the server.
-      let initialMetadata = try await update.initialMetadata
-      initialMetadata.assertFirst("200", forName: ":status")
+    // This isn't right after we make the call as servers are not guaranteed to send metadata back
+    // immediately. Concretely, we don't send initial metadata back until the first response
+    // message is sent by the server.
+    let initialMetadata = try await update.initialMetadata
+    initialMetadata.assertFirst("200", forName: ":status")
 
-      let trailingMetadata = try await update.trailingMetadata
-      trailingMetadata.assertFirst("0", forName: "grpc-status")
+    let trailingMetadata = try await update.trailingMetadata
+    trailingMetadata.assertFirst("0", forName: "grpc-status")
 
-      let status = await update.status
-      XCTAssertTrue(status.isOk)
-    }
+    let status = await update.status
+    XCTAssertTrue(status.isOk)
   }
 
-  func testBidirectionalStreamingWrapper() {
-    XCTAsyncTest {
-      let requests: [Echo_EchoRequest] = [
-        .with { $0.text = "boyle" },
-        .with { $0.text = "jeffers" },
-        .with { $0.text = "holt" },
-      ]
-
-      let responseStream = self.echo.update(requests)
-      let responses = try await responseStream.map { $0.text }.collect()
-      XCTAssertEqual(responses, [
-        "Swift echo update (0): boyle",
-        "Swift echo update (1): jeffers",
-        "Swift echo update (2): holt",
-      ])
-    }
+  func testBidirectionalStreamingWrapper() async throws {
+    let requests: [Echo_EchoRequest] = [
+      .with { $0.text = "boyle" },
+      .with { $0.text = "jeffers" },
+      .with { $0.text = "holt" },
+    ]
+
+    let responseStream = self.echo.update(requests)
+    let responses = try await responseStream.map { $0.text }.collect()
+    XCTAssertEqual(responses, [
+      "Swift echo update (0): boyle",
+      "Swift echo update (1): jeffers",
+      "Swift echo update (2): holt",
+    ])
   }
 
-  func testServerCloseAfterMessage() {
-    XCTAsyncTest {
-      let update = self.echo.makeUpdateCall()
-      try await update.requestStream.send(.with { $0.text = "hello" })
-      _ = try await update.responseStream.first(where: { _ in true })
-      XCTAssertNoThrow(try self.server.close().wait())
-      self.server = nil // So that tearDown() does not call close() again.
-      try await update.requestStream.finish()
-    }
+  func testServerCloseAfterMessage() async throws {
+    let update = self.echo.makeUpdateCall()
+    try await update.requestStream.send(.with { $0.text = "hello" })
+    _ = try await update.responseStream.first(where: { _ in true })
+    XCTAssertNoThrow(try self.server.close().wait())
+    self.server = nil // So that tearDown() does not call close() again.
+    try await update.requestStream.finish()
   }
 }
 

+ 169 - 189
Tests/GRPCTests/AsyncAwaitSupport/AsyncWriterTests.swift

@@ -20,248 +20,228 @@ import XCTest
 
 @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
 internal class AsyncWriterTests: GRPCTestCase {
-  func testSingleWriterHappyPath() {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
+  func testSingleWriterHappyPath() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
 
-      try await writer.write("jimmy")
-      XCTAssertEqual(delegate.elements, ["jimmy"])
+    try await writer.write("jimmy")
+    XCTAssertEqual(delegate.elements, ["jimmy"])
 
-      try await writer.write("jab")
-      XCTAssertEqual(delegate.elements, ["jimmy", "jab"])
+    try await writer.write("jab")
+    XCTAssertEqual(delegate.elements, ["jimmy", "jab"])
 
-      try await writer.finish(99)
-      XCTAssertEqual(delegate.end, 99)
-    }
+    try await writer.finish(99)
+    XCTAssertEqual(delegate.end, 99)
   }
 
-  func testPauseAndResumeWrites() {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
+  func testPauseAndResumeWrites() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
 
-      // pause
-      await writer.toggleWritability()
+    // pause
+    await writer.toggleWritability()
 
-      async let written1: Void = writer.write("wunch")
-      XCTAssert(delegate.elements.isEmpty)
+    async let written1: Void = writer.write("wunch")
+    XCTAssert(delegate.elements.isEmpty)
 
-      // resume
-      await writer.toggleWritability()
-      try await written1
-      XCTAssertEqual(delegate.elements, ["wunch"])
+    // resume
+    await writer.toggleWritability()
+    try await written1
+    XCTAssertEqual(delegate.elements, ["wunch"])
 
-      try await writer.finish(0)
-      XCTAssertEqual(delegate.end, 0)
-    }
+    try await writer.finish(0)
+    XCTAssertEqual(delegate.end, 0)
   }
 
-  func testTooManyWrites() throws {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      // Zero pending elements means that any write when paused will trigger an error.
-      let writer = AsyncWriter(maxPendingElements: 0, delegate: delegate)
+  func testTooManyWrites() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    // Zero pending elements means that any write when paused will trigger an error.
+    let writer = AsyncWriter(maxPendingElements: 0, delegate: delegate)
 
-      // pause
-      await writer.toggleWritability()
+    // pause
+    await writer.toggleWritability()
 
-      await XCTAssertThrowsError(try await writer.write("pontiac")) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .tooManyPendingWrites)
-      }
-
-      // resume (we must finish the writer.)
-      await writer.toggleWritability()
-      try await writer.finish(0)
-      XCTAssertEqual(delegate.end, 0)
-      XCTAssertTrue(delegate.elements.isEmpty)
+    await XCTAssertThrowsError(try await writer.write("pontiac")) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .tooManyPendingWrites)
     }
-  }
 
-  func testWriteAfterFinish() throws {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
+    // resume (we must finish the writer.)
+    await writer.toggleWritability()
+    try await writer.finish(0)
+    XCTAssertEqual(delegate.end, 0)
+    XCTAssertTrue(delegate.elements.isEmpty)
+  }
 
-      try await writer.finish(0)
-      XCTAssertEqual(delegate.end, 0)
+  func testWriteAfterFinish() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
 
-      await XCTAssertThrowsError(try await writer.write("cheddar")) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-      }
+    try await writer.finish(0)
+    XCTAssertEqual(delegate.end, 0)
 
-      XCTAssertTrue(delegate.elements.isEmpty)
+    await XCTAssertThrowsError(try await writer.write("cheddar")) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
     }
+
+    XCTAssertTrue(delegate.elements.isEmpty)
   }
 
-  func testTooManyCallsToFinish() throws {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
+  func testTooManyCallsToFinish() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
 
-      try await writer.finish(0)
-      XCTAssertEqual(delegate.end, 0)
+    try await writer.finish(0)
+    XCTAssertEqual(delegate.end, 0)
 
-      await XCTAssertThrowsError(try await writer.finish(1)) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-      }
-
-      // Still 0.
-      XCTAssertEqual(delegate.end, 0)
+    await XCTAssertThrowsError(try await writer.finish(1)) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
     }
+
+    // Still 0.
+    XCTAssertEqual(delegate.end, 0)
   }
 
-  func testCallToFinishWhilePending() throws {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
+  func testCallToFinishWhilePending() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
 
-      // Pause.
-      await writer.toggleWritability()
+    // Pause.
+    await writer.toggleWritability()
 
-      async let finished: Void = writer.finish(42)
-      XCTAssertNil(delegate.end)
+    async let finished: Void = writer.finish(42)
+    XCTAssertNil(delegate.end)
 
-      // Resume.
-      await writer.toggleWritability()
-      try await finished
+    // Resume.
+    await writer.toggleWritability()
+    try await finished
 
-      XCTAssertEqual(delegate.end, 42)
-    }
+    XCTAssertEqual(delegate.end, 42)
   }
 
-  func testTooManyCallsToFinishWhilePending() throws {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
-
-      // Pause.
-      await writer.toggleWritability()
-
-      // We want to test that when a finish has suspended that another task calling finish results
-      // in an `AsyncWriterError.alreadyFinished` error.
-      //
-      // It's hard to achieve this reliably in an obvious way because we can't guarantee the
-      // ordering of `Task`s or when they will be suspended during `finish`. However, by pausing the
-      // writer and calling finish in two separate tasks we guarantee that one will run first and
-      // suspend (because the writer is paused) and the other will throw an error. When one throws
-      // an error it can resume the writer allowing the other task to resume successfully.
-      await withThrowingTaskGroup(of: Void.self) { group in
-        group.addTask {
-          do {
-            try await writer.finish(1)
-          } catch {
-            XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-            // Resume.
-            await writer.toggleWritability()
-          }
+  func testTooManyCallsToFinishWhilePending() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
+
+    // Pause.
+    await writer.toggleWritability()
+
+    // We want to test that when a finish has suspended that another task calling finish results
+    // in an `AsyncWriterError.alreadyFinished` error.
+    //
+    // It's hard to achieve this reliably in an obvious way because we can't guarantee the
+    // ordering of `Task`s or when they will be suspended during `finish`. However, by pausing the
+    // writer and calling finish in two separate tasks we guarantee that one will run first and
+    // suspend (because the writer is paused) and the other will throw an error. When one throws
+    // an error it can resume the writer allowing the other task to resume successfully.
+    await withThrowingTaskGroup(of: Void.self) { group in
+      group.addTask {
+        do {
+          try await writer.finish(1)
+        } catch {
+          XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
+          // Resume.
+          await writer.toggleWritability()
         }
+      }
 
-        group.addTask {
-          do {
-            try await writer.finish(2)
-          } catch {
-            XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-            // Resume.
-            await writer.toggleWritability()
-          }
+      group.addTask {
+        do {
+          try await writer.finish(2)
+        } catch {
+          XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
+          // Resume.
+          await writer.toggleWritability()
         }
       }
+    }
 
-      // We should definitely be finished by this point.
-      await XCTAssertThrowsError(try await writer.finish(3)) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-      }
+    // We should definitely be finished by this point.
+    await XCTAssertThrowsError(try await writer.finish(3)) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
     }
   }
 
-  func testCancellationForPendingWrite() {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
-
-      // Pause.
-      await writer.toggleWritability()
-
-      async let pendingWrite: Void = writer.write("foo")
-
-      await writer.cancel()
-
-      do {
-        try await pendingWrite
-        XCTFail("Expected to throw an error.")
-      } catch is CancellationError {
-        // Cancellation is fine: we cancelled while the write was pending.
-        ()
-      } catch let error as GRPCAsyncWriterError {
-        // Already finish is also fine: we cancelled before the write was enqueued.
-        XCTAssertEqual(error, .alreadyFinished)
-      } catch {
-        XCTFail("Unexpected error: \(error)")
-      }
+  func testCancellationForPendingWrite() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
 
-      await XCTAssertThrowsError(try await writer.write("bar")) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-      }
+    // Pause.
+    await writer.toggleWritability()
 
-      XCTAssertTrue(delegate.elements.isEmpty)
-      XCTAssertNil(delegate.end)
+    async let pendingWrite: Void = writer.write("foo")
+
+    await writer.cancel()
+
+    do {
+      try await pendingWrite
+      XCTFail("Expected to throw an error.")
+    } catch is CancellationError {
+      // Cancellation is fine: we cancelled while the write was pending.
+      ()
+    } catch let error as GRPCAsyncWriterError {
+      // Already finish is also fine: we cancelled before the write was enqueued.
+      XCTAssertEqual(error, .alreadyFinished)
+    } catch {
+      XCTFail("Unexpected error: \(error)")
     }
+
+    await XCTAssertThrowsError(try await writer.write("bar")) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
+    }
+
+    XCTAssertTrue(delegate.elements.isEmpty)
+    XCTAssertNil(delegate.end)
   }
 
-  func testCancellationForPendingFinish() {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
-
-      // Pause.
-      await writer.toggleWritability()
-
-      async let pendingWrite: Void = writer.finish(42)
-
-      await writer.cancel()
-
-      do {
-        try await pendingWrite
-        XCTFail("Expected to throw an error.")
-      } catch is CancellationError {
-        // Cancellation is fine: we cancelled while the write was pending.
-        ()
-      } catch let error as GRPCAsyncWriterError {
-        // Already finish is also fine: we cancelled before the write was enqueued.
-        XCTAssertEqual(error, .alreadyFinished)
-      } catch {
-        XCTFail("Unexpected error: \(error)")
-      }
+  func testCancellationForPendingFinish() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
 
-      await XCTAssertThrowsError(try await writer.finish(42)) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-      }
+    // Pause.
+    await writer.toggleWritability()
+
+    async let pendingWrite: Void = writer.finish(42)
+
+    await writer.cancel()
 
-      XCTAssertTrue(delegate.elements.isEmpty)
-      XCTAssertNil(delegate.end)
+    do {
+      try await pendingWrite
+      XCTFail("Expected to throw an error.")
+    } catch is CancellationError {
+      // Cancellation is fine: we cancelled while the write was pending.
+      ()
+    } catch let error as GRPCAsyncWriterError {
+      // Already finish is also fine: we cancelled before the write was enqueued.
+      XCTAssertEqual(error, .alreadyFinished)
+    } catch {
+      XCTFail("Unexpected error: \(error)")
     }
-  }
 
-  func testMultipleCancellations() {
-    XCTAsyncTest {
-      let delegate = CollectingDelegate<String, Int>()
-      let writer = AsyncWriter(delegate: delegate)
+    await XCTAssertThrowsError(try await writer.finish(42)) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
+    }
 
-      await writer.cancel()
-      await XCTAssertThrowsError(try await writer.write("1")) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-      }
+    XCTAssertTrue(delegate.elements.isEmpty)
+    XCTAssertNil(delegate.end)
+  }
 
-      // Fine, no need to throw. Nothing should change.
-      await writer.cancel()
-      await XCTAssertThrowsError(try await writer.write("2")) { error in
-        XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
-      }
+  func testMultipleCancellations() async throws {
+    let delegate = CollectingDelegate<String, Int>()
+    let writer = AsyncWriter(delegate: delegate)
+
+    await writer.cancel()
+    await XCTAssertThrowsError(try await writer.write("1")) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
+    }
 
-      XCTAssertTrue(delegate.elements.isEmpty)
-      XCTAssertNil(delegate.end)
+    // Fine, no need to throw. Nothing should change.
+    await writer.cancel()
+    await XCTAssertThrowsError(try await writer.write("2")) { error in
+      XCTAssertEqual(error as? GRPCAsyncWriterError, .alreadyFinished)
     }
+
+    XCTAssertTrue(delegate.elements.isEmpty)
+    XCTAssertNil(delegate.end)
   }
 }
 

+ 18 - 18
Tests/GRPCTests/AsyncAwaitSupport/InterceptorsAsyncTests.swift

@@ -71,26 +71,26 @@ class InterceptorsAsyncTests: GRPCTestCase {
     super.tearDown()
   }
 
-  func testUnaryCall() { XCTAsyncTest {
+  func testUnaryCall() async throws {
     let get = try await self.echo.get(.with { $0.text = "hello" })
     await assertThat(get, .is(.with { $0.text = "hello :teg ohce tfiwS" }))
-  } }
+  }
 
-  func testMakingUnaryCall() { XCTAsyncTest {
+  func testMakingUnaryCall() async throws {
     let call = self.echo.makeGetCall(.with { $0.text = "hello" })
     await assertThat(try await call.response, .is(.with { $0.text = "hello :teg ohce tfiwS" }))
-  } }
+  }
 
-  func testClientStreamingSequence() { XCTAsyncTest {
+  func testClientStreamingSequence() async throws {
     let requests = ["1 2", "3 4"].map { item in
       Echo_EchoRequest.with { $0.text = item }
     }
     let response = try await self.echo.collect(requests, callOptions: .init())
 
     await assertThat(response, .is(.with { $0.text = "3 4 1 2 :tcelloc ohce tfiwS" }))
-  } }
+  }
 
-  func testClientStreamingAsyncSequence() { XCTAsyncTest {
+  func testClientStreamingAsyncSequence() async throws {
     let stream = AsyncStream<Echo_EchoRequest> { continuation in
       continuation.yield(.with { $0.text = "1 2" })
       continuation.yield(.with { $0.text = "3 4" })
@@ -99,9 +99,9 @@ class InterceptorsAsyncTests: GRPCTestCase {
     let response = try await self.echo.collect(stream, callOptions: .init())
 
     await assertThat(response, .is(.with { $0.text = "3 4 1 2 :tcelloc ohce tfiwS" }))
-  } }
+  }
 
-  func testMakingCallClientStreaming() { XCTAsyncTest {
+  func testMakingCallClientStreaming() async throws {
     let call = self.echo.makeCollectCall(callOptions: .init())
     try await call.requestStream.send(.with { $0.text = "1 2" })
     try await call.requestStream.send(.with { $0.text = "3 4" })
@@ -111,25 +111,25 @@ class InterceptorsAsyncTests: GRPCTestCase {
       try await call.response,
       .is(.with { $0.text = "3 4 1 2 :tcelloc ohce tfiwS" })
     )
-  } }
+  }
 
-  func testServerStreaming() { XCTAsyncTest {
+  func testServerStreaming() async throws {
     let responses = self.echo.expand(.with { $0.text = "hello" }, callOptions: .init())
     for try await response in responses {
       // Expand splits on spaces, so we only expect one response.
       await assertThat(response, .is(.with { $0.text = "hello :)0( dnapxe ohce tfiwS" }))
     }
-  } }
+  }
 
-  func testMakingCallServerStreaming() { XCTAsyncTest {
+  func testMakingCallServerStreaming() async throws {
     let call = self.echo.makeExpandCall(.with { $0.text = "hello" }, callOptions: .init())
     for try await response in call.responseStream {
       // Expand splits on spaces, so we only expect one response.
       await assertThat(response, .is(.with { $0.text = "hello :)0( dnapxe ohce tfiwS" }))
     }
-  } }
+  }
 
-  func testBidirectionalStreaming() { XCTAsyncTest {
+  func testBidirectionalStreaming() async throws {
     let requests = ["1 2", "3 4"].map { item in
       Echo_EchoRequest.with { $0.text = item }
     }
@@ -147,9 +147,9 @@ class InterceptorsAsyncTests: GRPCTestCase {
       }
       count += 1
     }
-  } }
+  }
 
-  func testMakingCallBidirectionalStreaming() { XCTAsyncTest {
+  func testMakingCallBidirectionalStreaming() async throws {
     let call = self.echo.makeUpdateCall(callOptions: .init())
     try await call.requestStream.send(.with { $0.text = "1 2" })
     try await call.requestStream.send(.with { $0.text = "3 4" })
@@ -167,7 +167,7 @@ class InterceptorsAsyncTests: GRPCTestCase {
       }
       count += 1
     }
-  } }
+  }
 }
 
 #endif

+ 76 - 88
Tests/GRPCTests/AsyncAwaitSupport/PassthroughMessageSourceTests.swift

@@ -19,123 +19,111 @@ import XCTest
 
 @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
 class PassthroughMessageSourceTests: GRPCTestCase {
-  func testBasicUsage() {
-    XCTAsyncTest {
-      let source = PassthroughMessageSource<String, Never>()
-      let sequence = PassthroughMessageSequence(consuming: source)
+  func testBasicUsage() async throws {
+    let source = PassthroughMessageSource<String, Never>()
+    let sequence = PassthroughMessageSequence(consuming: source)
 
-      XCTAssertEqual(source.yield("foo"), .accepted(queueDepth: 1))
-      XCTAssertEqual(source.yield("bar"), .accepted(queueDepth: 2))
-      XCTAssertEqual(source.yield("baz"), .accepted(queueDepth: 3))
+    XCTAssertEqual(source.yield("foo"), .accepted(queueDepth: 1))
+    XCTAssertEqual(source.yield("bar"), .accepted(queueDepth: 2))
+    XCTAssertEqual(source.yield("baz"), .accepted(queueDepth: 3))
 
-      let firstTwo = try await sequence.prefix(2).collect()
-      XCTAssertEqual(firstTwo, ["foo", "bar"])
+    let firstTwo = try await sequence.prefix(2).collect()
+    XCTAssertEqual(firstTwo, ["foo", "bar"])
 
-      XCTAssertEqual(source.yield("bar"), .accepted(queueDepth: 2))
-      XCTAssertEqual(source.yield("foo"), .accepted(queueDepth: 3))
+    XCTAssertEqual(source.yield("bar"), .accepted(queueDepth: 2))
+    XCTAssertEqual(source.yield("foo"), .accepted(queueDepth: 3))
 
-      XCTAssertEqual(source.finish(), .accepted(queueDepth: 4))
+    XCTAssertEqual(source.finish(), .accepted(queueDepth: 4))
 
-      let theRest = try await sequence.collect()
-      XCTAssertEqual(theRest, ["baz", "bar", "foo"])
-    }
+    let theRest = try await sequence.collect()
+    XCTAssertEqual(theRest, ["baz", "bar", "foo"])
   }
 
-  func testFinishWithError() {
-    XCTAsyncTest {
-      let source = PassthroughMessageSource<String, TestError>()
+  func testFinishWithError() async throws {
+    let source = PassthroughMessageSource<String, TestError>()
 
-      XCTAssertEqual(source.yield("one"), .accepted(queueDepth: 1))
-      XCTAssertEqual(source.yield("two"), .accepted(queueDepth: 2))
-      XCTAssertEqual(source.yield("three"), .accepted(queueDepth: 3))
-      XCTAssertEqual(source.finish(throwing: TestError()), .accepted(queueDepth: 4))
+    XCTAssertEqual(source.yield("one"), .accepted(queueDepth: 1))
+    XCTAssertEqual(source.yield("two"), .accepted(queueDepth: 2))
+    XCTAssertEqual(source.yield("three"), .accepted(queueDepth: 3))
+    XCTAssertEqual(source.finish(throwing: TestError()), .accepted(queueDepth: 4))
 
-      // We should still be able to get the elements before the error.
-      let sequence = PassthroughMessageSequence(consuming: source)
-      let elements = try await sequence.prefix(3).collect()
-      XCTAssertEqual(elements, ["one", "two", "three"])
+    // We should still be able to get the elements before the error.
+    let sequence = PassthroughMessageSequence(consuming: source)
+    let elements = try await sequence.prefix(3).collect()
+    XCTAssertEqual(elements, ["one", "two", "three"])
 
-      do {
-        for try await element in sequence {
-          XCTFail("Unexpected value '\(element)'")
-        }
-        XCTFail("AsyncSequence did not throw")
-      } catch {
-        XCTAssert(error is TestError)
+    do {
+      for try await element in sequence {
+        XCTFail("Unexpected value '\(element)'")
       }
+      XCTFail("AsyncSequence did not throw")
+    } catch {
+      XCTAssert(error is TestError)
     }
   }
 
-  func testYieldAfterFinish() {
-    XCTAsyncTest {
-      let source = PassthroughMessageSource<String, Never>()
-      XCTAssertEqual(source.finish(), .accepted(queueDepth: 1))
-      XCTAssertEqual(source.yield("foo"), .dropped)
+  func testYieldAfterFinish() async throws {
+    let source = PassthroughMessageSource<String, Never>()
+    XCTAssertEqual(source.finish(), .accepted(queueDepth: 1))
+    XCTAssertEqual(source.yield("foo"), .dropped)
 
-      let sequence = PassthroughMessageSequence(consuming: source)
-      let elements = try await sequence.count()
-      XCTAssertEqual(elements, 0)
-    }
+    let sequence = PassthroughMessageSequence(consuming: source)
+    let elements = try await sequence.count()
+    XCTAssertEqual(elements, 0)
   }
 
-  func testMultipleFinishes() {
-    XCTAsyncTest {
-      let source = PassthroughMessageSource<String, TestError>()
-      XCTAssertEqual(source.finish(), .accepted(queueDepth: 1))
-      XCTAssertEqual(source.finish(), .dropped)
-      XCTAssertEqual(source.finish(throwing: TestError()), .dropped)
+  func testMultipleFinishes() async throws {
+    let source = PassthroughMessageSource<String, TestError>()
+    XCTAssertEqual(source.finish(), .accepted(queueDepth: 1))
+    XCTAssertEqual(source.finish(), .dropped)
+    XCTAssertEqual(source.finish(throwing: TestError()), .dropped)
 
-      let sequence = PassthroughMessageSequence(consuming: source)
-      let elements = try await sequence.count()
-      XCTAssertEqual(elements, 0)
-    }
+    let sequence = PassthroughMessageSequence(consuming: source)
+    let elements = try await sequence.count()
+    XCTAssertEqual(elements, 0)
   }
 
-  func testConsumeBeforeYield() {
-    XCTAsyncTest {
-      let source = PassthroughMessageSource<String, Never>()
-      let sequence = PassthroughMessageSequence(consuming: source)
-
-      await withThrowingTaskGroup(of: Void.self) { group in
-        group.addTask(priority: .high) {
-          let iterator = sequence.makeAsyncIterator()
-          if let next = try await iterator.next() {
-            XCTAssertEqual(next, "one")
-          } else {
-            XCTFail("No value produced")
-          }
+  func testConsumeBeforeYield() async throws {
+    let source = PassthroughMessageSource<String, Never>()
+    let sequence = PassthroughMessageSequence(consuming: source)
+
+    await withThrowingTaskGroup(of: Void.self) { group in
+      group.addTask(priority: .high) {
+        let iterator = sequence.makeAsyncIterator()
+        if let next = try await iterator.next() {
+          XCTAssertEqual(next, "one")
+        } else {
+          XCTFail("No value produced")
         }
+      }
 
-        group.addTask(priority: .low) {
-          let result = source.yield("one")
-          // We can't guarantee that this task will run after the other so we *may* have a queue
-          // depth of one.
-          XCTAssert(result == .accepted(queueDepth: 0) || result == .accepted(queueDepth: 1))
-        }
+      group.addTask(priority: .low) {
+        let result = source.yield("one")
+        // We can't guarantee that this task will run after the other so we *may* have a queue
+        // depth of one.
+        XCTAssert(result == .accepted(queueDepth: 0) || result == .accepted(queueDepth: 1))
       }
     }
   }
 
-  func testConsumeBeforeFinish() {
-    XCTAsyncTest {
-      let source = PassthroughMessageSource<String, TestError>()
-      let sequence = PassthroughMessageSequence(consuming: source)
-
-      await withThrowingTaskGroup(of: Void.self) { group in
-        group.addTask(priority: .high) {
-          let iterator = sequence.makeAsyncIterator()
-          await XCTAssertThrowsError(_ = try await iterator.next()) { error in
-            XCTAssert(error is TestError)
-          }
-        }
+  func testConsumeBeforeFinish() async throws {
+    let source = PassthroughMessageSource<String, TestError>()
+    let sequence = PassthroughMessageSequence(consuming: source)
 
-        group.addTask(priority: .low) {
-          let result = source.finish(throwing: TestError())
-          // We can't guarantee that this task will run after the other so we *may* have a queue
-          // depth of one.
-          XCTAssert(result == .accepted(queueDepth: 0) || result == .accepted(queueDepth: 1))
+    await withThrowingTaskGroup(of: Void.self) { group in
+      group.addTask(priority: .high) {
+        let iterator = sequence.makeAsyncIterator()
+        await XCTAssertThrowsError(_ = try await iterator.next()) { error in
+          XCTAssert(error is TestError)
         }
       }
+
+      group.addTask(priority: .low) {
+        let result = source.finish(throwing: TestError())
+        // We can't guarantee that this task will run after the other so we *may* have a queue
+        // depth of one.
+        XCTAssert(result == .accepted(queueDepth: 0) || result == .accepted(queueDepth: 1))
+      }
     }
   }
 }

+ 0 - 32
Tests/GRPCTests/AsyncAwaitSupport/XCTest+AsyncAwait.swift

@@ -16,38 +16,6 @@
 #if compiler(>=5.5)
 import XCTest
 
-extension XCTestCase {
-  @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
-  /// Cross-platform XCTest support for async-await tests.
-  ///
-  /// Currently the Linux implementation of XCTest doesn't have async-await support.
-  /// Until it does, we make use of this shim which uses a detached `Task` along with
-  /// `XCTest.wait(for:timeout:)` to wrap the operation.
-  ///
-  /// - NOTE: Support for Linux is tracked by https://bugs.swift.org/browse/SR-14403.
-  /// - NOTE: Implementation currently in progress: https://github.com/apple/swift-corelibs-xctest/pull/326
-  func XCTAsyncTest(
-    expectationDescription: String = "Async operation",
-    timeout: TimeInterval = 30,
-    file: StaticString = #filePath,
-    line: UInt = #line,
-    function: StaticString = #function,
-    operation: @escaping () async throws -> Void
-  ) {
-    let expectation = self.expectation(description: expectationDescription)
-    Task {
-      do {
-        try await operation()
-      } catch {
-        XCTFail("Error thrown while executing \(function): \(error)", file: file, line: line)
-        Thread.callStackSymbols.forEach { print($0) }
-      }
-      expectation.fulfill()
-    }
-    self.wait(for: [expectation], timeout: timeout)
-  }
-}
-
 @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
 internal func XCTAssertThrowsError<T>(
   _ expression: @autoclosure () async throws -> T,

+ 12 - 12
Tests/GRPCTests/GRPCAsyncClientCallTests.swift

@@ -72,7 +72,7 @@ class GRPCAsyncClientCallTests: GRPCTestCase {
     super.tearDown()
   }
 
-  func testAsyncUnaryCall() throws { XCTAsyncTest {
+  func testAsyncUnaryCall() async throws {
     let channel = try self.setUpServerAndChannel()
     let get: GRPCAsyncUnaryCall<Echo_EchoRequest, Echo_EchoResponse> = channel.makeAsyncUnaryCall(
       path: "/echo.Echo/Get",
@@ -85,9 +85,9 @@ class GRPCAsyncClientCallTests: GRPCTestCase {
     await assertThat(try await get.trailingMetadata, .is(.equalTo(Self.OKTrailingMetadata)))
     await assertThat(await get.status, .hasCode(.ok))
     print(try await get.trailingMetadata)
-  } }
+  }
 
-  func testAsyncClientStreamingCall() throws { XCTAsyncTest {
+  func testAsyncClientStreamingCall() async throws {
     let channel = try self.setUpServerAndChannel()
     let collect: GRPCAsyncClientStreamingCall<Echo_EchoRequest, Echo_EchoResponse> = channel
       .makeAsyncClientStreamingCall(
@@ -104,9 +104,9 @@ class GRPCAsyncClientCallTests: GRPCTestCase {
     await assertThat(try await collect.response, .doesNotThrow())
     await assertThat(try await collect.trailingMetadata, .is(.equalTo(Self.OKTrailingMetadata)))
     await assertThat(await collect.status, .hasCode(.ok))
-  } }
+  }
 
-  func testAsyncServerStreamingCall() throws { XCTAsyncTest {
+  func testAsyncServerStreamingCall() async throws {
     let channel = try self.setUpServerAndChannel()
     let expand: GRPCAsyncServerStreamingCall<Echo_EchoRequest, Echo_EchoResponse> = channel
       .makeAsyncServerStreamingCall(
@@ -122,9 +122,9 @@ class GRPCAsyncClientCallTests: GRPCTestCase {
     await assertThat(numResponses, .is(.equalTo(3)))
     await assertThat(try await expand.trailingMetadata, .is(.equalTo(Self.OKTrailingMetadata)))
     await assertThat(await expand.status, .hasCode(.ok))
-  } }
+  }
 
-  func testAsyncBidirectionalStreamingCall() throws { XCTAsyncTest {
+  func testAsyncBidirectionalStreamingCall() async throws {
     let channel = try self.setUpServerAndChannel()
     let update: GRPCAsyncBidirectionalStreamingCall<Echo_EchoRequest, Echo_EchoResponse> = channel
       .makeAsyncBidirectionalStreamingCall(
@@ -142,9 +142,9 @@ class GRPCAsyncClientCallTests: GRPCTestCase {
     await assertThat(numResponses, .is(.equalTo(3)))
     await assertThat(try await update.trailingMetadata, .is(.equalTo(Self.OKTrailingMetadata)))
     await assertThat(await update.status, .hasCode(.ok))
-  } }
+  }
 
-  func testAsyncBidirectionalStreamingCall_InterleavedRequestsAndResponses() throws { XCTAsyncTest {
+  func testAsyncBidirectionalStreamingCall_InterleavedRequestsAndResponses() async throws {
     let channel = try self.setUpServerAndChannel()
     let update: GRPCAsyncBidirectionalStreamingCall<Echo_EchoRequest, Echo_EchoResponse> = channel
       .makeAsyncBidirectionalStreamingCall(
@@ -166,9 +166,9 @@ class GRPCAsyncClientCallTests: GRPCTestCase {
 
     await assertThat(try await update.trailingMetadata, .is(.equalTo(Self.OKTrailingMetadata)))
     await assertThat(await update.status, .hasCode(.ok))
-  } }
+  }
 
-  func testAsyncBidirectionalStreamingCall_ConcurrentTasks() throws { XCTAsyncTest {
+  func testAsyncBidirectionalStreamingCall_ConcurrentTasks() async throws {
     let channel = try self.setUpServerAndChannel()
     let update: GRPCAsyncBidirectionalStreamingCall<Echo_EchoRequest, Echo_EchoResponse> = channel
       .makeAsyncBidirectionalStreamingCall(
@@ -202,7 +202,7 @@ class GRPCAsyncClientCallTests: GRPCTestCase {
     await assertThat(await counter.numResponses, .is(.equalTo(3)))
     await assertThat(try await update.trailingMetadata, .is(.equalTo(Self.OKTrailingMetadata)))
     await assertThat(await update.status, .hasCode(.ok))
-  } }
+  }
 }
 
 // Workaround https://bugs.swift.org/browse/SR-15070 (compiler crashes when defining a class/actor

+ 34 - 34
Tests/GRPCTests/GRPCAsyncServerHandlerTests.swift

@@ -68,7 +68,7 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     XCTFail("This observer should never be called")
   }
 
-  func testHappyPath() { XCTAsyncTest {
+  func testHappyPath() async throws {
     let handler = self.makeHandler(
       observer: self.echo(requests:responseStreamWriter:context:)
     )
@@ -92,9 +92,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.messageMetadata.map { $0.compress }, .is([false, false, false]))
     await assertThat(self.recorder.status, .notNil(.hasCode(.ok)))
     await assertThat(self.recorder.trailers, .is([:]))
-  } }
+  }
 
-  func testHappyPathWithCompressionEnabled() { XCTAsyncTest {
+  func testHappyPathWithCompressionEnabled() async throws {
     let handler = self.makeHandler(
       encoding: .enabled(.init(decompressionLimit: .absolute(.max))),
       observer: self.echo(requests:responseStreamWriter:context:)
@@ -114,9 +114,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
       .is([ByteBuffer(string: "1"), ByteBuffer(string: "2"), ByteBuffer(string: "3")])
     )
     await assertThat(self.recorder.messageMetadata.map { $0.compress }, .is([true, true, true]))
-  } }
+  }
 
-  func testHappyPathWithCompressionEnabledButDisabledByCaller() { XCTAsyncTest {
+  func testHappyPathWithCompressionEnabledButDisabledByCaller() async throws {
     let handler = self.makeHandler(
       encoding: .enabled(.init(decompressionLimit: .absolute(.max)))
     ) { requests, responseStreamWriter, context in
@@ -142,9 +142,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
       .is([ByteBuffer(string: "1"), ByteBuffer(string: "2"), ByteBuffer(string: "3")])
     )
     await assertThat(self.recorder.messageMetadata.map { $0.compress }, .is([false, false, false]))
-  } }
+  }
 
-  func testResponseHeadersAndTrailersSentFromContext() { XCTAsyncTest {
+  func testResponseHeadersAndTrailersSentFromContext() async throws {
     let handler = self.makeHandler { _, responseStreamWriter, context in
       context.initialResponseMetadata = ["pontiac": "bandit"]
       try await responseStreamWriter.send("1")
@@ -158,9 +158,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
 
     await assertThat(self.recorder.metadata, .is(["pontiac": "bandit"]))
     await assertThat(self.recorder.trailers, .is(["disco": "strangler"]))
-  } }
+  }
 
-  func testResponseHeadersDroppedIfSetAfterFirstResponse() { XCTAsyncTest {
+  func testResponseHeadersDroppedIfSetAfterFirstResponse() async throws {
     let handler = self.makeHandler { _, responseStreamWriter, context in
       try await responseStreamWriter.send("1")
       context.initialResponseMetadata = ["pontiac": "bandit"]
@@ -174,9 +174,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
 
     await assertThat(self.recorder.metadata, .is([:]))
     await assertThat(self.recorder.trailers, .is(["disco": "strangler"]))
-  } }
+  }
 
-  func testTaskOnlyCreatedAfterHeaders() { XCTAsyncTest {
+  func testTaskOnlyCreatedAfterHeaders() async throws {
     let handler = self.makeHandler(observer: self.echo(requests:responseStreamWriter:context:))
 
     await assertThat(handler.userHandlerTask, .nil())
@@ -184,9 +184,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     handler.receiveMetadata([:])
 
     await assertThat(handler.userHandlerTask, .notNil())
-  } }
+  }
 
-  func testThrowingDeserializer() { XCTAsyncTest {
+  func testThrowingDeserializer() async throws {
     let handler = AsyncServerHandler(
       context: self.makeCallHandlerContext(),
       requestDeserializer: ThrowingStringDeserializer(),
@@ -204,9 +204,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.metadata, .nil())
     await assertThat(self.recorder.messages, .isEmpty())
     await assertThat(self.recorder.status, .notNil(.hasCode(.internalError)))
-  } }
+  }
 
-  func testThrowingSerializer() { XCTAsyncTest {
+  func testThrowingSerializer() async throws {
     let handler = AsyncServerHandler(
       context: self.makeCallHandlerContext(),
       requestDeserializer: StringDeserializer(),
@@ -225,9 +225,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.metadata, .is([:]))
     await assertThat(self.recorder.messages, .isEmpty())
     await assertThat(self.recorder.status, .notNil(.hasCode(.internalError)))
-  } }
+  }
 
-  func testReceiveMessageBeforeHeaders() { XCTAsyncTest {
+  func testReceiveMessageBeforeHeaders() async throws {
     let handler = self
       .makeHandler(observer: self.neverCalled(requests:responseStreamWriter:context:))
 
@@ -239,9 +239,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.metadata, .nil())
     await assertThat(self.recorder.messages, .isEmpty())
     await assertThat(self.recorder.status, .notNil(.hasCode(.internalError)))
-  } }
+  }
 
-  func testReceiveMultipleHeaders() { XCTAsyncTest {
+  func testReceiveMultipleHeaders() async throws {
     let handler = self
       .makeHandler(observer: self.neverReceivesMessage(requests:responseStreamWriter:context:))
 
@@ -254,9 +254,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.metadata, .nil())
     await assertThat(self.recorder.messages, .isEmpty())
     await assertThat(self.recorder.status, .notNil(.hasCode(.internalError)))
-  } }
+  }
 
-  func testFinishBeforeStarting() { XCTAsyncTest {
+  func testFinishBeforeStarting() async throws {
     let handler = self
       .makeHandler(observer: self.neverCalled(requests:responseStreamWriter:context:))
 
@@ -265,9 +265,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.messages, .isEmpty())
     await assertThat(self.recorder.status, .nil())
     await assertThat(self.recorder.trailers, .nil())
-  } }
+  }
 
-  func testFinishAfterHeaders() { XCTAsyncTest {
+  func testFinishAfterHeaders() async throws {
     let handler = self.makeHandler(observer: self.echo(requests:responseStreamWriter:context:))
 
     handler.receiveMetadata([:])
@@ -280,9 +280,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.messages, .isEmpty())
     await assertThat(self.recorder.status, .nil())
     await assertThat(self.recorder.trailers, .nil())
-  } }
+  }
 
-  func testFinishAfterMessage() { XCTAsyncTest {
+  func testFinishAfterMessage() async throws {
     let handler = self.makeHandler(observer: self.echo(requests:responseStreamWriter:context:))
 
     handler.receiveMetadata([:])
@@ -299,9 +299,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.messages.first, .is(ByteBuffer(string: "hello")))
     await assertThat(self.recorder.status, .nil())
     await assertThat(self.recorder.trailers, .nil())
-  } }
+  }
 
-  func testErrorAfterHeaders() { XCTAsyncTest {
+  func testErrorAfterHeaders() async throws {
     let handler = self.makeHandler(observer: self.echo(requests:responseStreamWriter:context:))
 
     handler.receiveMetadata([:])
@@ -312,9 +312,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
 
     await assertThat(self.recorder.status, .notNil(.hasCode(.unavailable)))
     await assertThat(self.recorder.trailers, .is([:]))
-  } }
+  }
 
-  func testErrorAfterMessage() { XCTAsyncTest {
+  func testErrorAfterMessage() async throws {
     let handler = self.makeHandler(observer: self.echo(requests:responseStreamWriter:context:))
 
     handler.receiveMetadata([:])
@@ -331,9 +331,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
     await assertThat(self.recorder.messages.first, .is(ByteBuffer(string: "hello")))
     await assertThat(self.recorder.status, .notNil(.hasCode(.unavailable)))
     await assertThat(self.recorder.trailers, .is([:]))
-  } }
+  }
 
-  func testHandlerThrowsGRPCStatusOKResultsInUnknownStatus() { XCTAsyncTest {
+  func testHandlerThrowsGRPCStatusOKResultsInUnknownStatus() async throws {
     // Create a user function that immediately throws GRPCStatus.ok.
     let handler = self.makeHandler { _, _, _ in
       throw GRPCStatus.ok
@@ -347,9 +347,9 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
 
     // Check the status is `.unknown`.
     await assertThat(self.recorder.status, .notNil(.hasCode(.unknown)))
-  } }
+  }
 
-  func testResponseStreamDrain() { XCTAsyncTest {
+  func testResponseStreamDrain() async throws {
     // Set up echo handler.
     let handler = self.makeHandler(
       observer: self.echo(requests:responseStreamWriter:context:)
@@ -379,6 +379,6 @@ class AsyncServerHandlerTests: ServerHandlerTestCaseBase {
       ByteBuffer(string: "santiago"),
     ]))
     await assertThat(self.recorder.status, .notNil(.hasCode(.ok)))
-  } }
+  }
 }
 #endif