diff --git a/reimplement/set.py b/reimplement/set.py index beb9c60..5ac4c5f 100644 --- a/reimplement/set.py +++ b/reimplement/set.py @@ -174,7 +174,6 @@ def decode_golomb(bits: List[int], Mshift: int) -> List[int]: if i < len(bits): if bits[i] == 1: r |= 1 << j - else: i += 1 delta_value = (q << Mshift) | r @@ -360,28 +359,3 @@ def set_fini(set: Set, bpp: int) -> str | None: encoded_string = encode_set(hash_values, bpp) return encoded_string - - -if __name__ == "__main__": - # Example usage - my_set = set_new() - set_add(my_set, "example_label_1") - set_add(my_set, "example_label_2") - result = set_fini(my_set, 8) - print(result) - set_free(my_set) - - # example with comparison - set1 = set_new() - set_add(set1, "label1") - set_add(set1, "label2") - str1 = set_fini(set1, 8) - set2 = set_new() - set_add(set2, "label1") - set_add(set2, "label2") - set_add(set2, "label3") - str2 = set_fini(set2, 8) - assert str1 is not None and str2 is not None, "Set strings should not be None" - comparison_result = rpmsetcmp(str1, str2) - assert comparison_result == -1, "Expected str1 to be less than str2" - print(comparison_result) diff --git a/tests/test_reimplement_set.py b/tests/test_reimplement_set.py index 43678fc..917a241 100644 --- a/tests/test_reimplement_set.py +++ b/tests/test_reimplement_set.py @@ -1,20 +1,92 @@ +import contextlib +import io +import runpy import unittest -from reimplement.set import downsample_set +from reimplement import set as rpmset + + +class Base64AlphabetTest(unittest.TestCase): + def test_int_to_char_and_char_to_int_roundtrip_all_values(self): + for value in range(64): + self.assertEqual(rpmset.char_to_int(rpmset.int_to_char(value)), value) + + def test_int_to_char_rejects_out_of_range_values(self): + with self.assertRaises(ValueError): + rpmset.int_to_char(64) + + def test_char_to_int_rejects_non_base64_character(self): + with self.assertRaises(ValueError): + rpmset.char_to_int("=") + + +class GolombEncodingTest(unittest.TestCase): + def test_delta_roundtrip(self): + values = [1, 3, 6, 10, 31] + self.assertEqual(rpmset.decode_delta(rpmset.encode_delta(values)), values) + + def test_golomb_roundtrip(self): + values = [0, 1, 2, 7, 8, 15] + bits = rpmset.encode_golomb(values, Mshift=3) + self.assertEqual(rpmset.decode_golomb(bits, Mshift=3), values) + + def test_base64_roundtrip_preserves_bits_with_zero_padding(self): + bits = [1, 0, 1, 1, 0, 0, 1] + encoded = rpmset.encode_base64(bits) + self.assertEqual(rpmset.decode_base64(encoded)[: len(bits)], bits) + + +class SetStringTest(unittest.TestCase): + def test_encode_decode_set_roundtrip(self): + values = [1, 3, 6, 10] + encoded = rpmset.encode_set(values, bpp=8) + bpp, mshift = rpmset.decode_set_init(encoded) + self.assertEqual(bpp, 8) + self.assertEqual(rpmset.decode_set(encoded, mshift)[: len(values)], values) + + def test_set_add_fini_and_free(self): + item_set = rpmset.set_new() + rpmset.set_add(item_set, "label1") + rpmset.set_add(item_set, "label2") + + encoded = rpmset.set_fini(item_set, bpp=8) + self.assertIsNotNone(encoded) + self.assertEqual(item_set.cnt, 2) + self.assertEqual(item_set.labels, sorted(item_set.labels, key=lambda item: item[1])) + + rpmset.set_free(item_set) + self.assertEqual(item_set.cnt, 0) + self.assertEqual(item_set.labels, []) + + def test_hash_is_stable_64_bit_ascii_integer(self): + self.assertEqual(rpmset.hash("ascii_symbol"), 10827468943333989194) + self.assertLessEqual(rpmset.hash("ascii_symbol"), 2**64 - 1) + + def test_hash_rejects_non_ascii_labels(self): + with self.assertRaises(UnicodeEncodeError): + rpmset.hash("юникод") class DownsampleSetTest(unittest.TestCase): def test_masks_high_half_and_keeps_sorted_unique_values(self): - self.assertEqual(downsample_set([1, 3, 6, 8, 10, 14], 3), [0, 1, 2, 3, 6]) + self.assertEqual(rpmset.downsample_set([1, 3, 6, 8, 10, 14], 3), [0, 1, 2, 3, 6]) def test_removes_duplicates_created_by_masking(self): - self.assertEqual(downsample_set([1, 6, 14], 3), [1, 6]) + self.assertEqual(rpmset.downsample_set([1, 6, 14], 3), [1, 6]) def test_keeps_low_only_set_unchanged(self): - self.assertEqual(downsample_set([1, 3, 6], 3), [1, 3, 6]) + self.assertEqual(rpmset.downsample_set([1, 3, 6], 3), [1, 3, 6]) def test_masks_high_only_set(self): - self.assertEqual(downsample_set([8, 10, 14], 3), [0, 2, 6]) + self.assertEqual(rpmset.downsample_set([8, 10, 14], 3), [0, 2, 6]) + + +class ModuleEntrypointTest(unittest.TestCase): + def test_module_has_no_main_side_effects(self): + output = io.StringIO() + with contextlib.redirect_stdout(output): + runpy.run_path("reimplement/set.py", run_name="__main__") + self.assertEqual(output.getvalue(), "") if __name__ == "__main__":