diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..7cc205090b --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.* text eol=lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6903bcfb45..2c644924a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,8 @@ name: CI +env: + DEVNET_REV: ef789b700770fa27a2fc057b3d1c610771be27d9 + on: pull_request: merge_group: @@ -11,7 +14,10 @@ on: jobs: test-forge-unit-and-integration: name: Test Forge / Unit and Integration Tests - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, windows-latest ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -19,11 +25,14 @@ jobs: - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 - run: cargo test --release --lib -p forge - - run: cargo test --release --bin snforge - run: cargo test --release integration -p forge build-test-forge-e2e-nextest-archive: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, windows-latest ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -31,48 +40,57 @@ jobs: - name: Install nextest uses: taiki-e/install-action@nextest - name: Build and archive tests - run: cargo nextest archive --release -p forge --archive-file nextest-archive.tar.zst + run: cargo nextest archive --release -p forge --archive-file 'nextest-archive-${{ matrix.os }}.tar.zst' - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: - name: nextest-archive - path: nextest-archive.tar.zst + name: nextest-archive-${{ matrix.os }} + path: nextest-archive-${{ matrix.os }}.tar.zst test-forge-e2e: name: Test Forge / E2E Tests - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} needs: build-test-forge-e2e-nextest-archive strategy: + fail-fast: false matrix: partition: [ 1, 2, 3, 4, 5, 6, 7, 8 ] + os: [ ubuntu-latest, windows-latest ] steps: - name: Extract branch name if: github.event_name != 'pull_request' run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV + shell: bash - name: Extract branch name on pull request if: github.event_name == 'pull_request' run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV + shell: bash - name: Extract repo name and owner if: github.event_name != 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.repository }}.git)" >> $GITHUB_ENV + shell: bash - name: Extract repo name and owner on pull request if: github.event_name == 'pull_request' run: echo "REPO_NAME=$(echo ${{ github.event.pull_request.head.repo.full_name }}.git)" >> $GITHUB_ENV + shell: bash - name: Print repo name run: echo 'The repo name is' $REPO_NAME + shell: bash - name: Get branch name run: echo 'The branch name is' $BRANCH_NAME + shell: bash - name: Install cairo-profiler + if: runner.os != 'Windows' run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-profiler/main/scripts/install.sh | sh - - name: Install cairo-coverage + if: runner.os != 'Windows' run: | curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh @@ -84,9 +102,9 @@ jobs: - uses: taiki-e/install-action@nextest - uses: actions/download-artifact@v4 with: - name: nextest-archive + name: nextest-archive-${{ matrix.os }} - name: nextest partition ${{ matrix.partition }}/8 - run: cargo nextest run --partition 'count:${{ matrix.partition }}/8' --archive-file 'nextest-archive.tar.zst' e2e + run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/8' --archive-file 'nextest-archive-${{ matrix.os }}.tar.zst' e2e test-scarb-2-8-3: name: Test scarb 2.8.3 @@ -188,23 +206,38 @@ jobs: test-cast: name: Test Cast - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, windows-latest ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a with: toolchain: stable - uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab - - name: Install starknet-devnet-rs - run: ./scripts/install_devnet.sh + - name: Install starknet-devnet-rs on Linux/Macos + if: runner.os != 'Windows' + run: | + ./scripts/install_devnet.sh + - name: Cache devnet build + if: runner.os == 'Windows' + id: windows-devnet-cache + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}\crates\sncast\tests\utils\devnet + key: ${{ runner.os }}-devnet-${{ env.DEVNET_REV }} + - name: Install devnet + if: runner.os == 'Windows' && steps.windows-devnet-cache.outputs.cache-hit != 'true' + run: | + $DEVNET_INSTALL_DIR = "${{ github.workspace }}\crates\sncast\tests\utils\devnet" + cargo install --git https://github.com/0xSpaceShard/starknet-devnet-rs.git --locked --rev ${{ env.DEVNET_REV }} --root $DEVNET_INSTALL_DIR - uses: software-mansion/setup-scarb@v1 with: scarb-version: "2.7.0" - uses: software-mansion/setup-universal-sierra-compiler@v1 - name: Run tests run: cargo test --release -p sncast - - name: Run test in debug profile - run: cargo test -p sncast test_happy_case_common_arguments_after_subcommand test-conversions: name: Test Conversions @@ -229,7 +262,10 @@ jobs: test-scarb-api: name: Test Scarb Api - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, windows-latest ] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable diff --git a/CHANGELOG.md b/CHANGELOG.md index b613a343c7..87c0f342ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Forge + +#### Changed + +- Trace files saved in `snfoundry_trace` directory will now use `_` as separators instead of `::` + ### Cast #### Added - interactive interface that allows setting created or imported account as the default - ## [0.35.1] - 2024-12-16 ### Forge diff --git a/Cargo.lock b/Cargo.lock index 459bfe7ef1..041d984469 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -2227,6 +2227,7 @@ dependencies = [ "rand", "rayon", "runtime", + "sanitize-filename", "scarb-api", "semver", "serde", @@ -4423,6 +4424,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sanitize-filename" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d" +dependencies = [ + "regex", +] + [[package]] name = "scarb-api" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 32d2cf3367..24851799cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,3 +111,4 @@ wiremock = "0.6.0" const-hex = "1.14.0" indicatif = "0.17.9" shell-words = "1.1.0" +sanitize-filename = "0.6.0" diff --git a/crates/forge-runner/Cargo.toml b/crates/forge-runner/Cargo.toml index 1abab5362f..ae0dfec1c1 100644 --- a/crates/forge-runner/Cargo.toml +++ b/crates/forge-runner/Cargo.toml @@ -45,3 +45,4 @@ shared = { path = "../shared" } universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } fs4.workspace = true which.workspace = true +sanitize-filename.workspace = true diff --git a/crates/forge-runner/src/build_trace_data.rs b/crates/forge-runner/src/build_trace_data.rs index fd9db15d9e..d5ce8019ba 100644 --- a/crates/forge-runner/src/build_trace_data.rs +++ b/crates/forge-runner/src/build_trace_data.rs @@ -292,7 +292,7 @@ fn build_profiler_trace_entry(value: &RelocatedTraceEntry) -> ProfilerTraceEntry } pub fn save_trace_data( - test_name: &String, + test_name: &str, trace_data: &VersionedProfilerCallTrace, ) -> Result { let serialized_trace = diff --git a/crates/forge-runner/src/lib.rs b/crates/forge-runner/src/lib.rs index cd65c6544c..93fee082b5 100644 --- a/crates/forge-runner/src/lib.rs +++ b/crates/forge-runner/src/lib.rs @@ -65,9 +65,10 @@ pub fn maybe_save_trace_and_profile( }) = result { if execution_data_to_save.is_vm_trace_needed() { - let trace_path = save_trace_data(name, trace_data)?; + let name = sanitize_filename::sanitize(name.replace("::", "_")); + let trace_path = save_trace_data(&name, trace_data)?; if execution_data_to_save.profile { - run_profiler(name, &trace_path, &execution_data_to_save.additional_args)?; + run_profiler(&name, &trace_path, &execution_data_to_save.additional_args)?; } return Ok(Some(trace_path)); } diff --git a/crates/forge/tests/data/exit_first/tests/ext_function_test.cairo b/crates/forge/tests/data/exit_first/tests/ext_function_test.cairo index 09d8dc6c93..e74e9adb88 100644 --- a/crates/forge/tests/data/exit_first/tests/ext_function_test.cairo +++ b/crates/forge/tests/data/exit_first/tests/ext_function_test.cairo @@ -2,7 +2,7 @@ use exit_first::fib; #[test] fn hard_test() { - fib(0, 1, 30344); + fib(0, 1, 99999999999999999999999); assert(2 == 2, 'simple check'); } diff --git a/crates/forge/tests/e2e/build_profile.rs b/crates/forge/tests/e2e/build_profile.rs index 975c8e452e..6b3fc59974 100644 --- a/crates/forge/tests/e2e/build_profile.rs +++ b/crates/forge/tests/e2e/build_profile.rs @@ -2,6 +2,7 @@ use super::common::runner::{setup_package, test_runner}; use forge_runner::profiler_api::PROFILE_DIR; #[test] +#[cfg(not(target_os = "windows"))] fn simple_package_build_profile() { let temp = setup_package("simple_package"); @@ -9,19 +10,19 @@ fn simple_package_build_profile() { assert!(temp .join(PROFILE_DIR) - .join("simple_package::tests::test_fib.pb.gz") + .join("simple_package_tests_test_fib.pb.gz") .is_file()); assert!(!temp .join(PROFILE_DIR) - .join("simple_package_integrationtest::test_simple::test_failing.pb.gz") + .join("simple_package_integrationtest_test_simple_test_failing.pb.gz") .is_file()); assert!(!temp .join(PROFILE_DIR) - .join("simple_package::tests::ignored_test.pb.gz") + .join("simple_package_tests_ignored_test.pb.gz") .is_file()); assert!(temp .join(PROFILE_DIR) - .join("simple_package_integrationtest::ext_function_test::test_simple.pb.gz") + .join("simple_package_integrationtest_ext_function_test_test_simple.pb.gz") .is_file()); // Check if it doesn't crash in case some data already exists @@ -29,6 +30,7 @@ fn simple_package_build_profile() { } #[test] +#[cfg(not(target_os = "windows"))] fn simple_package_build_profile_and_pass_args() { let temp = setup_package("simple_package"); diff --git a/crates/forge/tests/e2e/build_trace_data.rs b/crates/forge/tests/e2e/build_trace_data.rs index b8f4f22c45..a794309a9f 100644 --- a/crates/forge/tests/e2e/build_trace_data.rs +++ b/crates/forge/tests/e2e/build_trace_data.rs @@ -16,24 +16,24 @@ fn simple_package_save_trace() { assert!(temp .join(TRACE_DIR) - .join("simple_package::tests::test_fib.json") + .join("simple_package_tests_test_fib.json") .exists()); assert!(!temp .join(TRACE_DIR) - .join("simple_package_integrationtest::test_simple::test_failing.json") + .join("simple_package_integrationtest_test_simple_test_failing.json") .exists()); assert!(!temp .join(TRACE_DIR) - .join("simple_package::tests::ignored_test.json") + .join("simple_package_tests_ignored_test.json") .exists()); assert!(temp .join(TRACE_DIR) - .join("simple_package_integrationtest::ext_function_test::test_simple.json") + .join("simple_package_integrationtest_ext_function_test_test_simple.json") .exists()); let trace_data = fs::read_to_string( temp.join(TRACE_DIR) - .join("simple_package_integrationtest::ext_function_test::test_simple.json"), + .join("simple_package_integrationtest_ext_function_test_test_simple.json"), ) .unwrap(); @@ -56,7 +56,7 @@ fn trace_has_contract_and_function_names() { let trace_data = fs::read_to_string( temp.join(TRACE_DIR) - .join("trace_info_integrationtest::test_trace::test_trace.json"), + .join("trace_info_integrationtest_test_trace_test_trace.json"), ) .unwrap(); @@ -103,7 +103,7 @@ fn trace_has_cairo_execution_info() { let trace_data = fs::read_to_string( temp.join(TRACE_DIR) - .join("trace_info_integrationtest::test_trace::test_trace.json"), + .join("trace_info_integrationtest_test_trace_test_trace.json"), ) .unwrap(); @@ -145,7 +145,7 @@ fn trace_has_deploy_with_no_constructor_phantom_nodes() { let trace_data = fs::read_to_string( temp.join(TRACE_DIR) - .join("trace_info_integrationtest::test_trace::test_trace.json"), + .join("trace_info_integrationtest_test_trace_test_trace.json"), ) .unwrap(); @@ -177,7 +177,7 @@ fn trace_is_produced_even_if_contract_panics() { let trace_data = fs::read_to_string( temp.join(TRACE_DIR) - .join("backtrace_panic::Test::test_contract_panics.json"), + .join("backtrace_panic_Test_test_contract_panics.json"), ) .unwrap(); diff --git a/crates/forge/tests/e2e/io_operations.rs b/crates/forge/tests/e2e/io_operations.rs index af7394c16e..f5731d8e7e 100644 --- a/crates/forge/tests/e2e/io_operations.rs +++ b/crates/forge/tests/e2e/io_operations.rs @@ -1,6 +1,6 @@ use super::common::runner::{setup_package_with_file_patterns, test_runner, BASE_FILE_PATTERNS}; use assert_fs::fixture::PathChild; -use indoc::indoc; +use indoc::formatdoc; use shared::test_utils::output_assert::assert_stdout_contains; #[test] @@ -11,7 +11,13 @@ fn file_reading() { &[BASE_FILE_PATTERNS, &["**/*.txt", "**/*.json"]].concat(), ); - let expected = indoc! {r#" + let expected_file_error = if cfg!(target_os = "windows") { + "The system cannot find the file specified[..]" + } else { + "No such file or directory [..]" + }; + + let expected = formatdoc! {r#" [..]Compiling[..] [..]Finished[..] @@ -21,7 +27,7 @@ fn file_reading() { [FAIL] file_reading_integrationtest::test::json_non_existent Failure data: - "No such file or directory [..]" + "{}" [FAIL] file_reading_integrationtest::test::invalid_json @@ -31,7 +37,7 @@ fn file_reading() { [FAIL] file_reading_integrationtest::test::non_existent Failure data: - "No such file or directory [..]" + "{}" [FAIL] file_reading_integrationtest::test::non_ascii @@ -62,24 +68,24 @@ fn file_reading() { file_reading_integrationtest::test::non_ascii file_reading_integrationtest::test::valid_content_different_folder file_reading_integrationtest::test::negative_number - "#}; + "#, expected_file_error, expected_file_error}; // run from different directories to make sure cwd is always set to package directory let output = test_runner(&temp).assert().code(1); - assert_stdout_contains(output, expected); + assert_stdout_contains(output, &expected); let output = test_runner(&temp) .current_dir(temp.child("src")) .assert() .code(1); - assert_stdout_contains(output, expected); + assert_stdout_contains(output, &expected); let output = test_runner(&temp) .current_dir(temp.child("data")) .assert() .code(1); - assert_stdout_contains(output, expected); + assert_stdout_contains(output, &expected); } diff --git a/crates/forge/tests/e2e/mod.rs b/crates/forge/tests/e2e/mod.rs index 838700d5bd..27b4da0141 100644 --- a/crates/forge/tests/e2e/mod.rs +++ b/crates/forge/tests/e2e/mod.rs @@ -11,7 +11,9 @@ mod coverage; mod docs_snippets_validation; mod env; mod features; +#[cfg(not(target_os = "windows"))] mod fork_warning; +#[cfg(not(target_os = "windows"))] mod forking; mod fuzzing; mod io_operations; diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 0b18c2553d..d547747a75 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -1100,21 +1100,31 @@ fn detailed_resources_flag() { fn catch_runtime_errors() { let temp = setup_package("simple_package"); + let expected_panic = if cfg!(target_os = "windows") { + "The system cannot find the file specified" + } else { + "No such file or directory" + }; + temp.child("tests/test.cairo") - .write_str(indoc!( - r#" - use snforge_std::fs::{FileTrait, read_txt}; + .write_str( + formatdoc!( + r#" + use snforge_std::fs::{{FileTrait, read_txt}}; #[test] - #[should_panic(expected: "No such file or directory (os error 2)")] - fn catch_no_such_file() { + #[should_panic(expected: "{}")] + fn catch_no_such_file() {{ let file = FileTrait::new("no_way_this_file_exists"); let content = read_txt(@file); assert!(false); - } - "# - )) + }} + "#, + expected_panic + ) + .as_str(), + ) .unwrap(); let output = test_runner(&temp).assert(); diff --git a/crates/forge/tests/e2e/trace_resources.rs b/crates/forge/tests/e2e/trace_resources.rs index 7ceb37373f..8d2b69651a 100644 --- a/crates/forge/tests/e2e/trace_resources.rs +++ b/crates/forge/tests/e2e/trace_resources.rs @@ -65,7 +65,7 @@ fn assert_resources_for_test( fn deserialize_call_trace(test_name: &str, temp_dir: &TempDir) -> VersionedProfilerCallTrace { let trace_data = fs::read_to_string(temp_dir.join(TRACE_DIR).join(format!( - "trace_resources_tests::{test_name}::{test_name}.json" + "trace_resources_tests_{test_name}_{test_name}.json" ))) .unwrap(); serde_json::from_str(&trace_data).expect("Failed to parse call trace") diff --git a/crates/forge/tests/integration/mod.rs b/crates/forge/tests/integration/mod.rs index 50358ba7a3..6332e29e26 100644 --- a/crates/forge/tests/integration/mod.rs +++ b/crates/forge/tests/integration/mod.rs @@ -3,6 +3,7 @@ mod cheat_block_number; mod cheat_block_timestamp; mod cheat_caller_address; mod cheat_execution_info; +#[cfg(not(target_os = "windows"))] mod cheat_fork; mod cheat_sequencer_address; mod declare; @@ -23,6 +24,7 @@ mod pure_cairo; mod replace_bytecode; mod resources; mod runtime; +#[cfg(not(target_os = "windows"))] mod setup_fork; mod should_panic; mod signing; diff --git a/crates/forge/tests/integration/spy_events.rs b/crates/forge/tests/integration/spy_events.rs index 7e3d08385e..a1014c12a0 100644 --- a/crates/forge/tests/integration/spy_events.rs +++ b/crates/forge/tests/integration/spy_events.rs @@ -631,6 +631,7 @@ fn assert_not_emitted_fails() { assert_case_output_contains(&result, "assert_not_emitted_fails", "keys was emitted"); } +#[cfg(not(target_os = "windows"))] #[test] fn capture_cairo0_event() { let test = test_case!( diff --git a/crates/forge/tests/integration/store_load.rs b/crates/forge/tests/integration/store_load.rs index aebe21fdcf..3e8e52834c 100644 --- a/crates/forge/tests/integration/store_load.rs +++ b/crates/forge/tests/integration/store_load.rs @@ -556,6 +556,7 @@ fn store_load_felt_to_felt() { assert_passed(&result); } +#[cfg(not(target_os = "windows"))] #[test] fn fork_store_load() { let test = test_utils::test_case!(formatdoc!( diff --git a/crates/sncast/src/state/state_file.rs b/crates/sncast/src/state/state_file.rs index 920ea6f93c..bd0258d666 100644 --- a/crates/sncast/src/state/state_file.rs +++ b/crates/sncast/src/state/state_file.rs @@ -396,7 +396,7 @@ mod tests { } #[test] - #[should_panic(expected = "No such file or directory (os error 2)")] + #[should_panic(expected = "Failed to load state file")] fn test_load_state_file_invalid_path() { let state_file = Utf8PathBuf::from("bla/bla/crypto.json"); load_state_file(&state_file).unwrap(); diff --git a/crates/sncast/tests/e2e/account/import.rs b/crates/sncast/tests/e2e/account/import.rs index bbc3328f67..f67c7afb86 100644 --- a/crates/sncast/tests/e2e/account/import.rs +++ b/crates/sncast/tests/e2e/account/import.rs @@ -451,12 +451,18 @@ pub async fn test_invalid_private_key_file_path() { let snapbox = runner(&args); let output = snapbox.assert().success(); + let expected_file_error = if cfg!(target_os = "windows") { + "The system cannot find the file specified[..]" + } else { + "No such file or directory [..]" + }; + assert_stderr_contains( output, - indoc! {r" + formatdoc! {r" command: account import - error: Failed to obtain private key from the file my_private_key: No such file or directory (os error 2) - "}, + error: Failed to obtain private key from the file my_private_key: {} + ", expected_file_error}, ); } diff --git a/crates/sncast/tests/e2e/call.rs b/crates/sncast/tests/e2e/call.rs index 157102e856..6dd3e846ec 100644 --- a/crates/sncast/tests/e2e/call.rs +++ b/crates/sncast/tests/e2e/call.rs @@ -231,10 +231,23 @@ fn test_wrong_block_id() { #[test] fn test_happy_case_shell() { - let test_path = PathBuf::from("tests/shell/call.sh").canonicalize().unwrap(); + let script_extension = if cfg!(windows) { ".ps1" } else { ".sh" }; + let test_path = PathBuf::from(format!("tests/shell/call{script_extension}")) + .canonicalize() + .unwrap(); let binary_path = cargo_bin!("sncast"); - let snapbox = Command::new(test_path) + let command = if cfg!(windows) { + Command::new("powershell") + .arg("-ExecutionPolicy") + .arg("Bypass") + .arg("-File") + .arg(test_path) + } else { + Command::new(test_path) + }; + + let snapbox = command .arg(binary_path) .arg(URL) .arg(DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA); diff --git a/crates/sncast/tests/e2e/deploy.rs b/crates/sncast/tests/e2e/deploy.rs index feb57b8f0a..3de61512d4 100644 --- a/crates/sncast/tests/e2e/deploy.rs +++ b/crates/sncast/tests/e2e/deploy.rs @@ -440,12 +440,23 @@ fn test_too_low_max_fee() { async fn test_happy_case_shell() { let tempdir = create_and_deploy_oz_account().await; - let test_path = PathBuf::from("tests/shell/deploy.sh") + let script_extension = if cfg!(windows) { ".ps1" } else { ".sh" }; + let test_path = PathBuf::from(format!("tests/shell/deploy{script_extension}")) .canonicalize() .unwrap(); let binary_path = cargo_bin!("sncast"); - let snapbox = Command::new(test_path) + let command = if cfg!(windows) { + Command::new("powershell") + .arg("-ExecutionPolicy") + .arg("Bypass") + .arg("-File") + .arg(test_path) + } else { + Command::new(test_path) + }; + + let snapbox = command .current_dir(tempdir.path()) .arg(binary_path) .arg(URL) diff --git a/crates/sncast/tests/e2e/invoke.rs b/crates/sncast/tests/e2e/invoke.rs index 4d7fa721f2..63c85afba3 100644 --- a/crates/sncast/tests/e2e/invoke.rs +++ b/crates/sncast/tests/e2e/invoke.rs @@ -429,12 +429,23 @@ async fn test_happy_case_cairo_expression_calldata() { async fn test_happy_case_shell() { let tempdir = create_and_deploy_oz_account().await; - let test_path = PathBuf::from("tests/shell/invoke.sh") + let script_extension = if cfg!(windows) { ".ps1" } else { ".sh" }; + let test_path = PathBuf::from(format!("tests/shell/invoke{script_extension}")) .canonicalize() .unwrap(); let binary_path = cargo_bin!("sncast"); - let snapbox = Command::new(test_path) + let command = if cfg!(windows) { + Command::new("powershell") + .arg("-ExecutionPolicy") + .arg("Bypass") + .arg("-File") + .arg(test_path) + } else { + Command::new(test_path) + }; + + let snapbox = command .current_dir(tempdir.path()) .arg(binary_path) .arg(URL) diff --git a/crates/sncast/tests/e2e/multicall/new.rs b/crates/sncast/tests/e2e/multicall/new.rs index 79a8703ebc..0e813c085e 100644 --- a/crates/sncast/tests/e2e/multicall/new.rs +++ b/crates/sncast/tests/e2e/multicall/new.rs @@ -1,6 +1,6 @@ use crate::helpers::constants::ACCOUNT_FILE_PATH; use crate::helpers::runner::runner; -use indoc::indoc; +use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::{assert_stderr_contains, assert_stdout_contains, AsOutput}; use sncast::helpers::constants::DEFAULT_MULTICALL_CONTENTS; use tempfile::tempdir; @@ -47,7 +47,7 @@ async fn test_no_output_path_specified() { error: the following required arguments were not provided: - Usage: sncast multicall new + Usage: sncast[..] multicall new For more information, try '--help'. "}; @@ -73,12 +73,19 @@ async fn test_directory_non_existent() { let output = snapbox.assert().success(); assert!(output.as_stdout().is_empty()); + + let expected_file_error = if cfg!(target_os = "windows") { + "The system cannot find the path specified[..]" + } else { + "No such file or directory [..]" + }; + assert_stderr_contains( output, - indoc! {r" + formatdoc! {r" command: multicall new - error: No such file or directory[..] - "}, + error: {} + ", expected_file_error}, ); } diff --git a/crates/sncast/tests/e2e/multicall/run.rs b/crates/sncast/tests/e2e/multicall/run.rs index b944555c6f..557bed9c2f 100644 --- a/crates/sncast/tests/e2e/multicall/run.rs +++ b/crates/sncast/tests/e2e/multicall/run.rs @@ -1,7 +1,7 @@ use crate::helpers::constants::{ACCOUNT_FILE_PATH, MULTICALL_CONFIGS_DIR, URL}; use crate::helpers::fixtures::create_and_deploy_oz_account; use crate::helpers::runner::runner; -use indoc::indoc; +use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::{assert_stderr_contains, AsOutput}; use std::path::Path; use test_case::test_case; @@ -112,12 +112,19 @@ async fn test_invalid_path() { let output = snapbox.assert().success(); assert!(output.as_stdout().is_empty()); + + let expected_file_error = if cfg!(target_os = "windows") { + "The system cannot find the file specified[..]" + } else { + "No such file or directory [..]" + }; + assert_stderr_contains( output, - indoc! {r" + formatdoc! {r" command: multicall run - error: No such file or directory [..] - "}, + error: {} + ", expected_file_error}, ); } diff --git a/crates/sncast/tests/e2e/script/init.rs b/crates/sncast/tests/e2e/script/init.rs index 669ce5ea8f..5dcfe5c73c 100644 --- a/crates/sncast/tests/e2e/script/init.rs +++ b/crates/sncast/tests/e2e/script/init.rs @@ -133,7 +133,7 @@ fn test_initialized_script_compiles() { formatdoc! {r" [WARNING] The newly created script isn't auto-added to the workspace. [..] command: script init - message: Successfully initialized `{script_name}` at [..]/scripts/{script_name} + message: Successfully initialized `{script_name}` at [..]{script_name} "}, ); diff --git a/crates/sncast/tests/helpers/devnet.rs b/crates/sncast/tests/helpers/devnet.rs index 491f0634fb..63ebc92281 100644 --- a/crates/sncast/tests/helpers/devnet.rs +++ b/crates/sncast/tests/helpers/devnet.rs @@ -34,7 +34,13 @@ fn start_devnet() { } } - Command::new("tests/utils/devnet/starknet-devnet") + let devnet_path = if cfg!(target_os = "windows") { + "tests/utils/devnet/bin/starknet-devnet.exe" + } else { + "tests/utils/devnet/starknet-devnet" + }; + + Command::new(devnet_path) .args([ "--port", &port, @@ -81,11 +87,17 @@ fn start_devnet() { #[dtor] fn stop_devnet() { let port = Url::parse(URL).unwrap().port().unwrap_or(80).to_string(); - Command::new("pkill") - .args([ - "-f", - &format!("starknet-devnet.*{}.*{}", &port, &SEED.to_string())[..], - ]) - .spawn() - .expect("Failed to kill devnet processes"); + let pattern = format!("starknet-devnet.*{port}.*{SEED}"); + + if cfg!(target_os = "windows") { + Command::new("taskkill") + .args(["/IM", &pattern, "/F"]) + .output() + .expect("Failed to kill devnet processes"); + } else { + Command::new("pkill") + .args(["-f", &pattern]) + .output() + .expect("Failed to kill devnet processes"); + } } diff --git a/crates/sncast/tests/shell/call.ps1 b/crates/sncast/tests/shell/call.ps1 new file mode 100644 index 0000000000..12bcf8fbc3 --- /dev/null +++ b/crates/sncast/tests/shell/call.ps1 @@ -0,0 +1,19 @@ +$CAST_BINARY = $args[0] +$URL = $args[1] +$DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA = $args[2] +& $CAST_BINARY ` + --int-format ` + --json ` + call ` + --url $URL ` + --contract-address $DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA ` + --function complex_fn ` + --arguments @' +array![array![1, 2], array![3, 4, 5], array![6]], +12, +-128_i8, +\"Some string (a ByteArray)\", +('a shortstring', 32_u32), +true, +0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +'@ diff --git a/crates/sncast/tests/shell/deploy.ps1 b/crates/sncast/tests/shell/deploy.ps1 new file mode 100644 index 0000000000..40f369bad8 --- /dev/null +++ b/crates/sncast/tests/shell/deploy.ps1 @@ -0,0 +1,14 @@ +$CAST_BINARY = $args[0] +$URL = $args[1] +$CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA = $args[2] +& $CAST_BINARY ` + --accounts-file accounts.json ` + --account my_account ` + --int-format ` + --json ` + deploy ` + --url $URL ` + --class-hash $CONSTRUCTOR_WITH_PARAMS_CONTRACT_CLASS_HASH_SEPOLIA ` + --arguments '0x420, 0x2137_u256' ` + --max-fee 99999999999999999 ` + --fee-token eth diff --git a/crates/sncast/tests/shell/invoke.ps1 b/crates/sncast/tests/shell/invoke.ps1 new file mode 100644 index 0000000000..e9eeb02f07 --- /dev/null +++ b/crates/sncast/tests/shell/invoke.ps1 @@ -0,0 +1,23 @@ +$CAST_BINARY = $args[0] +$URL = $args[1] +$DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA = $args[2] +& $CAST_BINARY ` + --accounts-file accounts.json ` + --account my_account ` + --int-format ` + --json ` + invoke ` + --url $URL ` + --contract-address $DATA_TRANSFORMER_CONTRACT_ADDRESS_SEPOLIA ` + --function complex_fn ` + --arguments @' +array![array![1, 2], array![3, 4, 5], array![6]], +12, +-128_i8, +\"Some string (a ByteArray)\", +('a shortstring', 32_u32), +true, +0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +'@ ` + --max-fee 99999999999999999 ` + --fee-token eth diff --git a/scripts/install_devnet.ps1 b/scripts/install_devnet.ps1 new file mode 100644 index 0000000000..f73ea76f62 --- /dev/null +++ b/scripts/install_devnet.ps1 @@ -0,0 +1,14 @@ +Set-StrictMode -Version Latest + +$ErrorActionPreference = "Stop" + +$DEVNET_INSTALL_DIR = Join-Path (git rev-parse --show-toplevel) "crates\sncast\tests\utils\devnet" + +$DEVNET_REPO = "https://github.com/0xSpaceShard/starknet-devnet-rs.git" +$DEVNET_REV = "ef789b700770fa27a2fc057b3d1c610771be27d9" + +cargo install --locked --git $DEVNET_REPO --rev $DEVNET_REV --root $DEVNET_INSTALL_DIR --force + +Write-Host "All done!" + +exit 0