From f7c6c91e138b0cff2a49f85424042773aeda468c Mon Sep 17 00:00:00 2001 From: TapTap Date: Sat, 5 Sep 2026 13:16:42 +0200 Subject: [PATCH] fix: set *failed on delta oversize branch; add -m RSF skip test Review nits from independent review of the four fix branches: - receive_delta_file STATUS_NEXT oversize branch now sets *failed=true - receive_incremental_check oversize branch returns NULL (receiver sends the single STATUS_ERROR) instead of double-sending - add multithreaded -m --ignore-existing --remove-source-files integration coverage so the writer-thread outcome path is exercised --- src/shared/file_receive.c | 2 +- tests/integration/test_features.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/shared/file_receive.c b/src/shared/file_receive.c index 30cecb4..2b8cd2d 100644 --- a/src/shared/file_receive.c +++ b/src/shared/file_receive.c @@ -363,6 +363,7 @@ static File* receive_delta_file(int fd, const Config* config, const char* check_ data_destroy(uncompressed); file_destroy(file); send_status(fd, STATUS_ERROR); + *failed = true; return NULL; } file_data = uncompressed; @@ -581,7 +582,6 @@ File* receive_incremental_check(int fd, const Config* config, bool* skipped) { if (uncompressed->size > MAX_FILE_DATA_SIZE) { data_destroy(uncompressed); file_destroy(file); - send_status(fd, STATUS_ERROR); return NULL; } file_data = uncompressed; diff --git a/tests/integration/test_features.py b/tests/integration/test_features.py index 69b8c90..4c8c865 100644 --- a/tests/integration/test_features.py +++ b/tests/integration/test_features.py @@ -889,6 +889,30 @@ class TestRemoveSourceFilesSkips: assert os.path.isfile(source_file) assert _read_file(received_file) == b"newer destination payload" + def test_multithreaded_ignore_existing_keeps_skipped_source(self, shared_server): + """The multithreaded writer path must also report per-file outcomes so a + --remove-source-files sender does not delete skipped sources.""" + source = os.path.join(TEST_DATA_DIR, "remove_rsf_mt_ignore_src") + dest = os.path.join(TEST_DATA_DIR, "remove_rsf_mt_ignore_dst") + clean_dir(source) + clean_dir(dest) + source_file = os.path.join(source, "file.txt") + with open(source_file, "wb") as f: + f.write(b"payload") + + result, _ = run_client(source, dest, port=shared_server.port) + assert result.returncode == 0 + + result, _ = run_client(source, dest, + flags=["--remove-source-files", "--ignore-existing", "-m"], + port=shared_server.port) + assert result.returncode == 0, f"Sync failed: {result.stderr[:200]}" + # Destination already has the file, so the receiver (writer thread) + # skips it; the source must survive. + assert os.path.isfile(source_file) + received = get_dest_received_dir(dest, source) + assert _read_file(os.path.join(received, "file.txt")) == b"payload" + class TestBackup: def _sync(self, source, dest, flags, port):