diff --git a/.gitignore b/.gitignore index 53344cf..10a2f71 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .vscode/ .DS_Store node_modules +/*.json diff --git a/Cargo.lock b/Cargo.lock index 06f70e1..858b06d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "alloy-eip2124" version = "0.1.0" @@ -231,6 +240,56 @@ dependencies = [ "tracing", ] +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.97" @@ -501,6 +560,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "const-hex" version = "1.14.0" @@ -723,6 +828,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -929,6 +1057,12 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -944,6 +1078,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "k256" version = "0.13.4" @@ -1000,6 +1158,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + [[package]] name = "macro-string" version = "0.1.4" @@ -1136,6 +1300,21 @@ dependencies = [ "spki", ] +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1283,12 +1462,48 @@ dependencies = [ "rand_core", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "revive-differential-testing-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "env_logger", + "log", + "revive-differential-testing-format", + "serde", + "serde_json", +] + [[package]] name = "revive-differential-testing-format" version = "0.1.0" @@ -1299,19 +1514,12 @@ dependencies = [ "alloy-serde", "alloy-sol-types", "anyhow", + "log", "semver 1.0.26", "serde", "serde_json", ] -[[package]] -name = "revive-differential-testing-retester" -version = "0.1.0" -dependencies = [ - "revive-differential-testing-format", - "serde_json", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -1570,6 +1778,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.6.1" @@ -1745,6 +1959,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index e158118..2c6252c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,12 @@ alloy-genesis = "0.12.6" alloy-primitives = { version = "0.8.23", features = ["serde"] } alloy-serde = "0.12.6" alloy-sol-types = "0.8.23" +env_logger = "0.11.7" +log = "0.4.26" semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["arbitrary_precision"] } +clap = { version = "4", features = ["derive"] } # revive compiler revive-llvm-context = { git = "https://github.com/paritytech/revive", rev = "2bbc5d713d1bf8b38c43f6ce9382812561ca3ce1" } diff --git a/crates/retester/Cargo.toml b/crates/core/Cargo.toml similarity index 55% rename from crates/retester/Cargo.toml rename to crates/core/Cargo.toml index 1b41551..0ccae91 100644 --- a/crates/retester/Cargo.toml +++ b/crates/core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "revive-differential-testing-retester" +name = "revive-differential-testing-core" version.workspace = true authors.workspace = true license.workspace = true @@ -9,8 +9,13 @@ rust-version.workspace = true [[bin]] name = "retester" -path = "src/main.rs" +path = "src/bin/main.rs" [dependencies] +anyhow = { workspace = true } +clap = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } revive-differential-testing-format = { workspace = true } +serde = { workspace = true, features = [ "derive" ] } serde_json = { workspace = true } diff --git a/crates/core/src/arguments.rs b/crates/core/src/arguments.rs new file mode 100644 index 0000000..db1893e --- /dev/null +++ b/crates/core/src/arguments.rs @@ -0,0 +1,17 @@ +use std::path::PathBuf; + +use clap::Parser; + +#[derive(Debug, Parser)] +#[command(name = "The PolkaVM Solidity compiler", arg_required_else_help = true)] +pub struct Arguments { + /// The path where the `resolc` executable to be tested is found at. + /// + /// By default it uses the `resolc` found in `$PATH` + #[arg(long = "resolc")] + pub resolc: Option, + + /// A list of test corpus JSON files to be tested. + #[arg(long = "corpus")] + pub corpus: Vec, +} diff --git a/crates/core/src/bin/main.rs b/crates/core/src/bin/main.rs new file mode 100644 index 0000000..6949931 --- /dev/null +++ b/crates/core/src/bin/main.rs @@ -0,0 +1,24 @@ +use std::collections::BTreeSet; + +use clap::Parser; + +use revive_differential_testing_core::arguments::Arguments; +use revive_differential_testing_format::corpus::Corpus; + +fn main() -> anyhow::Result<()> { + env_logger::init(); + + let args = Arguments::try_parse()?; + + for path in args.corpus.iter().collect::>() { + log::trace!("attempting corpus {path:?}"); + let corpus = Corpus::try_from_path(path)?; + log::info!("found corpus: {corpus:?}"); + + let tests = corpus.enumerate_tests(); + + log::debug!("{tests:?}"); + } + + Ok(()) +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs new file mode 100644 index 0000000..6d66410 --- /dev/null +++ b/crates/core/src/lib.rs @@ -0,0 +1,6 @@ +//! The revive differential testing core library. +//! +//! This crate defines the testing configuration and +//! provides a helper utilty to execute tests. + +pub mod arguments; diff --git a/crates/format/Cargo.toml b/crates/format/Cargo.toml index 8c2d0a6..b1116ac 100644 --- a/crates/format/Cargo.toml +++ b/crates/format/Cargo.toml @@ -14,6 +14,7 @@ alloy-serde = { workspace = true } alloy-primitives = { workspace = true } alloy-sol-types = { workspace = true } anyhow = { workspace = true } +log = { workspace = true } semver = { workspace = true } serde = { workspace = true, features = [ "derive" ] } serde_json = { workspace = true } \ No newline at end of file diff --git a/crates/format/src/case.rs b/crates/format/src/case.rs index 10c0972..4c67581 100644 --- a/crates/format/src/case.rs +++ b/crates/format/src/case.rs @@ -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, pub modes: Option>, pub inputs: Vec, - pub expected: Vec, } diff --git a/crates/format/src/corpus.rs b/crates/format/src/corpus.rs new file mode 100644 index 0000000..2717207 --- /dev/null +++ b/crates/format/src/corpus.rs @@ -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 { + 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 { + 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) { + 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) + } + } + } +} diff --git a/crates/format/src/input.rs b/crates/format/src/input.rs index 87d6a45..8f1de01 100644 --- a/crates/format/src/input.rs +++ b/crates/format/src/input.rs @@ -41,7 +41,7 @@ where match calldata_string.parse::() { 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}" ))); } diff --git a/crates/format/src/lib.rs b/crates/format/src/lib.rs index 74de75b..21ae375 100644 --- a/crates/format/src/lib.rs +++ b/crates/format/src/lib.rs @@ -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; diff --git a/crates/format/src/metadata.rs b/crates/format/src/metadata.rs index 0e5e0eb..73f84fa 100644 --- a/crates/format/src/metadata.rs +++ b/crates/format/src/metadata.rs @@ -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, @@ -11,4 +18,64 @@ pub struct Metadata { pub libraries: Option>>, pub ignore: Option, pub modes: Option>, + pub path: Option, + pub directory: Option, +} + +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 { + 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 { + 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 { + todo!() + } } diff --git a/crates/format/src/mode.rs b/crates/format/src/mode.rs index 27616a7..75649f6 100644 --- a/crates/format/src/mode.rs +++ b/crates/format/src/mode.rs @@ -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}") diff --git a/crates/retester/src/main.rs b/crates/retester/src/main.rs deleted file mode 100644 index 7dc72d7..0000000 --- a/crates/retester/src/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -use revive_differential_testing_format::metadata::Metadata; - -fn main() { - let example_def = include_str!("../../../test.json"); - - let metadata: Metadata = serde_json::from_str(example_def).unwrap(); - - println!("{metadata:?}"); -}