fix(p7-cli-namespace): address c-review (setfacl -m mangled to --threads; two -s tests lost intent; label/doc sweeps)
- revert over-eager replacement of setfacl -m in test_features.py - use --chunk-serialization (+ set dirs) in the two append/append-verify chunk-serialization rejection unit tests so they exercise the real check - rename archive-negation integration test (--no-preserve is inert under archive because devices/specials force metadata) - update stale (-c)/(-m)/(-s)/(-f) display labels and RSYNC_COMPAT -c/-m refs - document the --no-perms negation limitation in the Wave A note
This commit is contained in:
@@ -101,7 +101,7 @@ class TestDeviceSpecial:
|
||||
assert os.major(st.st_rdev) == 1 and os.minor(st.st_rdev) == 3
|
||||
|
||||
def test_m_remove_source_files_keeps_recreated_fifo(self, shared_server):
|
||||
"""-m --remove-source-files --specials: a recreated FIFO must NOT be
|
||||
"""--threads --remove-source-files --specials: a recreated FIFO must NOT be
|
||||
acknowledged as a removable source (its outcome must not shift the
|
||||
per-file status stream, which would break the run and mis-remove the
|
||||
adjacent regular file). The regular file is removed; the FIFO stays."""
|
||||
@@ -122,7 +122,7 @@ class TestDeviceSpecial:
|
||||
|
||||
@pytest.mark.skipif(os.geteuid() != 0, reason="requires root to create device nodes")
|
||||
def test_m_remove_source_files_keeps_recreated_device(self, shared_server):
|
||||
"""Root-only: -m --remove-source-files --devices must not remove a
|
||||
"""Root-only: --threads --remove-source-files --devices must not remove a
|
||||
source device node the receiver recreated (mirrors the single-threaded
|
||||
behavior; the special is never acknowledged as a removable source)."""
|
||||
self._setup()
|
||||
@@ -320,7 +320,7 @@ class TestRemoveSourceFiles:
|
||||
with open(source_file, "wb") as f:
|
||||
f.write(b"keep after skip")
|
||||
|
||||
# The seed run preserves timestamps (-M) so the destination copy has the
|
||||
# The seed run preserves timestamps (--preserve) so the destination copy has the
|
||||
# source's exact mtime; otherwise the incremental skip would depend on
|
||||
# both writes landing in the same whole second (a race).
|
||||
result, _ = run_client(source, dest, flags=["--preserve"], port=shared_server.port)
|
||||
@@ -348,11 +348,15 @@ class TestArchiveMode:
|
||||
assert not missing, f"Missing: {missing}"
|
||||
assert not mismatches, f"Mismatch: {mismatches}"
|
||||
|
||||
def test_archive_implied_options_can_be_negated(self, shared_server):
|
||||
def test_archive_with_negated_links(self, shared_server):
|
||||
# --archive implies links + metadata + devices + specials. Devices/specials
|
||||
# force metadata transmission (recreating a node needs the metadata mode), so
|
||||
# the post-parse layer keeps use_metadata on even under --no-preserve; only the
|
||||
# independently-negatable --no-links actually takes effect here.
|
||||
clean_dir(DEST_DIR)
|
||||
result, dur = run_client(
|
||||
SOURCE_DIR, DEST_DIR,
|
||||
flags=["--archive", "--no-links", "--no-preserve"],
|
||||
flags=["--archive", "--no-links"],
|
||||
port=shared_server.port,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
@@ -1770,13 +1774,13 @@ class TestLogFileFormat:
|
||||
flags=["--log-file", log_path, "--log-file-format=%f %l", "--threads"],
|
||||
port=shared_server.port,
|
||||
)
|
||||
assert result.returncode == 0, f"log-file -m sync failed: {result.stderr[:200]}"
|
||||
assert result.returncode == 0, f"log-file --threads sync failed: {result.stderr[:200]}"
|
||||
assert os.path.exists(log_path), "--log-file created no log"
|
||||
with open(log_path, encoding="utf-8", errors="replace") as fh:
|
||||
content = fh.read()
|
||||
expected = {f"{os.path.join(source, rel)} {len(data)}" for rel, data in files.items()}
|
||||
for line in expected:
|
||||
assert line in content, f"log file (-m) missing {line!r}"
|
||||
assert line in content, f"log file (--threads) missing {line!r}"
|
||||
|
||||
|
||||
class TestDelayUpdates:
|
||||
@@ -4552,7 +4556,7 @@ class TestExtendedAttributes:
|
||||
pytest.skip("filesystem does not support xattrs")
|
||||
acl_blob = None
|
||||
if shutil.which("setfacl") is not None:
|
||||
acl = subprocess.run(["setfacl", "--threads", "o::r", f], capture_output=True, text=True)
|
||||
acl = subprocess.run(["setfacl", "-m", "o::r", f], capture_output=True, text=True)
|
||||
if acl.returncode == 0:
|
||||
try:
|
||||
acl_blob = os.getxattr(f, "system.posix_acl_access")
|
||||
|
||||
@@ -97,31 +97,31 @@ class TestSSHStandard:
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_multithreading(self):
|
||||
r = _run_ssh_test("SSH Multithreading (-m)", ["--threads"])
|
||||
r = _run_ssh_test("SSH Multithreading (--threads)", ["--threads"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_compression(self):
|
||||
r = _run_ssh_test("SSH Compression (-c)", ["-z"])
|
||||
r = _run_ssh_test("SSH Compression (-z)", ["-z"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_chunk_serialization(self):
|
||||
r = _run_ssh_test("SSH Chunk Serialization (-s)", ["--chunk-serialization"])
|
||||
r = _run_ssh_test("SSH Chunk Serialization (--chunk-serialization)", ["--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_compression_chunk(self):
|
||||
r = _run_ssh_test("SSH Compression + Chunk (-c -s)", ["-z", "--chunk-serialization"])
|
||||
r = _run_ssh_test("SSH Compression + Chunk (-z --chunk-serialization)", ["-z", "--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_multithread_compression(self):
|
||||
r = _run_ssh_test("SSH Multithread + Compression (-m -c)", ["--threads", "-z"])
|
||||
r = _run_ssh_test("SSH Multithread + Compression (--threads -z)", ["--threads", "-z"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_multithread_chunk(self):
|
||||
r = _run_ssh_test("SSH Multithread + Chunk (-m -s)", ["--threads", "--chunk-serialization"])
|
||||
r = _run_ssh_test("SSH Multithread + Chunk (--threads --chunk-serialization)", ["--threads", "--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_all_flags(self):
|
||||
r = _run_ssh_test("SSH All Flags (-m -c -s)", ["--threads", "-z", "--chunk-serialization"])
|
||||
r = _run_ssh_test("SSH All Flags (--threads -z --chunk-serialization)", ["--threads", "-z", "--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
|
||||
|
||||
@@ -234,8 +234,8 @@ class TestStopDelete:
|
||||
result, _ = run_client(source, dest, flags=["--threads", "--delete", "--stop-at=now+0s"],
|
||||
port=shared_server.port)
|
||||
assert result.returncode == 0, \
|
||||
f"-m --delete immediate stop failed: {(result.stderr or result.stdout)[:400]}"
|
||||
f"--threads --delete immediate stop failed: {(result.stderr or result.stdout)[:400]}"
|
||||
received = get_dest_received_dir(dest, source)
|
||||
mismatches, missing = verify_transfer(source, received)
|
||||
assert not mismatches and not missing, \
|
||||
f"-m --delete wiped source mirrors: missing={missing} mismatches={mismatches}"
|
||||
f"--threads --delete wiped source mirrors: missing={missing} mismatches={mismatches}"
|
||||
@@ -68,45 +68,45 @@ class TestTCPStandard:
|
||||
class TestTCPFlags:
|
||||
@pytest.mark.ci
|
||||
def test_multithreading(self, shared_server):
|
||||
r = _run_tcp_test("Multithreading (-m)", shared_server.port, ["--threads"])
|
||||
r = _run_tcp_test("Multithreading (--threads)", shared_server.port, ["--threads"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
@pytest.mark.ci
|
||||
def test_compression(self, shared_server):
|
||||
r = _run_tcp_test("Compression (-c)", shared_server.port, ["-z"])
|
||||
r = _run_tcp_test("Compression (-z)", shared_server.port, ["-z"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_compression_threads(self, shared_server):
|
||||
r = _run_tcp_test("Compression threads (-c --compress-threads=2)", shared_server.port,
|
||||
r = _run_tcp_test("Compression threads (-z --compress-threads=2)", shared_server.port,
|
||||
["-z", "--compress-threads=2"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_chunk_serialization(self, shared_server):
|
||||
r = _run_tcp_test("Chunk Serialization (-s)", shared_server.port, ["--chunk-serialization"])
|
||||
r = _run_tcp_test("Chunk Serialization (--chunk-serialization)", shared_server.port, ["--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_compression_chunk(self, shared_server):
|
||||
r = _run_tcp_test("Compression + Chunk (-c -s)", shared_server.port, ["-z", "--chunk-serialization"])
|
||||
r = _run_tcp_test("Compression + Chunk (-z --chunk-serialization)", shared_server.port, ["-z", "--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_multithread_compression(self, shared_server):
|
||||
r = _run_tcp_test("Multithreading + Compression (-m -c)", shared_server.port, ["--threads", "-z"])
|
||||
r = _run_tcp_test("Multithreading + Compression (--threads -z)", shared_server.port, ["--threads", "-z"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_multithread_chunk(self, shared_server):
|
||||
r = _run_tcp_test("Multithreading + Chunk (-m -s)", shared_server.port, ["--threads", "--chunk-serialization"])
|
||||
r = _run_tcp_test("Multithreading + Chunk (--threads --chunk-serialization)", shared_server.port, ["--threads", "--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_all_flags(self, shared_server):
|
||||
r = _run_tcp_test("Multithread + Compression + Chunk (-m -c -s)", shared_server.port, ["--threads", "-z", "--chunk-serialization"])
|
||||
r = _run_tcp_test("Multithread + Compression + Chunk (--threads -z --chunk-serialization)", shared_server.port, ["--threads", "-z", "--chunk-serialization"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_sendfile(self, shared_server):
|
||||
r = _run_tcp_test("Sendfile (-f)", shared_server.port, ["--sendfile"])
|
||||
r = _run_tcp_test("Sendfile (--sendfile)", shared_server.port, ["--sendfile"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
def test_sendfile_multithread(self, shared_server):
|
||||
r = _run_tcp_test("Sendfile + Multithreading (-f -m)", shared_server.port, ["--sendfile", "--threads"])
|
||||
r = _run_tcp_test("Sendfile + Multithreading (--sendfile --threads)", shared_server.port, ["--sendfile", "--threads"])
|
||||
assert r["status"] == "Success", r["error"]
|
||||
|
||||
|
||||
|
||||
@@ -2374,20 +2374,24 @@ static void test_parse_args_append_both() {
|
||||
|
||||
static void test_validate_config_append_rejects_chunk_serialization() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "--append", "-s", "/src", "/dst"};
|
||||
char* argv[] = {"fastsync", "--append", "--chunk-serialization", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), 0);
|
||||
cfg->send_directory = str_dup("/src");
|
||||
cfg->receive_root_directory = str_dup("/dst");
|
||||
EXPECT_FALSE(validate_config(cfg));
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
static void test_validate_config_append_verify_rejects_chunk_serialization() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "--append-verify", "-s", "/src", "/dst"};
|
||||
char* argv[] = {"fastsync", "--append-verify", "--chunk-serialization", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 5, argv, positional_args, &positional_count), 0);
|
||||
cfg->send_directory = str_dup("/src");
|
||||
cfg->receive_root_directory = str_dup("/dst");
|
||||
EXPECT_FALSE(validate_config(cfg));
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user