XCTestHelpers.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  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. static func headers<Response>(
  285. _ matcher: Matcher<HPACKHeaders>? = nil
  286. ) -> Matcher<_GRPCServerResponsePart<Response>> {
  287. return .init { actual in
  288. switch actual {
  289. case let .headers(headers):
  290. return matcher?.evaluate(headers) ?? .match
  291. default:
  292. return .noMatch(actual: String(describing: actual), expected: "headers")
  293. }
  294. }
  295. }
  296. static func message<Response>(
  297. _ matcher: Matcher<Response>? = nil
  298. ) -> Matcher<_GRPCServerResponsePart<Response>> {
  299. return .init { actual in
  300. switch actual {
  301. case let .message(message):
  302. return matcher?.evaluate(message.message) ?? .match
  303. default:
  304. return .noMatch(actual: String(describing: actual), expected: "message")
  305. }
  306. }
  307. }
  308. static func end<Response>(
  309. _ matcher: Matcher<GRPCStatus>? = nil
  310. ) -> Matcher<_GRPCServerResponsePart<Response>> {
  311. return .init { actual in
  312. switch actual {
  313. case let .statusAndTrailers(status, _):
  314. return matcher?.evaluate(status) ?? .match
  315. default:
  316. return .noMatch(actual: String(describing: actual), expected: "end")
  317. }
  318. }
  319. }
  320. // MARK: HTTP/1
  321. static func head(
  322. status: HTTPResponseStatus,
  323. headers: HTTPHeaders? = nil
  324. ) -> Matcher<HTTPServerResponsePart> {
  325. return .init { actual in
  326. switch actual {
  327. case let .head(head):
  328. let statusMatches = Matcher.is(status).evaluate(head.status)
  329. switch statusMatches {
  330. case .match:
  331. return headers.map { Matcher.is($0).evaluate(head.headers) } ?? .match
  332. case .noMatch:
  333. return statusMatches
  334. }
  335. case .body, .end:
  336. return .noMatch(actual: "\(actual)", expected: "head")
  337. }
  338. }
  339. }
  340. static func body(_ matcher: Matcher<ByteBuffer>? = nil) -> Matcher<HTTPServerResponsePart> {
  341. return .init { actual in
  342. switch actual {
  343. case let .body(.byteBuffer(buffer)):
  344. return matcher.map { $0.evaluate(buffer) } ?? .match
  345. default:
  346. return .noMatch(actual: "\(actual)", expected: "body")
  347. }
  348. }
  349. }
  350. static func end() -> Matcher<HTTPServerResponsePart> {
  351. return .init { actual in
  352. switch actual {
  353. case .end:
  354. return .match
  355. default:
  356. return .noMatch(actual: "\(actual)", expected: "end")
  357. }
  358. }
  359. }
  360. // MARK: HTTP/2
  361. static func contains(
  362. _ name: String,
  363. _ values: [String]? = nil
  364. ) -> Matcher<HPACKHeaders> {
  365. return .init { actual in
  366. let headers = actual[canonicalForm: name]
  367. if headers.isEmpty {
  368. return .noMatch(actual: "does not contain '\(name)'", expected: "contains '\(name)'")
  369. } else {
  370. return values.map { Matcher.equalTo($0).evaluate(headers) } ?? .match
  371. }
  372. }
  373. }
  374. static func contains(
  375. caseSensitive caseSensitiveName: String
  376. ) -> Matcher<HPACKHeaders> {
  377. return .init { actual in
  378. for (name, _, _) in actual {
  379. if name == caseSensitiveName {
  380. return .match
  381. }
  382. }
  383. return .noMatch(
  384. actual: "does not contain '\(caseSensitiveName)'",
  385. expected: "contains '\(caseSensitiveName)'"
  386. )
  387. }
  388. }
  389. static func headers(
  390. _ headers: Matcher<HPACKHeaders>? = nil,
  391. endStream: Bool? = nil
  392. ) -> Matcher<HTTP2Frame.FramePayload> {
  393. return .init { actual in
  394. switch actual {
  395. case let .headers(payload):
  396. let headersMatch = headers?.evaluate(payload.headers)
  397. switch headersMatch {
  398. case .none,
  399. .some(.match):
  400. return endStream.map { Matcher.is($0).evaluate(payload.endStream) } ?? .match
  401. case .some(.noMatch):
  402. return headersMatch!
  403. }
  404. default:
  405. return .noMatch(actual: "\(actual)", expected: "headers")
  406. }
  407. }
  408. }
  409. static func data(
  410. buffer: ByteBuffer? = nil,
  411. endStream: Bool? = nil
  412. ) -> Matcher<HTTP2Frame.FramePayload> {
  413. return .init { actual in
  414. switch actual {
  415. case let .data(payload):
  416. let endStreamMatches = endStream.map { Matcher.is($0).evaluate(payload.endStream) }
  417. switch (endStreamMatches, payload.data) {
  418. case let (.none, .byteBuffer(b)),
  419. let (.some(.match), .byteBuffer(b)):
  420. return buffer.map { Matcher.is($0).evaluate(b) } ?? .match
  421. case (.some(.noMatch), .byteBuffer):
  422. return endStreamMatches!
  423. case (_, .fileRegion):
  424. preconditionFailure("Unexpected IOData.fileRegion")
  425. }
  426. default:
  427. return .noMatch(actual: "\(actual)", expected: "data")
  428. }
  429. }
  430. }
  431. static func trailersOnly(
  432. code: GRPCStatus.Code,
  433. contentType: String = "application/grpc"
  434. ) -> Matcher<HTTP2Frame.FramePayload> {
  435. return .headers(
  436. .all(
  437. .contains(":status", ["200"]),
  438. .contains("content-type", [contentType]),
  439. .contains("grpc-status", ["\(code.rawValue)"])
  440. ),
  441. endStream: true
  442. )
  443. }
  444. static func trailers(code: GRPCStatus.Code, message: String) -> Matcher<HTTP2Frame.FramePayload> {
  445. return .headers(
  446. .all(
  447. .contains("grpc-status", ["\(code.rawValue)"]),
  448. .contains("grpc-message", [message])
  449. ),
  450. endStream: true
  451. )
  452. }
  453. // MARK: HTTP2ToRawGRPCStateMachine.Action
  454. static func errorCaught() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  455. return .init { actual in
  456. switch actual {
  457. case .errorCaught:
  458. return .match
  459. default:
  460. return .noMatch(actual: "\(actual)", expected: "errorCaught")
  461. }
  462. }
  463. }
  464. static func none() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  465. return .init { actual in
  466. switch actual {
  467. case .none:
  468. return .match
  469. default:
  470. return .noMatch(actual: "\(actual)", expected: "none")
  471. }
  472. }
  473. }
  474. static func configure() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  475. return .init { actual in
  476. switch actual {
  477. case .configure:
  478. return .match
  479. default:
  480. return .noMatch(actual: "\(actual)", expected: "configure")
  481. }
  482. }
  483. }
  484. static func completePromise(
  485. with matcher: Matcher<Result<Void, Error>>? = nil
  486. ) -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  487. return .init { actual in
  488. switch actual {
  489. case let .completePromise(_, result):
  490. return matcher?.evaluate(result) ?? .match
  491. default:
  492. return .noMatch(actual: "\(actual)", expected: "completePromise")
  493. }
  494. }
  495. }
  496. static func forwardHeaders() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  497. return .init { actual in
  498. switch actual {
  499. case .forwardHeaders:
  500. return .match
  501. default:
  502. return .noMatch(actual: "\(actual)", expected: "forwardHeaders")
  503. }
  504. }
  505. }
  506. static func forwardMessage() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  507. return .init { actual in
  508. switch actual {
  509. case .forwardMessage:
  510. return .match
  511. default:
  512. return .noMatch(actual: "\(actual)", expected: "forwardMessage")
  513. }
  514. }
  515. }
  516. static func forwardEnd() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  517. return .init { actual in
  518. switch actual {
  519. case .forwardEnd:
  520. return .match
  521. default:
  522. return .noMatch(actual: "\(actual)", expected: "forwardEnd")
  523. }
  524. }
  525. }
  526. static func forwardMessageAndEnd() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  527. return .init { actual in
  528. switch actual {
  529. case .forwardMessageAndEnd:
  530. return .match
  531. default:
  532. return .noMatch(actual: "\(actual)", expected: "forwardMessageAndEnd")
  533. }
  534. }
  535. }
  536. static func forwardHeadersThenRead() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  537. return .init { actual in
  538. switch actual {
  539. case .forwardHeadersThenReadNextMessage:
  540. return .match
  541. default:
  542. return .noMatch(actual: "\(actual)", expected: "forwardHeadersThenReadNextMessage")
  543. }
  544. }
  545. }
  546. static func forwardMessageThenRead() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  547. return .init { actual in
  548. switch actual {
  549. case .forwardMessageThenReadNextMessage:
  550. return .match
  551. default:
  552. return .noMatch(actual: "\(actual)", expected: "forwardMessageThenReadNextMessage")
  553. }
  554. }
  555. }
  556. static func readNextRequest() -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  557. return .init { actual in
  558. switch actual {
  559. case .readNextRequest:
  560. return .match
  561. default:
  562. return .noMatch(actual: "\(actual)", expected: "readNextRequest")
  563. }
  564. }
  565. }
  566. static func write(
  567. _ matcher: Matcher<HTTP2Frame.FramePayload>? = nil,
  568. flush: Bool = false
  569. ) -> Matcher<HTTP2ToRawGRPCStateMachine.Action> {
  570. return .init { actual in
  571. switch actual {
  572. case let .write(payload, _, insertFlush):
  573. if flush == insertFlush {
  574. return matcher.map { $0.evaluate(payload) } ?? .match
  575. } else {
  576. return .noMatch(actual: "write(_,_,\(insertFlush))", expected: "write(_,_,\(flush))")
  577. }
  578. default:
  579. return .noMatch(actual: "\(actual)", expected: "write")
  580. }
  581. }
  582. }
  583. }
  584. struct ExpressionMatcher<Value> {
  585. typealias Expression = () throws -> Value
  586. private typealias Evaluator = (Expression) -> MatchResult
  587. private var evaluator: Evaluator
  588. private init(_ evaluator: @escaping Evaluator) {
  589. self.evaluator = evaluator
  590. }
  591. fileprivate func evaluate(_ expression: Expression) -> MatchResult {
  592. return self.evaluator(expression)
  593. }
  594. /// Asserts that the expression does not throw and error. Returns the result of any provided
  595. /// matcher on the result of the expression.
  596. static func doesNotThrow<Value>(_ matcher: Matcher<Value>? = nil) -> ExpressionMatcher<Value> {
  597. return .init { expression in
  598. do {
  599. let value = try expression()
  600. return matcher?.evaluate(value) ?? .match
  601. } catch {
  602. return .noMatch(actual: "threw '\(error)'", expected: "should not throw error")
  603. }
  604. }
  605. }
  606. /// Asserts that the expression throws and error. Returns the result of any provided matcher
  607. /// on the error thrown by the expression.
  608. static func `throws`<Value>(_ matcher: Matcher<Error>? = nil) -> ExpressionMatcher<Value> {
  609. return .init { expression in
  610. do {
  611. let value = try expression()
  612. return .noMatch(actual: "returned '\(value)'", expected: "should throw error")
  613. } catch {
  614. return matcher?.evaluate(error) ?? .match
  615. }
  616. }
  617. }
  618. }