Browse Source

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
Tim Burks 7 years ago
parent
commit
13351fffa4

+ 1 - 1
Sources/SwiftGRPC/Core/Call.swift

@@ -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

+ 1 - 1
Sources/protoc-gen-swiftgrpc/options.swift

@@ -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: ",")

+ 231 - 33
Tests/SwiftGRPCTests/GRPCTests.swift

@@ -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
@@ -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()
@@ -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,
@@ -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)
@@ -152,7 +158,15 @@ 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)
@@ -160,18 +174,24 @@ func runClient(useSSL: Bool) throws {
     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()
     }
@@ -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)")
     }
@@ -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)
+}