main.swift 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // Sources/Conformance/main.swift - Conformance test main
  2. //
  3. // Copyright (c) 2014 - 2016 Apple Inc. and the project authors
  4. // Licensed under Apache License v2.0 with Runtime Library Exception
  5. //
  6. // See LICENSE.txt for license information:
  7. // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
  8. //
  9. // -----------------------------------------------------------------------------
  10. ///
  11. /// Google's conformance test is a C++ program that pipes data to/from another
  12. /// process. The tester sends data to the test wrapper which encodes and decodes
  13. /// data according to the provided instructions. This allows a single test
  14. /// scaffold to exercise many differnt implementations.
  15. ///
  16. // -----------------------------------------------------------------------------
  17. import Foundation
  18. import SwiftProtobuf
  19. extension FileHandle {
  20. fileprivate func _read(count: Int) -> Data? {
  21. if #available(macOS 10.15.4, *) {
  22. do {
  23. guard let result = try read(upToCount: count),
  24. result.count == count
  25. else {
  26. return nil
  27. }
  28. return result
  29. } catch {
  30. return nil
  31. }
  32. } else {
  33. let result = readData(ofLength: count)
  34. guard result.count == count else {
  35. return nil
  36. }
  37. return result
  38. }
  39. }
  40. }
  41. func readRequest() -> Data? {
  42. let stdIn = FileHandle.standardInput
  43. guard let countLEData = stdIn._read(count: 4) else {
  44. return nil
  45. }
  46. let countLE: UInt32 = countLEData.withUnsafeBytes { rawBuffer in
  47. rawBuffer.loadUnaligned(as: UInt32.self)
  48. }
  49. let count = UInt32(littleEndian: countLE)
  50. guard count < Int.max,
  51. let result = stdIn._read(count: Int(count))
  52. else {
  53. return nil
  54. }
  55. return result
  56. }
  57. func writeResponse(data: Data) {
  58. let count = UInt32(data.count)
  59. var countLE = count.littleEndian
  60. let countLEData = Data(bytes: &countLE, count: MemoryLayout.size(ofValue: countLE))
  61. let stdOut = FileHandle.standardOutput
  62. stdOut.write(countLEData)
  63. stdOut.write(data)
  64. }
  65. func buildResponse(serializedData: Data) -> Conformance_ConformanceResponse {
  66. var response = Conformance_ConformanceResponse()
  67. let request: Conformance_ConformanceRequest
  68. do {
  69. request = try Conformance_ConformanceRequest(serializedBytes: serializedData)
  70. } catch {
  71. response.runtimeError = "Failed to parse conformance request"
  72. return response
  73. }
  74. // Detect when something gets added to the conformance request that isn't
  75. // supported yet.
  76. guard request.unknownFields.data.isEmpty else {
  77. response.runtimeError =
  78. "ConformanceRequest had unknown fields; regenerate conformance.pb.swift and"
  79. + " see what support needs to be added."
  80. return response
  81. }
  82. switch request.testCategory {
  83. case .unspecifiedTest, .binaryTest, .jsonTest, .jsonIgnoreUnknownParsingTest, .textFormatTest:
  84. break // known, nothing to do.
  85. case .jspbTest:
  86. response.skipped =
  87. "ConformanceRequest had a JSPB_TEST TestCategory; those aren't supposed to"
  88. + " happen with opensource."
  89. return response
  90. case .UNRECOGNIZED(let x):
  91. response.runtimeError =
  92. "ConformanceRequest had a new testCategory (\(x)); regenerate conformance.pb.swift"
  93. + " and see what support needs to be added."
  94. return response
  95. }
  96. let msgType: any SwiftProtobuf.Message.Type
  97. let extensions: any SwiftProtobuf.ExtensionMap
  98. switch request.messageType {
  99. case "":
  100. // Note: This case is here to cover using a old version of the conformance test
  101. // runner that don't know about this field, and it is thus implicit.
  102. fallthrough
  103. case ProtobufTestMessages_Proto3_TestAllTypesProto3.protoMessageName:
  104. msgType = ProtobufTestMessages_Proto3_TestAllTypesProto3.self
  105. extensions = SwiftProtobuf.SimpleExtensionMap()
  106. case ProtobufTestMessages_Proto2_TestAllTypesProto2.protoMessageName:
  107. msgType = ProtobufTestMessages_Proto2_TestAllTypesProto2.self
  108. extensions = ProtobufTestMessages_Proto2_TestMessagesProto2_Extensions
  109. case ProtobufTestMessages_Editions_TestAllTypesEdition2023.protoMessageName:
  110. msgType = ProtobufTestMessages_Editions_TestAllTypesEdition2023.self
  111. extensions = ProtobufTestMessages_Editions_TestMessagesEdition2023_Extensions
  112. case ProtobufTestMessages_Editions_Proto3_TestAllTypesProto3.protoMessageName:
  113. msgType = ProtobufTestMessages_Editions_Proto3_TestAllTypesProto3.self
  114. extensions = SwiftProtobuf.SimpleExtensionMap()
  115. case ProtobufTestMessages_Editions_Proto2_TestAllTypesProto2.protoMessageName:
  116. msgType = ProtobufTestMessages_Editions_Proto2_TestAllTypesProto2.self
  117. extensions = ProtobufTestMessages_Editions_Proto2_TestMessagesProto2Editions_Extensions
  118. default:
  119. response.runtimeError = "Unexpected message type: \(request.messageType)"
  120. return response
  121. }
  122. let testMessage: any SwiftProtobuf.Message
  123. switch request.payload {
  124. case .protobufPayload(let data)?:
  125. do {
  126. testMessage = try msgType.init(serializedBytes: data, extensions: extensions)
  127. } catch let e {
  128. response.parseError = "Protobuf failed to parse: \(e)"
  129. return response
  130. }
  131. case .jsonPayload(let json)?:
  132. var options = JSONDecodingOptions()
  133. options.ignoreUnknownFields = (request.testCategory == .jsonIgnoreUnknownParsingTest)
  134. do {
  135. testMessage = try msgType.init(
  136. jsonString: json,
  137. extensions: extensions,
  138. options: options
  139. )
  140. } catch let e {
  141. response.parseError = "JSON failed to parse: \(e)"
  142. return response
  143. }
  144. case .jspbPayload(_)?:
  145. response.skipped =
  146. "ConformanceRequest had a jspb_payload ConformanceRequest payload; those aren't"
  147. + " supposed to happen with opensource."
  148. return response
  149. case .textPayload(let textFormat)?:
  150. do {
  151. testMessage = try msgType.init(textFormatString: textFormat, extensions: extensions)
  152. } catch let e {
  153. response.parseError = "TextFormat failed to parse: \(e)"
  154. return response
  155. }
  156. case nil:
  157. response.runtimeError = "No payload in request:\n\(request.textFormatString())"
  158. return response
  159. }
  160. switch request.requestedOutputFormat {
  161. case .protobuf:
  162. do {
  163. response.protobufPayload = try testMessage.serializedBytes()
  164. } catch let e {
  165. response.serializeError = "Failed to serialize: \(e)"
  166. }
  167. case .json:
  168. do {
  169. response.jsonPayload = try testMessage.jsonString()
  170. } catch let e {
  171. response.serializeError = "Failed to serialize: \(e)"
  172. }
  173. case .jspb:
  174. response.skipped =
  175. "ConformanceRequest had a requested_output_format of JSPB WireFormat; that"
  176. + " isn't supposed to happen with opensource."
  177. case .textFormat:
  178. var textFormatOptions = TextFormatEncodingOptions()
  179. textFormatOptions.printUnknownFields = request.printUnknownFields
  180. response.textPayload = testMessage.textFormatString(options: textFormatOptions)
  181. case .unspecified:
  182. response.runtimeError = "Request asked for the 'unspecified' result, that isn't valid."
  183. case .UNRECOGNIZED(let v):
  184. response.runtimeError = "Unknown output format: \(v)"
  185. }
  186. return response
  187. }
  188. func singleTest() throws -> Bool {
  189. if let indata = readRequest() {
  190. let response = buildResponse(serializedData: indata)
  191. let outdata: Data = try response.serializedData()
  192. writeResponse(data: outdata)
  193. return true
  194. } else {
  195. return false
  196. }
  197. }
  198. Google_Protobuf_Any.register(messageType: ProtobufTestMessages_Proto3_TestAllTypesProto3.self)
  199. Google_Protobuf_Any.register(messageType: ProtobufTestMessages_Editions_Proto3_TestAllTypesProto3.self)
  200. while try singleTest() {
  201. }