Skip to content

Commit f2d73c4

Browse files
authored
feat: refactor test utils (#2813)
* feat: refactor test utils * fix compilation * feat: add ee-test to unify ee tests * sort all json test fixtures * fmt * typo
1 parent 9008e93 commit f2d73c4

File tree

42 files changed

+1990
-2023
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1990
-2023
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ members = [
2222

2323
# utility
2424
"crates/statetest-types",
25+
"crates/ee-tests",
2526

2627
# examples
2728
"examples/block_traces",
@@ -54,6 +55,7 @@ context = { path = "crates/context", package = "revm-context", version = "8.0.4"
5455
context-interface = { path = "crates/context/interface", package = "revm-context-interface", version = "9.0.0", default-features = false }
5556
handler = { path = "crates/handler", package = "revm-handler", version = "8.1.0", default-features = false }
5657
op-revm = { path = "crates/op-revm", package = "op-revm", version = "8.1.0", default-features = false }
58+
ee-tests = { path = "crates/ee-tests", package = "revm-ee-tests", version = "0.1.0", default-features = false }
5759

5860
# alloy
5961
alloy-eip2930 = { version = "0.2.1", default-features = false }
@@ -165,7 +167,9 @@ strip = false
165167

166168
# Make sure debug symbols are in the bench profile
167169
[profile.bench]
168-
inherits = "profiling"
170+
debug = 2
171+
inherits = "release"
172+
strip = false
169173

170174
[profile.ethtests]
171175
inherits = "test"

crates/ee-tests/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "revm-ee-tests"
3+
version = "0.1.0"
4+
authors = ["revm"]
5+
edition = "2021"
6+
rust-version = "1.80.1"
7+
license = "MIT"
8+
description = "Common test utilities for REVM crates"
9+
repository = "https://github.com/bluealloy/revm"
10+
11+
[dependencies]
12+
serde = { version = "1.0", features = ["derive"] }
13+
serde_json = { version = "1.0", features = ["preserve_order"] }
14+
revm = { workspace = true, features = ["serde"] }
15+
op-revm = { workspace = true, features = ["serde"] }
16+
17+
[dev-dependencies]
18+
rstest = { workspace = true }
19+
alloy-sol-types = { workspace = true }
20+
sha2 = { workspace = true }
21+
alloy-primitives = { workspace = true }
22+
23+
[features]
24+
default = []
25+
optional_balance_check = ["revm/optional_balance_check", "op-revm/optional_balance_check"]

crates/ee-tests/src/lib.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//! Common test utilities for REVM crates.
2+
//!
3+
//! This crate provides shared test utilities that are used across different REVM crates.
4+
5+
use std::path::PathBuf;
6+
7+
use serde_json::Value;
8+
9+
/// Configuration for the test data comparison utility.
10+
pub struct TestdataConfig {
11+
/// The directory where test data files are stored.
12+
pub testdata_dir: PathBuf,
13+
}
14+
15+
impl Default for TestdataConfig {
16+
fn default() -> Self {
17+
Self {
18+
testdata_dir: PathBuf::from("tests/testdata"),
19+
}
20+
}
21+
}
22+
23+
/// Compares or saves the execution output to a testdata file.
24+
///
25+
/// This utility helps maintain consistent test behavior by comparing
26+
/// execution results against known-good outputs stored in JSON files.
27+
///
28+
/// # Arguments
29+
///
30+
/// * `filename` - The name of the testdata file, relative to tests/testdata/
31+
/// * `output` - The execution output to compare or save
32+
///
33+
/// # Panics
34+
///
35+
/// This function panics if:
36+
/// - The output doesn't match the expected testdata (when testdata file exists)
37+
/// - There's an error reading/writing files
38+
/// - JSON serialization/deserialization fails
39+
///
40+
/// # Note
41+
///
42+
/// Tests using this function require the `serde` feature to be enabled:
43+
/// ```bash
44+
/// cargo test --features serde
45+
/// ```
46+
pub fn compare_or_save_testdata<T>(filename: &str, output: &T)
47+
where
48+
T: serde::Serialize + for<'a> serde::Deserialize<'a> + PartialEq + std::fmt::Debug,
49+
{
50+
compare_or_save_testdata_with_config(filename, output, TestdataConfig::default());
51+
}
52+
53+
/// Compares or saves the execution output to a testdata file with custom configuration.
54+
///
55+
/// This is a more flexible version of [`compare_or_save_testdata`] that allows
56+
/// specifying a custom testdata directory.
57+
///
58+
/// # Arguments
59+
///
60+
/// * `filename` - The name of the testdata file, relative to the testdata directory
61+
/// * `output` - The execution output to compare or save
62+
/// * `config` - Configuration for the test data comparison
63+
pub fn compare_or_save_testdata_with_config<T>(filename: &str, output: &T, config: TestdataConfig)
64+
where
65+
T: serde::Serialize + for<'a> serde::Deserialize<'a> + PartialEq + std::fmt::Debug,
66+
{
67+
use std::fs;
68+
69+
let testdata_file = config.testdata_dir.join(filename);
70+
71+
// Create directory if it doesn't exist
72+
if !config.testdata_dir.exists() {
73+
fs::create_dir_all(&config.testdata_dir).unwrap();
74+
}
75+
76+
// Serialize the output to serde Value.
77+
let output_json = serde_json::to_string(&output).unwrap();
78+
79+
// convert to Value and sort all objects.
80+
let mut temp: Value = serde_json::from_str(&output_json).unwrap();
81+
temp.sort_all_objects();
82+
83+
// serialize to pretty string
84+
let output_json = serde_json::to_string_pretty(&temp).unwrap();
85+
86+
// If the testdata file doesn't exist, save the output
87+
if !testdata_file.exists() {
88+
fs::write(&testdata_file, &output_json).unwrap();
89+
println!("Saved testdata to {}", testdata_file.display());
90+
return;
91+
}
92+
93+
// Read the expected output from the testdata file
94+
let expected_json = fs::read_to_string(&testdata_file).unwrap();
95+
96+
// Deserialize to actual object for proper comparison
97+
let expected: T = serde_json::from_str(&expected_json).unwrap();
98+
99+
// Compare the output objects directly
100+
if *output != expected {
101+
// If they don't match, generate a nicer error by pretty-printing both as JSON
102+
// This helps with debugging by showing the exact differences
103+
let expected_pretty = serde_json::to_string_pretty(&expected).unwrap();
104+
105+
panic!(
106+
"Value does not match testdata.\nExpected:\n{expected_pretty}\n\nActual:\n{output_json}"
107+
);
108+
}
109+
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
115+
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
116+
struct TestData {
117+
value: u32,
118+
message: String,
119+
}
120+
121+
#[test]
122+
fn test_compare_or_save_testdata() {
123+
let test_data = TestData {
124+
value: 42,
125+
message: "test message".to_string(),
126+
};
127+
128+
// This will save the test data on first run, then compare on subsequent runs
129+
compare_or_save_testdata("test_data.json", &test_data);
130+
}
131+
}
132+
133+
#[cfg(test)]
134+
mod op_revm_tests;
135+
136+
#[cfg(test)]
137+
mod revm_tests;

0 commit comments

Comments
 (0)