diff --git a/oslo_utils/fixture.py b/oslo_utils/fixture.py index 9526154e..1872c70e 100644 --- a/oslo_utils/fixture.py +++ b/oslo_utils/fixture.py @@ -20,9 +20,12 @@ Test fixtures. .. versionadded:: 1.3 """ +import threading + import fixtures from oslo_utils import timeutils +from oslo_utils import uuidutils class TimeFixture(fixtures.Fixture): @@ -49,3 +52,41 @@ class TimeFixture(fixtures.Fixture): def advance_time_seconds(self, seconds): """Advance overridden time by seconds.""" timeutils.advance_time_seconds(seconds) + + +class _UUIDSentinels(object): + """Registry of dynamically created, named, random UUID strings. + + An instance of this class will dynamically generate attributes as they are + referenced, associating a random UUID string with each. Thereafter, + referring to the same attribute will give the same UUID for the life of the + instance. Plan accordingly. + + Usage: + + from oslo_utils.fixture import uuidsentinel as uuids + ... + foo = uuids.foo + do_a_thing(foo) + # Referencing the same sentinel again gives the same value + assert foo == uuids.foo + # But a different one will be different + assert foo != uuids.bar + """ + def __init__(self): + self._sentinels = {} + self._lock = threading.Lock() + + def __getattr__(self, name): + if name.startswith('_'): + raise ValueError('Sentinels must not start with _') + with self._lock: + if name not in self._sentinels: + self._sentinels[name] = uuidutils.generate_uuid() + return self._sentinels[name] + + +# Singleton sentinel instance. Caveat emptor: using this multiple times in the +# same process (including across multiple modules) will result in the same +# values +uuidsentinel = _UUIDSentinels() diff --git a/oslo_utils/tests/test_fixture.py b/oslo_utils/tests/test_fixture.py index 3e0898ab..dafac2f3 100644 --- a/oslo_utils/tests/test_fixture.py +++ b/oslo_utils/tests/test_fixture.py @@ -17,9 +17,12 @@ import datetime from oslotest import base as test_base +import six from oslo_utils import fixture +from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import timeutils +from oslo_utils import uuidutils class TimeFixtureTest(test_base.BaseTestCase): @@ -61,3 +64,21 @@ class TimeFixtureTest(test_base.BaseTestCase): time_fixture.advance_time_seconds(2) expected_time = datetime.datetime(2015, 1, 2, 3, 4, 8, 7) self.assertEqual(expected_time, timeutils.utcnow()) + + +class UUIDSentinelsTest(test_base.BaseTestCase): + + def test_different_sentinel(self): + uuid1 = uuids.foobar + uuid2 = uuids.barfoo + self.assertNotEqual(uuid1, uuid2) + + def test_returns_uuid(self): + self.assertTrue(uuidutils.is_uuid_like(uuids.foo)) + + def test_returns_string(self): + self.assertIsInstance(uuids.foo, str) + + def test_with_underline_prefix(self): + ex = self.assertRaises(ValueError, getattr, uuids, '_foo') + self.assertIn("Sentinels must not start with _", six.text_type(ex))