main.swift 5.8 KB

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