XCTestHelpers.swift 17 KB

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