|
@@ -19,12 +19,21 @@ import Foundation
|
|
|
import XCTest
|
|
import XCTest
|
|
|
|
|
|
|
|
class gRPCTests: XCTestCase {
|
|
class gRPCTests: XCTestCase {
|
|
|
|
|
+ // We have seen this test flake out in rare cases fairly often due to race conditions.
|
|
|
|
|
+ // To detect such rare errors, we run the tests several times.
|
|
|
|
|
+ // (By now, all known errors should have been fixed, but we'd still like to detect new ones.)
|
|
|
|
|
+ let testRepetitions = 10
|
|
|
|
|
+
|
|
|
func testConnectivity() {
|
|
func testConnectivity() {
|
|
|
- runTest(useSSL: false)
|
|
|
|
|
|
|
+ for _ in 0..<testRepetitions {
|
|
|
|
|
+ runTest(useSSL: false)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func testConnectivitySecure() {
|
|
func testConnectivitySecure() {
|
|
|
- runTest(useSSL: true)
|
|
|
|
|
|
|
+ for _ in 0..<testRepetitions {
|
|
|
|
|
+ runTest(useSSL: true)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static var allTests: [(String, (gRPCTests) -> () throws -> Void)] {
|
|
static var allTests: [(String, (gRPCTests) -> () throws -> Void)] {
|
|
@@ -75,11 +84,8 @@ let helloServerStream = "/hello.server-stream"
|
|
|
let helloBiDiStream = "/hello.bidi-stream"
|
|
let helloBiDiStream = "/hello.bidi-stream"
|
|
|
|
|
|
|
|
// Return code/message for unary test
|
|
// Return code/message for unary test
|
|
|
-let oddStatusCode = StatusCode.ok
|
|
|
|
|
let oddStatusMessage = "OK"
|
|
let oddStatusMessage = "OK"
|
|
|
-
|
|
|
|
|
-let evenStatusCode = StatusCode.notFound
|
|
|
|
|
-let eventStatusMessage = "Not Found"
|
|
|
|
|
|
|
+let evenStatusMessage = "some other status message"
|
|
|
|
|
|
|
|
func runTest(useSSL: Bool) {
|
|
func runTest(useSSL: Bool) {
|
|
|
gRPC.initialize()
|
|
gRPC.initialize()
|
|
@@ -141,9 +147,12 @@ func runClient(useSSL: Bool) throws {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
channel.host = host
|
|
channel.host = host
|
|
|
- try callUnary(channel: channel)
|
|
|
|
|
- try callServerStream(channel: channel)
|
|
|
|
|
- try callBiDiStream(channel: channel)
|
|
|
|
|
|
|
+ for _ in 0..<10 {
|
|
|
|
|
+ // Send several calls to each server we spin up, to ensure that each individual server can handle many requests.
|
|
|
|
|
+ try callUnary(channel: channel)
|
|
|
|
|
+ try callServerStream(channel: channel)
|
|
|
|
|
+ try callBiDiStream(channel: channel)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func callUnary(channel: Channel) throws {
|
|
func callUnary(channel: Channel) throws {
|
|
@@ -157,23 +166,32 @@ func callUnary(channel: Channel) throws {
|
|
|
try call.start(.unary, metadata: metadata, message: message) {
|
|
try call.start(.unary, metadata: metadata, message: message) {
|
|
|
response in
|
|
response in
|
|
|
// verify the basic response from the server
|
|
// verify the basic response from the server
|
|
|
- XCTAssertEqual(response.statusCode, (i % 2 == 0) ? evenStatusCode : oddStatusCode)
|
|
|
|
|
- XCTAssertEqual(response.statusMessage, (i % 2 == 0) ? eventStatusMessage : oddStatusMessage)
|
|
|
|
|
|
|
+ XCTAssertEqual(response.statusCode, .ok)
|
|
|
|
|
+ XCTAssertEqual(response.statusMessage, (i % 2 == 0) ? evenStatusMessage : oddStatusMessage)
|
|
|
|
|
|
|
|
// verify the message from the server
|
|
// verify the message from the server
|
|
|
if (i % 2) == 0 {
|
|
if (i % 2) == 0 {
|
|
|
- let resultData = response.resultData!
|
|
|
|
|
- let messageString = String(data: resultData, encoding: .utf8)
|
|
|
|
|
- XCTAssertEqual(messageString, serverText)
|
|
|
|
|
|
|
+ if let resultData = response.resultData {
|
|
|
|
|
+ let messageString = String(data: resultData, encoding: .utf8)
|
|
|
|
|
+ XCTAssertEqual(messageString, serverText)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("callUnary response missing")
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// verify the initial metadata from the server
|
|
// verify the initial metadata from the server
|
|
|
- let initialMetadata = response.initialMetadata!
|
|
|
|
|
- verify_metadata(initialMetadata, expected: initialServerMetadata)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if let initialMetadata = response.initialMetadata {
|
|
|
|
|
+ verify_metadata(initialMetadata, expected: initialServerMetadata)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("callUnary initial metadata missing")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// verify the trailing metadata from the server
|
|
// verify the trailing metadata from the server
|
|
|
- let trailingMetadata = response.trailingMetadata!
|
|
|
|
|
- verify_metadata(trailingMetadata, expected: trailingServerMetadata)
|
|
|
|
|
|
|
+ if let trailingMetadata = response.trailingMetadata {
|
|
|
|
|
+ verify_metadata(trailingMetadata, expected: trailingServerMetadata)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("callUnary trailing metadata missing")
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// report completion
|
|
// report completion
|
|
|
sem.signal()
|
|
sem.signal()
|
|
@@ -197,8 +215,11 @@ func callServerStream(channel: Channel) throws {
|
|
|
XCTAssertEqual(response.statusMessage, "Custom Status Message ServerStreaming")
|
|
XCTAssertEqual(response.statusMessage, "Custom Status Message ServerStreaming")
|
|
|
|
|
|
|
|
// verify the trailing metadata from the server
|
|
// verify the trailing metadata from the server
|
|
|
- let trailingMetadata = response.trailingMetadata!
|
|
|
|
|
- verify_metadata(trailingMetadata, expected: trailingServerMetadata)
|
|
|
|
|
|
|
+ if let trailingMetadata = response.trailingMetadata {
|
|
|
|
|
+ verify_metadata(trailingMetadata, expected: trailingServerMetadata)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("callServerStream trailing metadata missing")
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
sem.signal() // signal call is finished
|
|
sem.signal() // signal call is finished
|
|
|
}
|
|
}
|
|
@@ -224,29 +245,31 @@ let clientPing = "ping"
|
|
|
let serverPong = "pong"
|
|
let serverPong = "pong"
|
|
|
|
|
|
|
|
func callBiDiStream(channel: Channel) throws {
|
|
func callBiDiStream(channel: Channel) throws {
|
|
|
- let message = clientPing.data(using: .utf8)
|
|
|
|
|
let metadata = Metadata(initialClientMetadata)
|
|
let metadata = Metadata(initialClientMetadata)
|
|
|
|
|
|
|
|
let sem = DispatchSemaphore(value: 0)
|
|
let sem = DispatchSemaphore(value: 0)
|
|
|
let method = helloBiDiStream
|
|
let method = helloBiDiStream
|
|
|
let call = channel.makeCall(method)
|
|
let call = channel.makeCall(method)
|
|
|
- try call.start(.bidiStreaming, metadata: metadata, message: message) {
|
|
|
|
|
|
|
+ try call.start(.bidiStreaming, metadata: metadata, message: nil) {
|
|
|
response in
|
|
response in
|
|
|
|
|
|
|
|
XCTAssertEqual(response.statusCode, .ok)
|
|
XCTAssertEqual(response.statusCode, .ok)
|
|
|
XCTAssertEqual(response.statusMessage, "Custom Status Message BiDi")
|
|
XCTAssertEqual(response.statusMessage, "Custom Status Message BiDi")
|
|
|
|
|
|
|
|
// verify the trailing metadata from the server
|
|
// verify the trailing metadata from the server
|
|
|
- let trailingMetadata = response.trailingMetadata!
|
|
|
|
|
- verify_metadata(trailingMetadata, expected: trailingServerMetadata)
|
|
|
|
|
|
|
+ if let trailingMetadata = response.trailingMetadata {
|
|
|
|
|
+ verify_metadata(trailingMetadata, expected: trailingServerMetadata)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("callBiDiStream trailing metadata missing")
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
sem.signal() // signal call is finished
|
|
sem.signal() // signal call is finished
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Send pings
|
|
// Send pings
|
|
|
|
|
+ let message = clientPing.data(using: .utf8)!
|
|
|
for _ in 0..<steps {
|
|
for _ in 0..<steps {
|
|
|
- let message = clientPing.data(using: .utf8)
|
|
|
|
|
- try call.sendMessage(data: message!) { (err) in
|
|
|
|
|
|
|
+ try call.sendMessage(data: message) { err in
|
|
|
XCTAssertNil(err)
|
|
XCTAssertNil(err)
|
|
|
}
|
|
}
|
|
|
call.messageQueueEmpty.wait()
|
|
call.messageQueueEmpty.wait()
|
|
@@ -313,21 +336,28 @@ func handleUnary(requestHandler: Handler, requestCount: Int) throws {
|
|
|
let initialMetadata = requestHandler.requestMetadata
|
|
let initialMetadata = requestHandler.requestMetadata
|
|
|
verify_metadata(initialMetadata, expected: initialClientMetadata)
|
|
verify_metadata(initialMetadata, expected: initialClientMetadata)
|
|
|
let initialMetadataToSend = Metadata(initialServerMetadata)
|
|
let initialMetadataToSend = Metadata(initialServerMetadata)
|
|
|
- try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
|
|
|
|
|
- let messageString = String(data: messageData!, encoding: .utf8)
|
|
|
|
|
- XCTAssertEqual(messageString, clientText)
|
|
|
|
|
|
|
+ try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) {
|
|
|
|
|
+ if let messageData = $0 {
|
|
|
|
|
+ let messageString = String(data: messageData, encoding: .utf8)
|
|
|
|
|
+ XCTAssertEqual(messageString, clientText)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("handleUnary message missing")
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // We need to return status OK in both cases, as it seems like the server might never send out the last few messages
|
|
|
|
|
+ // once it has been asked to send a non-OK status. Alternatively, we could send a non-OK status here, but then we
|
|
|
|
|
+ // would need to sleep for a few milliseconds before sending the non-OK status.
|
|
|
|
|
+ let replyMessage = serverText.data(using: .utf8)!
|
|
|
if (requestCount % 2) == 0 {
|
|
if (requestCount % 2) == 0 {
|
|
|
- let replyMessage = serverText
|
|
|
|
|
let trailingMetadataToSend = Metadata(trailingServerMetadata)
|
|
let trailingMetadataToSend = Metadata(trailingServerMetadata)
|
|
|
- try requestHandler.sendResponse(message: replyMessage.data(using: .utf8)!,
|
|
|
|
|
- status: ServerStatus(code: evenStatusCode,
|
|
|
|
|
- message: eventStatusMessage,
|
|
|
|
|
|
|
+ try requestHandler.sendResponse(message: replyMessage,
|
|
|
|
|
+ status: ServerStatus(code: .ok,
|
|
|
|
|
+ message: evenStatusMessage,
|
|
|
trailingMetadata: trailingMetadataToSend))
|
|
trailingMetadata: trailingMetadataToSend))
|
|
|
} else {
|
|
} else {
|
|
|
let trailingMetadataToSend = Metadata(trailingServerMetadata)
|
|
let trailingMetadataToSend = Metadata(trailingServerMetadata)
|
|
|
- try requestHandler.sendStatus(ServerStatus(code: oddStatusCode,
|
|
|
|
|
|
|
+ try requestHandler.sendStatus(ServerStatus(code: .ok,
|
|
|
message: oddStatusMessage,
|
|
message: oddStatusMessage,
|
|
|
trailingMetadata: trailingMetadataToSend))
|
|
trailingMetadata: trailingMetadataToSend))
|
|
|
}
|
|
}
|
|
@@ -340,14 +370,18 @@ func handleServerStream(requestHandler: Handler) throws {
|
|
|
verify_metadata(initialMetadata, expected: initialClientMetadata)
|
|
verify_metadata(initialMetadata, expected: initialClientMetadata)
|
|
|
|
|
|
|
|
let initialMetadataToSend = Metadata(initialServerMetadata)
|
|
let initialMetadataToSend = Metadata(initialServerMetadata)
|
|
|
- try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
|
|
|
|
|
- let messageString = String(data: messageData!, encoding: .utf8)
|
|
|
|
|
- XCTAssertEqual(messageString, clientText)
|
|
|
|
|
|
|
+ try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) {
|
|
|
|
|
+ if let messageData = $0 {
|
|
|
|
|
+ let messageString = String(data: messageData, encoding: .utf8)
|
|
|
|
|
+ XCTAssertEqual(messageString, clientText)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("handleServerStream message missing")
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let replyMessage = serverText
|
|
|
|
|
|
|
+ let replyMessage = serverText.data(using: .utf8)!
|
|
|
for _ in 0..<steps {
|
|
for _ in 0..<steps {
|
|
|
- try requestHandler.call.sendMessage(data: replyMessage.data(using: .utf8)!) { error in
|
|
|
|
|
|
|
+ try requestHandler.call.sendMessage(data: replyMessage) { error in
|
|
|
XCTAssertNil(error)
|
|
XCTAssertNil(error)
|
|
|
}
|
|
}
|
|
|
requestHandler.call.messageQueueEmpty.wait()
|
|
requestHandler.call.messageQueueEmpty.wait()
|
|
@@ -380,8 +414,12 @@ func handleBiDiStream(requestHandler: Handler) throws {
|
|
|
for _ in 0..<steps {
|
|
for _ in 0..<steps {
|
|
|
let receiveSem = DispatchSemaphore(value: 0)
|
|
let receiveSem = DispatchSemaphore(value: 0)
|
|
|
try requestHandler.call.receiveMessage { callStatus in
|
|
try requestHandler.call.receiveMessage { callStatus in
|
|
|
- let messageString = String(data: callStatus.resultData!, encoding: .utf8)
|
|
|
|
|
- XCTAssertEqual(messageString, clientPing)
|
|
|
|
|
|
|
+ if let messageData = callStatus.resultData {
|
|
|
|
|
+ let messageString = String(data: messageData, encoding: .utf8)
|
|
|
|
|
+ XCTAssertEqual(messageString, clientPing)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ XCTFail("handleBiDiStream message empty")
|
|
|
|
|
+ }
|
|
|
receiveSem.signal()
|
|
receiveSem.signal()
|
|
|
}
|
|
}
|
|
|
_ = receiveSem.wait()
|
|
_ = receiveSem.wait()
|