From 65d328a40385f1f51b1296a6818137fd74b93a45 Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Tue, 24 Nov 2020 01:58:58 +0400 Subject: [PATCH 1/4] feat: accept regex in revert context manager --- brownie/test/managers/runner.py | 47 ++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/brownie/test/managers/runner.py b/brownie/test/managers/runner.py index 706b5c691..4e305748d 100644 --- a/brownie/test/managers/runner.py +++ b/brownie/test/managers/runner.py @@ -2,6 +2,7 @@ import builtins import json +import re import sys import warnings from pathlib import Path @@ -47,9 +48,23 @@ def revert_deprecation(revert_msg=None): class RevertContextManager: - def __init__(self, revert_msg=None, dev_revert_msg=None): + def __init__( + self, revert_msg=None, dev_revert_msg=None, revert_pattern=None, dev_revert_pattern=None + ): + if revert_msg is not None and revert_pattern is not None: + raise ValueError("Can only use one of`revert_msg` and `revert_pattern`") + if dev_revert_msg is not None and dev_revert_pattern is not None: + raise ValueError("Can only use one of `dev_revert_msg` and `dev_revert_pattern`") + + if revert_pattern: + re.compile(revert_pattern) + if dev_revert_pattern: + re.compile(dev_revert_pattern) + self.revert_msg = revert_msg self.dev_revert_msg = dev_revert_msg + self.revert_pattern = revert_pattern + self.dev_revert_pattern = dev_revert_pattern self.always_transact = CONFIG.argv["always_transact"] if revert_msg is not None and (revert_msg.startswith("dev:") or dev_revert_msg): @@ -68,15 +83,27 @@ def __exit__(self, exc_type, exc_value, traceback): if exc_type is not VirtualMachineError: raise - if self.dev_revert_msg is not None and self.dev_revert_msg != exc_value.dev_revert_msg: - raise AssertionError( - f"Unexpected dev revert string '{exc_value.dev_revert_msg}'\n{exc_value.source}" - ) from None - - if self.revert_msg is not None and self.revert_msg != exc_value.revert_msg: - raise AssertionError( - f"Unexpected revert string '{exc_value.revert_msg}'\n{exc_value.source}" - ) from None + if self.dev_revert_msg or self.dev_revert_pattern: + actual = exc_value.dev_revert_msg + if ( + actual is None + or (self.dev_revert_pattern and not re.fullmatch(self.dev_revert_pattern, actual)) + or (self.dev_revert_msg and self.dev_revert_msg != actual) + ): + raise AssertionError( + f"Unexpected dev revert string '{actual}'\n{exc_value.source}" + ) from None + + if self.revert_msg or self.revert_pattern: + actual = exc_value.revert_msg + if ( + actual is None + or (self.revert_pattern and not re.fullmatch(self.revert_pattern, actual)) + or (self.revert_msg and self.revert_msg != actual) + ): + raise AssertionError( + f"Unexpected revert string '{actual}'\n{exc_value.source}" + ) from None return True From 48197881dcbd6a5a6fca2830df5ddf623cc6a42f Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Tue, 24 Nov 2020 03:24:11 +0400 Subject: [PATCH 2/4] docs: regex in revert context manager --- docs/api-test.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/api-test.rst b/docs/api-test.rst index 4cdbca5d1..1e39db15b 100644 --- a/docs/api-test.rst +++ b/docs/api-test.rst @@ -130,14 +130,16 @@ One of these classes is instantiated in the ``pytest_configure`` method of ``bro RevertContextManager -------------------- -The ``RevertContextManager`` closely mimics the behaviour of :func:`pytest.raises `. +The ``RevertContextManager`` behaves similarly to :func:`pytest.raises `. -.. py:class:: brownie.test.plugin.RevertContextManager(revert_msg=None, dev_revert_msg=None) +.. py:class:: brownie.test.plugin.RevertContextManager(revert_msg=None, dev_revert_msg=None, revert_pattern=None, dev_revert_pattern=None) Context manager used to handle :func:`VirtualMachineError ` exceptions. Raises ``AssertionError`` if no transaction has reverted when the context closes. * ``revert_msg``: Optional. Raises if the transaction does not revert with this error string. * ``dev_revert_msg``: Optional. Raises if the transaction does not revert with this :ref:`dev revert string`. + * ``revert_pattern``: Regex pattern to compare against the transaction error string. Raises if the error string does not fully match the regex (partial matches are not allowed). + * ``dev_revert_pattern``: Regex pattern to compare against the transaction dev revert string. This class is available as ``brownie.reverts`` when ``pytest`` is active. From ac29fc1f576a1d9746c8ab214a906da75ce0960c Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Tue, 24 Nov 2020 03:17:27 +0400 Subject: [PATCH 3/4] test: regex in revert context manager --- tests/test/test_revert_context_manager.py | 75 +++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/test/test_revert_context_manager.py diff --git a/tests/test/test_revert_context_manager.py b/tests/test/test_revert_context_manager.py new file mode 100644 index 000000000..42a6903d0 --- /dev/null +++ b/tests/test/test_revert_context_manager.py @@ -0,0 +1,75 @@ +import pytest + +from brownie.test.managers.runner import RevertContextManager as reverts + + +def test_no_args(vypertester): + with reverts(): + vypertester.revertStrings(0) + + +def test_revert_msg(vypertester): + with reverts("zero"): + vypertester.revertStrings(0) + + +def test_both(vypertester): + with reverts(revert_msg="two", dev_revert_msg="dev: error"): + vypertester.revertStrings(2) + + +def test_revert_msg_raises_incorrect(vypertester): + with pytest.raises(AssertionError): + with reverts("one"): + vypertester.revertStrings(0) + + +def test_revert_msg_raises_partial(vypertester): + with pytest.raises(AssertionError): + with reverts("ze"): + vypertester.revertStrings(0) + + +def test_revert_pattern(vypertester): + with reverts(revert_pattern="z..o"): + vypertester.revertStrings(0) + + +def test_revert_pattern_raises_incorrect(vypertester): + with pytest.raises(AssertionError): + with reverts(revert_pattern="foo[a-zA-Z]."): + vypertester.revertStrings(0) + + +def test_revert_pattern_raises_partial(vypertester): + with pytest.raises(AssertionError): + with reverts(revert_pattern=".ro"): + vypertester.revertStrings(0) + + +def test_dev_revert(vypertester): + with reverts(dev_revert_msg="dev: error"): + vypertester.revertStrings(2) + + +def test_dev_revert_fails(vypertester): + with pytest.raises(AssertionError): + with reverts(dev_revert_msg="dev: foo"): + vypertester.revertStrings(2) + + +def test_dev_revert_pattern(vypertester): + with reverts(dev_revert_pattern="[a-z]*:\\serror"): + vypertester.revertStrings(2) + + +def test_dev_revert_pattern_raises_incorrect(vypertester): + with pytest.raises(AssertionError): + with reverts(dev_revert_pattern="bleerg[a-zA-Z]."): + vypertester.revertStrings(2) + + +def test_dev_revert_pattern_raises_partial(vypertester): + with pytest.raises(AssertionError): + with reverts(dev_revert_pattern="\\sfoo"): + vypertester.revertStrings(2) From 9968e20a623e55a85bdcc0719d0dc6f7e3efd351 Mon Sep 17 00:00:00 2001 From: Ben Hauser Date: Tue, 24 Nov 2020 22:29:31 +0400 Subject: [PATCH 4/4] ci: update tox deps, more targetted subdirectory for plugin tests --- tox.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tox.ini b/tox.ini index 8156547fc..47ef1a46e 100644 --- a/tox.ini +++ b/tox.ini @@ -12,11 +12,11 @@ passenv = GITHUB_TOKEN WEB3_INFURA_PROJECT_ID deps = - py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: coverage==5.1 - py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest==5.4.3 - py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest-cov==2.9.0 - py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest-mock==3.1.1 - py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest-xdist==1.32.0 + py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: coverage==5.2.1 + py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest==6.0.1 + py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest-cov==2.10.1 + py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest-mock==3.3.1 + py{36,37,38},{pm,plugin}test,evm-{byzantium,petersburg,istanbul,latest}: pytest-xdist==1.34.0 docs-{local,external}: sphinx docs-{local,external}: sphinx_rtd_theme docs-{local,external}: pygments_lexer_solidity @@ -27,7 +27,7 @@ commands = evm-istanbul: python -m pytest tests/ --evm 0.5.13,0.5.17,0.6.3,0.6.9 istanbul 0,10000 evm-latest: python -m pytest tests/ --evm latest byzantium,petersburg,istanbul 0,200,10000 pmtest: python -m pytest tests/ --target pm -n 0 - plugintest: python -m pytest tests/ --target plugin -n 0 + plugintest: python -m pytest tests/test/plugin --target plugin -n 0 docs-local: sphinx-build {posargs:-E} -b html docs dist/docs -n -q --color docs-external: sphinx-build -b linkcheck docs dist/docs -n -q --color