BasicEchoTestCase.swift 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. * Copyright 2018, 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 EchoImplementation
  18. import EchoModel
  19. import Foundation
  20. import GRPC
  21. import GRPCSampleData
  22. import NIOCore
  23. import XCTest
  24. #if canImport(NIOSSL)
  25. import NIOSSL
  26. #endif
  27. extension Echo_EchoRequest {
  28. init(text: String) {
  29. self = .with {
  30. $0.text = text
  31. }
  32. }
  33. }
  34. extension Echo_EchoResponse {
  35. init(text: String) {
  36. self = .with {
  37. $0.text = text
  38. }
  39. }
  40. }
  41. enum TransportSecurity {
  42. case none
  43. case anonymousClient
  44. case mutualAuthentication
  45. }
  46. class EchoTestCaseBase: GRPCTestCase {
  47. // Things can be slow when running under TSAN; bias towards a really long timeout so that we know
  48. // for sure a test is wedged rather than simply slow.
  49. var defaultTestTimeout: TimeInterval = 120.0
  50. var serverEventLoopGroup: EventLoopGroup!
  51. var clientEventLoopGroup: EventLoopGroup!
  52. var transportSecurity: TransportSecurity { return .none }
  53. var server: Server!
  54. var client: Echo_EchoNIOClient!
  55. var port: Int!
  56. // Prefer POSIX: subclasses can override this and add availability checks to ensure NIOTS
  57. // variants run where possible.
  58. var networkPreference: NetworkPreference {
  59. return .userDefined(.posix)
  60. }
  61. func connectionBuilder() -> ClientConnection.Builder {
  62. switch self.transportSecurity {
  63. case .none:
  64. return ClientConnection.insecure(group: self.clientEventLoopGroup)
  65. case .anonymousClient:
  66. #if canImport(NIOSSL)
  67. return ClientConnection.usingTLSBackedByNIOSSL(on: self.clientEventLoopGroup)
  68. .withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
  69. #else
  70. fatalError("NIOSSL must be imported to use TLS")
  71. #endif
  72. case .mutualAuthentication:
  73. #if canImport(NIOSSL)
  74. return ClientConnection.usingTLSBackedByNIOSSL(on: self.clientEventLoopGroup)
  75. .withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
  76. .withTLS(certificateChain: [SampleCertificate.client.certificate])
  77. .withTLS(privateKey: SamplePrivateKey.client)
  78. #else
  79. fatalError("NIOSSL must be imported to use TLS")
  80. #endif
  81. }
  82. }
  83. func serverBuilder() -> Server.Builder {
  84. switch self.transportSecurity {
  85. case .none:
  86. return Server.insecure(group: self.serverEventLoopGroup)
  87. case .anonymousClient:
  88. #if canImport(NIOSSL)
  89. return Server.usingTLSBackedByNIOSSL(
  90. on: self.serverEventLoopGroup,
  91. certificateChain: [SampleCertificate.server.certificate],
  92. privateKey: SamplePrivateKey.server
  93. ).withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
  94. #else
  95. fatalError("NIOSSL must be imported to use TLS")
  96. #endif
  97. case .mutualAuthentication:
  98. #if canImport(NIOSSL)
  99. return Server.usingTLSBackedByNIOSSL(
  100. on: self.serverEventLoopGroup,
  101. certificateChain: [SampleCertificate.server.certificate],
  102. privateKey: SamplePrivateKey.server
  103. )
  104. .withTLS(trustRoots: .certificates([SampleCertificate.ca.certificate]))
  105. .withTLS(certificateVerification: .noHostnameVerification)
  106. #else
  107. fatalError("NIOSSL must be imported to use TLS")
  108. #endif
  109. }
  110. }
  111. func makeServer() throws -> Server {
  112. return try self.serverBuilder()
  113. .withErrorDelegate(self.makeErrorDelegate())
  114. .withServiceProviders([self.makeEchoProvider()])
  115. .withLogger(self.serverLogger)
  116. .bind(host: "localhost", port: 0)
  117. .wait()
  118. }
  119. func makeClientConnection(port: Int) throws -> ClientConnection {
  120. return self.connectionBuilder()
  121. .withBackgroundActivityLogger(self.clientLogger)
  122. .connect(host: "localhost", port: port)
  123. }
  124. func makeEchoProvider() -> Echo_EchoProvider { return EchoProvider() }
  125. func makeErrorDelegate() -> ServerErrorDelegate? { return nil }
  126. func makeEchoClient(port: Int) throws -> Echo_EchoNIOClient {
  127. return Echo_EchoNIOClient(
  128. channel: try self.makeClientConnection(port: port),
  129. defaultCallOptions: self.callOptionsWithLogger
  130. )
  131. }
  132. override func setUp() {
  133. super.setUp()
  134. self.serverEventLoopGroup = PlatformSupport.makeEventLoopGroup(
  135. loopCount: 1,
  136. networkPreference: self.networkPreference
  137. )
  138. self.server = try! self.makeServer()
  139. self.port = self.server.channel.localAddress!.port!
  140. self.clientEventLoopGroup = PlatformSupport.makeEventLoopGroup(
  141. loopCount: 1,
  142. networkPreference: self.networkPreference
  143. )
  144. self.client = try! self.makeEchoClient(port: self.port)
  145. }
  146. override func tearDown() {
  147. // Some tests close the channel, so would throw here if called twice.
  148. try? self.client.channel.close().wait()
  149. XCTAssertNoThrow(try self.clientEventLoopGroup.syncShutdownGracefully())
  150. self.client = nil
  151. self.clientEventLoopGroup = nil
  152. XCTAssertNoThrow(try self.server.close().wait())
  153. XCTAssertNoThrow(try self.serverEventLoopGroup.syncShutdownGracefully())
  154. self.server = nil
  155. self.serverEventLoopGroup = nil
  156. self.port = nil
  157. super.tearDown()
  158. }
  159. }
  160. extension EchoTestCaseBase {
  161. func makeExpectation(
  162. description: String,
  163. expectedFulfillmentCount: Int = 1,
  164. assertForOverFulfill: Bool = true
  165. ) -> XCTestExpectation {
  166. let expectation = self.expectation(description: description)
  167. expectation.expectedFulfillmentCount = expectedFulfillmentCount
  168. expectation.assertForOverFulfill = assertForOverFulfill
  169. return expectation
  170. }
  171. func makeStatusExpectation(expectedFulfillmentCount: Int = 1) -> XCTestExpectation {
  172. return self.makeExpectation(
  173. description: "Expecting status received",
  174. expectedFulfillmentCount: expectedFulfillmentCount
  175. )
  176. }
  177. func makeResponseExpectation(expectedFulfillmentCount: Int = 1) -> XCTestExpectation {
  178. return self.makeExpectation(
  179. description: "Expecting \(expectedFulfillmentCount) response(s)",
  180. expectedFulfillmentCount: expectedFulfillmentCount
  181. )
  182. }
  183. func makeRequestExpectation(expectedFulfillmentCount: Int = 1) -> XCTestExpectation {
  184. return self.makeExpectation(
  185. description: "Expecting \(expectedFulfillmentCount) request(s) to have been sent",
  186. expectedFulfillmentCount: expectedFulfillmentCount
  187. )
  188. }
  189. func makeInitialMetadataExpectation() -> XCTestExpectation {
  190. return self.makeExpectation(description: "Expecting initial metadata")
  191. }
  192. }