NIOFunctionalTests.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Copyright 2019, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Dispatch
  17. import Foundation
  18. import NIO
  19. import NIOHTTP1
  20. import NIOHTTP2
  21. @testable import SwiftGRPCNIO
  22. import XCTest
  23. protocol NIOFunctionalTests: class {
  24. func testUnary() throws
  25. func testUnaryLotsOfRequests() throws
  26. func testUnaryWithLargeData() throws
  27. func testUnaryEmptyRequest() throws
  28. func testClientStreaming() throws
  29. func testClientStreamingLotsOfMessages() throws
  30. func testServerStreaming() throws
  31. func testServerStreamingLotsOfMessages() throws
  32. func testBidirectionalStreamingBatched() throws
  33. func testBidirectionalStreamingPingPong() throws
  34. func testBidirectionalStreamingLotsOfMessagesBatched() throws
  35. func testBidirectionalStreamingLotsOfMessagesPingPong() throws
  36. }
  37. extension NIOFunctionalTests {
  38. static var allTests: [(String, (Self) -> () throws -> Void)] {
  39. return [
  40. ("testUnary", testUnary),
  41. ("testUnaryLotsOfRequests", testUnaryLotsOfRequests),
  42. ("testUnaryWithLargeData", testUnaryWithLargeData),
  43. ("testUnaryEmptyRequest", testUnaryEmptyRequest),
  44. ("testClientStreaming", testClientStreaming),
  45. ("testClientStreamingLotsOfMessages", testClientStreamingLotsOfMessages),
  46. ("testServerStreaming", testServerStreaming),
  47. ("testServerStreamingLotsOfMessages", testServerStreamingLotsOfMessages),
  48. ("testBidirectionalStreamingBatched", testBidirectionalStreamingBatched),
  49. ("testBidirectionalStreamingPingPong", testBidirectionalStreamingPingPong),
  50. ("testBidirectionalStreamingLotsOfMessagesBatched", testBidirectionalStreamingLotsOfMessagesBatched),
  51. ("testBidirectionalStreamingLotsOfMessagesPingPong", testBidirectionalStreamingLotsOfMessagesPingPong)
  52. ]
  53. }
  54. }
  55. class NIOFunctionalTestsInsecureTransport: NIOEchoTestCaseBase, NIOFunctionalTests {
  56. override var transportSecurity: TransportSecurity {
  57. return .none
  58. }
  59. var aFewStrings: [String] {
  60. return ["foo", "bar", "baz"]
  61. }
  62. var lotsOfStrings: [String] {
  63. return (0..<5_000).map {
  64. String(describing: $0)
  65. }
  66. }
  67. }
  68. extension NIOFunctionalTestsInsecureTransport {
  69. func makeExpectation(description: String, expectedFulfillmentCount: Int = 1, assertForOverFulfill: Bool = true) -> XCTestExpectation {
  70. let expectation = self.expectation(description: description)
  71. expectation.expectedFulfillmentCount = expectedFulfillmentCount
  72. expectation.assertForOverFulfill = assertForOverFulfill
  73. return expectation
  74. }
  75. func makeStatusExpectation(expectedFulfillmentCount: Int = 1) -> XCTestExpectation {
  76. return makeExpectation(description: "Expecting status received",
  77. expectedFulfillmentCount: expectedFulfillmentCount)
  78. }
  79. func makeResponseExpectation(expectedFulfillmentCount: Int = 1) -> XCTestExpectation {
  80. return makeExpectation(description: "Expecting \(expectedFulfillmentCount) response(s)",
  81. expectedFulfillmentCount: expectedFulfillmentCount)
  82. }
  83. }
  84. extension NIOFunctionalTestsInsecureTransport {
  85. func doTestUnary(request: Echo_EchoRequest, expect response: Echo_EchoResponse, file: StaticString = #file, line: UInt = #line) {
  86. let responseExpectation = self.makeResponseExpectation()
  87. let statusExpectation = self.makeStatusExpectation()
  88. let call = client.get(request)
  89. call.response.assertEqual(response, fulfill: responseExpectation, file: file, line: line)
  90. call.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation, file: file, line: line)
  91. self.wait(for: [responseExpectation, statusExpectation], timeout: self.defaultTestTimeout)
  92. }
  93. func doTestUnary(message: String, file: StaticString = #file, line: UInt = #line) {
  94. self.doTestUnary(request: Echo_EchoRequest(text: message), expect: Echo_EchoResponse(text: "Swift echo get: \(message)"), file: file, line: line)
  95. }
  96. func testUnary() throws {
  97. self.doTestUnary(message: "foo")
  98. }
  99. func testUnaryLotsOfRequests() throws {
  100. self.defaultTestTimeout = 60.0
  101. // Sending that many requests at once can sometimes trip things up, it seems.
  102. let clockStart = clock()
  103. let numberOfRequests = 2_000
  104. let responseExpectation = self.makeResponseExpectation(expectedFulfillmentCount: numberOfRequests)
  105. let statusExpectation = self.makeStatusExpectation(expectedFulfillmentCount: numberOfRequests)
  106. for i in 0..<numberOfRequests {
  107. if i % 1_000 == 0 && i > 0 {
  108. print("\(i) requests sent so far, elapsed time: \(Double(clock() - clockStart) / Double(CLOCKS_PER_SEC))")
  109. }
  110. let request = Echo_EchoRequest(text: "foo \(i)")
  111. let response = Echo_EchoResponse(text: "Swift echo get: foo \(i)")
  112. let call = client.get(request)
  113. call.response.assertEqual(response, fulfill: responseExpectation)
  114. call.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation)
  115. }
  116. print("total time to send \(numberOfRequests) requests: \(Double(clock() - clockStart) / Double(CLOCKS_PER_SEC))")
  117. self.wait(for: [responseExpectation, statusExpectation], timeout: self.defaultTestTimeout)
  118. print("total time to receive \(numberOfRequests) responses: \(Double(clock() - clockStart) / Double(CLOCKS_PER_SEC))")
  119. }
  120. func testUnaryWithLargeData() throws {
  121. // Default max frame size is: 16,384. We'll exceed this as we also have to send the size and compression flag.
  122. let longMessage = String(repeating: "e", count: 16_384)
  123. self.doTestUnary(message: longMessage)
  124. }
  125. func testUnaryEmptyRequest() throws {
  126. self.doTestUnary(request: Echo_EchoRequest(), expect: Echo_EchoResponse(text: "Swift echo get: "))
  127. }
  128. }
  129. extension NIOFunctionalTestsInsecureTransport {
  130. func doTestClientStreaming(messages: [String], file: StaticString = #file, line: UInt = #line) throws {
  131. let responseExpectation = self.makeResponseExpectation()
  132. let statusExpectation = self.makeStatusExpectation()
  133. let call = client.collect(callOptions: CallOptions(timeout: .infinite))
  134. call.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation, file: file, line: line)
  135. call.response.assertEqual(Echo_EchoResponse(text: "Swift echo collect: \(messages.joined(separator: " "))"), fulfill: responseExpectation)
  136. var queue = call.newMessageQueue()
  137. for message in messages {
  138. queue = queue.flatMap { call.sendMessage(Echo_EchoRequest(text: message)) }
  139. }
  140. queue.whenSuccess { call.sendEnd(promise: nil) }
  141. self.wait(for: [responseExpectation, statusExpectation], timeout: self.defaultTestTimeout)
  142. }
  143. func testClientStreaming() {
  144. XCTAssertNoThrow(try doTestClientStreaming(messages: aFewStrings))
  145. }
  146. func testClientStreamingLotsOfMessages() throws {
  147. self.defaultTestTimeout = 15.0
  148. XCTAssertNoThrow(try doTestClientStreaming(messages: lotsOfStrings))
  149. }
  150. }
  151. extension NIOFunctionalTestsInsecureTransport {
  152. func doTestServerStreaming(messages: [String], file: StaticString = #file, line: UInt = #line) throws {
  153. let responseExpectation = self.makeResponseExpectation(expectedFulfillmentCount: messages.count)
  154. let statusExpectation = self.makeStatusExpectation()
  155. var iterator = messages.enumerated().makeIterator()
  156. let call = client.expand(Echo_EchoRequest(text: messages.joined(separator: " "))) { response in
  157. if let (index, message) = iterator.next() {
  158. XCTAssertEqual(Echo_EchoResponse(text: "Swift echo expand (\(index)): \(message)"), response, file: file, line: line)
  159. responseExpectation.fulfill()
  160. } else {
  161. XCTFail("Too many responses received", file: file, line: line)
  162. }
  163. }
  164. call.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation, file: file, line: line)
  165. self.wait(for: [responseExpectation, statusExpectation], timeout: self.defaultTestTimeout)
  166. }
  167. func testServerStreaming() {
  168. XCTAssertNoThrow(try doTestServerStreaming(messages: aFewStrings))
  169. }
  170. func testServerStreamingLotsOfMessages() {
  171. self.defaultTestTimeout = 15.0
  172. XCTAssertNoThrow(try doTestServerStreaming(messages: lotsOfStrings))
  173. }
  174. }
  175. extension NIOFunctionalTestsInsecureTransport {
  176. private func doTestBidirectionalStreaming(messages: [String], waitForEachResponse: Bool = false, file: StaticString = #file, line: UInt = #line) throws {
  177. let responseExpectation = self.makeResponseExpectation(expectedFulfillmentCount: messages.count)
  178. let statusExpectation = self.makeStatusExpectation()
  179. let responseReceived = waitForEachResponse ? DispatchSemaphore(value: 0) : nil
  180. var iterator = messages.enumerated().makeIterator()
  181. let call = client.update { response in
  182. if let (index, message) = iterator.next() {
  183. XCTAssertEqual(Echo_EchoResponse(text: "Swift echo update (\(index)): \(message)"), response, file: file, line: line)
  184. responseExpectation.fulfill()
  185. responseReceived?.signal()
  186. } else {
  187. XCTFail("Too many responses received", file: file, line: line)
  188. }
  189. }
  190. call.status.map { $0.code }.assertEqual(.ok, fulfill: statusExpectation, file: file, line: line)
  191. messages.forEach { part in
  192. call.sendMessage(Echo_EchoRequest(text: part), promise: nil)
  193. XCTAssertNotEqual(responseReceived?.wait(timeout: .now() + .seconds(1)), .some(.timedOut), file: file, line: line)
  194. }
  195. call.sendEnd(promise: nil)
  196. self.wait(for: [responseExpectation, statusExpectation], timeout: self.defaultTestTimeout)
  197. }
  198. func testBidirectionalStreamingBatched() throws {
  199. XCTAssertNoThrow(try doTestBidirectionalStreaming(messages: aFewStrings))
  200. }
  201. func testBidirectionalStreamingPingPong() throws {
  202. XCTAssertNoThrow(try doTestBidirectionalStreaming(messages: aFewStrings, waitForEachResponse: true))
  203. }
  204. func testBidirectionalStreamingLotsOfMessagesBatched() throws {
  205. self.defaultTestTimeout = 15.0
  206. XCTAssertNoThrow(try doTestBidirectionalStreaming(messages: lotsOfStrings))
  207. }
  208. func testBidirectionalStreamingLotsOfMessagesPingPong() throws {
  209. self.defaultTestTimeout = 15.0
  210. XCTAssertNoThrow(try doTestBidirectionalStreaming(messages: lotsOfStrings, waitForEachResponse: true))
  211. }
  212. }
  213. class NIOFunctionalTestsAnonymousClient: NIOFunctionalTestsInsecureTransport {
  214. override var transportSecurity: TransportSecurity {
  215. return .anonymousClient
  216. }
  217. }
  218. class NIOFunctionalTestsMutualAuthentication: NIOFunctionalTestsInsecureTransport {
  219. override var transportSecurity: TransportSecurity {
  220. return .mutualAuthentication
  221. }
  222. }