Skip to content
Merged
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
6 changes: 0 additions & 6 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ slow-timeout = { period = "120s", terminate-after = 3 }
filter = 'test(state_migration_generate_actors_metadata)'
slow-timeout = { period = "120s", terminate-after = 3 }

# This test downloads RPC test snapshot files from the network, which can take a while.
# It is only run on CI, so we can afford to be more patient.
[[profile.default.overrides]]
filter = 'test(tool::subcommands::api_cmd::test_snapshot::tests::rpc_regression_tests)'
slow-timeout = { period = "120s", terminate-after = 3 }

[[profile.default.overrides]]
# lint runs `cargo check` for source file discovery, which can take a while
filter = 'binary(lint)'
Expand Down
39 changes: 39 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright 2019-2025 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use std::io::Write;

fn main() {
// whitelist the cfg for cargo clippy
println!("cargo::rustc-check-cfg=cfg(f3sidecar)");
Expand Down Expand Up @@ -30,6 +32,8 @@ fn main() {
// })
.build();
}

rpc_regression_tests_gen();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// See <https://docs.rs/about/builds#detecting-docsrs>
Expand All @@ -44,3 +48,38 @@ fn is_sidecar_ffi_enabled() -> bool {
_ => true,
}
}

fn rpc_regression_tests_gen() {
use std::{fs::File, io::BufWriter, path::PathBuf};

println!("cargo:rerun-if-changed=src/tool/subcommands/api_cmd/test_snapshots.txt");

let tests: Vec<&str> = include_str!("src/tool/subcommands/api_cmd/test_snapshots.txt")
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.collect();
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
let out_path = out_dir.join("__rpc_regression_tests_gen.rs");
let mut w = BufWriter::new(File::create(&out_path).unwrap());
for test in tests {
// Derive a valid Rust identifier from the snapshot filename.
let ident = test
.strip_suffix(".rpcsnap.json.zst")
.unwrap_or(test)
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
.collect::<String>();

writeln!(
w,
r#"
#[tokio::test(flavor = "multi_thread")]
async fn rpc_test_{ident}() {{
rpc_regression_test_run("{test}").await
}}
"#,
)
.unwrap();
}
}
6 changes: 3 additions & 3 deletions docs/docs/developers/guides/rpc_test_snapshot.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ A test snapshot generated in the previous step is in JSON format, for easier ins

- Manual Method
1. Compress the test snapshots if not already done.
2. Upload the `.json.zst` files to the DigitalOcean space `forest-snapshots/rpc_test`
2. Upload the `{filename}.rpcsnap.json.zst` files to the DigitalOcean space `forest-snapshots/rpc_test`
3. Include the file names in `src/tool/subcommands/api_cmd/test_snapshots.txt`
4. Run the tests:
4. Run the test:
```
cargo test --lib -- --test rpc_regression_tests --nocapture
cargo test --lib {filename} -- --nocapture
```

- Using the Script
Expand Down
77 changes: 32 additions & 45 deletions src/tool/subcommands/api_cmd/test_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,68 +184,55 @@ mod tests {
use crate::utils::net::{DownloadFileOption, download_file_with_cache};
use crate::utils::proofs_api::ensure_proof_params_downloaded;
use ahash::HashSet;
use anyhow::Context as _;
use directories::ProjectDirs;
use futures::{StreamExt, stream::FuturesUnordered};
use itertools::Itertools as _;
use std::sync::LazyLock;
use std::time::Instant;
use tokio::sync::Semaphore;
use tokio::sync::Mutex;
use url::Url;

#[tokio::test(flavor = "multi_thread")]
async fn rpc_regression_tests() {
// To run a single test: cargo test --lib filecoin_multisig_statedecodeparams_1754230255631789 -- --nocapture
include!(concat!(env!("OUT_DIR"), "/__rpc_regression_tests_gen.rs"));

async fn rpc_regression_test_run(name: &str) {
// Skip for debug build on CI as the downloading is slow and flaky
if crate::utils::is_ci() && crate::utils::is_debug_build() {
return;
}

// Set proof parameter data dir and make sure the proofs are available
crate::utils::proofs_api::maybe_set_proofs_parameter_cache_dir_env(
&Config::default().client.data_dir,
);
ensure_proof_params_downloaded().await.unwrap();
let urls = include_str!("test_snapshots.txt")
.trim()
.split("\n")
.filter_map(|n| {
Url::parse(
format!(
"https://forest-snapshots.fra1.cdn.digitaloceanspaces.com/rpc_test/{n}"
)
.as_str(),
)
.ok()
.map(|url| (n, url))
})
.collect_vec();
{
static PROOF_PARAMS_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
let _guard = PROOF_PARAMS_LOCK.lock().await;
crate::utils::proofs_api::maybe_set_proofs_parameter_cache_dir_env(
&Config::default().client.data_dir,
);
ensure_proof_params_downloaded().await.unwrap();
}
let url: Url =
format!("https://forest-snapshots.fra1.cdn.digitaloceanspaces.com/rpc_test/{name}")
.parse()
.with_context(|| format!("Failed to parse URL for test: {name}"))
.unwrap();
let project_dir = ProjectDirs::from("com", "ChainSafe", "Forest").unwrap();
let cache_dir = project_dir.cache_dir().join("test").join("rpc-snapshots");
let semaphore = Arc::new(Semaphore::new(4));
let mut tasks = FuturesUnordered::from_iter(urls.into_iter().map(|(filename, url)| {
let cache_dir = cache_dir.clone();
let semaphore = semaphore.clone();
async move {
let _permit = semaphore.acquire().await.unwrap();
let result =
download_file_with_cache(&url, &cache_dir, DownloadFileOption::NonResumable)
.await
.unwrap();
(filename, result.path)
}
}));
let path = download_file_with_cache(&url, &cache_dir, DownloadFileOption::NonResumable)
.await
.unwrap()
.path;

// We need to set RNG seed so that tests are run with deterministic
// output. The snapshots should be generated with a node running with the same seed, if
// they are testing methods that are not deterministic, e.g.,
// `[`crate::rpc::methods::gas::estimate_gas_premium`]`.
unsafe { std::env::set_var(crate::utils::rand::FIXED_RNG_SEED_ENV, "4213666") };
while let Some((filename, file_path)) = tasks.next().await {
print!("Testing {filename} ...");
let start = Instant::now();
run_test_from_snapshot(&file_path).await.unwrap();
println!(
" succeeded, took {}.",
humantime::format_duration(start.elapsed())
);
}
print!("Testing {name} ...");
let start = Instant::now();
run_test_from_snapshot(&path).await.unwrap();
println!(
" succeeded, took {}.",
humantime::format_duration(start.elapsed())
);
Comment on lines +229 to +235
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need the manual timings? Isn't it redundant by what nextest provides already?

Copy link
Copy Markdown
Contributor Author

@hanabi1224 hanabi1224 Aug 13, 2025

Choose a reason for hiding this comment

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

It's still helpful for cargo test running multiple tests. It also eliminates time for downloading the snapshot file. e.g.

 cargo test --lib -- --nocapture --test filecoin_ethgetblocktransactioncount
    Finished `test` profile [unoptimized] target(s) in 0.37s
     Running unittests src/lib.rs (target/debug/deps/forest-7c1bb6fa1af2e8fa)

running 2 tests
Testing filecoin_ethgetblocktransactioncountbynumber_1737446676697272.rpcsnap.json.zst ...  succeeded, took 203ms 866us 755ns.
test tool::subcommands::api_cmd::test_snapshot::tests::rpc_test_filecoin_ethgetblocktransactioncountbynumber_1737446676697272 ... ok
Testing filecoin_ethgetblocktransactioncountbyhash_1740132538001911.rpcsnap.json.zst ...  succeeded, took 500ms 885us 826ns.
test tool::subcommands::api_cmd::test_snapshot::tests::rpc_test_filecoin_ethgetblocktransactioncountbyhash_1740132538001911 ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 738 filtered out; finished in 48.51s

}

#[test]
Expand Down
Loading