ServerHandlerStateMachineTests.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * Copyright 2022, 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 NIOCore
  17. import NIOEmbedded
  18. import NIOHPACK
  19. import XCTest
  20. @testable import GRPC
  21. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  22. internal final class ServerHandlerStateMachineTests: GRPCTestCase {
  23. private enum InitialState {
  24. case idle
  25. case handling
  26. case draining
  27. case finished
  28. }
  29. private func makeStateMachine(inState state: InitialState = .idle) -> ServerHandlerStateMachine {
  30. var stateMachine = ServerHandlerStateMachine()
  31. switch state {
  32. case .idle:
  33. return stateMachine
  34. case .handling:
  35. stateMachine.handleMetadata().assertInvokeHandler()
  36. stateMachine.handlerInvoked(requestHeaders: [:])
  37. return stateMachine
  38. case .draining:
  39. stateMachine.handleMetadata().assertInvokeHandler()
  40. stateMachine.handlerInvoked(requestHeaders: [:])
  41. stateMachine.handleEnd().assertForward()
  42. return stateMachine
  43. case .finished:
  44. stateMachine.cancel().assertNone()
  45. return stateMachine
  46. }
  47. }
  48. private func makeCallHandlerContext() -> CallHandlerContext {
  49. let loop = EmbeddedEventLoop()
  50. defer {
  51. try! loop.syncShutdownGracefully()
  52. }
  53. return CallHandlerContext(
  54. logger: self.logger,
  55. encoding: .disabled,
  56. eventLoop: loop,
  57. path: "",
  58. responseWriter: NoOpResponseWriter(),
  59. allocator: ByteBufferAllocator(),
  60. closeFuture: loop.makeSucceededVoidFuture()
  61. )
  62. }
  63. // MARK: - Test Cases
  64. func testHandleMetadataWhenIdle() {
  65. var stateMachine = self.makeStateMachine()
  66. // Receiving metadata is the signal to invoke the user handler.
  67. stateMachine.handleMetadata().assertInvokeHandler()
  68. // On invoking the handler we move to the next state. No output.
  69. stateMachine.handlerInvoked(requestHeaders: [:])
  70. }
  71. func testHandleMetadataWhenHandling() {
  72. var stateMachine = self.makeStateMachine(inState: .handling)
  73. // Must not receive metadata more than once.
  74. stateMachine.handleMetadata().assertInvokeCancel()
  75. }
  76. func testHandleMetadataWhenDraining() {
  77. var stateMachine = self.makeStateMachine(inState: .draining)
  78. // We can't receive metadata more than once.
  79. stateMachine.handleMetadata().assertInvokeCancel()
  80. }
  81. func testHandleMetadataWhenFinished() {
  82. var stateMachine = self.makeStateMachine(inState: .finished)
  83. // We can't receive anything when finished.
  84. stateMachine.handleMetadata().assertInvokeCancel()
  85. }
  86. func testHandleMessageWhenIdle() {
  87. var stateMachine = self.makeStateMachine()
  88. // Metadata must be received first.
  89. stateMachine.handleMessage().assertCancel()
  90. }
  91. func testHandleMessageWhenHandling() {
  92. var stateMachine = self.makeStateMachine(inState: .handling)
  93. // Messages are good, we can forward those while handling.
  94. for _ in 0 ..< 10 {
  95. stateMachine.handleMessage().assertForward()
  96. }
  97. }
  98. func testHandleMessageWhenDraining() {
  99. var stateMachine = self.makeStateMachine(inState: .draining)
  100. // We entered the 'draining' state as we received 'end', another message is a protocol
  101. // violation so cancel.
  102. stateMachine.handleMessage().assertCancel()
  103. }
  104. func testHandleMessageWhenFinished() {
  105. var stateMachine = self.makeStateMachine(inState: .finished)
  106. // We can't receive anything when finished.
  107. stateMachine.handleMessage().assertCancel()
  108. }
  109. func testHandleEndWhenIdle() {
  110. var stateMachine = self.makeStateMachine()
  111. // Metadata must be received first.
  112. stateMachine.handleEnd().assertCancel()
  113. }
  114. func testHandleEndWhenHandling() {
  115. var stateMachine = self.makeStateMachine(inState: .handling)
  116. // End is good; it transitions us to the draining state.
  117. stateMachine.handleEnd().assertForward()
  118. }
  119. func testHandleEndWhenDraining() {
  120. var stateMachine = self.makeStateMachine(inState: .draining)
  121. // We entered the 'draining' state as we received 'end', another 'end' is a protocol
  122. // violation so cancel.
  123. stateMachine.handleEnd().assertCancel()
  124. }
  125. func testHandleEndWhenFinished() {
  126. var stateMachine = self.makeStateMachine(inState: .finished)
  127. // We can't receive anything when finished.
  128. stateMachine.handleEnd().assertCancel()
  129. }
  130. func testSendMessageWhenHandling() {
  131. var stateMachine = self.makeStateMachine(inState: .handling)
  132. // The first message should prompt headers to be sent as well.
  133. stateMachine.sendMessage().assertInterceptHeadersThenMessage()
  134. // Additional messages should be just the message.
  135. stateMachine.sendMessage().assertInterceptMessage()
  136. }
  137. func testSendMessageWhenDraining() {
  138. var stateMachine = self.makeStateMachine(inState: .draining)
  139. // The first message should prompt headers to be sent as well.
  140. stateMachine.sendMessage().assertInterceptHeadersThenMessage()
  141. // Additional messages should be just the message.
  142. stateMachine.sendMessage().assertInterceptMessage()
  143. }
  144. func testSendMessageWhenFinished() {
  145. var stateMachine = self.makeStateMachine(inState: .finished)
  146. // We can't send anything if we're finished.
  147. stateMachine.sendMessage().assertDrop()
  148. }
  149. func testSendStatusWhenHandling() {
  150. var stateMachine = self.makeStateMachine(inState: .handling)
  151. // This moves the state machine to the 'finished' state.
  152. stateMachine.sendStatus().assertIntercept()
  153. }
  154. func testSendStatusWhenDraining() {
  155. var stateMachine = self.makeStateMachine(inState: .draining)
  156. // This moves the state machine to the 'finished' state.
  157. stateMachine.sendStatus().assertIntercept()
  158. }
  159. func testSendStatusWhenFinished() {
  160. var stateMachine = self.makeStateMachine(inState: .finished)
  161. // We can't send anything if we're finished.
  162. stateMachine.sendStatus().assertDrop()
  163. }
  164. func testCancelWhenIdle() {
  165. var stateMachine = self.makeStateMachine()
  166. // Cancelling when idle is effectively a no-op; there's nothing to cancel.
  167. stateMachine.cancel().assertNone()
  168. }
  169. func testCancelWhenHandling() {
  170. var stateMachine = self.makeStateMachine(inState: .handling)
  171. // We have things to cancel in this state.
  172. stateMachine.cancel().assertDoCancel()
  173. }
  174. func testCancelWhenDraining() {
  175. var stateMachine = self.makeStateMachine(inState: .draining)
  176. // We have things to cancel in this state.
  177. stateMachine.cancel().assertDoCancel()
  178. }
  179. func testCancelWhenFinished() {
  180. var stateMachine = self.makeStateMachine(inState: .finished)
  181. stateMachine.cancel().assertDoCancel()
  182. }
  183. func testSetResponseHeadersWhenHandling() {
  184. var stateMachine = self.makeStateMachine(inState: .handling)
  185. XCTAssertTrue(stateMachine.setResponseHeaders(["foo": "bar"]))
  186. stateMachine.sendMessage().assertInterceptHeadersThenMessage { headers in
  187. XCTAssertEqual(headers, ["foo": "bar"])
  188. }
  189. }
  190. func testSetResponseHeadersWhenHandlingAreMovedToDraining() {
  191. var stateMachine = self.makeStateMachine(inState: .handling)
  192. XCTAssertTrue(stateMachine.setResponseHeaders(["foo": "bar"]))
  193. stateMachine.handleEnd().assertForward()
  194. stateMachine.sendMessage().assertInterceptHeadersThenMessage { headers in
  195. XCTAssertEqual(headers, ["foo": "bar"])
  196. }
  197. }
  198. func testSetResponseHeadersWhenDraining() {
  199. var stateMachine = self.makeStateMachine(inState: .draining)
  200. XCTAssertTrue(stateMachine.setResponseHeaders(["foo": "bar"]))
  201. stateMachine.sendMessage().assertInterceptHeadersThenMessage { headers in
  202. XCTAssertEqual(headers, ["foo": "bar"])
  203. }
  204. }
  205. func testSetResponseHeadersWhenFinished() {
  206. var stateMachine = self.makeStateMachine(inState: .finished)
  207. XCTAssertFalse(stateMachine.setResponseHeaders(["foo": "bar"]))
  208. }
  209. func testSetResponseTrailersWhenHandling() {
  210. var stateMachine = self.makeStateMachine(inState: .handling)
  211. stateMachine.setResponseTrailers(["foo": "bar"])
  212. stateMachine.sendStatus().assertIntercept { trailers in
  213. XCTAssertEqual(trailers, ["foo": "bar"])
  214. }
  215. }
  216. func testSetResponseTrailersWhenDraining() {
  217. var stateMachine = self.makeStateMachine(inState: .draining)
  218. stateMachine.setResponseTrailers(["foo": "bar"])
  219. stateMachine.sendStatus().assertIntercept { trailers in
  220. XCTAssertEqual(trailers, ["foo": "bar"])
  221. }
  222. }
  223. func testSetResponseTrailersWhenFinished() {
  224. var stateMachine = self.makeStateMachine(inState: .finished)
  225. stateMachine.setResponseTrailers(["foo": "bar"])
  226. // Nothing we can assert on, only that we don't crash.
  227. }
  228. }
  229. // MARK: - Action Assertions
  230. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  231. extension ServerHandlerStateMachine.HandleMetadataAction {
  232. func assertInvokeHandler() {
  233. XCTAssertEqual(self, .invokeHandler)
  234. }
  235. func assertInvokeCancel() {
  236. XCTAssertEqual(self, .cancel)
  237. }
  238. }
  239. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  240. extension ServerHandlerStateMachine.HandleMessageAction {
  241. func assertForward() {
  242. XCTAssertEqual(self, .forward)
  243. }
  244. func assertCancel() {
  245. XCTAssertEqual(self, .cancel)
  246. }
  247. }
  248. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  249. extension ServerHandlerStateMachine.SendMessageAction {
  250. func assertInterceptHeadersThenMessage(_ verify: (HPACKHeaders) -> Void = { _ in }) {
  251. switch self {
  252. case let .intercept(headers: .some(headers)):
  253. verify(headers)
  254. default:
  255. XCTFail("Expected .intercept(.some) but got \(self)")
  256. }
  257. }
  258. func assertInterceptMessage() {
  259. XCTAssertEqual(self, .intercept(headers: nil))
  260. }
  261. func assertDrop() {
  262. XCTAssertEqual(self, .drop)
  263. }
  264. }
  265. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  266. extension ServerHandlerStateMachine.SendStatusAction {
  267. func assertIntercept(_ verify: (HPACKHeaders) -> Void = { _ in }) {
  268. switch self {
  269. case let .intercept(_, trailers: trailers):
  270. verify(trailers)
  271. case .drop:
  272. XCTFail("Expected .intercept but got .drop")
  273. }
  274. }
  275. func assertDrop() {
  276. XCTAssertEqual(self, .drop)
  277. }
  278. }
  279. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  280. extension ServerHandlerStateMachine.CancelAction {
  281. func assertNone() {
  282. XCTAssertEqual(self, .none)
  283. }
  284. func assertDoCancel() {
  285. XCTAssertEqual(self, .cancelAndNilOutHandlerComponents)
  286. }
  287. }