mirror of
https://github.com/codex-storage/deluge.git
synced 2025-01-24 10:18:51 +00:00
[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
This commit is contained in:
parent
0745c0eff8
commit
c38b4c72d0
@ -3,6 +3,9 @@
|
|||||||
# the additional special exception to link portions of this program with the OpenSSL library.
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
# See LICENSE for more details.
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
|
import inspect
|
||||||
|
import time
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_twisted
|
import pytest_twisted
|
||||||
@ -13,95 +16,59 @@ import deluge.component as component
|
|||||||
|
|
||||||
class ComponentTester(component.Component):
|
class ComponentTester(component.Component):
|
||||||
def __init__(self, name, depend=None):
|
def __init__(self, name, depend=None):
|
||||||
component.Component.__init__(self, name, depend=depend)
|
super().__init__(name, depend=depend)
|
||||||
self.start_count = 0
|
event_methods = ('start', 'update', 'stop', 'shutdown')
|
||||||
self.stop_count = 0
|
for event_method in event_methods:
|
||||||
|
setattr(self, event_method, Mock())
|
||||||
def start(self):
|
|
||||||
self.start_count += 1
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.stop_count += 1
|
|
||||||
|
|
||||||
|
|
||||||
class ComponentTesterDelayStart(ComponentTester):
|
class ComponentTesterDelayStart(ComponentTester):
|
||||||
def start(self):
|
def __init__(self, name, depend=None):
|
||||||
def do_sleep():
|
super().__init__(name, depend=depend)
|
||||||
import time
|
self.start = Mock(side_effect=self.delay)
|
||||||
|
|
||||||
time.sleep(1)
|
@pytest_twisted.inlineCallbacks
|
||||||
|
def delay(self):
|
||||||
d = threads.deferToThread(do_sleep)
|
yield threads.deferToThread(time.sleep, 0.5)
|
||||||
|
|
||||||
def on_done(result):
|
|
||||||
self.start_count += 1
|
|
||||||
|
|
||||||
return d.addCallback(on_done)
|
|
||||||
|
|
||||||
|
|
||||||
class ComponentTesterUpdate(component.Component):
|
def pytest_twisted_ensuredeferred_for_class(cls):
|
||||||
def __init__(self, name):
|
"""Applies ensureDeferred to all async test_ methods in class"""
|
||||||
component.Component.__init__(self, name)
|
for name, method in inspect.getmembers(cls, inspect.iscoroutinefunction):
|
||||||
self.counter = 0
|
if name.startswith('test'):
|
||||||
self.start_count = 0
|
setattr(cls, name, pytest_twisted.ensureDeferred(method))
|
||||||
self.stop_count = 0
|
return cls
|
||||||
|
|
||||||
def update(self):
|
|
||||||
self.counter += 1
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.stop_count += 1
|
|
||||||
|
|
||||||
|
|
||||||
class ComponentTesterShutdown(component.Component):
|
|
||||||
def __init__(self, name):
|
|
||||||
component.Component.__init__(self, name)
|
|
||||||
self.shutdowned = False
|
|
||||||
self.stop_count = 0
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
self.shutdowned = True
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.stop_count += 1
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_twisted_ensuredeferred_for_class
|
||||||
@pytest.mark.usefixtures('component')
|
@pytest.mark.usefixtures('component')
|
||||||
class TestComponent:
|
class TestComponent:
|
||||||
def test_start_component(self):
|
async def test_start_component(self):
|
||||||
def on_start(result, c):
|
c = ComponentTester('test_start')
|
||||||
assert c._component_state == 'Started'
|
await component.start(['test_start'])
|
||||||
assert c.start_count == 1
|
|
||||||
|
|
||||||
c = ComponentTester('test_start_c1')
|
assert c._component_state == 'Started'
|
||||||
d = component.start(['test_start_c1'])
|
assert c.start.call_count == 1
|
||||||
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_stop_depends(self):
|
||||||
c1 = ComponentTester('test_start_depends_c1')
|
c1 = ComponentTester('test_start_depends_c1')
|
||||||
c2 = ComponentTester('test_start_depends_c2', depend=['test_start_depends_c1'])
|
c2 = ComponentTester('test_start_depends_c2', depend=['test_start_depends_c1'])
|
||||||
|
|
||||||
d = component.start(['test_start_depends_c2'])
|
await component.start('test_start_depends_c2')
|
||||||
d.addCallback(on_start, c1, c2)
|
|
||||||
return d
|
|
||||||
|
|
||||||
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')
|
c1 = ComponentTesterDelayStart('test_start_all_c1')
|
||||||
c2 = ComponentTester('test_start_all_c2', depend=['test_start_all_c4'])
|
c2 = ComponentTester('test_start_all_c2', depend=['test_start_all_c4'])
|
||||||
c3 = ComponentTesterDelayStart(
|
c3 = ComponentTesterDelayStart(
|
||||||
@ -110,141 +77,108 @@ class TestComponent:
|
|||||||
c4 = ComponentTester('test_start_all_c4', depend=['test_start_all_c3'])
|
c4 = ComponentTester('test_start_all_c4', depend=['test_start_all_c3'])
|
||||||
c5 = ComponentTester('test_start_all_c5')
|
c5 = ComponentTester('test_start_all_c5')
|
||||||
|
|
||||||
d = component.start()
|
await component.start()
|
||||||
return (d, c1, c2, c3, c4, c5)
|
return c1, c2, c3, c4, c5
|
||||||
|
|
||||||
def finish_start_with_depends(self, *args):
|
def finish_start_with_depends(self, *args):
|
||||||
for c in args[1:]:
|
for c in args[1:]:
|
||||||
component.deregister(c)
|
component.deregister(c)
|
||||||
|
|
||||||
def test_start_all(self):
|
async def test_start_all(self):
|
||||||
def on_start(*args):
|
components = await self.start_with_depends()
|
||||||
for c in args[1:]:
|
for c in components:
|
||||||
assert c._component_state == 'Started'
|
assert c._component_state == 'Started'
|
||||||
assert c.start_count == 1
|
assert c.start.call_count == 1
|
||||||
|
|
||||||
ret = self.start_with_depends()
|
self.finish_start_with_depends(components)
|
||||||
ret[0].addCallback(on_start, *ret[1:])
|
|
||||||
ret[0].addCallback(self.finish_start_with_depends, *ret[1:])
|
|
||||||
return ret[0]
|
|
||||||
|
|
||||||
def test_register_exception(self):
|
def test_register_exception(self):
|
||||||
ComponentTester('test_register_exception_c1')
|
ComponentTester('test_register_exception')
|
||||||
with pytest.raises(component.ComponentAlreadyRegistered):
|
with pytest.raises(component.ComponentAlreadyRegistered):
|
||||||
ComponentTester(
|
ComponentTester(
|
||||||
'test_register_exception_c1',
|
'test_register_exception',
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_stop_component(self):
|
async def test_stop(self):
|
||||||
def on_stop(result, c):
|
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 c._component_state == 'Stopped'
|
||||||
assert not c._component_timer.running
|
assert c.stop.call_count == 1
|
||||||
assert c.stop_count == 1
|
|
||||||
|
|
||||||
def on_start(result, c):
|
self.finish_start_with_depends(components)
|
||||||
assert c._component_state == 'Started'
|
|
||||||
return component.stop(['test_stop_component_c1']).addCallback(on_stop, c)
|
|
||||||
|
|
||||||
c = ComponentTesterUpdate('test_stop_component_c1')
|
async def test_update(self):
|
||||||
d = component.start(['test_stop_component_c1'])
|
c = ComponentTester('test_update')
|
||||||
d.addCallback(on_start, c)
|
init_update_count = int(c.update.call_count)
|
||||||
return d
|
await component.start(['test_update'])
|
||||||
|
|
||||||
def test_stop_all(self):
|
assert c._component_timer
|
||||||
def on_stop(result, *args):
|
assert c._component_timer.running
|
||||||
for c in args:
|
assert c.update.call_count != init_update_count
|
||||||
assert c._component_state == 'Stopped'
|
await component.stop()
|
||||||
assert c.stop_count == 1
|
|
||||||
|
|
||||||
def on_start(result, *args):
|
async def test_pause(self):
|
||||||
for c in args:
|
c = ComponentTester('test_pause')
|
||||||
assert c._component_state == 'Started'
|
init_update_count = int(c.update.call_count)
|
||||||
return component.stop().addCallback(on_stop, *args)
|
|
||||||
|
|
||||||
ret = self.start_with_depends()
|
await component.start(['test_pause'])
|
||||||
ret[0].addCallback(on_start, *ret[1:])
|
|
||||||
ret[0].addCallback(self.finish_start_with_depends, *ret[1:])
|
|
||||||
return ret[0]
|
|
||||||
|
|
||||||
def test_update(self):
|
assert c._component_timer
|
||||||
def on_start(result, c1, counter):
|
assert c.update.call_count != init_update_count
|
||||||
assert c1._component_timer
|
|
||||||
assert c1._component_timer.running
|
|
||||||
assert c1.counter != counter
|
|
||||||
return component.stop()
|
|
||||||
|
|
||||||
c1 = ComponentTesterUpdate('test_update_c1')
|
await component.pause(['test_pause'])
|
||||||
cnt = int(c1.counter)
|
|
||||||
d = component.start(['test_update_c1'])
|
|
||||||
|
|
||||||
d.addCallback(on_start, c1, cnt)
|
assert c._component_state == 'Paused'
|
||||||
return d
|
assert c.update.call_count != init_update_count
|
||||||
|
assert not c._component_timer.running
|
||||||
|
|
||||||
def test_pause(self):
|
async def test_component_start_error(self):
|
||||||
def on_pause(result, c1, counter):
|
ComponentTester('test_start_error')
|
||||||
assert c1._component_state == 'Paused'
|
await component.start(['test_start_error'])
|
||||||
assert c1.counter != counter
|
await component.pause(['test_start_error'])
|
||||||
assert not c1._component_timer.running
|
test_comp = component.get('test_start_error')
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
@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'):
|
with pytest.raises(component.ComponentException, match='Current state: Paused'):
|
||||||
yield test_comp._component_start()
|
await test_comp._component_start()
|
||||||
|
|
||||||
@pytest_twisted.inlineCallbacks
|
async def test_start_paused_error(self):
|
||||||
def test_start_paused_error(self):
|
name = 'test_pause_error'
|
||||||
ComponentTesterUpdate('test_pause_c1')
|
ComponentTester(name)
|
||||||
yield component.start(['test_pause_c1'])
|
await component.start([name])
|
||||||
yield component.pause(['test_pause_c1'])
|
await component.pause([name])
|
||||||
|
|
||||||
# Deferreds that fail in component have to error handler which results in
|
(failure, error), *_ = await component.start()
|
||||||
# twisted doing a log.err call which causes the test to fail.
|
assert (failure, error.type, error.value.message) == (
|
||||||
# Prevent failure by ignoring the exception
|
defer.FAILURE,
|
||||||
# self._observer._ignoreErrors(component.ComponentException)
|
component.ComponentException,
|
||||||
|
|
||||||
result = yield component.start()
|
|
||||||
assert [(result[0][0], result[0][1].value)] == [
|
|
||||||
(
|
(
|
||||||
defer.FAILURE,
|
f'Trying to start component "{name}" but it is '
|
||||||
component.ComponentException(
|
'not in a stopped state. Current state: Paused'
|
||||||
'Trying to start component "%s" but it is '
|
),
|
||||||
'not in a stopped state. Current state: %s'
|
)
|
||||||
% ('test_pause_c1', 'Paused'),
|
|
||||||
'',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
def test_shutdown(self):
|
async def test_shutdown(self):
|
||||||
def on_shutdown(result, c1):
|
c = ComponentTester('test_shutdown')
|
||||||
assert c1.shutdowned
|
|
||||||
assert c1._component_state == 'Stopped'
|
|
||||||
assert c1.stop_count == 1
|
|
||||||
|
|
||||||
def on_start(result, c1):
|
await component.start(['test_shutdown'])
|
||||||
d = component.shutdown()
|
await component.shutdown()
|
||||||
d.addCallback(on_shutdown, c1)
|
|
||||||
return d
|
|
||||||
|
|
||||||
c1 = ComponentTesterShutdown('test_shutdown_c1')
|
assert c.shutdown.call_count == 1
|
||||||
d = component.start(['test_shutdown_c1'])
|
assert c._component_state == 'Stopped'
|
||||||
d.addCallback(on_start, c1)
|
assert not c._component_timer.running
|
||||||
return d
|
assert c.stop.call_count == 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user