isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit 9fbf5c2f6ccb3691776a671dfb715615ec8b8ba7
parent 4423a932f3e280a6aa8afd2ae611c9064e840dae
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Fri, 18 Dec 2020 14:31:16 +0100

autotest: pass containers by reference

this makes function prototypes a lot more useful for parameter checking.

Diffstat:
Msrc/run-tests.pl | 258++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 132 insertions(+), 126 deletions(-)

diff --git a/src/run-tests.pl b/src/run-tests.pl @@ -33,7 +33,7 @@ if (!-d "tmp") { chdir "tmp" or die "Cannot enter temp direcory.\n"; sub show($$$); -sub test($$$@); +sub test($$$$); ################################################################################ @@ -64,7 +64,7 @@ my @X01 = ( [ 10, 0, 10, 1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 0, "", 7, 7, "FT", 0, 8, "", 10, 9, "", 9, 10, "" ], ); -test("full", \@x01, \@X01, @O01); +test("full", \@x01, \@X01, \@O01); my @O02 = ("", "", "Expunge Both\n"); #show("01", "02", "02"); @@ -76,7 +76,7 @@ my @X02 = ( [ 10, 0, 10, 1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 10, 9, "", 9, 10, "" ], ); -test("full + expunge both", \@x01, \@X02, @O02); +test("full + expunge both", \@x01, \@X02, \@O02); my @O03 = ("", "", "Expunge Near\n"); #show("01", "03", "03"); @@ -88,7 +88,7 @@ my @X03 = ( [ 10, 0, 10, 1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 0, "T", 6, 0, "", 7, 0, "T", 10, 9, "", 9, 10, "" ], ); -test("full + expunge near side", \@x01, \@X03, @O03); +test("full + expunge near side", \@x01, \@X03, \@O03); my @O04 = ("", "", "Sync Pull\n"); #show("01", "04", "04"); @@ -100,7 +100,7 @@ my @X04 = ( [ 9, 0, 0, 1, 1, "F", 2, 2, "", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "", 7, 7, "FT", 0, 8, "", 9, 10, "" ], ); -test("pull", \@x01, \@X04, @O04); +test("pull", \@x01, \@X04, \@O04); my @O05 = ("", "", "Sync Flags\n"); #show("01", "05", "05"); @@ -112,7 +112,7 @@ my @X05 = ( [ 8, 0, 0, 1, 1, "F", 2, 2, "F", 3, 3, "FS", 4, 4, "", 5, 5, "T", 6, 6, "", 7, 7, "FT", 8, 8, "" ], ); -test("flags", \@x01, \@X05, @O05); +test("flags", \@x01, \@X05, \@O05); my @O06 = ("", "", "Sync Delete\n"); #show("01", "06", "06"); @@ -124,7 +124,7 @@ my @X06 = ( [ 8, 0, 0, 1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 0, "", 7, 7, "", 0, 8, "" ], ); -test("deletions", \@x01, \@X06, @O06); +test("deletions", \@x01, \@X06, \@O06); my @O07 = ("", "", "Sync New\n"); #show("01", "07", "07"); @@ -136,7 +136,7 @@ my @X07 = ( [ 10, 0, 10, 1, 1, "", 2, 2, "", 3, 3, "", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 8, 8, "", 10, 9, "", 9, 10, "" ], ); -test("new", \@x01, \@X07, @O07); +test("new", \@x01, \@X07, \@O07); my @O08 = ("", "", "Sync PushFlags PullDelete\n"); #show("01", "08", "08"); @@ -148,7 +148,7 @@ my @X08 = ( [ 8, 0, 0, 1, 1, "", 2, 2, "F", 3, 3, "F", 4, 4, "", 5, 5, "", 6, 6, "", 7, 7, "", 0, 8, "" ], ); -test("push flags + pull deletions", \@x01, \@X08, @O08); +test("push flags + pull deletions", \@x01, \@X08, \@O08); # size restriction tests @@ -171,7 +171,7 @@ my @X11 = ( [ 3, 0, 3, 3, 1, "<", 1, 2, "", 2, 3, ">" ], ); -test("max size", \@x10, \@X11, @O11); +test("max size", \@x10, \@X11, \@O11); my @x22 = ( [ 3, @@ -191,7 +191,7 @@ my @X22 = ( [ 4, 0, 4, 4, 1, "F", 3, 0, "T", 1, 2, "", 2, 4, "" ], ); -test("max size + flagging", \@x22, \@X22, @O11); +test("max size + flagging", \@x22, \@X22, \@O11); my @x23 = ( [ 2, @@ -210,7 +210,7 @@ my @X23 = ( [ 3, 0, 3, 3, 1, "F", 1, 2, "", 2, 3, "F" ] ); -test("max size + initial flagging", \@x23, \@X23, @O11); +test("max size + initial flagging", \@x23, \@X23, \@O11); my @x24 = ( [ 3, @@ -229,7 +229,7 @@ my @X24 = ( [ 3, 0, 3, 1, 1, "", 2, 2, ">", 3, 3, "F" ], ); -test("max size (pre-1.4 legacy)", \@x24, \@X24, @O11); +test("max size (pre-1.4 legacy)", \@x24, \@X24, \@O11); # expiration tests @@ -252,7 +252,7 @@ my @X31 = ( [ 6, 3, 5, 1, 1, "F", 2, 2, "", 4, 3, "", 5, 4, "S", 6, 5, "" ], ); -test("max messages", \@x30, \@X31, @O31); +test("max messages", \@x30, \@X31, \@O31); my @O32 = ("", "", "MaxMessages 3\nExpireUnread yes\n"); #show("30", "32", "32"); @@ -264,7 +264,7 @@ my @X32 = ( [ 6, 3, 4, 1, 1, "F", 4, 2, "", 5, 3, "S", 6, 4, "" ], ); -test("max messages vs. unread", \@x30, \@X32, @O32); +test("max messages vs. unread", \@x30, \@X32, \@O32); my @x50 = ( [ 6, @@ -285,7 +285,7 @@ my @X51 = ( [ 6, 3, 6, 2, 2, "FS", 4, 4, "", 5, 5, "", 6, 6, "" ], ); -test("max messages + expunge", \@x50, \@X51, @O51); +test("max messages + expunge", \@x50, \@X51, \@O51); ################################################################################ @@ -304,9 +304,11 @@ sub qm($) return $_; } -# $far, $near, $channel -sub writecfg($$$) +# [ $far, $near, $channel ] +sub writecfg($) { + my ($sfx) = @_; + open(FILE, ">", ".mbsyncrc") or die "Cannot open .mbsyncrc.\n"; print FILE @@ -315,16 +317,16 @@ sub writecfg($$$) MaildirStore far Path ./ Inbox ./far -".shift()." +".$$sfx[0]." MaildirStore near Path ./ Inbox ./near -".shift()." +".$$sfx[1]." Channel test Far :far: Near :near: SyncState * -".shift(); +".$$sfx[2]; close FILE; } @@ -335,6 +337,7 @@ sub killcfg() } # $run_async, $mbsync_options, $log_file +# Return: $exit_code, \@mbsync_output sub runsync($$$) { my ($async, $flags, $file) = @_; @@ -355,11 +358,12 @@ sub runsync($$$) print FILE @out; close FILE; } - return $?, @out; + return $?, \@out; } # $path +# Return: $max_uid, { uid => [ seq, flags ] } sub readbox($) { my $bn = shift; @@ -397,7 +401,7 @@ sub readbox($) @{ $ms{$uid} } = ($num, $flg.($sz>1000?"*":"").($ph?"?":"")); } } - return ($mu, %ms); + return $mu, \%ms; } # $boxname @@ -408,12 +412,12 @@ sub showbox($) { my ($bn) = @_; - my ($mu, %ms) = readbox($bn); - my @MS = ($mu); - for my $uid (sort { $a <=> $b } keys %ms) { - push @MS, $ms{$uid}[0], $uid, $ms{$uid}[1]; + my ($mu, $ms) = readbox($bn); + my @bc = ($mu); + for my $uid (sort { $a <=> $b } keys %$ms) { + push @bc, $$ms{$uid}[0], $uid, $$ms{$uid}[1]; } - printbox($bn, @MS); + printbox($bn, \@bc); } # $filename @@ -453,7 +457,7 @@ sub showstate($) /^(\d+) (\d+) (.*)$/; push @T, $1, $2, $3; } - printstate(@T); + printstate(\@T); } # $filename @@ -470,37 +474,37 @@ sub showchan($) sub show($$$) { my ($sx, $tx, $sfxn) = @_; - my (@sp, @sfx); - eval "\@sp = \@x$sx"; - eval "\@sfx = \@O$sfxn"; - mkchan($sp[0], $sp[1], @{ $sp[2] }); + my ($sp, $sfx); + eval "\$sp = \\\@x$sx"; + eval "\$sfx = \\\@O$sfxn"; + mkchan($$sp[0], $$sp[1], $$sp[2]); print "my \@x$sx = (\n"; showchan("near/.mbsyncstate"); print ");\n"; - &writecfg(@sfx); + writecfg($sfx); runsync(0, "", ""); killcfg(); print "my \@X$tx = (\n"; showchan("near/.mbsyncstate"); print ");\n"; - print "test(\"\", \\\@x$sx, \\\@X$tx, \@O$sfxn);\n\n"; + print "test(\"\", \\\@x$sx, \\\@X$tx, \\\@O$sfxn);\n\n"; rmtree "near"; rmtree "far"; } -# $boxname, $maxuid, @msgs -sub mkbox($$@) +# $box_name, \@box_state +sub mkbox($$) { - my ($bn, $mu, @ms) = @_; + my ($bn, $bs) = @_; rmtree($bn); (mkdir($bn) and mkdir($bn."/tmp") and mkdir($bn."/new") and mkdir($bn."/cur")) or die "Cannot create mailbox $bn.\n"; open(FILE, ">", $bn."/.uidvalidity") or die "Cannot create UID validity for mailbox $bn.\n"; - print FILE "1\n$mu\n"; + print FILE "1\n$$bs[0]\n"; close FILE; - while (@ms) { - my ($num, $uid, $flg) = (shift @ms, shift @ms, shift @ms); + for (my $i = 1; $i < @$bs; $i += 3) { + my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]); my $big = $flg =~ s/\*//; my $ph = $flg =~ s/\?//; open(FILE, ">", $bn."/".($flg =~ /S/ ? "cur" : "new")."/0.1_".$num.".local,U=".$uid.":2,".$flg) or @@ -510,35 +514,35 @@ sub mkbox($$@) } } -# \@far, \@near, @syncstate -sub mkchan($$@) +# \@far_state, \@near_state, \@sync_state +sub mkchan($$$) { - my ($m, $s, @t) = @_; - &mkbox("far", @{ $m }); - &mkbox("near", @{ $s }); + my ($f, $n, $t) = @_; + mkbox("far", $f); + mkbox("near", $n); open(FILE, ">", "near/.mbsyncstate") or die "Cannot create sync state.\n"; - print FILE "FarUidValidity 1\nMaxPulledUid ".shift(@t)."\n". - "NearUidValidity 1\nMaxExpiredFarUid ".shift(@t)."\nMaxPushedUid ".shift(@t)."\n\n"; - while (@t) { - print FILE shift(@t)." ".shift(@t)." ".shift(@t)."\n"; + print FILE "FarUidValidity 1\nMaxPulledUid ".$$t[0]."\n". + "NearUidValidity 1\nMaxExpiredFarUid ".$$t[1]."\nMaxPushedUid ".$$t[2]."\n\n"; + for (my $i = 3; $i < @$t; $i += 3) { + print FILE $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."\n"; } close FILE; } -# $boxname, $maxuid, @msgs -sub ckbox($$@) +# $box_name, \@box_state +sub ckbox($$) { - my ($bn, $MU, @MS) = @_; + my ($bn, $bs) = @_; - my ($mu, %ms) = readbox($bn); - if ($mu != $MU) { - print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $MU).\n"; + my ($mu, $ms) = readbox($bn); + if ($mu != $$bs[0]) { + print STDERR "MAXUID mismatch for '$bn' (got $mu, wanted $$bs[0]).\n"; return 1; } - while (@MS) { - my ($num, $uid, $flg) = (shift @MS, shift @MS, shift @MS); - my $m = delete $ms{$uid}; + for (my $i = 1; $i < @$bs; $i += 3) { + my ($num, $uid, $flg) = ($$bs[$i], $$bs[$i + 1], $$bs[$i + 2]); + my $m = delete $$ms{$uid}; if (!defined $m) { print STDERR "No message $bn:$uid.\n"; return 1; @@ -552,23 +556,23 @@ sub ckbox($$@) return 1; } } - if (%ms) { - print STDERR "Excess messages in '$bn': ".join(", ", sort({$a <=> $b } keys(%ms))).".\n"; + if (%$ms) { + print STDERR "Excess messages in '$bn': ".join(", ", sort({ $a <=> $b } keys(%$ms))).".\n"; return 1; } return 0; } -# $filename, @syncstate -sub ckstate($@) +# $state_file, \@sync_state +sub ckstate($$) { - my ($fn, $fmaxuid, $maxxfuid, $nmaxuid, @T) = @_; + my ($fn, $t) = @_; my %hdr; $hdr{'FarUidValidity'} = "1"; $hdr{'NearUidValidity'} = "1"; - $hdr{'MaxPulledUid'} = $fmaxuid; - $hdr{'MaxPushedUid'} = $nmaxuid; - $hdr{'MaxExpiredFarUid'} = $maxxfuid if ($maxxfuid ne 0); + $hdr{'MaxPulledUid'} = $$t[0]; + $hdr{'MaxPushedUid'} = $$t[2]; + $hdr{'MaxExpiredFarUid'} = $$t[1] if ($$t[1] ne 0); open(FILE, "<", $fn) or die "Cannot read sync state $fn.\n"; chomp(my @ls = <FILE>); close FILE; @@ -598,66 +602,68 @@ sub ckstate($@) print STDERR "Keys missing from sync state header: @ky\n"; return 1; } + my $i = 3; for my $l (@ls) { - if (!@T) { + if ($i == @$t) { print STDERR "Excess sync state entry: '$l'.\n"; return 1; } - my $xl = shift(@T)." ".shift(@T)." ".shift(@T); + my $xl = $$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]; if ($l ne $xl) { print STDERR "Sync state entry mismatch: '$l' instead of '$xl'.\n"; return 1; } + $i += 3; } - if (@T) { - print STDERR "Missing sync state entry: '".shift(@T)." ".shift(@T)." ".shift(@T)."'.\n"; + if ($i < @$t) { + print STDERR "Missing sync state entry: '".$$t[$i]." ".$$t[$i + 1]." ".$$t[$i + 2]."'.\n"; return 1; } return 0; } -# $statefile, \@chan_state +# $state_file, \@chan_state sub ckchan($$) { - my ($F, $cs) = @_; - my $rslt = ckstate($F, @{ $$cs[2] }); - $rslt |= &ckbox("far", @{ $$cs[0] }); - $rslt |= &ckbox("near", @{ $$cs[1] }); + my ($fn, $cs) = @_; + my $rslt = ckstate($fn, $$cs[2]); + $rslt |= ckbox("far", $$cs[0]); + $rslt |= ckbox("near", $$cs[1]); return $rslt; } -# $boxname, $maxuid, @msgs -sub printbox($$@) +# $box_name, \@box_state +sub printbox($$) { - my ($bn, $mu, @ms) = @_; + my ($bn, $bs) = @_; - print " [ $mu,\n "; + print " [ $$bs[0],\n "; my $frst = 1; - while (@ms) { + for (my $i = 1; $i < @$bs; $i += 3) { if ($frst) { $frst = 0; } else { print ", "; } - print mn(shift(@ms)).", ".shift(@ms).", \"".shift(@ms)."\""; + print mn($$bs[$i]).", ".$$bs[$i + 1].", \"".$$bs[$i + 2]."\""; } print " ],\n"; } -# @syncstate -sub printstate(@) +# \@sync_state +sub printstate($) { - my (@t) = @_; + my ($t) = @_; - print " [ ".shift(@t).", ".shift(@t).", ".shift(@t).",\n "; + print " [ ".$$t[0].", ".$$t[1].", ".$$t[2].",\n "; my $frst = 1; - while (@t) { + for (my $i = 3; $i < @$t; $i += 3) { if ($frst) { $frst = 0; } else { print ", "; } - print((shift(@t) // "??").", ".(shift(@t) // "??").", \"".(shift(@t) // "??")."\""); + print(($$t[$i] // "??").", ".($$t[$i + 1] // "??").", \"".($$t[$i + 2] // "??")."\""); } print " ],\n"; } @@ -667,9 +673,9 @@ sub printchan($) { my ($cs) = @_; - &printbox("far", @{ $$cs[0] }); - &printbox("near", @{ $$cs[1] }); - printstate(@{ $$cs[2] }); + printbox("far", $$cs[0]); + printbox("near", $$cs[1]); + printstate($$cs[2]); } sub readfile($) @@ -679,22 +685,22 @@ sub readfile($) open(FILE, $file) or return; my @nj = <FILE>; close FILE; - return @nj; + return \@nj; } -# $run_async, \@source_state, \@target_state, @channel_configs -sub test_impl($$$@) +# $run_async, \@source_state, \@target_state, \@channel_configs +sub test_impl($$$$) { - my ($async, $sx, $tx, @sfx) = @_; + my ($async, $sx, $tx, $sfx) = @_; - mkchan($$sx[0], $$sx[1], @{ $$sx[2] }); + mkchan($$sx[0], $$sx[1], $$sx[2]); - my ($xc, @ret) = runsync($async, "-Tj", "1-initial.log"); + my ($xc, $ret) = runsync($async, "-Tj", "1-initial.log"); if ($xc || ckchan("near/.mbsyncstate.new", $tx)) { print "Input:\n"; printchan($sx); print "Options:\n"; - print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n"; + print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n"; if (!$xc) { print "Expected result:\n"; printchan($tx); @@ -702,72 +708,72 @@ sub test_impl($$$@) showchan("near/.mbsyncstate.new"); } print "Debug output:\n"; - print @ret; + print @$ret; exit 1; } - my @nj = readfile("near/.mbsyncstate.journal"); - my ($jxc, @jret) = runsync($async, "-0 --no-expunge", "2-replay.log"); - if ($jxc || ckstate("near/.mbsyncstate", @{ $$tx[2] })) { + my $nj = readfile("near/.mbsyncstate.journal"); + my ($jxc, $jret) = runsync($async, "-0 --no-expunge", "2-replay.log"); + if ($jxc || ckstate("near/.mbsyncstate", $$tx[2])) { print "Journal replay failed.\n"; print "Options:\n"; - print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ], [ \"-0\", \"--no-expunge\" ]\n"; + print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ], [ \"-0\", \"--no-expunge\" ]\n"; print "Old State:\n"; - printstate(@{ $$sx[2] }); - print "Journal:\n".join("", @nj)."\n"; + printstate($$sx[2]); + print "Journal:\n".join("", @$nj)."\n"; if (!$jxc) { print "Expected New State:\n"; - printstate(@{ $$tx[2] }); + printstate($$tx[2]); print "New State:\n"; showstate("near/.mbsyncstate"); } print "Debug output:\n"; - print @jret; + print @$jret; exit 1; } - my ($ixc, @iret) = runsync($async, "", "3-verify.log"); + my ($ixc, $iret) = runsync($async, "", "3-verify.log"); if ($ixc || ckchan("near/.mbsyncstate", $tx)) { print "Idempotence verification run failed.\n"; print "Input == Expected result:\n"; printchan($tx); print "Options:\n"; - print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n"; + print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n"; if (!$ixc) { print "Actual result:\n"; showchan("near/.mbsyncstate"); } print "Debug output:\n"; - print @iret; + print @$iret; exit 1; } rmtree "near"; rmtree "far"; - my $njl = (@nj - 1) * 2; + my $njl = (@$nj - 1) * 2; for (my $l = 1; $l <= $njl; $l++) { - mkchan($$sx[0], $$sx[1], @{ $$sx[2] }); + mkchan($$sx[0], $$sx[1], $$sx[2]); - my ($nxc, @nret) = runsync($async, "-Tj$l", "4-interrupt.log"); + my ($nxc, $nret) = runsync($async, "-Tj$l", "4-interrupt.log"); if ($nxc != (100 + ($l & 1)) << 8) { print "Interrupting at step $l/$njl failed.\n"; print "Debug output:\n"; - print @nret; + print @$nret; exit 1; } - ($nxc, @nret) = runsync($async, "-Tj", "5-resume.log"); + ($nxc, $nret) = runsync($async, "-Tj", "5-resume.log"); if ($nxc || ckchan("near/.mbsyncstate.new", $tx)) { print "Resuming from step $l/$njl failed.\n"; print "Input:\n"; printchan($sx); print "Options:\n"; - print " [ ".join(", ", map('"'.qm($_).'"', @sfx))." ]\n"; - my @nnj = readfile("near/.mbsyncstate.journal"); + print " [ ".join(", ", map('"'.qm($_).'"', @$sfx))." ]\n"; + my $nnj = readfile("near/.mbsyncstate.journal"); my $ln = int($l / 2); - print "Journal:\n".join("", @nnj[0..$ln])."-------\n".join("", @nnj[($ln + 1)..$#nnj])."\n"; - print "Full journal:\n".join("", @nj)."\n"; + print "Journal:\n".join("", @$nnj[0..$ln])."-------\n".join("", @$nnj[($ln + 1)..$#$nnj])."\n"; + print "Full journal:\n".join("", @$nj)."\n"; if (!$nxc) { print "Expected result:\n"; printchan($tx); @@ -775,7 +781,7 @@ sub test_impl($$$@) showchan("near/.mbsyncstate.new"); } print "Debug output:\n"; - print @nret; + print @$nret; exit 1; } @@ -784,17 +790,17 @@ sub test_impl($$$@) } } -# $title, \@source_state, \@target_state, @channel_configs -sub test($$$@) +# $title, \@source_state, \@target_state, \@channel_configs +sub test($$$$) { - my ($ttl, $sx, $tx, @sfx) = @_; + my ($ttl, $sx, $tx, $sfx) = @_; return 0 if (scalar(@ARGV) && !grep { $_ eq $ttl } @ARGV); print "Testing: ".$ttl." ...\n"; - &writecfg(@sfx); + writecfg($sfx); - test_impl(0, $sx, $tx, @sfx); - test_impl(1, $sx, $tx, @sfx); + test_impl(0, $sx, $tx, $sfx); + test_impl(1, $sx, $tx, $sfx); killcfg(); }