Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/minimal-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,36 @@ jobs:
run: cargo test $TEST_FEATURES


unit-test-web:
name: unit-test-web
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: "Patch Cargo.toml to use nightly extension API"
run: .github/other/patch-prebuilt.sh nightly

- name: "Install Rust"
uses: ./.github/composite/rust
with:
rust: nightly
components: rust-src

- name: "Install emscripten"
env:
EMSCRIPTEN_SRC_REF: 4.0.13
EMSCRIPTEN_VERSION: 3.1.74
run: |
git clone https://github.com/emscripten-core/emsdk.git --depth 1 --branch $EMSCRIPTEN_SRC_REF --single-branch
./emsdk/emsdk install $EMSCRIPTEN_VERSION
./emsdk/emsdk activate $EMSCRIPTEN_VERSION
source ./emsdk/emsdk_env.sh
echo PATH=$PATH >> $GITHUB_ENV
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
echo PATH=$PATH >> $GITHUB_ENV
echo "PATH=$PATH" >> $GITHUB_ENV

I don't expect spaces, but these are a real pain to debug 😬

But apart from that, this just defines it with itself?
Should we not use GITHUB_PATH?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I didn't know about that variable. We should probably be using it.


- name: "Test (Wasm)"
run: ./check.sh testweb


# For complex matrix workflow, see https://stackoverflow.com/a/65434401
godot-itest:
name: godot-itest (${{ matrix.name }})
Expand Down
35 changes: 34 additions & 1 deletion check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Commands:
fmt format code, fail if bad
test run unit tests (no Godot needed)
itest run integration tests (from within Godot)
testweb run unit tests on web environment (requires node.js and emcc)
clippy validate clippy lints
klippy validate + fix clippy
doc generate docs for 'godot' crate
Expand Down Expand Up @@ -179,6 +180,38 @@ function cmd_itest() {
run "$godotBin" $GODOT_ARGS --path itest/godot --headless -- "[${extraArgs[@]}]"
}

function cmd_testweb() {
# Add more debug symbols to build
common_flags="-C link-args=-g"

# Avoid problems with emcc potentially writing to read-only dir
cache_dir="$(realpath ./target)/emscripten_cache"
mkdir -p "${cache_dir}"

echo "==============================="
echo "Initiating threaded Wasm tests."
echo "==============================="

# For the runner env var: https://github.com/rust-lang/cargo/issues/7471
# More memory (256 MiB) is needed for the parallel godot-cell tests which
# spawn 70 threads each.
Comment on lines +196 to +197
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good observation. Do you think this still makes sense, pushing the limits also on web?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine for what it's worth. We could consider reducing the tests a bit, but I'd rather get existing tests to work first.

CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER=node RUSTFLAGS="-C link-args=-pthread \
-C target-feature=+atomics \
-C link-args=-sINITIAL_MEMORY=268435456 \
${common_flags}" EM_CACHE="${cache_dir}" run cargo +nightly test \
--features godot/experimental-wasm,godot/lazy-function-tables \
-Zbuild-std --target wasm32-unknown-emscripten

echo "==================================="
echo "Initiating non-threaded Wasm tests."
echo "==================================="

CARGO_TARGET_WASM32_UNKNOWN_EMSCRIPTEN_RUNNER=node RUSTFLAGS="${common_flags}" \
EM_CACHE="${cache_dir}" run cargo +nightly test \
--features godot/experimental-wasm-nothreads,godot/experimental-wasm,godot/lazy-function-tables \
-Zbuild-std --target wasm32-unknown-emscripten
}

function cmd_doc() {
run cargo doc --lib -p godot --no-deps "${extraCargoArgs[@]}"
}
Expand Down Expand Up @@ -211,7 +244,7 @@ while [[ $# -gt 0 ]]; do
--double)
extraCargoArgs+=("--features" "godot/double-precision,godot/api-custom")
;;
fmt | test | itest | clippy | klippy | doc | dok)
fmt | test | itest | testweb | clippy | klippy | doc | dok)
cmds+=("$arg")
;;
-f | --filter)
Expand Down
20 changes: 20 additions & 0 deletions godot-cell/tests/mock/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ impl MyClass {
///
/// This should not cause borrow failures and should not lead to deadlocks.
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
Comment on lines +43 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth a module to not annotate each individual test? 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good idea!

fn calls_parallel() {
use std::thread;

Expand Down Expand Up @@ -72,6 +76,10 @@ fn calls_parallel() {
/// Runs each method several times in a row. This should reduce the non-determinism that comes from
/// scheduling of threads.
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn calls_parallel_many_serial() {
use std::thread;

Expand Down Expand Up @@ -106,6 +114,10 @@ fn calls_parallel_many_serial() {
/// Runs all the tests several times. This is different from [`calls_parallel_many_serial`] as that calls the
/// methods like AAA...BBB...CCC..., whereas this interleaves the methods like ABC...ABC...ABC...
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn calls_parallel_many_parallel() {
use std::thread;

Expand Down Expand Up @@ -142,6 +154,10 @@ fn calls_parallel_many_parallel() {
/// a) Thread A holds mutable reference AND thread B holds no references.
/// b) One or more threads hold shared references AND thread A holds no references
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn non_blocking_reborrow() {
use std::thread;
let instance_id = MyClass::init();
Expand Down Expand Up @@ -172,6 +188,10 @@ fn non_blocking_reborrow() {
/// This verifies that the thread which initialized the `GdCell` does not panic when it attempts to mutably borrow while there is already a
/// shared borrow on an other thread.
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn no_mut_panic_on_main() {
use std::thread;
let instance_id = MyClass::init();
Expand Down
16 changes: 16 additions & 0 deletions godot-cell/tests/mock/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ fn all_calls_work() {

/// Run each method both from the main thread and a newly created thread.
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn calls_different_thread() {
use std::thread;

Expand Down Expand Up @@ -87,6 +91,10 @@ fn calls_different_thread() {
/// if the first call failed, so then we know the integer was incremented by 0. Otherwise, we at least know
/// the range of values that it can be incremented by.
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn calls_parallel() {
use std::thread;

Expand Down Expand Up @@ -121,6 +129,10 @@ fn calls_parallel() {
/// Runs each method several times in a row. This should reduce the non-determinism that comes from
/// scheduling of threads.
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn calls_parallel_many_serial() {
use std::thread;

Expand Down Expand Up @@ -157,6 +169,10 @@ fn calls_parallel_many_serial() {
/// Runs all the tests several times. This is different from [`calls_parallel_many_serial`] as that calls the
/// methods like AAA...BBB...CCC..., whereas this interleaves the methods like ABC...ABC...ABC...
#[test]
#[cfg_attr(
all(target_family = "wasm", not(target_feature = "atomics")),
ignore = "Threading not available"
)]
fn calls_parallel_many_parallel() {
use std::thread;

Expand Down
Loading