main.swift 5.7 KB

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