Skip to content

Commit 5f81bb2

Browse files
committed
prepare_report.py: Print some statistics about contracts and errors
1 parent 9e02d6b commit 5f81bb2

File tree

2 files changed

+122
-36
lines changed

2 files changed

+122
-36
lines changed

scripts/bytecodecompare/prepare_report.py

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,33 @@ def format_summary(self, verbose: bool) -> str:
8383
return '.'
8484

8585

86+
@dataclass
87+
class Statistics:
88+
file_count: int = 0
89+
contract_count: int = 0
90+
error_count: int = 0
91+
missing_bytecode_count: int = 0
92+
missing_metadata_count: int = 0
93+
94+
def aggregate(self, report: FileReport):
95+
contract_reports = report.contract_reports if report.contract_reports is not None else []
96+
97+
self.file_count += 1
98+
self.contract_count += len(contract_reports)
99+
self.error_count += (1 if report.contract_reports is None else 0)
100+
self.missing_bytecode_count += sum(1 for c in contract_reports if c.bytecode is None)
101+
self.missing_metadata_count += sum(1 for c in contract_reports if c.metadata is None)
102+
103+
def __str__(self) -> str:
104+
return "test cases: {}, contracts: {}, errors: {}, missing bytecode: {}, missing metadata: {}".format(
105+
self.file_count,
106+
str(self.contract_count) + ('+' if self.error_count > 0 else ''),
107+
self.error_count,
108+
self.missing_bytecode_count,
109+
self.missing_metadata_count,
110+
)
111+
112+
86113
def load_source(path: Union[Path, str], smt_use: SMTUse) -> str:
87114
# NOTE: newline='' disables newline conversion.
88115
# We want the file exactly as is because changing even a single byte in the source affects metadata.
@@ -292,7 +319,7 @@ def run_compiler( # pylint: disable=too-many-arguments
292319
return parse_cli_output(Path(source_file_name), process.stdout)
293320

294321

295-
def generate_report( # pylint: disable=too-many-arguments
322+
def generate_report( # pylint: disable=too-many-arguments,too-many-locals
296323
source_file_names: List[str],
297324
compiler_path: Path,
298325
interface: CompilerInterface,
@@ -302,42 +329,49 @@ def generate_report( # pylint: disable=too-many-arguments
302329
verbose: bool,
303330
exit_on_error: bool,
304331
):
332+
statistics = Statistics()
305333
metadata_option_supported = detect_metadata_cli_option_support(compiler_path)
306334

307-
with open(report_file_path, mode='w', encoding='utf8', newline='\n') as report_file:
308-
for optimize in [False, True]:
309-
with TemporaryDirectory(prefix='prepare_report-') as tmp_dir:
310-
for source_file_name in sorted(source_file_names):
311-
try:
312-
report = run_compiler(
313-
compiler_path,
314-
Path(source_file_name),
315-
optimize,
316-
force_no_optimize_yul,
317-
interface,
318-
smt_use,
319-
metadata_option_supported,
320-
Path(tmp_dir),
321-
exit_on_error,
322-
)
323-
print(report.format_summary(verbose), end=('\n' if verbose else ''), flush=True)
324-
report_file.write(report.format_report())
325-
except subprocess.CalledProcessError as exception:
326-
print(
327-
f"\n\nInterrupted by an exception while processing file "
328-
f"'{source_file_name}' with optimize={optimize}\n\n"
329-
f"COMPILER STDOUT:\n{exception.stdout}\n"
330-
f"COMPILER STDERR:\n{exception.stderr}\n",
331-
file=sys.stderr
332-
)
333-
raise
334-
except:
335-
print(
336-
f"\n\nInterrupted by an exception while processing file "
337-
f"'{source_file_name}' with optimize={optimize}\n",
338-
file=sys.stderr
339-
)
340-
raise
335+
try:
336+
with open(report_file_path, mode='w', encoding='utf8', newline='\n') as report_file:
337+
for optimize in [False, True]:
338+
with TemporaryDirectory(prefix='prepare_report-') as tmp_dir:
339+
for source_file_name in sorted(source_file_names):
340+
try:
341+
report = run_compiler(
342+
compiler_path,
343+
Path(source_file_name),
344+
optimize,
345+
force_no_optimize_yul,
346+
interface,
347+
smt_use,
348+
metadata_option_supported,
349+
Path(tmp_dir),
350+
exit_on_error,
351+
)
352+
353+
statistics.aggregate(report)
354+
print(report.format_summary(verbose), end=('\n' if verbose else ''), flush=True)
355+
356+
report_file.write(report.format_report())
357+
except subprocess.CalledProcessError as exception:
358+
print(
359+
f"\n\nInterrupted by an exception while processing file "
360+
f"'{source_file_name}' with optimize={optimize}\n\n"
361+
f"COMPILER STDOUT:\n{exception.stdout}\n"
362+
f"COMPILER STDERR:\n{exception.stderr}\n",
363+
file=sys.stderr
364+
)
365+
raise
366+
except:
367+
print(
368+
f"\n\nInterrupted by an exception while processing file "
369+
f"'{source_file_name}' with optimize={optimize}\n",
370+
file=sys.stderr
371+
)
372+
raise
373+
finally:
374+
print('\n', statistics, '\n', sep='')
341375

342376

343377
def commandline_parser() -> ArgumentParser:

test/scripts/test_bytecodecompare_prepare_report.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports
1111
# pragma pylint: disable=import-error
12-
from bytecodecompare.prepare_report import CompilerInterface, FileReport, ContractReport, SMTUse
12+
from bytecodecompare.prepare_report import CompilerInterface, FileReport, ContractReport, SMTUse, Statistics
1313
from bytecodecompare.prepare_report import load_source, parse_cli_output, parse_standard_json_output, prepare_compiler_input
1414
# pragma pylint: enable=import-error
1515

@@ -99,6 +99,58 @@ def test_format_report_should_not_print_anything_if_contract_report_list_is_empt
9999

100100
self.assertEqual(report.format_report(), '')
101101

102+
class TestPrepareReport_Statistics(unittest.TestCase):
103+
def test_initialization(self):
104+
self.assertEqual(Statistics(), Statistics(0, 0, 0, 0, 0))
105+
106+
def test_aggregate_bytecode_and_metadata_present(self):
107+
statistics = Statistics()
108+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=[ContractReport('C', 'c.sol', 'B', 'M')]))
109+
self.assertEqual(statistics, Statistics(1, 1, 0, 0, 0))
110+
111+
def test_aggregate_bytecode_missing(self):
112+
statistics = Statistics()
113+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=[ContractReport('C', 'c.sol', None, 'M')]))
114+
self.assertEqual(statistics, Statistics(1, 1, 0, 1, 0))
115+
116+
def test_aggregate_metadata_missing(self):
117+
statistics = Statistics()
118+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=[ContractReport('C', 'c.sol', 'B', None)]))
119+
self.assertEqual(statistics, Statistics(1, 1, 0, 0, 1))
120+
121+
def test_aggregate_no_contract_reports(self):
122+
statistics = Statistics()
123+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=[]))
124+
self.assertEqual(statistics, Statistics(1, 0, 0, 0, 0))
125+
126+
def test_aggregate_missing_contract_report_list(self):
127+
statistics = Statistics()
128+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=None))
129+
self.assertEqual(statistics, Statistics(1, 0, 1, 0, 0))
130+
131+
def test_aggregate_multiple_contract_reports(self):
132+
statistics = Statistics()
133+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=[
134+
ContractReport('C', 'c.sol', 'B', 'M'),
135+
ContractReport('C', 'c.sol', None, 'M'),
136+
ContractReport('C', 'c.sol', 'B', None),
137+
ContractReport('C', 'c.sol', None, None),
138+
]))
139+
self.assertEqual(statistics, Statistics(1, 4, 0, 2, 2))
140+
141+
def test_str(self):
142+
statistics = Statistics()
143+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=[
144+
ContractReport('C', 'c.sol', 'B', 'M'),
145+
ContractReport('C', 'c.sol', None, 'M'),
146+
ContractReport('C', 'c.sol', 'B', None),
147+
ContractReport('C', 'c.sol', None, None),
148+
]))
149+
statistics.aggregate(FileReport(file_name=Path('F'), contract_reports=None))
150+
151+
self.assertEqual(statistics, Statistics(2, 4, 1, 2, 2))
152+
self.assertEqual(str(statistics), "test cases: 2, contracts: 4+, errors: 1, missing bytecode: 2, missing metadata: 2")
153+
102154

103155
class TestLoadSource(PrepareReportTestBase):
104156
def test_load_source_should_strip_smt_pragmas_if_requested(self):

0 commit comments

Comments
 (0)