From 4c51bae6e7724e3465de1dda46eaec75eaf2d6b0 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 5 Oct 2025 20:45:41 +0900 Subject: [PATCH 1/6] Tests: Migrate WasmKitTests to use swift-testing --- .../Execution/HostModuleTests.swift | 144 ++++++----- .../Runtime/StoreAllocatorTests.swift | 84 +++--- Tests/WasmKitTests/ExecutionTests.swift | 242 +++++++++--------- .../FuzzTranslatorRegressionTests.swift | 47 ++-- Tests/WasmKitTests/Spectest/Spectest.swift | 234 +++++++---------- Tests/WasmKitTests/Spectest/TestCase.swift | 23 +- Tests/WasmKitTests/SpectestTests.swift | 84 +++--- Tests/WasmKitTests/Trait+Platform.swift | 31 +++ 8 files changed, 452 insertions(+), 437 deletions(-) create mode 100644 Tests/WasmKitTests/Trait+Platform.swift diff --git a/Tests/WasmKitTests/Execution/HostModuleTests.swift b/Tests/WasmKitTests/Execution/HostModuleTests.swift index ad30e170..dcbbb835 100644 --- a/Tests/WasmKitTests/Execution/HostModuleTests.swift +++ b/Tests/WasmKitTests/Execution/HostModuleTests.swift @@ -1,77 +1,83 @@ -import WAT -import XCTest +#if canImport(Testing) + import WAT + import Testing -@testable import WasmKit -@testable import WasmParser + @testable import WasmKit + @testable import WasmParser -final class HostModuleTests: XCTestCase { - func testImportMemory() throws { - let engine = Engine() - let store = Store(engine: engine) - let memoryType = MemoryType(min: 1, max: nil) - let memory = try WasmKit.Memory(store: store, type: memoryType) - let imports: Imports = [ - "env": ["memory": memory] - ] - - let module = try parseWasm( - bytes: wat2wasm( - """ - (module - (import "env" "memory" (memory 1)) - ) - """)) - XCTAssertNoThrow(try module.instantiate(store: store, imports: imports)) - // Ensure the allocated address is valid - _ = memory.data - } + @Suite + struct HostModuleTests { + @Test + func importMemory() throws { + let engine = Engine() + let store = Store(engine: engine) + let memoryType = MemoryType(min: 1, max: nil) + let memory = try WasmKit.Memory(store: store, type: memoryType) + let imports: Imports = [ + "env": ["memory": memory] + ] - func testReentrancy() throws { - let engine = Engine() - let store = Store(engine: engine) - let voidSignature = WasmTypes.FunctionType(parameters: [], results: []) - let module = try parseWasm( - bytes: wat2wasm( - """ - (module - (import "env" "bar" (func $bar)) - (import "env" "qux" (func $qux)) - (func (export "foo") - (call $bar) - (call $bar) - (call $bar) + let module = try parseWasm( + bytes: wat2wasm( + """ + (module + (import "env" "memory" (memory 1)) ) - (func (export "baz") - (call $qux) + """)) + #expect(throws: Never.self) { try module.instantiate(store: store, imports: imports) } + // Ensure the allocated address is valid + _ = memory.data + } + + @Test + func reentrancy() throws { + let engine = Engine() + let store = Store(engine: engine) + let voidSignature = WasmTypes.FunctionType(parameters: [], results: []) + let module = try parseWasm( + bytes: wat2wasm( + """ + (module + (import "env" "bar" (func $bar)) + (import "env" "qux" (func $qux)) + (func (export "foo") + (call $bar) + (call $bar) + (call $bar) + ) + (func (export "baz") + (call $qux) + ) ) - ) - """) - ) + """) + ) - var isExecutingFoo = false - var isQuxCalled = false - let imports: Imports = [ - "env": [ - "bar": Function(store: store, type: voidSignature) { caller, _ in - // Ensure "invoke" executes instructions under the current call - XCTAssertFalse(isExecutingFoo, "bar should not be called recursively") - isExecutingFoo = true - defer { isExecutingFoo = false } - let foo = try XCTUnwrap(caller.instance?.exportedFunction(name: "baz")) - _ = try foo() - return [] - }, - "qux": Function(store: store, type: voidSignature) { caller, _ in - XCTAssertTrue(isExecutingFoo) - isQuxCalled = true - return [] - }, + var isExecutingFoo = false + var isQuxCalled = false + let imports: Imports = [ + "env": [ + "bar": Function(store: store, type: voidSignature) { caller, _ in + // Ensure "invoke" executes instructions under the current call + #expect(isExecutingFoo == false, "bar should not be called recursively") + isExecutingFoo = true + defer { isExecutingFoo = false } + let foo = try #require(caller.instance?.exportedFunction(name: "baz")) + _ = try foo() + return [] + }, + "qux": Function(store: store, type: voidSignature) { caller, _ in + #expect(isExecutingFoo == true) + isQuxCalled = true + return [] + }, + ] ] - ] - let instance = try module.instantiate(store: store, imports: imports) - // Check foo(wasm) -> bar(host) -> baz(wasm) -> qux(host) - let foo = try XCTUnwrap(instance.exports[function: "foo"]) - try foo() - XCTAssertTrue(isQuxCalled) + let instance = try module.instantiate(store: store, imports: imports) + // Check foo(wasm) -> bar(host) -> baz(wasm) -> qux(host) + let foo = try #require(instance.exports[function: "foo"]) + try foo() + #expect(isQuxCalled == true) + } } -} + +#endif diff --git a/Tests/WasmKitTests/Execution/Runtime/StoreAllocatorTests.swift b/Tests/WasmKitTests/Execution/Runtime/StoreAllocatorTests.swift index 806e8958..af8adcf7 100644 --- a/Tests/WasmKitTests/Execution/Runtime/StoreAllocatorTests.swift +++ b/Tests/WasmKitTests/Execution/Runtime/StoreAllocatorTests.swift @@ -1,47 +1,53 @@ -import WAT -import WasmParser -import XCTest +#if canImport(Testing) + import WAT + import WasmParser + import Testing -@testable import WasmKit + @testable import WasmKit -final class StoreAllocatorTests: XCTestCase { - func testBumpAllocatorDeallocates() { - class NonTrivialEntity {} - weak var weakEntity: NonTrivialEntity? - do { - let allocator = BumpAllocator(initialCapacity: 2) + @Suite + struct StoreAllocatorTests { + @Test + func bumpAllocatorDeallocates() { + class NonTrivialEntity {} + weak var weakEntity: NonTrivialEntity? do { - let entity = NonTrivialEntity() - // Allocate space placing non-trivial entity - // This `allocate` call should retain the entity - _ = allocator.allocate(initializing: entity) - // Ensure that the initial page is full - _ = allocator.allocate(initializing: entity) - _ = allocator.allocate(initializing: entity) - weakEntity = entity + let allocator = BumpAllocator(initialCapacity: 2) + do { + let entity = NonTrivialEntity() + // Allocate space placing non-trivial entity + // This `allocate` call should retain the entity + _ = allocator.allocate(initializing: entity) + // Ensure that the initial page is full + _ = allocator.allocate(initializing: entity) + _ = allocator.allocate(initializing: entity) + weakEntity = entity + } + // The entity is still alive because the allocator retains it + #expect(weakEntity != nil) } - // The entity is still alive because the allocator retains it - XCTAssertNotNil(weakEntity) + // The entity should be deallocated when the allocator is deallocated + #expect(weakEntity == nil) } - // The entity should be deallocated when the allocator is deallocated - XCTAssertNil(weakEntity) - } - func testStoreAllocatorLeak() throws { - weak var weakAllocator: StoreAllocator? - do { - let module = try parseWasm( - bytes: wat2wasm( - """ - (module - (memory (;0;) 0) - (export "a" (memory 0))) - """)) - let engine = Engine() - let store = Store(engine: engine) - _ = try module.instantiate(store: store) - weakAllocator = store.allocator + @Test + func storeAllocatorLeak() throws { + weak var weakAllocator: StoreAllocator? + do { + let module = try parseWasm( + bytes: wat2wasm( + """ + (module + (memory (;0;) 0) + (export "a" (memory 0))) + """)) + let engine = Engine() + let store = Store(engine: engine) + _ = try module.instantiate(store: store) + weakAllocator = store.allocator + } + #expect(weakAllocator == nil) } - XCTAssertNil(weakAllocator) } -} + +#endif diff --git a/Tests/WasmKitTests/ExecutionTests.swift b/Tests/WasmKitTests/ExecutionTests.swift index b4de3efc..e961c101 100644 --- a/Tests/WasmKitTests/ExecutionTests.swift +++ b/Tests/WasmKitTests/ExecutionTests.swift @@ -1,140 +1,142 @@ -import WAT -import XCTest +#if canImport(Testing) + import WAT + import Testing -@testable import WasmKit + @testable import WasmKit -final class ExecutionTests: XCTestCase { - func testDropWithRelinkingOptimization() throws { - let module = try parseWasm( - bytes: wat2wasm( - """ - (module - (func (export "_start") (result i32) (local $x i32) - (i32.const 42) - (i32.const 0) - (i32.eqz) - (drop) - (local.set $x) - (local.get $x) + @Suite + struct ExecutionTests { + + @Test + func dropWithRelinkingOptimization() throws { + let module = try parseWasm( + bytes: wat2wasm( + """ + (module + (func (export "_start") (result i32) (local $x i32) + (i32.const 42) + (i32.const 0) + (i32.eqz) + (drop) + (local.set $x) + (local.get $x) + ) ) + """ ) - """ ) - ) - let engine = Engine() - let store = Store(engine: engine) - let instance = try module.instantiate(store: store) - let _start = try XCTUnwrap(instance.exports[function: "_start"]) - let results = try _start() - XCTAssertEqual(results, [.i32(42)]) - } + let engine = Engine() + let store = Store(engine: engine) + let instance = try module.instantiate(store: store) + let _start = try #require(instance.exports[function: "_start"]) + let results = try _start() + #expect(results == [.i32(42)]) + } - func testUpdateCurrentMemoryCacheOnGrow() throws { - let module = try parseWasm( - bytes: wat2wasm( - """ - (module - (memory 0) - (func (export "_start") (result i32) - (drop (memory.grow (i32.const 1))) - (i32.store (i32.const 1) (i32.const 42)) - (i32.load (i32.const 1)) + @Test + func updateCurrentMemoryCacheOnGrow() throws { + let module = try parseWasm( + bytes: wat2wasm( + """ + (module + (memory 0) + (func (export "_start") (result i32) + (drop (memory.grow (i32.const 1))) + (i32.store (i32.const 1) (i32.const 42)) + (i32.load (i32.const 1)) + ) ) + """ ) - """ ) - ) - let engine = Engine() - let store = Store(engine: engine) - let instance = try module.instantiate(store: store) - let _start = try XCTUnwrap(instance.exports[function: "_start"]) - let results = try _start() - XCTAssertEqual(results, [.i32(42)]) - } - - func expectTrap(_ wat: String, assertTrap: (Trap) throws -> Void) throws { - let module = try parseWasm( - bytes: wat2wasm(wat, options: EncodeOptions(nameSection: true)) - ) + let engine = Engine() + let store = Store(engine: engine) + let instance = try module.instantiate(store: store) + let _start = try #require(instance.exports[function: "_start"]) + let results = try _start() + #expect(results == [.i32(42)]) + } - let engine = Engine() - let store = Store(engine: engine) - var imports = Imports() - for importEntry in module.imports { - guard case .function(let type) = importEntry.descriptor else { continue } - let function = try Function( - store: store, - type: module.resolveFunctionType(type), - body: { _, _ in - return [] - } + func expectTrap(_ wat: String, assertTrap: (Trap) throws -> Void) throws { + let module = try parseWasm( + bytes: wat2wasm(wat, options: EncodeOptions(nameSection: true)) ) - imports.define(importEntry, .function(function)) - } - let instance = try module.instantiate(store: store, imports: imports) - let _start = try XCTUnwrap(instance.exports[function: "_start"]) - let trap: Trap - do { - try _start() - XCTFail("expect unreachable trap") - return - } catch let error { - trap = try XCTUnwrap(error as? Trap) + let engine = Engine() + let store = Store(engine: engine) + var imports = Imports() + for importEntry in module.imports { + guard case .function(let type) = importEntry.descriptor else { continue } + let function = try Function( + store: store, + type: module.resolveFunctionType(type), + body: { _, _ in + return [] + } + ) + imports.define(importEntry, .function(function)) + } + let instance = try module.instantiate(store: store, imports: imports) + let _start = try #require(instance.exports[function: "_start"]) + + guard let trap: Trap = (#expect(throws: Trap.self) { try _start() }) else { + return + } + try assertTrap(trap) } - try assertTrap(trap) - } - func testBacktraceBasic() throws { - try expectTrap( - """ - (module - (func $foo - unreachable - ) - (func $bar - (call $foo) - ) - (func (export "_start") - (call $bar) + @Test + func backtraceBasic() throws { + try expectTrap( + """ + (module + (func $foo + unreachable + ) + (func $bar + (call $foo) + ) + (func (export "_start") + (call $bar) + ) ) - ) - """ - ) { trap in - XCTAssertEqual( - trap.backtrace?.symbols.compactMap(\.?.name), - [ - "foo", - "bar", - "_start", - ]) + """ + ) { trap in + #expect( + trap.backtrace?.symbols.compactMap(\.?.name) == [ + "foo", + "bar", + "_start", + ]) + } } - } - func testBacktraceWithImports() throws { - try expectTrap( - """ - (module - (func (import "env" "bar")) - (func - unreachable - ) - (func $bar - (call 1) - ) - (func (export "_start") - (call $bar) + @Test + func backtraceWithImports() throws { + try expectTrap( + """ + (module + (func (import "env" "bar")) + (func + unreachable + ) + (func $bar + (call 1) + ) + (func (export "_start") + (call $bar) + ) ) - ) - """ - ) { trap in - XCTAssertEqual( - trap.backtrace?.symbols.compactMap(\.?.name), - [ - "wasm function[1]", - "bar", - "_start", - ]) + """ + ) { trap in + #expect( + trap.backtrace?.symbols.compactMap(\.?.name) == [ + "wasm function[1]", + "bar", + "_start", + ]) + } } } -} + +#endif diff --git a/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift b/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift index ce03d9b7..54c19494 100644 --- a/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift +++ b/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift @@ -1,22 +1,34 @@ -import WasmKit -import WasmKitFuzzing -import XCTest +#if canImport(Testing) + import WasmKit + import WasmKitFuzzing + import Foundation + import Testing -final class FuzzTranslatorRegressionTests: XCTestCase { - func testRunAll() throws { - #if os(Android) - throw XCTSkip("Test skipped due to absolute path #filePath unavailable on emulator") - #endif - let sourceRoot = URL(fileURLWithPath: #filePath) + @Suite + struct FuzzTranslatorRegressionTests { + + struct Case: CustomStringConvertible { + let path: URL + let description: String + } + + static let sourceRoot = URL(fileURLWithPath: #filePath) .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() - let failCasesDir = - sourceRoot - .appendingPathComponent("FuzzTesting/FailCases/FuzzTranslator") - for file in try FileManager.default.contentsOfDirectory(atPath: failCasesDir.path) { - let path = failCasesDir.appendingPathComponent(file).path - print("Fuzz regression test: \(path.dropFirst(sourceRoot.path.count + 1))") - let data = try Data(contentsOf: URL(fileURLWithPath: path)) + static func failCases() throws -> [Case] { + let failCasesDir = sourceRoot.appendingPathComponent("FuzzTesting/FailCases/FuzzTranslator") + return try FileManager.default.contentsOfDirectory(atPath: failCasesDir.path).map { + let url = failCasesDir.appendingPathComponent($0) + return Case(path: url, description: String(url.path.dropFirst(sourceRoot.path.count + 1))) + } + } + + @Test( + .disabled("unable to run fuzz translator regression tests on Android due to missing files on emulator", platforms: [.android]), + arguments: try failCases() + ) + func run(test: Case) throws { + let data = try Data(contentsOf: test.path) do { try WasmKitFuzzing.fuzzInstantiation(bytes: Array(data)) } catch { @@ -24,4 +36,5 @@ final class FuzzTranslatorRegressionTests: XCTestCase { } } } -} + +#endif diff --git a/Tests/WasmKitTests/Spectest/Spectest.swift b/Tests/WasmKitTests/Spectest/Spectest.swift index f42cd9ae..7dc094a6 100644 --- a/Tests/WasmKitTests/Spectest/Spectest.swift +++ b/Tests/WasmKitTests/Spectest/Spectest.swift @@ -7,166 +7,130 @@ private func loadStringArrayFromEnvironment(_ key: String) -> [String] { ProcessInfo.processInfo.environment[key]?.split(separator: ",").map(String.init) ?? [] } -@available(macOS 11, iOS 14.0, watchOS 7.0, tvOS 14.0, *) -public func spectest( - path: [String], - include: [String]? = nil, - exclude: [String]? = nil, - verbose: Bool = false, - parallel: Bool = true, - configuration: EngineConfiguration = .init() -) async throws -> Bool { - let printVerbose = verbose - @Sendable func log(_ message: String, verbose: Bool = false) { - if !verbose || printVerbose { - fputs(message + "\n", stderr) - } +struct SpectestDiscovery { + let path: [String] + let include: [String] + let exclude: [String] + + init( + path: [String], + include: [String] = loadStringArrayFromEnvironment("WASMKIT_SPECTEST_INCLUDE"), + exclude: [String] = loadStringArrayFromEnvironment("WASMKIT_SPECTEST_EXCLUDE") + ) { + self.path = path + self.include = include + self.exclude = exclude } - @Sendable func log(_ message: String, path: String, location: Location, verbose: Bool = false) { - if !verbose || printVerbose { - let (line, _) = location.computeLineAndColumn() - fputs("\(path):\(line): " + message + "\n", stderr) - } + + func discover() throws -> [TestCase] { + return try TestCase.load(include: include, exclude: exclude, in: path) + } +} + +protocol SpectestProgressReporter { + func log(_ message: String, verbose: Bool) + func log(_ message: String, path: String, location: Location, verbose: Bool) +} + +extension SpectestProgressReporter { + func log(_ message: String, verbose: Bool = false) { + log(message, verbose: verbose) } - func percentage(_ numerator: Int, _ denominator: Int) -> String { - "\(Int(Double(numerator) / Double(denominator) * 100))%" + func log(_ message: String, path: String, location: Location, verbose: Bool = false) { + log(message, path: path, location: location, verbose: verbose) } +} - let include = include ?? loadStringArrayFromEnvironment("WASMKIT_SPECTEST_INCLUDE") - let exclude = exclude ?? loadStringArrayFromEnvironment("WASMKIT_SPECTEST_EXCLUDE") +struct NullSpectestProgressReporter: SpectestProgressReporter { + func log(_ message: String, verbose: Bool) {} + func log(_ message: String, path: String, location: Location, verbose: Bool) {} +} - let testCases: [TestCase] - do { - testCases = try TestCase.load(include: include, exclude: exclude, in: path, log: { log($0) }) - } catch { - fatalError("failed to load test: \(error)") +struct StderrSpectestProgressReporter: SpectestProgressReporter { + func log(_ message: String, verbose: Bool) { + fputs(message + "\n", stderr) } + func log(_ message: String, path: String, location: Location, verbose: Bool) { + let (line, _) = location.computeLineAndColumn() + fputs("\(path):\(line): " + message + "\n", stderr) + } +} - guard !testCases.isEmpty else { - log("No test found") - return true +struct SpectestRunner { + let hostModule: Module + let configuration: EngineConfiguration + + init(configuration: EngineConfiguration) throws { + self.configuration = configuration + // https://github.com/WebAssembly/spec/tree/8a352708cffeb71206ca49a0f743bdc57269fb1a/interpreter#spectest-host-module + hostModule = try parseWasm( + bytes: wat2wasm( + """ + (module + (global (export "global_i32") i32 (i32.const 666)) + (global (export "global_i64") i64 (i64.const 666)) + (global (export "global_f32") f32 (f32.const 666.6)) + (global (export "global_f64") f64 (f64.const 666.6)) + + (table (export "table") 10 20 funcref) + (table (export "table64") 10 20 funcref) + + (memory (export "memory") 1 2) + + (func (export "print")) + (func (export "print_i32") (param i32)) + (func (export "print_i64") (param i64)) + (func (export "print_f32") (param f32)) + (func (export "print_f64") (param f64)) + (func (export "print_i32_f32") (param i32 f32)) + (func (export "print_f64_f64") (param f64 f64)) + ) + """ + ) + ) } - // https://github.com/WebAssembly/spec/tree/8a352708cffeb71206ca49a0f743bdc57269fb1a/interpreter#spectest-host-module - let hostModule = try parseWasm( - bytes: wat2wasm( - """ - (module - (global (export "global_i32") i32 (i32.const 666)) - (global (export "global_i64") i64 (i64.const 666)) - (global (export "global_f32") f32 (f32.const 666.6)) - (global (export "global_f64") f64 (f64.const 666.6)) - - (table (export "table") 10 20 funcref) - (table (export "table64") 10 20 funcref) - - (memory (export "memory") 1 2) - - (func (export "print")) - (func (export "print_i32") (param i32)) - (func (export "print_i64") (param i64)) - (func (export "print_f32") (param f32)) - (func (export "print_f64") (param f64)) - (func (export "print_i32_f32") (param i32 f32)) - (func (export "print_f64_f64") (param f64 f64)) - ) - """)) - - @Sendable func runTestCase(testCase: TestCase) throws -> [Result] { - var testCaseResults = [Result]() + struct Failures: Error, CustomStringConvertible { + let test: TestCase + let failures: [(Location, reason: String)] + + var description: String { + return failures.map { (location, reason) in + let (line, _) = location.computeLineAndColumn() + return "\(test.relativePath):\(line): \(reason)" + }.joined(separator: "\n") + } + } + + func run(test: TestCase, reporter: SpectestProgressReporter) throws { let logDuration: () -> Void if #available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) { let start = ContinuousClock.now logDuration = { let elapsed = ContinuousClock.now - start - log("Finished \(testCase.relativePath) in \(elapsed)") + reporter.log("Finished \(test.relativePath) in \(elapsed)") } } else { // Fallback on earlier versions logDuration = {} } - log("Testing \(testCase.relativePath)") - try testCase.run(spectestModule: hostModule, configuration: configuration) { testCase, location, result in + reporter.log("Testing \(test.relativePath)") + var failures = [(Location, reason: String)]() + try test.run(spectestModule: hostModule, configuration: configuration) { test, location, result in switch result { - case let .failed(reason): - log("\(result.banner) \(reason)", path: testCase.path, location: location) - case let .skipped(reason): - log("\(result.banner) \(reason)", path: testCase.path, location: location, verbose: true) + case .failed(let reason): + reporter.log("\(result.banner) \(reason)", path: test.path, location: location) + failures.append((location, reason)) + case .skipped(let reason): + reporter.log("\(result.banner) \(reason)", path: test.path, location: location, verbose: true) case .passed: - log(result.banner, path: testCase.path, location: location, verbose: true) + reporter.log(result.banner, path: test.path, location: location, verbose: true) } - testCaseResults.append(result) } - logDuration() - return testCaseResults - } - - struct SpectestResult { - var passed = 0 - var skipped = 0 - var failed = 0 - var total: Int { passed + skipped + failed } - var failedCases: Set = [] - - mutating func append(_ testCase: TestCase, _ result: Result) { - switch result { - case .passed: - passed += 1 - case .skipped: - skipped += 1 - case .failed: - failed += 1 - failedCases.insert(testCase.path) - } - } - - func dump() { - print( - "\(passed)/\(total) (\(percentage(passed, total)) passing, \(percentage(skipped, total)) skipped, \(percentage(failed, total)) failed)" - ) - guard !failedCases.isEmpty else { return } - print("Failed cases:") - for testCase in failedCases.sorted() { - print(" \(testCase)") - } + if !failures.isEmpty { + throw Failures(test: test, failures: failures) } } - - let result: SpectestResult - - if parallel { - result = try await withThrowingTaskGroup(of: (TestCase, [Result]).self) { group in - for testCase in testCases { - group.addTask { - try await Task { - let results = try runTestCase(testCase: testCase) - return (testCase, results) - }.value - } - } - - var allResults = SpectestResult() - for try await (testCase, results) in group { - for result in results { - allResults.append(testCase, result) - } - } - return allResults - } - } else { - var results = SpectestResult() - for testCase in testCases { - let testCaseResults = try runTestCase(testCase: testCase) - for result in testCaseResults { - results.append(testCase, result) - } - } - result = results - } - - result.dump() - - return result.failed == 0 } diff --git a/Tests/WasmKitTests/Spectest/TestCase.swift b/Tests/WasmKitTests/Spectest/TestCase.swift index 2fb1809e..37c35578 100644 --- a/Tests/WasmKitTests/Spectest/TestCase.swift +++ b/Tests/WasmKitTests/Spectest/TestCase.swift @@ -5,12 +5,11 @@ import WasmParser @testable import WasmKit -struct TestCase { +struct TestCase: CustomStringConvertible { enum Error: Swift.Error { case invalidPath } - let content: Wast let path: String var relativePath: String { // Relative path from the current working directory @@ -21,7 +20,11 @@ struct TestCase { return path } - static func load(include: [String], exclude: [String], in path: [String], log: ((String) -> Void)? = nil) throws -> [TestCase] { + var description: String { + return relativePath + } + + static func load(include: [String], exclude: [String], in path: [String]) throws -> [TestCase] { let fileManager = FileManager.default var filePaths: [URL] = [] for path in path { @@ -62,13 +65,7 @@ struct TestCase { var testCases: [TestCase] = [] for filePath in filePaths where try matchesPattern(filePath) { - guard let data = fileManager.contents(atPath: filePath.path) else { - assertionFailure("failed to load \(filePath)") - continue - } - - let wast = try parseWAST(String(data: data, encoding: .utf8)!) - let spec = TestCase(content: wast, path: filePath.path) + let spec = TestCase(path: filePath.path) testCases.append(spec) } @@ -130,12 +127,16 @@ class WastRunContext { extension TestCase { func run(spectestModule: Module, configuration: EngineConfiguration, handler: @escaping (TestCase, Location, Result) -> Void) throws { + guard let data = FileManager.default.contents(atPath: path) else { + assertionFailure("failed to load \(path)") + return + } let engine = Engine(configuration: configuration) let store = Store(engine: engine) let spectestInstance = try spectestModule.instantiate(store: store) let rootPath = FilePath(path).removingLastComponent().string - var content = content + var content = try parseWAST(String(data: data, encoding: .utf8)!) let context = WastRunContext(store: store, rootPath: rootPath) context.importsSpace.define(module: "spectest", spectestInstance.exports) do { diff --git a/Tests/WasmKitTests/SpectestTests.swift b/Tests/WasmKitTests/SpectestTests.swift index 6fece81f..5734e018 100644 --- a/Tests/WasmKitTests/SpectestTests.swift +++ b/Tests/WasmKitTests/SpectestTests.swift @@ -1,52 +1,44 @@ -import WasmKit -import XCTest +#if canImport(Testing) + import Foundation + import Testing + import WasmKit -@available(macOS 11, iOS 14.0, watchOS 7.0, tvOS 14.0, *) -final class SpectestTests: XCTestCase { - static let projectDir = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() - static let testsuite = - projectDir - .appendingPathComponent("Vendor/testsuite") - static var testPaths: [String] { - [ - Self.testsuite.path, - Self.testsuite.appendingPathComponent("proposals/memory64").path, - Self.testsuite.appendingPathComponent("proposals/tail-call").path, - Self.projectDir.appendingPathComponent("Tests/WasmKitTests/ExtraSuite").path, - ] - } + @Suite + struct SpectestTests { + static let projectDir = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() + static let testsuite = + projectDir + .appendingPathComponent("Vendor/testsuite") + static var testPaths: [String] { + [ + Self.testsuite.path, + Self.testsuite.appendingPathComponent("proposals/memory64").path, + Self.testsuite.appendingPathComponent("proposals/tail-call").path, + Self.projectDir.appendingPathComponent("Tests/WasmKitTests/ExtraSuite").path, + ] + } - /// Run all the tests in the spectest suite. - func testRunAll() async throws { - #if os(Android) - throw XCTSkip("unable to run spectest on Android due to missing files on emulator") - #endif - let defaultConfig = EngineConfiguration() - let ok = try await spectest( - path: Self.testPaths, - include: [], - exclude: [], - parallel: true, - configuration: defaultConfig + @Test( + .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), + arguments: try SpectestDiscovery(path: Self.testPaths).discover() ) - XCTAssertTrue(ok) - } + func run(test: TestCase) throws { + let defaultConfig = EngineConfiguration() + let runner = try SpectestRunner(configuration: defaultConfig) + try runner.run(test: test, reporter: NullSpectestProgressReporter()) + } - func testRunAllWithTokenThreading() async throws { - #if os(Android) - throw XCTSkip("unable to run spectest on Android due to missing files on emulator") - #endif - let defaultConfig = EngineConfiguration() - guard defaultConfig.threadingModel != .token else { return } - // Sanity check that non-default threading models work. - var config = defaultConfig - config.threadingModel = .token - let ok = try await spectest( - path: Self.testPaths, - parallel: true, - configuration: config + @Test( + .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), + arguments: try SpectestDiscovery(path: Self.testPaths).discover() ) - XCTAssertTrue(ok) + func runWithTokenThreading(test: TestCase) throws { + let defaultConfig = EngineConfiguration() + guard defaultConfig.threadingModel != .token else { return } + // Sanity check that non-default threading models work. + let runner = try SpectestRunner(configuration: defaultConfig) + try runner.run(test: test, reporter: NullSpectestProgressReporter()) + } } -} +#endif diff --git a/Tests/WasmKitTests/Trait+Platform.swift b/Tests/WasmKitTests/Trait+Platform.swift new file mode 100644 index 00000000..c1b86270 --- /dev/null +++ b/Tests/WasmKitTests/Trait+Platform.swift @@ -0,0 +1,31 @@ +#if canImport(Testing) + import Testing + + enum TargetPlatform { + case android + + func isCurrentPlatform() -> Bool { + switch self { + case .android: + #if os(Android) + return true + #else + return false + #endif + } + } + } + + extension Trait where Self == ConditionTrait { + static func disabled( + _ comment: Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + platforms: [TargetPlatform] + ) -> Self { + return .disabled(comment, sourceLocation: sourceLocation) { + platforms.contains { $0.isCurrentPlatform() } + } + } + } + +#endif From 6f63290683d814bd5c42971132218ad804c1c781 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 5 Oct 2025 21:12:24 +0900 Subject: [PATCH 2/6] Tests: Migrate WATTests to use swift-testing --- Tests/WATTests/EncoderTests.swift | 250 +++++++++++----------- Tests/WATTests/LexerTests.swift | 173 ++++++++------- Tests/WATTests/ParserTests.swift | 345 +++++++++++++++--------------- Tests/WATTests/Spectest.swift | 29 ++- 4 files changed, 391 insertions(+), 406 deletions(-) diff --git a/Tests/WATTests/EncoderTests.swift b/Tests/WATTests/EncoderTests.swift index d5adb8ca..28f5ad25 100644 --- a/Tests/WATTests/EncoderTests.swift +++ b/Tests/WATTests/EncoderTests.swift @@ -1,115 +1,114 @@ -import Foundation -import WasmParser -import XCTest +#if canImport(Testing) + import Testing + import Foundation + import WasmParser -@testable import WAT + @testable import WAT -class EncoderTests: XCTestCase { + @Suite + struct EncoderTests { - struct CompatibilityTestStats { - var run: Int = 0 - var failed: Set = [] - } - - func checkWabtCompatibility( - wast: URL, json: URL, stats parentStats: inout CompatibilityTestStats - ) throws { - var stats = parentStats - defer { parentStats = stats } - func recordFail() { - stats.failed.insert(wast.lastPathComponent) + struct CompatibilityTestStats { + var run: Int = 0 + var failed: Set = [] } - func assertEqual(_ lhs: T, _ rhs: T, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(lhs, rhs, file: file, line: line) - if lhs != rhs { - recordFail() + + func checkWabtCompatibility( + wast: URL, json: URL, stats parentStats: inout CompatibilityTestStats, + sourceLocation: SourceLocation = #_sourceLocation + ) throws { + var stats = parentStats + defer { parentStats = stats } + func recordFail() { + stats.failed.insert(wast.lastPathComponent) + } + func assertEqual(_ lhs: T, _ rhs: T, sourceLocation: SourceLocation = #_sourceLocation) { + #expect(lhs == rhs, sourceLocation: sourceLocation) + 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] = [] + 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 { + while let directive = try parser.nextDirective() { + switch directive { + case .module(let moduleDirective): + watModules.append(moduleDirective) + case .assertMalformed(let module, let message): + let diagnostic: () -> Comment = { + let (line, column) = module.location.computeLineAndColumn() + return "\(wast.path):\(line):\(column) should be malformed: \(message)" + } + switch module.source { + case .text(var wat): + #expect(throws: (any Error).self, diagnostic(), sourceLocation: sourceLocation) { _ = try wat.encode() recordFail() - }(), diagnostic()) - case .quote(let bytes): - XCTAssertThrowsError( - try { + } + case .quote(let bytes): + #expect(throws: (any Error).self, diagnostic(), sourceLocation: sourceLocation) { _ = try wat2wasm(String(decoding: bytes, as: UTF8.self)) recordFail() - }(), diagnostic()) - case .binary: break + } + case .binary: break + } + default: 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) + 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(_ lhs: T, _ rhs: T, file: StaticString = #file, line: UInt = #line) { - XCTAssertEqual(lhs, rhs, moduleBinaryFile.path, file: file, line: line) - if lhs != rhs { + for (watModule, (moduleBinaryFile, expectedName)) in zip(watModules, moduleBinaryFiles) { + func assertEqual(_ lhs: T, _ rhs: T, sourceLocation: SourceLocation = #_sourceLocation) { + #expect(lhs == rhs, sourceLocation: sourceLocation) + 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() + #expect((false), "Error while encoding \(moduleBinaryFile.lastPathComponent): \(error)") + return } - } - 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)) + if moduleBytes != expectedBytes { + recordFail() + } + assertEqual(moduleBytes.count, expectedBytes.count) + if moduleBytes.count == expectedBytes.count { + assertEqual(moduleBytes, expectedBytes) } - } 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") - } + #if !(os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)) + @Test( + arguments: Spectest.wastFiles(include: [], exclude: []) + ) + func spectest(wastFile: URL) throws { + guard let wast2json = TestSupport.lookupExecutable("wast2json") else { + return // Skip the test if wast2json is not found in PATH + } - var stats = CompatibilityTestStats() - let excluded: [String] = [] - for wastFile in Spectest.wastFiles(include: [], exclude: excluded) { + var stats = CompatibilityTestStats() try TestSupport.withTemporaryDirectory { tempDir, shouldRetain in let jsonFileName = wastFile.deletingPathExtension().lastPathComponent + ".json" let json = URL(fileURLWithPath: tempDir).appendingPathComponent(jsonFileName) @@ -130,47 +129,48 @@ class EncoderTests: XCTestCase { } catch { stats.failed.insert(wastFile.lastPathComponent) shouldRetain = true - XCTFail("Error while checking compatibility between \(wastFile) and \(json.path): \(error)") + #expect((false), "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())") + + if !stats.failed.isEmpty { + #expect((false), "Failed test cases: \(stats.failed.sorted())") + } } #endif - } - func testEncodeNameSection() throws { - let bytes = try wat2wasm( - """ - (module - (func $foo) - (func) - (func $bar) + @Test + func encodeNameSection() throws { + let bytes = try wat2wasm( + """ + (module + (func $foo) + (func) + (func $bar) + ) + """, + options: EncodeOptions(nameSection: true) ) - """, - 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 + 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) } - 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 + let nameSection = customSections.first(where: { $0.name == "name" }) + let nameParser = NameSectionParser( + stream: StaticByteStream(bytes: nameSection?.bytes ?? []) + ) + let names = try nameParser.parseAll() + #expect(names.count == 1) + guard case .functions(let functionNames) = try #require(names.first) else { + #expect((false), "Expected functions name section") + return + } + #expect(functionNames == [0: "foo", 2: "bar"]) } - XCTAssertEqual(functionNames, [0: "foo", 2: "bar"]) } -} +#endif diff --git a/Tests/WATTests/LexerTests.swift b/Tests/WATTests/LexerTests.swift index ac987110..440b4f98 100644 --- a/Tests/WATTests/LexerTests.swift +++ b/Tests/WATTests/LexerTests.swift @@ -1,107 +1,104 @@ -import Foundation -import WasmParser -import XCTest +#if canImport(Testing) + import Testing + import Foundation + import WasmParser -@testable import WAT + @testable import WAT -class LexerTests: XCTestCase { - func collectToken(_ source: String) throws -> [TokenKind] { - var lexer = Lexer(input: source) - var tokens: [TokenKind] = [] - while let token = try lexer.rawLex() { - tokens.append(token.kind) + @Suite + struct LexerTests { + func collectToken(_ source: String) throws -> [TokenKind] { + var lexer = Lexer(input: source) + var tokens: [TokenKind] = [] + while let token = try lexer.rawLex() { + tokens.append(token.kind) + } + return tokens } - return tokens - } - func testLexBasics() { - try XCTAssertEqual(collectToken(""), []) - try XCTAssertEqual(collectToken("(module"), [.leftParen, .keyword]) - try XCTAssertEqual(collectToken("( module"), [.leftParen, .keyword]) - try XCTAssertEqual(collectToken("(\tmodule"), [.leftParen, .keyword]) - try XCTAssertEqual(collectToken("(\nmodule"), [.leftParen, .keyword]) - try XCTAssertEqual(collectToken("(module)"), [.leftParen, .keyword, .rightParen]) + @Test + func lexBasics() throws { + #expect(try collectToken("") == []) + #expect(try collectToken("(module") == [.leftParen, .keyword]) + #expect(try collectToken("( module") == [.leftParen, .keyword]) + #expect(try collectToken("(\tmodule") == [.leftParen, .keyword]) + #expect(try collectToken("(\nmodule") == [.leftParen, .keyword]) + #expect(try collectToken("(module)") == [.leftParen, .keyword, .rightParen]) - } + } - func testLexComment() { - XCTAssertEqual(try collectToken("(; foo ;)"), [.blockComment]) - try XCTAssertEqual( - collectToken( - """ - (; - multi-line comment - ;) - """ - ), - [.blockComment] - ) - try XCTAssertEqual(collectToken(";; foo"), [.lineComment]) - try XCTAssertEqual(collectToken(";; foo\n(bar"), [.lineComment, .leftParen, .keyword]) + @Test + func testLexComment() throws { + #expect(try collectToken("(; foo ;)") == [.blockComment]) + #expect( + try collectToken( + """ + (; + multi-line comment + ;) + """ + ) == [.blockComment] + ) + #expect(try collectToken(";; foo") == [.lineComment]) + #expect(try collectToken(";; foo\n(bar") == [.lineComment, .leftParen, .keyword]) - } + } - func testLexBrokenComment() { - XCTAssertThrowsError(try collectToken("(;)")) - XCTAssertThrowsError(try collectToken("(; foo )")) - XCTAssertThrowsError(try collectToken(";)")) - } + @Test + func lexBrokenComment() throws { + #expect(throws: (any Error).self) { try collectToken("(;)") } + #expect(throws: (any Error).self) { try collectToken("(; foo )") } + #expect(throws: (any Error).self) { try collectToken(";)") } + } - func testLexIdAndString() throws { - try XCTAssertEqual(collectToken("$foo"), [.id]) - try XCTAssertEqual(collectToken("\"foo\""), [.string(Array("foo".utf8))]) - try XCTAssertEqual(collectToken("\"\\t\\n\\r\\\"\\\\\""), [.string(Array("\t\n\r\"\\".utf8))]) - try XCTAssertEqual(collectToken("\"\\u{1F600}\""), [.string(Array("😀".utf8))]) - try XCTAssertEqual(collectToken("$\"foo\""), [.id]) - try XCTAssertEqual(collectToken("0$x"), [.unknown]) - } + @Test + func lexIdAndString() throws { + #expect(try collectToken("$foo") == [.id]) + #expect(try collectToken("\"foo\"") == [.string(Array("foo".utf8))]) + #expect(try collectToken("\"\\t\\n\\r\\\"\\\\\"") == [.string(Array("\t\n\r\"\\".utf8))]) + #expect(try collectToken("\"\\u{1F600}\"") == [.string(Array("😀".utf8))]) + #expect(try collectToken("$\"foo\"") == [.id]) + #expect(try collectToken("0$x") == [.unknown]) + } - func testLexInteger() throws { - try XCTAssertEqual(collectToken("inf"), [.float(nil, .inf)]) - try XCTAssertEqual(collectToken("+inf"), [.float(.plus, .inf)]) - try XCTAssertEqual(collectToken("-inf"), [.float(.minus, .inf)]) - try XCTAssertEqual(collectToken("nan"), [.float(nil, .nan(hexPattern: nil))]) - try XCTAssertEqual(collectToken("+nan"), [.float(.plus, .nan(hexPattern: nil))]) - try XCTAssertEqual(collectToken("-nan"), [.float(.minus, .nan(hexPattern: nil))]) - try XCTAssertEqual(collectToken("nan:0x7f_ffff"), [.float(nil, .nan(hexPattern: "7fffff"))]) - try XCTAssertEqual(collectToken("3.14"), [.float(nil, .decimalPattern("3.14"))]) - try XCTAssertEqual(collectToken("1e+07"), [.float(nil, .decimalPattern("1e+07"))]) - try XCTAssertEqual(collectToken("1E+07"), [.float(nil, .decimalPattern("1E+07"))]) - try XCTAssertEqual(collectToken("0xff"), [.integer(nil, .hexPattern("ff"))]) - try XCTAssertEqual(collectToken("8_128"), [.integer(nil, .decimalPattern("8128"))]) - try XCTAssertEqual(collectToken("1.e10"), [.float(nil, .decimalPattern("1.e10"))]) - } + @Test + func lexInteger() throws { + #expect(try collectToken("inf") == [.float(nil, .inf)]) + #expect(try collectToken("+inf") == [.float(.plus, .inf)]) + #expect(try collectToken("-inf") == [.float(.minus, .inf)]) + #expect(try collectToken("nan") == [.float(nil, .nan(hexPattern: nil))]) + #expect(try collectToken("+nan") == [.float(.plus, .nan(hexPattern: nil))]) + #expect(try collectToken("-nan") == [.float(.minus, .nan(hexPattern: nil))]) + #expect(try collectToken("nan:0x7f_ffff") == [.float(nil, .nan(hexPattern: "7fffff"))]) + #expect(try collectToken("3.14") == [.float(nil, .decimalPattern("3.14"))]) + #expect(try collectToken("1e+07") == [.float(nil, .decimalPattern("1e+07"))]) + #expect(try collectToken("1E+07") == [.float(nil, .decimalPattern("1E+07"))]) + #expect(try collectToken("0xff") == [.integer(nil, .hexPattern("ff"))]) + #expect(try collectToken("8_128") == [.integer(nil, .decimalPattern("8128"))]) + #expect(try collectToken("1.e10") == [.float(nil, .decimalPattern("1.e10"))]) + } - func testLexFloatLiteral() throws { - try XCTAssertEqual(collectToken("nan:canonical"), [.keyword]) - try XCTAssertEqual(collectToken("0x1.921fb6p+2"), [.float(nil, .hexPattern("1.921fb6p+2"))]) - } + @Test + func lexFloatLiteral() throws { + #expect(try collectToken("nan:canonical") == [.keyword]) + #expect(try collectToken("0x1.921fb6p+2") == [.float(nil, .hexPattern("1.921fb6p+2"))]) + } - func testLexMemory() throws { - try XCTAssertEqual(collectToken("(module (memory 1))"), [.leftParen, .keyword, .leftParen, .keyword, .integer(nil, .decimalPattern("1")), .rightParen, .rightParen]) - } + @Test + func lexMemory() throws { + #expect(try collectToken("(module (memory 1))") == [.leftParen, .keyword, .leftParen, .keyword, .integer(nil, .decimalPattern("1")), .rightParen, .rightParen]) + } - func testLexSpectest() throws { // NOTE: We do the same check as a part of the EncoderTests, so it's // usually redundant and time-wasting to run this test every time. // Keeping it here just for local unit testing purposes. - try XCTSkipIf( - ProcessInfo.processInfo.environment["WASMKIT_LEXER_SPECTEST"] != "1" + @Test( + .enabled(if: ProcessInfo.processInfo.environment["WASMKIT_PARSER_SPECTEST"] == "1"), + arguments: Spectest.wastFiles(include: []) ) - var failureCount = 0 - for filePath in Spectest.wastFiles() { - print("Lexing \(filePath.path)...") - let source = try String(contentsOf: filePath) - do { - _ = try collectToken(source) - } catch { - failureCount += 1 - XCTFail("Failed to lex \(filePath.path):\(error)") - } - } - - if failureCount > 0 { - XCTFail("Failed to lex \(failureCount) files") + func lexSpectest(wastFile: URL) throws { + let source = try String(contentsOf: wastFile) + _ = try collectToken(source) } } -} +#endif diff --git a/Tests/WATTests/ParserTests.swift b/Tests/WATTests/ParserTests.swift index dd3d638c..a6498bb2 100644 --- a/Tests/WATTests/ParserTests.swift +++ b/Tests/WATTests/ParserTests.swift @@ -1,205 +1,198 @@ -import Foundation -import WasmParser -import XCTest - -@testable import WAT - -class ParserTests: XCTestCase { - func parseWast(_ source: String, features: WasmFeatureSet = .default) throws -> [WastDirective] { - var parser = WastParser(source, features: features) - var directives: [WastDirective] = [] - while let directive = try parser.nextDirective() { - directives.append(directive) +#if canImport(Testing) + import Testing + import Foundation + import WasmParser + + @testable import WAT + + @Suite + struct ParserTests { + func parseWast(_ source: String, features: WasmFeatureSet = .default) throws -> [WastDirective] { + var parser = WastParser(source, features: features) + var directives: [WastDirective] = [] + while let directive = try parser.nextDirective() { + directives.append(directive) + } + return directives } - return directives - } - func parseModule(_ source: String) throws -> ModuleDirective? { - let directives = try parseWast(source) - guard case let .module(moduleDirective) = directives.first else { - XCTFail("Expected module directive") - return nil + func parseModule(_ source: String) throws -> ModuleDirective? { + let directives = try parseWast(source) + guard case let .module(moduleDirective) = directives.first else { + #expect((false), "Expected module directive") + return nil + } + return moduleDirective } - return moduleDirective - } - func parseBinaryModule(_ source: String) throws -> (source: [UInt8], id: String?)? { - guard let module = try parseModule(source) else { return nil } - guard case let .binary(content) = module.source else { return nil } - return (content, module.id) - } + func parseBinaryModule(_ source: String) throws -> (source: [UInt8], id: String?)? { + guard let module = try parseModule(source) else { return nil } + guard case let .binary(content) = module.source else { return nil } + return (content, module.id) + } - func testParseWastBinaryModule() throws { - try XCTAssertEqual( - parseBinaryModule(#"(module binary "\00asm\01\00\00\00")"#)?.source, - [0, 97, 115, 109, 1, 0, 0, 0] - ) - try XCTAssertEqual( - parseBinaryModule( - #""" - (module binary - "\00asm" "\01\00\00\00" - ;; comment between strings - "foo" - ) - """#)?.source, - [0, 97, 115, 109, 1, 0, 0, 0, 102, 111, 111] - ) + @Test + func parseWastBinaryModule() throws { + #expect( + try parseBinaryModule(#"(module binary "\00asm\01\00\00\00")"#)?.source == [0, 97, 115, 109, 1, 0, 0, 0] + ) + #expect( + try parseBinaryModule( + #""" + (module binary + "\00asm" "\01\00\00\00" + ;; comment between strings + "foo" + ) + """#)?.source == [0, 97, 115, 109, 1, 0, 0, 0, 102, 111, 111] + ) - do { - let m1 = try parseBinaryModule(#"(module $M1 binary "\00asm\01\00\00\00")"#) - XCTAssertEqual(m1?.id, "$M1") - XCTAssertEqual(m1?.source, [0, 97, 115, 109, 1, 0, 0, 0]) + do { + let m1 = try parseBinaryModule(#"(module $M1 binary "\00asm\01\00\00\00")"#) + #expect(m1?.id == "$M1") + #expect(m1?.source == [0, 97, 115, 109, 1, 0, 0, 0]) + } } - } - func testParseWastModule() throws { - var parser = WastParser( - #""" - (module - ;; comment here - (memory 1) + @Test + func parseWastModule() throws { + var parser = WastParser( + #""" + (module + ;; comment here + (memory 1) - (func $dummy) + (func $dummy) - (func (export "empty") - (unknown expr) - ) - ) - """#, features: .default) - - while let directive = try parser.nextDirective() { - switch directive { - case .module(let directive): - guard case .text(_) = directive.source else { - XCTFail("Expected text module field") - return + (func (export "empty") + (unknown expr) + ) + ) + """#, features: .default) + + while let directive = try parser.nextDirective() { + switch directive { + case .module(let directive): + guard case .text(_) = directive.source else { + #expect((false), "Expected text module field") + return + } + case _: + #expect((false), "Expected only module directive") } - case _: - XCTFail("Expected only module directive") } } - } - func testParseWastModuleSkip() throws { - let directives = try parseWast( - #""" - (module - ;; comment here - (memory 1) - - (func $dummy) + @Test + func parseWastModuleSkip() throws { + let directives = try parseWast( + #""" + (module + ;; comment here + (memory 1) - (func (export "empty") - (unknown expr) - ) - ) - (module binary "ok") - """#) - - XCTAssertEqual(directives.count, 2) - guard case let .module(directive) = try XCTUnwrap(directives.last), - case let .binary(content) = directive.source - else { - return - } - XCTAssertEqual(content, Array("ok".utf8)) - } + (func $dummy) - func testSpecForward() throws { - let source = """ - (module - (func $even (export "even") (param $n i32) (result i32) - (if (result i32) (i32.eq (local.get $n) (i32.const 0)) - (then (i32.const 1)) - (else (call $odd (i32.sub (local.get $n) (i32.const 1)))) + (func (export "empty") + (unknown expr) + ) ) - ) + (module binary "ok") + """#) + + #expect(directives.count == 2) + guard case let .module(directive) = try #require(directives.last), + case let .binary(content) = directive.source + else { + return + } + #expect(content == Array("ok".utf8)) + } - (func $odd (export "odd") (param $n i32) (result i32) - (if (result i32) (i32.eq (local.get $n) (i32.const 0)) - (then (i32.const 0)) - (else (call $even (i32.sub (local.get $n) (i32.const 1)))) + @Test + func specForward() throws { + let source = """ + (module + (func $even (export "even") (param $n i32) (result i32) + (if (result i32) (i32.eq (local.get $n) (i32.const 0)) + (then (i32.const 1)) + (else (call $odd (i32.sub (local.get $n) (i32.const 1)))) + ) + ) + + (func $odd (export "odd") (param $n i32) (result i32) + (if (result i32) (i32.eq (local.get $n) (i32.const 0)) + (then (i32.const 0)) + (else (call $even (i32.sub (local.get $n) (i32.const 1)))) + ) + ) ) - ) - ) - (assert_return (invoke "even" (i32.const 13)) (i32.const 0)) - (assert_return (invoke "even" (i32.const 20)) (i32.const 1)) - (assert_return (invoke "odd" (i32.const 13)) (i32.const 1)) - (assert_return (invoke "odd" (i32.const 20)) (i32.const 0)) - """ - let wast = try parseWast(source) - XCTAssertEqual(wast.count, 5) - guard case let .module(module) = wast.first, case var .text(wat) = module.source else { - XCTFail("expect a module directive") - return + (assert_return (invoke "even" (i32.const 13)) (i32.const 0)) + (assert_return (invoke "even" (i32.const 20)) (i32.const 1)) + (assert_return (invoke "odd" (i32.const 13)) (i32.const 1)) + (assert_return (invoke "odd" (i32.const 20)) (i32.const 0)) + """ + let wast = try parseWast(source) + #expect(wast.count == 5) + guard case let .module(module) = wast.first, case var .text(wat) = module.source else { + #expect((false), "expect a module directive") + return + } + #expect(wat.functionsMap.count == 2) + let even = wat.functionsMap[0] + let (evenType, _) = try wat.types.resolve(use: even.typeUse) + #expect(evenType.signature.parameters == [.i32]) + #expect(evenType.signature.results.first == .i32) + #expect(evenType.parameterNames.map(\.?.value) == ["$n"]) } - XCTAssertEqual(wat.functionsMap.count, 2) - let even = wat.functionsMap[0] - let (evenType, _) = try wat.types.resolve(use: even.typeUse) - XCTAssertEqual(evenType.signature.parameters, [.i32]) - XCTAssertEqual(evenType.signature.results.first, .i32) - XCTAssertEqual(evenType.parameterNames.map(\.?.value), ["$n"]) - } - func testFuncIdBinding() throws { - let source = """ - (module - (table $t 10 funcref) - (func $f) - (func $g) - - ;; Passive - (elem funcref) - (elem funcref (ref.func $f) (item ref.func $f) (item (ref.null func)) (ref.func $g)) - (elem func) - (elem func $f $f $g $g) - - (elem $p1 funcref) - (elem $p2 funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) - (elem $p3 func) - (elem $p4 func $f $f $g $g) - - ;; Active - (elem (table $t) (i32.const 0) funcref) - (elem (table $t) (i32.const 0) funcref (ref.func $f) (ref.null func)) - (elem (table $t) (i32.const 0) func) - (elem (table $t) (i32.const 0) func $f $g) - (elem (table $t) (offset (i32.const 0)) funcref) - (elem (table $t) (offset (i32.const 0)) func $f $g) - ) - """ - let wat = try parseWAT(source) - XCTAssertEqual(wat.tables.count, 1) - let table = wat.tables[0] - XCTAssertEqual(table.type, TableType(elementType: .funcRef, limits: Limits(min: 10, max: nil))) - XCTAssertEqual(wat.elementsMap.count, 14) - } + @Test + func funcIdBinding() throws { + let source = """ + (module + (table $t 10 funcref) + (func $f) + (func $g) + + ;; Passive + (elem funcref) + (elem funcref (ref.func $f) (item ref.func $f) (item (ref.null func)) (ref.func $g)) + (elem func) + (elem func $f $f $g $g) + + (elem $p1 funcref) + (elem $p2 funcref (ref.func $f) (ref.func $f) (ref.null func) (ref.func $g)) + (elem $p3 func) + (elem $p4 func $f $f $g $g) + + ;; Active + (elem (table $t) (i32.const 0) funcref) + (elem (table $t) (i32.const 0) funcref (ref.func $f) (ref.null func)) + (elem (table $t) (i32.const 0) func) + (elem (table $t) (i32.const 0) func $f $g) + (elem (table $t) (offset (i32.const 0)) funcref) + (elem (table $t) (offset (i32.const 0)) func $f $g) + ) + """ + let wat = try parseWAT(source) + #expect(wat.tables.count == 1) + let table = wat.tables[0] + #expect(table.type == TableType(elementType: .funcRef, limits: Limits(min: 10, max: nil))) + #expect(wat.elementsMap.count == 14) + } - func testParseSpectest() throws { // NOTE: We do the same check as a part of the EncoderTests, so it's // usually redundant and time-wasting to run this test every time. // Keeping it here just for local unit testing purposes. - try XCTSkipIf( - ProcessInfo.processInfo.environment["WASMKIT_PARSER_SPECTEST"] != "1" + @Test( + .enabled(if: ProcessInfo.processInfo.environment["WASMKIT_PARSER_SPECTEST"] == "1"), + arguments: Spectest.wastFiles(include: []) ) - var failureCount = 0 - var totalCount = 0 - for filePath in Spectest.wastFiles(include: []) { - print("Parsing \(filePath.path)...") - totalCount += 1 - let source = try String(contentsOf: filePath) - do { - _ = try parseWast(source, features: Spectest.deriveFeatureSet(wast: filePath)) - } catch { - failureCount += 1 - XCTFail("Failed to parse \(filePath.path):\(error)") - } - } - - if failureCount > 0 { - XCTFail("Failed to parse \(failureCount) / \(totalCount) files") + func parseSpectest(wastFile: URL) throws { + let source = try String(contentsOf: wastFile) + _ = try parseWast(source, features: Spectest.deriveFeatureSet(wast: wastFile)) } } -} + +#endif diff --git a/Tests/WATTests/Spectest.swift b/Tests/WATTests/Spectest.swift index bc743ecf..65fe7bdf 100644 --- a/Tests/WATTests/Spectest.swift +++ b/Tests/WATTests/Spectest.swift @@ -16,30 +16,25 @@ enum Spectest { testsuitePath.appendingPathComponent(file) } - static func wastFiles(include: [String] = [], exclude: [String] = ["annotations.wast"]) -> AnyIterator { - var allFiles = [ + static func wastFiles(include: [String] = [], exclude: [String] = ["annotations.wast"]) -> [URL] { + return [ testsuitePath, testsuitePath.appendingPathComponent("proposals/memory64"), testsuitePath.appendingPathComponent("proposals/tail-call"), rootDirectory.appendingPathComponent("Tests/WasmKitTests/ExtraSuite"), ].flatMap { try! FileManager.default.contentsOfDirectory(at: $0, includingPropertiesForKeys: nil) - }.makeIterator() - - return AnyIterator { - while let filePath = allFiles.next() { - guard filePath.pathExtension == "wast" else { - continue - } - guard !filePath.lastPathComponent.starts(with: "simd_") else { continue } - if !include.isEmpty { - guard include.contains(filePath.lastPathComponent) else { continue } - } else { - guard !exclude.contains(filePath.lastPathComponent) else { continue } - } - return filePath + }.compactMap { filePath in + guard filePath.pathExtension == "wast" else { + return nil + } + guard !filePath.lastPathComponent.starts(with: "simd_") else { return nil } + if !include.isEmpty { + guard include.contains(filePath.lastPathComponent) else { return nil } + } else { + guard !exclude.contains(filePath.lastPathComponent) else { return nil } } - return nil + return filePath } } From 9d787bd1f860972157cd3e62dcfc46011e2a26b8 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 5 Oct 2025 21:15:26 +0900 Subject: [PATCH 3/6] Tests: Fix 6.0 swift-testing compatibility --- Tests/WasmKitTests/ExecutionTests.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Tests/WasmKitTests/ExecutionTests.swift b/Tests/WasmKitTests/ExecutionTests.swift index e961c101..e5b61c87 100644 --- a/Tests/WasmKitTests/ExecutionTests.swift +++ b/Tests/WasmKitTests/ExecutionTests.swift @@ -79,7 +79,15 @@ let instance = try module.instantiate(store: store, imports: imports) let _start = try #require(instance.exports[function: "_start"]) - guard let trap: Trap = (#expect(throws: Trap.self) { try _start() }) else { + let trap: Trap + do { + let _ = try _start() + #expect((false), "Expected trap") + return + } catch let _trap as Trap { + trap = _trap + } catch { + #expect((false), "Expected trap: \(error)") return } try assertTrap(trap) From e7c68fc286faf1eb9697d10ae8f3057369d34a9a Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 5 Oct 2025 21:21:48 +0900 Subject: [PATCH 4/6] Fix 6.1 swift-testing compatibility --- Tests/WasmKitTests/SpectestTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/WasmKitTests/SpectestTests.swift b/Tests/WasmKitTests/SpectestTests.swift index 5734e018..f4cd5bc8 100644 --- a/Tests/WasmKitTests/SpectestTests.swift +++ b/Tests/WasmKitTests/SpectestTests.swift @@ -21,7 +21,7 @@ @Test( .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), - arguments: try SpectestDiscovery(path: Self.testPaths).discover() + arguments: try SpectestDiscovery(path: SpectestTests.testPaths).discover() ) func run(test: TestCase) throws { let defaultConfig = EngineConfiguration() @@ -31,7 +31,7 @@ @Test( .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), - arguments: try SpectestDiscovery(path: Self.testPaths).discover() + arguments: try SpectestDiscovery(path: SpectestTests.testPaths).discover() ) func runWithTokenThreading(test: TestCase) throws { let defaultConfig = EngineConfiguration() From fe7c7003238a0d23eb4a392d2733e8fbfd0f5193 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 5 Oct 2025 21:32:11 +0900 Subject: [PATCH 5/6] Tests: Migrate WASITests to use swift-testing --- Tests/WASITests/IntegrationTests.swift | 393 +++++++++--------- .../SandboxPrimitives/OpenParentTests.swift | 65 +-- .../RandomBufferGeneratorTests.swift | 61 +-- Tests/WASITests/WASITests.swift | 266 ++++++------ 4 files changed, 393 insertions(+), 392 deletions(-) diff --git a/Tests/WASITests/IntegrationTests.swift b/Tests/WASITests/IntegrationTests.swift index 0ed174cc..c4aba1ad 100644 --- a/Tests/WASITests/IntegrationTests.swift +++ b/Tests/WASITests/IntegrationTests.swift @@ -1,224 +1,215 @@ -import Foundation -import SystemPackage -import WasmKit -import WasmKitWASI -import XCTest - -final class IntegrationTests: XCTestCase { - func testRunAll() throws { - #if os(Android) - throw XCTSkip("unable to run spectest on Android due to missing files on emulator") +#if canImport(Testing) + + import Foundation + import SystemPackage + import WasmKit + import WasmKitWASI + import Testing + + @Suite + struct IntegrationTests { + + #if !os(Android) + @Test( + arguments: try IntegrationTests.discoverAllTests() + ) + func run(test: URL) throws { + try runTest(path: test) + } #endif - let testDir = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() - .appendingPathComponent("Vendor/wasi-testsuite") - var failedTests = [FailedTest]() - for testSuitePath in ["tests/assemblyscript/testsuite", "tests/c/testsuite", "tests/rust/testsuite"] { - let suitePath = testDir.appendingPathComponent(testSuitePath) - failedTests.append(contentsOf: try runTestSuite(path: suitePath)) - } - if !failedTests.isEmpty { - XCTFail("Failed tests: \(failedTests.map { "\($0.suite)/\($0.name)" }.joined(separator: ", "))") - - if ProcessInfo.processInfo.environment["WASMKIT_WASI_DUMP_SKIPS"] != nil { - var itemsToSkip = [String: [String: String]]() - for test in failedTests { - itemsToSkip[test.suite, default: [:]][test.name] = "Not implemented" - } - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - print(String(data: try encoder.encode(itemsToSkip), encoding: .utf8)!) - } + struct FailedTest { + let suite: String + let name: String + let path: URL + let reason: String + } + struct SuiteManifest: Codable { + let name: String } - } - - struct FailedTest { - let suite: String - let name: String - let path: URL - let reason: String - } - struct SuiteManifest: Codable { - let name: String - } - static var skipTests: [String: Set] { - #if os(Windows) - return [ - "WASI Assemblyscript tests": [], - "WASI C tests": [ - "fdopendir-with-access", - "fopen-with-access", - "lseek", - "pread-with-access", - "pwrite-with-access", - "pwrite-with-append", - "sock_shutdown-invalid_fd", - "sock_shutdown-not_sock", - "stat-dev-ino", - ], - "WASI Rust tests": [ - "close_preopen", - "dangling_fd", - "dangling_symlink", - "dir_fd_op_failures", - "directory_seek", - "fd_advise", - "fd_fdstat_set_rights", - "fd_filestat_set", - "fd_flags_set", - "fd_readdir", - "file_allocate", - "file_pread_pwrite", - "file_seek_tell", - "file_truncation", - "file_unbuffered_write", - "fstflags_validate", - "interesting_paths", - "isatty", - "nofollow_errors", - "overwrite_preopen", - "path_exists", - "path_filestat", - "path_link", - "path_open_create_existing", - "path_open_dirfd_not_dir", - "path_open_missing", - "path_open_nonblock", - "path_open_preopen", - "path_open_read_write", - "path_rename", - "path_rename_dir_trailing_slashes", - "path_symlink_trailing_slashes", - "poll_oneoff_stdio", - "readlink", - "remove_directory_trailing_slashes", - "remove_nonempty_directory", - "renumber", - "sched_yield", - "stdio", - "symlink_create", - "symlink_filestat", - "symlink_loop", - "truncation_rights", - "unlink_file_trailing_slashes", - ], - ] - #else - var tests: [String: Set] = [ - "WASI Rust tests": [ - "path_link", - "dir_fd_op_failures", - "pwrite-with-append", - "poll_oneoff_stdio", - "overwrite_preopen", - "path_filestat", - "renumber", - "symlink_filestat", - "path_open_read_write", - "path_open_preopen", - "fd_fdstat_set_rights", - "file_allocate", - "stdio", - "remove_directory_trailing_slashes", - "symlink_create", - "readlink", - "sched_yield", - ], - "WASI C tests": [ - "sock_shutdown-invalid_fd", - "sock_shutdown-not_sock", - ], - ] - #if os(Linux) - tests["WASI C tests"]?.insert("pwrite-with-append") + static var skipTests: [String: Set] { + #if os(Windows) + return [ + "WASI Assemblyscript tests": [], + "WASI C tests": [ + "fdopendir-with-access", + "fopen-with-access", + "lseek", + "pread-with-access", + "pwrite-with-access", + "pwrite-with-append", + "sock_shutdown-invalid_fd", + "sock_shutdown-not_sock", + "stat-dev-ino", + ], + "WASI Rust tests": [ + "close_preopen", + "dangling_fd", + "dangling_symlink", + "dir_fd_op_failures", + "directory_seek", + "fd_advise", + "fd_fdstat_set_rights", + "fd_filestat_set", + "fd_flags_set", + "fd_readdir", + "file_allocate", + "file_pread_pwrite", + "file_seek_tell", + "file_truncation", + "file_unbuffered_write", + "fstflags_validate", + "interesting_paths", + "isatty", + "nofollow_errors", + "overwrite_preopen", + "path_exists", + "path_filestat", + "path_link", + "path_open_create_existing", + "path_open_dirfd_not_dir", + "path_open_missing", + "path_open_nonblock", + "path_open_preopen", + "path_open_read_write", + "path_rename", + "path_rename_dir_trailing_slashes", + "path_symlink_trailing_slashes", + "poll_oneoff_stdio", + "readlink", + "remove_directory_trailing_slashes", + "remove_nonempty_directory", + "renumber", + "sched_yield", + "stdio", + "symlink_create", + "symlink_filestat", + "symlink_loop", + "truncation_rights", + "unlink_file_trailing_slashes", + ], + ] + #else + var tests: [String: Set] = [ + "WASI Rust tests": [ + "path_link", + "dir_fd_op_failures", + "pwrite-with-append", + "poll_oneoff_stdio", + "overwrite_preopen", + "path_filestat", + "renumber", + "symlink_filestat", + "path_open_read_write", + "path_open_preopen", + "fd_fdstat_set_rights", + "file_allocate", + "stdio", + "remove_directory_trailing_slashes", + "symlink_create", + "readlink", + "sched_yield", + ], + "WASI C tests": [ + "sock_shutdown-invalid_fd", + "sock_shutdown-not_sock", + ], + ] + #if os(Linux) + tests["WASI C tests"]?.insert("pwrite-with-append") + #endif + return tests #endif + } + + static func discoverAllTests() throws -> [URL] { + let testDir = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() + .appendingPathComponent("Vendor/wasi-testsuite") + var tests = [URL]() + for testSuitePath in ["tests/assemblyscript/testsuite", "tests/c/testsuite", "tests/rust/testsuite"] { + let suitePath = testDir.appendingPathComponent(testSuitePath) + tests.append(contentsOf: try discoverTestsFromSuite(path: suitePath)) + } return tests - #endif - } + } - func runTestSuite(path: URL) throws -> [FailedTest] { - let manifestPath = path.appendingPathComponent("manifest.json") - let manifest = try JSONDecoder().decode(SuiteManifest.self, from: Data(contentsOf: manifestPath)) + static func discoverTestsFromSuite(path: URL) throws -> [URL] { + let manifestPath = path.appendingPathComponent("manifest.json") + let manifest = try JSONDecoder().decode(SuiteManifest.self, from: Data(contentsOf: manifestPath)) - // Clean up **/*.cleanup - do { - let enumerator = FileManager.default.enumerator(at: path, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles])! - for case let url as URL in enumerator { - if url.pathExtension == "cleanup" { - try FileManager.default.removeItem(at: url) + // Clean up **/*.cleanup + do { + let enumerator = FileManager.default.enumerator(at: path, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles])! + for case let url as URL in enumerator { + if url.pathExtension == "cleanup" { + try FileManager.default.removeItem(at: url) + } } } - } - print("Running test suite: \(manifest.name)") - let tests = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: nil, options: []) + let tests = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: nil, options: []) - let skipTests = Self.skipTests[manifest.name] ?? [] + let skipTests = Self.skipTests[manifest.name] ?? [] - var failedTests = [FailedTest]() - for test in tests { - guard test.pathExtension == "wasm" else { continue } - let testName = test.deletingPathExtension().lastPathComponent - if skipTests.contains(testName) { - print("Skipping test \(testName)") - continue - } - do { - try runTest(path: test) - } catch { - failedTests.append(FailedTest(suite: manifest.name, name: testName, path: test, reason: String(describing: error))) + var testCases = [URL]() + for test in tests { + guard test.pathExtension == "wasm" else { continue } + let testName = test.deletingPathExtension().lastPathComponent + if skipTests.contains(testName) { + continue + } + testCases.append(test) } + return testCases } - return failedTests - } - - struct CaseManifest: Codable { - var dirs: [String]? - var args: [String]? - var env: [String: String]? - var exitCode: UInt32? - static var empty: CaseManifest { - CaseManifest(dirs: nil, args: nil, env: nil, exitCode: nil) - } - } + struct CaseManifest: Codable { + var dirs: [String]? + var args: [String]? + var env: [String: String]? + var exitCode: UInt32? - func runTest(path: URL) throws { - let manifestPath = path.deletingPathExtension().appendingPathExtension("json") - var manifest: CaseManifest - if FileManager.default.fileExists(atPath: manifestPath.path) { - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - manifest = try decoder.decode(CaseManifest.self, from: Data(contentsOf: manifestPath)) - } else { - manifest = .empty + static var empty: CaseManifest { + CaseManifest(dirs: nil, args: nil, env: nil, exitCode: nil) + } } - // HACK: WasmKit intentionally does not support fd_allocate - if path.lastPathComponent == "fd_advise.wasm" { - manifest.env = (manifest.env ?? [:]).merging(["NO_FD_ALLOCATE": "1"]) { _, new in new } - } + func runTest(path: URL) throws { + let manifestPath = path.deletingPathExtension().appendingPathExtension("json") + var manifest: CaseManifest + if FileManager.default.fileExists(atPath: manifestPath.path) { + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + manifest = try decoder.decode(CaseManifest.self, from: Data(contentsOf: manifestPath)) + } else { + manifest = .empty + } - let suitePath = path.deletingLastPathComponent() + // HACK: WasmKit intentionally does not support fd_allocate + if path.lastPathComponent == "fd_advise.wasm" { + manifest.env = (manifest.env ?? [:]).merging(["NO_FD_ALLOCATE": "1"]) { _, new in new } + } - print("Testing \(path.path)") + let suitePath = path.deletingLastPathComponent() - let wasi = try WASIBridgeToHost( - args: [path.path] + (manifest.args ?? []), - environment: manifest.env ?? [:], - preopens: (manifest.dirs ?? []).reduce(into: [String: String]()) { - $0[$1] = suitePath.appendingPathComponent($1).path - } - ) - let engine = Engine() - let store = Store(engine: engine) - var imports = Imports() - wasi.link(to: &imports, store: store) - let module = try parseWasm(filePath: FilePath(path.path)) - let instance = try module.instantiate(store: store, imports: imports) - let exitCode = try wasi.start(instance) - XCTAssertEqual(exitCode, manifest.exitCode ?? 0, path.path) + let wasi = try WASIBridgeToHost( + args: [path.path] + (manifest.args ?? []), + environment: manifest.env ?? [:], + preopens: (manifest.dirs ?? []).reduce(into: [String: String]()) { + $0[$1] = suitePath.appendingPathComponent($1).path + } + ) + let engine = Engine() + let store = Store(engine: engine) + var imports = Imports() + wasi.link(to: &imports, store: store) + let module = try parseWasm(filePath: FilePath(path.path)) + let instance = try module.instantiate(store: store, imports: imports) + let exitCode = try wasi.start(instance) + #expect(exitCode == manifest.exitCode ?? 0, "\(path.path)") + } } -} + +#endif diff --git a/Tests/WASITests/Platform/SandboxPrimitives/OpenParentTests.swift b/Tests/WASITests/Platform/SandboxPrimitives/OpenParentTests.swift index 7b458ba7..220dc609 100644 --- a/Tests/WASITests/Platform/SandboxPrimitives/OpenParentTests.swift +++ b/Tests/WASITests/Platform/SandboxPrimitives/OpenParentTests.swift @@ -1,39 +1,42 @@ -import SystemPackage -import XCTest +#if canImport(Testing) + import Testing + import SystemPackage -@testable import WASI + @testable import WASI -final class OpenParentTests: XCTestCase { - func testSplitParent() { - func XCTCheck( - _ lhs: (FilePath, FilePath.Component)?, - _ rhs: (FilePath, FilePath.Component)?, - file: StaticString = #file, - line: UInt = #line - ) { - switch (lhs, rhs) { - case (.none, .none): return - case let (.some(lhs), .some(rhs)): - XCTAssertEqual(lhs.0, rhs.0, file: file, line: line) - XCTAssertEqual(lhs.1, rhs.1, file: file, line: line) - default: - XCTFail("\(String(describing: lhs)) and \(String(describing: rhs)) are not equal", file: file, line: line) + @Suite + struct OpenParentTests { + @Test + func testSplitParent() { + func check( + _ lhs: (FilePath, FilePath.Component)?, + _ rhs: (FilePath, FilePath.Component)?, + sourceLocation: SourceLocation = #_sourceLocation + ) { + switch (lhs, rhs) { + case (.none, .none): return + case let (.some(lhs), .some(rhs)): + #expect(lhs.0 == rhs.0, sourceLocation: sourceLocation) + #expect(lhs.1 == rhs.1, sourceLocation: sourceLocation) + default: + #expect((false), "\(String(describing: lhs)) and \(String(describing: rhs)) are not equal", sourceLocation: sourceLocation) + } } - } - XCTCheck(splitParent(path: ""), nil) + check(splitParent(path: ""), nil) - XCTCheck(splitParent(path: "/"), (FilePath("/"), FilePath.Component("."))) - XCTCheck(splitParent(path: "/."), (FilePath("/."), FilePath.Component("."))) - XCTCheck(splitParent(path: "/a"), (FilePath("/"), FilePath.Component("a"))) - XCTCheck(splitParent(path: "/a/"), (FilePath("/a"), FilePath.Component("."))) - XCTCheck(splitParent(path: "/a/."), (FilePath("/a/."), FilePath.Component("."))) - XCTCheck(splitParent(path: "/a/.."), (FilePath("/a/.."), FilePath.Component("."))) + check(splitParent(path: "/"), (FilePath("/"), FilePath.Component("."))) + check(splitParent(path: "/."), (FilePath("/."), FilePath.Component("."))) + check(splitParent(path: "/a"), (FilePath("/"), FilePath.Component("a"))) + check(splitParent(path: "/a/"), (FilePath("/a"), FilePath.Component("."))) + check(splitParent(path: "/a/."), (FilePath("/a/."), FilePath.Component("."))) + check(splitParent(path: "/a/.."), (FilePath("/a/.."), FilePath.Component("."))) - XCTCheck(splitParent(path: "b"), (FilePath(""), FilePath.Component("b"))) - XCTCheck(splitParent(path: "b/."), (FilePath("b/."), FilePath.Component("."))) - XCTCheck(splitParent(path: "b/.."), (FilePath("b/.."), FilePath.Component("."))) + check(splitParent(path: "b"), (FilePath(""), FilePath.Component("b"))) + check(splitParent(path: "b/."), (FilePath("b/."), FilePath.Component("."))) + check(splitParent(path: "b/.."), (FilePath("b/.."), FilePath.Component("."))) - XCTCheck(splitParent(path: "../c"), (FilePath(".."), FilePath.Component("c"))) + check(splitParent(path: "../c"), (FilePath(".."), FilePath.Component("c"))) + } } -} +#endif diff --git a/Tests/WASITests/RandomBufferGeneratorTests.swift b/Tests/WASITests/RandomBufferGeneratorTests.swift index e6497e59..680ea267 100644 --- a/Tests/WASITests/RandomBufferGeneratorTests.swift +++ b/Tests/WASITests/RandomBufferGeneratorTests.swift @@ -1,35 +1,40 @@ -import XCTest +#if canImport(Testing) + import Testing -@testable import WASI + @testable import WASI -final class RandomBufferGeneratorTests: XCTestCase { - struct DeterministicGenerator: RandomNumberGenerator, RandomBufferGenerator { - var items: [UInt64] + @Suite + struct RandomBufferGeneratorTests { + struct DeterministicGenerator: RandomNumberGenerator, RandomBufferGenerator { + var items: [UInt64] - mutating func next() -> UInt64 { - items.removeFirst() + mutating func next() -> UInt64 { + items.removeFirst() + } } - } - func testDefaultFill() { - var generator = DeterministicGenerator(items: [ - 0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210, 0xdead_beef_badd_cafe, - ]) - for (bufferSize, expectedBytes): (Int, [UInt8]) in [ - (10, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32]), - (2, [0xfe, 0xca]), - (0, []), - ] { - var buffer: [UInt8] = Array(repeating: 0, count: bufferSize) - buffer.withUnsafeMutableBufferPointer { - generator.fill(buffer: $0) + @Test + func defaultFill() { + var generator = DeterministicGenerator(items: [ + 0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210, 0xdead_beef_badd_cafe, + ]) + for (bufferSize, expectedBytes): (Int, [UInt8]) in [ + (10, [0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x10, 0x32]), + (2, [0xfe, 0xca]), + (0, []), + ] { + var buffer: [UInt8] = Array(repeating: 0, count: bufferSize) + buffer.withUnsafeMutableBufferPointer { + generator.fill(buffer: $0) + } + let expected: [UInt8] + #if _endian(little) + expected = expectedBytes + #else + expected = Array(expectedBytes.reversed()) + #endif + #expect(buffer == expected) } - let expected: [UInt8] - #if _endian(little) - expected = expectedBytes - #else - expected = Array(expectedBytes.reversed()) - #endif - XCTAssertEqual(buffer, expected) } } -} + +#endif diff --git a/Tests/WASITests/WASITests.swift b/Tests/WASITests/WASITests.swift index 68ef114b..bcbc7490 100644 --- a/Tests/WASITests/WASITests.swift +++ b/Tests/WASITests/WASITests.swift @@ -1,137 +1,139 @@ -import XCTest +#if canImport(Testing) + import Testing + + @testable import WASI + + @Suite + struct WASITests { + #if !os(Windows) + @Test + func pathOpen() throws { + let t = try TestSupport.TemporaryDirectory() + + try t.createDir(at: "External") + try t.createDir(at: "External/secret-dir-b") + try t.createFile(at: "External/secret-a.txt", contents: "Secret A") + try t.createFile(at: "External/secret-dir-b/secret-c.txt", contents: "Secret C") + try t.createDir(at: "Sandbox") + try t.createFile(at: "Sandbox/hello.txt", contents: "Hello") + try t.createSymlink(at: "Sandbox/link-hello.txt", to: "hello.txt") + try t.createDir(at: "Sandbox/world.dir") + try t.createSymlink(at: "Sandbox/link-world.dir", to: "world.dir") + try t.createSymlink(at: "Sandbox/link-external-secret-a.txt", to: "../External/secret-a.txt") + try t.createSymlink(at: "Sandbox/link-secret-dir-b", to: "../External/secret-dir-b") + try t.createSymlink(at: "Sandbox/link-updown-hello.txt", to: "../Sandbox/link-updown-hello.txt") + try t.createSymlink(at: "Sandbox/link-external-non-existent.txt", to: "../External/non-existent.txt") + try t.createSymlink(at: "Sandbox/link-root", to: "/") + try t.createSymlink(at: "Sandbox/link-loop.txt", to: "link-loop.txt") + + let wasi = try WASIBridgeToHost( + preopens: ["/Sandbox": t.url.appendingPathComponent("Sandbox").path] + ) + let mntFd: WASIAbi.Fd = 3 + + func assertResolve(_ path: String, followSymlink: Bool, directory: Bool = false) throws { + let fd = try wasi.path_open( + dirFd: mntFd, + dirFlags: followSymlink ? [.SYMLINK_FOLLOW] : [], + path: path, + oflags: directory ? [.DIRECTORY] : [], + fsRightsBase: .DIRECTORY_BASE_RIGHTS, + fsRightsInheriting: .DIRECTORY_INHERITING_RIGHTS, + fdflags: [] + ) + try wasi.fd_close(fd: fd) + } -@testable import WASI + func assertNotResolve( + _ path: String, + followSymlink: Bool, + directory: Bool = false, + sourceLocation: SourceLocation = #_sourceLocation, + _ checkError: ((WASIAbi.Errno) throws -> Void)? + ) throws { + do { + _ = try wasi.path_open( + dirFd: mntFd, + dirFlags: followSymlink ? [.SYMLINK_FOLLOW] : [], + path: path, + oflags: directory ? [.DIRECTORY] : [], + fsRightsBase: .DIRECTORY_BASE_RIGHTS, + fsRightsInheriting: .DIRECTORY_INHERITING_RIGHTS, + fdflags: [] + ) + #expect((false), "Expected not to be able to open \(path)", sourceLocation: sourceLocation) + } catch { + guard let error = error as? WASIAbi.Errno else { + #expect((false), "Expected WASIAbi.Errno error but got \(error)", sourceLocation: sourceLocation) + return + } + try checkError?(error) + } + } -final class WASITests: XCTestCase { - func testPathOpen() throws { - #if os(Windows) - try XCTSkipIf(true) - #endif - let t = try TestSupport.TemporaryDirectory() - - try t.createDir(at: "External") - try t.createDir(at: "External/secret-dir-b") - try t.createFile(at: "External/secret-a.txt", contents: "Secret A") - try t.createFile(at: "External/secret-dir-b/secret-c.txt", contents: "Secret C") - try t.createDir(at: "Sandbox") - try t.createFile(at: "Sandbox/hello.txt", contents: "Hello") - try t.createSymlink(at: "Sandbox/link-hello.txt", to: "hello.txt") - try t.createDir(at: "Sandbox/world.dir") - try t.createSymlink(at: "Sandbox/link-world.dir", to: "world.dir") - try t.createSymlink(at: "Sandbox/link-external-secret-a.txt", to: "../External/secret-a.txt") - try t.createSymlink(at: "Sandbox/link-secret-dir-b", to: "../External/secret-dir-b") - try t.createSymlink(at: "Sandbox/link-updown-hello.txt", to: "../Sandbox/link-updown-hello.txt") - try t.createSymlink(at: "Sandbox/link-external-non-existent.txt", to: "../External/non-existent.txt") - try t.createSymlink(at: "Sandbox/link-root", to: "/") - try t.createSymlink(at: "Sandbox/link-loop.txt", to: "link-loop.txt") - - let wasi = try WASIBridgeToHost( - preopens: ["/Sandbox": t.url.appendingPathComponent("Sandbox").path] - ) - let mntFd: WASIAbi.Fd = 3 - - func assertResolve(_ path: String, followSymlink: Bool, directory: Bool = false) throws { - let fd = try wasi.path_open( - dirFd: mntFd, - dirFlags: followSymlink ? [.SYMLINK_FOLLOW] : [], - path: path, - oflags: directory ? [.DIRECTORY] : [], - fsRightsBase: .DIRECTORY_BASE_RIGHTS, - fsRightsInheriting: .DIRECTORY_INHERITING_RIGHTS, - fdflags: [] - ) - try wasi.fd_close(fd: fd) - } - - func assertNotResolve( - _ path: String, - followSymlink: Bool, - directory: Bool = false, - file: StaticString = #file, - line: UInt = #line, - _ checkError: ((WASIAbi.Errno) throws -> Void)? - ) throws { - do { - _ = try wasi.path_open( - dirFd: mntFd, - dirFlags: followSymlink ? [.SYMLINK_FOLLOW] : [], - path: path, - oflags: directory ? [.DIRECTORY] : [], - fsRightsBase: .DIRECTORY_BASE_RIGHTS, - fsRightsInheriting: .DIRECTORY_INHERITING_RIGHTS, - fdflags: [] - ) - XCTFail("Expected not to be able to open \(path)", file: file, line: line) - } catch { - guard let error = error as? WASIAbi.Errno else { - XCTFail("Expected WASIAbi.Errno error but got \(error)", file: file, line: line) - return + try assertNotResolve("non-existent.txt", followSymlink: false) { error in + #expect(error == .ENOENT) + } + + try assertResolve("link-hello.txt", followSymlink: true) + try assertNotResolve("link-hello.txt", followSymlink: false) { error in + #expect(error == .ELOOP) + } + try assertNotResolve("link-hello.txt", followSymlink: true, directory: true) { error in + #expect(error == .ENOTDIR) + } + + try assertNotResolve("link-hello.txt/", followSymlink: true) { error in + #expect(error == .ENOTDIR) + } + + try assertResolve("link-world.dir", followSymlink: true) + try assertNotResolve("link-world.dir", followSymlink: false) { error in + #expect(error == .ELOOP) + } + + try assertNotResolve("link-external-secret-a.txt", followSymlink: true) { error in + #expect(error == .EPERM) + } + try assertNotResolve("link-external-secret-a.txt", followSymlink: false) { error in + #expect(error == .ELOOP) + } + + try assertNotResolve("link-external-non-existent.txt", followSymlink: true) { error in + #expect(error == .EPERM) + } + try assertNotResolve("link-external-non-existent.txt", followSymlink: false) { error in + #expect(error == .ELOOP) + } + + try assertNotResolve("link-updown-hello.txt", followSymlink: true) { error in + #expect(error == .EPERM) + } + try assertNotResolve("link-updown-hello.txt", followSymlink: false) { error in + #expect(error == .ELOOP) + } + + try assertNotResolve("link-secret-dir-b/secret-c.txt", followSymlink: true) { error in + #expect(error == .EPERM) + } + try assertNotResolve("link-secret-dir-b/secret-c.txt", followSymlink: false) { error in + #expect(error == .ENOTDIR) + } + + try assertNotResolve("link-root", followSymlink: true) { error in + #expect(error == .EPERM) + } + try assertNotResolve("link-root", followSymlink: false) { error in + #expect(error == .ELOOP) + } + + try assertNotResolve("link-loop.txt", followSymlink: false) { error in + #expect(error == .ELOOP) + } + try assertNotResolve("link-loop.txt", followSymlink: true) { error in + #expect(error == .ELOOP) } - try checkError?(error) } - } - - try assertNotResolve("non-existent.txt", followSymlink: false) { error in - XCTAssertEqual(error, .ENOENT) - } - - try assertResolve("link-hello.txt", followSymlink: true) - try assertNotResolve("link-hello.txt", followSymlink: false) { error in - XCTAssertEqual(error, .ELOOP) - } - try assertNotResolve("link-hello.txt", followSymlink: true, directory: true) { error in - XCTAssertEqual(error, .ENOTDIR) - } - - try assertNotResolve("link-hello.txt/", followSymlink: true) { error in - XCTAssertEqual(error, .ENOTDIR) - } - - try assertResolve("link-world.dir", followSymlink: true) - try assertNotResolve("link-world.dir", followSymlink: false) { error in - XCTAssertEqual(error, .ELOOP) - } - - try assertNotResolve("link-external-secret-a.txt", followSymlink: true) { error in - XCTAssertEqual(error, .EPERM) - } - try assertNotResolve("link-external-secret-a.txt", followSymlink: false) { error in - XCTAssertEqual(error, .ELOOP) - } - - try assertNotResolve("link-external-non-existent.txt", followSymlink: true) { error in - XCTAssertEqual(error, .EPERM) - } - try assertNotResolve("link-external-non-existent.txt", followSymlink: false) { error in - XCTAssertEqual(error, .ELOOP) - } - - try assertNotResolve("link-updown-hello.txt", followSymlink: true) { error in - XCTAssertEqual(error, .EPERM) - } - try assertNotResolve("link-updown-hello.txt", followSymlink: false) { error in - XCTAssertEqual(error, .ELOOP) - } - - try assertNotResolve("link-secret-dir-b/secret-c.txt", followSymlink: true) { error in - XCTAssertEqual(error, .EPERM) - } - try assertNotResolve("link-secret-dir-b/secret-c.txt", followSymlink: false) { error in - XCTAssertEqual(error, .ENOTDIR) - } - - try assertNotResolve("link-root", followSymlink: true) { error in - XCTAssertEqual(error, .EPERM) - } - try assertNotResolve("link-root", followSymlink: false) { error in - XCTAssertEqual(error, .ELOOP) - } - - try assertNotResolve("link-loop.txt", followSymlink: false) { error in - XCTAssertEqual(error, .ELOOP) - } - try assertNotResolve("link-loop.txt", followSymlink: true) { error in - XCTAssertEqual(error, .ELOOP) - } + #endif } -} +#endif From 932b1dd029413c25db348fb118718c92d0d04a50 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sun, 5 Oct 2025 21:53:50 +0900 Subject: [PATCH 6/6] Tests: Hard-skip spectests on Android --- Tests/WATTests/Spectest.swift | 40 ++++++++++-------- .../FuzzTranslatorRegressionTests.swift | 24 ++++++----- Tests/WasmKitTests/SpectestTests.swift | 42 ++++++++++--------- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/Tests/WATTests/Spectest.swift b/Tests/WATTests/Spectest.swift index 65fe7bdf..8216d08d 100644 --- a/Tests/WATTests/Spectest.swift +++ b/Tests/WATTests/Spectest.swift @@ -17,25 +17,29 @@ enum Spectest { } static func wastFiles(include: [String] = [], exclude: [String] = ["annotations.wast"]) -> [URL] { - return [ - testsuitePath, - testsuitePath.appendingPathComponent("proposals/memory64"), - testsuitePath.appendingPathComponent("proposals/tail-call"), - rootDirectory.appendingPathComponent("Tests/WasmKitTests/ExtraSuite"), - ].flatMap { - try! FileManager.default.contentsOfDirectory(at: $0, includingPropertiesForKeys: nil) - }.compactMap { filePath in - guard filePath.pathExtension == "wast" else { - return nil + #if os(Android) + return [] + #else + return [ + testsuitePath, + testsuitePath.appendingPathComponent("proposals/memory64"), + testsuitePath.appendingPathComponent("proposals/tail-call"), + rootDirectory.appendingPathComponent("Tests/WasmKitTests/ExtraSuite"), + ].flatMap { + try! FileManager.default.contentsOfDirectory(at: $0, includingPropertiesForKeys: nil) + }.compactMap { filePath in + guard filePath.pathExtension == "wast" else { + return nil + } + guard !filePath.lastPathComponent.starts(with: "simd_") else { return nil } + if !include.isEmpty { + guard include.contains(filePath.lastPathComponent) else { return nil } + } else { + guard !exclude.contains(filePath.lastPathComponent) else { return nil } + } + return filePath } - guard !filePath.lastPathComponent.starts(with: "simd_") else { return nil } - if !include.isEmpty { - guard include.contains(filePath.lastPathComponent) else { return nil } - } else { - guard !exclude.contains(filePath.lastPathComponent) else { return nil } - } - return filePath - } + #endif } static func deriveFeatureSet(wast: URL) -> WasmFeatureSet { diff --git a/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift b/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift index 54c19494..5a05580a 100644 --- a/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift +++ b/Tests/WasmKitTests/FuzzTranslatorRegressionTests.swift @@ -23,18 +23,20 @@ } } - @Test( - .disabled("unable to run fuzz translator regression tests on Android due to missing files on emulator", platforms: [.android]), - arguments: try failCases() - ) - func run(test: Case) throws { - let data = try Data(contentsOf: test.path) - do { - try WasmKitFuzzing.fuzzInstantiation(bytes: Array(data)) - } catch { - // Skip exceptions without crash + #if !os(Android) + @Test( + .disabled("unable to run fuzz translator regression tests on Android due to missing files on emulator", platforms: [.android]), + arguments: try failCases() + ) + func run(test: Case) throws { + let data = try Data(contentsOf: test.path) + do { + try WasmKitFuzzing.fuzzInstantiation(bytes: Array(data)) + } catch { + // Skip exceptions without crash + } } - } + #endif } #endif diff --git a/Tests/WasmKitTests/SpectestTests.swift b/Tests/WasmKitTests/SpectestTests.swift index f4cd5bc8..000a9138 100644 --- a/Tests/WasmKitTests/SpectestTests.swift +++ b/Tests/WasmKitTests/SpectestTests.swift @@ -19,26 +19,28 @@ ] } - @Test( - .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), - arguments: try SpectestDiscovery(path: SpectestTests.testPaths).discover() - ) - func run(test: TestCase) throws { - let defaultConfig = EngineConfiguration() - let runner = try SpectestRunner(configuration: defaultConfig) - try runner.run(test: test, reporter: NullSpectestProgressReporter()) - } + #if !os(Android) + @Test( + .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), + arguments: try SpectestDiscovery(path: SpectestTests.testPaths).discover() + ) + func run(test: TestCase) throws { + let defaultConfig = EngineConfiguration() + let runner = try SpectestRunner(configuration: defaultConfig) + try runner.run(test: test, reporter: NullSpectestProgressReporter()) + } - @Test( - .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), - arguments: try SpectestDiscovery(path: SpectestTests.testPaths).discover() - ) - func runWithTokenThreading(test: TestCase) throws { - let defaultConfig = EngineConfiguration() - guard defaultConfig.threadingModel != .token else { return } - // Sanity check that non-default threading models work. - let runner = try SpectestRunner(configuration: defaultConfig) - try runner.run(test: test, reporter: NullSpectestProgressReporter()) - } + @Test( + .disabled("unable to run spectest on Android due to missing files on emulator", platforms: [.android]), + arguments: try SpectestDiscovery(path: SpectestTests.testPaths).discover() + ) + func runWithTokenThreading(test: TestCase) throws { + let defaultConfig = EngineConfiguration() + guard defaultConfig.threadingModel != .token else { return } + // Sanity check that non-default threading models work. + let runner = try SpectestRunner(configuration: defaultConfig) + try runner.run(test: test, reporter: NullSpectestProgressReporter()) + } + #endif } #endif