def test_run_ghostwriter_roundtrip(): # This test covers the whole lifecycle: first, we get the default code. # The first argument is unknown, so we fail to draw from st.nothing() source_code = ghostwriter.roundtrip(json.dumps, json.loads) with pytest.raises(Unsatisfiable): get_test_function(source_code)() # Replacing that nothing() with a strategy for JSON allows us to discover # two possible failures: `nan` is not equal to itself, and if dumps is # passed allow_nan=False it is a ValueError to pass a non-finite float. source_code = source_code.replace( "st.nothing()", "st.recursive(st.one_of(st.none(), st.booleans(), st.floats(), st.text()), " "lambda v: st.lists(v, max_size=2) | st.dictionaries(st.text(), v, max_size=2)" ", max_leaves=2)", ) try: get_test_function(source_code)() except (AssertionError, ValueError, MultipleFailures): pass # Finally, restricting ourselves to finite floats makes the test pass! source_code = source_code.replace( "st.floats()", "st.floats(allow_nan=False, allow_infinity=False)") get_test_function(source_code)()
( "timsort_idempotent_asserts", ghostwriter.idempotent(timsort, except_=AssertionError), ), ("eval_equivalent", ghostwriter.equivalent(eval, ast.literal_eval)), ("sorted_self_equivalent", ghostwriter.equivalent(sorted, sorted, sorted)), ("addition_op_magic", ghostwriter.magic(add)), ("addition_op_multimagic", ghostwriter.magic(add, operator.add, numpy.add)), ("division_fuzz_error_handler", ghostwriter.fuzz(divide)), ( "division_binop_error_handler", ghostwriter.binary_operation(divide, identity=1), ), ( "division_roundtrip_error_handler", ghostwriter.roundtrip(divide, operator.mul), ), ( "division_roundtrip_arithmeticerror_handler", ghostwriter.roundtrip(divide, operator.mul, except_=ArithmeticError), ), ( "division_roundtrip_typeerror_handler", ghostwriter.roundtrip(divide, operator.mul, except_=TypeError), ), ( "division_operator", ghostwriter.binary_operation( operator.truediv, associative=False, commutative=False ), ),
@pytest.mark.parametrize( "cli,code", [ # Passing one argument falls back to one-argument tests ("--equivalent re.compile", lambda: fuzz(re.compile)), ("--roundtrip sorted", lambda: idempotent(sorted)), # For multiple arguments, they're equivalent to the function call ( "--equivalent eval ast.literal_eval", lambda: equivalent(eval, ast.literal_eval), ), ( "--roundtrip json.loads json.dumps --except ValueError", lambda: roundtrip(json.loads, json.dumps, except_=ValueError), ), # Imports submodule (importlib.import_module passes; __import__ fails) ("hypothesis.errors.StopTest", lambda: fuzz(StopTest)), # Search for identity element does not print e.g. "You can use @seed ..." ("--binary-op operator.add", lambda: binary_operation(operator.add)), ], ) def test_cli_python_equivalence(cli, code): result = subprocess.run( "hypothesis write " + cli, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True, universal_newlines=True, )