|
| 1 | +"""Property-based tests for Black. |
| 2 | +
|
| 3 | +By Zac Hatfield-Dodds, based on my Hypothesmith tool for source code |
| 4 | +generation. You can run this file with `python`, `pytest`, or (soon) |
| 5 | +a coverage-guided fuzzer I'm working on. |
| 6 | +""" |
| 7 | + |
| 8 | +import hypothesmith |
| 9 | +from hypothesis import HealthCheck, given, settings, strategies as st |
| 10 | + |
| 11 | +import black |
| 12 | + |
| 13 | + |
| 14 | +# This test uses the Hypothesis and Hypothesmith libraries to generate random |
| 15 | +# syntatically-valid Python source code and run Black in odd modes. |
| 16 | +@settings( |
| 17 | + max_examples=1000, # roughly 1k tests/minute, or half that under coverage |
| 18 | + derandomize=True, # deterministic mode to avoid CI flakiness |
| 19 | + deadline=None, # ignore Hypothesis' health checks; we already know that |
| 20 | + suppress_health_check=HealthCheck.all(), # this is slow and filter-heavy. |
| 21 | +) |
| 22 | +@given( |
| 23 | + # Note that while Hypothesmith might generate code unlike that written by |
| 24 | + # humans, it's a general test that should pass for any *valid* source code. |
| 25 | + # (so e.g. running it against code scraped of the internet might also help) |
| 26 | + src_contents=hypothesmith.from_grammar() | hypothesmith.from_node(), |
| 27 | + # Using randomly-varied modes helps us to exercise less common code paths. |
| 28 | + mode=st.builds( |
| 29 | + black.FileMode, |
| 30 | + line_length=st.just(88) | st.integers(0, 200), |
| 31 | + string_normalization=st.booleans(), |
| 32 | + is_pyi=st.booleans(), |
| 33 | + ), |
| 34 | +) |
| 35 | +def test_idempotent_any_syntatically_valid_python(src_contents, mode): |
| 36 | + # Before starting, let's confirm that the input string is valid Python: |
| 37 | + compile(src_contents, "<string>", "exec") # else the bug is in hypothesmith |
| 38 | + |
| 39 | + # Then format the code... |
| 40 | + try: |
| 41 | + dst_contents = black.format_str(src_contents, mode=mode) |
| 42 | + except black.InvalidInput: |
| 43 | + # This is a bug - if it's valid Python code, as above, black should be |
| 44 | + # able to code with it. See issues #970, #1012, #1358, and #1557. |
| 45 | + # TODO: remove this try-except block when issues are resolved. |
| 46 | + return |
| 47 | + |
| 48 | + # And check that we got equivalent and stable output. |
| 49 | + black.assert_equivalent(src_contents, dst_contents) |
| 50 | + black.assert_stable(src_contents, dst_contents, mode=mode) |
| 51 | + |
| 52 | + # Future test: check that pure-python and mypyc versions of black |
| 53 | + # give identical output for identical input? |
| 54 | + |
| 55 | + |
| 56 | +if __name__ == "__main__": |
| 57 | + test_idempotent_any_syntatically_valid_python() |
0 commit comments