main.swift 5.5 KB

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