diff --git a/doc/source/conf.py b/doc/source/conf.py index 92f02171..40bd6bb8 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -22,6 +22,7 @@ sys.path.insert(0, os.path.abspath('../..')) # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', + 'sphinx.ext.todo', 'openstackdocstheme', 'oslo_config.sphinxext', ] diff --git a/doc/source/reference/fixture.rst b/doc/source/reference/fixture.rst new file mode 100644 index 00000000..b44b8a8b --- /dev/null +++ b/doc/source/reference/fixture.rst @@ -0,0 +1,8 @@ +========= + fixture +========= + +.. automodule:: oslo_service.fixture + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/reference/index.rst b/doc/source/reference/index.rst index 17dedc47..60190941 100644 --- a/doc/source/reference/index.rst +++ b/doc/source/reference/index.rst @@ -6,6 +6,7 @@ API Reference :maxdepth: 1 eventlet_backdoor + fixture loopingcall periodic_task service diff --git a/oslo_service/fixture.py b/oslo_service/fixture.py new file mode 100644 index 00000000..056eef60 --- /dev/null +++ b/oslo_service/fixture.py @@ -0,0 +1,52 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import fixtures + + +class SleepFixture(fixtures.Fixture): + """A fixture for mocking the ``wait()`` within :doc:`loopingcall` events. + + This exists so test cases can exercise code that uses :doc:`loopingcall` + without actually incurring wall clock time for sleeping. + + The mock for the ``wait()`` is accessible via the fixture's ``mock_wait`` + attribute. + + .. note:: It is not recommended to assert specific arguments (i.e. timeout + values) to the mock, as this relies on the internals of + :doc:`loopingcall` not changing. + + .. todo:: Figure out a way to make an enforceable contract allowing + verification of timeout values. + + Example usage:: + + from oslo.service import fixture + ... + class MyTest(...): + def setUp(self): + ... + self.sleepfx = self.useFixture(fixture.SleepFixture()) + ... + + def test_this(self): + ... + thing_that_hits_a_loopingcall() + ... + self.assertEqual(5, self.sleepfx.mock_wait.call_count) + ... + """ + def _setUp(self): + # Provide access to the mock so that calls to it can be asserted + self.mock_wait = self.useFixture(fixtures.MockPatch( + 'oslo_service.loopingcall._Event.wait')).mock diff --git a/oslo_service/tests/test_fixture.py b/oslo_service/tests/test_fixture.py new file mode 100644 index 00000000..08107a4f --- /dev/null +++ b/oslo_service/tests/test_fixture.py @@ -0,0 +1,36 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from oslotest import base as test_base + +from oslo_service import fixture +from oslo_service import loopingcall + + +class FixtureTestCase(test_base.BaseTestCase): + def setUp(self): + super(FixtureTestCase, self).setUp() + self.sleepfx = self.useFixture(fixture.SleepFixture()) + + def test_sleep_fixture(self): + @loopingcall.RetryDecorator(max_retry_count=3, inc_sleep_time=2, + exceptions=(ValueError,)) + def retried_method(): + raise ValueError("!") + + self.assertRaises(ValueError, retried_method) + self.assertEqual(3, self.sleepfx.mock_wait.call_count) + # TODO(efried): This is cheating, and shouldn't be done by real callers + # yet - see todo in SleepFixture. + self.sleepfx.mock_wait.assert_has_calls( + [mock.call(x) for x in (2, 4, 6)]) diff --git a/requirements.txt b/requirements.txt index 08537f0f..4afebff0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ WebOb>=1.7.1 # MIT eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT +fixtures>=3.0.0 # Apache-2.0/BSD greenlet>=0.4.10 # MIT monotonic>=0.6;python_version<'3.3' # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0