def test_keeps_using_solid_passes_while_they_shrink_size(): good = { hbytes([0, 1, 2, 3, 4, 5]), hbytes([0, 1, 2, 3, 5]), hbytes([0, 1, 3, 5]), hbytes([1, 3, 5]), hbytes([1, 5]), } initial = max(good, key=sort_key) @shrinking_from(initial) def shrinker(data): while True: data.draw_bits(8) if hbytes(data.buffer) in good: data.mark_interesting() shrinker.clear_passes() d1 = shrinker.add_new_pass(block_program("X")) d2 = shrinker.add_new_pass(block_program("-")) for _ in range(3): shrinker.single_greedy_shrink_iteration() assert d1.classification == PassClassification.HOPEFUL assert d2.classification == PassClassification.CANDIDATE
def test_block_deletion_can_delete_short_ranges(monkeypatch): @shrinking_from([v for i in range(5) for _ in range(i + 1) for v in [0, i]]) def shrinker(data): while True: n = data.draw_bits(16) for _ in range(n): if data.draw_bits(16) != n: data.mark_invalid() if n == 4: data.mark_interesting() for i in range(1, 5): block_program("X" * i)(shrinker) assert list(shrinker.shrink_target.buffer) == [0, 4] * 5
def test_block_programs_fail_efficiently(monkeypatch): # Create 256 byte-sized blocks. None of the blocks can be deleted, and # every deletion attempt produces a different buffer. @shrinking_from(bytes(range(256))) def shrinker(data): values = set() for _ in range(256): v = data.draw_bits(8) values.add(v) if len(values) == 256: data.mark_interesting() monkeypatch.setattr( Shrinker, "run_block_program", counts_calls(Shrinker.run_block_program) ) shrinker.max_stall = 500 shrinker.fixate_shrink_passes([block_program("XX")]) assert shrinker.shrinks == 0 assert 250 <= shrinker.calls <= 260 # The block program should have been run roughly 255 times, with a little # bit of wiggle room for implementation details. # - Too many calls mean that failing steps are doing too much work. # - Too few calls mean that this test is probably miscounting and buggy. assert 250 <= Shrinker.run_block_program.calls <= 260
def test_block_programs_fail_efficiently(monkeypatch): # Create 256 byte-sized blocks. None of the blocks can be deleted, and # every deletion attempt produces a different buffer. @shrinking_from(hbytes(hrange(256))) def shrinker(data): values = set() for _ in hrange(256): v = data.draw_bits(8) values.add(v) if len(values) == 256: data.mark_interesting() monkeypatch.setattr( Shrinker, "run_block_program", counts_calls(Shrinker.run_block_program) ) shrinker.fixate_shrink_passes([block_program("XX")]) assert shrinker.shrinks == 0 assert 250 <= shrinker.calls <= 260 # The block program should have been run roughly 255 times, with a little # bit of wiggle room for implementation details. # - Too many calls mean that failing steps are doing too much work. # - Too few calls mean that this test is probably miscounting and buggy. assert 250 <= Shrinker.run_block_program.calls <= 260
def test_shrink_pass_method_is_idempotent(): @shrinking_from([255]) def shrinker(data): data.draw_bits(8) data.mark_interesting() sp = shrinker.shrink_pass(block_program("X")) assert isinstance(sp, ShrinkPass) assert shrinker.shrink_pass(sp) is sp
def test_block_programs_are_adaptive(): @shrinking_from(hbytes(1000) + hbytes([1])) def shrinker(data): while not data.draw_bits(1): pass data.mark_interesting() p = shrinker.add_new_pass(block_program("X")) shrinker.fixate_shrink_passes([p.name]) assert len(shrinker.shrink_target.buffer) == 1 assert shrinker.calls <= 60
def test_passes_can_come_back_to_life(): initial = hbytes([1, 2, 3, 4, 5, 6]) buf1 = hbytes([0, 1, 3, 4, 5, 6]) buf2 = hbytes([0, 1, 3, 4, 4, 6]) good = {initial, buf1, buf2} @shrinking_from(initial) def shrinker(data): string = hbytes([data.draw_bits(8) for _ in range(6)]) if string in good: data.mark_interesting() shrinker.clear_passes() shrinker.add_new_pass(block_program("--")) shrinker.add_new_pass(block_program("-")) shrinker.single_greedy_shrink_iteration() assert shrinker.shrink_target.buffer == buf1 shrinker.single_greedy_shrink_iteration() assert shrinker.shrink_target.buffer == buf2
def test_pandas_hack(): @shrinking_from([2, 1, 1, 7]) def shrinker(data): n = data.draw_bits(8) m = data.draw_bits(8) if n == 1: if m == 7: data.mark_interesting() data.draw_bits(8) if data.draw_bits(8) == 7: data.mark_interesting() shrinker.run_shrink_pass(block_program("-XX")) assert list(shrinker.shrink_target.buffer) == [1, 7]
def test_block_deletion_can_delete_short_ranges(monkeypatch): @shrinking_from([v for i in range(5) for _ in range(i + 1) for v in [0, i]]) def shrinker(data): while True: n = data.draw_bits(16) for _ in range(n): if data.draw_bits(16) != n: data.mark_invalid() if n == 4: data.mark_interesting() for i in range(1, 5): shrinker.run_shrink_pass(block_program("X" * i)) assert list(shrinker.shrink_target.buffer) == [0, 4] * 5
def test_will_enable_previously_bad_passes_when_failing_to_shrink(): # We lead the shrinker down the garden path a bit where it keeps making # progress but only lexically. When it finally gets down to the minimum good = { hbytes([1, 2, 3, 4, 5, 6]), hbytes([1, 2, 3, 4, 5, 5]), hbytes([1, 2, 2, 4, 5, 5]), hbytes([1, 2, 2, 4, 4, 5]), hbytes([0, 2, 2, 4, 4, 5]), hbytes([0, 1, 2, 4, 4, 5]), } initial = max(good) final = min(good) @shrinking_from(initial + hbytes([0, 7])) def shrinker(data): string = hbytes([data.draw_bits(8) for _ in range(6)]) if string in good: n = 0 while data.draw_bits(8) != 7: n += 1 if not (string == final or n > 0): data.mark_invalid() data.mark_interesting() # In order to get to the minimized result we want to run both of these, # but the second pass starts out as disabled (and anyway won't work until # the first has hit fixity). shrinker.clear_passes() shrinker.add_new_pass(block_program("-")) shrinker.add_new_pass(block_program("X")) shrinker.shrink() assert shrinker.shrink_target.buffer == final + hbytes([7])
def test_will_let_fixate_shrink_passes_do_a_full_run_through(): @shrinking_from(range(50)) def shrinker(data): for i in range(50): if data.draw_bits(8) != i: data.mark_invalid() data.mark_interesting() shrinker.max_stall = 5 passes = [block_program("X" * i) for i in range(1, 11)] with pytest.raises(StopShrinking): shrinker.fixate_shrink_passes(passes) assert shrinker.shrink_pass(passes[-1]).calls > 0