Skip to content

Commit 10bfaed

Browse files
authored
Implement basic reporting facility (#18)
* wip Signed-off-by: Cyrill Leutwiler <[email protected]> * save to file after all tasks done Signed-off-by: Cyrill Leutwiler <[email protected]> * error out early if the workdir does not exist Signed-off-by: Cyrill Leutwiler <[email protected]> * the compiler statistics Signed-off-by: Cyrill Leutwiler <[email protected]> * allow compiler statistics per implementation Signed-off-by: Cyrill Leutwiler <[email protected]> * save compiler problems Signed-off-by: Cyrill Leutwiler <[email protected]> * add flag whether to extract compiler errors Signed-off-by: Cyrill Leutwiler <[email protected]> * whitespace Signed-off-by: Cyrill Leutwiler <[email protected]> --------- Signed-off-by: Cyrill Leutwiler <[email protected]>
1 parent 399f782 commit 10bfaed

File tree

18 files changed

+528
-60
lines changed

18 files changed

+528
-60
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ revive-dt-format = { version = "0.1.0", path = "crates/format" }
2020
revive-dt-node = { version = "0.1.0", path = "crates/node" }
2121
revive-dt-node-interaction = { version = "0.1.0", path = "crates/node-interaction" }
2222
revive-dt-node-pool = { version = "0.1.0", path = "crates/node-pool" }
23+
revive-dt-report = { version = "0.1.0", path = "crates/report" }
2324
revive-dt-solc-binaries = { version = "0.1.0", path = "crates/solc-binaries" }
2425

2526
anyhow = "1.0"
@@ -38,9 +39,9 @@ temp-dir = { version = "0.1.14" }
3839
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
3940

4041
# revive compiler
41-
revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev = "497dae2494dabe12d1af32d6d687122903cb2ada" }
42-
revive-common = { git = "https://github.com/paritytech/revive", rev = "497dae2494dabe12d1af32d6d687122903cb2ada" }
43-
revive-differential = { git = "https://github.com/paritytech/revive", rev = "497dae2494dabe12d1af32d6d687122903cb2ada" }
42+
revive-solc-json-interface = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" }
43+
revive-common = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" }
44+
revive-differential = { git = "https://github.com/paritytech/revive", rev = "3389865af7c3ff6f29a586d82157e8bc573c1a8e" }
4445

4546
[workspace.dependencies.alloy]
4647
version = "0.13.0"

crates/compiler/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,12 @@ pub struct CompilerInput<T: PartialEq + Eq + Hash> {
4848

4949
/// The generic compilation output configuration.
5050
pub struct CompilerOutput<T: PartialEq + Eq + Hash> {
51+
/// The solc standard JSON input.
5152
pub input: CompilerInput<T>,
53+
/// The produced solc standard JSON output.
5254
pub output: SolcStandardJsonOutput,
55+
/// The error message in case the compiler returns abnormally.
56+
pub error: Option<String>,
5357
}
5458

5559
impl<T> PartialEq for CompilerInput<T>
@@ -110,6 +114,7 @@ where
110114
false,
111115
),
112116
None,
117+
None,
113118
),
114119
},
115120
extra_options: Default::default(),
@@ -156,4 +161,9 @@ where
156161
input: self.input,
157162
})
158163
}
164+
165+
/// Returns the compiler JSON input.
166+
pub fn input(&self) -> SolcStandardJsonInput {
167+
self.input.clone()
168+
}
159169
}

crates/compiler/src/revive_resolc.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,18 @@ impl SolidityCompiler for Resolc {
4141
let stderr = output.stderr;
4242

4343
if !output.status.success() {
44+
let message = String::from_utf8_lossy(&stderr);
4445
log::error!(
4546
"resolc failed exit={} stderr={} JSON-in={} ",
4647
output.status,
47-
String::from_utf8_lossy(&stderr),
48+
&message,
4849
json_in,
4950
);
51+
return Ok(CompilerOutput {
52+
input,
53+
output: Default::default(),
54+
error: Some(message.into()),
55+
});
5056
}
5157

5258
let parsed: SolcStandardJsonOutput = serde_json::from_slice(&stdout).map_err(|e| {
@@ -59,6 +65,7 @@ impl SolidityCompiler for Resolc {
5965
Ok(CompilerOutput {
6066
input,
6167
output: parsed,
68+
error: None,
6269
})
6370
}
6471

crates/compiler/src/solc.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,22 @@ impl SolidityCompiler for Solc {
3030

3131
let stdin = child.stdin.as_mut().expect("should be piped");
3232
serde_json::to_writer(stdin, &input.input)?;
33+
let output = child.wait_with_output()?;
34+
35+
if !output.status.success() {
36+
let message = String::from_utf8_lossy(&output.stderr);
37+
log::error!("solc failed exit={} stderr={}", output.status, &message);
38+
return Ok(CompilerOutput {
39+
input,
40+
output: Default::default(),
41+
error: Some(message.into()),
42+
});
43+
}
3344

34-
let output = child.wait_with_output()?.stdout;
3545
Ok(CompilerOutput {
3646
input,
37-
output: serde_json::from_slice(&output)?,
47+
output: serde_json::from_slice(&output.stdout)?,
48+
error: None,
3849
})
3950
}
4051

crates/config/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ alloy = { workspace = true }
1313
clap = { workspace = true }
1414
semver = { workspace = true }
1515
temp-dir = { workspace = true }
16+
serde = { workspace = true }
17+

crates/config/src/lib.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//! The global configuration used accross all revive differential testing crates.
22
3-
use std::path::{Path, PathBuf};
3+
use std::{
4+
fmt::Display,
5+
path::{Path, PathBuf},
6+
};
47

58
use alloy::{network::EthereumWallet, signers::local::PrivateKeySigner};
69
use clap::{Parser, ValueEnum};
710
use semver::Version;
11+
use serde::{Deserialize, Serialize};
812
use temp_dir::TempDir;
913

10-
#[derive(Debug, Parser, Clone)]
14+
#[derive(Debug, Parser, Clone, Serialize, Deserialize)]
1115
#[command(name = "retester")]
1216
pub struct Arguments {
1317
/// The `solc` version to use if the test didn't specify it explicitly.
@@ -40,6 +44,7 @@ pub struct Arguments {
4044
///
4145
/// We attach it here because [TempDir] prunes itself on drop.
4246
#[clap(skip)]
47+
#[serde(skip)]
4348
pub temp_dir: Option<&'static TempDir>,
4449

4550
/// The path to the `geth` executable.
@@ -83,6 +88,10 @@ pub struct Arguments {
8388
/// Determines the amount of tests that are executed in parallel.
8489
#[arg(long = "workers", default_value = "12")]
8590
pub workers: usize,
91+
92+
/// Extract problems back to the test corpus.
93+
#[arg(short, long = "extract-problems")]
94+
pub extract_problems: bool,
8695
}
8796

8897
impl Arguments {
@@ -124,11 +133,20 @@ impl Default for Arguments {
124133
/// The Solidity compatible node implementation.
125134
///
126135
/// This describes the solutions to be tested against on a high level.
127-
#[derive(Clone, Debug, Eq, Hash, PartialEq, ValueEnum)]
136+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum, Serialize, Deserialize)]
128137
#[clap(rename_all = "lower")]
129138
pub enum TestingPlatform {
130139
/// The go-ethereum reference full node EVM implementation.
131140
Geth,
132141
/// The kitchensink runtime provides the PolkaVM (PVM) based node implentation.
133142
Kitchensink,
134143
}
144+
145+
impl Display for TestingPlatform {
146+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147+
match self {
148+
Self::Geth => f.write_str("geth"),
149+
Self::Kitchensink => f.write_str("revive"),
150+
}
151+
}
152+
}

crates/core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ revive-dt-config = { workspace = true }
1818
revive-dt-format = { workspace = true }
1919
revive-dt-node = { workspace = true }
2020
revive-dt-node-interaction = { workspace = true }
21+
revive-dt-report = { workspace = true }
2122

2223
alloy = { workspace = true }
2324
anyhow = { workspace = true }

crates/core/src/driver/mod.rs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use revive_dt_compiler::{Compiler, CompilerInput, SolidityCompiler};
88
use revive_dt_config::Arguments;
99
use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode};
1010
use revive_dt_node_interaction::EthereumNode;
11+
use revive_dt_report::reporter::{CompilationTask, Report, Span};
1112
use revive_solc_json_interface::SolcStandardJsonOutput;
1213

1314
use crate::Platform;
@@ -19,6 +20,7 @@ type Contracts<T> = HashMap<
1920

2021
pub struct State<'a, T: Platform> {
2122
config: &'a Arguments,
23+
span: Span,
2224
contracts: Contracts<T>,
2325
deployed_contracts: HashMap<String, Address>,
2426
}
@@ -27,37 +29,65 @@ impl<'a, T> State<'a, T>
2729
where
2830
T: Platform,
2931
{
30-
pub fn new(config: &'a Arguments) -> Self {
32+
pub fn new(config: &'a Arguments, span: Span) -> Self {
3133
Self {
3234
config,
35+
span,
3336
contracts: Default::default(),
3437
deployed_contracts: Default::default(),
3538
}
3639
}
3740

41+
/// Returns a copy of the current span.
42+
fn span(&self) -> Span {
43+
self.span
44+
}
45+
3846
pub fn build_contracts(&mut self, mode: &SolcMode, metadata: &Metadata) -> anyhow::Result<()> {
47+
let mut span = self.span();
48+
span.next_metadata(
49+
metadata
50+
.file_path
51+
.as_ref()
52+
.expect("metadata should have been read from a file")
53+
.clone(),
54+
);
55+
3956
let Some(version) = mode.last_patch_version(&self.config.solc) else {
40-
anyhow::bail!("unsupported solc version: {:?}", mode.solc_version);
57+
anyhow::bail!("unsupported solc version: {:?}", &mode.solc_version);
4158
};
4259

43-
let sources = metadata.contract_sources()?;
44-
let base_path = metadata.directory()?.display().to_string();
60+
let mut compiler = Compiler::<T::Compiler>::new()
61+
.base_path(metadata.directory()?.display().to_string())
62+
.solc_optimizer(mode.solc_optimize());
4563

46-
let mut compiler = Compiler::<T::Compiler>::new().base_path(base_path.clone());
47-
for (file, _contract) in sources.values() {
64+
for (file, _contract) in metadata.contract_sources()?.values() {
4865
log::debug!("contract source {}", file.display());
4966
compiler = compiler.with_source(file)?;
5067
}
5168

52-
let compiler_path = T::Compiler::get_compiler_executable(self.config, version)?;
53-
54-
let output = compiler
55-
.solc_optimizer(mode.solc_optimize())
56-
.try_build(compiler_path)?;
57-
58-
self.contracts.insert(output.input, output.output);
69+
let mut task = CompilationTask {
70+
json_input: compiler.input(),
71+
json_output: None,
72+
mode: mode.clone(),
73+
compiler_version: format!("{}", &version),
74+
error: None,
75+
};
5976

60-
Ok(())
77+
let compiler_path = T::Compiler::get_compiler_executable(self.config, version)?;
78+
match compiler.try_build(compiler_path) {
79+
Ok(output) => {
80+
task.json_output = Some(output.output.clone());
81+
task.error = output.error;
82+
self.contracts.insert(output.input, output.output);
83+
Report::compilation(span, T::config_id(), task);
84+
Ok(())
85+
}
86+
Err(error) => {
87+
task.error = Some(error.to_string());
88+
Err(error)
89+
}
90+
}
6191
}
6292

6393
pub fn execute_input(
@@ -102,12 +132,12 @@ where
102132
}
103133
}
104134

105-
pub fn execute(&mut self) -> anyhow::Result<()> {
135+
pub fn execute(&mut self, span: Span) -> anyhow::Result<()> {
106136
for mode in self.metadata.solc_modes() {
107-
let mut leader_state = State::<L>::new(self.config);
137+
let mut leader_state = State::<L>::new(self.config, span);
108138
leader_state.build_contracts(&mode, self.metadata)?;
109139

110-
let mut follower_state = State::<F>::new(self.config);
140+
let mut follower_state = State::<F>::new(self.config, span);
111141
follower_state.build_contracts(&mode, self.metadata)?;
112142

113143
for case in &self.metadata.cases {

crates/core/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! provides a helper utilty to execute tests.
55
66
use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc};
7+
use revive_dt_config::TestingPlatform;
78
use revive_dt_node::geth;
89
use revive_dt_node_interaction::EthereumNode;
910

@@ -15,6 +16,9 @@ pub mod driver;
1516
pub trait Platform {
1617
type Blockchain: EthereumNode;
1718
type Compiler: SolidityCompiler;
19+
20+
/// Returns the matching [TestingPlatform] of the [revive_dt_config::Arguments].
21+
fn config_id() -> TestingPlatform;
1822
}
1923

2024
#[derive(Default)]
@@ -23,6 +27,10 @@ pub struct Geth;
2327
impl Platform for Geth {
2428
type Blockchain = geth::Instance;
2529
type Compiler = solc::Solc;
30+
31+
fn config_id() -> TestingPlatform {
32+
TestingPlatform::Geth
33+
}
2634
}
2735

2836
#[derive(Default)]
@@ -31,4 +39,8 @@ pub struct Kitchensink;
3139
impl Platform for Kitchensink {
3240
type Blockchain = geth::Instance;
3341
type Compiler = revive_resolc::Resolc;
42+
43+
fn config_id() -> TestingPlatform {
44+
TestingPlatform::Kitchensink
45+
}
3446
}

0 commit comments

Comments
 (0)