diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index f83d336ed69b..7038fc0b5a8b 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -5,7 +5,7 @@ use clap::{Parser, ValueHint}; use eyre::{Context, OptionExt, Result}; use forge::{ decode::decode_console_logs, - gas_report::GasReport, + gas_report::{GasReport, GasReportKind}, multi_runner::matches_contract, result::{SuiteResult, TestOutcome, TestStatus}, traces::{ @@ -112,7 +112,7 @@ pub struct TestArgs { json: bool, /// Output test results as JUnit XML report. - #[arg(long, conflicts_with = "json", help_heading = "Display options")] + #[arg(long, conflicts_with_all(["json", "gas_report"]), help_heading = "Display options")] junit: bool, /// Stop running tests after the first failure. @@ -474,6 +474,9 @@ impl TestArgs { trace!(target: "forge::test", "running all tests"); + // If we need to render to a serialized format, we should not print anything else to stdout. + let silent = self.gas_report && self.json; + let num_filtered = runner.matching_test_functions(filter).count(); if num_filtered != 1 && (self.debug.is_some() || self.flamegraph || self.flamechart) { let action = if self.flamegraph { @@ -500,7 +503,7 @@ impl TestArgs { } // Run tests in a non-streaming fashion and collect results for serialization. - if self.json { + if !self.gas_report && self.json { let mut results = runner.test_collect(filter); results.values_mut().for_each(|suite_result| { for test_result in suite_result.test_results.values_mut() { @@ -565,9 +568,13 @@ impl TestArgs { } let mut decoder = builder.build(); - let mut gas_report = self - .gas_report - .then(|| GasReport::new(config.gas_reports.clone(), config.gas_reports_ignore.clone())); + let mut gas_report = self.gas_report.then(|| { + GasReport::new( + config.gas_reports.clone(), + config.gas_reports_ignore.clone(), + if self.json { GasReportKind::JSON } else { GasReportKind::Markdown }, + ) + }); let mut gas_snapshots = BTreeMap::>::new(); @@ -588,30 +595,34 @@ impl TestArgs { self.flamechart; // Print suite header. - println!(); - for warning in suite_result.warnings.iter() { - eprintln!("{} {warning}", "Warning:".yellow().bold()); - } - if !tests.is_empty() { - let len = tests.len(); - let tests = if len > 1 { "tests" } else { "test" }; - println!("Ran {len} {tests} for {contract_name}"); + if !silent { + println!(); + for warning in suite_result.warnings.iter() { + eprintln!("{} {warning}", "Warning:".yellow().bold()); + } + if !tests.is_empty() { + let len = tests.len(); + let tests = if len > 1 { "tests" } else { "test" }; + println!("Ran {len} {tests} for {contract_name}"); + } } // Process individual test results, printing logs and traces when necessary. for (name, result) in tests { - shell::println(result.short_result(name))?; - - // We only display logs at level 2 and above - if verbosity >= 2 { - // We only decode logs from Hardhat and DS-style console events - let console_logs = decode_console_logs(&result.logs); - if !console_logs.is_empty() { - println!("Logs:"); - for log in console_logs { - println!(" {log}"); + if !silent { + shell::println(result.short_result(name))?; + + // We only display logs at level 2 and above + if verbosity >= 2 { + // We only decode logs from Hardhat and DS-style console events + let console_logs = decode_console_logs(&result.logs); + if !console_logs.is_empty() { + println!("Logs:"); + for log in console_logs { + println!(" {log}"); + } + println!(); } - println!(); } } @@ -653,7 +664,7 @@ impl TestArgs { } } - if !decoded_traces.is_empty() { + if !silent && !decoded_traces.is_empty() { shell::println("Traces:")?; for trace in &decoded_traces { shell::println(trace)?; @@ -760,7 +771,9 @@ impl TestArgs { } // Print suite summary. - shell::println(suite_result.summary())?; + if !silent { + shell::println(suite_result.summary())?; + } // Add the suite result to the outcome. outcome.results.insert(contract_name, suite_result); @@ -781,7 +794,7 @@ impl TestArgs { outcome.gas_report = Some(finalized); } - if !outcome.results.is_empty() { + if !silent && !outcome.results.is_empty() { shell::println(outcome.summary(duration))?; if self.summary { @@ -1063,7 +1076,7 @@ contract FooBarTest is DSTest { let call_cnts = gas_report .contracts .values() - .flat_map(|c| c.functions.values().flat_map(|f| f.values().map(|v| v.calls.len()))) + .flat_map(|c| c.functions.values().flat_map(|f| f.values().map(|v| v.frames.len()))) .collect::>(); // assert that all functions were called at least 100 times assert!(call_cnts.iter().all(|c| *c > 100)); diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index ff7628bd5c4f..59c417b97097 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -12,11 +12,25 @@ use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, fmt::Display}; use yansi::Paint; +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum GasReportKind { + Markdown, + JSON, +} + +impl Default for GasReportKind { + fn default() -> Self { + Self::Markdown + } +} + /// Represents the gas report for a set of contracts. #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct GasReport { /// Whether to report any contracts. report_any: bool, + /// What kind of report to generate. + report_type: GasReportKind, /// Contracts to generate the report for. report_for: HashSet, /// Contracts to ignore when generating the report. @@ -30,11 +44,13 @@ impl GasReport { pub fn new( report_for: impl IntoIterator, ignore: impl IntoIterator, + report_kind: GasReportKind, ) -> Self { let report_for = report_for.into_iter().collect::>(); let ignore = ignore.into_iter().collect::>(); let report_any = report_for.is_empty() || report_for.contains("*"); - Self { report_any, report_for, ignore, ..Default::default() } + let report_type = report_kind; + Self { report_any, report_type, report_for, ignore, ..Default::default() } } /// Whether the given contract should be reported. @@ -113,7 +129,7 @@ impl GasReport { .or_default() .entry(signature.clone()) .or_default(); - gas_info.calls.push(trace.gas_used); + gas_info.frames.push(trace.gas_used); } } } @@ -125,11 +141,12 @@ impl GasReport { for contract in self.contracts.values_mut() { for sigs in contract.functions.values_mut() { for func in sigs.values_mut() { - func.calls.sort_unstable(); - func.min = func.calls.first().copied().unwrap_or_default(); - func.max = func.calls.last().copied().unwrap_or_default(); - func.mean = calc::mean(&func.calls); - func.median = calc::median_sorted(&func.calls); + func.frames.sort_unstable(); + func.min = func.frames.first().copied().unwrap_or_default(); + func.max = func.frames.last().copied().unwrap_or_default(); + func.mean = calc::mean(&func.frames); + func.median = calc::median_sorted(&func.frames); + func.calls = func.frames.len() as u64; } } } @@ -145,6 +162,11 @@ impl Display for GasReport { continue; } + if self.report_type == GasReportKind::JSON { + writeln!(f, "{}", serde_json::to_string(&contract).unwrap())?; + continue; + } + let mut table = Table::new(); table.load_preset(ASCII_MARKDOWN); table.set_header([Cell::new(format!("{name} contract")) @@ -176,7 +198,7 @@ impl Display for GasReport { Cell::new(gas_info.mean.to_string()).fg(Color::Yellow), Cell::new(gas_info.median.to_string()).fg(Color::Yellow), Cell::new(gas_info.max.to_string()).fg(Color::Red), - Cell::new(gas_info.calls.len().to_string()), + Cell::new(gas_info.calls.to_string()), ]); }) }); @@ -197,9 +219,12 @@ pub struct ContractInfo { #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct GasInfo { - pub calls: Vec, + pub calls: u64, pub min: u64, pub mean: u64, pub median: u64, pub max: u64, + + #[serde(skip)] + pub frames: Vec, } diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 6c8bab9289f6..61a5aeab8942 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -8,6 +8,7 @@ use foundry_config::{ use foundry_test_utils::{ foundry_compilers::PathStyle, rpc::next_mainnet_etherscan_api_key, + snapbox::IntoData, util::{pretty_err, read_string, OutputExt, TestCommand}, }; use semver::Version; @@ -1434,11 +1435,7 @@ Compiler run successful! } ); -forgetest!(gas_report_all_contracts, |prj, cmd| { - prj.insert_ds_test(); - prj.add_source( - "Contracts.sol", - r#" +const GAS_REPORT_CONTRACTS: &str = r#" //SPDX-license-identifier: MIT import "./test.sol"; @@ -1521,9 +1518,11 @@ contract ContractThreeTest is DSTest { c3.baz(); } } - "#, - ) - .unwrap(); +"#; + +forgetest!(gas_report_all_contracts, |prj, cmd| { + prj.insert_ds_test(); + prj.add_source("Contracts.sol", GAS_REPORT_CONTRACTS).unwrap(); // report for all prj.write_config(Config { @@ -1532,34 +1531,121 @@ contract ContractThreeTest is DSTest { ..Default::default() }); - let first_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(first_out.contains("foo") && first_out.contains("bar") && first_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); prj.write_config(Config { gas_reports: (vec![]), ..Default::default() }); - let second_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(second_out.contains("foo") && second_out.contains("bar") && second_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); prj.write_config(Config { gas_reports: (vec!["*".to_string()]), ..Default::default() }); - let third_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(third_out.contains("foo") && third_out.contains("bar") && third_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); prj.write_config(Config { gas_reports: (vec![ @@ -1569,125 +1655,90 @@ contract ContractThreeTest is DSTest { ]), ..Default::default() }); - let fourth_out = cmd - .forge_fuse() + cmd.forge_fuse().arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() .arg("test") .arg("--gas-report") + .arg("--json") .assert_success() - .get_output() - .stdout_lossy(); - assert!(fourth_out.contains("foo") && fourth_out.contains("bar") && fourth_out.contains("baz")); + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); }); forgetest!(gas_report_some_contracts, |prj, cmd| { prj.insert_ds_test(); - prj.add_source( - "Contracts.sol", - r#" -//SPDX-license-identifier: MIT - -import "./test.sol"; - -contract ContractOne { - int public i; - - constructor() { - i = 0; - } - - function foo() public{ - while(i<5){ - i++; - } - } -} - -contract ContractOneTest is DSTest { - ContractOne c1; - - function setUp() public { - c1 = new ContractOne(); - } - - function testFoo() public { - c1.foo(); - } -} - - -contract ContractTwo { - int public i; - - constructor() { - i = 0; - } - - function bar() public{ - while(i<50){ - i++; - } - } -} - -contract ContractTwoTest is DSTest { - ContractTwo c2; - - function setUp() public { - c2 = new ContractTwo(); - } - - function testBar() public { - c2.bar(); - } -} - -contract ContractThree { - int public i; - - constructor() { - i = 0; - } - - function baz() public{ - while(i<500){ - i++; - } - } -} - -contract ContractThreeTest is DSTest { - ContractThree c3; - - function setUp() public { - c3 = new ContractThree(); - } - - function testBaz() public { - c3.baz(); - } -} - "#, - ) - .unwrap(); + prj.add_source("Contracts.sol", GAS_REPORT_CONTRACTS).unwrap(); // report for One prj.write_config(Config { gas_reports: vec!["ContractOne".to_string()], ..Default::default() }); cmd.forge_fuse(); - let first_out = - cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); - assert!( - first_out.contains("foo") && !first_out.contains("bar") && !first_out.contains("baz"), - "foo:\n{first_out}" - ); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +"#]].is_jsonlines()); // report for Two prj.write_config(Config { gas_reports: vec!["ContractTwo".to_string()], ..Default::default() }); cmd.forge_fuse(); - let second_out = - cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); - assert!( - !second_out.contains("foo") && second_out.contains("bar") && !second_out.contains("baz"), - "bar:\n{second_out}" + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse().arg("test").arg("--gas-report").arg("--json").assert_success().stdout_eq( + str![[r#" +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines(), ); // report for Three @@ -1696,104 +1747,30 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let third_out = - cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); - assert!( - !third_out.contains("foo") && !third_out.contains("bar") && third_out.contains("baz"), - "baz:\n{third_out}" - ); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +"#]].is_jsonlines()); }); forgetest!(gas_ignore_some_contracts, |prj, cmd| { prj.insert_ds_test(); - prj.add_source( - "Contracts.sol", - r#" -//SPDX-license-identifier: MIT - -import "./test.sol"; - -contract ContractOne { - int public i; - - constructor() { - i = 0; - } - - function foo() public{ - while(i<5){ - i++; - } - } -} - -contract ContractOneTest is DSTest { - ContractOne c1; - - function setUp() public { - c1 = new ContractOne(); - } - - function testFoo() public { - c1.foo(); - } -} - - -contract ContractTwo { - int public i; - - constructor() { - i = 0; - } - - function bar() public{ - while(i<50){ - i++; - } - } -} - -contract ContractTwoTest is DSTest { - ContractTwo c2; - - function setUp() public { - c2 = new ContractTwo(); - } - - function testBar() public { - c2.bar(); - } -} - -contract ContractThree { - int public i; - - constructor() { - i = 0; - } - - function baz() public{ - while(i<500){ - i++; - } - } -} - -contract ContractThreeTest is DSTest { - ContractThree c3; - - function setUp() public { - c3 = new ContractThree(); - } - - function testBaz() public { - c3.baz(); - } -} - "#, - ) - .unwrap(); + prj.add_source("Contracts.sol", GAS_REPORT_CONTRACTS).unwrap(); // ignore ContractOne prj.write_config(Config { @@ -1802,9 +1779,34 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let first_out = - cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); - assert!(!first_out.contains("foo") && first_out.contains("bar") && first_out.contains("baz")); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); // ignore ContractTwo cmd.forge_fuse(); @@ -1814,11 +1816,34 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let second_out = - cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); - assert!( - second_out.contains("foo") && !second_out.contains("bar") && second_out.contains("baz") - ); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +"#]].is_jsonlines()); // ignore ContractThree cmd.forge_fuse(); @@ -1832,9 +1857,43 @@ contract ContractThreeTest is DSTest { ..Default::default() }); cmd.forge_fuse(); - let third_out = - cmd.arg("test").arg("--gas-report").assert_success().get_output().stdout_lossy(); - assert!(third_out.contains("foo") && third_out.contains("bar") && third_out.contains("baz")); + cmd.arg("test").arg("--gas-report").assert_success().stdout_eq(str![[r#" +... +| src/Contracts.sol:ContractOne contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| foo | 45387 | 45387 | 45387 | 45387 | 1 | + + +| src/Contracts.sol:ContractThree contract | | | | | | +|------------------------------------------|-----------------|--------|--------|--------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103591 | 256 | | | | | +| Function Name | min | avg | median | max | # calls | +| baz | 260712 | 260712 | 260712 | 260712 | 1 | + + +| src/Contracts.sol:ContractTwo contract | | | | | | +|----------------------------------------|-----------------|-------|--------|-------|---------| +| Deployment Cost | Deployment Size | | | | | +| 103375 | 255 | | | | | +| Function Name | min | avg | median | max | # calls | +| bar | 64984 | 64984 | 64984 | 64984 | 1 | +... + +"#]]); + cmd.forge_fuse() + .arg("test") + .arg("--gas-report") + .arg("--json") + .assert_success() + .stdout_eq(str![[r#" +{"gas":103375,"size":255,"functions":{"foo":{"foo()":{"calls":1,"min":45387,"mean":45387,"median":45387,"max":45387}}}} +{"gas":103591,"size":256,"functions":{"baz":{"baz()":{"calls":1,"min":260712,"mean":260712,"median":260712,"max":260712}}}} +{"gas":103375,"size":255,"functions":{"bar":{"bar()":{"calls":1,"min":64984,"mean":64984,"median":64984,"max":64984}}}} +"#]].is_jsonlines()); }); forgetest_init!(can_use_absolute_imports, |prj, cmd| {