| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695 |
- /*
- * Copyright 2024, 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 GRPCCore
- import struct Foundation.Data
- /// This test verifies that implementations support zero-size messages. Ideally, client
- /// implementations would verify that the request and response were zero bytes serialized, but
- /// this is generally prohibitive to perform, so is not required.
- ///
- /// Server features:
- /// - EmptyCall
- ///
- /// Procedure:
- /// 1. Client calls EmptyCall with the default Empty message
- ///
- /// Client asserts:
- /// - call was successful
- /// - response is non-null
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct EmptyUnary: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- try await testServiceClient.emptyCall(
- request: ClientRequest.Single(message: Grpc_Testing_Empty())
- ) { response in
- try assertEqual(response.message, Grpc_Testing_Empty())
- }
- }
- }
- /// This test verifies unary calls succeed in sending messages, and touches on flow control (even
- /// if compression is enabled on the channel).
- ///
- /// Server features:
- /// - UnaryCall
- ///
- /// Procedure:
- /// 1. Client calls UnaryCall with:
- /// ```
- /// {
- /// response_size: 314159
- /// payload:{
- /// body: 271828 bytes of zeros
- /// }
- /// }
- /// ```
- ///
- /// Client asserts:
- /// - call was successful
- /// - response payload body is 314159 bytes in size
- /// - clients are free to assert that the response payload body contents are zero and comparing
- /// the entire response message against a golden response
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct LargeUnary: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let request = Grpc_Testing_SimpleRequest.with { request in
- request.responseSize = 314_159
- request.payload = Grpc_Testing_Payload.with {
- $0.body = Data(count: 271_828)
- }
- }
- try await testServiceClient.unaryCall(
- request: ClientRequest.Single(message: request)
- ) { response in
- try assertEqual(
- response.message.payload,
- Grpc_Testing_Payload.with {
- $0.body = Data(count: 314_159)
- }
- )
- }
- }
- }
- /// This test verifies that client-only streaming succeeds.
- ///
- /// Server features:
- /// - StreamingInputCall
- ///
- /// Procedure:
- /// 1. Client calls StreamingInputCall
- /// 2. Client sends:
- /// ```
- /// {
- /// payload:{
- /// body: 27182 bytes of zeros
- /// }
- /// }
- /// ```
- /// 3. Client then sends:
- /// ```
- /// {
- /// payload:{
- /// body: 8 bytes of zeros
- /// }
- /// }
- /// ```
- /// 4. Client then sends:
- /// ```
- /// {
- /// payload:{
- /// body: 1828 bytes of zeros
- /// }
- /// }
- /// ```
- /// 5. Client then sends:
- /// ```
- /// {
- /// payload:{
- /// body: 45904 bytes of zeros
- /// }
- /// }
- /// ```
- /// 6. Client half-closes
- ///
- /// Client asserts:
- /// - call was successful
- /// - response aggregated_payload_size is 74922
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct ClientStreaming: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let request = ClientRequest.Stream { writer in
- for bytes in [27182, 8, 1828, 45904] {
- let message = Grpc_Testing_StreamingInputCallRequest.with {
- $0.payload = Grpc_Testing_Payload.with {
- $0.body = Data(count: bytes)
- }
- }
- try await writer.write(message)
- }
- }
- try await testServiceClient.streamingInputCall(request: request) { response in
- try assertEqual(response.message.aggregatedPayloadSize, 74922)
- }
- }
- }
- /// This test verifies that server-only streaming succeeds.
- ///
- /// Server features:
- /// - StreamingOutputCall
- ///
- /// Procedure:
- /// 1. Client calls StreamingOutputCall with StreamingOutputCallRequest:
- /// ```
- /// {
- /// response_parameters:{
- /// size: 31415
- /// }
- /// response_parameters:{
- /// size: 9
- /// }
- /// response_parameters:{
- /// size: 2653
- /// }
- /// response_parameters:{
- /// size: 58979
- /// }
- /// }
- /// ```
- ///
- /// Client asserts:
- /// - call was successful
- /// - exactly four responses
- /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
- /// - clients are free to assert that the response payload body contents are zero and
- /// comparing the entire response messages against golden responses
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct ServerStreaming: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let responseSizes = [31415, 9, 2653, 58979]
- let request = Grpc_Testing_StreamingOutputCallRequest.with { request in
- request.responseParameters = responseSizes.map {
- var parameter = Grpc_Testing_ResponseParameters()
- parameter.size = Int32($0)
- return parameter
- }
- }
- try await testServiceClient.streamingOutputCall(
- request: ClientRequest.Single(message: request)
- ) { response in
- var responseParts = response.messages.makeAsyncIterator()
- // There are 4 response sizes, so if there isn't a message for each one,
- // it means that the client didn't receive 4 messages back.
- for responseSize in responseSizes {
- if let message = try await responseParts.next() {
- try assertEqual(message.payload.body.count, responseSize)
- } else {
- throw AssertionFailure(
- message: "There were less than four responses received."
- )
- }
- }
- // Check that there were not more than 4 responses from the server.
- try assertEqual(try await responseParts.next(), nil)
- }
- }
- }
- /// This test verifies that full duplex bidi is supported.
- ///
- /// Server features:
- /// - FullDuplexCall
- ///
- /// Procedure:
- /// 1. Client calls FullDuplexCall with:
- /// ```
- /// {
- /// response_parameters:{
- /// size: 31415
- /// }
- /// payload:{
- /// body: 27182 bytes of zeros
- /// }
- /// }
- /// ```
- /// 2. After getting a reply, it sends:
- /// ```
- /// {
- /// response_parameters:{
- /// size: 9
- /// }
- /// payload:{
- /// body: 8 bytes of zeros
- /// }
- /// }
- /// ```
- /// 3. After getting a reply, it sends:
- /// ```
- /// {
- /// response_parameters:{
- /// size: 2653
- /// }
- /// payload:{
- /// body: 1828 bytes of zeros
- /// }
- /// }
- /// ```
- /// 4. After getting a reply, it sends:
- /// ```
- /// {
- /// response_parameters:{
- /// size: 58979
- /// }
- /// payload:{
- /// body: 45904 bytes of zeros
- /// }
- /// }
- /// ```
- /// 5. After getting a reply, client half-closes
- ///
- /// Client asserts:
- /// - call was successful
- /// - exactly four responses
- /// - response payload bodies are sized (in order): 31415, 9, 2653, 58979
- /// - clients are free to assert that the response payload body contents are zero and
- /// comparing the entire response messages against golden responses
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct PingPong: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let ids = AsyncStream.makeStream(of: Int.self)
- let request = ClientRequest.Stream { writer in
- let sizes = [(31_415, 27_182), (9, 8), (2_653, 1_828), (58_979, 45_904)]
- for try await id in ids.stream {
- var message = Grpc_Testing_StreamingOutputCallRequest()
- switch id {
- case 1 ... 4:
- let (responseSize, bodySize) = sizes[id - 1]
- message.responseParameters = [
- Grpc_Testing_ResponseParameters.with {
- $0.size = Int32(responseSize)
- }
- ]
- message.payload = Grpc_Testing_Payload.with {
- $0.body = Data(count: bodySize)
- }
- default:
- // When the id is higher than 4 it means the client received all the expected responses
- // and it doesn't need to send another message.
- return
- }
- try await writer.write(message)
- }
- }
- ids.continuation.yield(1)
- try await testServiceClient.fullDuplexCall(request: request) { response in
- var id = 1
- for try await message in response.messages {
- switch id {
- case 1:
- try assertEqual(message.payload.body, Data(count: 31_415))
- case 2:
- try assertEqual(message.payload.body, Data(count: 9))
- case 3:
- try assertEqual(message.payload.body, Data(count: 2_653))
- case 4:
- try assertEqual(message.payload.body, Data(count: 58_979))
- default:
- throw AssertionFailure(
- message: "We should only receive messages with ids between 1 and 4."
- )
- }
- // Add the next id to the continuation.
- id += 1
- ids.continuation.yield(id)
- }
- }
- }
- }
- /// This test verifies that streams support having zero-messages in both directions.
- ///
- /// Server features:
- /// - FullDuplexCall
- ///
- /// Procedure:
- /// 1. Client calls FullDuplexCall and then half-closes
- ///
- /// Client asserts:
- /// - call was successful
- /// - exactly zero responses
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct EmptyStream: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let request = ClientRequest.Stream<Grpc_Testing_StreamingOutputCallRequest> { _ in }
- try await testServiceClient.fullDuplexCall(request: request) { response in
- var messages = response.messages.makeAsyncIterator()
- try await assertEqual(messages.next(), nil)
- }
- }
- }
- /// This test verifies that custom metadata in either binary or ascii format can be sent as
- /// initial-metadata by the client and as both initial- and trailing-metadata by the server.
- ///
- /// Server features:
- /// - UnaryCall
- /// - FullDuplexCall
- /// - Echo Metadata
- ///
- /// Procedure:
- /// 1. The client attaches custom metadata with the following keys and values
- /// to a UnaryCall with request:
- /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
- /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
- /// ```
- /// {
- /// response_size: 314159
- /// payload:{
- /// body: 271828 bytes of zeros
- /// }
- /// }
- /// ```
- /// 2. The client attaches custom metadata with the following keys and values
- /// to a FullDuplexCall with request:
- /// - key: "x-grpc-test-echo-initial", value: "test_initial_metadata_value"
- /// - key: "x-grpc-test-echo-trailing-bin", value: 0xababab
- /// ```
- /// {
- /// response_parameters:{
- /// size: 314159
- /// }
- /// payload:{
- /// body: 271828 bytes of zeros
- /// }
- /// }
- /// ```
- /// and then half-closes
- ///
- /// Client asserts:
- /// - call was successful
- /// - metadata with key "x-grpc-test-echo-initial" and value "test_initial_metadata_value" is
- /// received in the initial metadata for calls in Procedure steps 1 and 2.
- /// - metadata with key "x-grpc-test-echo-trailing-bin" and value 0xababab is received in the
- /// trailing metadata for calls in Procedure steps 1 and 2.
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct CustomMetadata: InteroperabilityTest {
- let initialMetadataName = "x-grpc-test-echo-initial"
- let initialMetadataValue = "test_initial_metadata_value"
- let trailingMetadataName = "x-grpc-test-echo-trailing-bin"
- let trailingMetadataValue: [UInt8] = [0xAB, 0xAB, 0xAB]
- func checkInitialMetadata(_ metadata: Metadata) throws {
- let values = metadata[self.initialMetadataName]
- try assertEqual(Array(values), [.string(self.initialMetadataValue)])
- }
- func checkTrailingMetadata(_ metadata: Metadata) throws {
- let values = metadata[self.trailingMetadataName]
- try assertEqual(Array(values), [.binary(self.trailingMetadataValue)])
- }
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let unaryRequest = Grpc_Testing_SimpleRequest.with { request in
- request.responseSize = 314_159
- request.payload = Grpc_Testing_Payload.with {
- $0.body = Data(count: 271_828)
- }
- }
- let metadata: Metadata = [
- self.initialMetadataName: .string(self.initialMetadataValue),
- self.trailingMetadataName: .binary(self.trailingMetadataValue),
- ]
- try await testServiceClient.unaryCall(
- request: ClientRequest.Single(message: unaryRequest, metadata: metadata)
- ) { response in
- // Check the initial metadata.
- let receivedInitialMetadata = response.metadata
- try checkInitialMetadata(receivedInitialMetadata)
- // Check the message.
- try assertEqual(response.message.payload.body, Data(count: 314_159))
- // Check the trailing metadata.
- try checkTrailingMetadata(response.trailingMetadata)
- }
- let streamingRequest = ClientRequest.Stream(metadata: metadata) { writer in
- let message = Grpc_Testing_StreamingOutputCallRequest.with {
- $0.responseParameters = [
- Grpc_Testing_ResponseParameters.with {
- $0.size = 314_159
- }
- ]
- $0.payload = Grpc_Testing_Payload.with {
- $0.body = Data(count: 271_828)
- }
- }
- try await writer.write(message)
- }
- try await testServiceClient.fullDuplexCall(request: streamingRequest) { response in
- switch response.accepted {
- case .success(let contents):
- // Check the initial metadata.
- let receivedInitialMetadata = response.metadata
- try self.checkInitialMetadata(receivedInitialMetadata)
- let parts = try await contents.bodyParts.reduce(into: []) { $0.append($1) }
- try assertEqual(parts.count, 2)
- for part in parts {
- switch part {
- // Check the message.
- case .message(let message):
- try assertEqual(message.payload.body, Data(count: 314_159))
- // Check the trailing metadata.
- case .trailingMetadata(let receivedTrailingMetadata):
- try self.checkTrailingMetadata(receivedTrailingMetadata)
- }
- }
- case .failure(_):
- throw AssertionFailure(
- message: "The client should have received a response from the server."
- )
- }
- }
- }
- }
- /// This test verifies unary calls succeed in sending messages, and propagate back status code and
- /// message sent along with the messages.
- ///
- /// Server features:
- /// - UnaryCall
- /// - FullDuplexCall
- /// - Echo Status
- ///
- /// Procedure:
- /// 1. Client calls UnaryCall with:
- /// ```
- /// {
- /// response_status:{
- /// code: 2
- /// message: "test status message"
- /// }
- /// }
- /// ```
- /// 2. Client calls FullDuplexCall with:
- /// ```
- /// {
- /// response_status:{
- /// code: 2
- /// message: "test status message"
- /// }
- /// }
- /// ```
- /// 3. and then half-closes
- ///
- /// Client asserts:
- /// - received status code is the same as the sent code for both Procedure steps 1 and 2
- /// - received status message is the same as the sent message for both Procedure steps 1 and 2
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct StatusCodeAndMessage: InteroperabilityTest {
- let expectedCode = 2
- let expectedMessage = "test status message"
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let message = Grpc_Testing_SimpleRequest.with {
- $0.responseStatus = Grpc_Testing_EchoStatus.with {
- $0.code = Int32(self.expectedCode)
- $0.message = self.expectedMessage
- }
- }
- try await testServiceClient.unaryCall(
- request: ClientRequest.Single(message: message)
- ) { response in
- switch response.accepted {
- case .failure(let error):
- try assertEqual(error.code.rawValue, self.expectedCode)
- try assertEqual(error.message, self.expectedMessage)
- case .success(_):
- throw AssertionFailure(
- message:
- "The client should receive an error with the status code and message sent by the client."
- )
- }
- }
- let request = ClientRequest.Stream { writer in
- let message = Grpc_Testing_StreamingOutputCallRequest.with {
- $0.responseStatus = Grpc_Testing_EchoStatus.with {
- $0.code = Int32(self.expectedCode)
- $0.message = self.expectedMessage
- }
- }
- try await writer.write(message)
- }
- try await testServiceClient.fullDuplexCall(request: request) { response in
- do {
- for try await _ in response.messages {
- throw AssertionFailure(
- message:
- "The client should receive an error with the status code and message sent by the client."
- )
- }
- } catch let error as RPCError {
- try assertEqual(error.code.rawValue, self.expectedCode)
- try assertEqual(error.message, self.expectedMessage)
- }
- }
- }
- }
- /// This test verifies Unicode and whitespace is correctly processed in status message. "\t" is
- /// horizontal tab. "\r" is carriage return. "\n" is line feed.
- ///
- /// Server features:
- /// - UnaryCall
- /// - Echo Status
- ///
- /// Procedure:
- /// 1. Client calls UnaryCall with:
- /// ```
- /// {
- /// response_status:{
- /// code: 2
- /// message: "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
- /// }
- /// }
- /// ```
- ///
- /// Client asserts:
- /// - received status code is the same as the sent code for Procedure step 1
- /// - received status message is the same as the sent message for Procedure step 1, including all
- /// whitespace characters
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct SpecialStatusMessage: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- let responseMessage = "\t\ntest with whitespace\r\nand Unicode BMP ☺ and non-BMP 😈\t\n"
- let message = Grpc_Testing_SimpleRequest.with {
- $0.responseStatus = Grpc_Testing_EchoStatus.with {
- $0.code = 2
- $0.message = responseMessage
- }
- }
- try await testServiceClient.unaryCall(
- request: ClientRequest.Single(message: message)
- ) { response in
- switch response.accepted {
- case .success(_):
- throw AssertionFailure(
- message: "The response should be an error with the error code 2."
- )
- case .failure(let error):
- try assertEqual(error.code.rawValue, 2)
- try assertEqual(error.message, responseMessage)
- }
- }
- }
- }
- /// This test verifies that calling an unimplemented RPC method returns the UNIMPLEMENTED status
- /// code.
- ///
- /// Server features: N/A
- ///
- /// Procedure:
- /// 1. Client calls grpc.testing.TestService/UnimplementedCall with an empty request (defined as
- /// grpc.testing.Empty):
- /// ```
- /// {
- /// }
- /// ```
- ///
- /// Client asserts:
- /// - received status code is 12 (UNIMPLEMENTED)
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct UnimplementedMethod: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let testServiceClient = Grpc_Testing_TestService.Client(client: client)
- try await testServiceClient.unimplementedCall(
- request: ClientRequest.Single(message: Grpc_Testing_Empty())
- ) { response in
- let result = response.accepted
- switch result {
- case .success(_):
- throw AssertionFailure(
- message: "The result should be an error."
- )
- case .failure(let error):
- try assertEqual(error.code, .unimplemented)
- }
- }
- }
- }
- /// This test verifies calling an unimplemented server returns the UNIMPLEMENTED status code.
- ///
- /// Server features: N/A
- ///
- /// Procedure:
- /// 1. Client calls grpc.testing.UnimplementedService/UnimplementedCall with an empty request
- /// (defined as grpc.testing.Empty):
- /// ```
- /// {
- /// }
- /// ```
- ///
- /// Client asserts:
- /// - received status code is 12 (UNIMPLEMENTED)
- @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
- struct UnimplementedService: InteroperabilityTest {
- func run(client: GRPCClient) async throws {
- let unimplementedServiceClient = Grpc_Testing_UnimplementedService.Client(client: client)
- try await unimplementedServiceClient.unimplementedCall(
- request: ClientRequest.Single(message: Grpc_Testing_Empty())
- ) { response in
- let result = response.accepted
- switch result {
- case .success(_):
- throw AssertionFailure(
- message: "The result should be an error."
- )
- case .failure(let error):
- try assertEqual(error.code, .unimplemented)
- }
- }
- }
- }
|