Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added tests for server & bi-directional streaming #183

Merged
merged 4 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/SwiftGRPC/Core/Call.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public struct CallResult: CustomStringConvertible {
initialMetadata = op.receivedInitialMetadata()
trailingMetadata = op.receivedTrailingMetadata()
} else {
statusCode = .ok
statusCode = .unknown
statusMessage = nil
resultData = nil
initialMetadata = nil
Expand Down
2 changes: 1 addition & 1 deletion Sources/protoc-gen-swiftgrpc/options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class GeneratorOptions {
}

static func parseParameter(string: String?) -> [(key: String, value: String)] {
guard let string = string, string.characters.count > 0 else {
guard let string = string, !string.isEmpty else {
return []
}
let parts = string.components(separatedBy: ",")
Expand Down
264 changes: 231 additions & 33 deletions Tests/SwiftGRPCTests/GRPCTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ let initialClientMetadata =
"x": "xylophone",
"y": "yu",
"z": "zither"
]
]
let initialServerMetadata =
[
"a": "Apple",
"b": "Banana",
"c": "Cherry"
]
]
let trailingServerMetadata =
[
// We have more than ten entries here to ensure that even large metadata entries work
Expand All @@ -68,11 +68,18 @@ let trailingServerMetadata =
"10": "ten",
"11": "eleven",
"12": "twelve"
]
]
let steps = 10
let hello = "/hello"
let statusCode = StatusCode.ok
let statusMessage = "OK"
let hello = "/hello.unary"
let helloServerStream = "/hello.server-stream"
let helloBiDiStream = "/hello.bidi-stream"

// Return code/message for unary test
let oddStatusCode = StatusCode.ok
let oddStatusMessage = "OK"

let evenStatusCode = StatusCode.notFound
let eventStatusMessage = "Not Found"

func runTest(useSSL: Bool) {
gRPC.initialize()
Expand All @@ -87,9 +94,9 @@ func runTest(useSSL: Bool) {
guard
let certificate = try? String(contentsOf: certificateURL, encoding: .utf8),
let key = try? String(contentsOf: keyURL, encoding: .utf8)
else {
// FIXME: We don't want tests to silently pass just because the certificates can't be loaded.
return
else {
// FIXME: We don't want tests to silently pass just because the certificates can't be loaded.
return
}
server = Server(address: address,
key: key,
Expand Down Expand Up @@ -135,15 +142,14 @@ func verify_metadata(_ metadata: Metadata, expected: [String: String], file: Sta
}

func runClient(useSSL: Bool) throws {
let message = clientText.data(using: .utf8)
let channel: Channel

if useSSL {
let certificateURL = URL(fileURLWithPath: "Tests/ssl.crt")
guard
let certificates = try? String(contentsOf: certificateURL, encoding: .utf8)
else {
return
else {
return
}
let host = "example.com"
channel = Channel(address: address, certificates: certificates, host: host)
Expand All @@ -152,26 +158,40 @@ func runClient(useSSL: Bool) throws {
}

channel.host = host
for _ in 0..<steps {
try callUnary(channel: channel)
try callServerStream(channel: channel)
try callBiDiStream(channel: channel)
}

func callUnary(channel: Channel) throws {
let message = clientText.data(using: .utf8)

for i in 0..<steps {
let sem = DispatchSemaphore(value: 0)
let method = hello
let call = channel.makeCall(method)
let metadata = Metadata(initialClientMetadata)
try call.start(.unary, metadata: metadata, message: message) {
response in
// verify the basic response from the server
XCTAssertEqual(response.statusCode, statusCode)
XCTAssertEqual(response.statusMessage, statusMessage)
XCTAssertEqual(response.statusCode, (i % 2 == 0) ? evenStatusCode : oddStatusCode)
XCTAssertEqual(response.statusMessage, (i % 2 == 0) ? eventStatusMessage : oddStatusMessage)

// verify the message from the server
let resultData = response.resultData!
let messageString = String(data: resultData, encoding: .utf8)
XCTAssertEqual(messageString, serverText)
if (i % 2) == 0 {
let resultData = response.resultData!
let messageString = String(data: resultData, encoding: .utf8)
XCTAssertEqual(messageString, serverText)
}

// verify the initial metadata from the server
let initialMetadata = response.initialMetadata!
verify_metadata(initialMetadata, expected: initialServerMetadata)

// verify the trailing metadata from the server
let trailingMetadata = response.trailingMetadata!
verify_metadata(trailingMetadata, expected: trailingServerMetadata)

// report completion
sem.signal()
}
Expand All @@ -180,27 +200,111 @@ func runClient(useSSL: Bool) throws {
}
}

func callServerStream(channel: Channel) throws {
let message = clientText.data(using: .utf8)
let metadata = Metadata(initialClientMetadata)

let sem = DispatchSemaphore(value: 0)
let method = helloServerStream
let call = channel.makeCall(method)
try call.start(.serverStreaming, metadata: metadata, message: message) {
response in

XCTAssertEqual(response.statusCode, StatusCode.outOfRange)
XCTAssertEqual(response.statusMessage, "Out of range")

// verify the trailing metadata from the server
let trailingMetadata = response.trailingMetadata!
verify_metadata(trailingMetadata, expected: trailingServerMetadata)

sem.signal() // signal call is finished
}

for _ in 0..<steps {
let messageSem = DispatchSemaphore(value: 0)
try call.receiveMessage(completion: { (data) in
if let data = data {
let messageString = String(data: data, encoding: .utf8)
XCTAssertEqual(messageString, serverText)
}
messageSem.signal()
})

_ = messageSem.wait()
}

_ = sem.wait()
}

let clientPing = "ping"
let serverPong = "pong"

func callBiDiStream(channel: Channel) throws {
let message = clientPing.data(using: .utf8)
let metadata = Metadata(initialClientMetadata)

let sem = DispatchSemaphore(value: 0)
let method = helloBiDiStream
let call = channel.makeCall(method)
try call.start(.bidiStreaming, metadata: metadata, message: message) {
response in

XCTAssertEqual(response.statusCode, StatusCode.resourceExhausted)
XCTAssertEqual(response.statusMessage, "Resource Exhausted")

// verify the trailing metadata from the server
let trailingMetadata = response.trailingMetadata!
verify_metadata(trailingMetadata, expected: trailingServerMetadata)

sem.signal() // signal call is finished
}

// Send pings
for _ in 0..<steps {
let pingSem = DispatchSemaphore(value: 0)
let message = clientPing.data(using: .utf8)
try call.sendMessage(data: message!) { (err) in
XCTAssertNil(err)
pingSem.signal()
}
_ = pingSem.wait()
}

// Receive pongs
for _ in 0..<steps {
let pongSem = DispatchSemaphore(value: 0)
try call.receiveMessage(completion: { (data) in
if let data = data {
let messageString = String(data: data, encoding: .utf8)
XCTAssertEqual(messageString, serverPong)
}
pongSem.signal()
})
_ = pongSem.wait()
}

_ = sem.wait()
}

func runServer(server: Server) throws {
var requestCount = 0
let sem = DispatchSemaphore(value: 0)
server.run { requestHandler in
do {
requestCount += 1
XCTAssertEqual(requestHandler.host, host)
XCTAssertEqual(requestHandler.method, hello)
let initialMetadata = requestHandler.requestMetadata
verify_metadata(initialMetadata, expected: initialClientMetadata)
let initialMetadataToSend = Metadata(initialServerMetadata)
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
let messageString = String(data: messageData!, encoding: .utf8)
XCTAssertEqual(messageString, clientText)
if let method = requestHandler.method {
switch method {
case hello:
try handleUnary(requestHandler: requestHandler, requestCount: requestCount)
case helloServerStream:
try handleServerStream(requestHandler: requestHandler)
case helloBiDiStream:
try handleBiDiStream(requestHandler: requestHandler)
default:
XCTFail("Invalid method \(method)")
}
}
let replyMessage = serverText
let trailingMetadataToSend = Metadata(trailingServerMetadata)
try requestHandler.sendResponse(message: replyMessage.data(using: .utf8)!,
statusCode: statusCode,
statusMessage: statusMessage,
trailingMetadata: trailingMetadataToSend)

requestCount += 1
} catch (let error) {
XCTFail("error \(error)")
}
Expand All @@ -212,3 +316,97 @@ func runServer(server: Server) throws {
// wait for the server to exit
_ = sem.wait()
}

func handleUnary(requestHandler: Handler, requestCount: Int) throws {
XCTAssertEqual(requestHandler.host, host)
XCTAssertEqual(requestHandler.method, hello)
let initialMetadata = requestHandler.requestMetadata
verify_metadata(initialMetadata, expected: initialClientMetadata)
let initialMetadataToSend = Metadata(initialServerMetadata)
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
let messageString = String(data: messageData!, encoding: .utf8)
XCTAssertEqual(messageString, clientText)
}

if (requestCount % 2) == 0 {
let replyMessage = serverText
let trailingMetadataToSend = Metadata(trailingServerMetadata)
try requestHandler.sendResponse(message: replyMessage.data(using: .utf8)!,
statusCode: evenStatusCode,
statusMessage: eventStatusMessage,
trailingMetadata: trailingMetadataToSend)
} else {
let trailingMetadataToSend = Metadata(trailingServerMetadata)
try requestHandler.sendResponse(statusCode: oddStatusCode,
statusMessage: oddStatusMessage,
trailingMetadata: trailingMetadataToSend)
}
}

func handleServerStream(requestHandler: Handler) throws {
XCTAssertEqual(requestHandler.host, host)
XCTAssertEqual(requestHandler.method, helloServerStream)
let initialMetadata = requestHandler.requestMetadata
verify_metadata(initialMetadata, expected: initialClientMetadata)

let initialMetadataToSend = Metadata(initialServerMetadata)
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
let messageString = String(data: messageData!, encoding: .utf8)
XCTAssertEqual(messageString, clientText)
}

let replyMessage = serverText
for _ in 0..<steps {
let sendSem = DispatchSemaphore(value: 0)
try requestHandler.sendResponse(message: replyMessage.data(using: .utf8)!, completion: { (error) in
XCTAssertNil(error)
sendSem.signal()
})
_ = sendSem.wait()
}

let trailingMetadataToSend = Metadata(trailingServerMetadata)
try requestHandler.sendStatus(statusCode: StatusCode.outOfRange,
statusMessage: "Out of range",
trailingMetadata: trailingMetadataToSend)
}

func handleBiDiStream(requestHandler: Handler) throws {
XCTAssertEqual(requestHandler.host, host)
XCTAssertEqual(requestHandler.method, helloBiDiStream)
let initialMetadata = requestHandler.requestMetadata
verify_metadata(initialMetadata, expected: initialClientMetadata)

let initialMetadataToSend = Metadata(initialServerMetadata)
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
let messageString = String(data: messageData!, encoding: .utf8)
XCTAssertEqual(messageString, clientPing)
}

// Receive remaining pings
for _ in 0..<steps-1 {
let receiveSem = DispatchSemaphore(value: 0)
try requestHandler.receiveMessage(completion: { (data) in
let messageString = String(data: data!, encoding: .utf8)
XCTAssertEqual(messageString, clientPing)
receiveSem.signal()
})
_ = receiveSem.wait()
}

// Send back pongs
let replyMessage = serverPong.data(using: .utf8)!
for _ in 0..<steps {
let sendSem = DispatchSemaphore(value: 0)
try requestHandler.sendResponse(message: replyMessage, completion: { (error) in
XCTAssertNil(error)
sendSem.signal()
})
_ = sendSem.wait()
}

let trailingMetadataToSend = Metadata(trailingServerMetadata)
try requestHandler.sendStatus(statusCode: StatusCode.resourceExhausted,
statusMessage: "Resource Exhausted",
trailingMetadata: trailingMetadataToSend)
}