main.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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 ArgumentParser
  17. import Foundation
  18. import GRPC
  19. import GRPCInteroperabilityTestsImplementation
  20. import Logging
  21. import NIOCore
  22. import NIOPosix
  23. // Reduce stdout noise.
  24. LoggingSystem.bootstrap(StreamLogHandler.standardError)
  25. enum InteroperabilityTestError: LocalizedError {
  26. case testNotFound(String)
  27. case testFailed(Error)
  28. var errorDescription: String? {
  29. switch self {
  30. case let .testNotFound(name):
  31. return "No test named '\(name)' was found"
  32. case let .testFailed(error):
  33. return "Test failed with error: \(error)"
  34. }
  35. }
  36. }
  37. /// Runs the test instance using the given connection.
  38. ///
  39. /// Success or failure is indicated by the lack or presence of thrown errors, respectively.
  40. ///
  41. /// - Parameters:
  42. /// - instance: `InteroperabilityTest` instance to run.
  43. /// - name: the name of the test, use for logging only.
  44. /// - host: host of the test server.
  45. /// - port: port of the test server.
  46. /// - useTLS: whether to use TLS when connecting to the test server.
  47. /// - Throws: `InteroperabilityTestError` if the test fails.
  48. func runTest(
  49. _ instance: InteroperabilityTest,
  50. name: String,
  51. host: String,
  52. port: Int,
  53. useTLS: Bool
  54. ) throws {
  55. let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  56. defer {
  57. try! group.syncShutdownGracefully()
  58. }
  59. do {
  60. print("Running '\(name)' ... ", terminator: "")
  61. let builder = makeInteroperabilityTestClientBuilder(group: group, useTLS: useTLS)
  62. instance.configure(builder: builder)
  63. let connection = builder.connect(host: host, port: port)
  64. defer {
  65. _ = connection.close()
  66. }
  67. try instance.run(using: connection)
  68. print("PASSED")
  69. } catch {
  70. print("FAILED")
  71. throw InteroperabilityTestError.testFailed(error)
  72. }
  73. }
  74. /// Creates a new `InteroperabilityTest` instance with the given name, or throws an
  75. /// `InteroperabilityTestError` if no test matches the given name. Implemented test names can be
  76. /// found by running the `list_tests` target.
  77. func makeRunnableTest(name: String) throws -> InteroperabilityTest {
  78. guard let testCase = InteroperabilityTestCase(rawValue: name) else {
  79. throw InteroperabilityTestError.testNotFound(name)
  80. }
  81. return testCase.makeTest()
  82. }
  83. // MARK: - Command line options and "main".
  84. struct InteroperabilityTests: ParsableCommand {
  85. static var configuration = CommandConfiguration(
  86. abstract: "gRPC Swift Interoperability Runner",
  87. subcommands: [StartServer.self, RunTest.self, ListTests.self]
  88. )
  89. struct StartServer: ParsableCommand {
  90. static var configuration = CommandConfiguration(
  91. abstract: "Start the gRPC Swift interoperability test server."
  92. )
  93. @Option(help: "The port to listen on for new connections")
  94. var port: Int
  95. @Flag(help: "Whether TLS should be used or not")
  96. var tls = false
  97. func run() throws {
  98. let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  99. defer {
  100. try! group.syncShutdownGracefully()
  101. }
  102. let server = try makeInteroperabilityTestServer(
  103. port: self.port,
  104. eventLoopGroup: group,
  105. useTLS: self.tls
  106. ).wait()
  107. print("server started: \(server.channel.localAddress!)")
  108. // We never call close; run until we get killed.
  109. try server.onClose.wait()
  110. }
  111. }
  112. struct RunTest: ParsableCommand {
  113. static var configuration = CommandConfiguration(
  114. abstract: "Runs a gRPC interoperability test using a gRPC Swift client."
  115. )
  116. @Flag(help: "Whether TLS should be used or not")
  117. var tls = false
  118. @Option(help: "The host the server is running on")
  119. var host: String
  120. @Option(help: "The port to connect to")
  121. var port: Int
  122. @Argument(help: "The name of the test to run")
  123. var testName: String
  124. func run() throws {
  125. let test = try makeRunnableTest(name: self.testName)
  126. try runTest(
  127. test,
  128. name: self.testName,
  129. host: self.host,
  130. port: self.port,
  131. useTLS: self.tls
  132. )
  133. }
  134. }
  135. struct ListTests: ParsableCommand {
  136. static var configuration = CommandConfiguration(
  137. abstract: "List all interoperability test names."
  138. )
  139. func run() throws {
  140. InteroperabilityTestCase.allCases.forEach {
  141. print($0.name)
  142. }
  143. }
  144. }
  145. }
  146. InteroperabilityTests.main()