Skip to content

Commit fe92e7e

Browse files
authored
feat: add gas_snapshot_check flag to config, fix FORGE_SNAPSHOT_CHECK behavior (#9791)
* add gas_snapshot_check configuration option w/ FORGE_SNAPSHOT_CHECK, checking for bool value - not just existence * add additional test to display behaviour * improve docs * fix clippy * improve test suite, tests all combinations exhaustively * fix failing test * fix nit * small nits
1 parent dbf1c2a commit fe92e7e

File tree

3 files changed

+197
-2
lines changed

3 files changed

+197
-2
lines changed

crates/config/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ pub struct Config {
200200
pub cache_path: PathBuf,
201201
/// where the gas snapshots are stored
202202
pub snapshots: PathBuf,
203+
/// whether to check for differences against previously stored gas snapshots
204+
pub gas_snapshot_check: bool,
203205
/// where the broadcast logs are stored
204206
pub broadcast: PathBuf,
205207
/// additional solc allow paths for `--allow-paths`
@@ -2316,6 +2318,7 @@ impl Default for Config {
23162318
cache_path: "cache".into(),
23172319
broadcast: "broadcast".into(),
23182320
snapshots: "snapshots".into(),
2321+
gas_snapshot_check: false,
23192322
allow_paths: vec![],
23202323
include_paths: vec![],
23212324
force: false,

crates/forge/bin/cmd/test/mod.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ pub struct TestArgs {
118118
#[arg(long, env = "FORGE_GAS_REPORT")]
119119
gas_report: bool,
120120

121+
/// Check gas snapshots against previous runs.
122+
#[arg(long, env = "FORGE_SNAPSHOT_CHECK")]
123+
gas_snapshot_check: Option<bool>,
124+
121125
/// Exit with code 0 even if a test fails.
122126
#[arg(long, env = "FORGE_ALLOW_FAILURE")]
123127
allow_failure: bool,
@@ -662,9 +666,18 @@ impl TestArgs {
662666

663667
// Write gas snapshots to disk if any were collected.
664668
if !gas_snapshots.is_empty() {
665-
// Check for differences in gas snapshots if `FORGE_SNAPSHOT_CHECK` is set.
669+
// By default `gas_snapshot_check` is set to `false` in the config.
670+
//
671+
// The user can either:
672+
// - Set `FORGE_SNAPSHOT_CHECK=true` in the environment.
673+
// - Pass `--gas-snapshot-check=true` as a CLI argument.
674+
// - Set `gas_snapshot_check = true` in the config.
675+
//
676+
// If the user passes `--gas-snapshot-check=<bool>` then it will override the config
677+
// and the environment variable, disabling the check if `false` is passed.
678+
//
666679
// Exiting early with code 1 if differences are found.
667-
if std::env::var("FORGE_SNAPSHOT_CHECK").is_ok() {
680+
if self.gas_snapshot_check.unwrap_or(config.gas_snapshot_check) {
668681
let differences_found = gas_snapshots.clone().into_iter().fold(
669682
false,
670683
|mut found, (group, snapshots)| {

crates/forge/tests/cli/config.rs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ forgetest!(can_extract_config_values, |prj, cmd| {
4040
cache: true,
4141
cache_path: "test-cache".into(),
4242
snapshots: "snapshots".into(),
43+
gas_snapshot_check: false,
4344
broadcast: "broadcast".into(),
4445
force: true,
4546
evm_version: EvmVersion::Byzantium,
@@ -978,6 +979,7 @@ libraries = []
978979
cache = true
979980
cache_path = "cache"
980981
snapshots = "snapshots"
982+
gas_snapshot_check = false
981983
broadcast = "broadcast"
982984
allow_paths = []
983985
include_paths = []
@@ -1132,6 +1134,7 @@ exclude = []
11321134
"cache": true,
11331135
"cache_path": "cache",
11341136
"snapshots": "snapshots",
1137+
"gas_snapshot_check": false,
11351138
"broadcast": "broadcast",
11361139
"allow_paths": [],
11371140
"include_paths": [],
@@ -1365,3 +1368,179 @@ optimizer_runs = 0
13651368
13661369
"#]]);
13671370
});
1371+
1372+
forgetest_init!(test_gas_snapshot_check_config, |prj, cmd| {
1373+
// Default settings: gas_snapshot_check disabled.
1374+
cmd.forge_fuse().args(["config"]).assert_success().stdout_eq(str![[r#"
1375+
...
1376+
gas_snapshot_check = false
1377+
...
1378+
1379+
"#]]);
1380+
1381+
prj.insert_ds_test();
1382+
1383+
prj.add_source(
1384+
"Flare.sol",
1385+
r#"
1386+
contract Flare {
1387+
bytes32[] public data;
1388+
1389+
function run(uint256 n_) public {
1390+
for (uint256 i = 0; i < n_; i++) {
1391+
data.push(keccak256(abi.encodePacked(i)));
1392+
}
1393+
}
1394+
}
1395+
"#,
1396+
)
1397+
.unwrap();
1398+
1399+
let test_contract = |n: u32| {
1400+
format!(
1401+
r#"
1402+
import "./test.sol";
1403+
import "./Flare.sol";
1404+
1405+
interface Vm {{
1406+
function startSnapshotGas(string memory name) external;
1407+
function stopSnapshotGas() external returns (uint256);
1408+
}}
1409+
1410+
contract GasSnapshotCheckTest is DSTest {{
1411+
Vm constant vm = Vm(HEVM_ADDRESS);
1412+
1413+
Flare public flare;
1414+
1415+
function setUp() public {{
1416+
flare = new Flare();
1417+
}}
1418+
1419+
function testSnapshotGasSectionExternal() public {{
1420+
vm.startSnapshotGas("testAssertGasExternal");
1421+
flare.run({n});
1422+
vm.stopSnapshotGas();
1423+
}}
1424+
}}
1425+
"#
1426+
)
1427+
};
1428+
1429+
// Assert that gas_snapshot_check is disabled by default.
1430+
prj.add_source("GasSnapshotCheckTest.sol", &test_contract(1)).unwrap();
1431+
cmd.forge_fuse().args(["test"]).assert_success().stdout_eq(str![[r#"
1432+
...
1433+
Ran 1 test for src/GasSnapshotCheckTest.sol:GasSnapshotCheckTest
1434+
[PASS] testSnapshotGasSectionExternal() ([GAS])
1435+
Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED]
1436+
...
1437+
"#]]);
1438+
1439+
// Enable gas_snapshot_check.
1440+
let config = Config { gas_snapshot_check: true, ..Default::default() };
1441+
prj.write_config(config);
1442+
cmd.forge_fuse().args(["config"]).assert_success().stdout_eq(str![[r#"
1443+
...
1444+
gas_snapshot_check = true
1445+
...
1446+
1447+
"#]]);
1448+
1449+
// Replace the test contract with a new one that will fail the gas snapshot check.
1450+
prj.add_source("GasSnapshotCheckTest.sol", &test_contract(2)).unwrap();
1451+
cmd.forge_fuse().args(["test"]).assert_failure().stderr_eq(str![[r#"
1452+
...
1453+
[GasSnapshotCheckTest] Failed to match snapshots:
1454+
- [testAssertGasExternal] [..] → [..]
1455+
1456+
Error: Snapshots differ from previous run
1457+
...
1458+
"#]]);
1459+
1460+
// Disable gas_snapshot_check, assert that running the test will pass.
1461+
let config = Config { gas_snapshot_check: false, ..Default::default() };
1462+
prj.write_config(config);
1463+
cmd.forge_fuse().args(["test"]).assert_success().stdout_eq(str![[r#"
1464+
...
1465+
Ran 1 test for src/GasSnapshotCheckTest.sol:GasSnapshotCheckTest
1466+
[PASS] testSnapshotGasSectionExternal() ([GAS])
1467+
Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED]
1468+
...
1469+
"#]]);
1470+
1471+
// Re-enable gas_snapshot_check
1472+
// Assert that the new value has been stored from the previous run and re-run the test.
1473+
let config = Config { gas_snapshot_check: true, ..Default::default() };
1474+
prj.write_config(config);
1475+
cmd.forge_fuse().args(["test"]).assert_success().stdout_eq(str![[r#"
1476+
...
1477+
Ran 1 test for src/GasSnapshotCheckTest.sol:GasSnapshotCheckTest
1478+
[PASS] testSnapshotGasSectionExternal() ([GAS])
1479+
Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED]
1480+
...
1481+
"#]]);
1482+
1483+
// Replace the test contract with a new one that will fail the gas_snapshot_check.
1484+
prj.add_source("GasSnapshotCheckTest.sol", &test_contract(3)).unwrap();
1485+
cmd.forge_fuse().args(["test"]).assert_failure().stderr_eq(str![[r#"
1486+
...
1487+
[GasSnapshotCheckTest] Failed to match snapshots:
1488+
- [testAssertGasExternal] [..] → [..]
1489+
1490+
Error: Snapshots differ from previous run
1491+
...
1492+
"#]]);
1493+
1494+
// Test that `--gas-snapshot-check=false` flag can be used to disable the gas_snapshot_check.
1495+
cmd.forge_fuse().args(["test", "--gas-snapshot-check=false"]).assert_success().stdout_eq(str![
1496+
[r#"
1497+
...
1498+
Ran 1 test for src/GasSnapshotCheckTest.sol:GasSnapshotCheckTest
1499+
[PASS] testSnapshotGasSectionExternal() ([GAS])
1500+
Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED]
1501+
...
1502+
"#]
1503+
]);
1504+
1505+
// Disable gas_snapshot_check in the config file.
1506+
// Enable using `FORGE_SNAPSHOT_CHECK` environment variable.
1507+
// Assert that this will override the config file value.
1508+
let config = Config { gas_snapshot_check: false, ..Default::default() };
1509+
prj.write_config(config);
1510+
prj.add_source("GasSnapshotCheckTest.sol", &test_contract(4)).unwrap();
1511+
cmd.forge_fuse();
1512+
cmd.env("FORGE_SNAPSHOT_CHECK", "true");
1513+
cmd.args(["test"]).assert_failure().stderr_eq(str![[r#"
1514+
...
1515+
[GasSnapshotCheckTest] Failed to match snapshots:
1516+
- [testAssertGasExternal] [..] → [..]
1517+
1518+
Error: Snapshots differ from previous run
1519+
...
1520+
"#]]);
1521+
1522+
// Assert that `--gas-snapshot-check=true` flag can be used to enable the gas_snapshot_check
1523+
// even when `FORGE_SNAPSHOT_CHECK` is set to false in the environment variable.
1524+
cmd.forge_fuse();
1525+
cmd.env("FORGE_SNAPSHOT_CHECK", "false");
1526+
cmd.args(["test", "--gas-snapshot-check=true"]).assert_failure().stderr_eq(str![[r#"
1527+
...
1528+
[GasSnapshotCheckTest] Failed to match snapshots:
1529+
- [testAssertGasExternal] [..] → [..]
1530+
1531+
Error: Snapshots differ from previous run
1532+
...
1533+
"#]]);
1534+
1535+
// Finally assert that `--gas-snapshot-check=false` flag can be used to disable the
1536+
// gas_snapshot_check even when `FORGE_SNAPSHOT_CHECK` is set to true
1537+
cmd.forge_fuse();
1538+
cmd.env("FORGE_SNAPSHOT_CHECK", "true");
1539+
cmd.args(["test", "--gas-snapshot-check=false"]).assert_success().stdout_eq(str![[r#"
1540+
...
1541+
Ran 1 test for src/GasSnapshotCheckTest.sol:GasSnapshotCheckTest
1542+
[PASS] testSnapshotGasSectionExternal() ([GAS])
1543+
Suite result: ok. 1 passed; 0 failed; 0 skipped; [ELAPSED]
1544+
...
1545+
"#]]);
1546+
});

0 commit comments

Comments
 (0)