Zac-HD / hypofuzz

Adaptive fuzzing of Hypothesis tests
https://hypofuzz.com/docs
GNU Affero General Public License v3.0
76 stars 3 forks source link

Hypofuzz not collecting any property-based tests #27

Closed CyberiaResurrection closed 10 months ago

CyberiaResurrection commented 10 months ago

Under pypy matching python 3.9, hypothesis 6.87, and I'm not sure what version of hypofuzz, I can't get hypofuzz to collect any property based tests, even when I explicitly specify them.

frinstance:

(venv2) [alex@localhost traveller_pyroute]$ hypothesis fuzz -- -k test_parse_line_to_star_and_back
Usage: hypothesis fuzz [OPTIONS] [-- PYTEST_ARGS]
Try 'hypothesis fuzz -h' for help.

Error: No property-based tests were collected
(venv2) [alex@localhost traveller_pyroute]$

Here's the test_parse_line_to_star_and_back test, which lives in Tests/Hypothesis/testStar.py, inside the testStar class (sans the 31 examples accumulated running under classic hypothesis)

    """
    Given a regex-matching string that results in a Star object when parsed, that Star should parse cleanly to an input
    line
    """
    @given(from_regex(regex=Star.starline,
                      alphabet='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWYXZ -{}()[]?\'+*'))
    @settings(
        suppress_health_check=[HealthCheck(3), HealthCheck(2)],  # suppress slow-data health check, too-much filtering
        deadline=timedelta(1000))
    def test_parse_line_to_star_and_back(self, s):
        sector = Sector(' Core', ' 0, 0')
        pop_code = 'scaled'
        ru_calc = 'scaled'
        foo = Star.parse_line_into_star(s, sector, pop_code, ru_calc)
        assume(foo is not None)
        foo.trim_self_ownership()
        foo.trim_self_colonisation()
        self.assertIsNotNone(foo._hash, "Hash not calculated for original star")

        foo.index = 0
        foo.allegiance_base = foo.alg_base_code
        self.assertTrue(foo.is_well_formed())

        parsed_line = foo.parse_to_line()
        self.assertIsNotNone(parsed_line)
        self.assertLessEqual(80, len(parsed_line), "Round-trip line unexpectedly short")

        nu_foo = Star.parse_line_into_star(parsed_line, sector, pop_code, ru_calc)
        self.assertTrue(isinstance(nu_foo, Star), "Round-trip line did not re-parse")
        nu_foo.index = 0
        nu_foo.allegiance_base = nu_foo.alg_base_code
        self.assertTrue(nu_foo.is_well_formed(log=False))
        self.assertIsNotNone(nu_foo._hash, "Hash not calculated for re-parsed star")

        self.assertEqual(foo, nu_foo, "Re-parsed star not __eq__ to original star.  Hypothesis input: " + s + '\n')
        self.assertEqual(
            str(foo.tradeCode),
            str(nu_foo.tradeCode),
            "Re-parsed trade codes not equal to original trade codes.  Hypothesis input: " + s + '\n'
        )
        self.assertEqual(
            len(foo.star_list),
            len(nu_foo.star_list),
            "Re-parsed star list different length to original star list.  Hypothesis input: " + s + '\n'
        )

        nu_parsed_line = nu_foo.parse_to_line()
        self.assertEqual(
            parsed_line,
            nu_parsed_line,
            "New reparsed starline does not equal original parse-to-line output.  Hypothesis input: " + s + '\n'
        )

Running that test thru pytest results in pytest seeing and collecting it:

(venv2) [alex@localhost traveller_pyroute]$ pytest -k test_parse_line_to_star_and_back
========================================================================================================= test session starts =========================================================================================================
platform linux -- Python 3.9.17[pypy-7.3.12-final], pytest-7.4.2, pluggy-1.2.0
Using --randomly-seed=2622186668
rootdir: /home/alex/gitstuf/traveller_pyroute
configfile: pytest.ini
testpaths: Tests, Tests/Pathfinding, Tests/Position
plugins: randomly-3.15.0, hypothesis-6.87.3, console-scripts-1.4.1, subtests-0.11.0
collected 225 items / 224 deselected / 1 selected                                                                                                                                                                                     

Tests/Hypothesis/testStar.py 

Before I get too wound up, I'd appreciate help figuring/ruling out what I've done wrong.

CyberiaResurrection commented 10 months ago

Running pytest to collect the tests directly results in:

(venv2) [alex@localhost traveller_pyroute]$ pytest --collect-only -m=hypothesis --pythonwarnings=ignore::pytest.PytestAssertRewriteWarning
========================================================================================================= test session starts =========================================================================================================
platform linux -- Python 3.9.17[pypy-7.3.12-final], pytest-7.4.2, pluggy-1.2.0
Using --randomly-seed=117694285
rootdir: /home/alex/gitstuf/traveller_pyroute
configfile: pytest.ini
testpaths: Tests, Tests/Pathfinding, Tests/Position
plugins: randomly-3.15.0, hypothesis-6.87.3, console-scripts-1.4.1, subtests-0.11.0
collected 236 items / 225 deselected / 11 selected                                                                                                                                                                                    

<Module Tests/Hypothesis/testSector.py>
  <UnitTestCase testSector>
    <TestCaseFunction test_create_sector>
<Module Tests/Hypothesis/testTradeCodes.py>
  <UnitTestCase testTradeCodes>
    <TestCaseFunction test_parse_text_to_trade_code>
<Module Tests/Hypothesis/testStar.py>
  <UnitTestCase testStar>
    <TestCaseFunction test_parse_line_to_star_and_back>
    <TestCaseFunction test_parse_line_to_star>
    <TestCaseFunction test_split_stellar_data>
<Module Tests/Hypothesis/testDeltaStar.py>
  <UnitTestCase testDeltaStar>
    <TestCaseFunction test_canonicalise_barren_worlds>
    <TestCaseFunction test_canonicalise_cx_on_non_barren_worlds>
    <TestCaseFunction test_canonicalise_ex_on_non_barren_worlds>
    <TestCaseFunction test_canonicalise_invalid_trade_codes>
    <TestCaseFunction test_canonicalise_missing_trade_codes>
    <TestCaseFunction test_check_canonicalisation>

I've trimmed the warnings summary. So how come Pytest itself is collecting hypothesis tests fine, but Hypofuzz isn't?

Zac-HD commented 10 months ago

I suspect we're misidentifying the self argument as a place that we need a fixture:

https://github.com/Zac-HD/hypofuzz/blob/21806b7e85322ae22826ed8cc68fd8a9ca639f03/src/hypofuzz/interface.py#L27-L40

probably classes just need special handling? They're notably more fiddly to set up though, since you have to manage instances, setup and teardown methods, etc. ...

Zac-HD commented 10 months ago

Whoops, no, this is just that the private Hypothesis internals which HypoFuzz relies on have changed (as they're entitled to do), and so the fuzzer needs to be updated too. Tracking in #29.