|
1 |
| -import unittest |
2 |
| -from test import support |
3 |
| -from test.support import import_helper |
4 | 1 | import builtins
|
5 | 2 | import contextlib
|
6 | 3 | import copy
|
|
10 | 7 | import pickle
|
11 | 8 | import random
|
12 | 9 | import sys
|
| 10 | +import unittest |
13 | 11 | import weakref
|
14 | 12 | from itertools import product
|
15 | 13 | from unittest import mock
|
16 | 14 |
|
| 15 | +from test import support |
| 16 | +from test.support import import_helper |
| 17 | + |
17 | 18 | py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid'])
|
18 | 19 | c_uuid = import_helper.import_fresh_module('uuid', fresh=['_uuid'])
|
19 | 20 |
|
@@ -724,6 +725,152 @@ def test_uuid5(self):
|
724 | 725 | equal(u, self.uuid.UUID(v))
|
725 | 726 | equal(str(u), v)
|
726 | 727 |
|
| 728 | + def test_uuid6(self): |
| 729 | + equal = self.assertEqual |
| 730 | + u = self.uuid.uuid6() |
| 731 | + equal(u.variant, self.uuid.RFC_4122) |
| 732 | + equal(u.version, 6) |
| 733 | + |
| 734 | + fake_nanoseconds = 0x1571_20a1_de1a_c533 |
| 735 | + fake_node_value = 0x54e1_acf6_da7f |
| 736 | + fake_clock_seq = 0x14c5 |
| 737 | + with ( |
| 738 | + mock.patch.object(self.uuid, '_last_timestamp_v6', None), |
| 739 | + mock.patch.object(self.uuid, 'getnode', return_value=fake_node_value), |
| 740 | + mock.patch('time.time_ns', return_value=fake_nanoseconds), |
| 741 | + mock.patch('random.getrandbits', return_value=fake_clock_seq) |
| 742 | + ): |
| 743 | + u = self.uuid.uuid6() |
| 744 | + equal(u.variant, self.uuid.RFC_4122) |
| 745 | + equal(u.version, 6) |
| 746 | + |
| 747 | + # 32 (top) | 16 (mid) | 12 (low) == 60 (timestamp) |
| 748 | + equal(u.time, 0x1e901fca_7a55_b92) |
| 749 | + equal(u.fields[0], 0x1e901fca) # 32 top bits of time |
| 750 | + equal(u.fields[1], 0x7a55) # 16 mid bits of time |
| 751 | + # 4 bits of version + 12 low bits of time |
| 752 | + equal((u.fields[2] >> 12) & 0xf, 6) |
| 753 | + equal((u.fields[2] & 0xfff), 0xb92) |
| 754 | + # 2 bits of variant + 6 high bits of clock_seq |
| 755 | + equal((u.fields[3] >> 6) & 0xf, 2) |
| 756 | + equal(u.fields[3] & 0x3f, fake_clock_seq >> 8) |
| 757 | + # 8 low bits of clock_seq |
| 758 | + equal(u.fields[4], fake_clock_seq & 0xff) |
| 759 | + equal(u.fields[5], fake_node_value) |
| 760 | + |
| 761 | + def test_uuid6_uniqueness(self): |
| 762 | + # Test that UUIDv6-generated values are unique. |
| 763 | + |
| 764 | + # Unlike UUIDv8, only 62 bits can be randomized for UUIDv6. |
| 765 | + # In practice, however, it remains unlikely to generate two |
| 766 | + # identical UUIDs for the same 60-bit timestamp if neither |
| 767 | + # the node ID nor the clock sequence is specified. |
| 768 | + uuids = {self.uuid.uuid6() for _ in range(1000)} |
| 769 | + self.assertEqual(len(uuids), 1000) |
| 770 | + versions = {u.version for u in uuids} |
| 771 | + self.assertSetEqual(versions, {6}) |
| 772 | + |
| 773 | + timestamp = 0x1ec9414c_232a_b00 |
| 774 | + fake_nanoseconds = (timestamp - 0x1b21dd21_3814_000) * 100 |
| 775 | + |
| 776 | + with mock.patch('time.time_ns', return_value=fake_nanoseconds): |
| 777 | + def gen(): |
| 778 | + with mock.patch.object(self.uuid, '_last_timestamp_v6', None): |
| 779 | + return self.uuid.uuid6(node=0, clock_seq=None) |
| 780 | + |
| 781 | + # By the birthday paradox, sampling N = 1024 UUIDs with identical |
| 782 | + # node IDs and timestamps results in duplicates with probability |
| 783 | + # close to 1 (not having a duplicate happens with probability of |
| 784 | + # order 1E-15) since only the 14-bit clock sequence is randomized. |
| 785 | + N = 1024 |
| 786 | + uuids = {gen() for _ in range(N)} |
| 787 | + self.assertSetEqual({u.node for u in uuids}, {0}) |
| 788 | + self.assertSetEqual({u.time for u in uuids}, {timestamp}) |
| 789 | + self.assertLess(len(uuids), N, 'collision property does not hold') |
| 790 | + |
| 791 | + def test_uuid6_node(self): |
| 792 | + # Make sure the given node ID appears in the UUID. |
| 793 | + # |
| 794 | + # Note: when no node ID is specified, the same logic as for UUIDv1 |
| 795 | + # is applied to UUIDv6. In particular, there is no need to test that |
| 796 | + # getnode() correctly returns positive integers of exactly 48 bits |
| 797 | + # since this is done in test_uuid1_eui64(). |
| 798 | + self.assertLessEqual(self.uuid.uuid6().node.bit_length(), 48) |
| 799 | + |
| 800 | + self.assertEqual(self.uuid.uuid6(0).node, 0) |
| 801 | + |
| 802 | + # tests with explicit values |
| 803 | + max_node = 0xffff_ffff_ffff |
| 804 | + self.assertEqual(self.uuid.uuid6(max_node).node, max_node) |
| 805 | + big_node = 0xE_1234_5678_ABCD # 52-bit node |
| 806 | + res_node = 0x0_1234_5678_ABCD # truncated to 48 bits |
| 807 | + self.assertEqual(self.uuid.uuid6(big_node).node, res_node) |
| 808 | + |
| 809 | + # randomized tests |
| 810 | + for _ in range(10): |
| 811 | + # node with > 48 bits is truncated |
| 812 | + for b in [24, 48, 72]: |
| 813 | + node = (1 << (b - 1)) | random.getrandbits(b) |
| 814 | + with self.subTest(node=node, bitlen=b): |
| 815 | + self.assertEqual(node.bit_length(), b) |
| 816 | + u = self.uuid.uuid6(node=node) |
| 817 | + self.assertEqual(u.node, node & 0xffff_ffff_ffff) |
| 818 | + |
| 819 | + def test_uuid6_clock_seq(self): |
| 820 | + # Make sure the supplied clock sequence appears in the UUID. |
| 821 | + # |
| 822 | + # For UUIDv6, clock sequence bits are stored from bit 48 to bit 62, |
| 823 | + # with the convention that the least significant bit is bit 0 and |
| 824 | + # the most significant bit is bit 127. |
| 825 | + get_clock_seq = lambda u: (u.int >> 48) & 0x3fff |
| 826 | + |
| 827 | + u = self.uuid.uuid6() |
| 828 | + self.assertLessEqual(get_clock_seq(u).bit_length(), 14) |
| 829 | + |
| 830 | + # tests with explicit values |
| 831 | + big_clock_seq = 0xffff # 16-bit clock sequence |
| 832 | + res_clock_seq = 0x3fff # truncated to 14 bits |
| 833 | + u = self.uuid.uuid6(clock_seq=big_clock_seq) |
| 834 | + self.assertEqual(get_clock_seq(u), res_clock_seq) |
| 835 | + |
| 836 | + # some randomized tests |
| 837 | + for _ in range(10): |
| 838 | + # clock_seq with > 14 bits is truncated |
| 839 | + for b in [7, 14, 28]: |
| 840 | + node = random.getrandbits(48) |
| 841 | + clock_seq = (1 << (b - 1)) | random.getrandbits(b) |
| 842 | + with self.subTest(node=node, clock_seq=clock_seq, bitlen=b): |
| 843 | + self.assertEqual(clock_seq.bit_length(), b) |
| 844 | + u = self.uuid.uuid6(node=node, clock_seq=clock_seq) |
| 845 | + self.assertEqual(get_clock_seq(u), clock_seq & 0x3fff) |
| 846 | + |
| 847 | + def test_uuid6_test_vectors(self): |
| 848 | + equal = self.assertEqual |
| 849 | + # https://www.rfc-editor.org/rfc/rfc9562#name-test-vectors |
| 850 | + # (separators are put at the 12th and 28th bits) |
| 851 | + timestamp = 0x1ec9414c_232a_b00 |
| 852 | + fake_nanoseconds = (timestamp - 0x1b21dd21_3814_000) * 100 |
| 853 | + # https://www.rfc-editor.org/rfc/rfc9562#name-example-of-a-uuidv6-value |
| 854 | + node = 0x9f6bdeced846 |
| 855 | + clock_seq = (3 << 12) | 0x3c8 |
| 856 | + |
| 857 | + with ( |
| 858 | + mock.patch.object(self.uuid, '_last_timestamp_v6', None), |
| 859 | + mock.patch('time.time_ns', return_value=fake_nanoseconds) |
| 860 | + ): |
| 861 | + u = self.uuid.uuid6(node=node, clock_seq=clock_seq) |
| 862 | + equal(str(u).upper(), '1EC9414C-232A-6B00-B3C8-9F6BDECED846') |
| 863 | + # 32 16 4 12 2 14 48 |
| 864 | + # time_hi | time_mid | ver | time_lo | var | clock_seq | node |
| 865 | + equal(u.time, timestamp) |
| 866 | + equal(u.int & 0xffff_ffff_ffff, node) |
| 867 | + equal((u.int >> 48) & 0x3fff, clock_seq) |
| 868 | + equal((u.int >> 62) & 0x3, 0b10) |
| 869 | + equal((u.int >> 64) & 0xfff, 0xb00) |
| 870 | + equal((u.int >> 76) & 0xf, 0x6) |
| 871 | + equal((u.int >> 80) & 0xffff, 0x232a) |
| 872 | + equal((u.int >> 96) & 0xffff_ffff, 0x1ec9_414c) |
| 873 | + |
727 | 874 | def test_uuid8(self):
|
728 | 875 | equal = self.assertEqual
|
729 | 876 | u = self.uuid.uuid8()
|
|
0 commit comments