From aa87efb621a674683a84b6bca2f1ee7ea14c1138 Mon Sep 17 00:00:00 2001 From: Piper Merriam Date: Thu, 11 Apr 2019 12:11:18 -0600 Subject: [PATCH] Update to latest SSZ with SOS and add Boolean test generators --- ssz/_utils.py | 25 ++++++++ ssz/bool_test_generators.py | 122 ++++++++++++++++++++++++++++++++++++ ssz/renderers.py | 5 ++ ssz/test_generator.py | 10 +++ ssz/uint_test_generators.py | 22 ++++--- 5 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 ssz/_utils.py create mode 100644 ssz/bool_test_generators.py diff --git a/ssz/_utils.py b/ssz/_utils.py new file mode 100644 index 0000000..71cb762 --- /dev/null +++ b/ssz/_utils.py @@ -0,0 +1,25 @@ +import contextlib +import functools +import random + + +@contextlib.contextmanager +def random_seed(seed=0): + original_state = random.getstate() + try: + random.seed(seed) + yield + finally: + random.setstate(original_state) + + +def seed(fn): + @functools.wraps(fn) + def inner(*args, **kwargs): + with random_seed(): + return fn(*args, **kwargs) + return inner + + +def get_random_bytes(length): + return bytes(random.randint(0, 255) for _ in range(length)) diff --git a/ssz/bool_test_generators.py b/ssz/bool_test_generators.py new file mode 100644 index 0000000..5747e18 --- /dev/null +++ b/ssz/bool_test_generators.py @@ -0,0 +1,122 @@ +from eth_utils import ( + to_tuple, +) + +import ssz +from ssz.sedes import ( + Boolean, +) +from renderers import ( + render_test, + render_test_case, +) +from _utils import ( + seed, +) + + + +@seed +def generate_bool_true_and_false_test(): + test_cases = mk_bool_true_and_false_test_cases() + + return render_test( + title="Bool Values", + summary="The two valid values for a boolean", + fork="phase0-0.2.0", + test_cases=test_cases, + ) + + +@seed +def generate_bool_wrong_length_test(): + test_cases = mk_bool_wrong_length_test_cases() + + return render_test( + title="Bool Wrong Length", + summary="Byte strings of incorrect length", + fork="phase0-0.2.0", + test_cases=test_cases, + ) + + +@seed +def generate_bool_invalid_byte_test(): + test_cases = mk_bool_invalid_byte_test_cases() + + return render_test( + title="Bool Invalid Byte", + summary="Single byte values that are not 0x00 or 0x01", + fork="phase0-0.2.0", + test_cases=test_cases, + ) + + +@to_tuple +def mk_bool_true_and_false_test_cases(): + sedes = Boolean() + + true_serial = ssz.encode(True, sedes) + yield render_test_case( + sedes=sedes, + valid=True, + value=True, + serial=true_serial, + tags=("atomic", "bool", "true") + ) + + false_serial = ssz.encode(False, sedes) + yield render_test_case( + sedes=sedes, + valid=True, + value=False, + serial=false_serial, + tags=("atomic", "bool", "false") + ) + + +WRONG_LENGTH_SERIALS = ( + b'', + b'\x00' * 2, + b'\x01' * 2, + b'\xFF' * 2, + b'\x00' * 3, + b'\x01' * 3, + b'\xFF' * 3, + b'\x00' * 5, + b'\x01' * 5, + b'\xFF' * 5, + b'\x00\x01', + b'\x01\x00', + b'\x00\xFF', + b'\x01\xFF', +) + + +@to_tuple +def mk_bool_wrong_length_test_cases(): + tags = ("atomic", "bool", "wrong_length") + sedes = Boolean() + + for serial in WRONG_LENGTH_SERIALS: + yield render_test_case( + sedes=sedes, + valid=False, + serial=serial, + tags=tags, + ) + + +@to_tuple +def mk_bool_invalid_byte_test_cases(): + tags = ("atomic", "bool", "invalid") + sedes = Boolean() + + for i in range(2, 255): + serial = bytes((i,)) + yield render_test_case( + sedes=sedes, + valid=False, + serial=serial, + tags=tags, + ) diff --git a/ssz/renderers.py b/ssz/renderers.py index e551ab1..3f66086 100644 --- a/ssz/renderers.py +++ b/ssz/renderers.py @@ -2,6 +2,7 @@ Mapping, Sequence, ) +import copy from eth_utils import ( encode_hex, @@ -82,6 +83,10 @@ def render_test_case(*, sedes, valid, value=None, serial=None, description=None, if tags is None: tags = [] + # Ensure that ruemel doesn't use references, allowing callers of this + # function to re-use the same `tags` object across multiple calls. + tags = tuple(tag for tag in tags) + yield "type", render_type_definition(sedes) yield "valid", valid if value is not None: diff --git a/ssz/test_generator.py b/ssz/test_generator.py index d19ec12..7176135 100644 --- a/ssz/test_generator.py +++ b/ssz/test_generator.py @@ -11,11 +11,21 @@ generate_uint_random_test, generate_uint_wrong_length_test, ) +from bool_test_generators import ( + generate_bool_true_and_false_test, + generate_bool_wrong_length_test, + generate_bool_invalid_byte_test, +) test_generators = [ + # uint generate_uint_random_test, generate_uint_wrong_length_test, generate_uint_bounds_test, + # bool + generate_bool_true_and_false_test, + generate_bool_wrong_length_test, + generate_bool_invalid_byte_test, ] diff --git a/ssz/uint_test_generators.py b/ssz/uint_test_generators.py index c8c841f..70db8e0 100644 --- a/ssz/uint_test_generators.py +++ b/ssz/uint_test_generators.py @@ -8,23 +8,23 @@ from ssz.sedes import ( UInt, ) + +from _utils import ( + seed, + get_random_bytes, +) from renderers import ( render_test, render_test_case, ) -random.seed(0) - BIT_SIZES = [i for i in range(8, 512 + 1, 8)] RANDOM_TEST_CASES_PER_BIT_SIZE = 10 RANDOM_TEST_CASES_PER_LENGTH = 3 -def get_random_bytes(length): - return bytes(random.randint(0, 255) for _ in range(length)) - - +@seed def generate_uint_bounds_test(): test_cases = generate_uint_bounds_test_cases() + generate_uint_out_of_bounds_test_cases() @@ -36,6 +36,7 @@ def generate_uint_bounds_test(): ) +@seed def generate_uint_random_test(): test_cases = generate_random_uint_test_cases() @@ -47,6 +48,7 @@ def generate_uint_random_test(): ) +@seed def generate_uint_wrong_length_test(): test_cases = generate_uint_wrong_length_test_cases() @@ -60,15 +62,14 @@ def generate_uint_wrong_length_test(): @to_tuple def generate_random_uint_test_cases(): + tags = ("atomic", "uint", "random") + for bit_size in BIT_SIZES: sedes = UInt(bit_size) for _ in range(RANDOM_TEST_CASES_PER_BIT_SIZE): value = random.randrange(0, 2 ** bit_size) serial = ssz.encode(value, sedes) - # note that we need to create the tags in each loop cycle, otherwise ruamel will use - # YAML references which makes the resulting file harder to read - tags = tuple(["atomic", "uint", "random"]) yield render_test_case( sedes=sedes, valid=True, @@ -80,6 +81,8 @@ def generate_random_uint_test_cases(): @to_tuple def generate_uint_wrong_length_test_cases(): + tags = ("atomic", "uint", "wrong_length") + for bit_size in BIT_SIZES: sedes = UInt(bit_size) lengths = sorted({ @@ -91,7 +94,6 @@ def generate_uint_wrong_length_test_cases(): }) for length in lengths: for _ in range(RANDOM_TEST_CASES_PER_LENGTH): - tags = tuple(["atomic", "uint", "wrong_length"]) yield render_test_case( sedes=sedes, valid=False,