diff --git a/crates/forge/tests/data/wasm_oracles/Scarb.toml b/crates/forge/tests/data/wasm_oracles/Scarb.toml new file mode 100644 index 0000000000..6141b240c5 --- /dev/null +++ b/crates/forge/tests/data/wasm_oracles/Scarb.toml @@ -0,0 +1,15 @@ +[package] +name = "oracles" +version = "0.1.0" +edition = "2024_07" +assets = ["wasm_oracle.wasm"] + +#[[target.starknet-contract]] +#sierra = true + +[dependencies] +oracle = "1" +starknet = "2.4.0" + +[dev-dependencies] +snforge_std = { path = "../../../../../snforge_std" } diff --git a/crates/forge/tests/data/wasm_oracles/build-fixtures.sh b/crates/forge/tests/data/wasm_oracles/build-fixtures.sh new file mode 100755 index 0000000000..8d865b6492 --- /dev/null +++ b/crates/forge/tests/data/wasm_oracles/build-fixtures.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +set -ex + +# Run this script to generate wasm fixtures from their sources. +# Prebuilt fixtures are expected to be committed to the repository. + +cd "$(dirname "$0")" + +cargo build --manifest-path=wasm_oracle/Cargo.toml --release --target wasm32-wasip2 +cp wasm_oracle/target/wasm32-wasip2/release/wasm_oracle.wasm . diff --git a/crates/forge/tests/data/wasm_oracles/src/lib.cairo b/crates/forge/tests/data/wasm_oracles/src/lib.cairo new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/crates/forge/tests/data/wasm_oracles/src/lib.cairo @@ -0,0 +1 @@ + diff --git a/crates/forge/tests/data/wasm_oracles/tests/test.cairo b/crates/forge/tests/data/wasm_oracles/tests/test.cairo new file mode 100644 index 0000000000..c0ca8e40ac --- /dev/null +++ b/crates/forge/tests/data/wasm_oracles/tests/test.cairo @@ -0,0 +1,43 @@ +use core::panics::panic_with_byte_array; + +mod wasm_oracle { + pub fn add(left: u64, right: u64) -> oracle::Result { + oracle::invoke("wasm:wasm_oracle.wasm", "add", (left, right)) + } + + pub fn err() -> oracle::Result> { + oracle::invoke("wasm:wasm_oracle.wasm", "err", ()) + } + + pub fn panic() -> oracle::Result> { + oracle::invoke("wasm:wasm_oracle.wasm", "panic", ()) + } +} + +#[test] +fn add() { + assert!(wasm_oracle::add(2, 3) == Ok(5)); +} + +#[test] +fn err() { + assert!(wasm_oracle::err() == Ok(Err("failed hard"))); +} + +#[should_panic] +#[test] +fn panic() { + wasm_oracle::panic().unwrap().unwrap(); +} + +#[test] +fn unexpected_panic() { + wasm_oracle::panic().unwrap().unwrap(); +} + +#[test] +fn panic_contents() { + let err = wasm_oracle::panic().unwrap_err(); + // Panic with error so we get better error than "unwrap failed" + panic_with_byte_array(@format!("{}", err)) +} diff --git a/crates/forge/tests/data/wasm_oracles/wasm_oracle.wasm b/crates/forge/tests/data/wasm_oracles/wasm_oracle.wasm new file mode 100644 index 0000000000..5908dd3006 Binary files /dev/null and b/crates/forge/tests/data/wasm_oracles/wasm_oracle.wasm differ diff --git a/crates/forge/tests/data/wasm_oracles/wasm_oracle/Cargo.lock b/crates/forge/tests/data/wasm_oracles/wasm_oracle/Cargo.lock new file mode 100644 index 0000000000..739a8355d2 --- /dev/null +++ b/crates/forge/tests/data/wasm_oracles/wasm_oracle/Cargo.lock @@ -0,0 +1,423 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasm-encoder" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm_oracle" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasmparser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" +dependencies = [ + "bitflags", + "hashbrown", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +dependencies = [ + "bitflags", + "futures", + "once_cell", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabd629f94da277abc739c71353397046401518efb2c707669f805205f0b9890" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4232e841089fa5f3c4fc732a92e1c74e1a3958db3b12f1de5934da2027f1f4" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d4698c2913d8d9c2b220d116409c3f51a7aa8d7765151b886918367179ee9" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.239.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] diff --git a/crates/forge/tests/data/wasm_oracles/wasm_oracle/Cargo.toml b/crates/forge/tests/data/wasm_oracles/wasm_oracle/Cargo.toml new file mode 100644 index 0000000000..256b1a140e --- /dev/null +++ b/crates/forge/tests/data/wasm_oracles/wasm_oracle/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] + +[package] +name = "wasm_oracle" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = "0.46.0" diff --git a/crates/forge/tests/data/wasm_oracles/wasm_oracle/src/lib.rs b/crates/forge/tests/data/wasm_oracles/wasm_oracle/src/lib.rs new file mode 100644 index 0000000000..be493b1bd0 --- /dev/null +++ b/crates/forge/tests/data/wasm_oracles/wasm_oracle/src/lib.rs @@ -0,0 +1,29 @@ +wit_bindgen::generate!({ + inline: r#" + package testing:oracle; + + world oracle { + export add: func(left: u64, right: u64) -> u64; + export err: func() -> result; + export panic: func() -> result; + } + "# +}); + +struct MyOracle; + +impl Guest for MyOracle { + fn add(left: u64, right: u64) -> u64 { + left + right + } + + fn err() -> Result { + Err("failed hard".into()) + } + + fn panic() -> Result { + panic!("panicked") + } +} + +export!(MyOracle); diff --git a/crates/forge/tests/e2e/mod.rs b/crates/forge/tests/e2e/mod.rs index 7b9c94a338..db2ac3d9f0 100644 --- a/crates/forge/tests/e2e/mod.rs +++ b/crates/forge/tests/e2e/mod.rs @@ -25,6 +25,7 @@ mod fuzzing; mod gas_report; mod io_operations; mod new; +mod oracles; mod package_warnings; mod plugin_diagnostics; mod plugin_versions; diff --git a/crates/forge/tests/e2e/oracles.rs b/crates/forge/tests/e2e/oracles.rs new file mode 100644 index 0000000000..dc0a7b703b --- /dev/null +++ b/crates/forge/tests/e2e/oracles.rs @@ -0,0 +1,61 @@ +use crate::e2e::common::runner::{ + BASE_FILE_PATTERNS, Package, setup_package_with_file_patterns, test_runner, +}; +use indoc::indoc; +use scarb_api::version::scarb_version; +use shared::test_utils::output_assert::assert_stdout_contains; + +fn scarb_supports_oracles() -> bool { + scarb_version().unwrap().scarb >= semver::Version::parse("2.12.3+nightly-2025-10-21").unwrap() +} + +#[test] +fn wasm() { + // TODO use feature here + if !scarb_supports_oracles() { + eprintln!("skipping because scarb does not fully support oracles"); + return; + } + + let temp = setup_package_with_file_patterns( + Package::Name("wasm_oracles".to_string()), + &[BASE_FILE_PATTERNS, &["*.wasm"]].concat(), + ); + + let output = test_runner(&temp) + // Output of oracle is different depending on the env, and Intellij sets it automatically + .env_remove("RUST_BACKTRACE") + .arg("--experimental-oracles") + .assert() + .code(1); + + assert_stdout_contains( + output, + indoc! {r#" + [..]Compiling[..] + [..]Finished[..] + + Collected 5 test(s) from oracles package + Running 5 test(s) from tests/ + [PASS] oracles_integrationtest::test::err ([..]) + [PASS] oracles_integrationtest::test::add ([..]) + [PASS] oracles_integrationtest::test::panic ([..]) + [FAIL] oracles_integrationtest::test::unexpected_panic + + Failure data: + 0x526573756c743a3a756e77726170206661696c65642e ('Result::unwrap failed.') + [FAIL] oracles_integrationtest::test::panic_contents + + Failure data: + "error while executing at wasm backtrace: + [..] + [..] wasm_oracle.wasm!panic + + Caused by: + wasm trap: wasm `unreachable` instruction executed" + + Running 0 test(s) from src/ + Tests: 3 passed, 2 failed, 0 ignored, 0 filtered out + "#}, + ); +}