implement new ssz generic tests

This commit is contained in:
protolambda 2019-07-27 16:45:30 +02:00
parent c628c8187b
commit 5b956b3d26
No known key found for this signature in database
GPG Key ID: EC89FDBB2B4C7623
8 changed files with 135 additions and 136 deletions

View File

@ -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__":

View File

@ -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

View File

@ -1,4 +1,4 @@
eth-utils==1.6.0
../../test_libs/gen_helpers
../../test_libs/config_helpers
ssz==0.1.0a2
../../test_libs/pyspec

View File

@ -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)))

View File

@ -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)))

View File

@ -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')

View File

@ -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

View File

@ -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'))