Ejemplo n.º 1
0
def replay(arguments):
    root = Path(".")

    with database_open(root) as db:
        command = db[lexode.pack((0, "command"))]

    command = lexode.unpack(command)
    command = dict(command)
    seed = command.pop("seed")
    random.seed(seed)
    command = command.pop("command")

    alpha, max_workers = check_tests(root, seed, arguments, command)

    with database_open(root) as db:
        while True:
            uids = (lexode.unpack(k)[1] for k, v in db[lexode.pack([2]):]
                    if v == b"\x00")
            uids = sorted(
                uids,
                key=functools.partial(mutation_diff_size, db),
                reverse=True,
            )
            if not uids:
                log.info("No mutation failures 👍")
                sys.exit(0)
            while uids:
                uid = uids.pop(0)
                replay_mutation(db, uid, alpha, seed, max_workers, command)
Ejemplo n.º 2
0
async def play(loop, arguments):
    root = Path(".")

    seed = arguments["--randomly-seed"] or int(time.time())
    log.info("Using random seed: {}".format(seed))
    random.seed(seed)

    alpha, max_workers = check_tests(root, seed, arguments)

    with database_open(root, recreate=True) as db:
        # store arguments used to execute command
        if arguments["TEST-COMMAND"]:
            command = list(arguments["TEST-COMMAND"])
        else:
            command = list(PYTEST)
            command += arguments["<file-or-directory>"]
        command = dict(
            command=command,
            seed=seed,
        )
        value = list(command.items())
        db[lexode.pack((0, "command"))] = lexode.pack(value)

        # let's create mutations!
        count = await play_create_mutations(loop, root, db, max_workers,
                                            arguments)
        # Let's run tests against mutations!
        await play_mutations(loop, db, seed, alpha, count, max_workers,
                             arguments)
Ejemplo n.º 3
0
def replay_mutation(db, uid, alpha, seed, max_workers, command):
    log.info("* Use Ctrl+C to exit.")

    command = list(command)
    command.append("--randomly-seed={}".format(seed))

    max_workers = 1
    if max_workers > 1:
        command.append("--numprocesses={}".format(max_workers))
    timeout = alpha * 2

    while True:
        ok = mutation_pass((command, uid, timeout))
        if not ok:
            mutation_show(uid.hex)
            msg = "* Type 'skip' to go to next mutation or just enter to retry."
            log.info(msg)
            skip = input().startswith("s")
            if skip:
                db[lexode.pack([2, uid])] = b"\x01"
                return
            # Otherwise loop to re-test...
        else:
            del db[lexode.pack([2, uid])]
            return
Ejemplo n.º 4
0
        def on_mutations_created(items):
            nonlocal total

            progress.update()
            total += len(items)
            for path, delta in items:
                # TODO: replace ULID with a content addressable hash.
                uid = ULID().to_uuid()
                # delta is a compressed unified diff
                db[lexode.pack([1, uid])] = lexode.pack([path, delta])
Ejemplo n.º 5
0
def mutation_pass(args):  # TODO: rename
    command, uid, timeout = args
    command = command + ["--mutation={}".format(uid.hex)]
    out = run(command, timeout=timeout, silent=True)
    if out == 0:
        msg = "no error with mutation: {} ({})"
        log.trace(msg, " ".join(command), out)
        with database_open(".") as db:
            db[lexode.pack([2, uid])] = b"\x00"
        return False
    else:
        # TODO: pass root path...
        with database_open(".") as db:
            del db[lexode.pack([2, uid])]
        return True
Ejemplo n.º 6
0
def mutation_show(uid):
    uid = UUID(hex=uid)
    log.info("mutation show {}", uid.hex)
    log.info("")
    with database_open(".") as db:
        path, diff = lexode.unpack(db[lexode.pack([1, uid])])
    diff = zstd.decompress(diff).decode("utf8")

    terminal256 = pygments.formatters.get_formatter_by_name("terminal256")
    python = pygments.lexers.get_lexer_by_name("python")

    print(diff)

    for line in diff.split("\n"):
        if line.startswith("+++"):
            delta = colored("+++", "green", attrs=["bold"])
            highlighted = pygments.highlight(line[3:], python, terminal256)
            log.info(delta + highlighted.rstrip())
        elif line.startswith("---"):
            delta = colored("---", "red", attrs=["bold"])
            highlighted = pygments.highlight(line[3:], python, terminal256)
            log.info(delta + highlighted.rstrip())
        elif line.startswith("+"):
            delta = colored("+", "green", attrs=["bold"])
            highlighted = pygments.highlight(line[1:], python, terminal256)
            log.info(delta + highlighted.rstrip())
        elif line.startswith("-"):
            delta = colored("-", "red", attrs=["bold"])
            highlighted = pygments.highlight(line[1:], python, terminal256)
            log.info(delta + highlighted.rstrip())
        else:
            highlighted = pygments.highlight(line, python, terminal256)
            log.info(highlighted.rstrip())
Ejemplo n.º 7
0
def mutation_apply(uid):
    uid = UUID(hex=uid)
    with database_open(".") as db:
        path, diff = lexode.unpack(db[lexode.pack([1, uid])])
    diff = zstd.decompress(diff).decode("utf8")
    with open(path, "r") as f:
        source = f.read()
    patched = patch(diff, source)
    with open(path, "w") as f:
        f.write(patched)
Ejemplo n.º 8
0
def mutation_list():
    with database_open(".") as db:
        uids = ((lexode.unpack(k)[1], v) for k, v in db[lexode.pack([2]):])
        uids = sorted(uids,
                      key=lambda x: mutation_diff_size(db, x[0]),
                      reverse=True)
    if not uids:
        log.info("No mutation failures 👍")
        sys.exit(0)
    for (uid, type) in uids:
        log.info("{}\t{}".format(uid.hex,
                                 "skipped" if type == b"\x01" else ""))
Ejemplo n.º 9
0
def install_module_loader(uid):
    db = LSM(".mutation.okvslite")

    mutation_show(uid.hex)

    path, diff = lexode.unpack(db[lexode.pack([1, uid])])
    diff = zstd.decompress(diff).decode("utf8")

    with open(path) as f:
        source = f.read()

    patched = patch(diff, source)

    import imp

    components = path[:-3].split("/")

    while components:
        for pythonpath in sys.path:
            filepath = os.path.join(pythonpath, "/".join(components))
            filepath += ".py"
            ok = os.path.exists(filepath)
            if ok:
                module_path = ".".join(components)
                break
        else:
            components.pop()
            continue
        break
    if module_path is None:
        raise Exception("sys.path oops!")

    patched_module = imp.new_module(module_path)
    try:
        exec(patched, patched_module.__dict__)
    except Exception:
        # TODO: syntaxerror, do not produce those mutations
        exec("", patched_module.__dict__)

    sys.modules[module_path] = patched_module
Ejemplo n.º 10
0
def mutation_diff_size(db, uid):
    _, diff = lexode.unpack(db[lexode.pack([1, uid])])
    out = len(zstd.decompress(diff))
    return out
Ejemplo n.º 11
0
async def play_mutations(loop, db, seed, alpha, total, max_workers, arguments):
    # prepare to run tests against mutations
    command = list(arguments["TEST-COMMAND"] or PYTEST)
    command.append("--randomly-seed={}".format(seed))
    command.extend(arguments["<file-or-directory>"])

    eta = humanize(alpha * total / max_workers)
    log.success("It will take at most {} to run the mutations", eta)

    timeout = alpha * 2
    uids = db[lexode.pack([1]):lexode.pack([2])]
    uids = ((command, lexode.unpack(key)[1], timeout) for (key, _) in uids)

    # sampling
    sampling = arguments["--sampling"]
    sampler, total = sampling_setup(sampling, total)
    uids = sampler(uids)

    step = 10

    gamma = time.perf_counter()

    remaining = total

    log.info("Testing mutations in progress...")

    with tqdm(total=100) as progress:

        def on_progress(_):
            nonlocal remaining
            nonlocal step
            nonlocal gamma

            remaining -= 1

            if (remaining % step) == 0:

                percent = 100 - ((remaining / total) * 100)
                now = time.perf_counter()
                delta = now - gamma
                eta = (delta / step) * remaining

                progress.update(int(percent))
                progress.set_description("ETA {}".format(humanize(eta)))

                msg = "Mutation tests {:.2f}% done..."
                log.debug(msg, percent)
                log.debug("ETA {}...", humanize(eta))

                for speed in [10_000, 1_000, 100, 10, 1]:
                    if total // speed == 0:
                        continue
                    step = speed
                    break

                gamma = time.perf_counter()

        with timeit() as delta:
            with futures.ThreadPoolExecutor(max_workers=max_workers) as pool:
                await pool_for_each_par_map(loop, pool, on_progress,
                                            mutation_pass, uids)

        errors = len(list(db[lexode.pack([2]):lexode.pack([3])]))