main.swift 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*
  2. * Copyright 2019, 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 Foundation
  17. import GRPC
  18. import GRPCInteroperabilityTestsImplementation
  19. import Logging
  20. import NIO
  21. import NIOSSL
  22. // Reduce stdout noise.
  23. LoggingSystem.bootstrap(StreamLogHandler.standardError)
  24. enum InteroperabilityTestError: LocalizedError {
  25. case testNotFound(String)
  26. case testFailed(Error)
  27. var errorDescription: String? {
  28. switch self {
  29. case let .testNotFound(name):
  30. return "No test named '\(name)' was found"
  31. case let .testFailed(error):
  32. return "Test failed with error: \(error)"
  33. }
  34. }
  35. }
  36. /// Runs the test instance using the given connection.
  37. ///
  38. /// Success or failure is indicated by the lack or presence of thrown errors, respectively.
  39. ///
  40. /// - Parameters:
  41. /// - instance: `InteroperabilityTest` instance to run.
  42. /// - name: the name of the test, use for logging only.
  43. /// - host: host of the test server.
  44. /// - port: port of the test server.
  45. /// - useTLS: whether to use TLS when connecting to the test server.
  46. /// - Throws: `InteroperabilityTestError` if the test fails.
  47. func runTest(
  48. _ instance: InteroperabilityTest,
  49. name: String,
  50. host: String,
  51. port: Int,
  52. useTLS: Bool
  53. ) throws {
  54. let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  55. defer {
  56. try! group.syncShutdownGracefully()
  57. }
  58. do {
  59. print("Running '\(name)' ... ", terminator: "")
  60. let builder = makeInteroperabilityTestClientBuilder(group: group, useTLS: useTLS)
  61. instance.configure(builder: builder)
  62. let connection = builder.connect(host: host, port: port)
  63. defer {
  64. _ = connection.close()
  65. }
  66. try instance.run(using: connection)
  67. print("PASSED")
  68. } catch {
  69. print("FAILED")
  70. throw InteroperabilityTestError.testFailed(error)
  71. }
  72. }
  73. /// Creates a new `InteroperabilityTest` instance with the given name, or throws an
  74. /// `InteroperabilityTestError` if no test matches the given name. Implemented test names can be
  75. /// found by running the `list_tests` target.
  76. func makeRunnableTest(name: String) throws -> InteroperabilityTest {
  77. guard let testCase = InteroperabilityTestCase(rawValue: name) else {
  78. throw InteroperabilityTestError.testNotFound(name)
  79. }
  80. return testCase.makeTest()
  81. }
  82. // MARK: - Command line options and "main".
  83. func printUsageAndExit(program: String) -> Never {
  84. print("""
  85. Usage: \(program) COMMAND [OPTIONS...]
  86. Commands:
  87. start_server [--tls|--notls] PORT Starts the interoperability test server.
  88. run_test [--tls|--notls] HOST PORT NAME Run an interoperability test.
  89. list_tests List all interoperability test names.
  90. """)
  91. exit(1)
  92. }
  93. enum Command {
  94. case startServer(port: Int, useTLS: Bool)
  95. case runTest(name: String, host: String, port: Int, useTLS: Bool)
  96. case listTests
  97. init?(from args: [String]) {
  98. guard !args.isEmpty else {
  99. return nil
  100. }
  101. var args = args
  102. let command = args.removeFirst()
  103. switch command {
  104. case "start_server":
  105. guard args.count == 1 || args.count == 2,
  106. let port = args.popLast().flatMap(Int.init),
  107. let useTLS = Command.parseTLSArg(args.popLast())
  108. else {
  109. return nil
  110. }
  111. self = .startServer(port: port, useTLS: useTLS)
  112. case "run_test":
  113. guard args.count == 3 || args.count == 4,
  114. let name = args.popLast(),
  115. let port = args.popLast().flatMap(Int.init),
  116. let host = args.popLast(),
  117. let useTLS = Command.parseTLSArg(args.popLast())
  118. else {
  119. return nil
  120. }
  121. self = .runTest(name: name, host: host, port: port, useTLS: useTLS)
  122. case "list_tests":
  123. self = .listTests
  124. default:
  125. return nil
  126. }
  127. }
  128. private static func parseTLSArg(_ arg: String?) -> Bool? {
  129. switch arg {
  130. case .some("--tls"):
  131. return true
  132. case .none, .some("--notls"):
  133. return false
  134. default:
  135. return nil
  136. }
  137. }
  138. }
  139. func main(args: [String]) {
  140. let program = args.first ?? "GRPC Interoperability Tests"
  141. guard let command = Command(from: .init(args.dropFirst())) else {
  142. printUsageAndExit(program: program)
  143. }
  144. switch command {
  145. case .listTests:
  146. InteroperabilityTestCase.allCases.forEach {
  147. print($0.name)
  148. }
  149. case let .startServer(port: port, useTLS: useTLS):
  150. let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  151. defer {
  152. try! group.syncShutdownGracefully()
  153. }
  154. do {
  155. let server = try makeInteroperabilityTestServer(
  156. port: port,
  157. eventLoopGroup: group,
  158. useTLS: useTLS
  159. ).wait()
  160. print("server started: \(server.channel.localAddress!)")
  161. // We never call close; run until we get killed.
  162. try server.onClose.wait()
  163. } catch {
  164. print("unable to start interoperability test server")
  165. }
  166. case let .runTest(name: name, host: host, port: port, useTLS: useTLS):
  167. let test: InteroperabilityTest
  168. do {
  169. test = try makeRunnableTest(name: name)
  170. } catch {
  171. print("\(error)")
  172. exit(1)
  173. }
  174. do {
  175. // Provide some basic configuration. Some tests may override this.
  176. try runTest(test, name: name, host: host, port: port, useTLS: useTLS)
  177. } catch {
  178. print("Error running test: \(error)")
  179. exit(1)
  180. }
  181. }
  182. }
  183. main(args: CommandLine.arguments)