fix(parity): dry-run delete protections, delete-delay actual-removal budget, --info name2
- -n/--delete sends the same protected/size-skipped/scope as a real run, so the read-only would-delete walk no longer over-reports (row -> caveat) - --delete-delay charges --max-delete on actual removals and recursively re-scans a refilled deferred directory at commit; independent deferred cap - --info=name emits the leading ./ root line and name2 'is uptodate' lines - differential tests promoted from residual pins to rsync parity assertions
This commit is contained in:
+28
-29
@@ -23,10 +23,9 @@ static void send_plan_frame(int fd, const char* dir) {
|
||||
}
|
||||
|
||||
/* --delete-delay: a directory snapshotted into the plan that is refilled before
|
||||
* the commit must NOT be counted as deleted once its unlink fails ENOTEMPTY.
|
||||
* Regression for delete_plan.c counting at snapshot (defer_add) instead of at
|
||||
* the actual removal. */
|
||||
static void test_delete_delay_refilled_dir_not_counted(void) {
|
||||
* the commit is re-scanned and removed recursively (rsync parity). Regression
|
||||
* for the old single-unlink ENOTEMPTY path that left the directory behind. */
|
||||
static void test_delete_delay_refilled_dir_removed_recursively(void) {
|
||||
char root[] = "/tmp/fastsync_dp_refill_XXXXXX";
|
||||
EXPECT_TRUE(mkdtemp(root) != NULL);
|
||||
char extra[1024];
|
||||
@@ -57,16 +56,14 @@ static void test_delete_delay_refilled_dir_not_counted(void) {
|
||||
close(fd);
|
||||
|
||||
EXPECT_EQ_INT(delete_plan_session_commit(session, config), DELETE_COMMIT_OK);
|
||||
/* ENOTEMPTY: the directory survives, so it must not be reported as deleted. */
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
||||
/* The late content and the directory itself are both removed. */
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 2);
|
||||
struct stat st;
|
||||
EXPECT_EQ_INT(lstat(extra, &st), 0);
|
||||
EXPECT_TRUE(lstat(extra, &st) != 0);
|
||||
|
||||
delete_plan_session_destroy(session);
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
unlink(refill);
|
||||
rmdir(extra);
|
||||
rmdir(root);
|
||||
config_delete(config);
|
||||
}
|
||||
@@ -106,8 +103,9 @@ static void test_delete_delay_removed_file_counted(void) {
|
||||
config_delete(config);
|
||||
}
|
||||
|
||||
/* --max-delete still bounds the deferred plan; the actual (removed) count must
|
||||
* not exceed the limit even though more extras existed. */
|
||||
/* --max-delete is charged on actual removals, not at plan/snapshot time: after
|
||||
* receiving the plans the budget is untouched, and only the commit removes up to
|
||||
* the limit. */
|
||||
static void test_delete_delay_max_delete_bounds_actual(void) {
|
||||
char root[] = "/tmp/fastsync_dp_max_XXXXXX";
|
||||
EXPECT_TRUE(mkdtemp(root) != NULL);
|
||||
@@ -133,8 +131,11 @@ static void test_delete_delay_max_delete_bounds_actual(void) {
|
||||
EXPECT_NOT_NULL(session);
|
||||
send_plan_frame(p[1], ".");
|
||||
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
||||
EXPECT_TRUE(delete_plan_session_limit_reached(session));
|
||||
/* Nothing removed yet, so the budget is not consumed at snapshot time. */
|
||||
EXPECT_FALSE(delete_plan_session_limit_reached(session));
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
||||
EXPECT_EQ_INT(delete_plan_session_commit(session, config), DELETE_COMMIT_LIMIT_REACHED);
|
||||
EXPECT_TRUE(delete_plan_session_limit_reached(session));
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 1);
|
||||
|
||||
delete_plan_session_destroy(session);
|
||||
@@ -149,13 +150,12 @@ static void test_delete_delay_max_delete_bounds_actual(void) {
|
||||
config_delete(config);
|
||||
}
|
||||
|
||||
/* --max-delete is charged at plan/snapshot time, not at actual removal: a
|
||||
* deferred entry that survives ENOTEMPTY still consumes its budget slot, so a
|
||||
* later directory's extra is skipped even though nothing was actually removed.
|
||||
* The reported count stays 0 (actual removals) while the run is partial. The
|
||||
* two plans are sent as separate frames for "a" then "b", so the ordering that
|
||||
* decides which entry gets the budget is deterministic (unlike readdir order). */
|
||||
static void test_delete_delay_refilled_dir_charges_budget_at_plan(void) {
|
||||
/* --max-delete is charged on ACTUAL removals: the refilled directory's late
|
||||
* content is removed first (consuming the single budget slot), so the directory
|
||||
* itself and the later extra are skipped, matching rsync. The two plans are
|
||||
* sent as separate frames for "a" then "b", so the ordering that decides which
|
||||
* entry gets the budget is deterministic (unlike readdir order). */
|
||||
static void test_delete_delay_actual_removal_charges_budget(void) {
|
||||
char root[] = "/tmp/fastsync_dp_planbudget_XXXXXX";
|
||||
EXPECT_TRUE(mkdtemp(root) != NULL);
|
||||
char adir[1024], bdir[1024], xdir[1024], ydir[1024];
|
||||
@@ -180,18 +180,16 @@ static void test_delete_delay_refilled_dir_charges_budget_at_plan(void) {
|
||||
|
||||
DeletePlanSession* session = delete_plan_session_create(config);
|
||||
EXPECT_NOT_NULL(session);
|
||||
/* Plan "a" first: its empty extra dir snapshots and charges the budget. */
|
||||
/* Both plan snapshots are taken; neither consumes budget yet. */
|
||||
send_plan_frame(p[1], "a");
|
||||
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
||||
EXPECT_FALSE(delete_plan_session_limit_reached(session));
|
||||
/* Plan "b": the budget is already spent at snapshot time, so b/y is skipped
|
||||
even though a/x has not (and will not) be removed. */
|
||||
send_plan_frame(p[1], "b");
|
||||
EXPECT_EQ_INT(delete_plan_session_receive(session, config, p[0]), 0);
|
||||
EXPECT_TRUE(delete_plan_session_limit_reached(session));
|
||||
EXPECT_FALSE(delete_plan_session_limit_reached(session));
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
||||
|
||||
/* Refill a/x so its deferred rmdir fails ENOTEMPTY. */
|
||||
/* Refill a/x after its plan: the recursive commit must remove this content. */
|
||||
char refill[1200];
|
||||
snprintf(refill, sizeof(refill), "%s/new.txt", xdir);
|
||||
int fd = open(refill, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
@@ -199,16 +197,17 @@ static void test_delete_delay_refilled_dir_charges_budget_at_plan(void) {
|
||||
close(fd);
|
||||
|
||||
EXPECT_EQ_INT(delete_plan_session_commit(session, config), DELETE_COMMIT_LIMIT_REACHED);
|
||||
/* Nothing was actually removed, and the plan-time budget still stopped b/y. */
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 0);
|
||||
/* The one budget slot removed the late content; the two directories survive. */
|
||||
EXPECT_EQ_INT((int)delete_plan_session_deleted(session), 1);
|
||||
EXPECT_TRUE(delete_plan_session_limit_reached(session));
|
||||
struct stat st;
|
||||
EXPECT_TRUE(lstat(refill, &st) != 0);
|
||||
EXPECT_EQ_INT(lstat(xdir, &st), 0);
|
||||
EXPECT_EQ_INT(lstat(ydir, &st), 0);
|
||||
|
||||
delete_plan_session_destroy(session);
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
unlink(refill);
|
||||
rmdir(xdir);
|
||||
rmdir(ydir);
|
||||
rmdir(adir);
|
||||
@@ -218,8 +217,8 @@ static void test_delete_delay_refilled_dir_charges_budget_at_plan(void) {
|
||||
}
|
||||
|
||||
void test_delete_plan(void) {
|
||||
test_delete_delay_refilled_dir_not_counted();
|
||||
test_delete_delay_refilled_dir_removed_recursively();
|
||||
test_delete_delay_removed_file_counted();
|
||||
test_delete_delay_max_delete_bounds_actual();
|
||||
test_delete_delay_refilled_dir_charges_budget_at_plan();
|
||||
test_delete_delay_actual_removal_charges_budget();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user