File: mutmut_workflow.py

package info (click to toggle)
python-executing 2.2.0-0.3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 11,860 kB
  • sloc: python: 10,235; sh: 48; makefile: 10
file content (99 lines) | stat: -rw-r--r-- 3,099 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import subprocess as sp
import os

import shelve
import time

files = ["executing/_position_node_finder.py", "tests/deadcode.py"]

py_version = "py311"


def mutmut_run(num: str | None = None):
    cmd = [
        "mutmut",
        "run",
        "--paths-to-mutate",
        ",".join(files),
        "--runner",
        f"tox -e {py_version} -- --ff",
        *([num] if num is not None else []),
    ]
    print(">", *cmd)
    sp.run(cmd)


def survived() -> set[str]:
    """
    set of all the ids which survived
    """
    nums = sp.check_output(["mutmut", "result-ids", "survived"]).decode().split()
    if not all(num.isnumeric() for num in nums):
        return set()
    return set(nums)


def main():
    # Mutmut is not really build for this kind of integration.
    # This is the reason for some weird code here.

    # make sure that there are no known bugs
    sp.check_call(["git", "checkout", *files])
    sp.check_call(["tox", "-e", py_version])

    # done.db contains all mutmut ids which have been already tried to fix.
    # Useful if this is run multiple times
    with shelve.open("done.db") as done:
        while True:
            todo = survived() - done.keys()

            if not todo:
                # mutmut has to run without id first
                # It also only checks for the first 100 untested mutations,
                # `not todo` does not imply that there is nothing more to test
                mutmut_run()
                todo = survived() - done.keys()

            if not todo:
                break

            print("survived mutations todo:", todo)
            for num in todo:
                # make sure to base this run on clean files
                sp.check_call(["git", "checkout", *files])

                # applies the mutated state to the files and runs the mutation
                mutmut_run(num)

                # skip if the mutation has not survived (is covered by some tests)
                if num not in survived():
                    continue

                header_diff = sp.check_output(["mutmut", "show", num]).decode()

                sp.check_call(["mutmut", "apply", num])

                # generate a sample for the mutmut run
                sp.check_call(
                    ["tox", "-e", f"generate_small_sample-{py_version}"],
                    env=os.environ | {"MUTMUT_HEADER": header_diff},
                )

                sp.check_call(["git", "checkout", *files])

                # The normal tests should pass.
                # There are some cases where generate_small_sample found a different bug
                # and the tests failed.
                # this script will fail in this case and the bug should be fixed by the developer
                result = sp.run(["tox", "-e", py_version])
                if result.returncode != 0:
                    print(
                        "generate_small_sample found a different bug that should be fixed by the developer"
                    )
                    exit(1)

                done[num] = True


if __name__ == "__main__":
    main()