Skip to content
Snippets Groups Projects
Commit 98429e78 authored by Filip Kučerák's avatar Filip Kučerák
Browse files

base58 example

parent b66a0f3e
No related branches found
No related tags found
No related merge requests found
KDTesting
import sys
from typing import Generator, List
from kdtesting import Test, RefImplTester, stdout_equality, ListA, \
vector_gen, oneof_gen, Arbitrary
BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
class Base58Block(Arbitrary[List[str]]):
def __init__(self):
# I created a new generator using vector_gen and oneof_gen (check the
# kdtestin.py for details about value generators
super().__init__(vector_gen(
oneof_gen(BASE58_ALPHABET), 6))
def shrink(self, value: str) -> Generator[str, None, None]:
# You can shrink to nothing if you can't find a good way to shrink your
# arbitrary
yield from ()
def to_str(self, value: List[str]) -> str:
# vector_gen will create not str but list of chars (List[str]) so let's
# join them
return ''.join(value)
class Base58Command(ListA[List[str]]):
def __init__(self, min_size: int, max_size: int):
# I just told the List Arbitrary to use Base58Block to generate list
# elements
super().__init__(Base58Block(), min_size, max_size)
def to_str(self, value: List[List[str]]):
# I first have apply to_str on each element, then join them using ''
# If you are defining list of commands, you can use Command and
# CommandSequence from .py or simply define your own arbitraries, then
# join different elements using '\n'
stringified = [self.arbitrary.to_str(val) for val in value]
return ''.join(stringified)
if __name__ == "__main__":
# Capturing the arguments from the system
# example run would be:
# python3 base58.py testing reference
[program_tst, program_exp] = sys.argv[1:]
# Note that the last argument is verbosity, 10 is log everything
tester = RefImplTester(program_tst, program_exp, 10)
# Adding flag for decoding
tester.set_flags(['-d'])
# Adding equality output test (works both for string output and bytes
# output)
tester.add_output_test(stdout_equality())
# Scales the tests
for i in range(1, 10):
base58_command = Base58Command(i * 10, i * 10 * 2)
tester.add_test(Test(f"base58_test_{i}", base58_command))
# Runs the tests and shrinks them (in this example it would remove blocks
# from the base58 command
reduced_tests = tester.run()
# If some tests failed it will both print and save the shrunk tests
for test, shrinks in reduced_tests:
print(test.name, shrinks)
print(test.to_str())
tester.save_shrunk(reduced_tests)
# Save all tests in they starting form
tester.save_tests()
......@@ -52,6 +52,12 @@ def constant_gen(const: T) -> Callable[[], T]:
return generator
def oneof_gen(constants: List[T]) -> Callable[[], T]:
def generator() -> T:
return random.choice(constants)
return generator
def vector_gen(gen: Callable[[], T], size: int) -> Callable[[], List[T]]:
def generator() -> List[T]:
res = []
......@@ -140,6 +146,14 @@ class ConstantA(Arbitrary[T]):
yield from ()
class OneOfA(Arbitrary[T]):
def __init__(self, constants: List[T]):
super().__init__(oneof_gen(constants))
def shrink(self, value: T) -> Generator[T, None, None]:
yield from ()
class TupleA(Arbitrary[Tuple[Generatable, ...]]):
"""
Tuple arbitrary returns values combined from the provided arbitraries,
......@@ -210,6 +224,9 @@ class ListA(Arbitrary[List[T]]):
# result = value[:i] + [shrunk_value] + value[i + 1:]
# yield result
def to_str(self, value: List[T]) -> str:
return str([self.arbitrary.to_str(val) for val in value])
class ChoiceA(Arbitrary[Tuple[int, T]]):
"""
......@@ -342,42 +359,47 @@ def stdout_processed_eqality(test_preprocessor: Callable[[str], str],
def stdout_equality() -> ProcessOutputTest:
def res_tester(test_out: ProcessOutput, exp_out: ProcessOutput) -> bool:
return str_id(test_out.stdout) == str_id(exp_out.stdout)
return test_out.stdout == exp_out.stdout
return res_tester
class RefImplTester(Tester):
def __init__(self, program_a: str, program_b: str, verbosity: int = 5,
def __init__(self, program_tested: str, program_referenced: str,
verbosity: int = 5,
stdout_preprocessor: Callable[[str], str] = lambda x: x) \
-> None:
super().__init__(verbosity)
self.program_a = program_a
self.program_b = program_b
self.program_tested = program_tested
self.program_referenced = program_referenced
self.flags: List[str] = []
self.output_tests: List[ProcessOutputTest] = []
def set_flags(self, flags: List[str]) -> None:
self.flags = flags
def run_test(self, test: Test[Generatable]) -> bool:
self.log(5, "test")
self.log(5, "run_test")
test_string = test.to_str()
self.log(6, test_string)
test_program = subprocess.run(
[self.program_a],
[self.program_tested] + self.flags,
capture_output=True,
text=True,
input=test_string)
input=str.encode(test_string))
exp_program = subprocess.run(
[self.program_b],
[self.program_referenced] + self.flags,
capture_output=True,
text=True,
input=test_string)
input=str.encode(test_string))
res = True
for process_test in self.output_tests:
res &= process_test(test_program, exp_program)
res = res and process_test(test_program, exp_program)
self.log(5, f"test result: {('pass' if res else 'fail')}")
return res
def add_output_test(self, test: ProcessOutputTest) -> None:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment