diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fd78e2fff8..8d23368eb0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -104,7 +104,7 @@ jobs: run: pip install tox - name: Run Tox - run: TOXENV=py${{ matrix.python-version[1] }} tox -r -- --optimize ${{ matrix.opt-mode }} ${{ matrix.debug && '--enable-compiler-debug-mode' || '' }} --reruns 10 --reruns-delay 1 -r aR tests/ + run: TOXENV=py${{ matrix.python-version[1] }} tox -r -- --optimize ${{ matrix.opt-mode }} ${{ matrix.debug && '--enable-compiler-debug-mode' || '' }} -r aR tests/ - name: Upload Coverage uses: codecov/codecov-action@v1 @@ -148,12 +148,12 @@ jobs: # fetch test durations # NOTE: if the tests get poorly distributed, run this and commit the resulting `.test_durations` file to the `vyper-test-durations` repo. - # `TOXENV=fuzzing tox -r -- --store-durations --reruns 10 --reruns-delay 1 -r aR tests/` + # `TOXENV=fuzzing tox -r -- --store-durations -r aR tests/` - name: Fetch test-durations run: curl --location "https://raw.githubusercontent.com/vyperlang/vyper-test-durations/5982755ee8459f771f2e8622427c36494646e1dd/test_durations" -o .test_durations - name: Run Tox - run: TOXENV=fuzzing tox -r -- --splits 60 --group ${{ matrix.group }} --splitting-algorithm least_duration --reruns 10 --reruns-delay 1 -r aR tests/ + run: TOXENV=fuzzing tox -r -- --splits 60 --group ${{ matrix.group }} --splitting-algorithm least_duration -r aR tests/ - name: Upload Coverage uses: codecov/codecov-action@v1 diff --git a/docs/control-structures.rst b/docs/control-structures.rst index fc8a472ff6..873135709a 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -271,16 +271,25 @@ Ranges are created using the ``range`` function. The following examples are vali ``STOP`` is a literal integer greater than zero. ``i`` begins as zero and increments by one until it is equal to ``STOP``. +.. code-block:: python + + for i in range(stop, bound=N): + ... + +Here, ``stop`` can be a variable with integer type, greater than zero. ``N`` must be a compile-time constant. ``i`` begins as zero and increments by one until it is equal to ``stop``. If ``stop`` is larger than ``N``, execution will revert at runtime. In certain cases, you may not have a guarantee that ``stop`` is less than ``N``, but still want to avoid the possibility of runtime reversion. To accomplish this, use the ``bound=`` keyword in combination with ``min(stop, N)`` as the argument to ``range``, like ``range(min(stop, N), bound=N)``. This is helpful for use cases like chunking up operations on larger arrays across multiple transactions. + +Another use of range can be with ``START`` and ``STOP`` bounds. + .. code-block:: python for i in range(START, STOP): ... -``START`` and ``STOP`` are literal integers, with ``STOP`` being a greater value than ``START``. ``i`` begins as ``START`` and increments by one until it is equal to ``STOP``. +Here, ``START`` and ``STOP`` are literal integers, with ``STOP`` being a greater value than ``START``. ``i`` begins as ``START`` and increments by one until it is equal to ``STOP``. .. code-block:: python for i in range(a, a + N): ... -``a`` is a variable with an integer type and ``N`` is a literal integer greater than zero. ``i`` begins as ``a`` and increments by one until it is equal to ``a + N``. +``a`` is a variable with an integer type and ``N`` is a literal integer greater than zero. ``i`` begins as ``a`` and increments by one until it is equal to ``a + N``. If ``a + N`` would overflow, execution will revert. diff --git a/examples/tokens/ERC1155ownable.vy b/examples/tokens/ERC1155ownable.vy index 8094225f18..f1070b8f89 100644 --- a/examples/tokens/ERC1155ownable.vy +++ b/examples/tokens/ERC1155ownable.vy @@ -214,7 +214,6 @@ def mint(receiver: address, id: uint256, amount:uint256): @param receiver the account that will receive the minted token @param id the ID of the token @param amount of tokens for this ID - @param data the data associated with this mint. Usually stays empty """ assert not self.paused, "The contract has been paused" assert self.owner == msg.sender, "Only the contract owner can mint" @@ -232,7 +231,6 @@ def mintBatch(receiver: address, ids: DynArray[uint256, BATCH_SIZE], amounts: Dy @param receiver the account that will receive the minted token @param ids array of ids for the tokens @param amounts amounts of tokens for each ID in the ids array - @param data the data associated with this mint. Usually stays empty """ assert not self.paused, "The contract has been paused" assert self.owner == msg.sender, "Only the contract owner can mint" diff --git a/tests/ast/nodes/test_evaluate_binop_decimal.py b/tests/ast/nodes/test_evaluate_binop_decimal.py index 3c8ba0888c..5c9956caba 100644 --- a/tests/ast/nodes/test_evaluate_binop_decimal.py +++ b/tests/ast/nodes/test_evaluate_binop_decimal.py @@ -13,7 +13,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=None) +@settings(max_examples=50) @given(left=st_decimals, right=st_decimals) @example(left=Decimal("0.9999999999"), right=Decimal("0.0000000001")) @example(left=Decimal("0.0000000001"), right=Decimal("0.9999999999")) @@ -52,7 +52,7 @@ def test_binop_pow(): @pytest.mark.fuzzing -@settings(max_examples=50, deadline=None) +@settings(max_examples=50) @given( values=st.lists(st_decimals, min_size=2, max_size=10), ops=st.lists(st.sampled_from("+-*/%"), min_size=11, max_size=11), diff --git a/tests/ast/nodes/test_evaluate_binop_int.py b/tests/ast/nodes/test_evaluate_binop_int.py index d632a95461..80c9381c0f 100644 --- a/tests/ast/nodes/test_evaluate_binop_int.py +++ b/tests/ast/nodes/test_evaluate_binop_int.py @@ -9,7 +9,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st_int32, right=st_int32) @example(left=1, right=1) @example(left=1, right=-1) @@ -42,7 +42,7 @@ def foo(a: int128, b: int128) -> int128: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st_uint64, right=st_uint64) @pytest.mark.parametrize("op", "+-*/%") def test_binop_uint256(get_contract, assert_tx_failed, op, left, right): @@ -69,7 +69,7 @@ def foo(a: uint256, b: uint256) -> uint256: @pytest.mark.xfail(reason="need to implement safe exponentiation logic") @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st.integers(min_value=2, max_value=245), right=st.integers(min_value=0, max_value=16)) @example(left=0, right=0) @example(left=0, right=1) @@ -89,7 +89,7 @@ def foo(a: uint256, b: uint256) -> uint256: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given( values=st.lists(st.integers(min_value=-256, max_value=256), min_size=2, max_size=10), ops=st.lists(st.sampled_from("+-*/%"), min_size=11, max_size=11), diff --git a/tests/ast/nodes/test_evaluate_boolop.py b/tests/ast/nodes/test_evaluate_boolop.py index 6bd9ecc6cb..8b70537c39 100644 --- a/tests/ast/nodes/test_evaluate_boolop.py +++ b/tests/ast/nodes/test_evaluate_boolop.py @@ -8,7 +8,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(values=st.lists(st.booleans(), min_size=2, max_size=10)) @pytest.mark.parametrize("comparator", ["and", "or"]) def test_boolop_simple(get_contract, values, comparator): @@ -32,7 +32,7 @@ def foo({input_value}) -> bool: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given( values=st.lists(st.booleans(), min_size=2, max_size=10), comparators=st.lists(st.sampled_from(["and", "or"]), min_size=11, max_size=11), diff --git a/tests/ast/nodes/test_evaluate_compare.py b/tests/ast/nodes/test_evaluate_compare.py index 9ff5cea338..07f8e70de6 100644 --- a/tests/ast/nodes/test_evaluate_compare.py +++ b/tests/ast/nodes/test_evaluate_compare.py @@ -8,7 +8,7 @@ # TODO expand to all signed types @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st.integers(), right=st.integers()) @pytest.mark.parametrize("op", ["==", "!=", "<", "<=", ">=", ">"]) def test_compare_eq_signed(get_contract, op, left, right): @@ -28,7 +28,7 @@ def foo(a: int128, b: int128) -> bool: # TODO expand to all unsigned types @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st.integers(min_value=0), right=st.integers(min_value=0)) @pytest.mark.parametrize("op", ["==", "!=", "<", "<=", ">=", ">"]) def test_compare_eq_unsigned(get_contract, op, left, right): @@ -47,7 +47,7 @@ def foo(a: uint128, b: uint128) -> bool: @pytest.mark.fuzzing -@settings(max_examples=20, deadline=1000) +@settings(max_examples=20) @given(left=st.integers(), right=st.lists(st.integers(), min_size=1, max_size=16)) def test_compare_in(left, right, get_contract): source = f""" @@ -76,7 +76,7 @@ def bar(a: int128) -> bool: @pytest.mark.fuzzing -@settings(max_examples=20, deadline=1000) +@settings(max_examples=20) @given(left=st.integers(), right=st.lists(st.integers(), min_size=1, max_size=16)) def test_compare_not_in(left, right, get_contract): source = f""" diff --git a/tests/ast/nodes/test_evaluate_subscript.py b/tests/ast/nodes/test_evaluate_subscript.py index 3c0fa5d16d..ca50a076a5 100644 --- a/tests/ast/nodes/test_evaluate_subscript.py +++ b/tests/ast/nodes/test_evaluate_subscript.py @@ -6,7 +6,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given( idx=st.integers(min_value=0, max_value=9), array=st.lists(st.integers(), min_size=10, max_size=10), diff --git a/tests/base_conftest.py b/tests/base_conftest.py index 81e8dedc36..1c7c6f3aed 100644 --- a/tests/base_conftest.py +++ b/tests/base_conftest.py @@ -118,8 +118,8 @@ def _get_contract(w3, source_code, optimize, *args, override_opt_level=None, **k settings.optimize = override_opt_level or optimize out = compiler.compile_code( source_code, - # test that metadata gets generated - ["abi", "bytecode", "metadata"], + # test that metadata and natspecs get generated + ["abi", "bytecode", "metadata", "userdoc", "devdoc"], settings=settings, interface_codes=kwargs.pop("interface_codes", None), show_gas_estimates=True, # Enable gas estimates for testing diff --git a/tests/builtins/folding/test_abs.py b/tests/builtins/folding/test_abs.py index 831366da41..bb882951c8 100644 --- a/tests/builtins/folding/test_abs.py +++ b/tests/builtins/folding/test_abs.py @@ -8,7 +8,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(a=st.integers(min_value=-(2**255) + 1, max_value=2**255 - 1)) @example(a=0) def test_abs(get_contract, a): @@ -27,7 +27,7 @@ def foo(a: int256) -> int256: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(a=st.integers(min_value=2**255, max_value=2**256 - 1)) def test_abs_upper_bound_folding(get_contract, a): source = f""" diff --git a/tests/builtins/folding/test_addmod_mulmod.py b/tests/builtins/folding/test_addmod_mulmod.py index 0514dea18a..33dcc62984 100644 --- a/tests/builtins/folding/test_addmod_mulmod.py +++ b/tests/builtins/folding/test_addmod_mulmod.py @@ -9,7 +9,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(a=st_uint256, b=st_uint256, c=st_uint256) @pytest.mark.parametrize("fn_name", ["uint256_addmod", "uint256_mulmod"]) def test_modmath(get_contract, a, b, c, fn_name): diff --git a/tests/builtins/folding/test_bitwise.py b/tests/builtins/folding/test_bitwise.py index d28e482589..63e733644f 100644 --- a/tests/builtins/folding/test_bitwise.py +++ b/tests/builtins/folding/test_bitwise.py @@ -14,7 +14,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @pytest.mark.parametrize("op", ["&", "|", "^"]) @given(a=st_uint256, b=st_uint256) def test_bitwise_ops(get_contract, a, b, op): @@ -34,7 +34,7 @@ def foo(a: uint256, b: uint256) -> uint256: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @pytest.mark.parametrize("op", ["<<", ">>"]) @given(a=st_uint256, b=st.integers(min_value=0, max_value=256)) def test_bitwise_shift_unsigned(get_contract, a, b, op): @@ -64,7 +64,7 @@ def foo(a: uint256, b: uint256) -> uint256: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @pytest.mark.parametrize("op", ["<<", ">>"]) @given(a=st_sint256, b=st.integers(min_value=0, max_value=256)) def test_bitwise_shift_signed(get_contract, a, b, op): @@ -92,7 +92,7 @@ def foo(a: int256, b: uint256) -> int256: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(value=st_uint256) def test_bitwise_not(get_contract, value): source = """ diff --git a/tests/builtins/folding/test_floor_ceil.py b/tests/builtins/folding/test_floor_ceil.py index 763f8fec63..87db23889a 100644 --- a/tests/builtins/folding/test_floor_ceil.py +++ b/tests/builtins/folding/test_floor_ceil.py @@ -13,7 +13,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(value=st_decimals) @example(value=Decimal("0.9999999999")) @example(value=Decimal("0.0000000001")) diff --git a/tests/builtins/folding/test_fold_as_wei_value.py b/tests/builtins/folding/test_fold_as_wei_value.py index 11d23bd3bf..210ab51f0d 100644 --- a/tests/builtins/folding/test_fold_as_wei_value.py +++ b/tests/builtins/folding/test_fold_as_wei_value.py @@ -19,7 +19,7 @@ @pytest.mark.fuzzing -@settings(max_examples=10, deadline=1000) +@settings(max_examples=10) @given(value=st_decimals) @pytest.mark.parametrize("denom", denoms) def test_decimal(get_contract, value, denom): @@ -38,7 +38,7 @@ def foo(a: decimal) -> uint256: @pytest.mark.fuzzing -@settings(max_examples=10, deadline=1000) +@settings(max_examples=10) @given(value=st.integers(min_value=0, max_value=2**128)) @pytest.mark.parametrize("denom", denoms) def test_integer(get_contract, value, denom): diff --git a/tests/builtins/folding/test_keccak_sha.py b/tests/builtins/folding/test_keccak_sha.py index 8e283566de..a2fe460dd1 100644 --- a/tests/builtins/folding/test_keccak_sha.py +++ b/tests/builtins/folding/test_keccak_sha.py @@ -10,7 +10,7 @@ @pytest.mark.fuzzing @given(value=st.text(alphabet=alphabet, min_size=0, max_size=100)) -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @pytest.mark.parametrize("fn_name", ["keccak256", "sha256"]) def test_string(get_contract, value, fn_name): source = f""" @@ -29,7 +29,7 @@ def foo(a: String[100]) -> bytes32: @pytest.mark.fuzzing @given(value=st.binary(min_size=0, max_size=100)) -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @pytest.mark.parametrize("fn_name", ["keccak256", "sha256"]) def test_bytes(get_contract, value, fn_name): source = f""" @@ -48,7 +48,7 @@ def foo(a: Bytes[100]) -> bytes32: @pytest.mark.fuzzing @given(value=st.binary(min_size=1, max_size=100)) -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @pytest.mark.parametrize("fn_name", ["keccak256", "sha256"]) def test_hex(get_contract, value, fn_name): source = f""" diff --git a/tests/builtins/folding/test_min_max.py b/tests/builtins/folding/test_min_max.py index e2d33237ca..309f7519c0 100644 --- a/tests/builtins/folding/test_min_max.py +++ b/tests/builtins/folding/test_min_max.py @@ -18,7 +18,7 @@ @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st_decimals, right=st_decimals) @pytest.mark.parametrize("fn_name", ["min", "max"]) def test_decimal(get_contract, left, right, fn_name): @@ -37,7 +37,7 @@ def foo(a: decimal, b: decimal) -> decimal: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st_int128, right=st_int128) @pytest.mark.parametrize("fn_name", ["min", "max"]) def test_int128(get_contract, left, right, fn_name): @@ -56,7 +56,7 @@ def foo(a: int128, b: int128) -> int128: @pytest.mark.fuzzing -@settings(max_examples=50, deadline=1000) +@settings(max_examples=50) @given(left=st_uint256, right=st_uint256) @pytest.mark.parametrize("fn_name", ["min", "max"]) def test_min_uint256(get_contract, left, right, fn_name): diff --git a/tests/builtins/folding/test_powmod.py b/tests/builtins/folding/test_powmod.py index fdc0e300ab..8667ec93fd 100644 --- a/tests/builtins/folding/test_powmod.py +++ b/tests/builtins/folding/test_powmod.py @@ -9,7 +9,7 @@ @pytest.mark.fuzzing -@settings(max_examples=100, deadline=1000) +@settings(max_examples=100) @given(a=st_uint256, b=st_uint256) def test_powmod_uint256(get_contract, a, b): source = """ diff --git a/tests/conftest.py b/tests/conftest.py index d519ca3100..c9d3f794a0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ import logging from functools import wraps +import hypothesis import pytest from eth_tester import EthereumTester, PyEVMBackend from eth_utils import setup_DEBUG2_logging @@ -23,6 +24,11 @@ ############ +# disable hypothesis deadline globally +hypothesis.settings.register_profile("ci", deadline=None) +hypothesis.settings.load_profile("ci") + + def set_evm_verbose_logging(): logger = logging.getLogger("eth.vm.computation.Computation") setup_DEBUG2_logging() diff --git a/tests/fuzzing/test_exponents.py b/tests/fuzzing/test_exponents.py index 29c1f198ed..5726e4c1ca 100644 --- a/tests/fuzzing/test_exponents.py +++ b/tests/fuzzing/test_exponents.py @@ -92,7 +92,7 @@ def foo(a: int16) -> int16: @example(a=2**127 - 1) # 256 bits @example(a=2**256 - 1) -@settings(max_examples=200, deadline=1000) +@settings(max_examples=200) def test_max_exp(get_contract, assert_tx_failed, a): code = f""" @external @@ -127,7 +127,7 @@ def foo(b: uint256) -> uint256: @example(a=2**63 - 1) # 128 bits @example(a=2**127 - 1) -@settings(max_examples=200, deadline=1000) +@settings(max_examples=200) def test_max_exp_int128(get_contract, assert_tx_failed, a): code = f""" @external diff --git a/tests/grammar/test_grammar.py b/tests/grammar/test_grammar.py index d665ca2544..aa0286cfa5 100644 --- a/tests/grammar/test_grammar.py +++ b/tests/grammar/test_grammar.py @@ -4,7 +4,7 @@ import hypothesis import hypothesis.strategies as st import pytest -from hypothesis import HealthCheck, assume, given +from hypothesis import assume, given from hypothesis.extra.lark import LarkStrategy from vyper.ast import Module, parse_to_ast @@ -103,7 +103,7 @@ def has_no_docstrings(c): @pytest.mark.fuzzing @given(code=from_grammar().filter(lambda c: utf8_encodable(c))) -@hypothesis.settings(deadline=400, max_examples=500, suppress_health_check=(HealthCheck.too_slow,)) +@hypothesis.settings(max_examples=500) def test_grammar_bruteforce(code): if utf8_encodable(code): _, _, reformatted_code = pre_parse(code + "\n") diff --git a/tests/parser/features/iteration/test_for_range.py b/tests/parser/features/iteration/test_for_range.py index 395dd28231..ed6235d992 100644 --- a/tests/parser/features/iteration/test_for_range.py +++ b/tests/parser/features/iteration/test_for_range.py @@ -20,12 +20,12 @@ def test_range_bound(get_contract, assert_tx_failed): def repeat(n: uint256) -> uint256: x: uint256 = 0 for i in range(n, bound=6): - x += i + x += i + 1 return x """ c = get_contract(code) for n in range(7): - assert c.repeat(n) == sum(range(n)) + assert c.repeat(n) == sum(i + 1 for i in range(n)) # check codegen inserts assertion for n greater than bound assert_tx_failed(lambda: c.repeat(7)) diff --git a/tests/parser/features/test_internal_call.py b/tests/parser/features/test_internal_call.py index d7a41acbc0..f10d22ec99 100644 --- a/tests/parser/features/test_internal_call.py +++ b/tests/parser/features/test_internal_call.py @@ -669,7 +669,7 @@ def test_internal_call_kwargs(get_contract, typ1, strategy1, typ2, strategy2): # GHSA-ph9x-4vc9-m39g @given(kwarg1=strategy1, default1=strategy1, kwarg2=strategy2, default2=strategy2) - @settings(deadline=None, max_examples=5) # len(cases) * len(cases) * 5 * 5 + @settings(max_examples=5) # len(cases) * len(cases) * 5 * 5 def fuzz(kwarg1, kwarg2, default1, default2): code = f""" @internal diff --git a/tests/parser/functions/test_slice.py b/tests/parser/functions/test_slice.py index 6229b47921..3090dafda0 100644 --- a/tests/parser/functions/test_slice.py +++ b/tests/parser/functions/test_slice.py @@ -36,7 +36,7 @@ def slice_tower_test(inp1: Bytes[50]) -> Bytes[50]: @pytest.mark.parametrize("literal_length", (True, False)) @pytest.mark.parametrize("opt_level", list(OptimizationLevel)) @given(start=_draw_1024, length=_draw_1024, length_bound=_draw_1024_1, bytesdata=_bytes_1024) -@settings(max_examples=100, deadline=None) +@settings(max_examples=100) @pytest.mark.fuzzing def test_slice_immutable( get_contract, @@ -90,7 +90,7 @@ def _get_contract(): @pytest.mark.parametrize("literal_length", (True, False)) @pytest.mark.parametrize("opt_level", list(OptimizationLevel)) @given(start=_draw_1024, length=_draw_1024, length_bound=_draw_1024_1, bytesdata=_bytes_1024) -@settings(max_examples=100, deadline=None) +@settings(max_examples=100) @pytest.mark.fuzzing def test_slice_bytes( get_contract, diff --git a/tests/parser/test_call_graph_stability.py b/tests/parser/test_call_graph_stability.py index a6193610e2..4c85c330f3 100644 --- a/tests/parser/test_call_graph_stability.py +++ b/tests/parser/test_call_graph_stability.py @@ -15,7 +15,7 @@ def _valid_identifier(attr): # random names for functions -@settings(max_examples=20, deadline=None) +@settings(max_examples=20) @given( st.lists( st.tuples( diff --git a/tests/parser/test_selector_table.py b/tests/parser/test_selector_table.py index 3ac50707c2..161cd480fd 100644 --- a/tests/parser/test_selector_table.py +++ b/tests/parser/test_selector_table.py @@ -446,7 +446,7 @@ def aILR4U1Z()->uint256: seed=st.integers(min_value=0, max_value=2**64 - 1), ) @pytest.mark.fuzzing -@settings(max_examples=10, deadline=None) +@settings(max_examples=10) def test_sparse_jumptable_probe_depth(n_methods, seed): sigs = [f"foo{i + seed}()" for i in range(n_methods)] _, buckets = generate_sparse_jumptable_buckets(sigs) @@ -466,7 +466,7 @@ def test_sparse_jumptable_probe_depth(n_methods, seed): seed=st.integers(min_value=0, max_value=2**64 - 1), ) @pytest.mark.fuzzing -@settings(max_examples=10, deadline=None) +@settings(max_examples=10) def test_dense_jumptable_bucket_size(n_methods, seed): sigs = [f"foo{i + seed}()" for i in range(n_methods)] n = len(sigs) @@ -478,66 +478,72 @@ def test_dense_jumptable_bucket_size(n_methods, seed): assert n_buckets / n < 0.4 or n < 10 +@st.composite +def generate_methods(draw, max_calldata_bytes): + max_default_args = draw(st.integers(min_value=0, max_value=4)) + default_fn_mutability = draw(st.sampled_from(["", "@pure", "@view", "@nonpayable", "@payable"])) + + return ( + max_default_args, + default_fn_mutability, + draw( + st.lists( + st.tuples( + # function id: + st.integers(min_value=0), + # mutability: + st.sampled_from(["@pure", "@view", "@nonpayable", "@payable"]), + # n calldata words: + st.integers(min_value=0, max_value=max_calldata_bytes // 32), + # n bytes to strip from calldata + st.integers(min_value=1, max_value=4), + # n default args + st.integers(min_value=0, max_value=max_default_args), + ), + unique_by=lambda x: x[0], + min_size=1, + max_size=100, + ) + ), + ) + + @pytest.mark.parametrize("opt_level", list(OptimizationLevel)) # dense selector table packing boundaries at 256 and 65336 @pytest.mark.parametrize("max_calldata_bytes", [255, 256, 65336]) -@settings(max_examples=5, deadline=None) -@given( - seed=st.integers(min_value=0, max_value=2**64 - 1), - max_default_args=st.integers(min_value=0, max_value=4), - default_fn_mutability=st.sampled_from(["", "@pure", "@view", "@nonpayable", "@payable"]), -) @pytest.mark.fuzzing def test_selector_table_fuzz( - max_calldata_bytes, - seed, - max_default_args, - opt_level, - default_fn_mutability, - w3, - get_contract, - assert_tx_failed, - get_logs, + max_calldata_bytes, opt_level, w3, get_contract, assert_tx_failed, get_logs ): - def abi_sig(calldata_words, i, n_default_args): - args = [] if not calldata_words else [f"uint256[{calldata_words}]"] - args.extend(["uint256"] * n_default_args) - argstr = ",".join(args) - return f"foo{seed + i}({argstr})" + def abi_sig(func_id, calldata_words, n_default_args): + params = [] if not calldata_words else [f"uint256[{calldata_words}]"] + params.extend(["uint256"] * n_default_args) + paramstr = ",".join(params) + return f"foo{func_id}({paramstr})" - def generate_func_def(mutability, calldata_words, i, n_default_args): + def generate_func_def(func_id, mutability, calldata_words, n_default_args): arglist = [] if not calldata_words else [f"x: uint256[{calldata_words}]"] for j in range(n_default_args): arglist.append(f"x{j}: uint256 = 0") args = ", ".join(arglist) - _log_return = f"log _Return({i})" if mutability == "@payable" else "" + _log_return = f"log _Return({func_id})" if mutability == "@payable" else "" return f""" @external {mutability} -def foo{seed + i}({args}) -> uint256: +def foo{func_id}({args}) -> uint256: {_log_return} - return {i} + return {func_id} """ - @given( - methods=st.lists( - st.tuples( - st.sampled_from(["@pure", "@view", "@nonpayable", "@payable"]), - st.integers(min_value=0, max_value=max_calldata_bytes // 32), - # n bytes to strip from calldata - st.integers(min_value=1, max_value=4), - # n default args - st.integers(min_value=0, max_value=max_default_args), - ), - min_size=1, - max_size=100, - ) - ) - @settings(max_examples=25) - def _test(methods): + @given(_input=generate_methods(max_calldata_bytes)) + @settings(max_examples=125) + def _test(_input): + max_default_args, default_fn_mutability, methods = _input + func_defs = "\n".join( - generate_func_def(m, s, i, d) for i, (m, s, _, d) in enumerate(methods) + generate_func_def(func_id, mutability, calldata_words, n_default_args) + for (func_id, mutability, calldata_words, _, n_default_args) in (methods) ) if default_fn_mutability == "": @@ -571,8 +577,8 @@ def __default__(): c = get_contract(code, override_opt_level=opt_level) - for i, (mutability, n_calldata_words, n_strip_bytes, n_default_args) in enumerate(methods): - funcname = f"foo{seed + i}" + for func_id, mutability, n_calldata_words, n_strip_bytes, n_default_args in methods: + funcname = f"foo{func_id}" func = getattr(c, funcname) for j in range(n_default_args + 1): @@ -580,9 +586,9 @@ def __default__(): args.extend([1] * j) # check the function returns as expected - assert func(*args) == i + assert func(*args) == func_id - method_id = utils.method_id(abi_sig(n_calldata_words, i, j)) + method_id = utils.method_id(abi_sig(func_id, n_calldata_words, j)) argsdata = b"\x00" * (n_calldata_words * 32 + j * 32) @@ -590,7 +596,7 @@ def __default__(): if mutability == "@payable": tx = func(*args, transact={"value": 1}) (event,) = get_logs(tx, c, "_Return") - assert event.args.val == i + assert event.args.val == func_id else: hexstr = (method_id + argsdata).hex() txdata = {"to": c.address, "data": hexstr, "value": 1} diff --git a/tests/parser/types/numbers/test_isqrt.py b/tests/parser/types/numbers/test_isqrt.py index ce26d24d06..b734323a6e 100644 --- a/tests/parser/types/numbers/test_isqrt.py +++ b/tests/parser/types/numbers/test_isqrt.py @@ -119,7 +119,6 @@ def test(a: uint256) -> (uint256, uint256, uint256, uint256, uint256, String[100 @hypothesis.example(2704) @hypothesis.example(110889) @hypothesis.example(32239684) -@hypothesis.settings(deadline=1000) def test_isqrt_valid_range(isqrt_contract, value): vyper_isqrt = isqrt_contract.test(value) actual_isqrt = math.isqrt(value) diff --git a/tests/parser/types/numbers/test_sqrt.py b/tests/parser/types/numbers/test_sqrt.py index df1ed0539c..020a79e7ef 100644 --- a/tests/parser/types/numbers/test_sqrt.py +++ b/tests/parser/types/numbers/test_sqrt.py @@ -145,7 +145,6 @@ def test_sqrt_bounds(sqrt_contract, value): ) @hypothesis.example(value=Decimal(SizeLimits.MAX_INT128)) @hypothesis.example(value=Decimal(0)) -@hypothesis.settings(deadline=1000) def test_sqrt_valid_range(sqrt_contract, value): vyper_sqrt = sqrt_contract.test(value) actual_sqrt = decimal_sqrt(value) @@ -158,7 +157,6 @@ def test_sqrt_valid_range(sqrt_contract, value): min_value=Decimal(SizeLimits.MIN_INT128), max_value=Decimal("-1E10"), places=DECIMAL_PLACES ) ) -@hypothesis.settings(deadline=400) @hypothesis.example(value=Decimal(SizeLimits.MIN_INT128)) @hypothesis.example(value=Decimal("-1E10")) def test_sqrt_invalid_range(sqrt_contract, value): diff --git a/tests/parser/types/test_bytes_zero_padding.py b/tests/parser/types/test_bytes_zero_padding.py index ee938fdffb..f9fcf37b25 100644 --- a/tests/parser/types/test_bytes_zero_padding.py +++ b/tests/parser/types/test_bytes_zero_padding.py @@ -26,7 +26,6 @@ def get_count(counter: uint256) -> Bytes[24]: @pytest.mark.fuzzing @hypothesis.given(value=hypothesis.strategies.integers(min_value=0, max_value=2**64)) -@hypothesis.settings(deadline=400) def test_zero_pad_range(little_endian_contract, value): actual_bytes = value.to_bytes(8, byteorder="little") contract_bytes = little_endian_contract.get_count(value) diff --git a/vyper/codegen/stmt.py b/vyper/codegen/stmt.py index 3ecb0afdc3..c2951986c8 100644 --- a/vyper/codegen/stmt.py +++ b/vyper/codegen/stmt.py @@ -302,7 +302,9 @@ def _parse_For_range(self): loop_body.append(["mstore", iptr, i]) loop_body.append(parse_body(self.stmt.body, self.context)) - # NOTE: codegen for `repeat` inserts an assertion that rounds <= rounds_bound. + # NOTE: codegen for `repeat` inserts an assertion that + # (gt rounds_bound rounds). note this also covers the case where + # rounds < 0. # if we ever want to remove that, we need to manually add the assertion # where it makes sense. ir_node = IRnode.from_list( diff --git a/vyper/compiler/output.py b/vyper/compiler/output.py index 4ab47a1800..635053551c 100644 --- a/vyper/compiler/output.py +++ b/vyper/compiler/output.py @@ -107,7 +107,7 @@ def build_metadata_output(compiler_data: CompilerData) -> dict: sigs = compiler_data.function_signatures def _var_rec_dict(variable_record): - ret = vars(variable_record) + ret = vars(variable_record).copy() ret["typ"] = str(ret["typ"]) if ret["data_offset"] is None: del ret["data_offset"] @@ -117,7 +117,7 @@ def _var_rec_dict(variable_record): return ret def _to_dict(func_t): - ret = vars(func_t) + ret = vars(func_t).copy() ret["return_type"] = str(ret["return_type"]) ret["_ir_identifier"] = func_t._ir_info.ir_identifier @@ -133,7 +133,7 @@ def _to_dict(func_t): args = ret[attr] ret[attr] = {arg.name: str(arg.typ) for arg in args} - ret["frame_info"] = vars(func_t._ir_info.frame_info) + ret["frame_info"] = vars(func_t._ir_info.frame_info).copy() del ret["frame_info"]["frame_vars"] # frame_var.pos might be IR, cannot serialize keep_keys = { diff --git a/vyper/ir/compile_ir.py b/vyper/ir/compile_ir.py index 7a3e97155b..1c4dc1ef7c 100644 --- a/vyper/ir/compile_ir.py +++ b/vyper/ir/compile_ir.py @@ -415,7 +415,7 @@ def _height_of(witharg): ) ) # stack: i, rounds, rounds_bound - # assert rounds <= rounds_bound + # assert 0 <= rounds <= rounds_bound (for rounds_bound < 2**255) # TODO this runtime assertion shouldn't fail for # internally generated repeats. o.extend(["DUP2", "GT"] + _assert_false())