| # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
| # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt |
| |
| """Tests for coverage.numbits""" |
| |
| from __future__ import annotations |
| |
| import json |
| import sqlite3 |
| |
| from collections.abc import Iterable |
| |
| from hypothesis import example, given, settings |
| from hypothesis.strategies import sets, integers |
| |
| from coverage import env |
| from coverage.numbits import ( |
| nums_to_numbits, |
| numbits_to_nums, |
| numbits_union, |
| numbits_intersection, |
| numbits_any_intersection, |
| num_in_numbits, |
| register_sqlite_functions, |
| ) |
| |
| from tests.coveragetest import CoverageTest |
| |
| # Hypothesis-generated line number data |
| line_numbers = integers(min_value=1, max_value=9999) |
| line_number_sets = sets(line_numbers) |
| |
| # When coverage-testing ourselves, hypothesis complains about a test being |
| # flaky because the first run exceeds the deadline (and fails), and the second |
| # run succeeds. Disable the deadline if we are coverage-testing. |
| default_settings = settings(deadline=400) # milliseconds |
| if env.METACOV: |
| default_settings = settings(default_settings, deadline=None) |
| |
| |
| def good_numbits(numbits: bytes) -> None: |
| """Assert that numbits is good.""" |
| # It shouldn't end with a zero byte, that should have been trimmed off. |
| assert (not numbits) or (numbits[-1] != 0) |
| |
| |
| class NumbitsOpTest(CoverageTest): |
| """Tests of the numbits operations in numbits.py.""" |
| |
| run_in_temp_dir = False |
| |
| @given(line_number_sets) |
| @settings(default_settings) |
| def test_conversion(self, nums: Iterable[int]) -> None: |
| numbits = nums_to_numbits(nums) |
| good_numbits(numbits) |
| nums2 = numbits_to_nums(numbits) |
| assert nums == set(nums2) |
| |
| @given(line_number_sets, line_number_sets) |
| @settings(default_settings) |
| def test_union(self, nums1: set[int], nums2: set[int]) -> None: |
| nb1 = nums_to_numbits(nums1) |
| good_numbits(nb1) |
| nb2 = nums_to_numbits(nums2) |
| good_numbits(nb2) |
| nbu = numbits_union(nb1, nb2) |
| good_numbits(nbu) |
| union = numbits_to_nums(nbu) |
| assert nums1 | nums2 == set(union) |
| |
| @given(line_number_sets, line_number_sets) |
| @settings(default_settings) |
| def test_intersection(self, nums1: set[int], nums2: set[int]) -> None: |
| nb1 = nums_to_numbits(nums1) |
| good_numbits(nb1) |
| nb2 = nums_to_numbits(nums2) |
| good_numbits(nb2) |
| nbi = numbits_intersection(nb1, nb2) |
| good_numbits(nbi) |
| intersection = numbits_to_nums(nbi) |
| assert nums1 & nums2 == set(intersection) |
| |
| @given(line_number_sets, line_number_sets) |
| @settings(default_settings) |
| def test_any_intersection(self, nums1: set[int], nums2: set[int]) -> None: |
| nb1 = nums_to_numbits(nums1) |
| good_numbits(nb1) |
| nb2 = nums_to_numbits(nums2) |
| good_numbits(nb2) |
| inter = numbits_any_intersection(nb1, nb2) |
| expect = bool(nums1 & nums2) |
| assert expect == bool(inter) |
| |
| @given(line_numbers, line_number_sets) |
| @settings(default_settings) |
| @example(152, {144}) |
| def test_num_in_numbits(self, num: int, nums: Iterable[int]) -> None: |
| numbits = nums_to_numbits(nums) |
| good_numbits(numbits) |
| is_in = num_in_numbits(num, numbits) |
| assert (num in nums) == is_in |
| |
| |
| class NumbitsSqliteFunctionTest(CoverageTest): |
| """Tests of the SQLite integration for numbits functions.""" |
| |
| run_in_temp_dir = False |
| |
| def setUp(self) -> None: |
| super().setUp() |
| conn = sqlite3.connect(":memory:") |
| register_sqlite_functions(conn) |
| self.cursor = conn.cursor() |
| self.cursor.execute("create table data (id int, numbits blob)") |
| self.cursor.executemany( |
| "insert into data (id, numbits) values (?, ?)", |
| [(i, nums_to_numbits(range(i, 100, i))) for i in range(1, 11)], |
| ) |
| self.addCleanup(self.cursor.close) |
| |
| def test_numbits_union(self) -> None: |
| res = self.cursor.execute( |
| "select numbits_union(" |
| + "(select numbits from data where id = 7)," |
| + "(select numbits from data where id = 9)" |
| + ")", |
| ) |
| expected = [ |
| 7, |
| 9, |
| 14, |
| 18, |
| 21, |
| 27, |
| 28, |
| 35, |
| 36, |
| 42, |
| 45, |
| 49, |
| 54, |
| 56, |
| 63, |
| 70, |
| 72, |
| 77, |
| 81, |
| 84, |
| 90, |
| 91, |
| 98, |
| 99, |
| ] |
| answer = numbits_to_nums(list(res)[0][0]) |
| assert expected == answer |
| |
| def test_numbits_intersection(self) -> None: |
| res = self.cursor.execute( |
| "select numbits_intersection(" |
| + "(select numbits from data where id = 7)," |
| + "(select numbits from data where id = 9)" |
| + ")", |
| ) |
| answer = numbits_to_nums(list(res)[0][0]) |
| assert [63] == answer |
| |
| def test_numbits_any_intersection(self) -> None: |
| res = self.cursor.execute( |
| "select numbits_any_intersection(?, ?)", |
| (nums_to_numbits([1, 2, 3]), nums_to_numbits([3, 4, 5])), |
| ) |
| answer = [any_inter for (any_inter,) in res] |
| assert [1] == answer |
| |
| res = self.cursor.execute( |
| "select numbits_any_intersection(?, ?)", |
| (nums_to_numbits([1, 2, 3]), nums_to_numbits([7, 8, 9])), |
| ) |
| answer = [any_inter for (any_inter,) in res] |
| assert [0] == answer |
| |
| def test_num_in_numbits(self) -> None: |
| res = self.cursor.execute("select id, num_in_numbits(12, numbits) from data order by id") |
| answer = [is_in for (id, is_in) in res] |
| assert [1, 1, 1, 1, 0, 1, 0, 0, 0, 0] == answer |
| |
| def test_numbits_to_nums(self) -> None: |
| res = self.cursor.execute("select numbits_to_nums(?)", [nums_to_numbits([1, 2, 3])]) |
| assert [1, 2, 3] == json.loads(res.fetchone()[0]) |