parsing complex tests works modulo the contract addresses in calldata

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
Cyrill Leutwiler
2025-03-19 16:23:04 +01:00
parent d08d6fd66f
commit 67f068ca12
15 changed files with 429 additions and 27 deletions
+1 -2
View File
@@ -1,4 +1,4 @@
use serde::{Deserialize, de::Deserializer};
use serde::Deserialize;
use crate::{input::Input, mode::Mode};
@@ -8,5 +8,4 @@ pub struct Case {
pub comment: Option<String>,
pub modes: Option<Vec<Mode>>,
pub inputs: Vec<Input>,
pub expected: Vec<String>,
}
+67
View File
@@ -0,0 +1,67 @@
use std::{
fs::File,
path::{Path, PathBuf},
};
use serde::Deserialize;
use crate::metadata::Metadata;
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct Corpus {
pub name: String,
pub path: PathBuf,
}
impl Corpus {
/// Try to read and parse the corpus definition file at given `path`.
pub fn try_from_path(path: &Path) -> anyhow::Result<Self> {
let file = File::open(path)?;
Ok(serde_json::from_reader(file)?)
}
/// Scan the corpus base directory and return all tests found.
pub fn enumerate_tests(&self) -> Vec<Metadata> {
let mut tests = Vec::new();
collect_metadata(&self.path, &mut tests);
tests
}
}
/// Recursively walks `path` and parses any JSON or Solidity file into a test
/// definition [Metadata].
///
/// Found tests are inserted into `tests`.
///
/// `path` is expected to be a directory.
pub fn collect_metadata(path: &Path, tests: &mut Vec<Metadata>) {
let dir_entry = match std::fs::read_dir(path) {
Ok(dir_entry) => dir_entry,
Err(error) => {
log::error!("failed to read dir '{}': {error}", path.display());
return;
}
};
for entry in dir_entry {
let entry = match entry {
Ok(entry) => entry,
Err(error) => {
log::error!("error reading dir entry: {error}");
continue;
}
};
let path = entry.path();
if path.is_dir() {
collect_metadata(&path, tests);
continue;
}
if path.is_file() {
if let Some(metadata) = Metadata::try_from_file(&path) {
tests.push(metadata)
}
}
}
}
+2 -2
View File
@@ -41,7 +41,7 @@ where
match calldata_string.parse::<U256>() {
Ok(parsed) => result.extend_from_slice(&parsed.to_be_bytes::<32>()),
Err(error) => {
return Err(serde::de::Error::custom(&format!(
return Err(serde::de::Error::custom(format!(
"parsing U256 {calldata_string} error: {error}"
)));
}
@@ -67,7 +67,7 @@ where
match Function::parse(&signature) {
Ok(function) => Method::Function(function.selector().0),
Err(error) => {
return Err(serde::de::Error::custom(&format!(
return Err(serde::de::Error::custom(format!(
"parsing function signature '{signature}' error: {error}"
)));
}
+1
View File
@@ -1,6 +1,7 @@
//! The revive differential tests case format.
pub mod case;
pub mod corpus;
pub mod input;
pub mod metadata;
pub mod mode;
+68 -1
View File
@@ -1,9 +1,16 @@
use std::collections::BTreeMap;
use std::{
collections::BTreeMap,
fs::File,
path::{Path, PathBuf},
};
use serde::Deserialize;
use crate::{case::Case, mode::Mode};
pub const METADATA_FILE_EXTENSION: &str = "json";
pub const SOLIDITY_CASE_FILE_EXTENSION: &str = "sol";
#[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)]
pub struct Metadata {
pub cases: Vec<Case>,
@@ -11,4 +18,64 @@ pub struct Metadata {
pub libraries: Option<BTreeMap<String, BTreeMap<String, String>>>,
pub ignore: Option<bool>,
pub modes: Option<Vec<Mode>>,
pub path: Option<PathBuf>,
pub directory: Option<PathBuf>,
}
impl Metadata {
/// Try to parse the test metadata struct from the given file at `path`.
///
/// Returns `None` if `path` didn't contain a test metadata or case definition.
///
/// # Panics
/// Expects the supplied `path` to be a file.
pub fn try_from_file(path: &Path) -> Option<Self> {
assert!(path.is_file(), "not a file: {}", path.display());
let Some(file_extension) = path.extension() else {
log::debug!("skipping corpus file: {}", path.display());
return None;
};
if file_extension == METADATA_FILE_EXTENSION {
return Self::try_from_json(path);
}
if file_extension == SOLIDITY_CASE_FILE_EXTENSION {
return None;
//return Self::try_from_solidity(path);
}
log::debug!("ignoring invalid corpus file: {}", path.display());
None
}
fn try_from_json(path: &Path) -> Option<Self> {
let file = File::open(path)
.inspect_err(|error| {
log::error!(
"opening JSON test metadata file '{}' error: {error}",
path.display()
);
})
.ok()?;
match serde_json::from_reader::<_, Metadata>(file) {
Ok(mut metadata) => {
metadata.directory = Some(path.to_path_buf());
Some(metadata)
}
Err(error) => {
log::error!(
"parsing JSON test metadata file '{}' error: {error}",
path.display()
);
None
}
}
}
fn try_from_solidity(path: &Path) -> Option<Self> {
todo!()
}
}
+3 -3
View File
@@ -38,13 +38,13 @@ impl SolcMode {
_ => return None,
}
while let Some(part) = parts.next() {
for part in parts {
if let Ok(solc_version) = semver::VersionReq::parse(part) {
result.solc_version = Some(solc_version);
continue;
}
if part.starts_with("-O") {
result.llvm_optimizer_settings.push(part[2..].to_string());
if let Some(level) = part.strip_prefix("-O") {
result.llvm_optimizer_settings.push(level.to_string());
continue;
}
panic!("the YUL mode string {mode_string} failed to parse, invalid part: {part}")