Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions tooling/ef_tests/state_v2/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Legacy report files (now generated in reports/ directory)
./success_report.txt
./failure_report.txt

# Generated test reports directory
./reports/

# vectors is not yet part of this folder but will be soon.
./vectors
2 changes: 2 additions & 0 deletions tooling/ef_tests/state_v2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ clap_complete.workspace = true
tokio = { workspace = true, features = ["full"] }
colored = "2.1.0"
alloy-rlp = "0.3.12"
chrono = "0.4"
prettytable-rs = "0.10"

[dev-dependencies]
hex = "0.4.3"
Expand Down
5 changes: 4 additions & 1 deletion tooling/ef_tests/state_v2/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
.PHONY: help run-tests
.PHONY: help run-tests clean-reports

help: ## 📚 Show help for each of the Makefile recipes
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

run-tests: ## 🧪 Run all tests with optional flags. Usage: make run-tests flags="--flag1 --flag2"
cargo test --package ef_tests-statev2 --test all --release -- $(flags)

clean-reports: ## 🗑️ Delete all generated test reports
rm -rf ./reports

244 changes: 180 additions & 64 deletions tooling/ef_tests/state_v2/src/modules/report.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
use std::{fmt, fs::OpenOptions, io::Write, path::PathBuf};
use std::{
fmt,
fs::{self, OpenOptions},
io::Write,
path::PathBuf,
};

use chrono::Local;
use prettytable::{Cell, Row, Table};

use crate::modules::{error::RunnerError, result_check::PostCheckResult, types::Test};

/// Ensures the reports directory exists, creating it if necessary
fn ensure_reports_dir() -> Result<(), RunnerError> {
let reports_dir = PathBuf::from("./reports");
if !reports_dir.exists() {
fs::create_dir_all(&reports_dir).map_err(|e| {
RunnerError::Custom(format!("Failed to create reports directory: {}", e))
})?;
}
Ok(())
}

/// Generates timestamped report paths
fn get_report_paths() -> (PathBuf, PathBuf) {
let timestamp = Local::now().format("%Y-%m-%d_%H-%M-%S");
let success_path = PathBuf::from(format!("./reports/success_report_{}.txt", timestamp));
let failure_path = PathBuf::from(format!("./reports/failure_report_{}.txt", timestamp));
(success_path, failure_path)
}

pub fn add_test_to_report(test_result: (&Test, Vec<PostCheckResult>)) -> Result<(), RunnerError> {
ensure_reports_dir()?;
let (test, failed_test_cases) = test_result;
if failed_test_cases.is_empty() {
write_passing_test_to_report(test);
Expand All @@ -11,8 +39,9 @@ pub fn add_test_to_report(test_result: (&Test, Vec<PostCheckResult>)) -> Result<
}
Ok(())
}

pub fn write_passing_test_to_report(test: &Test) {
let successful_report_path = PathBuf::from("./success_report.txt");
let (successful_report_path, _) = get_report_paths();
let mut report = OpenOptions::new()
.append(true)
.create(true)
Expand All @@ -21,79 +50,136 @@ pub fn write_passing_test_to_report(test: &Test) {
let content = format!("Test {:?} - Path {:?}\n", test.name, test.path);
report.write_all(content.as_bytes()).unwrap()
}

pub fn write_failing_test_to_report(test: &Test, failing_test_cases: Vec<PostCheckResult>) {
let failing_report_path = PathBuf::from("./failure_report.txt");
let (_, failing_report_path) = get_report_paths();
let mut report = OpenOptions::new()
.append(true)
.create(true)
.open(failing_report_path)
.unwrap();
let content = format!(
"Test checks failed for test: {:?}. \nTest path: {:?}\nTest description/comment: {}\nTest doc reference: {}\n ",
test.name,
test.path,
test._info.description.clone().unwrap_or(
test._info
.comment

// Create header table
let mut header_table = Table::new();
header_table.add_row(Row::new(vec![
Cell::new("Test Information").style_spec("Fb"),
]));
header_table.add_row(Row::new(vec![Cell::new("Name"), Cell::new(&test.name)]));
header_table.add_row(Row::new(vec![
Cell::new("Path"),
Cell::new(&test.path.display().to_string()),
]));
header_table.add_row(Row::new(vec![
Cell::new("Description"),
Cell::new(
&test._info.description.clone().unwrap_or(
test._info
.comment
.clone()
.unwrap_or("No description or comment".to_string()),
),
),
]));
header_table.add_row(Row::new(vec![
Cell::new("Reference"),
Cell::new(
&test
._info
.reference_spec
.clone()
.unwrap_or("This test has no description or comment".to_string())
.unwrap_or("No reference spec".to_string()),
),
test._info
.reference_spec
.clone()
.unwrap_or("This test has no reference spec".to_string())
);
report.write_all(content.as_bytes()).unwrap();
]));

let header_content = format!("{}\n", header_table);
report.write_all(header_content.as_bytes()).unwrap();

for check_result in failing_test_cases {
let content = format!("\n{}", check_result);
report.write_all(content.as_bytes()).unwrap();
}
let dividing_line = "-----------------------------------------------------\n\n".to_string();
let dividing_line =
"\n═══════════════════════════════════════════════════════════════════════\n\n".to_string();
let _ = report.write_all(dividing_line.as_bytes());
}

impl fmt::Display for PostCheckResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"Fork: {:?} - indexes (data: {}, gas: {}, value: {})\n",
self.fork, self.vector.0, self.vector.1, self.vector.2
)?;
// Fork and indexes table
let mut info_table = Table::new();
info_table.add_row(Row::new(vec![
Cell::new("Fork"),
Cell::new("Data Idx"),
Cell::new("Gas Idx"),
Cell::new("Val Idx"),
Cell::new("Status"),
]));
info_table.add_row(Row::new(vec![
Cell::new(&format!("{:?}", self.fork)),
Cell::new(&self.vector.0.to_string()),
Cell::new(&self.vector.1.to_string()),
Cell::new(&self.vector.2.to_string()),
Cell::new("FAILED").style_spec("Fr"),
]));
writeln!(f, "{}", info_table)?;

// Root mismatch
if let Some(root_mismatch) = self.root_diff {
let (expected_root, actual_root) = root_mismatch;
writeln!(
f,
" ERR - ROOT MISMATCH:\n Expected root: {:?}\n Actual root: {:?}",
expected_root, actual_root
)?;
writeln!(f, "\nERROR: Root Mismatch")?;
let mut root_table = Table::new();
root_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")]));
root_table.add_row(Row::new(vec![
Cell::new("Expected"),
Cell::new(&format!("{:?}", expected_root)),
]));
root_table.add_row(Row::new(vec![
Cell::new("Actual"),
Cell::new(&format!("{:?}", actual_root)),
]));
writeln!(f, "{}", root_table)?;
}

// Exception mismatch
if let Some(exception_diff) = self.exception_diff.clone() {
let (expected_exception, actual_exception) = exception_diff;
writeln!(
f,
" ERR - EXCEPTION MISMATCH:\n Expected exception: {:?}\n Actual exception: {:?}",
expected_exception, actual_exception
)?;
writeln!(f, "\nERROR: Exception Mismatch")?;
let mut exception_table = Table::new();
exception_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")]));
exception_table.add_row(Row::new(vec![
Cell::new("Expected"),
Cell::new(&format!("{:?}", expected_exception)),
]));
exception_table.add_row(Row::new(vec![
Cell::new("Actual"),
Cell::new(&format!("{:?}", actual_exception)),
]));
writeln!(f, "{}", exception_table)?;
}

// Logs mismatch
if let Some(logs_mismatch) = self.logs_diff {
let (expected_log_hash, actual_log_hash) = logs_mismatch;
writeln!(
f,
" ERR - LOGS MISMATCH:\n Expected logs hash: {:?}\n Actual logs hash: {:?}",
expected_log_hash, actual_log_hash
)?;
writeln!(f, "\nERROR: Logs Mismatch")?;
let mut logs_table = Table::new();
logs_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")]));
logs_table.add_row(Row::new(vec![
Cell::new("Expected"),
Cell::new(&format!("{:?}", expected_log_hash)),
]));
logs_table.add_row(Row::new(vec![
Cell::new("Actual"),
Cell::new(&format!("{:?}", actual_log_hash)),
]));
writeln!(f, "{}", logs_table)?;
}

// Account mismatches
if let Some(account_mismatches) = self.accounts_diff.clone() {
for acc_mismatch in account_mismatches {
writeln!(
f,
" ERR - ACCOUNT STATE MISMATCH:\n Address: {:?}\n",
acc_mismatch.address,
)?;
writeln!(f, "\nERROR: Account State Mismatch")?;
writeln!(f, "Address: {:?}", acc_mismatch.address)?;

if let Some(balance_diff) = acc_mismatch.balance_diff {
let (expected_balance, actual_balance) = balance_diff;
let net_difference = expected_balance.abs_diff(actual_balance);
Expand All @@ -102,37 +188,67 @@ impl fmt::Display for PostCheckResult {
} else {
"+"
};
writeln!(
f,
" Expected balance: {:?}\n Actual balance: {:?}\n Difference: {}{:?}\n",
expected_balance, actual_balance, difference_sign, net_difference
)?;

let mut balance_table = Table::new();
balance_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")]));
balance_table.add_row(Row::new(vec![
Cell::new("Expected Balance"),
Cell::new(&format!("{:?}", expected_balance)),
]));
balance_table.add_row(Row::new(vec![
Cell::new("Actual Balance"),
Cell::new(&format!("{:?}", actual_balance)),
]));
balance_table.add_row(Row::new(vec![
Cell::new("Difference"),
Cell::new(&format!("{}{:?}", difference_sign, net_difference)),
]));
writeln!(f, "{}", balance_table)?;
}

if let Some(nonce_diff) = acc_mismatch.nonce_diff {
let (expected_nonce, actual_nonce) = nonce_diff;
writeln!(
f,
" Expected nonce: {:?}\n Actual nonce: {:?}\n",
expected_nonce, actual_nonce
)?;
let mut nonce_table = Table::new();
nonce_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")]));
nonce_table.add_row(Row::new(vec![
Cell::new("Expected Nonce"),
Cell::new(&format!("{:?}", expected_nonce)),
]));
nonce_table.add_row(Row::new(vec![
Cell::new("Actual Nonce"),
Cell::new(&format!("{:?}", actual_nonce)),
]));
writeln!(f, "{}", nonce_table)?;
}

if let Some(code_diff) = acc_mismatch.code_diff {
let (expected_code_hash, actual_code_hash) = code_diff;
writeln!(
f,
" Expected code hash: 0x{}\n Actual code hash: 0x{}\n",
hex::encode(expected_code_hash),
hex::encode(actual_code_hash)
)?;
let mut code_table = Table::new();
code_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")]));
code_table.add_row(Row::new(vec![
Cell::new("Expected Code Hash"),
Cell::new(&format!("0x{}", hex::encode(expected_code_hash))),
]));
code_table.add_row(Row::new(vec![
Cell::new("Actual Code Hash"),
Cell::new(&format!("0x{}", hex::encode(actual_code_hash))),
]));
writeln!(f, "{}", code_table)?;
}

if let Some(storage_diff) = acc_mismatch.storage_diff {
let (expected_storage, actual_storage) = storage_diff;
writeln!(
f,
" Expected storage: {:?}\n Actual storage: {:?}",
expected_storage, actual_storage
)?;
let mut storage_table = Table::new();
storage_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")]));
storage_table.add_row(Row::new(vec![
Cell::new("Expected Storage"),
Cell::new(&format!("{:?}", expected_storage)),
]));
storage_table.add_row(Row::new(vec![
Cell::new("Actual Storage"),
Cell::new(&format!("{:?}", actual_storage)),
]));
writeln!(f, "{}", storage_table)?;
}
}
}
Expand Down