Skip to content

Commit

Permalink
Add Wasm CI. (#192)
Browse files Browse the repository at this point in the history
* Add Wasm CI.

* fix for wasi

* fix

* wip

* use dev snapshot

* include static swift stdlib

* wip

* fix wasi

* wasi fixes

* fix

* wip

* wip

* wip

* try env var

* wip

* wip

* wip

* wip

* Follow-up Wasm CI integration (#193)

* Skip several deprecated tests on WebAssembly

* Use the official compiler and Swift SDK for WebAssembly

* Grant access to the current directory to the XCTest harness

---------

Co-authored-by: Yuta Saito <kateinoigakukun@gmail.com>
  • Loading branch information
mbrandonw and kateinoigakukun authored Aug 29, 2024
1 parent 9f36d95 commit 2ed7c33
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 47 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,34 @@ jobs:
- name: Run tests (release)
run: swift test -c release --parallel

wasm:
name: SwiftWasm
runs-on: ubuntu-latest
env:
OMIT_MACRO_TESTS: 1
strategy:
matrix:
include:
- toolchain: swift-DEVELOPMENT-SNAPSHOT-2024-07-08-a
swift-sdk: swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a
steps:
- uses: actions/checkout@v4
- uses: bytecodealliance/actions/wasmtime/setup@v1
- name: Install Swift and Swift SDK for WebAssembly
run: |
PREFIX=/opt/swift
SWIFT_TOOLCHAIN_TAG="${{ matrix.toolchain }}"
SWIFT_SDK_TAG="${{ matrix.swift-sdk }}"
set -ex
curl -f -o /tmp/swift.tar.gz "https://download.swift.org/development/ubuntu2204/$SWIFT_TOOLCHAIN_TAG/$SWIFT_TOOLCHAIN_TAG-ubuntu22.04.tar.gz"
sudo mkdir -p $PREFIX; sudo tar -xzf /tmp/swift.tar.gz -C $PREFIX --strip-component 1
$PREFIX/usr/bin/swift experimental-sdk install "https://github.com/swiftwasm/swift/releases/download/$SWIFT_SDK_TAG/$SWIFT_SDK_TAG-wasm32-unknown-wasi.artifactbundle.zip"
echo "$PREFIX/usr/bin" >> $GITHUB_PATH
- name: Build tests
run: swift build --swift-sdk wasm32-unknown-wasi --build-tests -Xlinker -z -Xlinker stack-size=$((1024 * 1024))
- name: Run tests
run: wasmtime --dir . .build/debug/swift-case-pathsPackageTests.wasm

# windows:
# name: Windows (Swift ${{ matrix.swift }}, ${{ matrix.config }})
# strategy:
Expand Down
28 changes: 20 additions & 8 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"601.0.0-prerelease"),
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.2.2"),
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"),
],
targets: [
.target(
Expand All @@ -42,13 +41,6 @@ let package = Package(
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
]
),
.testTarget(
name: "CasePathsMacrosTests",
dependencies: [
"CasePathsMacros",
.product(name: "MacroTesting", package: "swift-macro-testing"),
]
),
]
)

Expand All @@ -59,6 +51,26 @@ let package = Package(
)
#endif

import Foundation

if ProcessInfo.processInfo.environment["OMIT_MACRO_TESTS"] == nil {
package.dependencies.append(
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0")
)
package.targets.append(
.testTarget(
name: "CasePathsMacrosTests",
dependencies: [
"CasePathsMacros",
.product(
name: "MacroTesting",
package: "swift-macro-testing"
)
]
)
)
}

for target in package.targets {
target.swiftSettings = target.swiftSettings ?? []
target.swiftSettings?.append(contentsOf: [
Expand Down
28 changes: 20 additions & 8 deletions Package@swift-6.0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/swiftlang/swift-syntax", "509.0.0"..<"601.0.0-prerelease"),
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.2.2"),
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0"),
],
targets: [
.target(
Expand All @@ -42,13 +41,6 @@ let package = Package(
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
]
),
.testTarget(
name: "CasePathsMacrosTests",
dependencies: [
"CasePathsMacros",
.product(name: "MacroTesting", package: "swift-macro-testing"),
]
),
],
swiftLanguageVersions: [.v6]
)
Expand All @@ -59,3 +51,23 @@ let package = Package(
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0")
)
#endif

import Foundation

if ProcessInfo.processInfo.environment["OMIT_MACRO_TESTS"] == nil {
package.dependencies.append(
.package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.2.0")
)
package.targets.append(
.testTarget(
name: "CasePathsMacrosTests",
dependencies: [
"CasePathsMacros",
.product(
name: "MacroTesting",
package: "swift-macro-testing"
)
]
)
)
}
2 changes: 1 addition & 1 deletion Sources/CasePaths/EnumReflection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func extractHelp<Root, Value>(
return { root in
let rootTag = metadata.tag(of: root)

if case let (cachedTag?, (isIndirect, associatedValueType)?) = cache.withLock({
if case let (cachedTag?, (isIndirect: isIndirect, associatedValueType: associatedValueType)?) = cache.withLock({
($0.tag, $0.strategy)
}) {
guard rootTag == cachedTag else { return nil }
Expand Down
2 changes: 2 additions & 0 deletions Tests/CasePathsMacrosTests/CasePathableMacroTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(MacroTesting)
import CasePathsMacros
import MacroTesting
import SwiftSyntaxMacros
Expand Down Expand Up @@ -1294,3 +1295,4 @@ final class CasePathableMacroTests: XCTestCase {
}
}
}
#endif
2 changes: 1 addition & 1 deletion Tests/CasePathsTests/CasePathableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ final class CasePathableTests: XCTestCase {
XCTAssertEqual(result, .success(2))
}

#if DEBUG && !os(Linux) && !os(Windows)
#if DEBUG && !os(Linux) && !os(Windows) && !os(WASI)
func testModifyWrongCase() {
guard ProcessInfo.processInfo.environment["CI"] == nil else { return }
var response = Result<Int, MyError>.failure(MyError())
Expand Down
2 changes: 1 addition & 1 deletion Tests/CasePathsTests/CasePathsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ final class CasePathsTests: XCTestCase {
XCTAssertEqual(foo, .bar(.int(42)))
}

#if DEBUG && !os(Linux) && !os(Windows)
#if DEBUG && !os(Linux) && !os(Windows) && !os(WASI)
func testCasePathableModify_Failure() {
guard ProcessInfo.processInfo.environment["CI"] == nil else { return }
var foo = Foo.bar(.int(21))
Expand Down
2 changes: 1 addition & 1 deletion Tests/CasePathsTests/CaseSetTests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#if swift(>=6)
#if canImport(Testing) && swift(>=6)
import CasePaths
import Testing

Expand Down
74 changes: 47 additions & 27 deletions Tests/CasePathsTests/DeprecatedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,37 @@ final class DeprecatedTests: XCTestCase {
XCTAssertNil(AnyCasePath(Enum.uninhabited).extract(from: .value))
}

func testClosurePayload() throws {
enum Enum { case closure(() -> Void) }
let path = /Enum.closure
for _ in 1...2 {
// Dynamic closure payload extraction is not supported on WebAssembly
//
// Discussion:
// Closure payloads `() -> Void` are extracted via generic abstraction
// `Value` in `extractHelp`, and the extracted closure is called as
// `() -> @out τ_0_0 where τ_0_0 == Void`.
// However, the closure value is stored without generic abstraction
// in the enum payload storage, and not wrapped by abstraction thunk.
// This abstraction mismatch causes signature mismatch between the
// call-site and the actual callee. On most platforms, this mismatch
// is luckily not a problem, but WebAssembly enforces the signature
// match and causes a runtime crash.
// Ideally, `extractHelp` should insert an abstraction thunk when
// extracting a closure payload, but it cannot be done without
// JIT code generation (and WebAssembly does not support JIT...)
#if !arch(wasm32)
func testClosurePayload() throws {
enum Enum { case closure(() -> Void) }
let path = /Enum.closure
for _ in 1...2 {
var invoked = false
let closure = try unwrap(path.extract(from: .closure { invoked = true }))
closure()
XCTAssertTrue(invoked)
}
var invoked = false
let closure = try unwrap(path.extract(from: .closure { invoked = true }))
let closure = try unwrap(AnyCasePath(Enum.closure).extract(from: .closure { invoked = true }))
closure()
XCTAssertTrue(invoked)
}
var invoked = false
let closure = try unwrap(AnyCasePath(Enum.closure).extract(from: .closure { invoked = true }))
closure()
XCTAssertTrue(invoked)
}
#endif

func testRecursivePayload() {
indirect enum Enum: Equatable {
Expand Down Expand Up @@ -602,9 +619,6 @@ final class DeprecatedTests: XCTestCase {
let actual = path.extract(from: root)
XCTAssertEqual(actual, "deadbeef")
}
#if swift(>=6)
XCTExpectFailure()
#endif
XCTAssertEqual(AnyCasePath(Authentication.authenticated).extract(from: root), "deadbeef")
}

Expand Down Expand Up @@ -894,21 +908,25 @@ final class DeprecatedTests: XCTestCase {
)
}

func testEnumsWithClosures() {
enum Foo {
case bar(() -> Void)
}
// Dynamic closure payload extraction is not supported on WebAssembly
// See testClosurePayload for more details
#if !arch(wasm32)
func testEnumsWithClosures() {
enum Foo {
case bar(() -> Void)
}

var didRun = false
let fooBar = /Foo.bar
guard let bar = fooBar.extract(from: .bar { didRun = true })
else {
XCTFail()
return
var didRun = false
let fooBar = /Foo.bar
guard let bar = fooBar.extract(from: .bar { didRun = true })
else {
XCTFail()
return
}
bar()
XCTAssertTrue(didRun)
}
bar()
XCTAssertTrue(didRun)
}
#endif

func testRecursive() {
indirect enum Foo {
Expand Down Expand Up @@ -1205,10 +1223,12 @@ final class DeprecatedTests: XCTestCase {
)
}

#if os(Windows)
#if os(Windows) || arch(wasm32)
// There seems to be some strangeness with the current
// concurrency implmentation on Windows that breaks if
// you have more than 100 tasks here.
// WebAssembly doesn't support tail-call for now, so we
// have smaller limit as well.
let maxIterations = 100
#else
let maxIterations = 100_000
Expand Down

0 comments on commit 2ed7c33

Please sign in to comment.