Skip to content

Commit

Permalink
Add num_in_numbits
Browse files Browse the repository at this point in the history
  • Loading branch information
nedbat committed Aug 1, 2019
1 parent 79111b1 commit a361ff3
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 3 deletions.
8 changes: 8 additions & 0 deletions coverage/backward.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ def binary_bytes(byte_values):
"""Produce a byte string with the ints from `byte_values`."""
return bytes(byte_values)

def byte_to_int(byte):
"""Turn a byte indexed from a bytes object into an int."""
return byte

def bytes_to_ints(bytes_value):
"""Turn a bytes object into a sequence of ints."""
# In Python 3, iterating bytes gives ints.
Expand All @@ -132,6 +136,10 @@ def binary_bytes(byte_values):
"""Produce a byte string with the ints from `byte_values`."""
return "".join(chr(b) for b in byte_values)

def byte_to_int(byte):
"""Turn a byte indexed from a bytes object into an int."""
return ord(byte)

def bytes_to_ints(bytes_value):
"""Turn a bytes object into a sequence of ints."""
for byte in bytes_value:
Expand Down
12 changes: 10 additions & 2 deletions coverage/numbits.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
"""

from coverage.backward import bytes_to_ints, binary_bytes, zip_longest
from coverage.backward import byte_to_int, bytes_to_ints, binary_bytes, zip_longest
from coverage.misc import contract


@contract(nums='Iterable', returns='bytes')
def nums_to_numbits(nums):
"""Convert `nums` (an iterable of ints) into a numbits."""
"""Convert `nums` (a non-empty iterable of ints) into a numbits."""
nbytes = max(nums) // 8 + 1
b = bytearray(nbytes)
for num in nums:
Expand Down Expand Up @@ -46,3 +46,11 @@ def numbits_any_intersection(numbits1, numbits2):
"""Is there any number that appears in both numbits?"""
byte_pairs = zip_longest(bytes_to_ints(numbits1), bytes_to_ints(numbits2), fillvalue=0)
return any(b1 & b2 for b1, b2 in byte_pairs)

@contract(num='int', numbits='bytes', returns='bool')
def num_in_numbits(num, numbits):
"""Does the integer `num` appear in `numbits`?"""
nbyte, nbit = divmod(num, 8)
if nbyte > len(numbits):
return False
return bool(byte_to_int(numbits[nbyte]) & (1 << nbit))
11 changes: 10 additions & 1 deletion tests/test_numbits.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
from coverage import env
from coverage.numbits import (
nums_to_numbits, numbits_to_nums, merge_numbits, numbits_any_intersection,
num_in_numbits,
)

from tests.coveragetest import CoverageTest

# Hypothesis-generated line number data
line_numbers = sets(integers(min_value=1, max_value=9999), min_size=1)
line_number = integers(min_value=1, max_value=9999)
line_numbers = sets(line_number, min_size=1)

# When coverage-testing ourselves, hypothesis complains about a test being
# flaky because the first run exceeds the deadline (and fails), and the second
Expand Down Expand Up @@ -47,3 +49,10 @@ def test_any_intersection(self, nums1, nums2):
inter = numbits_any_intersection(nums_to_numbits(nums1), nums_to_numbits(nums2))
expect = bool(nums1 & nums2)
self.assertEqual(expect, bool(inter))

@given(line_number, line_numbers)
@settings(default_settings)
def test_num_in_numbits(self, num, nums):
numbits = nums_to_numbits(nums)
is_in = num_in_numbits(num, numbits)
self.assertEqual(num in nums, is_in)

0 comments on commit a361ff3

Please sign in to comment.