Browse Source

Also add client-timeout tests and rename "ErrorHandlingTests" to "ConnectionFailureTests".

Daniel Alm 7 years ago
parent
commit
0985a47176

+ 3 - 2
Sources/Examples/Echo/EchoProvider.swift

@@ -49,7 +49,8 @@ class EchoProvider: Echo_EchoProvider {
           else { break }  // End of stream
         parts.append(request.text)
       } catch {
-        print("\(error)")
+        print("collect error: \(error)")
+        break
       }
     }
     var response = Echo_EchoResponse()
@@ -73,7 +74,7 @@ class EchoProvider: Echo_EchoProvider {
           }
         }
       } catch {
-        print("\(error)")
+        print("update error: \(error)")
         break
       }
     }

+ 2 - 1
Tests/LinuxMain.swift

@@ -18,7 +18,8 @@ import XCTest
 
 XCTMain([
   testCase(gRPCTests.allTests),
+  testCase(ClientTimeoutTests.allTests),
+  testCase(ConnectionFailureTests.allTests),
   testCase(EchoTests.allTests),
-  testCase(ErrorHandlingTests.allTests),
   testCase(ServerTimeoutTests.allTests)
 ])

+ 70 - 0
Tests/SwiftGRPCTests/BasicEchoTestCase.swift

@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Dispatch
+import Foundation
+@testable import SwiftGRPC
+import XCTest
+
+extension Echo_EchoRequest {
+  init(text: String) {
+    self.text = text
+  }
+}
+
+class BasicEchoTestCase: XCTestCase {
+  func makeProvider() -> Echo_EchoProvider { return EchoProvider() }
+  
+  var defaultTimeout: TimeInterval { return 1.0 }
+  
+  var provider: Echo_EchoProvider!
+  var server: Echo_EchoServer!
+  var client: Echo_EchoServiceClient!
+  
+  var secure: Bool { return false }
+  
+  override func setUp() {
+    super.setUp()
+    
+    provider = makeProvider()
+    
+    let address = "localhost:5050"
+    if secure {
+      let certificateString = String(data: certificateForTests, encoding: .utf8)!
+      server = Echo_EchoServer(address: address,
+                               certificateString: certificateString,
+                               keyString: String(data: keyForTests, encoding: .utf8)!,
+                               provider: provider)
+      server.start(queue: DispatchQueue.global())
+      client = Echo_EchoServiceClient(address: address, certificates: certificateString, host: "example.com")
+      client.host = "example.com"
+    } else {
+      server = Echo_EchoServer(address: address, provider: provider)
+      server.start(queue: DispatchQueue.global())
+      client = Echo_EchoServiceClient(address: address, secure: false)
+    }
+    
+    client.timeout = defaultTimeout
+  }
+  
+  override func tearDown() {
+    client = nil
+    
+    server.server.stop()
+    server = nil
+    
+    super.tearDown()
+  }
+}

+ 84 - 0
Tests/SwiftGRPCTests/ClientTimeoutTests.swift

@@ -0,0 +1,84 @@
+/*
+ * Copyright 2018, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Dispatch
+import Foundation
+@testable import SwiftGRPC
+import XCTest
+
+class ClientTimeoutTests: BasicEchoTestCase {
+  static var allTests: [(String, (ClientTimeoutTests) -> () throws -> Void)] {
+    return [
+      ("testClientStreamingTimeoutBeforeSending", testClientStreamingTimeoutBeforeSending),
+      ("testClientStreamingTimeoutAfterSending", testClientStreamingTimeoutAfterSending)
+    ]
+  }
+  
+  override var defaultTimeout: TimeInterval { return 0.1 }
+}
+
+extension ClientTimeoutTests {
+  func testClientStreamingTimeoutBeforeSending() {
+    let completionHandlerExpectation = expectation(description: "final completion handler called")
+    let call = try! client.collect { callResult in
+      XCTAssertEqual(.deadlineExceeded, callResult.statusCode)
+      completionHandlerExpectation.fulfill()
+    }
+    
+    Thread.sleep(forTimeInterval: 0.2)
+    
+    let sendExpectation = expectation(description: "send completion handler 1 called")
+    try! call.send(Echo_EchoRequest(text: "foo")) { [sendExpectation] in
+      XCTAssertEqual(.unknown, $0 as! CallError)
+      sendExpectation.fulfill()
+    }
+    call.waitForSendOperationsToFinish()
+    
+    do {
+      _ = try call.closeAndReceive()
+      XCTFail("should have thrown")
+    } catch let receiveError {
+      XCTAssertEqual(.unknown, (receiveError as! RPCError).callResult!.statusCode)
+    }
+    
+    waitForExpectations(timeout: defaultTimeout)
+  }
+  
+  func testClientStreamingTimeoutAfterSending() {
+    let completionHandlerExpectation = expectation(description: "final completion handler called")
+    let call = try! client.collect { callResult in
+      XCTAssertEqual(.deadlineExceeded, callResult.statusCode)
+      completionHandlerExpectation.fulfill()
+    }
+    
+    let sendExpectation = expectation(description: "send completion handler 1 called")
+    try! call.send(Echo_EchoRequest(text: "foo")) { [sendExpectation] in XCTAssertNil($0); sendExpectation.fulfill() }
+    call.waitForSendOperationsToFinish()
+    
+    Thread.sleep(forTimeInterval: 0.2)
+    
+    do {
+      _ = try call.closeAndReceive()
+      XCTFail("should have thrown")
+    } catch let receiveError {
+      XCTAssertEqual(.unknown, (receiveError as! RPCError).callResult!.statusCode)
+    }
+    
+    waitForExpectations(timeout: defaultTimeout)
+  }
+  
+  // FIXME(danielalm): Add support for setting a maximum timeout on the server, to prevent DoS attacks where clients
+  // start a ton of calls, but never finish them (i.e. essentially leaking a connection on the server side).
+}

+ 4 - 4
Tests/SwiftGRPCTests/ErrorHandlingTests.swift → Tests/SwiftGRPCTests/ConnectionFailureTests.swift

@@ -18,9 +18,9 @@ import Foundation
 @testable import SwiftGRPC
 import XCTest
 
-// TODO(danielalm): Also run a similar set of tests with SSL enabled.
-class ErrorHandlingTests: XCTestCase {
-  static var allTests: [(String, (ErrorHandlingTests) -> () throws -> Void)] {
+// TODO(danielalm): Also test connection failure with regards to SSL issues.
+class ConnectionFailureTests: XCTestCase {
+  static var allTests: [(String, (ConnectionFailureTests) -> () throws -> Void)] {
     return [
       ("testConnectionFailureUnary", testConnectionFailureUnary),
       ("testConnectionFailureClientStreaming", testConnectionFailureClientStreaming),
@@ -34,7 +34,7 @@ class ErrorHandlingTests: XCTestCase {
   let defaultTimeout: TimeInterval = 0.5
 }
 
-extension ErrorHandlingTests {
+extension ConnectionFailureTests {
   func testConnectionFailureUnary() {
     let client = Echo_EchoServiceClient(address: "localhost:1234", secure: false)
     client.timeout = defaultTimeout

+ 1 - 46
Tests/SwiftGRPCTests/EchoTests.swift

@@ -18,13 +18,7 @@ import Foundation
 @testable import SwiftGRPC
 import XCTest
 
-extension Echo_EchoRequest {
-  init(text: String) {
-    self.text = text
-  }
-}
-
-class EchoTests: XCTestCase {
+class EchoTests: BasicEchoTestCase {
   static var allTests: [(String, (EchoTests) -> () throws -> Void)] {
     return [
       ("testUnary", testUnary),
@@ -40,45 +34,6 @@ class EchoTests: XCTestCase {
   }
 
   static let lotsOfStrings = (0..<1000).map { String(describing: $0) }
-
-  let defaultTimeout: TimeInterval = 1.0
-
-  let provider = EchoProvider()
-  var server: Echo_EchoServer!
-  var client: Echo_EchoServiceClient!
-
-  var secure: Bool { return false }
-
-  override func setUp() {
-    super.setUp()
-
-    let address = "localhost:5050"
-    if secure {
-      let certificateString = String(data: certificateForTests, encoding: .utf8)!
-      server = Echo_EchoServer(address: address,
-                               certificateString: certificateString,
-                               keyString: String(data: keyForTests, encoding: .utf8)!,
-                               provider: provider)
-      server.start(queue: DispatchQueue.global())
-      client = Echo_EchoServiceClient(address: address, certificates: certificateString, host: "example.com")
-      client.host = "example.com"
-    } else {
-      server = Echo_EchoServer(address: address, provider: provider)
-      server.start(queue: DispatchQueue.global())
-      client = Echo_EchoServiceClient(address: address, secure: false)
-    }
-
-    client.timeout = defaultTimeout
-  }
-
-  override func tearDown() {
-    client = nil
-
-    server.server.stop()
-    server = nil
-
-    super.tearDown()
-  }
 }
 
 class EchoTestsSecure: EchoTests {

+ 2 - 2
Tests/SwiftGRPCTests/GRPCTests.swift

@@ -214,7 +214,7 @@ func callServerStream(channel: Channel) throws {
         let messageString = String(data: data, encoding: .utf8)
         XCTAssertEqual(messageString, serverText)
       } else {
-        print("callServerStream unexpected result: \(callResult)")
+        XCTFail("callServerStream unexpected result: \(callResult)")
       }
       messageSem.signal()
     }
@@ -271,7 +271,7 @@ func callBiDiStream(channel: Channel) throws {
         let messageString = String(data: data, encoding: .utf8)
         XCTAssertEqual(messageString, serverPong)
       } else {
-        print("callBiDiStream unexpected result: \(callResult)")
+        XCTFail("callBiDiStream unexpected result: \(callResult)")
       }
       pongSem.signal()
     }

+ 3 - 25
Tests/SwiftGRPCTests/ServerTimeoutTests.swift

@@ -37,7 +37,7 @@ fileprivate class TimingOutEchoProvider: Echo_EchoProvider {
   }
 }
 
-class ServerTimeoutTests: XCTestCase {
+class ServerTimeoutTests: BasicEchoTestCase {
   static var allTests: [(String, (ServerTimeoutTests) -> () throws -> Void)] {
     return [
       ("testTimeoutUnary", testTimeoutUnary),
@@ -47,31 +47,9 @@ class ServerTimeoutTests: XCTestCase {
     ]
   }
   
-  let defaultTimeout: TimeInterval = 0.1
+  override func makeProvider() -> Echo_EchoProvider { return TimingOutEchoProvider() }
   
-  fileprivate let provider = TimingOutEchoProvider()
-  var server: Echo_EchoServer!
-  var client: Echo_EchoServiceClient!
-  
-  override func setUp() {
-    super.setUp()
-    
-    let address = "localhost:5050"
-    server = Echo_EchoServer(address: address, provider: provider)
-    server.start(queue: DispatchQueue.global())
-    client = Echo_EchoServiceClient(address: address, secure: false)
-    
-    client.timeout = defaultTimeout
-  }
-  
-  override func tearDown() {
-    client = nil
-    
-    server.server.stop()
-    server = nil
-    
-    super.tearDown()
-  }
+  override var defaultTimeout: TimeInterval { return 0.1 }
 }
 
 extension ServerTimeoutTests {