add autotest for message trashing

This commit is contained in:
Oswald Buddenhagen 2022-04-04 18:55:14 +02:00
parent 4b0c5a0cd5
commit 8363dbf2d1

View File

@ -128,6 +128,8 @@ sub parse_msg($$$$$)
my $bs = $$cs{$t}; my $bs = $$cs{$t};
my ($msr, $n2ur) = ($$bs{messages}, $$bs{num2uid}); my ($msr, $n2ur) = ($$bs{messages}, $$bs{num2uid});
$$cs{"${t}_trash"}{$num} = 1
if ($sts =~ s,^#,,);
my $ouid; my $ouid;
my $uids = \@{$$n2ur{$num}}; my $uids = \@{$$n2ur{$num}};
if ($sts =~ s,^&$,,) { if ($sts =~ s,^&$,,) {
@ -212,6 +214,9 @@ sub parse_chan($;$)
# messages: { uid => [ subject, flags ], ... } # messages: { uid => [ subject, flags ], ... }
far => { max_uid => 0, messages => {}, num2uid => {} }, far => { max_uid => 0, messages => {}, num2uid => {} },
near => { max_uid => 0, messages => {}, num2uid => {} }, near => { max_uid => 0, messages => {}, num2uid => {} },
# trashed messages: { subject => is_placeholder, ... }
far_trash => { },
near_trash => { },
# entries: [ [ far_uid, near_uid, flags ], ... ] # entries: [ [ far_uid, near_uid, flags ], ... ]
state => { entries => [] } state => { entries => [] }
}; };
@ -352,30 +357,17 @@ sub readfile($;$)
} }
# $path # $path
sub readbox($) sub readbox_impl($$)
{ {
my $bn = shift; my ($bn, $cb) = @_;
(-d $bn) or
die "No mailbox '$bn'.\n";
(-d $bn."/tmp" and -d $bn."/new" and -d $bn."/cur") or (-d $bn."/tmp" and -d $bn."/new" and -d $bn."/cur") or
die "Invalid mailbox '$bn'.\n"; die "Invalid mailbox '$bn'.\n";
my $uidval = readfile($bn."/.uidvalidity", CHOMP);
die "Cannot read UID validity of mailbox '$bn': $!\n" if (!$uidval);
my $mu = $$uidval[1];
my %ms = ();
for my $d ("cur", "new") { for my $d ("cur", "new") {
opendir(DIR, $bn."/".$d) or next; opendir(DIR, $bn."/".$d) or next;
for my $f (grep(!/^\.\.?$/, readdir(DIR))) { for my $f (grep(!/^\.\.?$/, readdir(DIR))) {
my ($uid, $flg, $ph, $num);
if ($f =~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) {
($uid, $flg) = (int($1), $2);
} else {
print STDERR "unrecognided file name '$f' in '$bn'.\n";
exit 1;
}
open(FILE, "<", $bn."/".$d."/".$f) or die "Cannot read message '$f' in '$bn'.\n"; open(FILE, "<", $bn."/".$d."/".$f) or die "Cannot read message '$f' in '$bn'.\n";
my $sz = 0; my ($sz, $num, $ph) = (0);
while (<FILE>) { while (<FILE>) {
/^Subject: (\[placeholder\] )?(\d+)$/ && ($ph = defined($1), $num = int($2)); /^Subject: (\[placeholder\] )?(\d+)$/ && ($ph = defined($1), $num = int($2));
$sz += length($_); $sz += length($_);
@ -385,12 +377,49 @@ sub readbox($)
print STDERR "message '$f' in '$bn' has no identifier.\n"; print STDERR "message '$f' in '$bn' has no identifier.\n";
exit 1; exit 1;
} }
@{ $ms{$uid} } = ($num, $flg.($sz>1000?"*":"").($ph?"?":"")); $cb->($num, $ph, $sz, $f);
} }
} }
}
# $path
sub readbox($)
{
my $bn = shift;
(-d $bn) or
die "No mailbox '$bn'.\n";
my %ms;
readbox_impl($bn, sub {
my ($num, $ph, $sz, $f) = @_;
if ($f !~ /^\d+\.\d+_\d+\.[-[:alnum:]]+,U=(\d+):2,(.*)$/) {
print STDERR "unrecognided file name '$f' in '$bn'.\n";
exit 1;
}
my ($uid, $flg) = (int($1), $2);
@{$ms{$uid}} = ($num, $flg.($sz > 1000 ? "*" : "").($ph ? "?" : ""));
});
my $uidval = readfile($bn."/.uidvalidity", CHOMP);
die "Cannot read UID validity of mailbox '$bn': $!\n" if (!$uidval);
my $mu = $$uidval[1];
return { max_uid => $mu, messages => \%ms }; return { max_uid => $mu, messages => \%ms };
} }
# $path
sub readtrash($)
{
my $bn = shift;
(-d $bn) or
return {};
my %ms;
readbox_impl($bn, sub {
my ($num, $ph, undef, undef) = @_;
$ms{$num} = $ph;
});
return \%ms;
}
# \%fallback_sync_state # \%fallback_sync_state
sub readstate(;$) sub readstate(;$)
{ {
@ -467,6 +496,8 @@ sub readchan(;$)
return { return {
far => readbox("far"), far => readbox("far"),
near => readbox("near"), near => readbox("near"),
far_trash => readtrash("far_trash"),
near_trash => readtrash("near_trash"),
state => readstate($fbss) state => readstate($fbss)
}; };
} }
@ -517,6 +548,8 @@ sub mkchan($)
mkbox("far", $$cs{far}); mkbox("far", $$cs{far});
mkbox("near", $$cs{near}); mkbox("near", $$cs{near});
rmtree("far_trash");
rmtree("near_trash");
mkstate($$cs{state}); mkstate($$cs{state});
} }
@ -560,6 +593,28 @@ sub cmpbox($$$)
return $ret; return $ret;
} }
# $box_name, \%actual_box_state, \%reference_box_state
sub cmptrash($$$)
{
my ($bn, $ms, $ref_ms) = @_;
my $ret = 0;
for my $num (sort { $a <=> $b } keys %$ref_ms) {
my $ph = $$ms{$num};
if (!defined($ph)) {
print STDERR "Missing message $bn:".mn($num)."\n";
$ret = 1;
}
}
for my $num (sort { $a <=> $b } keys %$ms) {
if (!defined($$ref_ms{$num})) {
print STDERR "Excess message $bn:".mn($num)."\n";
$ret = 1;
}
}
return $ret;
}
sub mapmsg($$) sub mapmsg($$)
{ {
my ($uid, $bs) = @_; my ($uid, $bs) = @_;
@ -640,6 +695,8 @@ sub cmpchan($$)
my $rslt = 0; my $rslt = 0;
$rslt |= cmpbox("far", $$cs{far}, $$ref_cs{far}); $rslt |= cmpbox("far", $$cs{far}, $$ref_cs{far});
$rslt |= cmpbox("near", $$cs{near}, $$ref_cs{near}); $rslt |= cmpbox("near", $$cs{near}, $$ref_cs{near});
$rslt |= cmptrash("far_trash", $$cs{far_trash}, $$ref_cs{far_trash});
$rslt |= cmptrash("near_trash", $$cs{near_trash}, $$ref_cs{near_trash});
$rslt |= cmpstate($cs, $ref_cs); $rslt |= cmpstate($cs, $ref_cs);
return $rslt; return $rslt;
} }
@ -763,6 +820,8 @@ sub test_impl($$$$)
rmtree "near"; rmtree "near";
rmtree "far"; rmtree "far";
rmtree "near_trash";
rmtree "far_trash";
for (my $l = 1; $l <= $njl; $l++) { for (my $l = 1; $l <= $njl; $l++) {
mkchan($sx); mkchan($sx);
@ -800,6 +859,8 @@ sub test_impl($$$$)
rmtree "near"; rmtree "near";
rmtree "far"; rmtree "far";
rmtree "near_trash";
rmtree "far_trash";
} }
} }
@ -842,6 +903,7 @@ sub test($$$$)
# Special commands: # Special commands:
# _ => create phantom message (reserve UID for expunged message) # _ => create phantom message (reserve UID for expunged message)
# ^f => create with flags, duplicating the subject # ^f => create with flags, duplicating the subject
# # => create in trash; deletion may follow
# | => use zero UID for state modification, even if msg exists; cmd may follow # | => use zero UID for state modification, even if msg exists; cmd may follow
# & => use zero UID for state identification, even if message exists # & => use zero UID for state identification, even if message exists
# &n => use UID of n'th occurence of subject for state id; command may follow # &n => use UID of n'th occurence of subject for state id; command may follow
@ -1080,4 +1142,67 @@ my @X38 = (
); );
test("max messages + expunge", \@x38, \@X38, \@O38); test("max messages + expunge", \@x38, \@X38, \@O38);
# Trashing
my @x10 = (
E, A, E,
A, "*", "*~", "*T",
B, "*T", "*^", "",
C, "*T", "*", "*T",
D, "_", "*", "*",
E, "*", "*", "_",
L, "*T", "", "",
M, "", "", "*T",
R, "", "", "*", # Force maxuid in the interrupt-resume test.
S, "*", "", "",
);
my @O11 = ("Trash far_trash\n", "Trash near_trash\n",
"MaxMessages 20\nExpireUnread yes\nMaxSize 1k\nExpunge Both\n");
my @X11 = (
R, A, S,
A, "", "/", "/",
B, "#/", "/", "",
C, "#/", "/", "#/",
D, "", "/", "#/",
E, "#/", "/", "",
L, "#/", "", "",
M, "", "", "#/",
R, "*", "*", "",
S, "", "*", "*",
);
test("trash", \@x10, \@X11, \@O11);
my @O12 = ("Trash far_trash\n", "Trash near_trash\nTrashNewOnly true\n",
"MaxMessages 20\nExpireUnread yes\nMaxSize 1k\nExpunge Both\n");
my @X12 = (
R, A, S,
A, "", "/", "/",
B, "#/", "/", "",
C, "#/", "/", "/",
D, "", "/", "/",
E, "#/", "/", "",
L, "#/", "", "",
M, "", "", "#/",
R, "*", "*", "",
S, "", "*", "*",
);
test("trash only new", \@x10, \@X12, \@O12);
my @O13 = ("Trash far_trash\nTrashRemoteNew true\n", "",
"MaxMessages 20\nExpireUnread yes\nMaxSize 1k\nExpunge Both\n");
my @X13 = (
R, A, S,
A, "", "/", "/",
B, "#/", "/", "",
C, "#/", "/", "/",
D, "", "/", "/",
E, "#/", "/", "",
L, "#/", "", "",
M, "#", "", "/",
R, "*", "*", "",
S, "", "*", "*",
);
test("trash new remotely", \@x10, \@X13, \@O13);
print "OK.\n"; print "OK.\n";