Skip to content

Commit 1485e11

Browse files
authored
available_gas support (#1514)
<!-- Reference any GitHub issues resolved by this PR --> Closes #618 ## Introduced changes <!-- A brief description of the changes --> - `available_gas` attribute can be used with snforge now ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [x] Performed self-review of the code - [x] Added changes to `CHANGELOG.md`
1 parent 59c73c7 commit 1485e11

File tree

24 files changed

+189
-101
lines changed

24 files changed

+189
-101
lines changed

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
scarb 2.4.0
1+
scarb 2.4.4

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
- `store` and `load` cheatcodes
1515

16+
#### Changed
17+
18+
- `available_gas` attribute is now supported (Scarb >= 2.4.4 is required)
19+
1620
#### Fixed
1721

1822
- Error message for tests that should panic but pass

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/forge-runner/src/gas.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::HashMap;
22

3+
use crate::test_case_summary::{Single, TestCaseSummary};
34
use blockifier::fee::eth_gas_constants;
45
use blockifier::fee::fee_utils::calculate_tx_l1_gas_usage;
56
use blockifier::fee::gas_usage::get_message_segment_length;
@@ -106,3 +107,27 @@ fn get_onchain_data_segment_length(state_changes_count: StateChangesCount) -> us
106107

107108
onchain_data_segment_length
108109
}
110+
111+
pub fn check_available_gas(
112+
available_gas: &Option<usize>,
113+
summary: TestCaseSummary<Single>,
114+
) -> TestCaseSummary<Single> {
115+
match summary {
116+
TestCaseSummary::Passed {
117+
name,
118+
arguments,
119+
gas_info,
120+
..
121+
} if available_gas.map_or(false, |available_gas| gas_info > available_gas as u128) => {
122+
TestCaseSummary::Failed {
123+
name,
124+
msg: Some(format!(
125+
"\n\tTest cost exceeded the available gas. Consumed gas: ~{gas_info}"
126+
)),
127+
arguments,
128+
test_statistics: (),
129+
}
130+
}
131+
_ => summary,
132+
}
133+
}

crates/forge-runner/src/running.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,19 +176,15 @@ pub fn run_test_case(
176176
runner_params: &Arc<RunnerParams>,
177177
) -> Result<RunResultWithInfo> {
178178
ensure!(
179-
case.available_gas.is_none(),
180-
"\n Attribute `available_gas` is not supported\n"
179+
case.available_gas != Some(0),
180+
"\n\t`available_gas` attribute was incorrectly configured. Make sure you use scarb >= 2.4.4\n"
181181
);
182-
let available_gas = Some(usize::MAX);
183182

184183
let func = runner.find_function(case.name.as_str()).unwrap();
185-
let initial_gas = runner
186-
.get_initial_available_gas(func, available_gas)
187-
.unwrap();
188184
let runner_args: Vec<Arg> = args.into_iter().map(Arg::Value).collect();
189185

190186
let (entry_code, builtins) = runner
191-
.create_entry_code(func, &runner_args, initial_gas)
187+
.create_entry_code(func, &runner_args, usize::MAX)
192188
.unwrap();
193189
let footer = SierraCasmRunner::create_code_footer();
194190
let instructions = chain!(
@@ -301,7 +297,7 @@ fn extract_test_case_summary(
301297
}
302298
}
303299
// `ForkStateReader.get_block_info`, `get_fork_state_reader` may return an error
304-
// unsupported `available_gas` attribute may be specified
300+
// `available_gas` may be specified with Scarb ~2.4
305301
Err(error) => Ok(TestCaseSummary::Failed {
306302
name: case.name.clone(),
307303
msg: Some(error.to_string()),

crates/forge-runner/src/sierra_casm_runner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ impl SierraCasmRunner {
380380

381381
/// Returns the initial value for the gas counter.
382382
/// If `available_gas` is None returns 0.
383+
#[allow(dead_code)]
383384
pub fn get_initial_available_gas(
384385
&self,
385386
func: &Function,
@@ -401,6 +402,7 @@ impl SierraCasmRunner {
401402
.ok_or(RunnerError::NotEnoughGasToCall)
402403
}
403404

405+
#[allow(dead_code)]
404406
pub fn initial_required_gas(&self, func: &Function) -> Option<usize> {
405407
if self.metadata.gas_info.function_costs.is_empty() {
406408
return None;

crates/forge-runner/src/test_case_summary.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::compiled_runnable::TestCaseRunnable;
22
use crate::expected_result::{ExpectedPanicValue, ExpectedTestResult};
3+
use crate::gas::check_available_gas;
34
use cairo_felt::Felt252;
45
use cairo_lang_runner::short_string::as_cairo_short_string;
56
use cairo_lang_runner::{RunResult, RunResultValue};
@@ -195,13 +196,16 @@ impl TestCaseSummary<Single> {
195196
let msg = extract_result_data(&run_result, &test_case.expected_result);
196197
match run_result.value {
197198
RunResultValue::Success(_) => match &test_case.expected_result {
198-
ExpectedTestResult::Success => TestCaseSummary::Passed {
199-
name,
200-
msg,
201-
arguments,
202-
test_statistics: (),
203-
gas_info: gas,
204-
},
199+
ExpectedTestResult::Success => {
200+
let summary = TestCaseSummary::Passed {
201+
name,
202+
msg,
203+
arguments,
204+
test_statistics: (),
205+
gas_info: gas,
206+
};
207+
check_available_gas(&test_case.available_gas, summary)
208+
}
205209
ExpectedTestResult::Panics(_) => TestCaseSummary::Failed {
206210
name,
207211
msg,

crates/forge/src/init.rs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ use anyhow::{anyhow, Context, Ok, Result};
22

33
use include_dir::{include_dir, Dir};
44

5-
use regex::Regex;
5+
use scarb_api::version::scarb_version_command;
66
use scarb_api::ScarbCommand;
77
use std::fs::{self, OpenOptions};
88
use std::io::Write;
99
use std::path::Path;
10-
use std::str::from_utf8;
1110
use toml_edit::{value, ArrayOfTables, Document, Item, Table};
1211

1312
const CAIRO_EDITION: &str = "2023_10";
@@ -106,25 +105,7 @@ pub fn run(project_name: &str) -> Result<()> {
106105
.run()
107106
.context("Failed to add snforge_std")?;
108107

109-
let scarb_version = ScarbCommand::new()
110-
.arg("--version")
111-
.inherit_stderr()
112-
.command()
113-
.output()
114-
.context("Failed to execute `scarb --version`")?;
115-
let version_output = from_utf8(&scarb_version.stdout)
116-
.context("Failed to parse `scarb --version` output to UTF-8")?;
117-
118-
let cairo_version_regex = Regex::new(r"(?:cairo:?\s*)([0-9]+.[0-9]+.[0-9]+)")
119-
.expect("Could not create cairo version matching regex");
120-
let cairo_version_capture = cairo_version_regex
121-
.captures(version_output)
122-
.expect("Could not find cairo version");
123-
let cairo_version = cairo_version_capture
124-
.get(1)
125-
.expect("Could not find cairo version")
126-
.as_str();
127-
108+
let cairo_version = scarb_version_command()?.cairo;
128109
ScarbCommand::new_with_stdio()
129110
.current_dir(&project_path)
130111
.offline()

crates/forge/src/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ use camino::Utf8Path;
33

44
use crate::scarb::load_test_artifacts;
55
use forge_runner::test_case_summary::AnyTestCaseSummary;
6+
use semver::Version;
67
use std::sync::Arc;
78

89
use compiled_raw::{CompiledTestCrateRaw, RawForkConfig, RawForkParams};
910
use forge_runner::test_crate_summary::TestCrateSummary;
1011
use forge_runner::{RunnerConfig, RunnerParams, TestCrateRunResult};
1112

1213
use crate::block_number_map::BlockNumberMap;
14+
use crate::pretty_printing::print_warning;
1315
use forge_runner::compiled_runnable::{CompiledTestCrateRunnable, TestCaseRunnable};
16+
use scarb_api::version::scarb_version_command;
1417

1518
use crate::scarb::config::ForkTarget;
1619
use crate::test_filter::TestsFilter;
@@ -105,6 +108,8 @@ pub async fn run(
105108
let not_filtered: usize = test_crates.iter().map(|tc| tc.test_cases.len()).sum();
106109
let filtered = all_tests - not_filtered;
107110

111+
warn_if_available_gas_used_with_incompatible_scarb_version(&test_crates)?;
112+
108113
pretty_printing::print_collected_tests_count(
109114
test_crates.iter().map(|tests| tests.test_cases.len()).sum(),
110115
package_name,
@@ -164,6 +169,25 @@ pub async fn run(
164169
Ok(summaries)
165170
}
166171

172+
fn warn_if_available_gas_used_with_incompatible_scarb_version(
173+
test_crates: &Vec<CompiledTestCrateRaw>,
174+
) -> Result<()> {
175+
for test_crate in test_crates {
176+
for case in &test_crate.test_cases {
177+
if case.available_gas == Some(0)
178+
&& scarb_version_command()?.scarb <= Version::new(2, 4, 3)
179+
{
180+
print_warning(&anyhow!(
181+
"`available_gas` attribute was probably specified when using Scarb ~2.4.3 \
182+
Make sure to use Scarb >=2.4.4"
183+
));
184+
}
185+
}
186+
}
187+
188+
Ok(())
189+
}
190+
167191
#[cfg(test)]
168192
mod tests {
169193
use super::*;

crates/forge/tests/data/available_gas/Scarb.toml

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)