Skip to content

Commit c76eb53

Browse files
authored
Display gas report table (#3842)
Towards #3660
1 parent b01acb9 commit c76eb53

File tree

7 files changed

+271
-8
lines changed

7 files changed

+271
-8
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,4 @@ tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
130130
tracing = "0.1.41"
131131
tracing-chrome = "0.7.2"
132132
tracing-log = "0.2.0"
133+
comfy-table = "7"

crates/forge-runner/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ strum = "0.27"
4949
strum_macros = "0.27"
5050
scarb-oracle-hint-service.workspace = true
5151
tracing.workspace = true
52+
comfy-table.workspace = true

crates/forge-runner/src/gas/report.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
use crate::gas::stats::GasStats;
22
use cheatnet::trace_data::{CallTrace, CallTraceNode};
3+
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
4+
use comfy_table::{Attribute, Cell, Color, Table};
35
use debugging::ContractsDataStore;
46
use starknet_api::core::{ClassHash, EntryPointSelector};
57
use starknet_api::execution_resources::GasVector;
68
use std::collections::BTreeMap;
9+
use std::fmt;
10+
use std::fmt::Display;
711

812
type ContractName = String;
913
type Selector = String;
@@ -103,6 +107,23 @@ impl ReportData {
103107
}
104108
}
105109

110+
impl Display for ReportData {
111+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112+
if self.0.is_empty() {
113+
writeln!(
114+
f,
115+
"\nNo contract gas usage data to display, no contract calls made."
116+
)?;
117+
}
118+
119+
for (name, contract_info) in &self.0 {
120+
let table = format_table_output(contract_info, name);
121+
writeln!(f, "\n{table}")?;
122+
}
123+
Ok(())
124+
}
125+
}
126+
106127
fn get_contract_name(contracts_data: &ContractsDataStore, class_hash: ClassHash) -> ContractName {
107128
contracts_data
108129
.get_contract_name(&class_hash)
@@ -117,3 +138,36 @@ fn get_selector(contracts_data: &ContractsDataStore, selector: EntryPointSelecto
117138
.0
118139
.clone()
119140
}
141+
142+
pub fn format_table_output(contract_info: &ContractInfo, name: &ContractName) -> Table {
143+
let mut table = Table::new();
144+
table.apply_modifier(UTF8_ROUND_CORNERS);
145+
146+
table.set_header(vec![
147+
Cell::new(format!("{name} Contract")).fg(Color::Magenta),
148+
]);
149+
table.add_row(vec![
150+
Cell::new("Function Name").add_attribute(Attribute::Bold),
151+
Cell::new("Min").add_attribute(Attribute::Bold),
152+
Cell::new("Max").add_attribute(Attribute::Bold),
153+
Cell::new("Avg").add_attribute(Attribute::Bold),
154+
Cell::new("Std Dev").add_attribute(Attribute::Bold),
155+
Cell::new("# Calls").add_attribute(Attribute::Bold),
156+
]);
157+
158+
contract_info
159+
.functions
160+
.iter()
161+
.for_each(|(selector, report_data)| {
162+
table.add_row(vec![
163+
Cell::new(selector),
164+
Cell::new(report_data.gas_stats.min.to_string()),
165+
Cell::new(report_data.gas_stats.max.to_string()),
166+
Cell::new(report_data.gas_stats.mean.round().to_string()),
167+
Cell::new(report_data.gas_stats.std_deviation.round().to_string()),
168+
Cell::new(report_data.n_calls.to_string()),
169+
]);
170+
});
171+
172+
table
173+
}

crates/forge-runner/src/messages.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub struct TestResultMessage {
3939
fuzzer_report: String,
4040
gas_usage: String,
4141
used_resources: String,
42+
gas_report: String,
4243
}
4344

4445
impl TestResultMessage {
@@ -75,16 +76,25 @@ impl TestResultMessage {
7576
String::new()
7677
};
7778

78-
let gas_usage = match test_result {
79+
let (gas_usage, gas_report) = match test_result {
7980
AnyTestCaseSummary::Single(TestCaseSummary::Passed { gas_info, .. }) => {
80-
format!(
81-
" (l1_gas: ~{}, l1_data_gas: ~{}, l2_gas: ~{})",
82-
gas_info.gas_used.l1_gas,
83-
gas_info.gas_used.l1_data_gas,
84-
gas_info.gas_used.l2_gas
81+
let gas_report = gas_info
82+
.report_data
83+
.as_ref()
84+
.map(std::string::ToString::to_string)
85+
.unwrap_or_default();
86+
87+
(
88+
format!(
89+
" (l1_gas: ~{}, l1_data_gas: ~{}, l2_gas: ~{})",
90+
gas_info.gas_used.l1_gas,
91+
gas_info.gas_used.l1_data_gas,
92+
gas_info.gas_used.l2_gas
93+
),
94+
gas_report,
8595
)
8696
}
87-
_ => String::new(),
97+
_ => (String::new(), String::new()),
8898
};
8999

90100
let used_resources = match (show_detailed_resources, &test_result) {
@@ -104,6 +114,7 @@ impl TestResultMessage {
104114
fuzzer_report,
105115
gas_usage,
106116
used_resources,
117+
gas_report,
107118
}
108119
}
109120

@@ -140,10 +151,11 @@ impl Message for TestResultMessage {
140151

141152
let fuzzer_report = &self.fuzzer_report;
142153
let gas_usage = &self.gas_usage;
154+
let gas_report = &self.gas_report;
143155
let used_resources = &self.used_resources;
144156

145157
format!(
146-
"{result_header} {result_name}{fuzzer_report}{gas_usage}{used_resources}{result_msg}{result_debug_trace}"
158+
"{result_header} {result_name}{fuzzer_report}{gas_usage}{used_resources}{result_msg}{result_debug_trace}{gas_report}"
147159
)
148160
}
149161

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
use crate::e2e::common::runner::{setup_package, test_runner};
2+
use indoc::indoc;
3+
use shared::test_utils::output_assert::assert_stdout_contains;
4+
5+
#[test]
6+
fn basic() {
7+
let temp = setup_package("simple_package");
8+
let output = test_runner(&temp)
9+
.arg("--gas-report")
10+
.arg("call_and_invoke")
11+
.assert()
12+
.code(0);
13+
14+
assert_stdout_contains(
15+
output,
16+
indoc! {r"
17+
[..]Compiling[..]
18+
[..]Finished[..]
19+
20+
21+
Collected 1 test(s) from simple_package package
22+
Running 0 test(s) from src/
23+
Running 1 test(s) from tests/
24+
[PASS] simple_package_integrationtest::contract::call_and_invoke (l1_gas: ~0, l1_data_gas: ~[..], l2_gas: ~[..])
25+
╭------------------------+-------+-------+-------+---------+---------╮
26+
| HelloStarknet Contract | | | | | |
27+
+====================================================================+
28+
| Function Name | Min | Max | Avg | Std Dev | # Calls |
29+
|------------------------+-------+-------+-------+---------+---------|
30+
| get_balance | 13340 | 13340 | 13340 | 0 | 2 |
31+
|------------------------+-------+-------+-------+---------+---------|
32+
| increase_balance | 24940 | 24940 | 24940 | 0 | 1 |
33+
╰------------------------+-------+-------+-------+---------+---------╯
34+
35+
Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out
36+
"},
37+
);
38+
}
39+
40+
#[test]
41+
fn recursive_calls() {
42+
let temp = setup_package("debugging");
43+
44+
let output = test_runner(&temp)
45+
.arg("test_debugging_trace_success")
46+
.arg("--gas-report")
47+
.assert()
48+
.code(0);
49+
50+
assert_stdout_contains(
51+
output,
52+
indoc! {r"
53+
[..]Compiling[..]
54+
[..]Finished[..]
55+
56+
57+
Collected 1 test(s) from debugging package
58+
Running 0 test(s) from src/
59+
Running 1 test(s) from tests/
60+
[PASS] debugging_integrationtest::test_trace::test_debugging_trace_success (l1_gas: ~0, l1_data_gas: ~[..], l2_gas: ~[..])
61+
╭-------------------------+-------+--------+--------+---------+---------╮
62+
| SimpleContract Contract | | | | | |
63+
+=======================================================================+
64+
| Function Name | Min | Max | Avg | Std Dev | # Calls |
65+
|-------------------------+-------+--------+--------+---------+---------|
66+
| execute_calls | 11680 | 609080 | 183924 | 235859 | 5 |
67+
|-------------------------+-------+--------+--------+---------+---------|
68+
| fail | 17950 | 17950 | 17950 | 0 | 1 |
69+
╰-------------------------+-------+--------+--------+---------+---------╯
70+
71+
Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out
72+
"},
73+
);
74+
}
75+
76+
#[test]
77+
fn multiple_contracts_and_constructor() {
78+
let temp = setup_package("simple_package_with_cheats");
79+
let output = test_runner(&temp)
80+
.arg("--gas-report")
81+
.arg("call_and_invoke_proxy")
82+
.assert()
83+
.code(0);
84+
85+
assert_stdout_contains(
86+
output,
87+
indoc! {r"
88+
[..]Compiling[..]
89+
[..]Finished[..]
90+
91+
92+
Collected 1 test(s) from simple_package_with_cheats package
93+
Running 0 test(s) from src/
94+
Running 1 test(s) from tests/
95+
[PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy (l1_gas: ~0, l1_data_gas: ~[..], l2_gas: ~[..])
96+
╭------------------------+-------+-------+-------+---------+---------╮
97+
| HelloStarknet Contract | | | | | |
98+
+====================================================================+
99+
| Function Name | Min | Max | Avg | Std Dev | # Calls |
100+
|------------------------+-------+-------+-------+---------+---------|
101+
| get_block_number | 15780 | 15780 | 15780 | 0 | 2 |
102+
╰------------------------+-------+-------+-------+---------+---------╯
103+
104+
╭-----------------------------+--------+--------+--------+---------+---------╮
105+
| HelloStarknetProxy Contract | | | | | |
106+
+============================================================================+
107+
| Function Name | Min | Max | Avg | Std Dev | # Calls |
108+
|-----------------------------+--------+--------+--------+---------+---------|
109+
| constructor | 14650 | 14650 | 14650 | 0 | 1 |
110+
|-----------------------------+--------+--------+--------+---------+---------|
111+
| get_block_number | 125280 | 125280 | 125280 | 0 | 2 |
112+
╰-----------------------------+--------+--------+--------+---------+---------╯
113+
114+
Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out
115+
"},
116+
);
117+
}
118+
119+
#[test]
120+
fn no_transactions() {
121+
let temp = setup_package("simple_package");
122+
let output = test_runner(&temp)
123+
.arg("--gas-report")
124+
.arg("test_fib")
125+
.assert()
126+
.code(0);
127+
128+
assert_stdout_contains(
129+
output,
130+
indoc! {r"
131+
[..]Compiling[..]
132+
[..]Finished[..]
133+
134+
135+
Collected 1 test(s) from simple_package package
136+
Running 1 test(s) from src/
137+
[PASS] simple_package::tests::test_fib (l1_gas: ~0, l1_data_gas: ~0, l2_gas: ~[..])
138+
No contract gas usage data to display, no contract calls made.
139+
140+
Running 0 test(s) from tests/
141+
Tests: 1 passed, 0 failed, 0 ignored, [..] filtered out
142+
"},
143+
);
144+
}

crates/forge/tests/e2e/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod features;
2222
mod fork_warning;
2323
mod forking;
2424
mod fuzzing;
25+
mod gas_report;
2526
mod io_operations;
2627
mod new;
2728
mod package_warnings;

0 commit comments

Comments
 (0)