diff --git a/CHANGELOG.md b/CHANGELOG.md index 0914f1f9f..482f384ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/iamdefinitelyahuman/brownie) ### Changed - Allow connections to `wss://` endpoints ([#542](https://github.com/iamdefinitelyahuman/brownie/pull/542)) +- Improved `--gas` report ([#543](https://github.com/iamdefinitelyahuman/brownie/pull/543)) + ## [1.8.6](https://github.com/iamdefinitelyahuman/brownie/tree/v1.8.6) - 2020-05-19 ### Added diff --git a/brownie/test/output.py b/brownie/test/output.py index 9194eeb62..6926fa73d 100644 --- a/brownie/test/output.py +++ b/brownie/test/output.py @@ -32,11 +32,37 @@ def _print_gas_profile(): # Formats and prints a gas profile report to the console print("\n\nGas Profile:") gas = TxHistory().gas_profile - sorted_gas = sorted(gas.items(), key=lambda value: value[1]["avg"], reverse=True) - for fn_name, values in sorted_gas: - print( - f"{fn_name} - avg: {values['avg']:.0f} low: {values['low']} high: {values['high']}" + sorted_gas = sorted(gas.items()) + grouped_by_contract = {} + padding = {} + for full_name, values in sorted_gas: + contract, function = full_name.split(".", 1) + + # calculate padding to get table-like formatting + padding["fn"] = max(padding.get("fn", 0), len(str(function))) + for k, v in values.items(): + padding[k] = max(padding.get(k, 0), len(str(v))) + + # group functions with payload by contract name + if contract in grouped_by_contract.keys(): + grouped_by_contract[contract][function] = values + else: + grouped_by_contract[contract] = {function: values} + + for contract, functions in grouped_by_contract.items(): + print(f"{color('bright magenta')}{contract}{color} ") + sorted_functions = dict( + sorted(functions.items(), key=lambda value: value[1]["avg"], reverse=True) ) + for ix, (fn_name, values) in enumerate(sorted_functions.items()): + prefix = "\u2514\u2500" if ix == len(functions) - 1 else "\u251c\u2500" + fn_name = fn_name.ljust(padding["fn"]) + values["avg"] = int(values["avg"]) + values = {k: str(v).rjust(padding[k]) for k, v in values.items()} + print( + f" {prefix} {fn_name} - avg: {values['avg']}" + f" low: {values['low']} high: {values['high']}" + ) def _print_coverage_totals(build, coverage_eval): diff --git a/docs/api-test.rst b/docs/api-test.rst index 48b536bd8..eab569b2e 100644 --- a/docs/api-test.rst +++ b/docs/api-test.rst @@ -181,7 +181,7 @@ Internal Methods .. py:method:: output._print_gas_profile() - Formats and prints a gas profile report. Report is sorted by gas amount used. + Formats and prints a gas profile report. The report is grouped by contracts and functions are sorted by average gas used. .. py:method:: output._print_coverage_totals(build, coverage_eval) diff --git a/docs/tests-pytest-intro.rst b/docs/tests-pytest-intro.rst index 6bddafc0f..bad2eac91 100644 --- a/docs/tests-pytest-intro.rst +++ b/docs/tests-pytest-intro.rst @@ -379,6 +379,33 @@ Once you are finished, type ``quit()`` to continue with the next test. See :ref:`Inspecting and Debugging Transactions ` for more information on Brownie's debugging functionality. + + +Evaluating Gas Usage +-------------------- + +To generate a gas profile report, add the ``--gas`` flag: + +:: + + $ brownie test --gas + +When the tests complete, a report will display: + +:: + + Gas Profile: + Token + ├─ constructor - avg: 1099591 low: 1099591 high: 1099591 + ├─ transfer - avg: 43017 low: 43017 high: 43017 + └─ approve - avg: 21437 low: 21437 high: 21437 + Storage + ├─ constructor - avg: 211445 low: 211445 high: 211445 + └─ set - avg: 21658 low: 21658 high: 21658 + + + + Evaluating Coverage ------------------- diff --git a/tests/test/plugin/test_output.py b/tests/test/plugin/test_output.py index 8d14318a3..57293e0be 100644 --- a/tests/test/plugin/test_output.py +++ b/tests/test/plugin/test_output.py @@ -5,10 +5,14 @@ from brownie.test import output test_source = """ -def test_stuff(BrownieTester, accounts): +def test_stuff(BrownieTester, EVMTester, accounts): c = accounts[0].deploy(BrownieTester, True) print('oh hai', 'mark') - c.doNothing({'from': accounts[0]})""" + c.doNothing({'from': accounts[0]}) + c.sendEth({'from': accounts[0]}) + d = accounts[0].deploy(EVMTester) + d.modulusByZero(5, 1, {'from': accounts[0]}) + """ def test_print_gas(plugintester, mocker):