diff --git a/.gitignore b/.gitignore index 827274029..4a5b7ac18 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ pyethereum/todo.txt pyethereum/monkeypatch.py .eggs .cache +.hypothesis diff --git a/.travis.yml b/.travis.yml index 83ced25b8..8eb2bcfca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ env: - TOX_ENV=py27 global: secure: cKbIgpTJ1yjKLBxpCEiT6IH7NShDWZUE+BvnrAfc+ujCsR6LyLJcKxFQmKnWryJCqg7fp82Ep2bF2oDKzanAROar2xDY1SFGbai42seYMaFCw53YPGJ6u3VNCcfT0rN9BWgE7el/m4fjcD6CRsZYKArNNJbMX8csRt3uXXCFLso= +cache: + directories: + - $HOME/.cache/pip + - .hypothesis install: - pip install -Ur requirements.txt - pip install -Ur dev_requirements.txt diff --git a/dev_requirements.txt b/dev_requirements.txt index 8bd463833..054e4e75b 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -3,4 +3,5 @@ coveralls pytest>=2.9.0 pytest-catchlog==1.2.2 pytest-timeout==1.0.0 +hypothesis>=3.4.0 https://github.com/ethereum/serpent/tarball/develop diff --git a/ethereum/tests/abi_type_strategies.py b/ethereum/tests/abi_type_strategies.py new file mode 100644 index 000000000..86819709a --- /dev/null +++ b/ethereum/tests/abi_type_strategies.py @@ -0,0 +1,88 @@ +import itertools + +import hypothesis.strategies as st + + +strat_int256 = st.integers(min_value=-1 * 2**255, max_value=2**255 - 1) +strat_uint256 = st.integers(min_value=0, max_value=2**256 - 1) + +MAX_LIST_SIZE = 8 +MIN_LIST_SIZE = 0 + + +uint_raw_strats = [ + ('uint' + str(sub), st.integers(min_value=0, max_value=2**sub - 1)) + for sub in range(8, 257, 8) +] +uint_strats = [ + st.tuples(st.just(key), strat) for key, strat in uint_raw_strats +] + + +int_raw_strats = [ + ('int' + str(sub), st.integers(min_value=-1 * 2**(sub - 1), max_value=2**(sub - 1) - 1)) + for sub in range(8, 257, 8) +] +int_strats = [ + st.tuples(st.just(key), strat) for key, strat in int_raw_strats +] + + +bytes_raw_strats = [ + ('bytes' + str(sub), st.binary(min_size=sub, max_size=sub)) + for sub in range(1, 33) +] +bytes_strats = [ + st.tuples(st.just(key), strat) for key, strat in bytes_raw_strats +] + + +address_raw_strat = st.binary(min_size=20, max_size=20).map(lambda v: v.encode('hex')) +address_strat = st.tuples( + st.just('address'), + address_raw_strat, +) + + +all_basic_raw_strats = list(itertools.chain( + int_raw_strats, uint_raw_strats, bytes_raw_strats, [('address', address_raw_strat)], +)) +all_basic_strats = list(itertools.chain( + int_strats, uint_strats, bytes_strats, [address_strat], +)) + + +unsized_list_raw_strats = [ + (type_str + "[]", st.lists(type_strat, min_size=0, max_size=MAX_LIST_SIZE)) + for type_str, type_strat in all_basic_raw_strats +] +unsized_list_strats = [ + st.tuples(st.just(type_str), type_strat) + for type_str, type_strat in unsized_list_raw_strats +] + + +sized_list_strats = [ + st.tuples( + st.shared( + st.integers(min_value=MIN_LIST_SIZE, max_value=MAX_LIST_SIZE), + key="n", + ).map(lambda n: type_str + "[{0}]".format(n)), + st.shared( + st.integers(min_value=MIN_LIST_SIZE, max_value=MAX_LIST_SIZE), + key="n", + ).flatmap(lambda n: st.lists(type_strat, min_size=n, max_size=n)) + ) for type_str, type_strat in all_basic_raw_strats +] + + +def zip_types_and_values(types_and_values): + types, values = zip(*types_and_values) + return list(types), list(values) + + +all_abi_strats = st.lists( + st.one_of(itertools.chain(unsized_list_strats, sized_list_strats, all_basic_strats)), + min_size=1, + max_size=10, +).map(zip_types_and_values) diff --git a/ethereum/tests/test_abi.py b/ethereum/tests/test_abi.py index 03fd3933a..bf6dde62e 100644 --- a/ethereum/tests/test_abi.py +++ b/ethereum/tests/test_abi.py @@ -1,13 +1,28 @@ -import os import pytest +from hypothesis import given import ethereum.testutils as testutils from ethereum.slogging import get_logger import ethereum.abi as abi from ethereum.utils import zpad +from .abi_type_strategies import all_abi_strats + + logger = get_logger() +@given(all_abi_strats) +def test_reversability(types_and_values): + """ + Tests round trip encoding and decoding for basic types and lists of basic + types. + """ + types, value = types_and_values + encoded_value = abi.encode_abi(types, value) + decoded_value = abi.decode_abi(types, encoded_value) + assert value == decoded_value + + def test_abi_encode_var_sized_array(): abi.encode_abi(['address[]'], [[b'\x00' * 20] * 3])