Ejemplo n.º 1
0
def test_prints_debug_information(capsys):
    reducer = Reducer(initial=bytes(10),
                      predicate=lambda x: len(x) > 3,
                      parallelism=1,
                      debug=True)
    reducer.run()
    captured = capsys.readouterr()
    assert not captured.out
    assert len(captured.err.splitlines()) > 2
Ejemplo n.º 2
0
def test_removes_all_elements(parallelism):
    reducer = Reducer(
        initial=b"01" * 100,
        predicate=lambda t: t.count(b"0") == 100,
        parallelism=parallelism,
    )

    reducer.run()

    assert reducer.target == b"0" * 100
Ejemplo n.º 3
0
def test_runs_to_fixation(ls):
    ordered = bytes(sorted(ls))

    reducer = Reducer(
        initial=bytes(ls),
        predicate=lambda t: t and ordered.startswith(bytes(sorted(t))),
        parallelism=1,
    )

    reducer.run()

    assert reducer.target == ordered[:1]
Ejemplo n.º 4
0
def test_reduces_to_min_size(initial, n, parallelism, method):
    assume(n <= len(initial))
    reducer = Reducer(
        initial=initial,
        predicate=lambda t: len(t) >= n,
        parallelism=parallelism,
    )

    getattr(reducer, method)()

    assert len(reducer.target) == n
Ejemplo n.º 5
0
def test_explore_arbitrary_predicate(b, data):
    results = {}

    best = b

    def test(t):
        nonlocal best
        if not t:
            return False
        if t == b:
            return True
        try:
            return results[t]
        except KeyError:
            pass

        result = data.draw(st.booleans())
        results[t] = result
        if result:
            best = min(best, t, key=sort_key)
        return result

    class FakeRandom(object):
        def randrange(self, start, stop):
            if stop == start + 1:
                return start
            return data.draw(st.integers(start, stop - 1),
                             label=f"randrange({start}, {stop})")

    reducer = Reducer(
        initial=b,
        predicate=test,
        parallelism=1,
        random=FakeRandom(),
    )

    reducer.run()

    assert sort_key(reducer.target) <= sort_key(b)
    assert reducer.target == best
Ejemplo n.º 6
0
def test_runs_callbacks():
    results = []

    reducer = Reducer(initial=bytes(10),
                      predicate=lambda x: len(x) > 3,
                      parallelism=1,
                      debug=True)

    reducer.on_improve(results.append)

    reducer.run()

    assert results[-1] == reducer.target
Ejemplo n.º 7
0
def test_argument_validation(initial, test):
    with pytest.raises(InvalidArguments):
        Reducer(initial=initial, predicate=test)
Ejemplo n.º 8
0
def reducer(
    debug,
    test,
    filename,
    timeout,
    working_dir,
    parallelism,
    seed,
    input_mode,
    lexical,
    also_interesting,
    slow_mode,
    replace,
):

    if not working_dir:
        file_basename = os.path.basename(filename)
        if file_basename == "-":
            file_basename = "stdin"

        base = os.path.join(".shrinkray", file_basename)
        try:
            os.makedirs(base)
        except FileExistsError:
            pass

        i = 1

        while True:
            k = find_integer(
                lambda k: os.path.exists(os.path.join(base, str(i + k))))
            i += k + 1
            name = os.path.join(base, str(i))

            try:
                os.mkdir(name)
            except FileExistsError:
                continue
            working_dir = name
            break

        print(f"Saving results in {working_dir}")
    else:
        try:
            os.makedirs(working_dir)
        except FileExistsError:
            print(
                "Working directory already exists and would be overwritten by reduction",
                file=sys.stderr,
            )
            sys.exit(1)

    interesting_dir = os.path.join(working_dir, "interesting")
    reduction_dir = os.path.join(working_dir, "reductions")

    os.makedirs(reduction_dir)

    if input_mode == "file" and filename == "-":
        raise click.UsageError(
            "Cannot combine --input-mode=file with reading from stdin.")

    if filename == "-":
        initial = sys.stdin.buffer.read()
    else:
        with open(filename, "rb") as o:
            initial = o.read()

    if replace:
        if filename == "-":
            raise click.UsageError(
                "Cannot combine --replace with reading from stdin.")
        with open(filename + ".original", "wb") as o:
            o.write(initial)

    if debug:

        def dump_trace(signum, frame):
            traceback.print_stack()

        signal.signal(signal.SIGQUIT, dump_trace)

    basename = os.path.basename(filename)

    first_call = True

    def classify_data(string):
        nonlocal first_call

        should_print = debug and first_call and False

        first_call = False

        with tempfile.TemporaryDirectory() as d:
            sp = subprocess.Popen(
                test,
                stdin=subprocess.PIPE,
                stdout=sys.stdout if should_print else subprocess.DEVNULL,
                stderr=sys.stderr if should_print else subprocess.DEVNULL,
                universal_newlines=False,
                preexec_fn=os.setsid,
                cwd=d,
            )

            original_string = string

            if input_mode == "file":
                with open(os.path.join(d, basename), "wb") as o:
                    o.write(string)
                string = b""

            try:
                sp.communicate(string, timeout=timeout)
            except subprocess.TimeoutExpired:
                return False
            finally:
                interrupt_wait_and_kill(sp)

            if 0 != sp.returncode == also_interesting:
                key = hashlib.sha1(string).hexdigest()[:10]

                if filename == "-":
                    name = f"result-{key}"
                else:
                    base, ext = os.path.splitext(os.path.basename(filename))
                    name = f"{base}-{key}{ext}"

                try:
                    os.mkdir(interesting_dir)
                except FileExistsError:
                    pass

                path = os.path.join(interesting_dir, name)

                try:
                    with open(path, "xb") as o:
                        o.write(original_string)
                    if debug:
                        print(f"Recording interesting variant in {path}",
                              file=sys.stderr)
                except FileExistsError:
                    pass

            return sp.returncode == 0

    if replace:
        target = os.path.abspath(filename)
    elif filename == "-":
        target = os.path.join(working_dir, "result")
    else:
        target = os.path.join(working_dir, os.path.basename(filename))

    timeout *= 10
    if timeout <= 0:
        timeout = None

    if parallelism <= 0:
        parallelism = max(1, cpu_count() - 1)

    try:
        reducer = Reducer(
            initial,
            classify_data,
            debug=debug,
            parallelism=parallelism,
            random=Random(seed),
            lexical=lexical,
            slow=slow_mode,
        )
    except InvalidArguments as e:
        raise click.UsageError(e.args[0])

    pb = None

    prev = len(initial)

    reduction_count = 0

    @reducer.on_improve
    def _(s):
        nonlocal reduction_count
        reduction_count += 1

        with open(
                os.path.join(reduction_dir,
                             f"{reduction_count}-" + file_basename),
                "bx") as o:
            o.write(s)

        if pb is not None:
            nonlocal prev
            pb.update(prev - len(s))
            prev = len(s)
        with open(target, "wb") as o:
            o.write(s)

    try:
        if debug:
            reducer.run()
        else:
            with tqdm(total=len(initial)) as pb:
                reducer.run()
    finally:
        print(f"Reduced result left in {target}")