Skip to content

Commit 627a394

Browse files
committed
Added tests for the 'start', 'restart', 'stop', 'request_stop', 'enqueue', and 'flush_skipped' RSyncer class methods
1 parent 1cad035 commit 627a394

1 file changed

Lines changed: 202 additions & 8 deletions

File tree

tests/client/test_rsync.py

Lines changed: 202 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import threading
33
from datetime import datetime
44
from pathlib import Path
5+
from unittest import mock
56
from unittest.mock import MagicMock
67

78
import pytest
@@ -38,8 +39,6 @@ def test_rsyncer_initialises(
3839
# Assign values to parameters
3940
basepath_local = tmp_path / "local"
4041
basepath_remote = tmp_path / "remote"
41-
do_transfer = True
42-
remove_files = True
4342

4443
# Create a test substrings blacklist dict
4544
substrings_blacklist = {
@@ -58,8 +57,6 @@ def test_rsyncer_initialises(
5857
server_url=mock_server_url,
5958
stop_callback=dummy_callback,
6059
local=is_local,
61-
do_transfer=do_transfer,
62-
remove_files=remove_files,
6360
substrings_blacklist=substrings_blacklist,
6461
end_time=timestamp,
6562
)
@@ -71,14 +68,12 @@ def test_rsyncer_initialises(
7168
assert rsyncer._server_url == mock_server_url
7269
assert rsyncer._stop_callback == dummy_callback
7370
assert rsyncer._local == is_local
74-
assert rsyncer._do_transfer == do_transfer
75-
assert rsyncer._remove_files == remove_files
71+
assert rsyncer._do_transfer
72+
assert not rsyncer._remove_files
7673
assert rsyncer._required_substrings_for_removal == []
7774
assert rsyncer._substrings_blacklist == substrings_blacklist
7875
assert rsyncer._notify
7976
assert rsyncer._end_time == timestamp
80-
assert not rsyncer._finalising
81-
assert not rsyncer._finalised
8277
assert rsyncer._skipped_files == []
8378
assert (
8479
rsyncer._remote == str(basepath_remote)
@@ -91,6 +86,145 @@ def test_rsyncer_initialises(
9186
assert isinstance(rsyncer.thread, threading.Thread)
9287
assert not rsyncer._stopping
9388
assert not rsyncer._halt_thread
89+
assert not rsyncer._finalising
90+
assert not rsyncer._finalised
91+
92+
assert rsyncer.status == "ready"
93+
94+
# Check that it's represented correctly
95+
assert (
96+
str(rsyncer)
97+
== f"<RSyncer ({rsyncer._basepath}{rsyncer._remote}) [{rsyncer.status}]"
98+
)
99+
100+
101+
@pytest.mark.parametrize("rsyncer_status", ("default", "is_alive", "stopping"))
102+
def test_rsyncer_start(
103+
tmp_path: Path,
104+
mock_server_url: MagicMock,
105+
rsyncer_status: str,
106+
):
107+
# Mock the thread attribute so that it doesn't start an actual Thread
108+
mock_thread = MagicMock()
109+
mock_thread.start.return_value = None
110+
mock_thread.is_alive.return_value = rsyncer_status == "is_alive"
111+
112+
# Initialise the RSyncer and patch the attributes to be tested
113+
rsyncer = RSyncer(
114+
basepath_local=tmp_path / "local",
115+
basepath_remote=tmp_path / "remote",
116+
rsync_module=mock.ANY,
117+
server_url=mock_server_url,
118+
)
119+
rsyncer.thread = mock_thread
120+
rsyncer._stopping = rsyncer_status == "stopping"
121+
122+
# Start the RSyncer
123+
if rsyncer_status == "default":
124+
rsyncer.start()
125+
mock_thread.start.assert_called_once()
126+
else:
127+
with pytest.raises(RuntimeError):
128+
rsyncer.start()
129+
130+
131+
def test_rsyncer_restart(
132+
mocker: MockerFixture,
133+
tmp_path: Path,
134+
mock_server_url: MagicMock,
135+
):
136+
# Patch the 'start' class method, which is called by 'restart'
137+
mock_start = mocker.patch.object(RSyncer, "start")
138+
mock_start.return_value = None
139+
140+
# Mock the thread and the attributes used
141+
mock_thread = MagicMock()
142+
mock_thread.join.return_value = None
143+
144+
# Initialise the RSyncer and patch the attributes to be tested
145+
rsyncer = RSyncer(
146+
basepath_local=tmp_path / "local",
147+
basepath_remote=tmp_path / "remote",
148+
rsync_module=mock.ANY,
149+
server_url=mock_server_url,
150+
)
151+
rsyncer.thread = mock_thread
152+
153+
# Run 'restart'
154+
rsyncer.restart()
155+
156+
# Check that the correct calls and attributes are present
157+
mock_thread.join.assert_called_once()
158+
assert not rsyncer._halt_thread
159+
assert isinstance(rsyncer.thread, threading.Thread)
160+
mock_start.assert_called_once()
161+
162+
163+
@pytest.mark.parametrize("thread_is_alive", (True, False))
164+
def test_rsyncer_stop(
165+
tmp_path: Path,
166+
mock_server_url: MagicMock,
167+
thread_is_alive: bool,
168+
):
169+
# Mock the thread
170+
mock_thread = MagicMock()
171+
mock_thread.is_alive.return_value = thread_is_alive
172+
mock_thread.join.return_value = None
173+
174+
# Mock the queue
175+
mock_queue = MagicMock()
176+
mock_queue.join.return_value = None
177+
mock_queue.put.return_value = None
178+
179+
# Initialise the RSyncer and patch the attributes to be tested
180+
rsyncer = RSyncer(
181+
basepath_local=tmp_path / "local",
182+
basepath_remote=tmp_path / "remote",
183+
rsync_module=mock.ANY,
184+
server_url=mock_server_url,
185+
)
186+
rsyncer.thread = mock_thread
187+
rsyncer.queue = mock_queue
188+
189+
# Check that initial attributes are as expected
190+
assert not rsyncer._stopping
191+
assert not rsyncer._halt_thread
192+
193+
# Run 'stop' and check that the calls are as expected
194+
rsyncer.stop()
195+
196+
assert rsyncer._stopping
197+
assert rsyncer._halt_thread
198+
if thread_is_alive:
199+
mock_queue.join.assert_called_once()
200+
mock_queue.put.assert_called_with(None)
201+
mock_thread.join.assert_called_once()
202+
else:
203+
mock_queue.join.assert_not_called()
204+
mock_queue.put.assert_not_called()
205+
mock_thread.join.assert_not_called()
206+
207+
208+
def test_rsyncer_request_stop(
209+
tmp_path: Path,
210+
mock_server_url: MagicMock,
211+
):
212+
# Initialise the RSyncer
213+
rsyncer = RSyncer(
214+
basepath_local=tmp_path / "local",
215+
basepath_remote=tmp_path / "remote",
216+
rsync_module=mock.ANY,
217+
server_url=mock_server_url,
218+
)
219+
220+
# Check that initial attributes are as expected
221+
assert not rsyncer._stopping
222+
assert not rsyncer._halt_thread
223+
224+
# Run 'request_stop' and check that attributes have changed
225+
rsyncer.request_stop()
226+
assert rsyncer._stopping
227+
assert rsyncer._halt_thread
94228

95229

96230
@pytest.fixture
@@ -310,3 +444,63 @@ def test_rsyncer_finalise(
310444
# Check that the callback was set at the end
311445
if use_callback:
312446
mock_callback.assert_called_once()
447+
448+
449+
@pytest.mark.parametrize("is_stopping", (True, False))
450+
def test_rsyncer_enqueue(
451+
tmp_path: Path,
452+
mock_server_url: MagicMock,
453+
is_stopping: bool,
454+
):
455+
# Mock the queue
456+
mock_queue = MagicMock()
457+
mock_queue.put.return_value = None
458+
459+
# Initialise the RSyncer and patch the attributes used by the test
460+
rsyncer = RSyncer(
461+
basepath_local=tmp_path / "local",
462+
basepath_remote=tmp_path / "remote",
463+
rsync_module=mock.ANY,
464+
server_url=mock_server_url,
465+
)
466+
rsyncer._stopping = is_stopping
467+
rsyncer.queue = mock_queue
468+
469+
# Run enqueue with a test file and check that the expected calls were made
470+
rsyncer.enqueue(Path("test_file"))
471+
if is_stopping:
472+
mock_queue.put.assert_not_called()
473+
else:
474+
mock_queue.put.assert_called_once_with(
475+
(tmp_path / "local" / "test_file").absolute()
476+
)
477+
478+
479+
def test_rsyncer_flush_skipped(
480+
tmp_path: Path,
481+
mock_server_url: MagicMock,
482+
):
483+
# Mock the queue
484+
mock_queue = MagicMock()
485+
mock_queue.put.return_value = None
486+
487+
# Create a list of test files
488+
skipped_files = [
489+
tmp_path / "local" / f"file_{str(n).zfill(2)}.txt" for n in range(20)
490+
]
491+
492+
# Initialise the RSyncer and patch the attributes used by the test
493+
rsyncer = RSyncer(
494+
basepath_local=tmp_path / "local",
495+
basepath_remote=tmp_path / "remote",
496+
rsync_module=mock.ANY,
497+
server_url=mock_server_url,
498+
)
499+
rsyncer.queue = mock_queue
500+
rsyncer._skipped_files = skipped_files
501+
502+
# Run 'flush_skipped' and check that it works as intended
503+
rsyncer.flush_skipped()
504+
for f in skipped_files:
505+
mock_queue.put.assert_any_call(f)
506+
assert rsyncer._skipped_files == []

0 commit comments

Comments
 (0)