Skip to content

Commit 9f0956e

Browse files
Interop Tests implementation (#1821)
Motivation: The test cases test certain functionalities of the new GRPC server and client. Modifications: - created the InteroperabilityTest protocol with the run method - created the InteroperabilityTestCase enum that will be used by main to run certain tests according to the input - implemented the interop tests for the features we support (no caching or compression tests) - added new assertions to the test framework Result: We will be able to implement the main() function that runs the interop tests using in process transport.
1 parent 61b7262 commit 9f0956e

File tree

5 files changed

+848
-2
lines changed

5 files changed

+848
-2
lines changed

Sources/GRPCCore/Call/Client/ClientResponse.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,9 @@ extension ClientResponse.Stream {
344344
}
345345
}
346346

347-
/// Returns metadata received from the server at the end of the response.
347+
/// Returns the messages received from the server.
348348
///
349-
/// Unlike ``metadata``, for rejected RPCs the metadata returned may contain values.
349+
/// For rejected RPCs the `RPCAsyncSequence` throws a `RPCError``.
350350
public var messages: RPCAsyncSequence<Message> {
351351
switch self.accepted {
352352
case let .success(contents):

Sources/InteroperabilityTests/AssertionFailure.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ public struct AssertionFailure: Error {
2121
public var message: String
2222
public var file: String
2323
public var line: Int
24+
25+
public init(message: String, file: String = #fileID, line: Int = #line) {
26+
self.message = message
27+
self.file = file
28+
self.line = line
29+
}
2430
}
2531

2632
/// Asserts that the value of an expression is `true`.
@@ -34,3 +40,18 @@ public func assertTrue(
3440
throw AssertionFailure(message: message, file: file, line: line)
3541
}
3642
}
43+
44+
/// Asserts that the two given values are equal.
45+
public func assertEqual<T: Equatable>(
46+
_ value1: T,
47+
_ value2: T,
48+
file: String = #fileID,
49+
line: Int = #line
50+
) throws {
51+
return try assertTrue(
52+
value1 == value2,
53+
"'\(value1)' is not equal to '\(value2)'",
54+
file: file,
55+
line: line
56+
)
57+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#if swift(<5.9)
18+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
19+
extension AsyncStream {
20+
@inlinable
21+
static func makeStream(
22+
of elementType: Element.Type = Element.self,
23+
bufferingPolicy limit: AsyncStream<Element>.Continuation.BufferingPolicy = .unbounded
24+
) -> (stream: AsyncStream<Element>, continuation: AsyncStream<Element>.Continuation) {
25+
var continuation: AsyncStream<Element>.Continuation!
26+
let stream = AsyncStream(Element.self, bufferingPolicy: limit) {
27+
continuation = $0
28+
}
29+
return (stream, continuation)
30+
}
31+
}
32+
#endif
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import GRPCCore
17+
18+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
19+
public protocol InteroperabilityTest {
20+
/// Run a test case using the given connection.
21+
///
22+
/// The test case is considered unsuccessful if any exception is thrown, conversely if no
23+
/// exceptions are thrown it is successful.
24+
///
25+
/// - Parameter client: The client to use for the test.
26+
/// - Throws: Any exception may be thrown to indicate an unsuccessful test.
27+
func run(client: GRPCClient) async throws
28+
}
29+
30+
/// Test cases as listed by the [gRPC interoperability test description
31+
/// specification](https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md).
32+
///
33+
/// This is not a complete list, the following tests have not been implemented:
34+
/// - cacheable_unary
35+
/// - client-compressed-unary
36+
/// - server-compressed-unary
37+
/// - client_compressed_streaming
38+
/// - server_compressed_streaming
39+
/// - compute_engine_creds
40+
/// - jwt_token_creds
41+
/// - oauth2_auth_token
42+
/// - per_rpc_creds
43+
/// - google_default_credentials
44+
/// - compute_engine_channel_credentials
45+
/// - cancel_after_begin
46+
/// - cancel_after_first_response
47+
///
48+
/// Note: Tests for compression have not been implemented yet as compression is
49+
/// not supported. Once the API which allows for compression will be implemented
50+
/// these tests should be added.
51+
public enum InteroperabilityTestCase: String, CaseIterable {
52+
case emptyUnary = "empty_unary"
53+
case largeUnary = "large_unary"
54+
case clientStreaming = "client_streaming"
55+
case serverStreaming = "server_streaming"
56+
case pingPong = "ping_pong"
57+
case emptyStream = "empty_stream"
58+
case customMetadata = "custom_metadata"
59+
case statusCodeAndMessage = "status_code_and_message"
60+
case specialStatusMessage = "special_status_message"
61+
case unimplementedMethod = "unimplemented_method"
62+
case unimplementedService = "unimplemented_service"
63+
64+
public var name: String {
65+
return self.rawValue
66+
}
67+
}
68+
69+
@available(macOS 13.0, *)
70+
extension InteroperabilityTestCase {
71+
/// Return a new instance of the test case.
72+
public func makeTest() -> InteroperabilityTest {
73+
switch self {
74+
case .emptyUnary:
75+
return EmptyUnary()
76+
case .largeUnary:
77+
return LargeUnary()
78+
case .clientStreaming:
79+
return ClientStreaming()
80+
case .serverStreaming:
81+
return ServerStreaming()
82+
case .pingPong:
83+
return PingPong()
84+
case .emptyStream:
85+
return EmptyStream()
86+
case .customMetadata:
87+
return CustomMetadata()
88+
case .statusCodeAndMessage:
89+
return StatusCodeAndMessage()
90+
case .specialStatusMessage:
91+
return SpecialStatusMessage()
92+
case .unimplementedMethod:
93+
return UnimplementedMethod()
94+
case .unimplementedService:
95+
return UnimplementedService()
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)