Skip to content

Commit a549f58

Browse files
Merge pull request #213 from swiftwasm/katei/add-jsel-test-support
Add JavaScriptEventLoopTestSupport module to install executor
2 parents e9422fe + b34c15f commit a549f58

File tree

10 files changed

+133
-13
lines changed

10 files changed

+133
-13
lines changed

Diff for: .github/workflows/test.yml

+10-5
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,27 @@ jobs:
2525
- { os: ubuntu-20.04, toolchain: wasm-5.7.1-RELEASE, wasi-backend: MicroWASI }
2626

2727
runs-on: ${{ matrix.entry.os }}
28+
env:
29+
JAVASCRIPTKIT_WASI_BACKEND: ${{ matrix.entry.wasi-backend }}
30+
SWIFT_VERSION: ${{ matrix.entry.toolchain }}
2831
steps:
2932
- name: Checkout
3033
uses: actions/checkout@master
3134
with:
3235
fetch-depth: 1
33-
- name: Run Test
34-
env:
35-
JAVASCRIPTKIT_WASI_BACKEND: ${{ matrix.entry.wasi-backend }}
36+
- name: Install swiftenv
3637
run: |
3738
git clone https://github.com/kylef/swiftenv.git ~/.swiftenv
3839
export SWIFTENV_ROOT="$HOME/.swiftenv"
3940
export PATH="$SWIFTENV_ROOT/bin:$PATH"
4041
eval "$(swiftenv init -)"
41-
SWIFT_VERSION=${{ matrix.entry.toolchain }} make bootstrap
42+
echo $PATH >> $GITHUB_PATH
43+
env >> $GITHUB_ENV
4244
echo ${{ matrix.entry.toolchain }} > .swift-version
43-
make test
45+
- run: make bootstrap
46+
- run: make test
47+
- run: make unittest
48+
if: ${{ startsWith(matrix.toolchain, 'wasm-5.7.') }}
4449
- name: Check if SwiftPM resources are stale
4550
run: |
4651
make regenerate_swiftpm_resources

Diff for: IntegrationTests/lib.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const fs = require("fs");
99
const readFile = promisify(fs.readFile);
1010

1111
const WASI = {
12-
Wasmer: () => {
12+
Wasmer: ({ programName }) => {
1313
// Instantiate a new WASI Instance
1414
const wasmFs = new WasmFs();
1515
// Output stdout and stderr to console
@@ -27,7 +27,7 @@ const WASI = {
2727
return originalWriteSync(fd, buffer, offset, length, position);
2828
};
2929
const wasi = new WasmerWASI({
30-
args: [],
30+
args: [programName],
3131
env: {},
3232
bindings: {
3333
...WasmerWASI.defaultBindings,
@@ -44,9 +44,9 @@ const WASI = {
4444
}
4545
}
4646
},
47-
MicroWASI: () => {
47+
MicroWASI: ({ programName }) => {
4848
const wasi = new MicroWASI({
49-
args: [],
49+
args: [programName],
5050
env: {},
5151
features: [useAll()],
5252
})
@@ -59,11 +59,11 @@ const WASI = {
5959
}
6060
}
6161
},
62-
Node: () => {
62+
Node: ({ programName }) => {
6363
const wasi = new NodeWASI({
64-
args: [],
64+
args: [programName],
6565
env: {},
66-
returnOnExit: true,
66+
returnOnExit: false,
6767
})
6868

6969
return {
@@ -91,7 +91,7 @@ const startWasiTask = async (wasmPath, wasiConstructor = selectWASIBackend()) =>
9191
const swift = new SwiftRuntime();
9292
// Fetch our Wasm File
9393
const wasmBinary = await readFile(wasmPath);
94-
const wasi = wasiConstructor();
94+
const wasi = wasiConstructor({ programName: wasmPath });
9595

9696
// Instantiate the WebAssembly file
9797
let { instance } = await WebAssembly.instantiate(wasmBinary, {

Diff for: Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,19 @@ build:
1212

1313
.PHONY: test
1414
test:
15+
@echo Running integration tests
1516
cd IntegrationTests && \
1617
CONFIGURATION=debug make test && \
1718
CONFIGURATION=debug SWIFT_BUILD_FLAGS="-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS" make test && \
1819
CONFIGURATION=release make test && \
1920
CONFIGURATION=release SWIFT_BUILD_FLAGS="-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS" make test
2021

22+
.PHONY: unittest
23+
unittest:
24+
@echo Running unit tests
25+
swift build --build-tests --triple wasm32-unknown-wasi -Xswiftc -Xclang-linker -Xswiftc -mexec-model=reactor -Xlinker --export=main
26+
node --experimental-wasi-unstable-preview1 scripts/test-harness.js ./.build/wasm32-unknown-wasi/debug/JavaScriptKitPackageTests.wasm
27+
2128
.PHONY: benchmark_setup
2229
benchmark_setup:
2330
cd IntegrationTests && CONFIGURATION=release make benchmark_setup

Diff for: Package.swift

+16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ let package = Package(
88
.library(name: "JavaScriptKit", targets: ["JavaScriptKit"]),
99
.library(name: "JavaScriptEventLoop", targets: ["JavaScriptEventLoop"]),
1010
.library(name: "JavaScriptBigIntSupport", targets: ["JavaScriptBigIntSupport"]),
11+
.library(name: "JavaScriptEventLoopTestSupport", targets: ["JavaScriptEventLoopTestSupport"]),
1112
],
1213
targets: [
1314
.target(
@@ -26,5 +27,20 @@ let package = Package(
2627
dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"]
2728
),
2829
.target(name: "_CJavaScriptEventLoop"),
30+
.target(
31+
name: "JavaScriptEventLoopTestSupport",
32+
dependencies: [
33+
"_CJavaScriptEventLoopTestSupport",
34+
"JavaScriptEventLoop",
35+
]
36+
),
37+
.target(name: "_CJavaScriptEventLoopTestSupport"),
38+
.testTarget(
39+
name: "JavaScriptEventLoopTestSupportTests",
40+
dependencies: [
41+
"JavaScriptKit",
42+
"JavaScriptEventLoopTestSupport"
43+
]
44+
),
2945
]
3046
)

Diff for: README.md

+17
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,23 @@ asyncButtonElement.onclick = .object(JSClosure { _ in
125125
_ = document.body.appendChild(asyncButtonElement)
126126
```
127127

128+
### `JavaScriptEventLoop` activation in XCTest suites
129+
130+
If you need to execute Swift async functions that can be resumed by JS event loop in your XCTest suites, please add `JavaScriptEventLoopTestSupport` to your test target dependencies.
131+
132+
```diff
133+
.testTarget(
134+
name: "MyAppTests",
135+
dependencies: [
136+
"MyApp",
137+
+ "JavaScriptEventLoopTestSupport",
138+
]
139+
)
140+
```
141+
142+
Linking this module automatically activates JS event loop based global executor by calling `JavaScriptEventLoop.installGlobalExecutor()`
143+
144+
128145
## Requirements
129146

130147
### For developers
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// If you need to execute Swift async functions that can be resumed by JS
2+
/// event loop in your XCTest suites, please add `JavaScriptEventLoopTestSupport`
3+
/// to your test target dependencies.
4+
///
5+
/// ```diff
6+
/// .testTarget(
7+
/// name: "MyAppTests",
8+
/// dependencies: [
9+
/// "MyApp",
10+
/// + "JavaScriptEventLoopTestSupport",
11+
/// ]
12+
/// )
13+
/// ```
14+
///
15+
/// Linking this module automatically activates JS event loop based global
16+
/// executor by calling `JavaScriptEventLoop.installGlobalExecutor()`
17+
18+
import JavaScriptEventLoop
19+
20+
// This module just expose 'JavaScriptEventLoop.installGlobalExecutor' to C ABI
21+
// See _CJavaScriptEventLoopTestSupport.c for why this is needed
22+
23+
#if compiler(>=5.5)
24+
25+
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
26+
@_cdecl("swift_javascriptkit_activate_js_executor_impl")
27+
func swift_javascriptkit_activate_js_executor_impl() {
28+
JavaScriptEventLoop.installGlobalExecutor()
29+
}
30+
31+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// This 'ctor' function is called at startup time of this program.
2+
// It's invoked by '_start' of command-line or '_initialize' of reactor.
3+
// This ctor activate the event loop based global executor automatically
4+
// before running the test cases. For general applications, applications
5+
// have to activate the event loop manually on their responsibility.
6+
// However, XCTest framework doesn't provide a way to run arbitrary code
7+
// before running all of the test suites. So, we have to do it here.
8+
//
9+
// See also: https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md#current-unstable-abi
10+
11+
extern void swift_javascriptkit_activate_js_executor_impl(void);
12+
13+
// priority 0~100 is reserved by wasi-libc
14+
// https://github.com/WebAssembly/wasi-libc/blob/30094b6ed05f19cee102115215863d185f2db4f0/libc-bottom-half/sources/environ.c#L20
15+
__attribute__((constructor(/* priority */ 200)))
16+
void swift_javascriptkit_activate_js_executor(void) {
17+
swift_javascriptkit_activate_js_executor_impl();
18+
}
19+

Diff for: Sources/_CJavaScriptEventLoopTestSupport/include/dummy.h

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import XCTest
2+
import JavaScriptKit
3+
4+
final class JavaScriptEventLoopTestSupportTests: XCTestCase {
5+
func testAwaitMicrotask() async {
6+
let _: () = await withCheckedContinuation { cont in
7+
JSObject.global.queueMicrotask.function!(
8+
JSOneshotClosure { _ in
9+
cont.resume(returning: ())
10+
return .undefined
11+
}
12+
)
13+
}
14+
}
15+
}

Diff for: scripts/test-harness.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Error.stackTraceLimit = Infinity;
2+
3+
const { startWasiTask, WASI } = require("../IntegrationTests/lib");
4+
5+
const handleExitOrError = (error) => {
6+
console.log(error);
7+
process.exit(1);
8+
}
9+
10+
startWasiTask(process.argv[2]).catch(handleExitOrError);

0 commit comments

Comments
 (0)