Skip to content

Commit

Permalink
test: add tracing pytester tests (#108)
Browse files Browse the repository at this point in the history
  • Loading branch information
antazoey authored Nov 4, 2022
1 parent 3769443 commit 7691193
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 38 deletions.
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 22.6.0
rev: 22.10.0
hooks:
- id: black
name: black

- repo: https://gitlab.com/pycqa/flake8
rev: 4.0.1
rev: 5.0.4
hooks:
- id: flake8

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.971
rev: v0.982
hooks:
- id: mypy
additional_dependencies: [types-requests]
Expand Down
21 changes: 17 additions & 4 deletions ape_hardhat/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ def _set_web3(self):

# Handle if using PoA Hardhat

def is_likely_poa() -> bool:
def began_poa() -> bool:
try:
block = self.web3.eth.get_block("latest")
block = self.web3.eth.get_block(0)
except ExtraDataLengthError:
return True

Expand All @@ -273,7 +273,7 @@ def is_likely_poa() -> bool:
or len(block.get("extraData", "")) > MAX_EXTRADATA_LENGTH
)

if is_likely_poa():
if began_poa():
self._web3.middleware_onion.inject(geth_poa_middleware, layer=0)

def _start(self):
Expand Down Expand Up @@ -504,7 +504,20 @@ def connect(self):

# Verify that we're connected to a Hardhat node with mainnet-fork mode.
self._upstream_provider.connect()
upstream_genesis_block_hash = self._upstream_provider.get_block(0).hash

try:
upstream_genesis_block_hash = self._upstream_provider.get_block(0).hash
except ExtraDataLengthError as err:
if isinstance(self._upstream_provider, Web3Provider):
logger.error(
f"Upstream provider '{self._upstream_provider.name}' "
f"missing Geth PoA middleware."
)
self._upstream_provider.web3.middleware_onion.inject(geth_poa_middleware, layer=0)
upstream_genesis_block_hash = self._upstream_provider.get_block(0).hash
else:
raise ProviderError(f"Unable to get genesis block: {err}.") from err

self._upstream_provider.disconnect()

if self.get_block(0).hash != upstream_genesis_block_hash:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
requires = ["setuptools>=51.1.1", "wheel", "setuptools_scm[toml]>=5.0"]

[tool.mypy]
exclude = "(build/)|(tests/functional/data)"
exclude = ["build/", "tests/functional/data/"]

[tool.setuptools_scm]
write_to = "ape_hardhat/version.py"
Expand Down
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
"rich", # Needed for trace tests
],
"lint": [
"black>=22.6.0", # auto-formatter and linter
"mypy>=0.971", # Static type analyzer
"flake8>=4.0.1,<5.0", # Style linter
"isort>=5.10.1,<6.0", # Import sorting linter
"black>=22.10.0", # auto-formatter and linter
"mypy>=0.982", # Static type analyzer
"flake8>=5.0.4", # Style linter
"isort>=5.10.1", # Import sorting linter
"types-requests", # NOTE: Needed due to mypy typeshed
],
"doc": [
Expand Down
10 changes: 9 additions & 1 deletion tests/ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,12 @@ hardhat:
block_number: 15776634
goerli:
upstream_provider: alchemy
block_number: 3853585
block_number: 7849922

test:
# `false` because running pytest within pytest.
disconnect_providers_after: false

gas:
exclude:
- method_name: setAdd*
45 changes: 38 additions & 7 deletions tests/data/python/pytest_tests.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,64 @@
def test_provider(project, networks):
# The default gets set in `ape-config.yaml`
"""
Tests that the network gets set from ape-config.yaml.
"""
assert networks.provider.name == "hardhat"


def test_contract_interaction(owner, contract):
"""
Traditional ape-test style test.
"""
contract.setNumber(123, sender=owner)
assert contract.myNumber() == 123


def test_transfer(accounts):
# Useful for seeing transfer gas
"""
Tests that the ReceiptCapture handles transfer transactions.
"""
accounts[0].transfer(accounts[1], "100 gwei")


def test_using_contract_with_same_name(owner, project):
def test_using_contract_with_same_type_and_method_call(owner, project):
"""
Deploy the same contract from the ``contract`` fixture and call a method
that gets called elsewhere in the test suite. This shows that we amass
results across all instances of contract types when making the gas report.
"""
contract = project.TestContractVy.deploy(sender=owner)
contract.setNumber(123, sender=owner)
assert contract.myNumber() == 123


def test_two_contracts_with_same_symbol(owner, accounts, project):
# Tests against scenario when using 2 tokens with same symbol.
# There was almost a bug where the contract IDs clashed.
# This is to help prevent future bugs related to this.

"""
Tests against scenario when using 2 tokens with same symbol.
There was almost a bug where the contract IDs clashed.
This is to help prevent future bugs related to this.
"""
receiver = accounts[1]
token_a = project.TokenA.deploy(sender=owner)
token_b = project.TokenB.deploy(sender=owner)
token_a.transfer(receiver, 5, sender=owner)
token_b.transfer(receiver, 6, sender=owner)
assert token_a.balanceOf(receiver) == 5
assert token_b.balanceOf(receiver) == 6


def test_call_method_excluded_from_cli_options(owner, contract):
"""
Call a method so that we can intentionally ignore it via command
line options and test that it does not show in the report.
"""
receipt = contract.fooAndBar(sender=owner)
assert not receipt.failed


def test_call_method_excluded_from_config(owner, contract):
"""
Call a method excluded in the ``ape-config.yaml`` file
for asserting it does not show in gas report.
"""
receipt = contract.setAddress(owner.address, sender=owner)
assert not receipt.failed
6 changes: 3 additions & 3 deletions tests/expected_traces.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,13 +517,13 @@
\) \[174327 gas\]
"""
LOCAL_GAS_REPORT = r"""
contract_a.json Gas
ContractA Gas
Method Times called Min. Max. Mean Median
───────────────────────────────────────────────────────────────────────────
methodWithoutArguments +1 +\d+ +\d+ +\d+ + \d+
contract_b.json Gas
ContractB Gas
Method Times called Min. Max. Mean Median
─────────────────────────────────────────────────────────────────
Expand All @@ -532,7 +532,7 @@
methodB2 +1 +\d+ +\d+ +\d+ + \d+
bandPractice +1 +\d+ +\d+ +\d+ + \d+
contract_c.json Gas
ContractC Gas
Method Times called Min. Max. Mean Median
───────────────────────────────────────────────────────────────────
Expand Down
65 changes: 50 additions & 15 deletions tests/test_trace_pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,27 @@
CONFTEST = (BASE_DATA_PATH / "pytest_test_conftest.py").read_text()
TEST_FILE = (BASE_DATA_PATH / "pytest_tests.py").read_text()
NUM_TESTS = len([x for x in TEST_FILE.split("\n") if x.startswith("def test_")])
EXPECTED_GAS_REPORT = r"""
vyper_contract.json Gas
Method Times called Min. Max. Mean Median
───────────────────────────────────────────────────────────
setNumber 3 51021 53821 51958 51033
Transferring ETH Gas
Method Times called Min. Max. Mean Median
──────────────────────────────────────────────────────────
to:dev_1 1 21000 21000 21000 21000
token_a.json Gas
TOKEN_B_GAS_REPORT = r"""
TokenB Gas
Method Times called Min. Max. Mean Median
──────────────────────────────────────────────────────────
transfer 1 50911 50911 50911 50911
"""
EXPECTED_GAS_REPORT = rf"""
TestContractVy Gas
Method Times called Min. Max. Mean Median
───────────────────────────────────────────────────────────
setNumber 3 51021 53821 51958 51033
fooAndBar 1 23430 23430 23430 23430
token_b.json Gas
TokenA Gas
Method Times called Min. Max. Mean Median
──────────────────────────────────────────────────────────
transfer 1 50911 50911 50911 50911
{TOKEN_B_GAS_REPORT}
"""


Expand All @@ -38,3 +35,41 @@ def ape_pytester(project, pytester):
pytester.makeconftest(CONFTEST)
pytester.makepyfile(TEST_FILE)
return pytester


def run_trace_test(result, expected_report: str = EXPECTED_GAS_REPORT):
result.assert_outcomes(passed=NUM_TESTS), "\n".join(result.outlines)

gas_header_line_index = None
for index, line in enumerate(result.outlines):
if "Gas Profile" in line:
gas_header_line_index = index

assert gas_header_line_index is not None, "'Gas Profile' not in output."
expected = expected_report.split("\n")[1:]
start_index = gas_header_line_index + 1
end_index = start_index + len(expected)
actual = [x.rstrip() for x in result.outlines[start_index:end_index]]
assert len(actual) == len(expected)
for actual_line, expected_line in zip(actual, expected):
assert actual_line == expected_line


@pytest.mark.sync
def test_gas_flag_in_tests(ape_pytester):
result = ape_pytester.runpytest("--gas")
run_trace_test(result)


@pytest.mark.sync
def test_gas_flag_exclude_method_using_cli_option(ape_pytester):
line = "\n fooAndBar 1 23430 23430 23430 23430"
expected = EXPECTED_GAS_REPORT.replace(line, "")
result = ape_pytester.runpytest("--gas", "--gas-exclude", "*:fooAndBar")
run_trace_test(result, expected_report=expected)


@pytest.mark.sync
def test_gas_flag_excluding_contracts(ape_pytester):
result = ape_pytester.runpytest("--gas", "--gas-exclude", "TestContractVy,TokenA")
run_trace_test(result, expected_report=TOKEN_B_GAS_REPORT)

0 comments on commit 7691193

Please sign in to comment.