diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2019-08-01 09:42:54 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2019-08-01 09:42:54 -0400 |
commit | a361ff3897736917544da1a4e28d8db6457f0fcc (patch) | |
tree | cdbee5c982a527dd89974ac38feb41fbc063b00f | |
parent | 79111b14cd70bbfd75555bb819c6dbdc25a543aa (diff) | |
download | python-coveragepy-git-a361ff3897736917544da1a4e28d8db6457f0fcc.tar.gz |
Add num_in_numbits
-rw-r--r-- | coverage/backward.py | 8 | ||||
-rw-r--r-- | coverage/numbits.py | 12 | ||||
-rw-r--r-- | tests/test_numbits.py | 11 |
3 files changed, 28 insertions, 3 deletions
diff --git a/coverage/backward.py b/coverage/backward.py index 0df2a41e..34ab2f1a 100644 --- a/coverage/backward.py +++ b/coverage/backward.py @@ -114,6 +114,10 @@ if env.PY3: """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. @@ -132,6 +136,10 @@ else: """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: diff --git a/coverage/numbits.py b/coverage/numbits.py index 19c328c4..c044073c 100644 --- a/coverage/numbits.py +++ b/coverage/numbits.py @@ -12,13 +12,13 @@ work with those binary blobs of data. """ -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: @@ -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)) diff --git a/tests/test_numbits.py b/tests/test_numbits.py index 4a835c4f..f4e52997 100644 --- a/tests/test_numbits.py +++ b/tests/test_numbits.py @@ -9,12 +9,14 @@ from hypothesis.strategies import sets, integers 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 @@ -47,3 +49,10 @@ class NumbitsOpTest(CoverageTest): 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) |