Skip to content

Commit 13351ff

Browse files
authored
Merge pull request #183 from postmates/add_tests
* Added basic tests for server & bidirectional streaming * Converted to 2-spaces * Fix isEmpty check * Fix race condition
2 parents 975822c + 4489716 commit 13351ff

File tree

3 files changed

+233
-35
lines changed

3 files changed

+233
-35
lines changed

Sources/SwiftGRPC/Core/Call.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public struct CallResult: CustomStringConvertible {
108108
initialMetadata = op.receivedInitialMetadata()
109109
trailingMetadata = op.receivedTrailingMetadata()
110110
} else {
111-
statusCode = .ok
111+
statusCode = .unknown
112112
statusMessage = nil
113113
resultData = nil
114114
initialMetadata = nil

Sources/protoc-gen-swiftgrpc/options.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class GeneratorOptions {
8585
}
8686

8787
static func parseParameter(string: String?) -> [(key: String, value: String)] {
88-
guard let string = string, string.characters.count > 0 else {
88+
guard let string = string, !string.isEmpty else {
8989
return []
9090
}
9191
let parts = string.components(separatedBy: ",")

Tests/SwiftGRPCTests/GRPCTests.swift

Lines changed: 231 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ let initialClientMetadata =
4444
"x": "xylophone",
4545
"y": "yu",
4646
"z": "zither"
47-
]
47+
]
4848
let initialServerMetadata =
4949
[
5050
"a": "Apple",
5151
"b": "Banana",
5252
"c": "Cherry"
53-
]
53+
]
5454
let trailingServerMetadata =
5555
[
5656
// We have more than ten entries here to ensure that even large metadata entries work
@@ -68,11 +68,18 @@ let trailingServerMetadata =
6868
"10": "ten",
6969
"11": "eleven",
7070
"12": "twelve"
71-
]
71+
]
7272
let steps = 10
73-
let hello = "/hello"
74-
let statusCode = StatusCode.ok
75-
let statusMessage = "OK"
73+
let hello = "/hello.unary"
74+
let helloServerStream = "/hello.server-stream"
75+
let helloBiDiStream = "/hello.bidi-stream"
76+
77+
// Return code/message for unary test
78+
let oddStatusCode = StatusCode.ok
79+
let oddStatusMessage = "OK"
80+
81+
let evenStatusCode = StatusCode.notFound
82+
let eventStatusMessage = "Not Found"
7683

7784
func runTest(useSSL: Bool) {
7885
gRPC.initialize()
@@ -87,9 +94,9 @@ func runTest(useSSL: Bool) {
8794
guard
8895
let certificate = try? String(contentsOf: certificateURL, encoding: .utf8),
8996
let key = try? String(contentsOf: keyURL, encoding: .utf8)
90-
else {
91-
// FIXME: We don't want tests to silently pass just because the certificates can't be loaded.
92-
return
97+
else {
98+
// FIXME: We don't want tests to silently pass just because the certificates can't be loaded.
99+
return
93100
}
94101
server = Server(address: address,
95102
key: key,
@@ -135,15 +142,14 @@ func verify_metadata(_ metadata: Metadata, expected: [String: String], file: Sta
135142
}
136143

137144
func runClient(useSSL: Bool) throws {
138-
let message = clientText.data(using: .utf8)
139145
let channel: Channel
140146

141147
if useSSL {
142148
let certificateURL = URL(fileURLWithPath: "Tests/ssl.crt")
143149
guard
144150
let certificates = try? String(contentsOf: certificateURL, encoding: .utf8)
145-
else {
146-
return
151+
else {
152+
return
147153
}
148154
let host = "example.com"
149155
channel = Channel(address: address, certificates: certificates, host: host)
@@ -152,26 +158,40 @@ func runClient(useSSL: Bool) throws {
152158
}
153159

154160
channel.host = host
155-
for _ in 0..<steps {
161+
try callUnary(channel: channel)
162+
try callServerStream(channel: channel)
163+
try callBiDiStream(channel: channel)
164+
}
165+
166+
func callUnary(channel: Channel) throws {
167+
let message = clientText.data(using: .utf8)
168+
169+
for i in 0..<steps {
156170
let sem = DispatchSemaphore(value: 0)
157171
let method = hello
158172
let call = channel.makeCall(method)
159173
let metadata = Metadata(initialClientMetadata)
160174
try call.start(.unary, metadata: metadata, message: message) {
161175
response in
162176
// verify the basic response from the server
163-
XCTAssertEqual(response.statusCode, statusCode)
164-
XCTAssertEqual(response.statusMessage, statusMessage)
177+
XCTAssertEqual(response.statusCode, (i % 2 == 0) ? evenStatusCode : oddStatusCode)
178+
XCTAssertEqual(response.statusMessage, (i % 2 == 0) ? eventStatusMessage : oddStatusMessage)
179+
165180
// verify the message from the server
166-
let resultData = response.resultData!
167-
let messageString = String(data: resultData, encoding: .utf8)
168-
XCTAssertEqual(messageString, serverText)
181+
if (i % 2) == 0 {
182+
let resultData = response.resultData!
183+
let messageString = String(data: resultData, encoding: .utf8)
184+
XCTAssertEqual(messageString, serverText)
185+
}
186+
169187
// verify the initial metadata from the server
170188
let initialMetadata = response.initialMetadata!
171189
verify_metadata(initialMetadata, expected: initialServerMetadata)
190+
172191
// verify the trailing metadata from the server
173192
let trailingMetadata = response.trailingMetadata!
174193
verify_metadata(trailingMetadata, expected: trailingServerMetadata)
194+
175195
// report completion
176196
sem.signal()
177197
}
@@ -180,27 +200,111 @@ func runClient(useSSL: Bool) throws {
180200
}
181201
}
182202

203+
func callServerStream(channel: Channel) throws {
204+
let message = clientText.data(using: .utf8)
205+
let metadata = Metadata(initialClientMetadata)
206+
207+
let sem = DispatchSemaphore(value: 0)
208+
let method = helloServerStream
209+
let call = channel.makeCall(method)
210+
try call.start(.serverStreaming, metadata: metadata, message: message) {
211+
response in
212+
213+
XCTAssertEqual(response.statusCode, StatusCode.outOfRange)
214+
XCTAssertEqual(response.statusMessage, "Out of range")
215+
216+
// verify the trailing metadata from the server
217+
let trailingMetadata = response.trailingMetadata!
218+
verify_metadata(trailingMetadata, expected: trailingServerMetadata)
219+
220+
sem.signal() // signal call is finished
221+
}
222+
223+
for _ in 0..<steps {
224+
let messageSem = DispatchSemaphore(value: 0)
225+
try call.receiveMessage(completion: { (data) in
226+
if let data = data {
227+
let messageString = String(data: data, encoding: .utf8)
228+
XCTAssertEqual(messageString, serverText)
229+
}
230+
messageSem.signal()
231+
})
232+
233+
_ = messageSem.wait()
234+
}
235+
236+
_ = sem.wait()
237+
}
238+
239+
let clientPing = "ping"
240+
let serverPong = "pong"
241+
242+
func callBiDiStream(channel: Channel) throws {
243+
let message = clientPing.data(using: .utf8)
244+
let metadata = Metadata(initialClientMetadata)
245+
246+
let sem = DispatchSemaphore(value: 0)
247+
let method = helloBiDiStream
248+
let call = channel.makeCall(method)
249+
try call.start(.bidiStreaming, metadata: metadata, message: message) {
250+
response in
251+
252+
XCTAssertEqual(response.statusCode, StatusCode.resourceExhausted)
253+
XCTAssertEqual(response.statusMessage, "Resource Exhausted")
254+
255+
// verify the trailing metadata from the server
256+
let trailingMetadata = response.trailingMetadata!
257+
verify_metadata(trailingMetadata, expected: trailingServerMetadata)
258+
259+
sem.signal() // signal call is finished
260+
}
261+
262+
// Send pings
263+
for _ in 0..<steps {
264+
let pingSem = DispatchSemaphore(value: 0)
265+
let message = clientPing.data(using: .utf8)
266+
try call.sendMessage(data: message!) { (err) in
267+
XCTAssertNil(err)
268+
pingSem.signal()
269+
}
270+
_ = pingSem.wait()
271+
}
272+
273+
// Receive pongs
274+
for _ in 0..<steps {
275+
let pongSem = DispatchSemaphore(value: 0)
276+
try call.receiveMessage(completion: { (data) in
277+
if let data = data {
278+
let messageString = String(data: data, encoding: .utf8)
279+
XCTAssertEqual(messageString, serverPong)
280+
}
281+
pongSem.signal()
282+
})
283+
_ = pongSem.wait()
284+
}
285+
286+
_ = sem.wait()
287+
}
288+
183289
func runServer(server: Server) throws {
184290
var requestCount = 0
185291
let sem = DispatchSemaphore(value: 0)
186292
server.run { requestHandler in
187293
do {
188-
requestCount += 1
189-
XCTAssertEqual(requestHandler.host, host)
190-
XCTAssertEqual(requestHandler.method, hello)
191-
let initialMetadata = requestHandler.requestMetadata
192-
verify_metadata(initialMetadata, expected: initialClientMetadata)
193-
let initialMetadataToSend = Metadata(initialServerMetadata)
194-
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
195-
let messageString = String(data: messageData!, encoding: .utf8)
196-
XCTAssertEqual(messageString, clientText)
294+
if let method = requestHandler.method {
295+
switch method {
296+
case hello:
297+
try handleUnary(requestHandler: requestHandler, requestCount: requestCount)
298+
case helloServerStream:
299+
try handleServerStream(requestHandler: requestHandler)
300+
case helloBiDiStream:
301+
try handleBiDiStream(requestHandler: requestHandler)
302+
default:
303+
XCTFail("Invalid method \(method)")
304+
}
197305
}
198-
let replyMessage = serverText
199-
let trailingMetadataToSend = Metadata(trailingServerMetadata)
200-
try requestHandler.sendResponse(message: replyMessage.data(using: .utf8)!,
201-
statusCode: statusCode,
202-
statusMessage: statusMessage,
203-
trailingMetadata: trailingMetadataToSend)
306+
307+
requestCount += 1
204308
} catch (let error) {
205309
XCTFail("error \(error)")
206310
}
@@ -212,3 +316,97 @@ func runServer(server: Server) throws {
212316
// wait for the server to exit
213317
_ = sem.wait()
214318
}
319+
320+
func handleUnary(requestHandler: Handler, requestCount: Int) throws {
321+
XCTAssertEqual(requestHandler.host, host)
322+
XCTAssertEqual(requestHandler.method, hello)
323+
let initialMetadata = requestHandler.requestMetadata
324+
verify_metadata(initialMetadata, expected: initialClientMetadata)
325+
let initialMetadataToSend = Metadata(initialServerMetadata)
326+
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
327+
let messageString = String(data: messageData!, encoding: .utf8)
328+
XCTAssertEqual(messageString, clientText)
329+
}
330+
331+
if (requestCount % 2) == 0 {
332+
let replyMessage = serverText
333+
let trailingMetadataToSend = Metadata(trailingServerMetadata)
334+
try requestHandler.sendResponse(message: replyMessage.data(using: .utf8)!,
335+
statusCode: evenStatusCode,
336+
statusMessage: eventStatusMessage,
337+
trailingMetadata: trailingMetadataToSend)
338+
} else {
339+
let trailingMetadataToSend = Metadata(trailingServerMetadata)
340+
try requestHandler.sendResponse(statusCode: oddStatusCode,
341+
statusMessage: oddStatusMessage,
342+
trailingMetadata: trailingMetadataToSend)
343+
}
344+
}
345+
346+
func handleServerStream(requestHandler: Handler) throws {
347+
XCTAssertEqual(requestHandler.host, host)
348+
XCTAssertEqual(requestHandler.method, helloServerStream)
349+
let initialMetadata = requestHandler.requestMetadata
350+
verify_metadata(initialMetadata, expected: initialClientMetadata)
351+
352+
let initialMetadataToSend = Metadata(initialServerMetadata)
353+
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
354+
let messageString = String(data: messageData!, encoding: .utf8)
355+
XCTAssertEqual(messageString, clientText)
356+
}
357+
358+
let replyMessage = serverText
359+
for _ in 0..<steps {
360+
let sendSem = DispatchSemaphore(value: 0)
361+
try requestHandler.sendResponse(message: replyMessage.data(using: .utf8)!, completion: { (error) in
362+
XCTAssertNil(error)
363+
sendSem.signal()
364+
})
365+
_ = sendSem.wait()
366+
}
367+
368+
let trailingMetadataToSend = Metadata(trailingServerMetadata)
369+
try requestHandler.sendStatus(statusCode: StatusCode.outOfRange,
370+
statusMessage: "Out of range",
371+
trailingMetadata: trailingMetadataToSend)
372+
}
373+
374+
func handleBiDiStream(requestHandler: Handler) throws {
375+
XCTAssertEqual(requestHandler.host, host)
376+
XCTAssertEqual(requestHandler.method, helloBiDiStream)
377+
let initialMetadata = requestHandler.requestMetadata
378+
verify_metadata(initialMetadata, expected: initialClientMetadata)
379+
380+
let initialMetadataToSend = Metadata(initialServerMetadata)
381+
try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { messageData in
382+
let messageString = String(data: messageData!, encoding: .utf8)
383+
XCTAssertEqual(messageString, clientPing)
384+
}
385+
386+
// Receive remaining pings
387+
for _ in 0..<steps-1 {
388+
let receiveSem = DispatchSemaphore(value: 0)
389+
try requestHandler.receiveMessage(completion: { (data) in
390+
let messageString = String(data: data!, encoding: .utf8)
391+
XCTAssertEqual(messageString, clientPing)
392+
receiveSem.signal()
393+
})
394+
_ = receiveSem.wait()
395+
}
396+
397+
// Send back pongs
398+
let replyMessage = serverPong.data(using: .utf8)!
399+
for _ in 0..<steps {
400+
let sendSem = DispatchSemaphore(value: 0)
401+
try requestHandler.sendResponse(message: replyMessage, completion: { (error) in
402+
XCTAssertNil(error)
403+
sendSem.signal()
404+
})
405+
_ = sendSem.wait()
406+
}
407+
408+
let trailingMetadataToSend = Metadata(trailingServerMetadata)
409+
try requestHandler.sendStatus(statusCode: StatusCode.resourceExhausted,
410+
statusMessage: "Resource Exhausted",
411+
trailingMetadata: trailingMetadataToSend)
412+
}

0 commit comments

Comments
 (0)