Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
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());
Comment thread
coderabbitai[bot] marked this conversation as resolved.
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();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
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 -- --nocapture --test filecoin_multisig_statedecodeparams_1754230255631789

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we can include this info in rpc_test_snapshot.md

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

updated

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?

@hanabi1224 hanabi1224 Aug 13, 2025

Copy link
Copy Markdown
Contributor Author

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