FakeChannel.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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 Logging
  17. import NIO
  18. import SwiftProtobuf
  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
  34. SwiftLogNoOpLogHandler()
  35. })) {
  36. self.responseStreams = [:]
  37. self.logger = logger
  38. }
  39. /// Make and store a fake unary response for the given path. Users should prefer making a response
  40. /// stream for their RPC directly via the appropriate method on their generated test client.
  41. public func makeFakeUnaryResponse<Request, Response>(
  42. path: String,
  43. requestHandler: @escaping (FakeRequestPart<Request>) -> Void
  44. ) -> FakeUnaryResponse<Request, Response> {
  45. let proxy = FakeUnaryResponse<Request, Response>(requestHandler: requestHandler)
  46. self.responseStreams[path, default: []].append(proxy)
  47. return proxy
  48. }
  49. /// Make and store a fake streaming response for the given path. Users should prefer making a
  50. /// response stream for their RPC directly via the appropriate method on their generated test
  51. /// client.
  52. public func makeFakeStreamingResponse<Request, Response>(
  53. path: String,
  54. requestHandler: @escaping (FakeRequestPart<Request>) -> Void
  55. ) -> FakeStreamingResponse<Request, Response> {
  56. let proxy = FakeStreamingResponse<Request, Response>(requestHandler: requestHandler)
  57. self.responseStreams[path, default: []].append(proxy)
  58. return proxy
  59. }
  60. /// Returns true if there are fake responses enqueued for the given path.
  61. public func hasFakeResponseEnqueued(forPath path: String) -> Bool {
  62. guard let noStreamsForPath = self.responseStreams[path]?.isEmpty else {
  63. return false
  64. }
  65. return !noStreamsForPath
  66. }
  67. public func makeCall<Request: Message, Response: Message>(
  68. path: String,
  69. type: GRPCCallType,
  70. callOptions: CallOptions,
  71. interceptors: [ClientInterceptor<Request, Response>]
  72. ) -> Call<Request, Response> {
  73. return self._makeCall(
  74. path: path,
  75. type: type,
  76. callOptions: callOptions,
  77. interceptors: interceptors
  78. )
  79. }
  80. public func makeCall<Request: GRPCPayload, Response: GRPCPayload>(
  81. path: String,
  82. type: GRPCCallType,
  83. callOptions: CallOptions,
  84. interceptors: [ClientInterceptor<Request, Response>]
  85. ) -> Call<Request, Response> {
  86. return self._makeCall(
  87. path: path,
  88. type: type,
  89. callOptions: callOptions,
  90. interceptors: interceptors
  91. )
  92. }
  93. private func _makeCall<Request, Response>(
  94. path: String,
  95. type: GRPCCallType,
  96. callOptions: CallOptions,
  97. interceptors: [ClientInterceptor<Request, Response>]
  98. ) -> Call<Request, Response> {
  99. let stream: _FakeResponseStream<Request, Response>? = self.dequeueResponseStream(forPath: path)
  100. let eventLoop = stream?.channel.eventLoop ?? EmbeddedEventLoop()
  101. return Call(
  102. path: path,
  103. type: type,
  104. eventLoop: eventLoop,
  105. options: callOptions,
  106. interceptors: interceptors,
  107. transportFactory: .fake(stream, on: eventLoop)
  108. )
  109. }
  110. // (Docs inherited from `GRPCChannel`)
  111. public func makeUnaryCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
  112. path: String,
  113. request: Request,
  114. callOptions: CallOptions
  115. ) -> UnaryCall<Request, Response> {
  116. return self.makeUnaryCall(
  117. serializer: ProtobufSerializer(),
  118. deserializer: ProtobufDeserializer(),
  119. path: path,
  120. request: request,
  121. callOptions: callOptions
  122. )
  123. }
  124. // (Docs inherited from `GRPCChannel`)
  125. public func makeUnaryCall<Request: GRPCPayload, Response: GRPCPayload>(
  126. path: String,
  127. request: Request,
  128. callOptions: CallOptions
  129. ) -> UnaryCall<Request, Response> {
  130. return self.makeUnaryCall(
  131. serializer: GRPCPayloadSerializer(),
  132. deserializer: GRPCPayloadDeserializer(),
  133. path: path,
  134. request: request,
  135. callOptions: callOptions
  136. )
  137. }
  138. // (Docs inherited from `GRPCChannel`)
  139. public func makeServerStreamingCall<
  140. Request: SwiftProtobuf.Message,
  141. Response: SwiftProtobuf.Message
  142. >(
  143. path: String,
  144. request: Request,
  145. callOptions: CallOptions,
  146. handler: @escaping (Response) -> Void
  147. ) -> ServerStreamingCall<Request, Response> {
  148. return self.makeServerStreamingCall(
  149. serializer: ProtobufSerializer(),
  150. deserializer: ProtobufDeserializer(),
  151. path: path,
  152. request: request,
  153. callOptions: callOptions,
  154. handler: handler
  155. )
  156. }
  157. // (Docs inherited from `GRPCChannel`)
  158. public func makeServerStreamingCall<Request: GRPCPayload, Response: GRPCPayload>(
  159. path: String,
  160. request: Request,
  161. callOptions: CallOptions,
  162. handler: @escaping (Response) -> Void
  163. ) -> ServerStreamingCall<Request, Response> {
  164. return self.makeServerStreamingCall(
  165. serializer: GRPCPayloadSerializer(),
  166. deserializer: GRPCPayloadDeserializer(),
  167. path: path,
  168. request: request,
  169. callOptions: callOptions,
  170. handler: handler
  171. )
  172. }
  173. // (Docs inherited from `GRPCChannel`)
  174. public func makeClientStreamingCall<
  175. Request: SwiftProtobuf.Message,
  176. Response: SwiftProtobuf.Message
  177. >(
  178. path: String,
  179. callOptions: CallOptions
  180. ) -> ClientStreamingCall<Request, Response> {
  181. return self.makeClientStreamingCall(
  182. serializer: ProtobufSerializer(),
  183. deserializer: ProtobufDeserializer(),
  184. path: path,
  185. callOptions: callOptions
  186. )
  187. }
  188. // (Docs inherited from `GRPCChannel`)
  189. public func makeClientStreamingCall<Request: GRPCPayload, Response: GRPCPayload>(
  190. path: String,
  191. callOptions: CallOptions
  192. ) -> ClientStreamingCall<Request, Response> {
  193. return self.makeClientStreamingCall(
  194. serializer: GRPCPayloadSerializer(),
  195. deserializer: GRPCPayloadDeserializer(),
  196. path: path,
  197. callOptions: callOptions
  198. )
  199. }
  200. // (Docs inherited from `GRPCChannel`)
  201. public func makeBidirectionalStreamingCall<
  202. Request: SwiftProtobuf.Message,
  203. Response: SwiftProtobuf.Message
  204. >(
  205. path: String,
  206. callOptions: CallOptions,
  207. handler: @escaping (Response) -> Void
  208. ) -> BidirectionalStreamingCall<Request, Response> {
  209. return self.makeBidirectionalStreamingCall(
  210. serializer: ProtobufSerializer(),
  211. deserializer: ProtobufDeserializer(),
  212. path: path,
  213. callOptions: callOptions,
  214. handler: handler
  215. )
  216. }
  217. // (Docs inherited from `GRPCChannel`)
  218. public func makeBidirectionalStreamingCall<Request: GRPCPayload, Response: GRPCPayload>(
  219. path: String,
  220. callOptions: CallOptions,
  221. handler: @escaping (Response) -> Void
  222. ) -> BidirectionalStreamingCall<Request, Response> {
  223. return self.makeBidirectionalStreamingCall(
  224. serializer: GRPCPayloadSerializer(),
  225. deserializer: GRPCPayloadDeserializer(),
  226. path: path,
  227. callOptions: callOptions,
  228. handler: handler
  229. )
  230. }
  231. public func close() -> EventLoopFuture<Void> {
  232. // We don't have anything to close.
  233. return EmbeddedEventLoop().makeSucceededFuture(())
  234. }
  235. }
  236. extension FakeChannel {
  237. /// Dequeue a proxy for the given path and casts it to the given type, if one exists.
  238. private func dequeueResponseStream<Stream>(
  239. forPath path: String,
  240. as: Stream.Type = Stream.self
  241. ) -> Stream? {
  242. guard var streams = self.responseStreams[path], !streams.isEmpty else {
  243. return nil
  244. }
  245. // This is fine: we know we're non-empty.
  246. let first = streams.removeFirst()
  247. self.responseStreams.updateValue(streams, forKey: path)
  248. return first as? Stream
  249. }
  250. private func makeRequestHead(path: String, callOptions: CallOptions) -> _GRPCRequestHead {
  251. return _GRPCRequestHead(
  252. scheme: "http",
  253. path: path,
  254. host: "localhost",
  255. options: callOptions,
  256. requestID: nil
  257. )
  258. }
  259. }
  260. extension FakeChannel {
  261. private func makeUnaryCall<Serializer: MessageSerializer, Deserializer: MessageDeserializer>(
  262. serializer: Serializer,
  263. deserializer: Deserializer,
  264. path: String,
  265. request: Serializer.Input,
  266. callOptions: CallOptions
  267. ) -> UnaryCall<Serializer.Input, Deserializer.Output> {
  268. let call = UnaryCall<Serializer.Input, Deserializer.Output>.make(
  269. serializer: serializer,
  270. deserializer: deserializer,
  271. fakeResponse: self.dequeueResponseStream(forPath: path),
  272. callOptions: callOptions,
  273. logger: self.logger
  274. )
  275. call.send(self.makeRequestHead(path: path, callOptions: callOptions), request: request)
  276. return call
  277. }
  278. private func makeClientStreamingCall<
  279. Serializer: MessageSerializer,
  280. Deserializer: MessageDeserializer
  281. >(
  282. serializer: Serializer,
  283. deserializer: Deserializer,
  284. path: String,
  285. callOptions: CallOptions
  286. ) -> ClientStreamingCall<Serializer.Input, Deserializer.Output> {
  287. let call = ClientStreamingCall<Serializer.Input, Deserializer.Output>.make(
  288. serializer: serializer,
  289. deserializer: deserializer,
  290. fakeResponse: self.dequeueResponseStream(forPath: path),
  291. callOptions: callOptions,
  292. logger: self.logger
  293. )
  294. call.sendHead(self.makeRequestHead(path: path, callOptions: callOptions))
  295. return call
  296. }
  297. private func makeServerStreamingCall<
  298. Serializer: MessageSerializer,
  299. Deserializer: MessageDeserializer
  300. >(
  301. serializer: Serializer,
  302. deserializer: Deserializer,
  303. path: String,
  304. request: Serializer.Input,
  305. callOptions: CallOptions,
  306. handler: @escaping (Deserializer.Output) -> Void
  307. ) -> ServerStreamingCall<Serializer.Input, Deserializer.Output> {
  308. let call = ServerStreamingCall<Serializer.Input, Deserializer.Output>.make(
  309. serializer: serializer,
  310. deserializer: deserializer,
  311. fakeResponse: self.dequeueResponseStream(forPath: path),
  312. callOptions: callOptions,
  313. logger: self.logger,
  314. responseHandler: handler
  315. )
  316. call.send(self.makeRequestHead(path: path, callOptions: callOptions), request: request)
  317. return call
  318. }
  319. private func makeBidirectionalStreamingCall<
  320. Serializer: MessageSerializer,
  321. Deserializer: MessageDeserializer
  322. >(
  323. serializer: Serializer,
  324. deserializer: Deserializer,
  325. path: String,
  326. callOptions: CallOptions,
  327. handler: @escaping (Deserializer.Output) -> Void
  328. ) -> BidirectionalStreamingCall<Serializer.Input, Deserializer.Output> {
  329. let call = BidirectionalStreamingCall<Serializer.Input, Deserializer.Output>.make(
  330. serializer: serializer,
  331. deserializer: deserializer,
  332. fakeResponse: self.dequeueResponseStream(forPath: path),
  333. callOptions: callOptions,
  334. logger: self.logger,
  335. responseHandler: handler
  336. )
  337. call.sendHead(self.makeRequestHead(path: path, callOptions: callOptions))
  338. return call
  339. }
  340. }