Browse Source

Add back GRPCPayload support for server handlers (#894)

Motivation:

Recent work in #886 and #889 made `GRPCProtobufPayload` redundant. Since
we broke this work into multiple PRs we temporarily dropped support for
custom `GRPCPayload`s on the server. This PR adds that back.

Modifications:

- Add `GRPCPayload` support back to the server by adding the relevant
  call handler factory functions
- Re-enable the custom payload tests
- Add a few more custom payload tests (since they were limited to just
  bidirectional streaming)

Result:

- Clients and servers support `SwiftProtobuf.Message` and `GRPCPayload`
  separately without using `GRPCProtobufPayload` to bridge between them.
George Barnett 5 years ago
parent
commit
183e380435

+ 62 - 14
Sources/GRPC/CallHandlers/CallHandlerFactory.swift

@@ -1,18 +1,18 @@
 /*
-* Copyright 2020, gRPC Authors All rights reserved.
-*
-* 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.
-*/
+ * Copyright 2020, gRPC Authors All rights reserved.
+ *
+ * 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.
+ */
 import NIO
 import SwiftProtobuf
 
@@ -34,6 +34,18 @@ public enum CallHandlerFactory {
     )
   }
 
+  public static func makeUnary<Request: GRPCPayload, Response: GRPCPayload>(
+    callHandlerContext: CallHandlerContext,
+    eventObserverFactory: @escaping (UnaryContext<Response>) -> UnaryEventObserver<Request, Response>
+  ) -> UnaryCallHandler<Request, Response> {
+    return UnaryCallHandler(
+      serializer: GRPCPayloadSerializer(),
+      deserializer: GRPCPayloadDeserializer(),
+      callHandlerContext: callHandlerContext,
+      eventObserverFactory: eventObserverFactory
+    )
+  }
+
   public typealias ClientStreamingContext<Response> = UnaryResponseCallContext<Response>
   public typealias ClientStreamingEventObserver<Request> = EventLoopFuture<(StreamEvent<Request>) -> Void>
 
@@ -49,6 +61,18 @@ public enum CallHandlerFactory {
     )
   }
 
+  public static func makeClientStreaming<Request: GRPCPayload, Response: GRPCPayload>(
+    callHandlerContext: CallHandlerContext,
+    eventObserverFactory: @escaping (ClientStreamingContext<Response>) -> ClientStreamingEventObserver<Request>
+  ) -> ClientStreamingCallHandler<Request, Response> {
+    return ClientStreamingCallHandler(
+      serializer: GRPCPayloadSerializer(),
+      deserializer: GRPCPayloadDeserializer(),
+      callHandlerContext: callHandlerContext,
+      eventObserverFactory: eventObserverFactory
+    )
+  }
+
   public typealias ServerStreamingContext<Response> = StreamingResponseCallContext<Response>
   public typealias ServerStreamingEventObserver<Request> = (Request) -> EventLoopFuture<GRPCStatus>
 
@@ -64,6 +88,18 @@ public enum CallHandlerFactory {
     )
   }
 
+  public static func makeServerStreaming<Request: GRPCPayload, Response: GRPCPayload>(
+    callHandlerContext: CallHandlerContext,
+    eventObserverFactory: @escaping (ServerStreamingContext<Response>) -> ServerStreamingEventObserver<Request>
+  ) -> ServerStreamingCallHandler<Request, Response> {
+    return ServerStreamingCallHandler(
+      serializer: GRPCPayloadSerializer(),
+      deserializer: GRPCPayloadDeserializer(),
+      callHandlerContext: callHandlerContext,
+      eventObserverFactory: eventObserverFactory
+    )
+  }
+
   public typealias BidirectionalStreamingContext<Response> = StreamingResponseCallContext<Response>
   public typealias BidirectionalStreamingEventObserver<Request> = EventLoopFuture<(StreamEvent<Request>) -> Void>
 
@@ -78,4 +114,16 @@ public enum CallHandlerFactory {
       eventObserverFactory: eventObserverFactory
     )
   }
+
+  public static func makeBidirectionalStreaming<Request: GRPCPayload, Response: GRPCPayload>(
+    callHandlerContext: CallHandlerContext,
+    eventObserverFactory: @escaping (BidirectionalStreamingContext<Response>) -> BidirectionalStreamingEventObserver<Request>
+  ) -> BidirectionalStreamingCallHandler<Request, Response> {
+    return BidirectionalStreamingCallHandler(
+      serializer: GRPCPayloadSerializer(),
+      deserializer: GRPCPayloadDeserializer(),
+      callHandlerContext: callHandlerContext,
+      eventObserverFactory: eventObserverFactory
+    )
+  }
 }

+ 291 - 185
Tests/GRPCTests/GRPCCustomPayloadTests.swift

@@ -17,188 +17,294 @@ import GRPC
 import NIO
 import XCTest
 
-// TODO: Fixup these tests when we support custom server payloads again.
-
-//// These tests demonstrate how to use gRPC to create a service provider using your own payload type,
-//// or alternatively, how to avoid deserialization and just extract the raw bytes from a payload.
-//class GRPCCustomPayloadTests: GRPCTestCase {
-//  var group: EventLoopGroup!
-//  var server: Server!
-//  var client: AnyServiceClient!
-//
-//  override func setUp() {
-//    super.setUp()
-//    self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
-//
-//    self.server = try! Server.insecure(group: self.group)
-//      .withServiceProviders([CustomPayloadProvider()])
-//      .bind(host: "localhost", port: 0)
-//      .wait()
-//
-//    let channel = ClientConnection.insecure(group: self.group)
-//      .connect(host: "localhost", port: server.channel.localAddress!.port!)
-//
-//    self.client = AnyServiceClient(channel: channel)
-//  }
-//
-//  override func tearDown() {
-//    XCTAssertNoThrow(try self.server.close().wait())
-//    XCTAssertNoThrow(try self.client.channel.close().wait())
-//    XCTAssertNoThrow(try self.group.syncShutdownGracefully())
-//  }
-//
-//  func testCustomPayload() throws {
-//    // This test demonstrates how to call a manually created bidirectional RPC with custom payloads.
-//    let statusExpectation = self.expectation(description: "status received")
-//
-//    var responses: [CustomPayload] = []
-//
-//    // Make a bidirectional stream using `CustomPayload` as the request and response type.
-//    // The service defined below is called "CustomPayload", and the method we call on it
-//    // is "AddOneAndReverseMessage"
-//    let rpc: BidirectionalStreamingCall<CustomPayload, CustomPayload> = self.client.makeBidirectionalStreamingCall(
-//      path: "/CustomPayload/AddOneAndReverseMessage",
-//      handler: { responses.append($0) }
-//    )
-//
-//    // Make and send some requests:
-//    let requests: [CustomPayload] = [
-//      CustomPayload(message: "one", number: .random(in: Int64.min..<Int64.max)),
-//      CustomPayload(message: "two", number: .random(in: Int64.min..<Int64.max)),
-//      CustomPayload(message: "three", number: .random(in: Int64.min..<Int64.max))
-//    ]
-//    rpc.sendMessages(requests, promise: nil)
-//    rpc.sendEnd(promise: nil)
-//
-//    // Wait for the RPC to finish before comparing responses.
-//    rpc.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation)
-//    self.wait(for: [statusExpectation], timeout: 1.0)
-//
-//    // Are the responses as expected?
-//    let expected = requests.map { request in
-//      CustomPayload(message: String(request.message.reversed()), number: request.number + 1)
-//    }
-//    XCTAssertEqual(responses, expected)
-//  }
-//
-//  func testNoDeserializationOnTheClient() throws {
-//    // This test demonstrates how to skip the deserialization step on the client. It isn't necessary
-//    // to use a custom service provider to do this, although we do here.
-//    let statusExpectation = self.expectation(description: "status received")
-//
-//    var responses: [IdentityPayload] = []
-//    // Here we use `IdentityPayload` for our response type: we define it below such that it does
-//    // not deserialize the bytes provided to it by gRPC.
-//    let rpc: BidirectionalStreamingCall<CustomPayload, IdentityPayload> = self.client.makeBidirectionalStreamingCall(
-//      path: "/CustomPayload/AddOneAndReverseMessage",
-//      handler: { responses.append($0) }
-//    )
-//
-//    let request = CustomPayload(message: "message", number: 42)
-//    rpc.sendMessage(request, promise: nil)
-//    rpc.sendEnd(promise: nil)
-//
-//    // Wait for the RPC to finish before comparing responses.
-//    rpc.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation)
-//    self.wait(for: [statusExpectation], timeout: 1.0)
-//
-//    guard var response = responses.first?.buffer else {
-//      XCTFail("RPC completed without a response")
-//      return
-//    }
-//
-//    // We just took the raw bytes from the payload: we can still decode it because we know the
-//    // server returned a serialized `CustomPayload`.
-//    let actual = try CustomPayload(serializedByteBuffer: &response)
-//    XCTAssertEqual(actual.message, "egassem")
-//    XCTAssertEqual(actual.number, 43)
-//  }
-//}
-//
-//// MARK: Custom Payload Service
-//
-//fileprivate class CustomPayloadProvider: CallHandlerProvider {
-//  var serviceName: String = "CustomPayload"
-//
-//  // Bidirectional RPC which returns a new `CustomPayload` for each `CustomPayload` received.
-//  // The returned payloads have their `message` reversed and their `number` incremented by one.
-//  fileprivate func addOneAndReverseMessage(
-//    context: StreamingResponseCallContext<CustomPayload>
-//  ) -> EventLoopFuture<(StreamEvent<CustomPayload>) -> Void> {
-//    return context.eventLoop.makeSucceededFuture({ event in
-//      switch event {
-//      case .message(let payload):
-//        let response = CustomPayload(
-//          message: String(payload.message.reversed()),
-//          number: payload.number + 1
-//        )
-//        _ = context.sendResponse(response)
-//
-//      case .end:
-//        context.statusPromise.succeed(.ok)
-//      }
-//    })
-//  }
-//
-//  func handleMethod(_ methodName: String, callHandlerContext: CallHandlerContext) -> GRPCCallHandler? {
-//    switch methodName {
-//    case "AddOneAndReverseMessage":
-//      return BidirectionalStreamingCallHandler<CustomPayload, CustomPayload>(callHandlerContext: callHandlerContext) { context in
-//        return self.addOneAndReverseMessage(context: context)
-//      }
-//
-//    default:
-//      return nil
-//    }
-//  }
-//}
-//
-//fileprivate struct IdentityPayload: GRPCPayload {
-//  var buffer: ByteBuffer
-//
-//  init(serializedByteBuffer: inout ByteBuffer) throws {
-//    self.buffer = serializedByteBuffer
-//  }
-//
-//  func serialize(into buffer: inout ByteBuffer) throws {
-//    // This will never be called, however, it could be implemented as a direct copy of the bytes
-//    // we hold, e.g.:
-//    //
-//    //   var copy = self.buffer
-//    //   buffer.writeBuffer(&copy)
-//    fatalError("Unimplemented")
-//  }
-//}
-//
-///// A toy custom payload which holds a `String` and an `Int64`.
-/////
-///// The payload is serialized as:
-///// - the `UInt32` encoded length of the message,
-///// - the UTF-8 encoded bytes of the message, and
-///// - the `Int64` bytes of the number.
-//fileprivate struct CustomPayload: GRPCPayload, Equatable {
-//  var message: String
-//  var number: Int64
-//
-//  init(message: String, number: Int64) {
-//    self.message = message
-//    self.number = number
-//  }
-//
-//  init(serializedByteBuffer: inout ByteBuffer) throws {
-//    guard let messageLength = serializedByteBuffer.readInteger(as: UInt32.self),
-//      let message = serializedByteBuffer.readString(length: Int(messageLength)),
-//      let number = serializedByteBuffer.readInteger(as: Int64.self) else {
-//        throw GRPCError.DeserializationFailure()
-//    }
-//
-//    self.message = message
-//    self.number = number
-//  }
-//
-//  func serialize(into buffer: inout ByteBuffer) throws {
-//    buffer.writeInteger(UInt32(self.message.count))
-//    buffer.writeString(self.message)
-//    buffer.writeInteger(self.number)
-//  }
-//}
+// These tests demonstrate how to use gRPC to create a service provider using your own payload type,
+// or alternatively, how to avoid deserialization and just extract the raw bytes from a payload.
+class GRPCCustomPayloadTests: GRPCTestCase {
+  var group: EventLoopGroup!
+  var server: Server!
+  var client: AnyServiceClient!
+
+  override func setUp() {
+    super.setUp()
+    self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
+
+    self.server = try! Server.insecure(group: self.group)
+      .withServiceProviders([CustomPayloadProvider()])
+      .bind(host: "localhost", port: 0)
+      .wait()
+
+    let channel = ClientConnection.insecure(group: self.group)
+      .connect(host: "localhost", port: server.channel.localAddress!.port!)
+
+    self.client = AnyServiceClient(channel: channel)
+  }
+
+  override func tearDown() {
+    XCTAssertNoThrow(try self.server.close().wait())
+    XCTAssertNoThrow(try self.client.channel.close().wait())
+    XCTAssertNoThrow(try self.group.syncShutdownGracefully())
+  }
+
+  func testCustomPayload() throws {
+    // This test demonstrates how to call a manually created bidirectional RPC with custom payloads.
+    let statusExpectation = self.expectation(description: "status received")
+
+    var responses: [CustomPayload] = []
+
+    // Make a bidirectional stream using `CustomPayload` as the request and response type.
+    // The service defined below is called "CustomPayload", and the method we call on it
+    // is "AddOneAndReverseMessage"
+    let rpc: BidirectionalStreamingCall<CustomPayload, CustomPayload> = self.client.makeBidirectionalStreamingCall(
+      path: "/CustomPayload/AddOneAndReverseMessage",
+      handler: { responses.append($0) }
+    )
+
+    // Make and send some requests:
+    let requests: [CustomPayload] = [
+      CustomPayload(message: "one", number: .random(in: Int64.min..<Int64.max)),
+      CustomPayload(message: "two", number: .random(in: Int64.min..<Int64.max)),
+      CustomPayload(message: "three", number: .random(in: Int64.min..<Int64.max))
+    ]
+    rpc.sendMessages(requests, promise: nil)
+    rpc.sendEnd(promise: nil)
+
+    // Wait for the RPC to finish before comparing responses.
+    rpc.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation)
+    self.wait(for: [statusExpectation], timeout: 1.0)
+
+    // Are the responses as expected?
+    let expected = requests.map { request in
+      CustomPayload(message: String(request.message.reversed()), number: request.number + 1)
+    }
+    XCTAssertEqual(responses, expected)
+  }
+
+  func testNoDeserializationOnTheClient() throws {
+    // This test demonstrates how to skip the deserialization step on the client. It isn't necessary
+    // to use a custom service provider to do this, although we do here.
+    let statusExpectation = self.expectation(description: "status received")
+
+    var responses: [IdentityPayload] = []
+    // Here we use `IdentityPayload` for our response type: we define it below such that it does
+    // not deserialize the bytes provided to it by gRPC.
+    let rpc: BidirectionalStreamingCall<CustomPayload, IdentityPayload> = self.client.makeBidirectionalStreamingCall(
+      path: "/CustomPayload/AddOneAndReverseMessage",
+      handler: { responses.append($0) }
+    )
+
+    let request = CustomPayload(message: "message", number: 42)
+    rpc.sendMessage(request, promise: nil)
+    rpc.sendEnd(promise: nil)
+
+    // Wait for the RPC to finish before comparing responses.
+    rpc.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation)
+    self.wait(for: [statusExpectation], timeout: 1.0)
+
+    guard var response = responses.first?.buffer else {
+      XCTFail("RPC completed without a response")
+      return
+    }
+
+    // We just took the raw bytes from the payload: we can still decode it because we know the
+    // server returned a serialized `CustomPayload`.
+    let actual = try CustomPayload(serializedByteBuffer: &response)
+    XCTAssertEqual(actual.message, "egassem")
+    XCTAssertEqual(actual.number, 43)
+  }
+
+  func testCustomPayloadUnary() throws {
+    let rpc: UnaryCall<StringPayload, StringPayload> = self.client.makeUnaryCall(
+      path: "/CustomPayload/Reverse",
+      request: StringPayload(message: "foobarbaz")
+    )
+
+    XCTAssertEqual(try rpc.response.map { $0.message }.wait(), "zabraboof")
+    XCTAssertEqual(try rpc.status.map { $0.code }.wait(), .ok)
+  }
+
+  func testCustomPayloadClientStreaming() throws {
+    let rpc: ClientStreamingCall<StringPayload, StringPayload> = self.client.makeClientStreamingCall(path: "/CustomPayload/ReverseThenJoin")
+    rpc.sendMessages(["foo", "bar", "baz"].map(StringPayload.init(message:)), promise: nil)
+    rpc.sendEnd(promise: nil)
+
+    XCTAssertEqual(try rpc.response.map { $0.message }.wait(), "baz bar foo")
+    XCTAssertEqual(try rpc.status.map { $0.code }.wait(), .ok)
+  }
+
+  func testCustomPayloadServerStreaming() throws {
+    let message = "abc"
+    var expectedIterator = message.reversed().makeIterator()
+
+    let rpc: ServerStreamingCall<StringPayload, StringPayload> = self.client.makeServerStreamingCall(
+      path: "/CustomPayload/ReverseThenSplit",
+      request: StringPayload(message: message)
+    ) { response in
+      if let next = expectedIterator.next() {
+        XCTAssertEqual(String(next), response.message)
+      } else {
+        XCTFail("Unexpected message: \(response.message)")
+      }
+    }
+
+    XCTAssertEqual(try rpc.status.map { $0.code }.wait(), .ok)
+  }
+}
+
+// MARK: Custom Payload Service
+
+fileprivate class CustomPayloadProvider: CallHandlerProvider {
+  var serviceName: String = "CustomPayload"
+
+  fileprivate func reverseString(
+    request: StringPayload,
+    context: UnaryResponseCallContext<StringPayload>
+  ) -> EventLoopFuture<StringPayload> {
+    let reversed = StringPayload(message: String(request.message.reversed()))
+    return context.eventLoop.makeSucceededFuture(reversed)
+  }
+
+  fileprivate func reverseThenJoin(
+    context: UnaryResponseCallContext<StringPayload>
+  ) -> EventLoopFuture<(StreamEvent<StringPayload>) -> Void> {
+    var messages: [String] = []
+
+    return context.eventLoop.makeSucceededFuture({ event in
+      switch event {
+      case .message(let request):
+        messages.append(request.message)
+
+      case .end:
+        let response = messages.reversed().joined(separator: " ")
+        context.responsePromise.succeed(StringPayload(message: response))
+      }
+    })
+  }
+
+  fileprivate func reverseThenSplit(
+    request: StringPayload,
+    context: StreamingResponseCallContext<StringPayload>
+  ) -> EventLoopFuture<GRPCStatus> {
+    let responses = request.message.reversed().map {
+      context.sendResponse(StringPayload(message: String($0)))
+    }
+
+    return EventLoopFuture.andAllSucceed(responses, on: context.eventLoop).map { .ok }
+  }
+
+  // Bidirectional RPC which returns a new `CustomPayload` for each `CustomPayload` received.
+  // The returned payloads have their `message` reversed and their `number` incremented by one.
+  fileprivate func addOneAndReverseMessage(
+    context: StreamingResponseCallContext<CustomPayload>
+  ) -> EventLoopFuture<(StreamEvent<CustomPayload>) -> Void> {
+    return context.eventLoop.makeSucceededFuture({ event in
+      switch event {
+      case .message(let payload):
+        let response = CustomPayload(
+          message: String(payload.message.reversed()),
+          number: payload.number + 1
+        )
+        _ = context.sendResponse(response)
+
+      case .end:
+        context.statusPromise.succeed(.ok)
+      }
+    })
+  }
+
+  func handleMethod(_ methodName: String, callHandlerContext: CallHandlerContext) -> GRPCCallHandler? {
+    switch methodName {
+    case "Reverse":
+      return CallHandlerFactory.makeUnary(callHandlerContext: callHandlerContext) { context in
+        return { request in
+          return self.reverseString(request: request, context: context)
+        }
+      }
+
+    case "ReverseThenJoin":
+      return CallHandlerFactory.makeClientStreaming(callHandlerContext: callHandlerContext) { context in
+        return self.reverseThenJoin(context: context)
+      }
+
+    case "ReverseThenSplit":
+      return CallHandlerFactory.makeServerStreaming(callHandlerContext: callHandlerContext) { context in
+        return { request in
+          return self.reverseThenSplit(request: request, context: context)
+        }
+      }
+
+    case "AddOneAndReverseMessage":
+      return CallHandlerFactory.makeBidirectionalStreaming(callHandlerContext: callHandlerContext) { context in
+        return self.addOneAndReverseMessage(context: context)
+      }
+
+    default:
+      return nil
+    }
+  }
+}
+
+fileprivate struct IdentityPayload: GRPCPayload {
+  var buffer: ByteBuffer
+
+  init(serializedByteBuffer: inout ByteBuffer) throws {
+    self.buffer = serializedByteBuffer
+  }
+
+  func serialize(into buffer: inout ByteBuffer) throws {
+    // This will never be called, however, it could be implemented as a direct copy of the bytes
+    // we hold, e.g.:
+    //
+    //   var copy = self.buffer
+    //   buffer.writeBuffer(&copy)
+    fatalError("Unimplemented")
+  }
+}
+
+/// A toy custom payload which holds a `String` and an `Int64`.
+///
+/// The payload is serialized as:
+/// - the `UInt32` encoded length of the message,
+/// - the UTF-8 encoded bytes of the message, and
+/// - the `Int64` bytes of the number.
+fileprivate struct CustomPayload: GRPCPayload, Equatable {
+  var message: String
+  var number: Int64
+
+  init(message: String, number: Int64) {
+    self.message = message
+    self.number = number
+  }
+
+  init(serializedByteBuffer: inout ByteBuffer) throws {
+    guard let messageLength = serializedByteBuffer.readInteger(as: UInt32.self),
+      let message = serializedByteBuffer.readString(length: Int(messageLength)),
+      let number = serializedByteBuffer.readInteger(as: Int64.self) else {
+        throw GRPCError.DeserializationFailure()
+    }
+
+    self.message = message
+    self.number = number
+  }
+
+  func serialize(into buffer: inout ByteBuffer) throws {
+    buffer.writeInteger(UInt32(self.message.count))
+    buffer.writeString(self.message)
+    buffer.writeInteger(self.number)
+  }
+}
+
+fileprivate struct StringPayload: GRPCPayload {
+  var message: String
+
+  init(message: String) {
+    self.message = message
+  }
+
+  init(serializedByteBuffer: inout ByteBuffer) throws {
+    self.message = serializedByteBuffer.readString(length: serializedByteBuffer.readableBytes)!
+  }
+
+  func serialize(into buffer: inout ByteBuffer) throws {
+    buffer.writeString(self.message)
+  }
+}

+ 14 - 0
Tests/GRPCTests/XCTestManifests.swift

@@ -443,6 +443,19 @@ extension GRPCClientStateMachineTests {
     ]
 }
 
+extension GRPCCustomPayloadTests {
+    // DO NOT MODIFY: This is autogenerated, use:
+    //   `swift test --generate-linuxmain`
+    // to regenerate.
+    static let __allTests__GRPCCustomPayloadTests = [
+        ("testCustomPayload", testCustomPayload),
+        ("testCustomPayloadClientStreaming", testCustomPayloadClientStreaming),
+        ("testCustomPayloadServerStreaming", testCustomPayloadServerStreaming),
+        ("testCustomPayloadUnary", testCustomPayloadUnary),
+        ("testNoDeserializationOnTheClient", testNoDeserializationOnTheClient),
+    ]
+}
+
 extension GRPCIdleTests {
     // DO NOT MODIFY: This is autogenerated, use:
     //   `swift test --generate-linuxmain`
@@ -902,6 +915,7 @@ public func __allTests() -> [XCTestCaseEntry] {
         testCase(FunctionalTestsMutualAuthentication.__allTests__FunctionalTestsMutualAuthentication),
         testCase(FunctionalTestsMutualAuthenticationNIOTS.__allTests__FunctionalTestsMutualAuthenticationNIOTS),
         testCase(GRPCClientStateMachineTests.__allTests__GRPCClientStateMachineTests),
+        testCase(GRPCCustomPayloadTests.__allTests__GRPCCustomPayloadTests),
         testCase(GRPCIdleTests.__allTests__GRPCIdleTests),
         testCase(GRPCInsecureInteroperabilityTests.__allTests__GRPCInsecureInteroperabilityTests),
         testCase(GRPCPingHandlerTests.__allTests__GRPCPingHandlerTests),