From 92a5a904fd1b0de5e12ebc34fd0411bb9d77a4cc Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 11:52:30 +0100 Subject: [PATCH 01/14] add global json --flag --- crates/cast/bin/args.rs | 28 ------------------- crates/cast/bin/cmd/access_list.rs | 8 ++---- crates/cast/bin/cmd/call.rs | 7 +---- crates/cast/bin/cmd/interface.rs | 10 +++---- crates/cast/bin/cmd/logs.rs | 20 +++----------- crates/cast/bin/cmd/send.rs | 15 +++-------- crates/cast/bin/cmd/wallet/mod.rs | 24 ++++++----------- crates/cast/bin/main.rs | 14 +++++----- crates/cast/src/lib.rs | 28 +++++++------------ crates/cli/src/opts/shell.rs | 14 ++++++++-- crates/common/src/io/shell.rs | 43 ++++++++++++++++++++++++++++-- crates/forge/bin/cmd/create.rs | 4 --- crates/forge/bin/cmd/test/mod.rs | 14 +++------- crates/script/src/lib.rs | 4 --- crates/verify/src/bytecode.rs | 7 ++--- 15 files changed, 97 insertions(+), 143 deletions(-) diff --git a/crates/cast/bin/args.rs b/crates/cast/bin/args.rs index 03ca46d238268..a16436dd2ea7f 100644 --- a/crates/cast/bin/args.rs +++ b/crates/cast/bin/args.rs @@ -369,10 +369,6 @@ pub enum CastSubcommand { #[arg(long, env = "CAST_FULL_BLOCK")] full: bool, - /// Print the block as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, - #[command(flatten)] rpc: RpcOpts, }, @@ -464,10 +460,6 @@ pub enum CastSubcommand { #[arg(long, conflicts_with = "field")] raw: bool, - /// Print as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, - #[command(flatten)] rpc: RpcOpts, }, @@ -489,10 +481,6 @@ pub enum CastSubcommand { #[arg(id = "async", long = "async", env = "CAST_ASYNC", alias = "cast-async")] cast_async: bool, - /// Print as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, - #[command(flatten)] rpc: RpcOpts, }, @@ -530,10 +518,6 @@ pub enum CastSubcommand { /// The ABI-encoded calldata. calldata: String, - - /// Print the decoded calldata as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, }, /// Decode ABI-encoded string. @@ -543,10 +527,6 @@ pub enum CastSubcommand { StringDecode { /// The ABI-encoded string. data: String, - - /// Print the decoded string as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, }, /// Decode ABI-encoded input or output data. @@ -565,10 +545,6 @@ pub enum CastSubcommand { /// Whether to decode the input or output data. #[arg(long, short, help_heading = "Decode input data instead of output data")] input: bool, - - /// Print the decoded calldata as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, }, /// ABI encode the given function argument, excluding the selector. @@ -655,10 +631,6 @@ pub enum CastSubcommand { FourByteDecode { /// The ABI-encoded calldata. calldata: Option, - - /// Print the decoded calldata as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, }, /// Get the event signature for a given topic 0 from https://openchain.xyz. diff --git a/crates/cast/bin/cmd/access_list.rs b/crates/cast/bin/cmd/access_list.rs index 3d60b52903a34..c283f60c40b0b 100644 --- a/crates/cast/bin/cmd/access_list.rs +++ b/crates/cast/bin/cmd/access_list.rs @@ -35,10 +35,6 @@ pub struct AccessListArgs { #[arg(long, short = 'B')] block: Option, - /// Print the access list as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, - #[command(flatten)] tx: TransactionOpts, @@ -48,7 +44,7 @@ pub struct AccessListArgs { impl AccessListArgs { pub async fn run(self) -> Result<()> { - let Self { to, sig, args, tx, eth, block, json: to_json } = self; + let Self { to, sig, args, tx, eth, block } = self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; @@ -65,7 +61,7 @@ impl AccessListArgs { let cast = Cast::new(&provider); - let access_list: String = cast.access_list(&tx, block, to_json).await?; + let access_list: String = cast.access_list(&tx, block).await?; sh_println!("{access_list}")?; diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 355d18ee6ad7d..aefc5f1c02f58 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -69,10 +69,6 @@ pub struct CallArgs { #[arg(long, short)] block: Option, - /// Print the decoded output as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, - /// Enable Alphanet features. #[arg(long, alias = "odyssey")] pub alphanet: bool, @@ -131,7 +127,6 @@ impl CallArgs { decode_internal, labels, data, - json, .. } = self; @@ -205,7 +200,7 @@ impl CallArgs { return Ok(()); } - sh_println!("{}", Cast::new(provider).call(&tx, func.as_ref(), block, json).await?)?; + sh_println!("{}", Cast::new(provider).call(&tx, func.as_ref(), block).await?)?; Ok(()) } diff --git a/crates/cast/bin/cmd/interface.rs b/crates/cast/bin/cmd/interface.rs index 45df4983e4f09..659ce6ccaa4fa 100644 --- a/crates/cast/bin/cmd/interface.rs +++ b/crates/cast/bin/cmd/interface.rs @@ -4,7 +4,7 @@ use clap::Parser; use eyre::{Context, Result}; use foundry_block_explorers::Client; use foundry_cli::opts::EtherscanOpts; -use foundry_common::{compile::ProjectCompiler, fs}; +use foundry_common::{compile::ProjectCompiler, fs, shell}; use foundry_compilers::{info::ContractInfo, utils::canonicalize}; use foundry_config::{load_config_with_root, try_find_project_root, Config}; use itertools::Itertools; @@ -44,17 +44,13 @@ pub struct InterfaceArgs { )] output: Option, - /// If specified, the interface will be output as JSON rather than Solidity. - #[arg(long, short)] - json: bool, - #[command(flatten)] etherscan: EtherscanOpts, } impl InterfaceArgs { pub async fn run(self) -> Result<()> { - let Self { contract, name, pragma, output: output_location, etherscan, json } = self; + let Self { contract, name, pragma, output: output_location, etherscan } = self; // Determine if the target contract is an ABI file, a local contract or an Ethereum address. let abis = if Path::new(&contract).is_file() && @@ -75,7 +71,7 @@ impl InterfaceArgs { let interfaces = get_interfaces(abis)?; // Print result or write to file. - let res = if json { + let res = if shell::is_json() { // Format as JSON. interfaces.iter().map(|iface| &iface.json_abi).format("\n").to_string() } else { diff --git a/crates/cast/bin/cmd/logs.rs b/crates/cast/bin/cmd/logs.rs index b38281d51e341..154b5c9d21354 100644 --- a/crates/cast/bin/cmd/logs.rs +++ b/crates/cast/bin/cmd/logs.rs @@ -49,26 +49,14 @@ pub struct LogsArgs { #[arg(long)] subscribe: bool, - /// Print the logs as JSON.s - #[arg(long, short, help_heading = "Display options")] - json: bool, - #[command(flatten)] eth: EthereumOpts, } impl LogsArgs { pub async fn run(self) -> Result<()> { - let Self { - from_block, - to_block, - address, - sig_or_topic, - topics_or_args, - subscribe, - json, - eth, - } = self; + let Self { from_block, to_block, address, sig_or_topic, topics_or_args, subscribe, eth } = + self; let config = Config::from(ð); let provider = utils::get_provider(&config)?; @@ -88,7 +76,7 @@ impl LogsArgs { let filter = build_filter(from_block, to_block, address, sig_or_topic, topics_or_args)?; if !subscribe { - let logs = cast.filter_logs(filter, json).await?; + let logs = cast.filter_logs(filter).await?; sh_println!("{logs}")?; return Ok(()) } @@ -102,7 +90,7 @@ impl LogsArgs { .await?; let cast = Cast::new(&provider); let mut stdout = io::stdout(); - cast.subscribe(filter, &mut stdout, json).await?; + cast.subscribe(filter, &mut stdout).await?; Ok(()) } diff --git a/crates/cast/bin/cmd/send.rs b/crates/cast/bin/cmd/send.rs index 9503bccbd5499..77b6a2cddae56 100644 --- a/crates/cast/bin/cmd/send.rs +++ b/crates/cast/bin/cmd/send.rs @@ -39,10 +39,6 @@ pub struct SendTxArgs { #[arg(long, default_value = "1")] confirmations: u64, - /// Print the transaction receipt as JSON. - #[arg(long, short, help_heading = "Display options")] - json: bool, - #[command(subcommand)] command: Option, @@ -98,7 +94,6 @@ impl SendTxArgs { mut args, tx, confirmations, - json: to_json, command, unlocked, path, @@ -159,7 +154,7 @@ impl SendTxArgs { let (tx, _) = builder.build(config.sender).await?; - cast_send(provider, tx, cast_async, confirmations, timeout, to_json).await + cast_send(provider, tx, cast_async, confirmations, timeout).await // Case 2: // An option to use a local signer was provided. // If we cannot successfully instantiate a local signer, then we will assume we don't have @@ -178,7 +173,7 @@ impl SendTxArgs { .wallet(wallet) .on_provider(&provider); - cast_send(provider, tx, cast_async, confirmations, timeout, to_json).await + cast_send(provider, tx, cast_async, confirmations, timeout).await } } } @@ -189,7 +184,6 @@ async fn cast_send, T: Transport + Clone>( cast_async: bool, confs: u64, timeout: u64, - to_json: bool, ) -> Result<()> { let cast = Cast::new(provider); let pending_tx = cast.send(tx).await?; @@ -199,9 +193,8 @@ async fn cast_send, T: Transport + Clone>( if cast_async { sh_println!("{tx_hash:#x}")?; } else { - let receipt = cast - .receipt(format!("{tx_hash:#x}"), None, confs, Some(timeout), false, to_json) - .await?; + let receipt = + cast.receipt(format!("{tx_hash:#x}"), None, confs, Some(timeout), false).await?; sh_println!("{receipt}")?; } diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index 1dc2fb1c5824f..c1b5963e3deca 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -11,7 +11,7 @@ use cast::revm::primitives::Authorization; use clap::Parser; use eyre::{Context, Result}; use foundry_cli::{opts::RpcOpts, utils}; -use foundry_common::{fs, sh_println}; +use foundry_common::{fs, sh_println, shell}; use foundry_config::Config; use foundry_wallets::{RawWalletOpts, WalletOpts, WalletSigner}; use rand::thread_rng; @@ -49,10 +49,6 @@ pub enum WalletSubcommands { /// Number of wallets to generate. #[arg(long, short, default_value = "1")] number: u32, - - /// Output generated wallets as JSON. - #[arg(long, short, default_value = "false")] - json: bool, }, /// Generates a random BIP39 mnemonic phrase @@ -69,10 +65,6 @@ pub enum WalletSubcommands { /// Entropy to use for the mnemonic #[arg(long, short, conflicts_with = "words")] entropy: Option, - - /// Output generated mnemonic phrase and accounts as JSON. - #[arg(long, short, default_value = "false")] - json: bool, }, /// Generate a vanity address. @@ -219,10 +211,10 @@ pub enum WalletSubcommands { impl WalletSubcommands { pub async fn run(self) -> Result<()> { match self { - Self::New { path, unsafe_password, number, json, .. } => { + Self::New { path, unsafe_password, number, .. } => { let mut rng = thread_rng(); - let mut json_values = if json { Some(vec![]) } else { None }; + let mut json_values = if shell::is_json() { Some(vec![]) } else { None }; if let Some(path) = path { let path = match dunce::canonicalize(path.clone()) { Ok(path) => path, @@ -294,7 +286,7 @@ impl WalletSubcommands { } } } - Self::NewMnemonic { words, accounts, entropy, json } => { + Self::NewMnemonic { words, accounts, entropy } => { let phrase = if let Some(entropy) = entropy { let entropy = Entropy::from_slice(hex::decode(entropy)?)?; Mnemonic::::new_from_entropy(entropy).to_phrase() @@ -303,7 +295,7 @@ impl WalletSubcommands { Mnemonic::::new_with_count(&mut rng, words)?.to_phrase() }; - if !json { + if !shell::is_json() { sh_println!("{}", "Generating mnemonic from provided entropy...".yellow())?; } @@ -315,7 +307,7 @@ impl WalletSubcommands { let wallets = wallets.into_iter().map(|b| b.build()).collect::, _>>()?; - if !json { + if !shell::is_json() { sh_println!("{}", "Successfully generated a new mnemonic.".green())?; sh_println!("Phrase:\n{phrase}")?; sh_println!("\nAccounts:")?; @@ -324,7 +316,7 @@ impl WalletSubcommands { let mut accounts = json!([]); for (i, wallet) in wallets.iter().enumerate() { let private_key = hex::encode(wallet.credential().to_bytes()); - if json { + if shell::is_json() { accounts.as_array_mut().unwrap().push(json!({ "address": format!("{}", wallet.address()), "private_key": format!("0x{}", private_key), @@ -336,7 +328,7 @@ impl WalletSubcommands { } } - if json { + if shell::is_json() { let obj = json!({ "mnemonic": phrase, "accounts": accounts, diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 974b7eb861570..2df44bb94ef8e 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -19,7 +19,7 @@ use foundry_common::{ import_selectors, parse_signatures, pretty_calldata, ParsedSignatures, SelectorImportData, SelectorType, }, - stdin, + shell, stdin, }; use foundry_config::Config; use std::time::Instant; @@ -187,9 +187,9 @@ async fn main_args(args: CastArgs) -> Result<()> { } // ABI encoding & decoding - CastSubcommand::AbiDecode { sig, calldata, input, json } => { + CastSubcommand::AbiDecode { sig, calldata, input } => { let tokens = SimpleCast::abi_decode(&sig, &calldata, input)?; - print_tokens(&tokens, json) + print_tokens(&tokens, shell::is_json()) } CastSubcommand::AbiEncode { sig, packed, args } => { if !packed { @@ -198,16 +198,16 @@ async fn main_args(args: CastArgs) -> Result<()> { sh_println!("{}", SimpleCast::abi_encode_packed(&sig, &args)?)? } } - CastSubcommand::CalldataDecode { sig, calldata, json } => { + CastSubcommand::CalldataDecode { sig, calldata } => { let tokens = SimpleCast::calldata_decode(&sig, &calldata, true)?; - print_tokens(&tokens, json) + print_tokens(&tokens, shell::is_json()) } CastSubcommand::CalldataEncode { sig, args } => { sh_println!("{}", SimpleCast::calldata_encode(sig, &args)?)?; } - CastSubcommand::StringDecode { data, json } => { + CastSubcommand::StringDecode { data } => { let tokens = SimpleCast::calldata_decode("Any(string)", &data, true)?; - print_tokens(&tokens, json) + print_tokens(&tokens, shell::is_json()) } CastSubcommand::Interface(cmd) => cmd.run().await?, CastSubcommand::CreationCode(cmd) => cmd.run().await?, diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index bfa33c6e93f8e..807b367f6e304 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -27,7 +27,7 @@ use foundry_common::{ abi::{encode_function_args, get_func}, compile::etherscan_project, fmt::*, - fs, get_pretty_tx_receipt_attr, TransactionReceiptWithRevertReason, + fs, get_pretty_tx_receipt_attr, shell, TransactionReceiptWithRevertReason, }; use foundry_compilers::flatten::Flattener; use foundry_config::Chain; @@ -133,7 +133,6 @@ where req: &WithOtherFields, func: Option<&Function>, block: Option, - json: bool, ) -> Result { let res = self.provider.call(req).block(block.unwrap_or_default()).await?; @@ -174,7 +173,7 @@ where // handle case when return type is not specified Ok(if decoded.is_empty() { res.to_string() - } else if json { + } else if shell::is_json() { let tokens = decoded.iter().map(format_token_raw).collect::>(); serde_json::to_string_pretty(&tokens).unwrap() } else { @@ -217,11 +216,10 @@ where &self, req: &WithOtherFields, block: Option, - to_json: bool, ) -> Result { let access_list = self.provider.create_access_list(req).block_id(block.unwrap_or_default()).await?; - let res = if to_json { + let res = if shell::is_json() { serde_json::to_string(&access_list)? } else { let mut s = @@ -773,7 +771,6 @@ where confs: u64, timeout: Option, cast_async: bool, - to_json: bool, ) -> Result { let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; @@ -802,7 +799,7 @@ where Ok(if let Some(ref field) = field { get_pretty_tx_receipt_attr(&receipt, field) .ok_or_else(|| eyre::eyre!("invalid receipt field: {}", field))? - } else if to_json { + } else if shell::is_json() { // to_value first to sort json object keys serde_json::to_value(&receipt)?.to_string() } else { @@ -878,10 +875,10 @@ where )) } - pub async fn filter_logs(&self, filter: Filter, to_json: bool) -> Result { + pub async fn filter_logs(&self, filter: Filter) -> Result { let logs = self.provider.get_logs(&filter).await?; - let res = if to_json { + let res = if shell::is_json() { serde_json::to_string(&logs)? } else { let mut s = vec![]; @@ -973,12 +970,7 @@ where /// # Ok(()) /// # } /// ``` - pub async fn subscribe( - &self, - filter: Filter, - output: &mut dyn io::Write, - to_json: bool, - ) -> Result<()> { + pub async fn subscribe(&self, filter: Filter, output: &mut dyn io::Write) -> Result<()> { // Initialize the subscription stream for logs let mut subscription = self.provider.subscribe_logs(&filter).await?.into_stream(); @@ -992,7 +984,7 @@ where let to_block_number = filter.get_to_block(); // If output should be JSON, start with an opening bracket - if to_json { + if shell::is_json() { write!(output, "[")?; } @@ -1014,7 +1006,7 @@ where }, // Process incoming log log = subscription.next() => { - if to_json { + if shell::is_json() { if !first { write!(output, ",")?; } @@ -1037,7 +1029,7 @@ where } // If output was JSON, end with a closing bracket - if to_json { + if shell::is_json() { write!(output, "]")?; } diff --git a/crates/cli/src/opts/shell.rs b/crates/cli/src/opts/shell.rs index 9213c670224c3..e05e72734f602 100644 --- a/crates/cli/src/opts/shell.rs +++ b/crates/cli/src/opts/shell.rs @@ -1,5 +1,5 @@ use clap::Parser; -use foundry_common::shell::{ColorChoice, Shell, Verbosity}; +use foundry_common::shell::{ColorChoice, OutputFormat, Shell, Verbosity}; // note: `verbose` and `quiet` cannot have `short` because of conflicts with multiple commands. @@ -21,6 +21,10 @@ pub struct ShellOpts { )] pub quiet: bool, + /// Format output as JSON. + #[clap(short, long, global = true, help_heading = "Display options")] + pub json: bool, + /// Log messages coloring. #[clap(long, global = true, value_enum, help_heading = "Display options")] pub color: Option, @@ -34,6 +38,12 @@ impl ShellOpts { (false, false) => Verbosity::Normal, (true, true) => unreachable!(), }; - Shell::new_with(self.color.unwrap_or_default(), verbosity) + let color = self.json.then_some(ColorChoice::Never).or(self.color).unwrap_or_default(); + let format = match self.json { + true => OutputFormat::Json, + false => OutputFormat::Text, + }; + + Shell::new_with(format, color, verbosity) } } diff --git a/crates/common/src/io/shell.rs b/crates/common/src/io/shell.rs index a132b343d11ec..416ab9fe4a399 100644 --- a/crates/common/src/io/shell.rs +++ b/crates/common/src/io/shell.rs @@ -27,6 +27,11 @@ pub fn is_quiet() -> bool { verbosity().is_quiet() } +/// Returns whether the output format is [`OutputFormat::Json`]. +pub fn is_json() -> bool { + Shell::get().output_format().is_json() +} + /// The global shell instance. static GLOBAL_SHELL: OnceLock> = OnceLock::new(); @@ -95,6 +100,30 @@ impl Verbosity { } } +/// The requested output format. +#[derive(Debug, Default, Clone, Copy, PartialEq)] +pub enum OutputFormat { + /// Plain text output. + #[default] + Text, + /// JSON output. + Json, +} + +impl OutputFormat { + /// Returns true if the output format is `Text`. + #[inline] + pub fn is_text(self) -> bool { + self == Self::Text + } + + /// Returns true if the output format is `Json`. + #[inline] + pub fn is_json(self) -> bool { + self == Self::Json + } +} + /// An abstraction around console output that remembers preferences for output /// verbosity and color. pub struct Shell { @@ -102,6 +131,9 @@ pub struct Shell { /// output to a memory buffer which is useful for tests. output: ShellOut, + /// The format to use for output. + output_format: OutputFormat, + /// How verbose messages should be. verbosity: Verbosity, @@ -158,12 +190,12 @@ impl Shell { /// output. #[inline] pub fn new() -> Self { - Self::new_with(ColorChoice::Auto, Verbosity::Verbose) + Self::new_with(OutputFormat::Text, ColorChoice::Auto, Verbosity::Verbose) } /// Creates a new shell with the given color choice and verbosity. #[inline] - pub fn new_with(color: ColorChoice, verbosity: Verbosity) -> Self { + pub fn new_with(format: OutputFormat, color: ColorChoice, verbosity: Verbosity) -> Self { Self { output: ShellOut::Stream { stdout: AutoStream::new(std::io::stdout(), color.to_anstream_color_choice()), @@ -171,6 +203,7 @@ impl Shell { color_choice: color, stderr_tty: std::io::stderr().is_terminal(), }, + output_format: format, verbosity, needs_clear: AtomicBool::new(false), } @@ -181,6 +214,7 @@ impl Shell { pub fn empty() -> Self { Self { output: ShellOut::Empty(std::io::empty()), + output_format: OutputFormat::Text, verbosity: Verbosity::Quiet, needs_clear: AtomicBool::new(false), } @@ -239,6 +273,11 @@ impl Shell { self.verbosity } + /// Gets the output format of the shell. + pub fn output_format(&self) -> OutputFormat { + self.output_format + } + /// Gets the current color choice. /// /// If we are not using a color stream, this will always return `Never`, even if the color diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index e9f6cac745e1e..371dac0fd600c 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -57,10 +57,6 @@ pub struct CreateArgs { )] constructor_args_path: Option, - /// Print the deployment information as JSON. - #[arg(long, help_heading = "Display options")] - json: bool, - /// Verify contract after creation. #[arg(long)] verify: bool, diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 4c973217f4d7a..4218b4aa4759c 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -118,10 +118,6 @@ pub struct TestArgs { #[arg(long, env = "FORGE_ALLOW_FAILURE")] allow_failure: bool, - /// Output test results in JSON format. - #[arg(long, help_heading = "Display options")] - pub json: bool, - /// Output test results as JUnit XML report. #[arg(long, conflicts_with_all(["json", "gas_report"]), help_heading = "Display options")] pub junit: bool, @@ -480,7 +476,7 @@ impl TestArgs { output: &ProjectCompileOutput, ) -> eyre::Result { if self.list { - return list(runner, filter, self.json); + return list(runner, filter); } trace!(target: "forge::test", "running all tests"); @@ -908,14 +904,10 @@ impl Provider for TestArgs { } /// Lists all matching tests -fn list( - runner: MultiContractRunner, - filter: &ProjectPathsAwareFilter, - json: bool, -) -> Result { +fn list(runner: MultiContractRunner, filter: &ProjectPathsAwareFilter) -> Result { let results = runner.list(filter); - if json { + if shell::is_json() { println!("{}", serde_json::to_string(&results)?); } else { for (file, contracts) in results.iter() { diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index ec19c0bf97162..40766ad2b2383 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -180,10 +180,6 @@ pub struct ScriptArgs { #[arg(long)] pub verify: bool, - /// Output results in JSON format. - #[arg(long)] - pub json: bool, - /// Gas price for legacy transactions, or max fee per gas for EIP1559 transactions, either /// specified in wei, or as a string with a unit type. /// diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 02ca28c200665..bbe48b864822c 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -16,6 +16,7 @@ use foundry_cli::{ opts::EtherscanOpts, utils::{self, read_constructor_args_file, LoadConfig}, }; +use foundry_common::shell; use foundry_compilers::{artifacts::EvmVersion, info::ContractInfo}; use foundry_config::{figment, impl_figment_convert, Config}; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, utils::configure_tx_env}; @@ -75,10 +76,6 @@ pub struct VerifyBytecodeArgs { #[clap(flatten)] pub verifier: VerifierArgs, - /// Suppress logs and emit json results to stdout - #[clap(long, default_value = "false")] - pub json: bool, - /// The project's root path. /// /// By default root of the Git repository, if in one, @@ -144,7 +141,7 @@ impl VerifyBytecodeArgs { eyre::bail!("No bytecode found at address {}", self.address); } - if !self.json { + if !shell::is_json() { println!( "Verifying bytecode for contract {} at address {}", self.contract.name.clone().green(), From 0872f1894179d4f16dea19746666fc33717f991e Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 11:58:40 +0100 Subject: [PATCH 02/14] finish port to shell::is_json --- crates/cast/bin/main.rs | 16 ++++++++-------- crates/cast/src/lib.rs | 13 ++++--------- crates/forge/bin/cmd/create.rs | 5 +++-- crates/forge/bin/cmd/test/mod.rs | 10 +++++----- crates/forge/bin/main.rs | 3 ++- crates/script/src/lib.rs | 4 ++-- crates/verify/src/bytecode.rs | 12 ++++-------- crates/verify/src/utils.rs | 7 +++---- 8 files changed, 31 insertions(+), 39 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 2df44bb94ef8e..57ff612c844d6 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -271,13 +271,13 @@ async fn main_args(args: CastArgs) -> Result<()> { Cast::new(provider).base_fee(block.unwrap_or(BlockId::Number(Latest))).await? )? } - CastSubcommand::Block { block, full, field, json, rpc } => { + CastSubcommand::Block { block, full, field, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; sh_println!( "{}", Cast::new(provider) - .block(block.unwrap_or(BlockId::Number(Latest)), full, field, json) + .block(block.unwrap_or(BlockId::Number(Latest)), full, field) .await? )? } @@ -432,26 +432,26 @@ async fn main_args(args: CastArgs) -> Result<()> { sh_println!("{}", serde_json::json!(receipt))?; } } - CastSubcommand::Receipt { tx_hash, field, json, cast_async, confirmations, rpc } => { + CastSubcommand::Receipt { tx_hash, field, cast_async, confirmations, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; sh_println!( "{}", Cast::new(provider) - .receipt(tx_hash, field, confirmations, None, cast_async, json) + .receipt(tx_hash, field, confirmations, None, cast_async) .await? )? } CastSubcommand::Run(cmd) => cmd.run().await?, CastSubcommand::SendTx(cmd) => cmd.run().await?, - CastSubcommand::Tx { tx_hash, field, raw, json, rpc } => { + CastSubcommand::Tx { tx_hash, field, raw, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; // Can use either --raw or specify raw as a field let raw = raw || field.as_ref().is_some_and(|f| f == "raw"); - sh_println!("{}", Cast::new(&provider).transaction(tx_hash, field, raw, json).await?)? + sh_println!("{}", Cast::new(&provider).transaction(tx_hash, field, raw).await?)? } // 4Byte @@ -465,7 +465,7 @@ async fn main_args(args: CastArgs) -> Result<()> { sh_println!("{sig}")? } } - CastSubcommand::FourByteDecode { calldata, json } => { + CastSubcommand::FourByteDecode { calldata } => { let calldata = stdin::unwrap_line(calldata)?; let sigs = decode_calldata(&calldata).await?; sigs.iter().enumerate().for_each(|(i, sig)| { @@ -482,7 +482,7 @@ async fn main_args(args: CastArgs) -> Result<()> { }; let tokens = SimpleCast::calldata_decode(sig, &calldata, true)?; - print_tokens(&tokens, json) + print_tokens(&tokens, shell::is_json()) } CastSubcommand::FourByteEvent { topic } => { let topic = stdin::unwrap_line(topic)?; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 807b367f6e304..88a54213bba92 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -337,7 +337,6 @@ where block: B, full: bool, field: Option, - to_json: bool, ) -> Result { let block = block.into(); if let Some(ref field) = field { @@ -355,7 +354,7 @@ where let block = if let Some(ref field) = field { get_pretty_block_attr(&block, field) .unwrap_or_else(|| format!("{field} is not a valid block field")) - } else if to_json { + } else if shell::is_json() { serde_json::to_value(&block).unwrap().to_string() } else { block.pretty() @@ -372,7 +371,6 @@ where false, // Select only select field Some(field), - false, ) .await?; @@ -406,14 +404,12 @@ where false, // Select only block hash Some(String::from("hash")), - false, ) .await?; Ok(match &genesis_hash[..] { "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" => { - match &(Self::block(self, 1920000, false, Some("hash".to_string()), false).await?)[..] - { + match &(Self::block(self, 1920000, false, Some("hash".to_string())).await?)[..] { "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" => { "etclive" } @@ -451,7 +447,7 @@ where "0x6d3c66c5357ec91d5c43af47e234a939b22557cbb552dc45bebbceeed90fbe34" => "bsctest", "0x0d21840abff46b96c84b2ac9e10e4f5cdaeb5693cb665db62a2f3b02d2d57b5b" => "bsc", "0x31ced5b9beb7f8782b014660da0cb18cc409f121f408186886e1ca3e8eeca96b" => { - match &(Self::block(self, 1, false, Some(String::from("hash")), false).await?)[..] { + match &(Self::block(self, 1, false, Some(String::from("hash"))).await?)[..] { "0x738639479dc82d199365626f90caa82f7eafcfe9ed354b456fb3d294597ceb53" => { "avalanche-fuji" } @@ -726,7 +722,6 @@ where tx_hash: String, field: Option, raw: bool, - to_json: bool, ) -> Result { let tx_hash = TxHash::from_str(&tx_hash).wrap_err("invalid tx hash")?; let tx = self @@ -740,7 +735,7 @@ where } else if let Some(field) = field { get_pretty_tx_attr(&tx, field.as_str()) .ok_or_else(|| eyre::eyre!("invalid tx field: {}", field.to_string()))? - } else if to_json { + } else if shell::is_json() { // to_value first to sort json object keys serde_json::to_value(&tx)?.to_string() } else { diff --git a/crates/forge/bin/cmd/create.rs b/crates/forge/bin/cmd/create.rs index 371dac0fd600c..93de30e98eab8 100644 --- a/crates/forge/bin/cmd/create.rs +++ b/crates/forge/bin/cmd/create.rs @@ -18,6 +18,7 @@ use foundry_cli::{ use foundry_common::{ compile::{self}, fmt::parse_tokens, + shell, }; use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize}; use foundry_config::{ @@ -105,7 +106,7 @@ impl CreateArgs { project.find_contract_path(&self.contract.name)? }; - let mut output = compile::compile_target(&target_path, &project, self.json)?; + let mut output = compile::compile_target(&target_path, &project, shell::is_json())?; let (abi, bin, _) = remove_contract(&mut output, &target_path, &self.contract.name)?; @@ -311,7 +312,7 @@ impl CreateArgs { let (deployed_contract, receipt) = deployer.send_with_receipt().await?; let address = deployed_contract; - if self.json { + if shell::is_json() { let output = json!({ "deployer": deployer_address.to_string(), "deployedTo": address.to_string(), diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 4218b4aa4759c..9a85690a8e17f 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -20,7 +20,7 @@ use foundry_cli::{ opts::CoreBuildArgs, utils::{self, LoadConfig}, }; -use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs}; +use foundry_common::{compile::ProjectCompiler, evm::EvmArgs, fs, shell}; use foundry_compilers::{ artifacts::output_selection::OutputSelection, compilers::{multi::MultiCompilerLanguage, CompilerSettings, Language}, @@ -304,7 +304,7 @@ impl TestArgs { let sources_to_compile = self.get_sources_to_compile(&config, &filter)?; let compiler = - ProjectCompiler::new().quiet(self.json || self.junit).files(sources_to_compile); + ProjectCompiler::new().quiet(shell::is_json() || self.junit).files(sources_to_compile); let output = compiler.compile(&project)?; @@ -482,7 +482,7 @@ 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 silent = self.gas_report && shell::is_json(); let num_filtered = runner.matching_test_functions(filter).count(); if num_filtered != 1 && (self.debug.is_some() || self.flamegraph || self.flamechart) { @@ -510,7 +510,7 @@ impl TestArgs { } // Run tests in a non-streaming fashion and collect results for serialization. - if !self.gas_report && self.json { + if !self.gas_report && shell::is_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() { @@ -580,7 +580,7 @@ impl TestArgs { config.gas_reports.clone(), config.gas_reports_ignore.clone(), config.gas_reports_include_tests, - if self.json { GasReportKind::JSON } else { GasReportKind::Markdown }, + if shell::is_json() { GasReportKind::JSON } else { GasReportKind::Markdown }, ) }); diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs index a78218e94e0ab..95c94a814cafb 100644 --- a/crates/forge/bin/main.rs +++ b/crates/forge/bin/main.rs @@ -2,6 +2,7 @@ use clap::{CommandFactory, Parser}; use clap_complete::generate; use eyre::Result; use foundry_cli::{handler, utils}; +use foundry_common::shell; use foundry_evm::inspectors::cheatcodes::{set_execution_context, ForgeContext}; mod cmd; @@ -42,7 +43,7 @@ fn run() -> Result<()> { if cmd.is_watch() { utils::block_on(watch::watch_test(cmd)) } else { - let silent = cmd.junit || cmd.json; + let silent = cmd.junit || shell::is_json(); let outcome = utils::block_on(cmd.run())?; outcome.ensure_ok(silent) } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 40766ad2b2383..707399d6305dc 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -30,7 +30,7 @@ use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::{ abi::{encode_function_args, get_func}, evm::{Breakpoints, EvmArgs}, - ContractsByArtifact, CONTRACT_MAX_SIZE, SELECTOR_LEN, + shell, ContractsByArtifact, CONTRACT_MAX_SIZE, SELECTOR_LEN, }; use foundry_compilers::ArtifactId; use foundry_config::{ @@ -258,7 +258,7 @@ impl ScriptArgs { }; } - if pre_simulation.args.json { + if shell::is_json() { pre_simulation.show_json()?; } else { pre_simulation.show_traces().await?; diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index bbe48b864822c..a4f368a961f7a 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -211,7 +211,7 @@ impl VerifyBytecodeArgs { crate::utils::check_args_len(&artifact, &constructor_args)?; if maybe_predeploy { - if !self.json { + if !shell::is_json() { println!( "{}", format!("Attempting to verify predeployed contract at {:?}. Ignoring creation code verification.", self.address) @@ -287,7 +287,6 @@ impl VerifyBytecodeArgs { ); crate::utils::print_result( - &self, match_type, BytecodeType::Runtime, &mut json_results, @@ -295,7 +294,7 @@ impl VerifyBytecodeArgs { &config, ); - if self.json { + if shell::is_json() { sh_println!("{}", serde_json::to_string(&json_results)?)?; } @@ -373,7 +372,6 @@ impl VerifyBytecodeArgs { ); crate::utils::print_result( - &self, match_type, BytecodeType::Creation, &mut json_results, @@ -384,14 +382,13 @@ impl VerifyBytecodeArgs { // If the creation code does not match, the runtime also won't match. Hence return. if match_type.is_none() { crate::utils::print_result( - &self, None, BytecodeType::Runtime, &mut json_results, etherscan_metadata, &config, ); - if self.json { + if shell::is_json() { sh_println!("{}", serde_json::to_string(&json_results)?)?; } return Ok(()); @@ -485,7 +482,6 @@ impl VerifyBytecodeArgs { ); crate::utils::print_result( - &self, match_type, BytecodeType::Runtime, &mut json_results, @@ -494,7 +490,7 @@ impl VerifyBytecodeArgs { ); } - if self.json { + if shell::is_json() { sh_println!("{}", serde_json::to_string(&json_results)?)?; } Ok(()) diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index aaf8a0e016b75..e3065aca08539 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -9,7 +9,7 @@ use foundry_block_explorers::{ contract::{ContractCreationData, ContractMetadata, Metadata}, errors::EtherscanError, }; -use foundry_common::{abi::encode_args, compile::ProjectCompiler, provider::RetryProvider}; +use foundry_common::{abi::encode_args, compile::ProjectCompiler, provider::RetryProvider, shell}; use foundry_compilers::artifacts::{BytecodeHash, CompactContractBytecode, EvmVersion}; use foundry_config::Config; use foundry_evm::{constants::DEFAULT_CREATE2_DEPLOYER, executors::TracingExecutor, opts::EvmOpts}; @@ -137,7 +137,6 @@ pub fn build_using_cache( } pub fn print_result( - args: &VerifyBytecodeArgs, res: Option, bytecode_type: BytecodeType, json_results: &mut Vec, @@ -145,7 +144,7 @@ pub fn print_result( config: &Config, ) { if let Some(res) = res { - if !args.json { + if !shell::is_json() { println!( "{} with status {}", format!("{bytecode_type:?} code matched").green().bold(), @@ -155,7 +154,7 @@ pub fn print_result( let json_res = JsonResult { bytecode_type, match_type: Some(res), message: None }; json_results.push(json_res); } - } else if !args.json { + } else if !shell::is_json() { println!( "{}", format!( From 405e2c2a14bf16e680b56bdee5a3e637c3aec69c Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 12:09:14 +0100 Subject: [PATCH 03/14] fix test --- crates/cast/tests/cli/main.rs | 3 +++ crates/cli/src/opts/shell.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/tests/cli/cmd.rs | 3 +++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 8a8d512f83c22..631fac91fd96a 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -40,6 +40,9 @@ Display options: - always: Force color output - never: Force disable color output + -j, --json + Format log messages as JSON + -q, --quiet Do not print log messages diff --git a/crates/cli/src/opts/shell.rs b/crates/cli/src/opts/shell.rs index e05e72734f602..563df081f6ff5 100644 --- a/crates/cli/src/opts/shell.rs +++ b/crates/cli/src/opts/shell.rs @@ -21,7 +21,7 @@ pub struct ShellOpts { )] pub quiet: bool, - /// Format output as JSON. + /// Format log messages as JSON. #[clap(short, long, global = true, help_heading = "Display options")] pub json: bool, diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 9a85690a8e17f..e3f12d1b6eb66 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -147,7 +147,7 @@ pub struct TestArgs { /// Max concurrent threads to use. /// Default value is the number of available CPUs. - #[arg(long, short = 'j', visible_alias = "jobs")] + #[arg(long, visible_alias = "jobs")] pub threads: Option, /// Show test execution progress. diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index f9e8997bd99ce..46d0689ba7550 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -45,6 +45,9 @@ Display options: - always: Force color output - never: Force disable color output + -j, --json + Format log messages as JSON + -q, --quiet Do not print log messages From 82a32dea3ed99cf8b3586a35fde894045ad5e499 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 12:53:32 +0100 Subject: [PATCH 04/14] update message --- crates/common/src/io/shell.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/io/shell.rs b/crates/common/src/io/shell.rs index 416ab9fe4a399..0831b500bf103 100644 --- a/crates/common/src/io/shell.rs +++ b/crates/common/src/io/shell.rs @@ -131,7 +131,7 @@ pub struct Shell { /// output to a memory buffer which is useful for tests. output: ShellOut, - /// The format to use for output. + /// The format to use for message output. output_format: OutputFormat, /// How verbose messages should be. From 9f22dd5c950ee0e705139637e430240a60238627 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 13:45:15 +0100 Subject: [PATCH 05/14] very strange stalling bug, fixed by assignment? --- crates/cast/bin/main.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 57ff612c844d6..6c64818e128d4 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -274,12 +274,10 @@ async fn main_args(args: CastArgs) -> Result<()> { CastSubcommand::Block { block, full, field, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - sh_println!( - "{}", - Cast::new(provider) - .block(block.unwrap_or(BlockId::Number(Latest)), full, field) - .await? - )? + let block = Cast::new(provider) + .block(block.unwrap_or(BlockId::Number(Latest)), full, field) + .await?; + sh_println!("{block}")? } CastSubcommand::BlockNumber { rpc, block } => { let config = Config::from(&rpc); @@ -435,12 +433,10 @@ async fn main_args(args: CastArgs) -> Result<()> { CastSubcommand::Receipt { tx_hash, field, cast_async, confirmations, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - sh_println!( - "{}", - Cast::new(provider) - .receipt(tx_hash, field, confirmations, None, cast_async) - .await? - )? + let receipt = Cast::new(provider) + .receipt(tx_hash, field, confirmations, None, cast_async) + .await?; + sh_println!("{receipt}")? } CastSubcommand::Run(cmd) => cmd.run().await?, CastSubcommand::SendTx(cmd) => cmd.run().await?, From 3f0175cda3d02b2b4bdf5096b42ac0d9d65703c5 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 14:16:27 +0100 Subject: [PATCH 06/14] remove jobs -j shorthand clashing with global json flag --- crates/cast/bin/cmd/create2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index 271e5b43ef602..250703060571a 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -74,7 +74,7 @@ pub struct Create2Args { init_code_hash: Option, /// Number of threads to use. Defaults to and caps at the number of logical cores. - #[arg(short, long)] + #[arg(long)] jobs: Option, /// Address of the caller. Used for the first 20 bytes of the salt. From eaa714ab533a5b89bf3a78e92f259c5e27c36305 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 14:23:02 +0100 Subject: [PATCH 07/14] fix test after -j change --- crates/cast/bin/cmd/create2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index 250703060571a..84e5fe1574aa4 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -405,7 +405,7 @@ mod tests { "--starts-with=0x00", "--init-code-hash=0x479d7e8f31234e208d704ba1a123c76385cea8a6981fd675b784fbd9cffb918d", "--seed=0x479d7e8f31234e208d704ba1a123c76385cea8a6981fd675b784fbd9cffb918d", - "-j1", + "--jobs=1", ]); let out = args.run().unwrap(); assert_eq!(out.address, address!("00614b3D65ac4a09A376a264fE1aE5E5E12A6C43")); @@ -422,7 +422,7 @@ mod tests { "--starts-with=0x00", "--init-code-hash=0x479d7e8f31234e208d704ba1a123c76385cea8a6981fd675b784fbd9cffb918d", "--no-random", - "-j1", + "--jobs=1", ]); let out = args.run().unwrap(); assert_eq!(out.address, address!("00bF495b8b42fdFeb91c8bCEB42CA4eE7186AEd2")); From 0eed4012a4b6582b24447c1701f0a91aad4478e8 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 14:25:36 +0100 Subject: [PATCH 08/14] fix doctests --- crates/cast/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 88a54213bba92..c22d931bcd754 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -123,7 +123,7 @@ where /// let tx = TransactionRequest::default().to(to).input(bytes.into()); /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(alloy_provider); - /// let data = cast.call(&tx, None, None, false).await?; + /// let data = cast.call(&tx, None, None).await?; /// println!("{}", data); /// # Ok(()) /// # } @@ -207,7 +207,7 @@ where /// let tx = TransactionRequest::default().to(to).input(bytes.into()); /// let tx = WithOtherFields::new(tx); /// let cast = Cast::new(&provider); - /// let access_list = cast.access_list(&tx, None, false).await?; + /// let access_list = cast.access_list(&tx, None).await?; /// println!("{}", access_list); /// # Ok(()) /// # } @@ -327,7 +327,7 @@ where /// let provider = /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); - /// let block = cast.block(5, true, None, false).await?; + /// let block = cast.block(5, true, None).await?; /// println!("{}", block); /// # Ok(()) /// # } @@ -712,7 +712,7 @@ where /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; - /// let tx = cast.transaction(tx_hash.to_string(), None, false, false).await?; + /// let tx = cast.transaction(tx_hash.to_string(), None, false).await?; /// println!("{}", tx); /// # Ok(()) /// # } @@ -754,7 +754,7 @@ where /// ProviderBuilder::<_, _, AnyNetwork>::default().on_builtin("http://localhost:8545").await?; /// let cast = Cast::new(provider); /// let tx_hash = "0xf8d1713ea15a81482958fb7ddf884baee8d3bcc478c5f2f604e008dc788ee4fc"; - /// let receipt = cast.receipt(tx_hash.to_string(), None, 1, None, false, false).await?; + /// let receipt = cast.receipt(tx_hash.to_string(), None, 1, None, false).await?; /// println!("{}", receipt); /// # Ok(()) /// # } @@ -961,7 +961,7 @@ where /// let filter = /// Filter::new().address(Address::from_str("0x00000000006c3852cbEf3e08E8dF289169EdE581")?); /// let mut output = io::stdout(); - /// cast.subscribe(filter, &mut output, false).await?; + /// cast.subscribe(filter, &mut output).await?; /// # Ok(()) /// # } /// ``` From 38b7c99ce6043c661e0ff0c5ed8e6129ac03cd49 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 16:39:33 +0100 Subject: [PATCH 09/14] temporarily disable junit conflict, revert -j as --json shorthand --- crates/cast/bin/cmd/create2.rs | 6 +++--- crates/cast/tests/cli/main.rs | 2 +- crates/cli/src/opts/shell.rs | 2 +- crates/forge/bin/cmd/test/mod.rs | 4 ++-- crates/forge/tests/cli/cmd.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/cast/bin/cmd/create2.rs b/crates/cast/bin/cmd/create2.rs index 84e5fe1574aa4..271e5b43ef602 100644 --- a/crates/cast/bin/cmd/create2.rs +++ b/crates/cast/bin/cmd/create2.rs @@ -74,7 +74,7 @@ pub struct Create2Args { init_code_hash: Option, /// Number of threads to use. Defaults to and caps at the number of logical cores. - #[arg(long)] + #[arg(short, long)] jobs: Option, /// Address of the caller. Used for the first 20 bytes of the salt. @@ -405,7 +405,7 @@ mod tests { "--starts-with=0x00", "--init-code-hash=0x479d7e8f31234e208d704ba1a123c76385cea8a6981fd675b784fbd9cffb918d", "--seed=0x479d7e8f31234e208d704ba1a123c76385cea8a6981fd675b784fbd9cffb918d", - "--jobs=1", + "-j1", ]); let out = args.run().unwrap(); assert_eq!(out.address, address!("00614b3D65ac4a09A376a264fE1aE5E5E12A6C43")); @@ -422,7 +422,7 @@ mod tests { "--starts-with=0x00", "--init-code-hash=0x479d7e8f31234e208d704ba1a123c76385cea8a6981fd675b784fbd9cffb918d", "--no-random", - "--jobs=1", + "-j1", ]); let out = args.run().unwrap(); assert_eq!(out.address, address!("00bF495b8b42fdFeb91c8bCEB42CA4eE7186AEd2")); diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 631fac91fd96a..6fb3ddb73c58a 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -40,7 +40,7 @@ Display options: - always: Force color output - never: Force disable color output - -j, --json + --json Format log messages as JSON -q, --quiet diff --git a/crates/cli/src/opts/shell.rs b/crates/cli/src/opts/shell.rs index 563df081f6ff5..808e522ce5409 100644 --- a/crates/cli/src/opts/shell.rs +++ b/crates/cli/src/opts/shell.rs @@ -22,7 +22,7 @@ pub struct ShellOpts { pub quiet: bool, /// Format log messages as JSON. - #[clap(short, long, global = true, help_heading = "Display options")] + #[clap(long, global = true, help_heading = "Display options")] pub json: bool, /// Log messages coloring. diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index e3f12d1b6eb66..3f7691d7e5aeb 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -119,7 +119,7 @@ pub struct TestArgs { allow_failure: bool, /// Output test results as JUnit XML report. - #[arg(long, conflicts_with_all(["json", "gas_report"]), help_heading = "Display options")] + #[arg(long, conflicts_with = "gas_report", help_heading = "Display options")] pub junit: bool, /// Stop running tests after the first failure. @@ -147,7 +147,7 @@ pub struct TestArgs { /// Max concurrent threads to use. /// Default value is the number of available CPUs. - #[arg(long, visible_alias = "jobs")] + #[arg(long, short = 'j', visible_alias = "jobs")] pub threads: Option, /// Show test execution progress. diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 46d0689ba7550..1560850b26f51 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -45,7 +45,7 @@ Display options: - always: Force color output - never: Force disable color output - -j, --json + --json Format log messages as JSON -q, --quiet From 3f22b889ffa84e679137d4440d776e3878e74b29 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 16:59:31 +0100 Subject: [PATCH 10/14] tag --color, --quiet as conflicting with --json --- crates/cast/bin/cmd/wallet/mod.rs | 10 ++++++---- crates/cast/src/lib.rs | 7 ++++--- crates/cli/src/opts/shell.rs | 8 +++++++- crates/forge/bin/cmd/build.rs | 15 +++++---------- crates/forge/bin/cmd/test/mod.rs | 2 +- crates/forge/tests/cli/build.rs | 6 +++--- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/crates/cast/bin/cmd/wallet/mod.rs b/crates/cast/bin/cmd/wallet/mod.rs index c1b5963e3deca..8023b8bdff334 100644 --- a/crates/cast/bin/cmd/wallet/mod.rs +++ b/crates/cast/bin/cmd/wallet/mod.rs @@ -295,7 +295,9 @@ impl WalletSubcommands { Mnemonic::::new_with_count(&mut rng, words)?.to_phrase() }; - if !shell::is_json() { + let format_json = shell::is_json(); + + if !format_json { sh_println!("{}", "Generating mnemonic from provided entropy...".yellow())?; } @@ -307,7 +309,7 @@ impl WalletSubcommands { let wallets = wallets.into_iter().map(|b| b.build()).collect::, _>>()?; - if !shell::is_json() { + if !format_json { sh_println!("{}", "Successfully generated a new mnemonic.".green())?; sh_println!("Phrase:\n{phrase}")?; sh_println!("\nAccounts:")?; @@ -316,7 +318,7 @@ impl WalletSubcommands { let mut accounts = json!([]); for (i, wallet) in wallets.iter().enumerate() { let private_key = hex::encode(wallet.credential().to_bytes()); - if shell::is_json() { + if format_json { accounts.as_array_mut().unwrap().push(json!({ "address": format!("{}", wallet.address()), "private_key": format!("0x{}", private_key), @@ -328,7 +330,7 @@ impl WalletSubcommands { } } - if shell::is_json() { + if format_json { let obj = json!({ "mnemonic": phrase, "accounts": accounts, diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index c22d931bcd754..75272b3b1a997 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -976,10 +976,11 @@ where None }; + let format_json = shell::is_json(); let to_block_number = filter.get_to_block(); // If output should be JSON, start with an opening bracket - if shell::is_json() { + if format_json { write!(output, "[")?; } @@ -1001,7 +1002,7 @@ where }, // Process incoming log log = subscription.next() => { - if shell::is_json() { + if format_json { if !first { write!(output, ",")?; } @@ -1024,7 +1025,7 @@ where } // If output was JSON, end with a closing bracket - if shell::is_json() { + if format_json { write!(output, "]")?; } diff --git a/crates/cli/src/opts/shell.rs b/crates/cli/src/opts/shell.rs index 808e522ce5409..fd83b952bef0f 100644 --- a/crates/cli/src/opts/shell.rs +++ b/crates/cli/src/opts/shell.rs @@ -22,7 +22,13 @@ pub struct ShellOpts { pub quiet: bool, /// Format log messages as JSON. - #[clap(long, global = true, help_heading = "Display options")] + #[clap( + long, + global = true, + alias = "format-json", + conflicts_with_all = &["quiet", "color"], + help_heading = "Display options" + )] pub json: bool, /// Log messages coloring. diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index f6d3483342325..f2a1891e26343 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -2,7 +2,7 @@ use super::{install, watch::WatchArgs}; use clap::Parser; use eyre::Result; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; -use foundry_common::compile::ProjectCompiler; +use foundry_common::{compile::ProjectCompiler, shell}; use foundry_compilers::{ compilers::{multi::MultiCompilerLanguage, Language}, utils::source_files_iter, @@ -73,12 +73,6 @@ pub struct BuildArgs { #[command(flatten)] #[serde(skip)] pub watch: WatchArgs, - - /// Output the compilation errors in the json format. - /// This is useful when you want to use the output in other tools. - #[arg(long, conflicts_with = "quiet")] - #[serde(skip)] - pub format_json: bool, } impl BuildArgs { @@ -102,17 +96,18 @@ impl BuildArgs { } } + let format_json = shell::is_json(); let compiler = ProjectCompiler::new() .files(files) .print_names(self.names) .print_sizes(self.sizes) .ignore_eip_3860(self.ignore_eip_3860) - .quiet(self.format_json) - .bail(!self.format_json); + .quiet(format_json) + .bail(!format_json); let output = compiler.compile(&project)?; - if self.format_json { + if format_json { sh_println!("{}", serde_json::to_string_pretty(&output.output())?)?; } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 3f7691d7e5aeb..27638937b97cd 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -119,7 +119,7 @@ pub struct TestArgs { allow_failure: bool, /// Output test results as JUnit XML report. - #[arg(long, conflicts_with = "gas_report", help_heading = "Display options")] + #[arg(long, conflicts_with_all = ["quiet", "json", "gas_report"], help_heading = "Display options")] pub junit: bool, /// Stop running tests after the first failure. diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 812754c725520..1fe0b1775666a 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -21,10 +21,10 @@ Compiler run successful! forgetest!(throws_on_conflicting_args, |prj, cmd| { prj.clear(); - cmd.args(["compile", "--format-json", "--quiet"]).assert_failure().stderr_eq(str![[r#" -error: the argument '--format-json' cannot be used with '--quiet' + cmd.args(["compile", "--json", "--quiet"]).assert_failure().stderr_eq(str![[r#" +error: the argument '--json' cannot be used with '--quiet' -Usage: forge[..] build --format-json [PATHS]... +Usage: forge[..] build --json [PATHS]... For more information, try '--help'. From ef50b7764b738a65be8b3c7e15a4347ff58dad07 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 17:58:19 +0100 Subject: [PATCH 11/14] update tests to be aware of global args to avoid `Argument or group quiet specified in conflicts_with* for junit does not exist` error --- crates/cast/bin/main.rs | 20 +++++++++------- crates/forge/bin/cmd/test/mod.rs | 40 ++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 6c64818e128d4..57ff612c844d6 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -274,10 +274,12 @@ async fn main_args(args: CastArgs) -> Result<()> { CastSubcommand::Block { block, full, field, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - let block = Cast::new(provider) - .block(block.unwrap_or(BlockId::Number(Latest)), full, field) - .await?; - sh_println!("{block}")? + sh_println!( + "{}", + Cast::new(provider) + .block(block.unwrap_or(BlockId::Number(Latest)), full, field) + .await? + )? } CastSubcommand::BlockNumber { rpc, block } => { let config = Config::from(&rpc); @@ -433,10 +435,12 @@ async fn main_args(args: CastArgs) -> Result<()> { CastSubcommand::Receipt { tx_hash, field, cast_async, confirmations, rpc } => { let config = Config::from(&rpc); let provider = utils::get_provider(&config)?; - let receipt = Cast::new(provider) - .receipt(tx_hash, field, confirmations, None, cast_async) - .await?; - sh_println!("{receipt}")? + sh_println!( + "{}", + Cast::new(provider) + .receipt(tx_hash, field, confirmations, None, cast_async) + .await? + )? } CastSubcommand::Run(cmd) => cmd.run().await?, CastSubcommand::SendTx(cmd) => cmd.run().await?, diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 27638937b97cd..fe9ab64edf6b6 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -991,38 +991,54 @@ fn junit_xml_report(results: &BTreeMap, verbosity: u8) -> R #[cfg(test)] mod tests { + use crate::opts::{Forge, ForgeSubcommand}; + use super::*; use foundry_config::{Chain, InvariantConfig}; use foundry_test_utils::forgetest_async; #[test] fn watch_parse() { - let args: TestArgs = TestArgs::parse_from(["foundry-cli", "-vw"]); - assert!(args.watch.watch.is_some()); + let args = Forge::parse_from(["foundry-cli", "test", "-vw"]); + if let ForgeSubcommand::Test(args) = args.cmd { + assert!(args.watch.watch.is_some()); + } } #[test] fn fuzz_seed() { - let args: TestArgs = TestArgs::parse_from(["foundry-cli", "--fuzz-seed", "0x10"]); - assert!(args.fuzz_seed.is_some()); + let args = Forge::parse_from(["foundry-cli", "test", "--fuzz-seed", "0x10"]); + if let ForgeSubcommand::Test(args) = args.cmd { + assert!(args.fuzz_seed.is_some()); + } } // #[test] fn fuzz_seed_exists() { - let args: TestArgs = - TestArgs::parse_from(["foundry-cli", "-vvv", "--gas-report", "--fuzz-seed", "0x10"]); - assert!(args.fuzz_seed.is_some()); + let args = Forge::parse_from([ + "foundry-cli", + "test", + "-vvv", + "--gas-report", + "--fuzz-seed", + "0x10", + ]); + if let ForgeSubcommand::Test(args) = args.cmd { + assert!(args.fuzz_seed.is_some()); + } } #[test] fn extract_chain() { let test = |arg: &str, expected: Chain| { - let args = TestArgs::parse_from(["foundry-cli", arg]); - assert_eq!(args.evm_opts.env.chain, Some(expected)); - let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); - assert_eq!(config.chain, Some(expected)); - assert_eq!(evm_opts.env.chain_id, Some(expected.id())); + let args = Forge::parse_from(["foundry-cli", "test", arg]); + if let ForgeSubcommand::Test(args) = args.cmd { + assert_eq!(args.evm_opts.env.chain, Some(expected)); + let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); + assert_eq!(config.chain, Some(expected)); + assert_eq!(evm_opts.env.chain_id, Some(expected.id())); + } }; test("--chain-id=1", Chain::mainnet()); test("--chain-id=42", Chain::from_id(42)); From c10af4708c9dd3b219155060c90261d2b643cc28 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 18:04:27 +0100 Subject: [PATCH 12/14] fix missed test --- crates/forge/bin/cmd/test/mod.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index fe9ab64edf6b6..469eb434269d3 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -1088,23 +1088,26 @@ contract FooBarTest is DSTest { ) .unwrap(); - let args = TestArgs::parse_from([ + let args = Forge::parse_from([ "foundry-cli", + "test", "--gas-report", "--root", &prj.root().to_string_lossy(), ]); - let outcome = args.run().await.unwrap(); - let gas_report = outcome.gas_report.unwrap(); - - assert_eq!(gas_report.contracts.len(), 3); - let call_cnts = gas_report - .contracts - .values() - .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)); + if let ForgeSubcommand::Test(args) = args.cmd { + let outcome = args.run().await.unwrap(); + let gas_report = outcome.gas_report.unwrap(); + + assert_eq!(gas_report.contracts.len(), 3); + let call_cnts = gas_report + .contracts + .values() + .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)); + } }); } From dc6dc39eae5d40da1aa803c7c57b13c1674c91f0 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 18:24:09 +0100 Subject: [PATCH 13/14] make sure tests throw on non-matching command --- crates/forge/bin/cmd/test/mod.rs | 81 ++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 469eb434269d3..e2707b82fc196 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -999,46 +999,52 @@ mod tests { #[test] fn watch_parse() { - let args = Forge::parse_from(["foundry-cli", "test", "-vw"]); - if let ForgeSubcommand::Test(args) = args.cmd { - assert!(args.watch.watch.is_some()); - } + let args = match Forge::parse_from(["foundry-cli", "test", "-vw"]).cmd { + ForgeSubcommand::Test(args) => args, + _ => unreachable!(), + }; + assert!(args.watch.watch.is_some()); } #[test] fn fuzz_seed() { - let args = Forge::parse_from(["foundry-cli", "test", "--fuzz-seed", "0x10"]); - if let ForgeSubcommand::Test(args) = args.cmd { - assert!(args.fuzz_seed.is_some()); - } + let args = match Forge::parse_from(["foundry-cli", "test", "--fuzz-seed", "0x10"]).cmd { + ForgeSubcommand::Test(args) => args, + _ => unreachable!(), + }; + assert!(args.fuzz_seed.is_some()); } // #[test] fn fuzz_seed_exists() { - let args = Forge::parse_from([ + let args = match Forge::parse_from([ "foundry-cli", "test", "-vvv", "--gas-report", "--fuzz-seed", "0x10", - ]); - if let ForgeSubcommand::Test(args) = args.cmd { - assert!(args.fuzz_seed.is_some()); - } + ]) + .cmd + { + ForgeSubcommand::Test(args) => args, + _ => unreachable!(), + }; + assert!(args.fuzz_seed.is_some()); } #[test] fn extract_chain() { let test = |arg: &str, expected: Chain| { - let args = Forge::parse_from(["foundry-cli", "test", arg]); - if let ForgeSubcommand::Test(args) = args.cmd { - assert_eq!(args.evm_opts.env.chain, Some(expected)); - let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); - assert_eq!(config.chain, Some(expected)); - assert_eq!(evm_opts.env.chain_id, Some(expected.id())); - } + let args = match Forge::parse_from(["foundry-cli", "test", arg]).cmd { + ForgeSubcommand::Test(args) => args, + _ => unreachable!(), + }; + assert_eq!(args.evm_opts.env.chain, Some(expected)); + let (config, evm_opts) = args.load_config_and_evm_opts().unwrap(); + assert_eq!(config.chain, Some(expected)); + assert_eq!(evm_opts.env.chain_id, Some(expected.id())); }; test("--chain-id=1", Chain::mainnet()); test("--chain-id=42", Chain::from_id(42)); @@ -1088,26 +1094,29 @@ contract FooBarTest is DSTest { ) .unwrap(); - let args = Forge::parse_from([ + let args = match Forge::parse_from([ "foundry-cli", "test", "--gas-report", "--root", &prj.root().to_string_lossy(), - ]); - - if let ForgeSubcommand::Test(args) = args.cmd { - let outcome = args.run().await.unwrap(); - let gas_report = outcome.gas_report.unwrap(); - - assert_eq!(gas_report.contracts.len(), 3); - let call_cnts = gas_report - .contracts - .values() - .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)); - } + ]) + .cmd + { + ForgeSubcommand::Test(args) => args, + _ => unreachable!(), + }; + + let outcome = args.run().await.unwrap(); + let gas_report = outcome.gas_report.unwrap(); + + assert_eq!(gas_report.contracts.len(), 3); + let call_cnts = gas_report + .contracts + .values() + .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)); }); } From a276e47d139c69c9dd9d70e9a27686f5c71e8e34 Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 1 Nov 2024 18:41:18 +0100 Subject: [PATCH 14/14] use --format-json in command to show alias works --- crates/forge/tests/cli/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/forge/tests/cli/build.rs b/crates/forge/tests/cli/build.rs index 1fe0b1775666a..4b257fe8d20a2 100644 --- a/crates/forge/tests/cli/build.rs +++ b/crates/forge/tests/cli/build.rs @@ -21,7 +21,7 @@ Compiler run successful! forgetest!(throws_on_conflicting_args, |prj, cmd| { prj.clear(); - cmd.args(["compile", "--json", "--quiet"]).assert_failure().stderr_eq(str![[r#" + cmd.args(["compile", "--format-json", "--quiet"]).assert_failure().stderr_eq(str![[r#" error: the argument '--json' cannot be used with '--quiet' Usage: forge[..] build --json [PATHS]...