test: append/append-verify resume coverage

- CLI: --append/--append-verify acceptance (parse + imply --incremental,
  validate) and incompatibility rejection with -s and --whole-file; both
  removed from the unimplemented reject list.
- Config: on-the-wire append/append_verify round-trip.
- Unit: append_resume_eligible / append_tail_length pure resume math.
- Integration (test_append.py): matching-prefix resume is byte-identical and
  tail-only (wire bytes << source size); --append with a wrong prefix keeps
  prefix+tail (rsync parity) while --append-verify detects the mismatch and
  falls back to a byte-exact full transfer; --append with --inplace and -m.
This commit is contained in:
2026-09-07 15:43:33 +02:00
parent 6ad3887aa1
commit ffdbb6568f
4 changed files with 297 additions and 2 deletions
+44
View File
@@ -754,6 +754,49 @@ static void test_config_is_remote_dest() {
EXPECT_TRUE(config_is_remote_dest("user@host:"));
}
/* The append-mode fields cross the wire unchanged: --append and --append-verify
are negotiated to the receiver so it knows to reply STATUS_APPEND on a
shorter destination. */
static void test_config_append_wire_roundtrip() {
if (is_running_under_valgrind())
return;
struct {
bool append, append_verify;
} cases[] = {{true, false}, {false, true}, {true, true}, {false, false}};
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
int p[2];
EXPECT_EQ_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, p), 0);
pid_t pid = fork();
if (pid == 0) {
close(p[1]);
io_set_fds(p[0], p[0]);
Config* recv = config_receive(p[0]);
bool ok = recv != NULL;
if (ok)
ok = recv->append == cases[i].append && recv->append_verify == cases[i].append_verify;
config_delete(recv);
close(p[0]);
_exit(ok ? 0 : 1);
} else {
close(p[0]);
io_set_fds(p[1], p[1]);
Config* send_cfg = config_create();
EXPECT_NOT_NULL(send_cfg);
send_cfg->send_directory = str_dup("/src");
send_cfg->receive_root_directory = str_dup("/dst");
send_cfg->append = cases[i].append;
send_cfg->append_verify = cases[i].append_verify;
bool sent = config_send(p[1], send_cfg);
int status;
waitpid(pid, &status, 0);
close(p[1]);
config_delete(send_cfg);
EXPECT_TRUE(sent);
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
}
}
void test_config() {
test_config_lifecycle();
test_config_ssh_dest();
@@ -771,6 +814,7 @@ void test_config() {
test_config_delete_timing_wire_roundtrip();
test_config_delete_timing_conflict_rejected();
test_config_delete_policy_wire_roundtrip();
test_config_append_wire_roundtrip();
test_config_basis_roundtrip();
test_config_basis_wire_rejects_escaping();
test_config_basis_normalization();