summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCalum Lind <calumlind+deluge@gmail.com>2023-03-05 16:57:43 +0000
committerCalum Lind <calumlind+deluge@gmail.com>2023-03-06 13:25:00 +0000
commitc38b4c72d03331ddda990b49cfda27c27c79bad5 (patch)
tree51935e947b4d8397911969bfae097829ddebe539
parent0745c0eff85ea4e4a3646c454098503ed08c56ff (diff)
downloaddeluge-c38b4c72d03331ddda990b49cfda27c27c79bad5.tar.gz
deluge-c38b4c72d03331ddda990b49cfda27c27c79bad5.tar.bz2
deluge-c38b4c72d03331ddda990b49cfda27c27c79bad5.zip
[Tests] Refactor component tests for readability
Modified test functions to be async. Used pytest_twisted_ensuredeferred_for_class decorator to avoid needed ensureDeferred for each test within the class. There might be a way to do this with fixtures so likely to be improvements for use in all test classes. Used Mock in component subclass for simpler tracking of event method calls
-rw-r--r--deluge/tests/test_component.py324
1 files changed, 129 insertions, 195 deletions
diff --git a/deluge/tests/test_component.py b/deluge/tests/test_component.py
index aee113668..7655eef11 100644
--- a/deluge/tests/test_component.py
+++ b/deluge/tests/test_component.py
@@ -3,6 +3,9 @@
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
+import inspect
+import time
+from unittest.mock import Mock
import pytest
import pytest_twisted
@@ -13,95 +16,59 @@ import deluge.component as component
class ComponentTester(component.Component):
def __init__(self, name, depend=None):
- component.Component.__init__(self, name, depend=depend)
- self.start_count = 0
- self.stop_count = 0
-
- def start(self):
- self.start_count += 1
-
- def stop(self):
- self.stop_count += 1
+ super().__init__(name, depend=depend)
+ event_methods = ('start', 'update', 'stop', 'shutdown')
+ for event_method in event_methods:
+ setattr(self, event_method, Mock())
class ComponentTesterDelayStart(ComponentTester):
- def start(self):
- def do_sleep():
- import time
-
- time.sleep(1)
-
- d = threads.deferToThread(do_sleep)
-
- def on_done(result):
- self.start_count += 1
-
- return d.addCallback(on_done)
-
-
-class ComponentTesterUpdate(component.Component):
- def __init__(self, name):
- component.Component.__init__(self, name)
- self.counter = 0
- self.start_count = 0
- self.stop_count = 0
-
- def update(self):
- self.counter += 1
-
- def stop(self):
- self.stop_count += 1
-
+ def __init__(self, name, depend=None):
+ super().__init__(name, depend=depend)
+ self.start = Mock(side_effect=self.delay)
-class ComponentTesterShutdown(component.Component):
- def __init__(self, name):
- component.Component.__init__(self, name)
- self.shutdowned = False
- self.stop_count = 0
+ @pytest_twisted.inlineCallbacks
+ def delay(self):
+ yield threads.deferToThread(time.sleep, 0.5)
- def shutdown(self):
- self.shutdowned = True
- def stop(self):
- self.stop_count += 1
+def pytest_twisted_ensuredeferred_for_class(cls):
+ """Applies ensureDeferred to all async test_ methods in class"""
+ for name, method in inspect.getmembers(cls, inspect.iscoroutinefunction):
+ if name.startswith('test'):
+ setattr(cls, name, pytest_twisted.ensureDeferred(method))
+ return cls
+@pytest_twisted_ensuredeferred_for_class
@pytest.mark.usefixtures('component')
class TestComponent:
- def test_start_component(self):
- def on_start(result, c):
- assert c._component_state == 'Started'
- assert c.start_count == 1
-
- c = ComponentTester('test_start_c1')
- d = component.start(['test_start_c1'])
- d.addCallback(on_start, c)
- return d
-
- def test_start_stop_depends(self):
- def on_stop(result, c1, c2):
- assert c1._component_state == 'Stopped'
- assert c2._component_state == 'Stopped'
- assert c1.stop_count == 1
- assert c2.stop_count == 1
-
- def on_start(result, c1, c2):
- assert c1._component_state == 'Started'
- assert c2._component_state == 'Started'
- assert c1.start_count == 1
- assert c2.start_count == 1
- return component.stop(['test_start_depends_c1']).addCallback(
- on_stop, c1, c2
- )
+ async def test_start_component(self):
+ c = ComponentTester('test_start')
+ await component.start(['test_start'])
+
+ assert c._component_state == 'Started'
+ assert c.start.call_count == 1
+ async def test_start_stop_depends(self):
c1 = ComponentTester('test_start_depends_c1')
c2 = ComponentTester('test_start_depends_c2', depend=['test_start_depends_c1'])
- d = component.start(['test_start_depends_c2'])
- d.addCallback(on_start, c1, c2)
- return d
+ await component.start('test_start_depends_c2')
- def start_with_depends(self):
+ assert c1._component_state == 'Started'
+ assert c2._component_state == 'Started'
+ assert c1.start.call_count == 1
+ assert c2.start.call_count == 1
+
+ await component.stop(['test_start_depends_c1'])
+
+ assert c1._component_state == 'Stopped'
+ assert c2._component_state == 'Stopped'
+ assert c1.stop.call_count == 1
+ assert c2.stop.call_count == 1
+
+ async def start_with_depends(self):
c1 = ComponentTesterDelayStart('test_start_all_c1')
c2 = ComponentTester('test_start_all_c2', depend=['test_start_all_c4'])
c3 = ComponentTesterDelayStart(
@@ -110,141 +77,108 @@ class TestComponent:
c4 = ComponentTester('test_start_all_c4', depend=['test_start_all_c3'])
c5 = ComponentTester('test_start_all_c5')
- d = component.start()
- return (d, c1, c2, c3, c4, c5)
+ await component.start()
+ return c1, c2, c3, c4, c5
def finish_start_with_depends(self, *args):
for c in args[1:]:
component.deregister(c)
- def test_start_all(self):
- def on_start(*args):
- for c in args[1:]:
- assert c._component_state == 'Started'
- assert c.start_count == 1
+ async def test_start_all(self):
+ components = await self.start_with_depends()
+ for c in components:
+ assert c._component_state == 'Started'
+ assert c.start.call_count == 1
- ret = self.start_with_depends()
- ret[0].addCallback(on_start, *ret[1:])
- ret[0].addCallback(self.finish_start_with_depends, *ret[1:])
- return ret[0]
+ self.finish_start_with_depends(components)
def test_register_exception(self):
- ComponentTester('test_register_exception_c1')
+ ComponentTester('test_register_exception')
with pytest.raises(component.ComponentAlreadyRegistered):
ComponentTester(
- 'test_register_exception_c1',
+ 'test_register_exception',
)
- def test_stop_component(self):
- def on_stop(result, c):
+ async def test_stop(self):
+ c = ComponentTester('test_stop')
+
+ await component.start(['test_stop'])
+
+ assert c._component_state == 'Started'
+
+ await component.stop(['test_stop'])
+
+ assert c._component_state == 'Stopped'
+ assert not c._component_timer.running
+ assert c.stop.call_count == 1
+
+ async def test_stop_all(self):
+ components = await self.start_with_depends()
+ assert all(c._component_state == 'Started' for c in components)
+
+ component.stop()
+ for c in components:
assert c._component_state == 'Stopped'
- assert not c._component_timer.running
- assert c.stop_count == 1
+ assert c.stop.call_count == 1
- def on_start(result, c):
- assert c._component_state == 'Started'
- return component.stop(['test_stop_component_c1']).addCallback(on_stop, c)
-
- c = ComponentTesterUpdate('test_stop_component_c1')
- d = component.start(['test_stop_component_c1'])
- d.addCallback(on_start, c)
- return d
-
- def test_stop_all(self):
- def on_stop(result, *args):
- for c in args:
- assert c._component_state == 'Stopped'
- assert c.stop_count == 1
-
- def on_start(result, *args):
- for c in args:
- assert c._component_state == 'Started'
- return component.stop().addCallback(on_stop, *args)
-
- ret = self.start_with_depends()
- ret[0].addCallback(on_start, *ret[1:])
- ret[0].addCallback(self.finish_start_with_depends, *ret[1:])
- return ret[0]
-
- def test_update(self):
- def on_start(result, c1, counter):
- assert c1._component_timer
- assert c1._component_timer.running
- assert c1.counter != counter
- return component.stop()
-
- c1 = ComponentTesterUpdate('test_update_c1')
- cnt = int(c1.counter)
- d = component.start(['test_update_c1'])
-
- d.addCallback(on_start, c1, cnt)
- return d
-
- def test_pause(self):
- def on_pause(result, c1, counter):
- assert c1._component_state == 'Paused'
- assert c1.counter != counter
- assert not c1._component_timer.running
-
- def on_start(result, c1, counter):
- assert c1._component_timer
- assert c1.counter != counter
- d = component.pause(['test_pause_c1'])
- d.addCallback(on_pause, c1, counter)
- return d
-
- c1 = ComponentTesterUpdate('test_pause_c1')
- cnt = int(c1.counter)
- d = component.start(['test_pause_c1'])
-
- d.addCallback(on_start, c1, cnt)
- return d
+ self.finish_start_with_depends(components)
- @pytest_twisted.inlineCallbacks
- def test_component_start_error(self):
- ComponentTesterUpdate('test_pause_c1')
- yield component.start(['test_pause_c1'])
- yield component.pause(['test_pause_c1'])
- test_comp = component.get('test_pause_c1')
- with pytest.raises(component.ComponentException, match='Current state: Paused'):
- yield test_comp._component_start()
+ async def test_update(self):
+ c = ComponentTester('test_update')
+ init_update_count = int(c.update.call_count)
+ await component.start(['test_update'])
- @pytest_twisted.inlineCallbacks
- def test_start_paused_error(self):
- ComponentTesterUpdate('test_pause_c1')
- yield component.start(['test_pause_c1'])
- yield component.pause(['test_pause_c1'])
-
- # Deferreds that fail in component have to error handler which results in
- # twisted doing a log.err call which causes the test to fail.
- # Prevent failure by ignoring the exception
- # self._observer._ignoreErrors(component.ComponentException)
-
- result = yield component.start()
- assert [(result[0][0], result[0][1].value)] == [
+ assert c._component_timer
+ assert c._component_timer.running
+ assert c.update.call_count != init_update_count
+ await component.stop()
+
+ async def test_pause(self):
+ c = ComponentTester('test_pause')
+ init_update_count = int(c.update.call_count)
+
+ await component.start(['test_pause'])
+
+ assert c._component_timer
+ assert c.update.call_count != init_update_count
+
+ await component.pause(['test_pause'])
+
+ assert c._component_state == 'Paused'
+ assert c.update.call_count != init_update_count
+ assert not c._component_timer.running
+
+ async def test_component_start_error(self):
+ ComponentTester('test_start_error')
+ await component.start(['test_start_error'])
+ await component.pause(['test_start_error'])
+ test_comp = component.get('test_start_error')
+ with pytest.raises(component.ComponentException, match='Current state: Paused'):
+ await test_comp._component_start()
+
+ async def test_start_paused_error(self):
+ name = 'test_pause_error'
+ ComponentTester(name)
+ await component.start([name])
+ await component.pause([name])
+
+ (failure, error), *_ = await component.start()
+ assert (failure, error.type, error.value.message) == (
+ defer.FAILURE,
+ component.ComponentException,
(
- defer.FAILURE,
- component.ComponentException(
- 'Trying to start component "%s" but it is '
- 'not in a stopped state. Current state: %s'
- % ('test_pause_c1', 'Paused'),
- '',
- ),
- )
- ]
-
- def test_shutdown(self):
- def on_shutdown(result, c1):
- assert c1.shutdowned
- assert c1._component_state == 'Stopped'
- assert c1.stop_count == 1
-
- def on_start(result, c1):
- d = component.shutdown()
- d.addCallback(on_shutdown, c1)
- return d
-
- c1 = ComponentTesterShutdown('test_shutdown_c1')
- d = component.start(['test_shutdown_c1'])
- d.addCallback(on_start, c1)
- return d
+ f'Trying to start component "{name}" but it is '
+ 'not in a stopped state. Current state: Paused'
+ ),
+ )
+
+ async def test_shutdown(self):
+ c = ComponentTester('test_shutdown')
+
+ await component.start(['test_shutdown'])
+ await component.shutdown()
+
+ assert c.shutdown.call_count == 1
+ assert c._component_state == 'Stopped'
+ assert not c._component_timer.running
+ assert c.stop.call_count == 1