Test_Bytecode.swift 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // Tests/SwiftProtobufPluginLibraryTests/Test_Bytecode.swift - Test Bytecode
  2. //
  3. // Copyright (c) 2014 - 2025 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. import SwiftProtobuf
  11. import SwiftProtobufPluginLibrary
  12. import XCTest
  13. import protoc_gen_swift
  14. /// All tests for the bytecode are in this test target even though the interpreter and reader are
  15. /// defined in the SwiftProtobuf module so that we can test everything end-to-end in one place.
  16. final class Test_Bytecode: XCTestCase {
  17. private enum TestInstruction: UInt64 {
  18. case first = 1
  19. case second = 2
  20. }
  21. /// In the writer and reader tests, each line of the multi-line string corresponds to the value
  22. /// of a single `writer.write*` call (where the first line is the always-written open-quote and
  23. /// program format integer and the last is the close-quote.
  24. func testWriter_opcodes() {
  25. var writer = BytecodeWriter<TestInstruction>()
  26. writer.writeOpcode(of: .first)
  27. writer.writeOpcode(of: .second)
  28. XCTAssertEqual(
  29. writer.stringLiteral,
  30. """
  31. "\\0\
  32. \\u{1}\
  33. \\u{2}\
  34. "
  35. """
  36. )
  37. }
  38. func testWriter_integers() {
  39. var writer = BytecodeWriter<TestInstruction>()
  40. writer.writeUInt64(0)
  41. writer.writeUInt64(63)
  42. writer.writeUInt64(64)
  43. writer.writeUInt64(123_456_789)
  44. writer.writeInt32(0)
  45. writer.writeInt32(-1)
  46. XCTAssertEqual(
  47. writer.stringLiteral,
  48. """
  49. "\\0\
  50. \\0\
  51. ?\
  52. @\\u{1}\
  53. Ut|V\\u{7}\
  54. \\0\
  55. \\u{7f}\\u{7f}\\u{7f}\\u{7f}\\u{7f}\\u{3}\
  56. "
  57. """
  58. )
  59. }
  60. func testWriter_strings() {
  61. var writer = BytecodeWriter<TestInstruction>()
  62. writer.writeNullTerminatedString("hello")
  63. writer.writeNullTerminatedString("ütf-èíght")
  64. XCTAssertEqual(
  65. writer.stringLiteral,
  66. """
  67. "\\0\
  68. hello\\0\
  69. ütf-èíght\\0\
  70. "
  71. """
  72. )
  73. }
  74. func testWriter_stringArray() {
  75. var writer = BytecodeWriter<TestInstruction>()
  76. writer.writeNullTerminatedStringArray(["hello", "ütf-èíght", "world"])
  77. XCTAssertEqual(
  78. writer.stringLiteral,
  79. """
  80. "\\0\
  81. \\u{3}\
  82. hello\\0\
  83. ütf-èíght\\0\
  84. world\\0\
  85. "
  86. """
  87. )
  88. }
  89. func testWriter_hasInstructions() {
  90. var writer = BytecodeWriter<TestInstruction>()
  91. XCTAssertFalse(writer.hasData)
  92. writer.writeUInt64(10)
  93. XCTAssertTrue(writer.hasData)
  94. }
  95. func testReader_opcodes() {
  96. let program: StaticString = """
  97. \0\
  98. \u{1}\
  99. \u{2}
  100. """
  101. program.withUTF8Buffer { buffer in
  102. var reader = BytecodeReader<TestInstruction>(remainingProgram: buffer[...])
  103. XCTAssertTrue(reader.hasData)
  104. XCTAssertEqual(reader.nextInstruction(), .first)
  105. XCTAssertTrue(reader.hasData)
  106. XCTAssertEqual(reader.nextInstruction(), .second)
  107. XCTAssertFalse(reader.hasData)
  108. }
  109. }
  110. func testReader_integers() {
  111. let program: StaticString = """
  112. \0\
  113. \0\
  114. ?\
  115. @\u{1}\
  116. Ut|V\u{7}\
  117. \0\
  118. \u{7f}\u{7f}\u{7f}\u{7f}\u{7f}\u{3}
  119. """
  120. program.withUTF8Buffer { buffer in
  121. var reader = BytecodeReader<TestInstruction>(remainingProgram: buffer[...])
  122. XCTAssertTrue(reader.hasData)
  123. XCTAssertEqual(reader.nextUInt64(), 0)
  124. XCTAssertTrue(reader.hasData)
  125. XCTAssertEqual(reader.nextUInt64(), 63)
  126. XCTAssertTrue(reader.hasData)
  127. XCTAssertEqual(reader.nextUInt64(), 64)
  128. XCTAssertTrue(reader.hasData)
  129. XCTAssertEqual(reader.nextUInt64(), 123_456_789)
  130. XCTAssertTrue(reader.hasData)
  131. XCTAssertEqual(reader.nextInt32(), 0)
  132. XCTAssertTrue(reader.hasData)
  133. XCTAssertEqual(reader.nextInt32(), -1)
  134. XCTAssertFalse(reader.hasData)
  135. }
  136. }
  137. func testReader_strings() {
  138. let program: StaticString = """
  139. \0\
  140. hello\0\
  141. ütf-èíght\0
  142. """
  143. program.withUTF8Buffer { buffer in
  144. var reader = BytecodeReader<TestInstruction>(remainingProgram: buffer[...])
  145. XCTAssertTrue(reader.hasData)
  146. XCTAssertEqual(
  147. String(decoding: reader.nextNullTerminatedString(), as: UTF8.self),
  148. "hello"
  149. )
  150. XCTAssertTrue(reader.hasData)
  151. XCTAssertEqual(
  152. String(decoding: reader.nextNullTerminatedString(), as: UTF8.self),
  153. "ütf-èíght"
  154. )
  155. XCTAssertFalse(reader.hasData)
  156. }
  157. }
  158. func testReader_stringArray() {
  159. let program: StaticString = """
  160. \0\
  161. \u{3}\
  162. hello\0\
  163. ütf-èíght\0\
  164. world\0
  165. """
  166. program.withUTF8Buffer { buffer in
  167. var reader = BytecodeReader<TestInstruction>(remainingProgram: buffer[...])
  168. XCTAssertTrue(reader.hasData)
  169. XCTAssertEqual(
  170. reader.nextNullTerminatedStringArray().map { String(decoding: $0, as: UTF8.self) },
  171. ["hello", "ütf-èíght", "world"]
  172. )
  173. XCTAssertFalse(reader.hasData)
  174. }
  175. }
  176. func testExecution() {
  177. enum CalculatorInstruction: UInt64 {
  178. // Push the integer operand onto the stack.
  179. case push = 1
  180. // Pops the top two integers off the stack and pushes their sum.
  181. case add = 2
  182. // Pop an integer off the stack and concatenate it to the output.
  183. case print = 3
  184. }
  185. var stack = [UInt64]()
  186. var output = ""
  187. // The program below contains the following instructions:
  188. // push(integer 1), push(integer 37), add, print
  189. let interpreter = BytecodeInterpreter<CalculatorInstruction>(
  190. program: """
  191. \0\
  192. \u{1}\u{5}\
  193. \u{1}!\
  194. \u{2}\
  195. \u{3}
  196. """
  197. )
  198. interpreter.execute { instruction, reader in
  199. switch instruction {
  200. case .push:
  201. stack.append(reader.nextUInt64())
  202. case .add:
  203. let first = stack.removeLast()
  204. let second = stack.removeLast()
  205. stack.append(first + second)
  206. case .print:
  207. let value = stack.removeLast()
  208. output.append(String(value))
  209. }
  210. }
  211. XCTAssertEqual(output, "38")
  212. }
  213. }