Browse Source

Extend interoperability tests to use async provider (#1312)

The interoperability tests exercise a well-defined sequence of requests
for all implementations of gRPC. We already had interop tests for the
ELG-based client and provider. This pull request extends the tests to
exercise an async-await–based provider.

Signed-off-by: Si Beaumont <beaumont@apple.com>
Si Beaumont 4 years ago
parent
commit
18624790ef

+ 710 - 0
Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift

@@ -292,6 +292,288 @@ public final class Grpc_Testing_TestServiceClient: Grpc_Testing_TestServiceClien
   }
 }
 
+#if compiler(>=5.5)
+/// A simple service to test the various types of RPCs and experiment with
+/// performance with various types of payload.
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public protocol Grpc_Testing_TestServiceAsyncClientProtocol: GRPCClient {
+  var serviceName: String { get }
+  var interceptors: Grpc_Testing_TestServiceClientInterceptorFactoryProtocol? { get }
+
+  func makeEmptyCallCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_Empty>
+
+  func makeUnaryCallCall(
+    _ request: Grpc_Testing_SimpleRequest,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_SimpleRequest, Grpc_Testing_SimpleResponse>
+
+  func makeCacheableUnaryCallCall(
+    _ request: Grpc_Testing_SimpleRequest,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_SimpleRequest, Grpc_Testing_SimpleResponse>
+
+  func makeStreamingOutputCallCall(
+    _ request: Grpc_Testing_StreamingOutputCallRequest,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncServerStreamingCall<Grpc_Testing_StreamingOutputCallRequest, Grpc_Testing_StreamingOutputCallResponse>
+
+  func makeStreamingInputCallCall(
+    callOptions: CallOptions?
+  ) -> GRPCAsyncClientStreamingCall<Grpc_Testing_StreamingInputCallRequest, Grpc_Testing_StreamingInputCallResponse>
+
+  func makeFullDuplexCallCall(
+    callOptions: CallOptions?
+  ) -> GRPCAsyncBidirectionalStreamingCall<Grpc_Testing_StreamingOutputCallRequest, Grpc_Testing_StreamingOutputCallResponse>
+
+  func makeHalfDuplexCallCall(
+    callOptions: CallOptions?
+  ) -> GRPCAsyncBidirectionalStreamingCall<Grpc_Testing_StreamingOutputCallRequest, Grpc_Testing_StreamingOutputCallResponse>
+
+  func makeUnimplementedCallCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_Empty>
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_TestServiceAsyncClientProtocol {
+  public var serviceName: String {
+    return "grpc.testing.TestService"
+  }
+
+  public var interceptors: Grpc_Testing_TestServiceClientInterceptorFactoryProtocol? {
+    return nil
+  }
+
+  public func makeEmptyCallCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_Empty> {
+    return self.makeAsyncUnaryCall(
+      path: "/grpc.testing.TestService/EmptyCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeUnaryCallCall(
+    _ request: Grpc_Testing_SimpleRequest,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_SimpleRequest, Grpc_Testing_SimpleResponse> {
+    return self.makeAsyncUnaryCall(
+      path: "/grpc.testing.TestService/UnaryCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeCacheableUnaryCallCall(
+    _ request: Grpc_Testing_SimpleRequest,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_SimpleRequest, Grpc_Testing_SimpleResponse> {
+    return self.makeAsyncUnaryCall(
+      path: "/grpc.testing.TestService/CacheableUnaryCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeStreamingOutputCallCall(
+    _ request: Grpc_Testing_StreamingOutputCallRequest,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncServerStreamingCall<Grpc_Testing_StreamingOutputCallRequest, Grpc_Testing_StreamingOutputCallResponse> {
+    return self.makeAsyncServerStreamingCall(
+      path: "/grpc.testing.TestService/StreamingOutputCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeStreamingInputCallCall(
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncClientStreamingCall<Grpc_Testing_StreamingInputCallRequest, Grpc_Testing_StreamingInputCallResponse> {
+    return self.makeAsyncClientStreamingCall(
+      path: "/grpc.testing.TestService/StreamingInputCall",
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeFullDuplexCallCall(
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncBidirectionalStreamingCall<Grpc_Testing_StreamingOutputCallRequest, Grpc_Testing_StreamingOutputCallResponse> {
+    return self.makeAsyncBidirectionalStreamingCall(
+      path: "/grpc.testing.TestService/FullDuplexCall",
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeHalfDuplexCallCall(
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncBidirectionalStreamingCall<Grpc_Testing_StreamingOutputCallRequest, Grpc_Testing_StreamingOutputCallResponse> {
+    return self.makeAsyncBidirectionalStreamingCall(
+      path: "/grpc.testing.TestService/HalfDuplexCall",
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeUnimplementedCallCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_Empty> {
+    return self.makeAsyncUnaryCall(
+      path: "/grpc.testing.TestService/UnimplementedCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_TestServiceAsyncClientProtocol {
+  public func emptyCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_Empty {
+    return try await self.performAsyncUnaryCall(
+      path: "/grpc.testing.TestService/EmptyCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func unaryCall(
+    _ request: Grpc_Testing_SimpleRequest,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_SimpleResponse {
+    return try await self.performAsyncUnaryCall(
+      path: "/grpc.testing.TestService/UnaryCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func cacheableUnaryCall(
+    _ request: Grpc_Testing_SimpleRequest,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_SimpleResponse {
+    return try await self.performAsyncUnaryCall(
+      path: "/grpc.testing.TestService/CacheableUnaryCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func streamingOutputCall(
+    _ request: Grpc_Testing_StreamingOutputCallRequest,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncResponseStream<Grpc_Testing_StreamingOutputCallResponse> {
+    return self.performAsyncServerStreamingCall(
+      path: "/grpc.testing.TestService/StreamingOutputCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func streamingInputCall<RequestStream>(
+    _ requests: RequestStream,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_StreamingInputCallResponse where RequestStream: Sequence, RequestStream.Element == Grpc_Testing_StreamingInputCallRequest {
+    return try await self.performAsyncClientStreamingCall(
+      path: "/grpc.testing.TestService/StreamingInputCall",
+      requests: requests,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func streamingInputCall<RequestStream>(
+    _ requests: RequestStream,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_StreamingInputCallResponse where RequestStream: AsyncSequence, RequestStream.Element == Grpc_Testing_StreamingInputCallRequest {
+    return try await self.performAsyncClientStreamingCall(
+      path: "/grpc.testing.TestService/StreamingInputCall",
+      requests: requests,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func fullDuplexCall<RequestStream>(
+    _ requests: RequestStream,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncResponseStream<Grpc_Testing_StreamingOutputCallResponse> where RequestStream: Sequence, RequestStream.Element == Grpc_Testing_StreamingOutputCallRequest {
+    return self.performAsyncBidirectionalStreamingCall(
+      path: "/grpc.testing.TestService/FullDuplexCall",
+      requests: requests,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func fullDuplexCall<RequestStream>(
+    _ requests: RequestStream,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncResponseStream<Grpc_Testing_StreamingOutputCallResponse> where RequestStream: AsyncSequence, RequestStream.Element == Grpc_Testing_StreamingOutputCallRequest {
+    return self.performAsyncBidirectionalStreamingCall(
+      path: "/grpc.testing.TestService/FullDuplexCall",
+      requests: requests,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func halfDuplexCall<RequestStream>(
+    _ requests: RequestStream,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncResponseStream<Grpc_Testing_StreamingOutputCallResponse> where RequestStream: Sequence, RequestStream.Element == Grpc_Testing_StreamingOutputCallRequest {
+    return self.performAsyncBidirectionalStreamingCall(
+      path: "/grpc.testing.TestService/HalfDuplexCall",
+      requests: requests,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func halfDuplexCall<RequestStream>(
+    _ requests: RequestStream,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncResponseStream<Grpc_Testing_StreamingOutputCallResponse> where RequestStream: AsyncSequence, RequestStream.Element == Grpc_Testing_StreamingOutputCallRequest {
+    return self.performAsyncBidirectionalStreamingCall(
+      path: "/grpc.testing.TestService/HalfDuplexCall",
+      requests: requests,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func unimplementedCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_Empty {
+    return try await self.performAsyncUnaryCall(
+      path: "/grpc.testing.TestService/UnimplementedCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public struct Grpc_Testing_TestServiceAsyncClient: Grpc_Testing_TestServiceAsyncClientProtocol {
+  public var channel: GRPCChannel
+  public var defaultCallOptions: CallOptions
+  public var interceptors: Grpc_Testing_TestServiceClientInterceptorFactoryProtocol?
+
+  public init(
+    channel: GRPCChannel,
+    defaultCallOptions: CallOptions = CallOptions(),
+    interceptors: Grpc_Testing_TestServiceClientInterceptorFactoryProtocol? = nil
+  ) {
+    self.channel = channel
+    self.defaultCallOptions = defaultCallOptions
+    self.interceptors = interceptors
+  }
+}
+
+#endif // compiler(>=5.5)
+
 /// A simple service NOT implemented at servers so clients can test for
 /// that case.
 ///
@@ -358,6 +640,75 @@ public final class Grpc_Testing_UnimplementedServiceClient: Grpc_Testing_Unimple
   }
 }
 
+#if compiler(>=5.5)
+/// A simple service NOT implemented at servers so clients can test for
+/// that case.
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public protocol Grpc_Testing_UnimplementedServiceAsyncClientProtocol: GRPCClient {
+  var serviceName: String { get }
+  var interceptors: Grpc_Testing_UnimplementedServiceClientInterceptorFactoryProtocol? { get }
+
+  func makeUnimplementedCallCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_Empty>
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_UnimplementedServiceAsyncClientProtocol {
+  public var serviceName: String {
+    return "grpc.testing.UnimplementedService"
+  }
+
+  public var interceptors: Grpc_Testing_UnimplementedServiceClientInterceptorFactoryProtocol? {
+    return nil
+  }
+
+  public func makeUnimplementedCallCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_Empty> {
+    return self.makeAsyncUnaryCall(
+      path: "/grpc.testing.UnimplementedService/UnimplementedCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_UnimplementedServiceAsyncClientProtocol {
+  public func unimplementedCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_Empty {
+    return try await self.performAsyncUnaryCall(
+      path: "/grpc.testing.UnimplementedService/UnimplementedCall",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public struct Grpc_Testing_UnimplementedServiceAsyncClient: Grpc_Testing_UnimplementedServiceAsyncClientProtocol {
+  public var channel: GRPCChannel
+  public var defaultCallOptions: CallOptions
+  public var interceptors: Grpc_Testing_UnimplementedServiceClientInterceptorFactoryProtocol?
+
+  public init(
+    channel: GRPCChannel,
+    defaultCallOptions: CallOptions = CallOptions(),
+    interceptors: Grpc_Testing_UnimplementedServiceClientInterceptorFactoryProtocol? = nil
+  ) {
+    self.channel = channel
+    self.defaultCallOptions = defaultCallOptions
+    self.interceptors = interceptors
+  }
+}
+
+#endif // compiler(>=5.5)
+
 /// A service used to control reconnect server.
 ///
 /// Usage: instantiate `Grpc_Testing_ReconnectServiceClient`, then call methods of this protocol to make API calls.
@@ -449,6 +800,101 @@ public final class Grpc_Testing_ReconnectServiceClient: Grpc_Testing_ReconnectSe
   }
 }
 
+#if compiler(>=5.5)
+/// A service used to control reconnect server.
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public protocol Grpc_Testing_ReconnectServiceAsyncClientProtocol: GRPCClient {
+  var serviceName: String { get }
+  var interceptors: Grpc_Testing_ReconnectServiceClientInterceptorFactoryProtocol? { get }
+
+  func makeStartCall(
+    _ request: Grpc_Testing_ReconnectParams,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_ReconnectParams, Grpc_Testing_Empty>
+
+  func makeStopCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions?
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_ReconnectInfo>
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_ReconnectServiceAsyncClientProtocol {
+  public var serviceName: String {
+    return "grpc.testing.ReconnectService"
+  }
+
+  public var interceptors: Grpc_Testing_ReconnectServiceClientInterceptorFactoryProtocol? {
+    return nil
+  }
+
+  public func makeStartCall(
+    _ request: Grpc_Testing_ReconnectParams,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_ReconnectParams, Grpc_Testing_Empty> {
+    return self.makeAsyncUnaryCall(
+      path: "/grpc.testing.ReconnectService/Start",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func makeStopCall(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) -> GRPCAsyncUnaryCall<Grpc_Testing_Empty, Grpc_Testing_ReconnectInfo> {
+    return self.makeAsyncUnaryCall(
+      path: "/grpc.testing.ReconnectService/Stop",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_ReconnectServiceAsyncClientProtocol {
+  public func start(
+    _ request: Grpc_Testing_ReconnectParams,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_Empty {
+    return try await self.performAsyncUnaryCall(
+      path: "/grpc.testing.ReconnectService/Start",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+
+  public func stop(
+    _ request: Grpc_Testing_Empty,
+    callOptions: CallOptions? = nil
+  ) async throws -> Grpc_Testing_ReconnectInfo {
+    return try await self.performAsyncUnaryCall(
+      path: "/grpc.testing.ReconnectService/Stop",
+      request: request,
+      callOptions: callOptions ?? self.defaultCallOptions
+    )
+  }
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public struct Grpc_Testing_ReconnectServiceAsyncClient: Grpc_Testing_ReconnectServiceAsyncClientProtocol {
+  public var channel: GRPCChannel
+  public var defaultCallOptions: CallOptions
+  public var interceptors: Grpc_Testing_ReconnectServiceClientInterceptorFactoryProtocol?
+
+  public init(
+    channel: GRPCChannel,
+    defaultCallOptions: CallOptions = CallOptions(),
+    interceptors: Grpc_Testing_ReconnectServiceClientInterceptorFactoryProtocol? = nil
+  ) {
+    self.channel = channel
+    self.defaultCallOptions = defaultCallOptions
+    self.interceptors = interceptors
+  }
+}
+
+#endif // compiler(>=5.5)
+
 /// A simple service to test the various types of RPCs and experiment with
 /// performance with various types of payload.
 ///
@@ -600,6 +1046,158 @@ public protocol Grpc_Testing_TestServiceServerInterceptorFactoryProtocol {
   ///   Defaults to calling `self.makeInterceptors()`.
   func makeUnimplementedCallInterceptors() -> [ServerInterceptor<Grpc_Testing_Empty, Grpc_Testing_Empty>]
 }
+
+#if compiler(>=5.5)
+
+/// A simple service to test the various types of RPCs and experiment with
+/// performance with various types of payload.
+///
+/// To implement a server, implement an object which conforms to this protocol.
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public protocol Grpc_Testing_TestServiceAsyncProvider: CallHandlerProvider {
+  var interceptors: Grpc_Testing_TestServiceServerInterceptorFactoryProtocol? { get }
+
+  /// One empty request followed by one empty response.
+  @Sendable func emptyCall(
+    request: Grpc_Testing_Empty,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_Empty
+
+  /// One request followed by one response.
+  @Sendable func unaryCall(
+    request: Grpc_Testing_SimpleRequest,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_SimpleResponse
+
+  /// One request followed by one response. Response has cache control
+  /// headers set such that a caching HTTP proxy (such as GFE) can
+  /// satisfy subsequent requests.
+  @Sendable func cacheableUnaryCall(
+    request: Grpc_Testing_SimpleRequest,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_SimpleResponse
+
+  /// One request followed by a sequence of responses (streamed download).
+  /// The server returns the payload with client desired type and sizes.
+  @Sendable func streamingOutputCall(
+    request: Grpc_Testing_StreamingOutputCallRequest,
+    responseStream: GRPCAsyncResponseStreamWriter<Grpc_Testing_StreamingOutputCallResponse>,
+    context: GRPCAsyncServerCallContext
+  ) async throws
+
+  /// A sequence of requests followed by one response (streamed upload).
+  /// The server returns the aggregated size of client payload as the result.
+  @Sendable func streamingInputCall(
+    requests: GRPCAsyncRequestStream<Grpc_Testing_StreamingInputCallRequest>,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_StreamingInputCallResponse
+
+  /// A sequence of requests with each request served by the server immediately.
+  /// As one request could lead to multiple responses, this interface
+  /// demonstrates the idea of full duplexing.
+  @Sendable func fullDuplexCall(
+    requests: GRPCAsyncRequestStream<Grpc_Testing_StreamingOutputCallRequest>,
+    responseStream: GRPCAsyncResponseStreamWriter<Grpc_Testing_StreamingOutputCallResponse>,
+    context: GRPCAsyncServerCallContext
+  ) async throws
+
+  /// A sequence of requests followed by a sequence of responses.
+  /// The server buffers all the client requests and then serves them in order. A
+  /// stream of responses are returned to the client when the server starts with
+  /// first request.
+  @Sendable func halfDuplexCall(
+    requests: GRPCAsyncRequestStream<Grpc_Testing_StreamingOutputCallRequest>,
+    responseStream: GRPCAsyncResponseStreamWriter<Grpc_Testing_StreamingOutputCallResponse>,
+    context: GRPCAsyncServerCallContext
+  ) async throws
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_TestServiceAsyncProvider {
+  public var serviceName: Substring {
+    return "grpc.testing.TestService"
+  }
+
+  public var interceptors: Grpc_Testing_TestServiceServerInterceptorFactoryProtocol? {
+    return nil
+  }
+
+  public func handle(
+    method name: Substring,
+    context: CallHandlerContext
+  ) -> GRPCServerHandlerProtocol? {
+    switch name {
+    case "EmptyCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_Empty>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_Empty>(),
+        interceptors: self.interceptors?.makeEmptyCallInterceptors() ?? [],
+        wrapping: self.emptyCall(request:context:)
+      )
+
+    case "UnaryCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_SimpleRequest>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_SimpleResponse>(),
+        interceptors: self.interceptors?.makeUnaryCallInterceptors() ?? [],
+        wrapping: self.unaryCall(request:context:)
+      )
+
+    case "CacheableUnaryCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_SimpleRequest>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_SimpleResponse>(),
+        interceptors: self.interceptors?.makeCacheableUnaryCallInterceptors() ?? [],
+        wrapping: self.cacheableUnaryCall(request:context:)
+      )
+
+    case "StreamingOutputCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_StreamingOutputCallRequest>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_StreamingOutputCallResponse>(),
+        interceptors: self.interceptors?.makeStreamingOutputCallInterceptors() ?? [],
+        wrapping: self.streamingOutputCall(request:responseStream:context:)
+      )
+
+    case "StreamingInputCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_StreamingInputCallRequest>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_StreamingInputCallResponse>(),
+        interceptors: self.interceptors?.makeStreamingInputCallInterceptors() ?? [],
+        wrapping: self.streamingInputCall(requests:context:)
+      )
+
+    case "FullDuplexCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_StreamingOutputCallRequest>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_StreamingOutputCallResponse>(),
+        interceptors: self.interceptors?.makeFullDuplexCallInterceptors() ?? [],
+        wrapping: self.fullDuplexCall(requests:responseStream:context:)
+      )
+
+    case "HalfDuplexCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_StreamingOutputCallRequest>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_StreamingOutputCallResponse>(),
+        interceptors: self.interceptors?.makeHalfDuplexCallInterceptors() ?? [],
+        wrapping: self.halfDuplexCall(requests:responseStream:context:)
+      )
+
+    default:
+      return nil
+    }
+  }
+}
+
+#endif // compiler(>=5.5)
+
 /// A simple service NOT implemented at servers so clients can test for
 /// that case.
 ///
@@ -642,6 +1240,56 @@ public protocol Grpc_Testing_UnimplementedServiceServerInterceptorFactoryProtoco
   ///   Defaults to calling `self.makeInterceptors()`.
   func makeUnimplementedCallInterceptors() -> [ServerInterceptor<Grpc_Testing_Empty, Grpc_Testing_Empty>]
 }
+
+#if compiler(>=5.5)
+
+/// A simple service NOT implemented at servers so clients can test for
+/// that case.
+///
+/// To implement a server, implement an object which conforms to this protocol.
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public protocol Grpc_Testing_UnimplementedServiceAsyncProvider: CallHandlerProvider {
+  var interceptors: Grpc_Testing_UnimplementedServiceServerInterceptorFactoryProtocol? { get }
+
+  /// A call that no server should implement
+  @Sendable func unimplementedCall(
+    request: Grpc_Testing_Empty,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_Empty
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_UnimplementedServiceAsyncProvider {
+  public var serviceName: Substring {
+    return "grpc.testing.UnimplementedService"
+  }
+
+  public var interceptors: Grpc_Testing_UnimplementedServiceServerInterceptorFactoryProtocol? {
+    return nil
+  }
+
+  public func handle(
+    method name: Substring,
+    context: CallHandlerContext
+  ) -> GRPCServerHandlerProtocol? {
+    switch name {
+    case "UnimplementedCall":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_Empty>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_Empty>(),
+        interceptors: self.interceptors?.makeUnimplementedCallInterceptors() ?? [],
+        wrapping: self.unimplementedCall(request:context:)
+      )
+
+    default:
+      return nil
+    }
+  }
+}
+
+#endif // compiler(>=5.5)
+
 /// A service used to control reconnect server.
 ///
 /// To build a server, implement a class that conforms to this protocol.
@@ -697,3 +1345,65 @@ public protocol Grpc_Testing_ReconnectServiceServerInterceptorFactoryProtocol {
   ///   Defaults to calling `self.makeInterceptors()`.
   func makeStopInterceptors() -> [ServerInterceptor<Grpc_Testing_Empty, Grpc_Testing_ReconnectInfo>]
 }
+
+#if compiler(>=5.5)
+
+/// A service used to control reconnect server.
+///
+/// To implement a server, implement an object which conforms to this protocol.
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public protocol Grpc_Testing_ReconnectServiceAsyncProvider: CallHandlerProvider {
+  var interceptors: Grpc_Testing_ReconnectServiceServerInterceptorFactoryProtocol? { get }
+
+  @Sendable func start(
+    request: Grpc_Testing_ReconnectParams,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_Empty
+
+  @Sendable func stop(
+    request: Grpc_Testing_Empty,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_ReconnectInfo
+}
+
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+extension Grpc_Testing_ReconnectServiceAsyncProvider {
+  public var serviceName: Substring {
+    return "grpc.testing.ReconnectService"
+  }
+
+  public var interceptors: Grpc_Testing_ReconnectServiceServerInterceptorFactoryProtocol? {
+    return nil
+  }
+
+  public func handle(
+    method name: Substring,
+    context: CallHandlerContext
+  ) -> GRPCServerHandlerProtocol? {
+    switch name {
+    case "Start":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_ReconnectParams>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_Empty>(),
+        interceptors: self.interceptors?.makeStartInterceptors() ?? [],
+        wrapping: self.start(request:context:)
+      )
+
+    case "Stop":
+      return GRPCAsyncServerHandler(
+        context: context,
+        requestDeserializer: ProtobufDeserializer<Grpc_Testing_Empty>(),
+        responseSerializer: ProtobufSerializer<Grpc_Testing_ReconnectInfo>(),
+        interceptors: self.interceptors?.makeStopInterceptors() ?? [],
+        wrapping: self.stop(request:context:)
+      )
+
+    default:
+      return nil
+    }
+  }
+}
+
+#endif // compiler(>=5.5)
+

+ 11 - 7
Sources/GRPCInteroperabilityTestModels/generate.sh

@@ -1,5 +1,9 @@
 #!/bin/sh
 
+set -euo pipefail
+
+CURRENT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
 PLUGIN_SWIFT=../../.build/release/protoc-gen-swift
 PLUGIN_SWIFTGRPC=../../.build/release/protoc-gen-grpc-swift
 PROTO="src/proto/grpc/testing/test.proto"
@@ -8,32 +12,32 @@ OUTPUT="Generated"
 FILE_NAMING="DropPath"
 VISIBILITY="Public"
 
-protoc "src/proto/grpc/testing/test.proto" \
+(cd "${CURRENT_SCRIPT_DIR}" && protoc "src/proto/grpc/testing/test.proto" \
   --plugin=${PLUGIN_SWIFT} \
   --plugin=${PLUGIN_SWIFTGRPC} \
   --swift_out=${OUTPUT} \
   --swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY} \
   --grpc-swift_out=${OUTPUT} \
-  --grpc-swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY}
+  --grpc-swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY},ExperimentalAsyncClient=true,ExperimentalAsyncServer=true)
 
-protoc "src/proto/grpc/testing/empty.proto" \
+(cd "${CURRENT_SCRIPT_DIR}" && protoc "src/proto/grpc/testing/empty.proto" \
   --plugin=${PLUGIN_SWIFT} \
   --plugin=${PLUGIN_SWIFTGRPC} \
   --swift_out=${OUTPUT} \
   --swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY} \
   --grpc-swift_out=${OUTPUT} \
-  --grpc-swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY}
+  --grpc-swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY},ExperimentalAsyncClient=true,ExperimentalAsyncServer=true)
 
-protoc "src/proto/grpc/testing/messages.proto" \
+(cd "${CURRENT_SCRIPT_DIR}" && protoc "src/proto/grpc/testing/messages.proto" \
   --plugin=${PLUGIN_SWIFT} \
   --plugin=${PLUGIN_SWIFTGRPC} \
   --swift_out=${OUTPUT} \
   --swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY} \
   --grpc-swift_out=${OUTPUT} \
-  --grpc-swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY}
+  --grpc-swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY},ExperimentalAsyncClient=true,ExperimentalAsyncServer=true)
 
 # The generated code needs to be modified to support testing an unimplemented method.
 # On the server side, the generated code needs to be removed so the server has no
 # knowledge of it. Client code requires no modification, since it is required to call
 # the unimplemented method.
-patch -p3 < unimplemented_call.patch
+(cd "${CURRENT_SCRIPT_DIR}" && patch -p3 < unimplemented_call.patch)

+ 41 - 14
Sources/GRPCInteroperabilityTestModels/unimplemented_call.patch

@@ -1,8 +1,6 @@
-diff --git a/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift b/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift
-index 6a5f3099..ae0ba123 100644
 --- a/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift
 +++ b/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift
-@@ -485,10 +485,6 @@ public protocol Grpc_Testing_TestServiceProvider: CallHandlerProvider {
+@@ -931,10 +931,6 @@
    /// stream of responses are returned to the client when the server starts with
    /// first request.
    func halfDuplexCall(context: StreamingResponseCallContext<Grpc_Testing_StreamingOutputCallResponse>) -> EventLoopFuture<(StreamEvent<Grpc_Testing_StreamingOutputCallRequest>) -> Void>
@@ -13,19 +11,48 @@ index 6a5f3099..ae0ba123 100644
  }
  
  extension Grpc_Testing_TestServiceProvider {
-@@ -565,16 +561,6 @@ extension Grpc_Testing_TestServiceProvider {
-         self.halfDuplexCall(context: context)
-       }
+@@ -1010,15 +1006,6 @@
+         observerFactory: self.halfDuplexCall(context:)
+       )
  
 -    case "UnimplementedCall":
--      return CallHandlerFactory.makeUnary(
--        callHandlerContext: callHandlerContext,
--        interceptors: self.interceptors?.makeUnimplementedCallInterceptors() ?? []
--      ) { context in
--        return { request in
--          self.unimplementedCall(request: request, context: context)
--        }
--      }
+-      return UnaryServerHandler(
+-        context: context,
+-        requestDeserializer: ProtobufDeserializer<Grpc_Testing_Empty>(),
+-        responseSerializer: ProtobufSerializer<Grpc_Testing_Empty>(),
+-        interceptors: self.interceptors?.makeUnimplementedCallInterceptors() ?? [],
+-        userFunction: self.unimplementedCall(request:context:)
+-      )
+-
+     default:
+       return nil
+     }
+@@ -1123,13 +1110,6 @@
+     responseStream: GRPCAsyncResponseStreamWriter<Grpc_Testing_StreamingOutputCallResponse>,
+     context: GRPCAsyncServerCallContext
+   ) async throws
+-
+-  /// The test server will not implement this method. It will be used
+-  /// to test the behavior when clients call unimplemented methods.
+-  @Sendable func unimplementedCall(
+-    request: Grpc_Testing_Empty,
+-    context: GRPCAsyncServerCallContext
+-  ) async throws -> Grpc_Testing_Empty
+ }
+ 
+ @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+@@ -1210,15 +1190,6 @@
+         wrapping: self.halfDuplexCall(requests:responseStream:context:)
+       )
+ 
+-    case "UnimplementedCall":
+-      return GRPCAsyncServerHandler(
+-        context: context,
+-        requestDeserializer: ProtobufDeserializer<Grpc_Testing_Empty>(),
+-        responseSerializer: ProtobufSerializer<Grpc_Testing_Empty>(),
+-        interceptors: self.interceptors?.makeUnimplementedCallInterceptors() ?? [],
+-        wrapping: self.unimplementedCall(request:context:)
+-      )
 -
      default:
        return nil

+ 220 - 0
Sources/GRPCInteroperabilityTestsImplementation/TestServiceAsyncProvider.swift

@@ -0,0 +1,220 @@
+/*
+ * Copyright 2021, 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.
+ */
+#if compiler(>=5.5)
+import Foundation
+import GRPC
+import GRPCInteroperabilityTestModels
+import NIOCore
+
+/// An async service provider for the gRPC interoperability test suite.
+///
+/// See: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md#server
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+public class TestServiceAsyncProvider: Grpc_Testing_TestServiceAsyncProvider {
+  public var interceptors: Grpc_Testing_TestServiceServerInterceptorFactoryProtocol?
+
+  public init() {}
+
+  private static let echoMetadataNotImplemented = GRPCStatus(
+    code: .unimplemented,
+    message: "Echoing metadata is not yet supported"
+  )
+
+  /// Features that this server implements.
+  ///
+  /// Some 'features' are methods, whilst others optionally modify the outcome of those methods. The
+  /// specification is not explicit about where these modifying features should be implemented (i.e.
+  /// which methods should support them) and they are not listed in the individual method
+  /// descriptions. As such implementation of these modifying features within each method is
+  /// determined by the features required by each test.
+  public static var implementedFeatures: Set<ServerFeature> {
+    return [
+      .emptyCall,
+      .unaryCall,
+      .streamingOutputCall,
+      .streamingInputCall,
+      .fullDuplexCall,
+      .echoStatus,
+      .compressedResponse,
+      .compressedRequest,
+    ]
+  }
+
+  /// Server implements `emptyCall` which immediately returns the empty message.
+  public func emptyCall(
+    request: Grpc_Testing_Empty,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_Empty {
+    return Grpc_Testing_Empty()
+  }
+
+  /// Server implements `unaryCall` which immediately returns a `SimpleResponse` with a payload
+  /// body of size `SimpleRequest.responseSize` bytes and type as appropriate for the
+  /// `SimpleRequest.responseType`.
+  ///
+  /// If the server does not support the `responseType`, then it should fail the RPC with
+  /// `INVALID_ARGUMENT`.
+  public func unaryCall(
+    request: Grpc_Testing_SimpleRequest,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_SimpleResponse {
+    // We can't validate messages at the wire-encoding layer (i.e. where the compression byte is
+    // set), so we have to check via the encoding header. Note that it is possible for the header
+    // to be set and for the message to not be compressed.
+    if request.expectCompressed.value, !context.requestMetadata.contains(name: "grpc-encoding") {
+      throw GRPCStatus(
+        code: .invalidArgument,
+        message: "Expected compressed request, but 'grpc-encoding' was missing"
+      )
+    }
+
+    // Should we enable compression? The C++ interoperability client only expects compression if
+    // explicitly requested; we'll do the same.
+    context.compressionEnabled = request.responseCompressed.value
+
+    if request.shouldEchoStatus {
+      let code = GRPCStatus.Code(rawValue: numericCast(request.responseStatus.code)) ?? .unknown
+      throw GRPCStatus(code: code, message: request.responseStatus.message)
+    }
+
+    if context.requestMetadata.shouldEchoMetadata {
+      throw Self.echoMetadataNotImplemented
+    }
+
+    if case .UNRECOGNIZED = request.responseType {
+      throw GRPCStatus(code: .invalidArgument, message: nil)
+    }
+
+    return Grpc_Testing_SimpleResponse.with { response in
+      response.payload = Grpc_Testing_Payload.with { payload in
+        payload.body = Data(repeating: 0, count: numericCast(request.responseSize))
+        payload.type = request.responseType
+      }
+    }
+  }
+
+  /// Server gets the default `SimpleRequest` proto as the request. The content of the request is
+  /// ignored. It returns the `SimpleResponse` proto with the payload set to current timestamp.
+  /// The timestamp is an integer representing current time with nanosecond resolution. This
+  /// integer is formated as ASCII decimal in the response. The format is not really important as
+  /// long as the response payload is different for each request. In addition it adds cache control
+  /// headers such that the response can be cached by proxies in the response path. Server should
+  /// be behind a caching proxy for this test to pass. Currently we set the max-age to 60 seconds.
+  public func cacheableUnaryCall(
+    request: Grpc_Testing_SimpleRequest,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_SimpleResponse {
+    throw GRPCStatus(
+      code: .unimplemented,
+      message: "'cacheableUnaryCall' requires control of the initial metadata which isn't supported"
+    )
+  }
+
+  /// Server implements `streamingOutputCall` by replying, in order, with one
+  /// `StreamingOutputCallResponse` for each `ResponseParameter`s in `StreamingOutputCallRequest`.
+  /// Each `StreamingOutputCallResponse` should have a payload body of size `ResponseParameter.size`
+  /// bytes, as specified by its respective `ResponseParameter`. After sending all responses, it
+  /// closes with OK.
+  public func streamingOutputCall(
+    request: Grpc_Testing_StreamingOutputCallRequest,
+    responseStream: GRPCAsyncResponseStreamWriter<Grpc_Testing_StreamingOutputCallResponse>,
+    context: GRPCAsyncServerCallContext
+  ) async throws {
+    for responseParameter in request.responseParameters {
+      let response = Grpc_Testing_StreamingOutputCallResponse.with { response in
+        response.payload = Grpc_Testing_Payload.with { payload in
+          payload.body = Data(repeating: 0, count: numericCast(responseParameter.size))
+        }
+      }
+
+      // Should we enable compression? The C++ interoperability client only expects compression if
+      // explicitly requested; we'll do the same.
+      let compression: Compression = responseParameter.compressed.value ? .enabled : .disabled
+      try await responseStream.send(response, compression: compression)
+    }
+  }
+
+  /// Server implements `streamingInputCall` which upon half close immediately returns a
+  /// `StreamingInputCallResponse` where `aggregatedPayloadSize` is the sum of all request payload
+  /// bodies received.
+  public func streamingInputCall(
+    requests: GRPCAsyncRequestStream<Grpc_Testing_StreamingInputCallRequest>,
+    context: GRPCAsyncServerCallContext
+  ) async throws -> Grpc_Testing_StreamingInputCallResponse {
+    var aggregatePayloadSize = 0
+
+    for try await request in requests {
+      if request.expectCompressed.value {
+        guard context.requestMetadata.contains(name: "grpc-encoding") else {
+          throw GRPCStatus(
+            code: .invalidArgument,
+            message: "Expected compressed request, but 'grpc-encoding' was missing"
+          )
+        }
+      }
+      aggregatePayloadSize += request.payload.body.count
+    }
+    return Grpc_Testing_StreamingInputCallResponse.with { response in
+      response.aggregatedPayloadSize = numericCast(aggregatePayloadSize)
+    }
+  }
+
+  /// Server implements `fullDuplexCall` by replying, in order, with one
+  /// `StreamingOutputCallResponse` for each `ResponseParameter`s in each
+  /// `StreamingOutputCallRequest`. Each `StreamingOutputCallResponse` should have a payload body
+  /// of size `ResponseParameter.size` bytes, as specified by its respective `ResponseParameter`s.
+  /// After receiving half close and sending all responses, it closes with OK.
+  public func fullDuplexCall(
+    requests: GRPCAsyncRequestStream<Grpc_Testing_StreamingOutputCallRequest>,
+    responseStream: GRPCAsyncResponseStreamWriter<Grpc_Testing_StreamingOutputCallResponse>,
+    context: GRPCAsyncServerCallContext
+  ) async throws {
+    // We don't have support for this yet so just fail the call.
+    if context.requestMetadata.shouldEchoMetadata {
+      throw Self.echoMetadataNotImplemented
+    }
+
+    for try await request in requests {
+      if request.shouldEchoStatus {
+        let code = GRPCStatus.Code(rawValue: numericCast(request.responseStatus.code))
+        let status = GRPCStatus(code: code ?? .unknown, message: request.responseStatus.message)
+        throw status
+      } else {
+        for responseParameter in request.responseParameters {
+          let response = Grpc_Testing_StreamingOutputCallResponse.with { response in
+            response.payload = .zeros(count: numericCast(responseParameter.size))
+          }
+          try await responseStream.send(response)
+        }
+      }
+    }
+  }
+
+  /// This is not implemented as it is not described in the specification.
+  ///
+  /// See: https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md
+  public func halfDuplexCall(
+    requests: GRPCAsyncRequestStream<Grpc_Testing_StreamingOutputCallRequest>,
+    responseStream: GRPCAsyncResponseStreamWriter<Grpc_Testing_StreamingOutputCallResponse>,
+    context: GRPCAsyncServerCallContext
+  ) async throws {
+    throw GRPCStatus(
+      code: .unimplemented,
+      message: "'halfDuplexCall' was not described in the specification"
+    )
+  }
+}
+#endif // compiler(>=5.5)

+ 21 - 0
Tests/GRPCTests/GRPCInteroperabilityTests.swift

@@ -39,6 +39,7 @@ class GRPCInsecureInteroperabilityTests: GRPCTestCase {
       host: "localhost",
       port: 0,
       eventLoopGroup: self.serverEventLoopGroup!,
+      serviceProviders: [self.makeProvider()],
       useTLS: self.useTLS,
       logger: self.serverLogger
     ).wait()
@@ -69,6 +70,10 @@ class GRPCInsecureInteroperabilityTests: GRPCTestCase {
     super.tearDown()
   }
 
+  internal func makeProvider() -> CallHandlerProvider {
+    return TestServiceProvider()
+  }
+
   private func doRunTest(_ testCase: InteroperabilityTestCase, line: UInt = #line) {
     // Does the server support the test?
     let implementedFeatures = TestServiceProvider.implementedFeatures
@@ -170,3 +175,19 @@ class GRPCSecureInteroperabilityTests: GRPCInsecureInteroperabilityTests {
   override var useTLS: Bool { return true }
 }
 #endif // canImport(NIOSSL)
+
+#if compiler(>=5.5)
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+class GRPCInsecureInteroperabilityAsyncTests: GRPCInsecureInteroperabilityTests {
+  override func makeProvider() -> CallHandlerProvider {
+    return TestServiceAsyncProvider()
+  }
+}
+
+#if canImport(NIOSSL)
+@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
+class GRPCSecureInteroperabilityAsyncTests: GRPCInsecureInteroperabilityAsyncTests {
+  override var useTLS: Bool { return true }
+}
+#endif // canImport(NIOSSL)
+#endif // compiler(>=5.5)