FakeChannel.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. import NIO
  17. import SwiftProtobuf
  18. import Logging
  19. /// A fake channel for use with generated test clients.
  20. ///
  21. /// The `FakeChannel` provides factories for calls which avoid most of the gRPC stack and don't do
  22. /// real networking. Each call relies on either a `FakeUnaryResponse` or a `FakeStreamingResponse`
  23. /// to get responses or errors. The fake response of each type should be registered with the channel
  24. /// prior to making a call via `makeFakeUnaryResponse` or `makeFakeStreamingResponse` respectively.
  25. ///
  26. /// Users will typically not be required to interact with the channel directly, instead they should
  27. /// do so via a generated test client.
  28. public class FakeChannel: GRPCChannel {
  29. /// Fake response streams keyed by their path.
  30. private var responseStreams: [String: CircularBuffer<Any>]
  31. /// A logger.
  32. public let logger: Logger
  33. public init(logger: Logger = Logger(label: "io.grpc", factory: { _ in SwiftLogNoOpLogHandler() })) {
  34. self.responseStreams = [:]
  35. self.logger = logger
  36. }
  37. /// Make and store a fake unary response for the given path. Users should prefer making a response
  38. /// stream for their RPC directly via the appropriate method on their generated test client.
  39. public func makeFakeUnaryResponse<Request, Response>(
  40. path: String,
  41. requestHandler: @escaping (FakeRequestPart<Request>) -> ()
  42. ) -> FakeUnaryResponse<Request, Response> {
  43. let proxy = FakeUnaryResponse<Request, Response>(requestHandler: requestHandler)
  44. self.responseStreams[path, default: []].append(proxy)
  45. return proxy
  46. }
  47. /// Make and store a fake streaming response for the given path. Users should prefer making a
  48. /// response stream for their RPC directly via the appropriate method on their generated test
  49. /// client.
  50. public func makeFakeStreamingResponse<Request, Response>(
  51. path: String,
  52. requestHandler: @escaping (FakeRequestPart<Request>) -> ()
  53. ) -> FakeStreamingResponse<Request, Response> {
  54. let proxy = FakeStreamingResponse<Request, Response>(requestHandler: requestHandler)
  55. self.responseStreams[path, default: []].append(proxy)
  56. return proxy
  57. }
  58. /// Returns true if there are fake responses enqueued for the given path.
  59. public func hasFakeResponseEnqueued(forPath path: String) -> Bool {
  60. guard let noStreamsForPath = self.responseStreams[path]?.isEmpty else {
  61. return false
  62. }
  63. return !noStreamsForPath
  64. }
  65. // (Docs inherited from `GRPCChannel`)
  66. public func makeUnaryCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
  67. path: String,
  68. request: Request,
  69. callOptions: CallOptions
  70. ) -> UnaryCall<Request, Response> {
  71. return self.makeUnaryCall(
  72. serializer: ProtobufSerializer(),
  73. deserializer: ProtobufDeserializer(),
  74. path: path,
  75. request: request,
  76. callOptions: callOptions
  77. )
  78. }
  79. // (Docs inherited from `GRPCChannel`)
  80. public func makeUnaryCall<Request: GRPCPayload, Response: GRPCPayload>(
  81. path: String,
  82. request: Request,
  83. callOptions: CallOptions
  84. ) -> UnaryCall<Request, Response> {
  85. return self.makeUnaryCall(
  86. serializer: GRPCPayloadSerializer(),
  87. deserializer: GRPCPayloadDeserializer(),
  88. path: path,
  89. request: request,
  90. callOptions: callOptions
  91. )
  92. }
  93. // (Docs inherited from `GRPCChannel`)
  94. public func makeServerStreamingCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
  95. path: String,
  96. request: Request,
  97. callOptions: CallOptions,
  98. handler: @escaping (Response) -> Void
  99. ) -> ServerStreamingCall<Request, Response> {
  100. return self.makeServerStreamingCall(
  101. serializer: ProtobufSerializer(),
  102. deserializer: ProtobufDeserializer(),
  103. path: path,
  104. request: request,
  105. callOptions: callOptions,
  106. handler: handler
  107. )
  108. }
  109. // (Docs inherited from `GRPCChannel`)
  110. public func makeServerStreamingCall<Request: GRPCPayload, Response: GRPCPayload>(
  111. path: String,
  112. request: Request,
  113. callOptions: CallOptions,
  114. handler: @escaping (Response) -> Void
  115. ) -> ServerStreamingCall<Request, Response> {
  116. return self.makeServerStreamingCall(
  117. serializer: GRPCPayloadSerializer(),
  118. deserializer: GRPCPayloadDeserializer(),
  119. path: path,
  120. request: request,
  121. callOptions: callOptions,
  122. handler: handler
  123. )
  124. }
  125. // (Docs inherited from `GRPCChannel`)
  126. public func makeClientStreamingCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
  127. path: String,
  128. callOptions: CallOptions
  129. ) -> ClientStreamingCall<Request, Response> {
  130. return self.makeClientStreamingCall(
  131. serializer: ProtobufSerializer(),
  132. deserializer: ProtobufDeserializer(),
  133. path: path,
  134. callOptions: callOptions
  135. )
  136. }
  137. // (Docs inherited from `GRPCChannel`)
  138. public func makeClientStreamingCall<Request: GRPCPayload, Response: GRPCPayload>(
  139. path: String,
  140. callOptions: CallOptions
  141. ) -> ClientStreamingCall<Request, Response> {
  142. return self.makeClientStreamingCall(
  143. serializer: GRPCPayloadSerializer(),
  144. deserializer: GRPCPayloadDeserializer(),
  145. path: path,
  146. callOptions: callOptions
  147. )
  148. }
  149. // (Docs inherited from `GRPCChannel`)
  150. public func makeBidirectionalStreamingCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
  151. path: String,
  152. callOptions: CallOptions,
  153. handler: @escaping (Response) -> Void
  154. ) -> BidirectionalStreamingCall<Request, Response> {
  155. return self.makeBidirectionalStreamingCall(
  156. serializer: ProtobufSerializer(),
  157. deserializer: ProtobufDeserializer(),
  158. path: path,
  159. callOptions: callOptions,
  160. handler: handler
  161. )
  162. }
  163. // (Docs inherited from `GRPCChannel`)
  164. public func makeBidirectionalStreamingCall<Request: GRPCPayload, Response: GRPCPayload>(
  165. path: String,
  166. callOptions: CallOptions,
  167. handler: @escaping (Response) -> Void
  168. ) -> BidirectionalStreamingCall<Request, Response> {
  169. return self.makeBidirectionalStreamingCall(
  170. serializer: GRPCPayloadSerializer(),
  171. deserializer: GRPCPayloadDeserializer(),
  172. path: path,
  173. callOptions: callOptions,
  174. handler: handler
  175. )
  176. }
  177. public func close() -> EventLoopFuture<Void> {
  178. // We don't have anything to close.
  179. return EmbeddedEventLoop().makeSucceededFuture(())
  180. }
  181. }
  182. extension FakeChannel {
  183. /// Dequeue a proxy for the given path and casts it to the given type, if one exists.
  184. private func dequeueResponseStream<Stream>(
  185. forPath path: String,
  186. as: Stream.Type = Stream.self
  187. ) -> Stream? {
  188. guard var streams = self.responseStreams[path], !streams.isEmpty else {
  189. return nil
  190. }
  191. // This is fine: we know we're non-empty.
  192. let first = streams.removeFirst()
  193. self.responseStreams.updateValue(streams, forKey: path)
  194. return first as? Stream
  195. }
  196. private func makeRequestHead(path: String, callOptions: CallOptions) -> _GRPCRequestHead {
  197. return _GRPCRequestHead(
  198. scheme: "http",
  199. path: path,
  200. host: "localhost",
  201. options: callOptions,
  202. requestID: nil
  203. )
  204. }
  205. }
  206. extension FakeChannel {
  207. private func makeUnaryCall<Serializer: MessageSerializer, Deserializer: MessageDeserializer>(
  208. serializer: Serializer,
  209. deserializer: Deserializer,
  210. path: String,
  211. request: Serializer.Input,
  212. callOptions: CallOptions
  213. ) -> UnaryCall<Serializer.Input, Deserializer.Output> {
  214. let call = UnaryCall<Serializer.Input, Deserializer.Output>.make(
  215. serializer: serializer,
  216. deserializer: deserializer,
  217. fakeResponse: self.dequeueResponseStream(forPath: path),
  218. callOptions: callOptions,
  219. logger: self.logger
  220. )
  221. call.send(self.makeRequestHead(path: path, callOptions: callOptions), request: request)
  222. return call
  223. }
  224. private func makeClientStreamingCall<Serializer: MessageSerializer, Deserializer: MessageDeserializer>(
  225. serializer: Serializer,
  226. deserializer: Deserializer,
  227. path: String,
  228. callOptions: CallOptions
  229. ) -> ClientStreamingCall<Serializer.Input, Deserializer.Output> {
  230. let call = ClientStreamingCall<Serializer.Input, Deserializer.Output>.make(
  231. serializer: serializer,
  232. deserializer: deserializer,
  233. fakeResponse: self.dequeueResponseStream(forPath: path),
  234. callOptions: callOptions,
  235. logger: self.logger
  236. )
  237. call.sendHead(self.makeRequestHead(path: path, callOptions: callOptions))
  238. return call
  239. }
  240. private func makeServerStreamingCall<Serializer: MessageSerializer, Deserializer: MessageDeserializer>(
  241. serializer: Serializer,
  242. deserializer: Deserializer,
  243. path: String,
  244. request: Serializer.Input,
  245. callOptions: CallOptions,
  246. handler: @escaping (Deserializer.Output) -> Void
  247. ) -> ServerStreamingCall<Serializer.Input, Deserializer.Output> {
  248. let call = ServerStreamingCall<Serializer.Input, Deserializer.Output>.make(
  249. serializer: serializer,
  250. deserializer: deserializer,
  251. fakeResponse: self.dequeueResponseStream(forPath: path),
  252. callOptions: callOptions,
  253. logger: self.logger,
  254. responseHandler: handler
  255. )
  256. call.send(self.makeRequestHead(path: path, callOptions: callOptions), request: request)
  257. return call
  258. }
  259. private func makeBidirectionalStreamingCall<Serializer: MessageSerializer, Deserializer: MessageDeserializer>(
  260. serializer: Serializer,
  261. deserializer: Deserializer,
  262. path: String,
  263. callOptions: CallOptions,
  264. handler: @escaping (Deserializer.Output) -> Void
  265. ) -> BidirectionalStreamingCall<Serializer.Input, Deserializer.Output> {
  266. let call = BidirectionalStreamingCall<Serializer.Input, Deserializer.Output>.make(
  267. serializer: serializer,
  268. deserializer: deserializer,
  269. fakeResponse: self.dequeueResponseStream(forPath: path),
  270. callOptions: callOptions,
  271. logger: self.logger,
  272. responseHandler: handler
  273. )
  274. call.sendHead(self.makeRequestHead(path: path, callOptions: callOptions))
  275. return call
  276. }
  277. }