Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

Commit

Permalink
Update to latest SSZ with SOS and add Boolean test generators
Browse files Browse the repository at this point in the history
  • Loading branch information
pipermerriam committed Apr 11, 2019
1 parent 78223b9 commit fa2f35b
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 0 deletions.
25 changes: 25 additions & 0 deletions ssz/_utils.py
Original file line number Diff line number Diff line change
@@ -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))
122 changes: 122 additions & 0 deletions ssz/bool_test_generators.py
Original file line number Diff line number Diff line change
@@ -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,
)
5 changes: 5 additions & 0 deletions ssz/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Mapping,
Sequence,
)
import copy

from eth_utils import (
encode_hex,
Expand Down Expand Up @@ -78,6 +79,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:
Expand Down
134 changes: 134 additions & 0 deletions ssz/uint_test_generators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import random

from eth_utils import (
to_tuple,
)

import ssz
from ssz.sedes import (
UInt,
)

from _utils import (
seed,
get_random_bytes,
)
from renderers import (
render_test,
render_test_case,
)


BIT_SIZES = [i for i in range(8, 512 + 1, 8)]
RANDOM_TEST_CASES_PER_BIT_SIZE = 10
RANDOM_TEST_CASES_PER_LENGTH = 3


@seed
def generate_uint_bounds_test():
test_cases = generate_uint_bounds_test_cases() + generate_uint_out_of_bounds_test_cases()

return render_test(
title="UInt Bounds",
summary="Integers right at or beyond the bounds of the allowed value range",
fork="phase0-0.2.0",
test_cases=test_cases,
)


@seed
def generate_uint_random_test():
test_cases = generate_random_uint_test_cases()

return render_test(
title="UInt Random",
summary="Random integers chosen uniformly over the allowed value range",
fork="phase0-0.2.0",
test_cases=test_cases,
)


@seed
def generate_uint_wrong_length_test():
test_cases = generate_uint_wrong_length_test_cases()

return render_test(
title="UInt Wrong Length",
summary="Serialized integers that are too short or too long",
fork="phase0-0.2.0",
test_cases=test_cases,
)


@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)
yield render_test_case(
sedes=sedes,
valid=True,
value=value,
serial=serial,
tags=tags,
)


@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({
0,
sedes.length // 2,
sedes.length - 1,
sedes.length + 1,
sedes.length * 2,
})
for length in lengths:
for _ in range(RANDOM_TEST_CASES_PER_LENGTH):
yield render_test_case(
sedes=sedes,
valid=False,
serial=get_random_bytes(length),
tags=tags,
)


@to_tuple
def generate_uint_bounds_test_cases():
common_tags = ("atomic", "uint")
for bit_size in BIT_SIZES:
sedes = UInt(bit_size)

for value, tag in ((0, "uint_lower_bound"), (2 ** bit_size - 1, "uint_upper_bound")):
serial = ssz.encode(value, sedes)
yield render_test_case(
sedes=sedes,
valid=True,
value=value,
serial=serial,
tags=common_tags + (tag,),
)


@to_tuple
def generate_uint_out_of_bounds_test_cases():
common_tags = ("atomic", "uint")
for bit_size in BIT_SIZES:
sedes = UInt(bit_size)

for value, tag in ((-1, "uint_underflow"), (2 ** bit_size, "uint_overflow")):
yield render_test_case(
sedes=sedes,
valid=False,
value=value,
tags=common_tags + (tag,),
)

0 comments on commit fa2f35b

Please sign in to comment.