1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
import Foundation
import WasmParser
import XCTest
@testable import WAT
class EncoderTests: XCTestCase {
struct CompatibilityTestStats {
var run: Int = 0
var failed: Set<String> = []
}
func checkWabtCompatibility(
wast: URL, json: URL, stats parentStats: inout CompatibilityTestStats
) throws {
var stats = parentStats
defer { parentStats = stats }
func recordFail() {
stats.failed.insert(wast.lastPathComponent)
}
func assertEqual<T: Equatable>(_ lhs: T, _ rhs: T, file: StaticString = #file, line: UInt = #line) {
XCTAssertEqual(lhs, rhs, file: file, line: line)
if lhs != rhs {
recordFail()
}
}
print("Checking\n wast: \(wast.path)\n json: \(json.path)")
var parser = WastParser(try String(contentsOf: wast), features: Spectest.deriveFeatureSet(wast: wast))
var watModules: [ModuleDirective] = []
while let directive = try parser.nextDirective() {
switch directive {
case .module(let moduleDirective):
watModules.append(moduleDirective)
case .assertMalformed(let module, let message):
let diagnostic = {
let (line, column) = module.location.computeLineAndColumn()
return "\(wast.path):\(line):\(column) should be malformed: \(message)"
}
switch module.source {
case .text(var wat):
XCTAssertThrowsError(
try {
_ = try wat.encode()
recordFail()
}(), diagnostic())
case .quote(let bytes):
XCTAssertThrowsError(
try {
_ = try wat2wasm(String(decoding: bytes, as: UTF8.self))
recordFail()
}(), diagnostic())
case .binary: break
}
default: break
}
}
guard FileManager.default.fileExists(atPath: json.path) else {
print("Skipping binary comparison because the oracle file (\(json.path)) does not exist.")
return
}
let moduleBinaryFiles = try Spectest.moduleFiles(json: json)
assertEqual(watModules.count, moduleBinaryFiles.count)
for (watModule, (moduleBinaryFile, expectedName)) in zip(watModules, moduleBinaryFiles) {
func assertEqual<T: Equatable>(_ lhs: T, _ rhs: T, file: StaticString = #file, line: UInt = #line) {
XCTAssertEqual(lhs, rhs, moduleBinaryFile.path, file: file, line: line)
if lhs != rhs {
recordFail()
}
}
stats.run += 1
let moduleBytes: [UInt8]
let expectedBytes = try Array(Data(contentsOf: moduleBinaryFile))
do {
assertEqual(watModule.id, expectedName)
switch watModule.source {
case .text(var watModule):
moduleBytes = try encode(module: &watModule, options: .default)
case .binary(let bytes):
moduleBytes = bytes
case .quote(let watText):
moduleBytes = try wat2wasm(String(decoding: watText, as: UTF8.self))
}
} catch {
recordFail()
XCTFail("Error while encoding \(moduleBinaryFile.lastPathComponent): \(error)")
return
}
if moduleBytes != expectedBytes {
recordFail()
}
assertEqual(moduleBytes.count, expectedBytes.count)
if moduleBytes.count == expectedBytes.count {
assertEqual(moduleBytes, expectedBytes)
}
}
}
func testSpectest() throws {
#if os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)
throw XCTSkip("Spectest compatibility test requires Foundation.Process")
#else
guard let wast2json = TestSupport.lookupExecutable("wast2json") else {
throw XCTSkip("wast2json not found in PATH")
}
var stats = CompatibilityTestStats()
let excluded: [String] = []
for wastFile in Spectest.wastFiles(include: [], exclude: excluded) {
try TestSupport.withTemporaryDirectory { tempDir, shouldRetain in
let jsonFileName = wastFile.deletingPathExtension().lastPathComponent + ".json"
let json = URL(fileURLWithPath: tempDir).appendingPathComponent(jsonFileName)
let wast2jsonProcess = try Process.run(
wast2json,
arguments: [
wastFile.path,
"--enable-memory64",
"--enable-tail-call",
"-o", json.path,
]
)
wast2jsonProcess.waitUntilExit()
do {
try checkWabtCompatibility(wast: wastFile, json: json, stats: &stats)
} catch {
stats.failed.insert(wastFile.lastPathComponent)
shouldRetain = true
XCTFail("Error while checking compatibility between \(wastFile) and \(json.path): \(error)")
}
}
}
print("Spectest compatibility: \(stats.run - stats.failed.count) / \(stats.run)")
if !stats.failed.isEmpty {
print("Failed test cases: \(stats.failed.sorted())")
}
#endif
}
func testEncodeNameSection() throws {
let bytes = try wat2wasm(
"""
(module
(func $foo)
(func)
(func $bar)
)
""",
options: EncodeOptions(nameSection: true)
)
var parser = WasmParser.Parser(bytes: bytes)
var customSections: [CustomSection] = []
while let payload = try parser.parseNext() {
guard case .customSection(let section) = payload else {
continue
}
customSections.append(section)
}
let nameSection = customSections.first(where: { $0.name == "name" })
let nameParser = NameSectionParser(
stream: StaticByteStream(bytes: nameSection?.bytes ?? [])
)
let names = try nameParser.parseAll()
XCTAssertEqual(names.count, 1)
guard case .functions(let functionNames) = try XCTUnwrap(names.first) else {
XCTFail()
return
}
XCTAssertEqual(functionNames, [0: "foo", 2: "bar"])
}
}
|