XCTestHelpers.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /*
  2. * Copyright 2020, 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. @testable import GRPC
  17. import NIO
  18. import NIOHPACK
  19. import NIOHTTP1
  20. import NIOHTTP2
  21. import XCTest
  22. struct UnwrapError: Error {}
  23. // We support Swift versions before 'XCTUnwrap' was introduced.
  24. func assertNotNil<Value>(
  25. _ expression: @autoclosure () throws -> Value?,
  26. message: @autoclosure () -> String = "Optional value was nil",
  27. file: StaticString = #file,
  28. line: UInt = #line
  29. ) throws -> Value {
  30. guard let value = try expression() else {
  31. XCTFail(message(), file: file, line: line)
  32. throw UnwrapError()
  33. }
  34. return value
  35. }
  36. func assertNoThrow<Value>(
  37. _ expression: @autoclosure () throws -> Value,
  38. message: @autoclosure () -> String = "Unexpected error thrown",
  39. file: StaticString = #file,
  40. line: UInt = #line
  41. ) throws -> Value {
  42. do {
  43. return try expression()
  44. } catch {
  45. XCTFail(message(), file: file, line: line)
  46. throw error
  47. }
  48. }
  49. // MARK: - Matchers.
  50. func assertThat<Value>(
  51. _ expression: @autoclosure @escaping () throws -> Value,
  52. _ matcher: Matcher<Value>,
  53. file: StaticString = #file,
  54. line: UInt = #line
  55. ) {
  56. // For value matchers we'll assert that we don't throw by default.
  57. assertThat(try expression(), .doesNotThrow(matcher), file: file, line: line)
  58. }
  59. func assertThat<Value>(
  60. _ expression: @autoclosure @escaping () throws -> Value,
  61. _ matcher: ExpressionMatcher<Value>,
  62. file: StaticString = #file,
  63. line: UInt = #line
  64. ) {
  65. switch matcher.evaluate(expression) {
  66. case .match:
  67. ()
  68. case let .noMatch(actual: actual, expected: expected):
  69. XCTFail("ACTUAL: \(actual), EXPECTED: \(expected)", file: file, line: line)
  70. }
  71. }
  72. enum MatchResult {
  73. case match
  74. case noMatch(actual: String, expected: String)
  75. }
  76. struct Matcher<Value> {
  77. fileprivate typealias Evaluator = (Value) -> MatchResult
  78. private var matcher: Evaluator
  79. fileprivate init(_ matcher: @escaping Evaluator) {
  80. self.matcher = matcher
  81. }
  82. fileprivate func evaluate(_ value: Value) -> MatchResult {
  83. return self.matcher(value)
  84. }
  85. // MARK: Sugar
  86. /// Just returns the provided matcher.
  87. static func `is`<Value>(_ matcher: Matcher<Value>) -> Matcher<Value> {
  88. return matcher
  89. }
  90. /// Just returns the provided matcher.
  91. static func and<Value>(_ matcher: Matcher<Value>) -> Matcher<Value> {
  92. return matcher
  93. }
  94. // MARK: Equality
  95. /// Checks the equality of the actual value against the provided value. See `equalTo(_:)`.
  96. static func `is`<Value: Equatable>(_ value: Value) -> Matcher<Value> {
  97. return .equalTo(value)
  98. }
  99. /// Checks the equality of the actual value against the provided value.
  100. static func equalTo<Value: Equatable>(_ expected: Value) -> Matcher<Value> {
  101. return .init { actual in
  102. actual == expected
  103. ? .match
  104. : .noMatch(actual: "\(actual)", expected: "equal to \(expected)")
  105. }
  106. }
  107. /// Always returns a 'match', useful when the expected value is `Void`.
  108. static func isVoid() -> Matcher<Void> {
  109. return .init {
  110. return .match
  111. }
  112. }
  113. /// Matches if the value is `nil`.
  114. static func `nil`<Value>() -> Matcher<Value?> {
  115. return .init { actual in
  116. actual == nil
  117. ? .match
  118. : .noMatch(actual: String(describing: actual), expected: "nil")
  119. }
  120. }
  121. /// Matches if the value is not `nil`.
  122. static func notNil<Value>(_ matcher: Matcher<Value>? = nil) -> Matcher<Value?> {
  123. return .init { actual in
  124. if let actual = actual {
  125. return matcher?.evaluate(actual) ?? .match
  126. } else {
  127. return .noMatch(actual: "nil", expected: "not nil")
  128. }
  129. }
  130. }
  131. // MARK: Result
  132. static func success<Value>(_ matcher: Matcher<Value>? = nil) -> Matcher<Result<Value, Error>> {
  133. return .init { actual in
  134. switch actual {
  135. case let .success(value):
  136. return matcher?.evaluate(value) ?? .match
  137. case let .failure(error):
  138. return .noMatch(actual: "\(error)", expected: "success")
  139. }
  140. }
  141. }
  142. static func success() -> Matcher<Result<Void, Error>> {
  143. return .init { actual in
  144. switch actual {
  145. case .success:
  146. return .match
  147. case let .failure(error):
  148. return .noMatch(actual: "\(error)", expected: "success")
  149. }
  150. }
  151. }
  152. static func failure<Success, Failure: Error>(
  153. _ matcher: Matcher<Failure>? = nil
  154. ) -> Matcher<Result<Success, Failure>> {
  155. return .init { actual in
  156. switch actual {
  157. case let .success(value):
  158. return .noMatch(actual: "\(value)", expected: "failure")
  159. case let .failure(error):
  160. return matcher?.evaluate(error) ?? .match
  161. }
  162. }
  163. }
  164. // MARK: Utility
  165. static func all<Value>(_ matchers: Matcher<Value>...) -> Matcher<Value> {
  166. return .init { actual in
  167. for matcher in matchers {
  168. let result = matcher.evaluate(actual)
  169. switch result {
  170. case .noMatch:
  171. return result
  172. case .match:
  173. ()
  174. }
  175. }
  176. return .match
  177. }
  178. }
  179. // MARK: Type
  180. /// Checks that the actual value is an instance of the given type.
  181. static func instanceOf<Value, Expected>(_: Expected.Type) -> Matcher<Value> {
  182. return .init { actual in
  183. if actual is Expected {
  184. return .match
  185. } else {
  186. return .noMatch(
  187. actual: String(describing: type(of: actual)) + " (\(actual))",
  188. expected: "value of type \(Expected.self)"
  189. )
  190. }
  191. }
  192. }
  193. // MARK: Collection
  194. /// Checks whether the collection has the expected count.
  195. static func hasCount<C: Collection>(_ count: Int) -> Matcher<C> {
  196. return .init { actual in
  197. actual.count == count
  198. ? .match
  199. : .noMatch(actual: "has count \(actual.count)", expected: "count of \(count)")
  200. }
  201. }
  202. static func isEmpty<C: Collection>() -> Matcher<C> {
  203. return .init { actual in
  204. actual.isEmpty
  205. ? .match
  206. : .noMatch(actual: "has \(actual.count) items", expected: "is empty")
  207. }
  208. }
  209. // MARK: gRPC matchers
  210. static func hasCode(_ code: GRPCStatus.Code) -> Matcher<GRPCStatus> {
  211. return .init { actual in
  212. actual.code == code
  213. ? .match
  214. : .noMatch(actual: "has status code \(actual)", expected: "\(code)")
  215. }
  216. }
  217. static func metadata<Request>(
  218. _ matcher: Matcher<HPACKHeaders>? = nil
  219. ) -> Matcher<GRPCServerRequestPart<Request>> {
  220. return .init { actual in
  221. switch actual {
  222. case let .metadata(headers):
  223. return matcher?.evaluate(headers) ?? .match
  224. default:
  225. return .noMatch(actual: String(describing: actual), expected: "metadata")
  226. }
  227. }
  228. }
  229. static func message<Request>(
  230. _ matcher: Matcher<Request>? = nil
  231. ) -> Matcher<GRPCServerRequestPart<Request>> {
  232. return .init { actual in
  233. switch actual {
  234. case let .message(message):
  235. return matcher?.evaluate(message) ?? .match
  236. default:
  237. return .noMatch(actual: String(describing: actual), expected: "message")
  238. }
  239. }
  240. }
  241. static func metadata<Response>(
  242. _ matcher: Matcher<HPACKHeaders>? = nil
  243. ) -> Matcher<GRPCServerResponsePart<Response>> {
  244. return .init { actual in
  245. switch actual {
  246. case let .metadata(headers):
  247. return matcher?.evaluate(headers) ?? .match
  248. default:
  249. return .noMatch(actual: String(describing: actual), expected: "metadata")
  250. }
  251. }
  252. }
  253. static func message<Response>(
  254. _ matcher: Matcher<Response>? = nil
  255. ) -> Matcher<GRPCServerResponsePart<Response>> {
  256. return .init { actual in
  257. switch actual {
  258. case let .message(message, _):
  259. return matcher?.evaluate(message) ?? .match
  260. default:
  261. return .noMatch(actual: String(describing: actual), expected: "message")
  262. }
  263. }
  264. }
  265. static func end<Response>(
  266. status statusMatcher: Matcher<GRPCStatus>? = nil,
  267. trailers trailersMatcher: Matcher<HPACKHeaders>? = nil
  268. ) -> Matcher<GRPCServerResponsePart<Response>> {
  269. return .init { actual in
  270. switch actual {
  271. case let .end(status, trailers):
  272. let statusMatch = (statusMatcher?.evaluate(status) ?? .match)
  273. switch statusMatcher?.evaluate(status) ?? .match {
  274. case .match:
  275. return trailersMatcher?.evaluate(trailers) ?? .match
  276. case .noMatch:
  277. return statusMatch
  278. }
  279. default:
  280. return .noMatch(actual: String(describing: actual), expected: "end")
  281. }
  282. }
  283. }
  284. // MARK: HTTP/1
  285. static func head(
  286. status: HTTPResponseStatus,
  287. headers: HTTPHeaders? = nil
  288. ) -> Matcher<HTTPServerResponsePart> {
  289. return .init { actual in
  290. switch actual {
  291. case let .head(head):
  292. let statusMatches = Matcher.is(status).evaluate(head.status)
  293. switch statusMatches {
  294. case .match:
  295. return headers.map { Matcher.is($0).evaluate(head.headers) } ?? .match
  296. case .noMatch:
  297. return statusMatches
  298. }
  299. case .body, .end:
  300. return .noMatch(actual: "\(actual)", expected: "head")
  301. }
  302. }
  303. }
  304. static func body(_ matcher: Matcher<ByteBuffer>? = nil) -> Matcher<HTTPServerResponsePart> {
  305. return .init { actual in
  306. switch actual {
  307. case let .body(.byteBuffer(buffer)):
  308. return matcher.map { $0.evaluate(buffer) } ?? .match
  309. default:
  310. return .noMatch(actual: "\(actual)", expected: "body")
  311. }
  312. }
  313. }
  314. static func end() -> Matcher<HTTPServerResponsePart> {
  315. return .init { actual in
  316. switch actual {
  317. case .end:
  318. return .match
  319. default:
  320. return .noMatch(actual: "\(actual)", expected: "end")
  321. }
  322. }
  323. }
  324. // MARK: HTTP/2
  325. static func contains(
  326. _ name: String,
  327. _ values: [String]? = nil
  328. ) -> Matcher<HPACKHeaders> {
  329. return .init { actual in
  330. let headers = actual[canonicalForm: name]
  331. if headers.isEmpty {
  332. return .noMatch(actual: "does not contain '\(name)'", expected: "contains '\(name)'")
  333. } else {
  334. return values.map { Matcher.equalTo($0).evaluate(headers) } ?? .match
  335. }
  336. }
  337. }
  338. static func contains(
  339. caseSensitive caseSensitiveName: String
  340. ) -> Matcher<HPACKHeaders> {
  341. return .init { actual in
  342. for (name, _, _) in actual {
  343. if name == caseSensitiveName {
  344. return .match
  345. }
  346. }
  347. return .noMatch(
  348. actual: "does not contain '\(caseSensitiveName)'",
  349. expected: "contains '\(caseSensitiveName)'"
  350. )
  351. }
  352. }
  353. static func headers(
  354. _ headers: Matcher<HPACKHeaders>? = nil,
  355. endStream: Bool? = nil
  356. ) -> Matcher<HTTP2Frame.FramePayload> {
  357. return .init { actual in
  358. switch actual {
  359. case let .headers(payload):
  360. let headersMatch = headers?.evaluate(payload.headers)
  361. switch headersMatch {
  362. case .none,
  363. .some(.match):
  364. return endStream.map { Matcher.is($0).evaluate(payload.endStream) } ?? .match
  365. case .some(.noMatch):
  366. return headersMatch!
  367. }
  368. default:
  369. return .noMatch(actual: "\(actual)", expected: "headers")
  370. }
  371. }
  372. }
  373. static func data(
  374. buffer: ByteBuffer? = nil,
  375. endStream: Bool? = nil
  376. ) -> Matcher<HTTP2Frame.FramePayload> {
  377. return .init { actual in
  378. switch actual {
  379. case let .data(payload):
  380. let endStreamMatches = endStream.map { Matcher.is($0).evaluate(payload.endStream) }
  381. switch (endStreamMatches, payload.data) {
  382. case let (.none, .byteBuffer(b)),
  383. let (.some(.match), .byteBuffer(b)):
  384. return buffer.map { Matcher.is($0).evaluate(b) } ?? .match
  385. case (.some(.noMatch), .byteBuffer):
  386. return endStreamMatches!
  387. case (_, .fileRegion):
  388. preconditionFailure("Unexpected IOData.fileRegion")
  389. }
  390. default:
  391. return .noMatch(actual: "\(actual)", expected: "data")
  392. }
  393. }
  394. }
  395. static func trailersOnly(
  396. code: GRPCStatus.Code,
  397. contentType: String = "application/grpc"
  398. ) -> Matcher<HTTP2Frame.FramePayload> {
  399. return .headers(
  400. .all(
  401. .contains(":status", ["200"]),
  402. .contains("content-type", [contentType]),
  403. .contains("grpc-status", ["\(code.rawValue)"])
  404. ),
  405. endStream: true
  406. )
  407. }
  408. static func trailers(code: GRPCStatus.Code, message: String) -> Matcher<HTTP2Frame.FramePayload> {
  409. return .headers(
  410. .all(
  411. .contains("grpc-status", ["\(code.rawValue)"]),
  412. .contains("grpc-message", [message])
  413. ),
  414. endStream: true
  415. )
  416. }
  417. // MARK: HTTP2ToRawGRPCStateMachine.Action
  418. static func errorCaught() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  419. return .init { actual in
  420. switch actual {
  421. case .errorCaught:
  422. return .match
  423. default:
  424. return .noMatch(actual: "\(actual)", expected: "errorCaught")
  425. }
  426. }
  427. }
  428. static func none() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  429. return .init { actual in
  430. switch actual {
  431. case .none:
  432. return .match
  433. default:
  434. return .noMatch(actual: "\(actual)", expected: "none")
  435. }
  436. }
  437. }
  438. static func configure() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  439. return .init { actual in
  440. switch actual {
  441. case .configure:
  442. return .match
  443. default:
  444. return .noMatch(actual: "\(actual)", expected: "configure")
  445. }
  446. }
  447. }
  448. static func completePromise(
  449. with matcher: Matcher<Result<Void, Error>>? = nil
  450. ) -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  451. return .init { actual in
  452. switch actual {
  453. case let .completePromise(_, result):
  454. return matcher?.evaluate(result) ?? .match
  455. default:
  456. return .noMatch(actual: "\(actual)", expected: "completePromise")
  457. }
  458. }
  459. }
  460. static func forwardHeaders() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  461. return .init { actual in
  462. switch actual {
  463. case .forwardHeaders:
  464. return .match
  465. default:
  466. return .noMatch(actual: "\(actual)", expected: "forwardHeaders")
  467. }
  468. }
  469. }
  470. static func forwardMessage() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  471. return .init { actual in
  472. switch actual {
  473. case .forwardMessage:
  474. return .match
  475. default:
  476. return .noMatch(actual: "\(actual)", expected: "forwardMessage")
  477. }
  478. }
  479. }
  480. static func forwardEnd() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  481. return .init { actual in
  482. switch actual {
  483. case .forwardEnd:
  484. return .match
  485. default:
  486. return .noMatch(actual: "\(actual)", expected: "forwardEnd")
  487. }
  488. }
  489. }
  490. static func forwardMessageAndEnd() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  491. return .init { actual in
  492. switch actual {
  493. case .forwardMessageAndEnd:
  494. return .match
  495. default:
  496. return .noMatch(actual: "\(actual)", expected: "forwardMessageAndEnd")
  497. }
  498. }
  499. }
  500. static func forwardHeadersThenRead() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  501. return .init { actual in
  502. switch actual {
  503. case .forwardHeadersThenReadNextMessage:
  504. return .match
  505. default:
  506. return .noMatch(actual: "\(actual)", expected: "forwardHeadersThenReadNextMessage")
  507. }
  508. }
  509. }
  510. static func forwardMessageThenRead() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  511. return .init { actual in
  512. switch actual {
  513. case .forwardMessageThenReadNextMessage:
  514. return .match
  515. default:
  516. return .noMatch(actual: "\(actual)", expected: "forwardMessageThenReadNextMessage")
  517. }
  518. }
  519. }
  520. static func readNextRequest() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  521. return .init { actual in
  522. switch actual {
  523. case .readNextRequest:
  524. return .match
  525. default:
  526. return .noMatch(actual: "\(actual)", expected: "readNextRequest")
  527. }
  528. }
  529. }
  530. static func write(
  531. _ matcher: Matcher<HTTP2Frame.FramePayload>? = nil,
  532. flush: Bool = false
  533. ) -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  534. return .init { actual in
  535. switch actual {
  536. case let .write(payload, _, insertFlush):
  537. if flush == insertFlush {
  538. return matcher.map { $0.evaluate(payload) } ?? .match
  539. } else {
  540. return .noMatch(actual: "write(_,_,\(insertFlush))", expected: "write(_,_,\(flush))")
  541. }
  542. default:
  543. return .noMatch(actual: "\(actual)", expected: "write")
  544. }
  545. }
  546. }
  547. }
  548. struct ExpressionMatcher<Value> {
  549. typealias Expression = () throws -> Value
  550. private typealias Evaluator = (Expression) -> MatchResult
  551. private var evaluator: Evaluator
  552. private init(_ evaluator: @escaping Evaluator) {
  553. self.evaluator = evaluator
  554. }
  555. fileprivate func evaluate(_ expression: Expression) -> MatchResult {
  556. return self.evaluator(expression)
  557. }
  558. /// Asserts that the expression does not throw and error. Returns the result of any provided
  559. /// matcher on the result of the expression.
  560. static func doesNotThrow<Value>(_ matcher: Matcher<Value>? = nil) -> ExpressionMatcher<Value> {
  561. return .init { expression in
  562. do {
  563. let value = try expression()
  564. return matcher?.evaluate(value) ?? .match
  565. } catch {
  566. return .noMatch(actual: "threw '\(error)'", expected: "should not throw error")
  567. }
  568. }
  569. }
  570. /// Asserts that the expression throws and error. Returns the result of any provided matcher
  571. /// on the error thrown by the expression.
  572. static func `throws`<Value>(_ matcher: Matcher<Error>? = nil) -> ExpressionMatcher<Value> {
  573. return .init { expression in
  574. do {
  575. let value = try expression()
  576. return .noMatch(actual: "returned '\(value)'", expected: "should throw error")
  577. } catch {
  578. return matcher?.evaluate(error) ?? .match
  579. }
  580. }
  581. }
  582. }