From 5b956b3d26ac2cea136e7e6b9e4f141c8f696db4 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 27 Jul 2019 16:45:30 +0200 Subject: [PATCH] implement new ssz generic tests --- test_generators/ssz_generic/main.py | 43 +-------- test_generators/ssz_generic/renderers.py | 93 -------------------- test_generators/ssz_generic/requirements.txt | 2 +- test_generators/ssz_generic/ssz_bitlist.py | 33 +++++++ test_generators/ssz_generic/ssz_bitvector.py | 30 +++++++ test_generators/ssz_generic/ssz_boolean.py | 15 ++++ test_generators/ssz_generic/ssz_test_case.py | 21 +++++ test_generators/ssz_generic/ssz_uints.py | 34 +++++++ 8 files changed, 135 insertions(+), 136 deletions(-) delete mode 100644 test_generators/ssz_generic/renderers.py create mode 100644 test_generators/ssz_generic/ssz_bitlist.py create mode 100644 test_generators/ssz_generic/ssz_bitvector.py create mode 100644 test_generators/ssz_generic/ssz_boolean.py create mode 100644 test_generators/ssz_generic/ssz_test_case.py create mode 100644 test_generators/ssz_generic/ssz_uints.py diff --git a/test_generators/ssz_generic/main.py b/test_generators/ssz_generic/main.py index fe01a68d7..2e34aacf4 100644 --- a/test_generators/ssz_generic/main.py +++ b/test_generators/ssz_generic/main.py @@ -1,46 +1,5 @@ -from uint_test_cases import ( - generate_random_uint_test_cases, - generate_uint_wrong_length_test_cases, - generate_uint_bounds_test_cases, - generate_uint_out_of_bounds_test_cases -) -from gen_base import gen_runner, gen_suite, gen_typing - -def ssz_random_uint_suite(configs_path: str) -> gen_typing.TestSuiteOutput: - return ("uint_random", "uint", gen_suite.render_suite( - title="UInt Random", - summary="Random integers chosen uniformly over the allowed value range", - forks_timeline= "mainnet", - forks=["phase0"], - config="mainnet", - runner="ssz", - handler="uint", - test_cases=generate_random_uint_test_cases())) - - -def ssz_wrong_uint_suite(configs_path: str) -> gen_typing.TestSuiteOutput: - return ("uint_wrong_length", "uint", gen_suite.render_suite( - title="UInt Wrong Length", - summary="Serialized integers that are too short or too long", - forks_timeline= "mainnet", - forks=["phase0"], - config="mainnet", - runner="ssz", - handler="uint", - test_cases=generate_uint_wrong_length_test_cases())) - - -def ssz_uint_bounds_suite(configs_path: str) -> gen_typing.TestSuiteOutput: - return ("uint_bounds", "uint", gen_suite.render_suite( - title="UInt Bounds", - summary="Integers right at or beyond the bounds of the allowed value range", - forks_timeline= "mainnet", - forks=["phase0"], - config="mainnet", - runner="ssz", - handler="uint", - test_cases=generate_uint_bounds_test_cases() + generate_uint_out_of_bounds_test_cases())) +from gen_base import gen_runner, gen_typing if __name__ == "__main__": diff --git a/test_generators/ssz_generic/renderers.py b/test_generators/ssz_generic/renderers.py deleted file mode 100644 index 28571cdda..000000000 --- a/test_generators/ssz_generic/renderers.py +++ /dev/null @@ -1,93 +0,0 @@ -from collections.abc import ( - Mapping, - Sequence, -) - -from eth_utils import ( - encode_hex, - to_dict, -) - -from ssz.sedes import ( - BaseSedes, - Boolean, - Bytes, - BytesN, - Container, - List, - UInt, -) - - -def render_value(value): - if isinstance(value, bool): - return value - elif isinstance(value, int): - return str(value) - elif isinstance(value, bytes): - return encode_hex(value) - elif isinstance(value, Sequence): - return tuple(render_value(element) for element in value) - elif isinstance(value, Mapping): - return render_dict_value(value) - else: - raise ValueError(f"Cannot render value {value}") - - -@to_dict -def render_dict_value(value): - for key, value in value.items(): - yield key, render_value(value) - - -def render_type_definition(sedes): - if isinstance(sedes, Boolean): - return "bool" - - elif isinstance(sedes, UInt): - return f"uint{sedes.length * 8}" - - elif isinstance(sedes, BytesN): - return f"bytes{sedes.length}" - - elif isinstance(sedes, Bytes): - return f"bytes" - - elif isinstance(sedes, List): - return [render_type_definition(sedes.element_sedes)] - - elif isinstance(sedes, Container): - return { - field_name: render_type_definition(field_sedes) - for field_name, field_sedes in sedes.fields - } - - elif isinstance(sedes, BaseSedes): - raise Exception("Unreachable: All sedes types have been checked") - - else: - raise TypeError("Expected BaseSedes") - - -@to_dict -def render_test_case(*, sedes, valid, value=None, serial=None, description=None, tags=None): - value_and_serial_given = value is not None and serial is not None - if valid: - if not value_and_serial_given: - raise ValueError("For valid test cases, both value and ssz must be present") - else: - if value_and_serial_given: - raise ValueError("For invalid test cases, one of either value or ssz must not be present") - - if tags is None: - tags = [] - - yield "type", render_type_definition(sedes) - yield "valid", valid - if value is not None: - yield "value", render_value(value) - if serial is not None: - yield "ssz", encode_hex(serial) - if description is not None: - yield description - yield "tags", tags diff --git a/test_generators/ssz_generic/requirements.txt b/test_generators/ssz_generic/requirements.txt index dcdb0824f..c540f26b5 100644 --- a/test_generators/ssz_generic/requirements.txt +++ b/test_generators/ssz_generic/requirements.txt @@ -1,4 +1,4 @@ eth-utils==1.6.0 ../../test_libs/gen_helpers ../../test_libs/config_helpers -ssz==0.1.0a2 +../../test_libs/pyspec diff --git a/test_generators/ssz_generic/ssz_bitlist.py b/test_generators/ssz_generic/ssz_bitlist.py new file mode 100644 index 000000000..45303123d --- /dev/null +++ b/test_generators/ssz_generic/ssz_bitlist.py @@ -0,0 +1,33 @@ +from .ssz_test_case import invalid_test_case, valid_test_case +from eth2spec.utils.ssz.ssz_typing import Bitlist +from eth2spec.utils.ssz.ssz_impl import serialize +from random import Random +from eth2spec.debug.random_value import RandomizationMode, get_random_ssz_object + + +def bitlist_case_fn(rng: Random, mode: RandomizationMode, limit: int): + return get_random_ssz_object(rng, Bitlist[limit], + max_bytes_length=(limit // 8) + 1, + max_list_length=limit, + mode=mode, chaos=False) + + +def valid_cases(): + rng = Random(1234) + for size in [1, 2, 3, 4, 5, 8, 16, 31, 512, 513]: + for variation in range(5): + for mode in [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max]: + yield f'bitlist_{size}_{mode.to_name()}_{variation}', \ + valid_test_case(lambda: bitlist_case_fn(rng, mode, size)) + + +def invalid_cases(): + yield 'bitlist_no_delimiter_empty', invalid_test_case(lambda: b'') + yield 'bitlist_no_delimiter_zero_byte', invalid_test_case(lambda: b'\x00') + yield 'bitlist_no_delimiter_zeroes', invalid_test_case(lambda: b'\x00\x00\x00') + rng = Random(1234) + for (typ_limit, test_limit) in [(1, 2), (1, 8), (1, 9), (2, 3), (3, 4), (4, 5), + (5, 6), (8, 9), (32, 64), (32, 33), (512, 513)]: + yield f'bitlist_{typ_limit}_but_{test_limit}', \ + invalid_test_case(lambda: serialize( + bitlist_case_fn(rng, RandomizationMode.mode_max_count, test_limit))) diff --git a/test_generators/ssz_generic/ssz_bitvector.py b/test_generators/ssz_generic/ssz_bitvector.py new file mode 100644 index 000000000..ab3b6831d --- /dev/null +++ b/test_generators/ssz_generic/ssz_bitvector.py @@ -0,0 +1,30 @@ +from .ssz_test_case import invalid_test_case, valid_test_case +from eth2spec.utils.ssz.ssz_typing import Bitvector +from eth2spec.utils.ssz.ssz_impl import serialize +from random import Random +from eth2spec.debug.random_value import RandomizationMode, get_random_ssz_object + + +def bitvector_case_fn(rng: Random, mode: RandomizationMode, size: int): + return get_random_ssz_object(rng, Bitvector[size], + max_bytes_length=(size + 7) // 8, + max_list_length=size, + mode=mode, chaos=False) + + +def valid_cases(): + rng = Random(1234) + for size in [1, 2, 3, 4, 5, 8, 16, 31, 512, 513]: + for mode in [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max]: + yield f'bitvec_{size}_{mode.to_name()}', valid_test_case(lambda: bitvector_case_fn(rng, mode, size)) + + +def invalid_cases(): + # zero length bitvecors are illegal + yield 'bitvec_0', lambda: b'' + rng = Random(1234) + for (typ_size, test_size) in [(1, 2), (2, 3), (3, 4), (4, 5), + (5, 6), (8, 9), (9, 8), (16, 8), (32, 33), (512, 513)]: + for mode in [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max]: + yield f'bitvec_{typ_size}_{mode.to_name()}_{test_size}', \ + invalid_test_case(lambda: serialize(bitvector_case_fn(rng, mode, test_size))) diff --git a/test_generators/ssz_generic/ssz_boolean.py b/test_generators/ssz_generic/ssz_boolean.py new file mode 100644 index 000000000..4463ab3e2 --- /dev/null +++ b/test_generators/ssz_generic/ssz_boolean.py @@ -0,0 +1,15 @@ +from .ssz_test_case import valid_test_case, invalid_test_case +from eth2spec.utils.ssz.ssz_typing import boolean + + +def valid_cases(): + yield "true", valid_test_case(lambda: boolean(True)) + yield "false", valid_test_case(lambda: boolean(False)) + + +def invalid_cases(): + yield "byte_2", invalid_test_case(lambda: b'\x02') + yield "byte_rev_nibble", invalid_test_case(lambda: b'\x10') + yield "byte_0x80", invalid_test_case(lambda: b'\x80') + yield "byte_full", invalid_test_case(lambda: b'\xff') + diff --git a/test_generators/ssz_generic/ssz_test_case.py b/test_generators/ssz_generic/ssz_test_case.py new file mode 100644 index 000000000..e6993888c --- /dev/null +++ b/test_generators/ssz_generic/ssz_test_case.py @@ -0,0 +1,21 @@ +from eth2spec.utils.ssz.ssz_impl import serialize, hash_tree_root, signing_root +from eth2spec.debug.encode import encode +from eth2spec.utils.ssz.ssz_typing import SSZValue, Container +from typing import Callable + + +def valid_test_case(value_fn: Callable[[], SSZValue]): + def case_fn(): + value = value_fn() + yield "value", "data", encode(value) + yield "serialized", "ssz", serialize(value) + yield "root", "meta", '0x' + hash_tree_root(value).hex() + if isinstance(value, Container): + yield "signing_root", "meta", '0x' + signing_root(value).hex() + return case_fn + + +def invalid_test_case(bytez_fn: Callable[[], bytes]): + def case_fn(): + yield "serialized", "ssz", bytez_fn() + return case_fn diff --git a/test_generators/ssz_generic/ssz_uints.py b/test_generators/ssz_generic/ssz_uints.py new file mode 100644 index 000000000..6fb55279d --- /dev/null +++ b/test_generators/ssz_generic/ssz_uints.py @@ -0,0 +1,34 @@ +from .ssz_test_case import invalid_test_case, valid_test_case +from eth2spec.utils.ssz.ssz_typing import BasicType, uint8, uint16, uint32, uint64, uint128, uint256 +from random import Random +from eth2spec.debug.random_value import RandomizationMode, get_random_ssz_object + + +def uint_case_fn(rng: Random, mode: RandomizationMode, typ: BasicType): + return get_random_ssz_object(rng, typ, + max_bytes_length=typ.byte_len, + max_list_length=1, + mode=mode, chaos=False) + + +def valid_cases(): + rng = Random(1234) + for uint_type in [uint8, uint16, uint32, uint64, uint128, uint256]: + yield f'uint_{uint_type.byte_len * 8}_last_byte_empty', \ + valid_test_case(lambda: uint_type((2 ** ((uint_type.byte_len - 1) * 8)) - 1)) + for variation in range(5): + for mode in [RandomizationMode.mode_random, RandomizationMode.mode_zero, RandomizationMode.mode_max]: + yield f'uint_{uint_type.byte_len * 8}_{mode.to_name()}_{variation}', \ + valid_test_case(lambda: uint_case_fn(rng, mode, uint_type)) + + +def invalid_cases(): + for uint_type in [uint8, uint16, uint32, uint64, uint128, uint256]: + yield f'uint_{uint_type.byte_len * 8}_one_too_high', \ + invalid_test_case(lambda: (2 ** (uint_type.byte_len * 8)).to_bytes(uint_type.byte_len + 1, 'little')) + for uint_type in [uint8, uint16, uint32, uint64, uint128, uint256]: + yield f'uint_{uint_type.byte_len * 8}_one_byte_longer', \ + invalid_test_case(lambda: (2 ** (uint_type.byte_len * 8) - 1).to_bytes(uint_type.byte_len + 1, 'little')) + for uint_type in [uint8, uint16, uint32, uint64, uint128, uint256]: + yield f'uint_{uint_type.byte_len * 8}_one_byte_shorter', \ + invalid_test_case(lambda: (2 ** ((uint_type.byte_len - 1) * 8) - 1).to_bytes(uint_type.byte_len - 1, 'little'))