diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2017-01-24 20:13:34 (GMT) |
---|---|---|
committer | Ethan Furman <ethan@stoneleaf.us> | 2017-01-24 20:13:34 (GMT) |
commit | 0105606f5531d9a22e672d3d2a72df2c5c447be8 (patch) | |
tree | dd66ee834498e910dc06ec33d98f905225a56ba8 /Lib/test/test_enum.py | |
parent | 675f2f8f903b83934e0b0cc0d62f12cee25270d2 (diff) | |
parent | 28cf663ff0af3ddac9a7dbe5e53e57763c532360 (diff) | |
download | cpython-0105606f5531d9a22e672d3d2a72df2c5c447be8.zip cpython-0105606f5531d9a22e672d3d2a72df2c5c447be8.tar.gz cpython-0105606f5531d9a22e672d3d2a72df2c5c447be8.tar.bz2 |
issue29167: fix race condition in (Int)Flag
Diffstat (limited to 'Lib/test/test_enum.py')
-rw-r--r-- | Lib/test/test_enum.py | 90 |
1 files changed, 88 insertions, 2 deletions
diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 4239412..a973081 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -7,6 +7,11 @@ from enum import Enum, IntEnum, EnumMeta, Flag, IntFlag, unique, auto from io import StringIO from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL from test import support +try: + import threading +except ImportError: + threading = None + # for pickle tests try: @@ -1983,6 +1988,45 @@ class TestFlag(unittest.TestCase): d = 6 self.assertEqual(repr(Bizarre(7)), '<Bizarre.d|c|b: 7>') + @unittest.skipUnless(threading, 'Threading required for this test.') + @support.reap_threads + def test_unique_composite(self): + # override __eq__ to be identity only + class TestFlag(Flag): + one = auto() + two = auto() + three = auto() + four = auto() + five = auto() + six = auto() + seven = auto() + eight = auto() + def __eq__(self, other): + return self is other + def __hash__(self): + return hash(self._value_) + # have multiple threads competing to complete the composite members + seen = set() + failed = False + def cycle_enum(): + nonlocal failed + try: + for i in range(256): + seen.add(TestFlag(i)) + except Exception: + failed = True + threads = [ + threading.Thread(target=cycle_enum) + for _ in range(8) + ] + with support.start_threads(threads): + pass + # check that only 248 members were created + self.assertFalse( + failed, + 'at least one thread failed while creating composite members') + self.assertEqual(256, len(seen), 'too many composite members created') + class TestIntFlag(unittest.TestCase): """Tests of the IntFlags.""" @@ -2275,6 +2319,46 @@ class TestIntFlag(unittest.TestCase): for f in Open: self.assertEqual(bool(f.value), bool(f)) + @unittest.skipUnless(threading, 'Threading required for this test.') + @support.reap_threads + def test_unique_composite(self): + # override __eq__ to be identity only + class TestFlag(IntFlag): + one = auto() + two = auto() + three = auto() + four = auto() + five = auto() + six = auto() + seven = auto() + eight = auto() + def __eq__(self, other): + return self is other + def __hash__(self): + return hash(self._value_) + # have multiple threads competing to complete the composite members + seen = set() + failed = False + def cycle_enum(): + nonlocal failed + try: + for i in range(256): + seen.add(TestFlag(i)) + except Exception: + failed = True + threads = [ + threading.Thread(target=cycle_enum) + for _ in range(8) + ] + with support.start_threads(threads): + pass + # check that only 248 members were created + self.assertFalse( + failed, + 'at least one thread failed while creating composite members') + self.assertEqual(256, len(seen), 'too many composite members created') + + class TestUnique(unittest.TestCase): def test_unique_clean(self): @@ -2488,7 +2572,8 @@ CONVERT_TEST_NAME_F = 5 class TestIntEnumConvert(unittest.TestCase): def test_convert_value_lookup_priority(self): test_type = enum.IntEnum._convert( - 'UnittestConvert', 'test.test_enum', + 'UnittestConvert', + ('test.test_enum', '__main__')[__name__=='__main__'], filter=lambda x: x.startswith('CONVERT_TEST_')) # We don't want the reverse lookup value to vary when there are # multiple possible names for a given value. It should always @@ -2497,7 +2582,8 @@ class TestIntEnumConvert(unittest.TestCase): def test_convert(self): test_type = enum.IntEnum._convert( - 'UnittestConvert', 'test.test_enum', + 'UnittestConvert', + ('test.test_enum', '__main__')[__name__=='__main__'], filter=lambda x: x.startswith('CONVERT_TEST_')) # Ensure that test_type has all of the desired names and values. self.assertEqual(test_type.CONVERT_TEST_NAME_F, |