Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: forge test snapshot flags #9710

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

turbocrime
Copy link

@turbocrime turbocrime commented Jan 18, 2025

Motivation

fixes #9697

Solution

i've added command-line flags and an environment variable FORGE_SNAPSHOT_EMIT which allow a user to control snapshot generation.

i also slightly refactored the use of environment variable FORGE_SNAPSHOT_CHECK.

existing default behaviors are maintained.

Comment on lines +126 to +127
#[arg(long, env = "FORGE_SNAPSHOT_EMIT", action = ArgAction::Set, default_value="true", default_missing_value = "true", require_equals=true, num_args = 0..=1)]
snapshot_emit: bool,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a little strange but it's the only way to get decent behavior out of clap

@@ -662,7 +670,7 @@ impl TestArgs {
if !gas_snapshots.is_empty() {
// Check for differences in gas snapshots if `FORGE_SNAPSHOT_CHECK` is set.
// Exiting early with code 1 if differences are found.
if std::env::var("FORGE_SNAPSHOT_CHECK").is_ok() {
if self.snapshot_check {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

direct env var access is now moved to another clap flag.

the behavior now supports 'false' values as well, instead of just conditioning on presence.

@turbocrime
Copy link
Author

cc @zerosnacks assigned to #9697

@zerosnacks
Copy link
Member

Generally supportive on the approach, defaulting to true also makes sense here

Copy link
Collaborator

@grandizzy grandizzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you! left couple of comments

@@ -973,6 +983,40 @@ mod tests {
test("--chain-id=42", Chain::from_id(42));
}

fn env_bool(env_name: &str, test_fn: impl Fn(Option<bool>)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is needed, you could use figment::Jail for such as in

fn test_toml_file_non_existing_config_var_failure() {
figment::Jail::expect_with(|jail| {
jail.set_env("FOUNDRY_CONFIG", "this config does not exist");
let _config = Config::load();
Ok(())
});
}

#[arg(long, env = "FORGE_SNAPSHOT_EMIT", action = ArgAction::Set, default_value="true", default_missing_value = "true", require_equals=true, num_args = 0..=1)]
snapshot_emit: bool,

/// Check snapshot results
Copy link
Collaborator

@grandizzy grandizzy Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Check snapshot results
/// Disable checking for differences in recorded gas snapshots.

@@ -122,6 +122,14 @@ pub struct TestArgs {
#[arg(long, env = "FORGE_ALLOW_FAILURE")]
allow_failure: bool,

/// Enable/disable writing snapshot results
Copy link
Collaborator

@grandizzy grandizzy Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Enable/disable writing snapshot results
/// Disable recording of gas snapshot results.

@@ -122,6 +122,14 @@ pub struct TestArgs {
#[arg(long, env = "FORGE_ALLOW_FAILURE")]
allow_failure: bool,

/// Enable/disable writing snapshot results
#[arg(long, env = "FORGE_SNAPSHOT_EMIT", action = ArgAction::Set, default_value="true", default_missing_value = "true", require_equals=true, num_args = 0..=1)]
Copy link
Collaborator

@grandizzy grandizzy Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe have FORGE_NO_SNAPSHOT_RECORD / no_snapshot_record like

    /// Disable recording of snapshot results.
    #[arg(long, env = "FORGE_SNAPSHOT_NO_RECORD", help_heading = "Gas snapshot options")]
    no_snapshot_record: bool,

so one should only pass --no-snapshot-record arg. Also suggest adding a help heading to have them grouped, e.g.

Gas snapshot options:
      --snapshot-no-record
          Enable/disable writing snapshot results
          
          [env: FORGE_SNAPSHOT_NO_RECORD=]

      --snapshot-no-check
          Omit checking snapshot results
          
          [env: FORGE_SNAPSHOT_CHECK=]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my use case, i'd like to mainly control via env vars. using a 'negative' env variable seems ergonomically poor.

additionally, clap seems to have trouble with 'negation' flags that interact with each other, especially if env variables are used, and especially when default values are present.

i can make another attempt at this, but clap seems to very strongly encourage the =[true/false] format.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, maybe put them in config then? You could then make different profiles to run gas snapshot without need of args

}
}

#[test]
Copy link
Collaborator

@grandizzy grandizzy Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO would be better to have test to pass these flags and check if the gas snapshot dir is (or is not) written to disk, something like

forgetest_init!(config_load_no_panic, |prj, cmd| {
    prj.add_test(
        "GasSnapshotRecordTest.t.sol",
        r#"
import "forge-std/Test.sol";

contract GasSnapshotRecordTest is Test {
    function test_start_stop_gas_recording() public {
        vm.startSnapshotGas("testRecord");
        uint256 a = 256;
        a++;
        vm.stopSnapshotGas();
    }
}
     "#,
    )
    .unwrap();

    cmd.args(["clean"]).assert_success();
    cmd.forge_fuse().args(["test", "--mc", "GasSnapshotRecordTest", "--snapshot-no-record"]).assert_success();
    assert!(!prj.root().join("snapshots/").exists());

    cmd.forge_fuse().args(["test", "--mc", "GasSnapshotRecordTest"]).assert_success();
    assert!(prj.root().join("snapshots/").exists());
});

snapshot_emit: bool,

/// Check snapshot results
#[arg(long, env = "FORGE_SNAPSHOT_CHECK", value_parser=FalseyValueParser::new())]
Copy link
Collaborator

@grandizzy grandizzy Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think #[arg(long, env = "FORGE_SNAPSHOT_NO_CHECK", help_heading = "Gas snapshot options")] is enough, that is one could pass --snapshot-no-check arg to omit differences check.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FORGE_SNAPSHOT_CHECK is already established as an env var, and i was avoiding the introduction of another FORGE_SNAPSHOT_NO_CHECK var.

if both variables exist, it raises a lot of questions! i would be worried about creating a breaking change.

@grandizzy
Copy link
Collaborator

Generally supportive on the approach, defaulting to true also makes sense here

I'd suggest having args like --snapshot-no-record and --snapshot-no-check that would disable default behavior if passed, or something along these lines, would that work?

@zerosnacks zerosnacks self-assigned this Jan 21, 2025
@turbocrime
Copy link
Author

the environment variable FORGE_SNAPSHOT_CHECK is already established. adding an additional FORGE_SNAPSHOT_NO_CHECK seems like it could be confusing. interactions between the env and the flags become unclear.

i made the FORGE_SNAPSHOT_CHECK change because

  • using a clap flag seemed simple and more consistent than checking the env directly in the conditional
  • it collected all the available options and env var access in the clap options block
  • the current behavior simply conditions on presence, so something like FORGE_SNAPSHOT_CHECK="false" is interpreted as a truthy value!!

but now i see that also raises many questions, and it is not entirely consistent with the behavior of my added flag.

i will revert changes to FORGE_SNAPSHOT_CHECK and create an issue describing its present behavior. it may be good to resolve that, and all the questions there, before adding a new flag.

@zerosnacks
Copy link
Member

i will revert changes to FORGE_SNAPSHOT_CHECK and create an issue describing its present behavior. it may be good to resolve that, and all the questions there, before adding a new flag.

added the gas_snapshot_check config entry + flag + suggested change to FORGE_SNAPSHOT_CHECK here: #9791

@turbocrime
Copy link
Author

thanks for working on this, wound up with other tasks on my plate and wasn't able to make time

@turbocrime turbocrime closed this Jan 30, 2025
@turbocrime turbocrime reopened this Jan 30, 2025
@turbocrime
Copy link
Author

the emit behavior still remains to be implemented? will leave this open for now, might get to it with free time this week

@zerosnacks
Copy link
Member

zerosnacks commented Jan 30, 2025

@turbocrime No worries! Feel free to ping me if you run into (time) issues / have questions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-forge Command: forge T-feature Type: feature
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

feat: provide some way to disable generation of section snapshots by the snapshot cheatcodes
3 participants