@ -47,7 +47,8 @@ sub test($$$$);
# state: [ MaxPulledUid, MaxExpiredFarUid, MaxPushedUid, { muid, suid, flags }... ]
# state: [ MaxPulledUid, MaxExpiredFarUid, MaxPushedUid, { muid, suid, flags }... ]
use enum qw( :=1 A..Z ) ;
use enum qw( :=1 A..Z ) ;
sub mn ($) { chr ( 64 + shift ) }
sub mn ($) { my ( $ n ) = @ _ ; $ n == 0 ? "0" : chr ( 64 + $ n ) }
sub mf ($) { my ( $ f ) = @ _ ; length ( $ f ) ? $ f : '-' }
# generic syncing tests
# generic syncing tests
my @ x01 = (
my @ x01 = (
@ -652,44 +653,63 @@ sub cmpbox($$$)
{
{
my ( $ bn , $ bs , $ ref_bs ) = @ _ ;
my ( $ bn , $ bs , $ ref_bs ) = @ _ ;
my $ ret = 0 ;
my ( $ ref_mu , $ ref_ms ) = ( $$ ref_bs { max_uid } , $$ ref_bs { messages } ) ;
my ( $ ref_mu , $ ref_ms ) = ( $$ ref_bs { max_uid } , $$ ref_bs { messages } ) ;
my ( $ mu , $ ms ) = ( $$ bs { max_uid } , $$ bs { messages } ) ;
my ( $ mu , $ ms ) = ( $$ bs { max_uid } , $$ bs { messages } ) ;
if ( $ mu != $ ref_mu ) {
if ( $ mu != $ ref_mu ) {
print STDERR "MAXUID mismatch for '$bn' ( got $mu, wanted $ref_mu). \n" ;
print STDERR "MAXUID mismatch for '$bn': got $mu, wanted $ref_mu\n" ;
return 1 ;
$ ret = 1 ;
}
}
for my $ uid ( sort { $ a <=> $ b } keys %$ ref_ms ) {
for my $ uid ( sort { $ a <=> $ b } keys %$ ref_ms ) {
my ( $ num , $ flg ) = @ { $$ ref_ms { $ uid } } ;
my ( $ num , $ flg ) = @ { $$ ref_ms { $ uid } } ;
my $ m = $$ ms { $ uid } ;
my $ m = $$ ms { $ uid } ;
if ( ! defined $ m ) {
if ( ! defined $ m ) {
print STDERR "No message $bn:$uid.\n" ;
print STDERR "Missing message $bn:$uid:" . mn ( $ num ) . "\n" ;
return 1 ;
$ ret = 1 ;
next ;
}
}
if ( $$ m [ 0 ] != $ num ) {
if ( $$ m [ 0 ] != $ num ) {
print STDERR "Subject mismatch for $bn:$uid.\n" ;
print STDERR "Subject mismatch for $bn:$uid:" .
" got " . mn ( $$ m [ 0 ] ) . ", wanted " . mn ( $ num ) . "\n" ;
return 1 ;
return 1 ;
}
}
if ( $$ m [ 1 ] ne $ flg ) {
if ( $$ m [ 1 ] ne $ flg ) {
print STDERR "Flag mismatch for $bn:$uid.\n" ;
print STDERR "Flag mismatch for $bn:$uid:" . mn ( $ num ) . ":" .
return 1 ;
" got " . mf ( $$ m [ 1 ] ) . ", wanted " . mf ( $ flg ) . "\n" ;
$ ret = 1 ;
}
}
}
}
for my $ uid ( sort { $ a <=> $ b } keys %$ ms ) {
for my $ uid ( sort { $ a <=> $ b } keys %$ ms ) {
if ( ! defined ( $$ ref_ms { $ uid } ) ) {
if ( ! defined ( $$ ref_ms { $ uid } ) ) {
print STDERR "Excess message $bn:$uid:" . mn ( $$ ms { $ uid } [ 0 ] ) . "\n" ;
print STDERR "Excess message $bn:$uid:" . mn ( $$ ms { $ uid } [ 0 ] ) . "\n" ;
return 1 ;
$ ret = 1 ;
}
}
return $ ret ;
}
sub mapmsg ($$)
{
my ( $ uid , $ bs ) = @ _ ;
if ( $ uid ) {
if ( my $ msg = $$ bs { messages } { $ uid } ) {
return $$ msg [ 0 ] ;
}
}
}
}
return 0 ;
return 0 ;
}
}
# \%actual_sync_state, \%reference_sync_state
# \%actual_chan _state, \%reference_chan _state
sub cmpstate ($$)
sub cmpstate ($$)
{
{
my ( $ ss , $ ref_ss ) = @ _ ;
my ( $ cs , $ ref_c s) = @ _ ;
my ( $ ss , $ fbs , $ nbs ) = ( $$ cs { state } , $$ cs { far } , $$ cs { near } ) ;
return 1 if ( ! $ ss ) ;
return 1 if ( ! $ ss ) ;
my ( $ ref_ss , $ ref_fbs , $ ref_nbs ) = ( $$ ref_cs { state } , $$ ref_cs { far } , $$ ref_cs { near } ) ;
return 0 if ( $ ss == $ ref_ss ) ;
return 0 if ( $ ss == $ ref_ss ) ;
my $ ret = 0 ;
for my $ h ( [ 'MaxPulledUid' , 'max_pulled' ] ,
for my $ h ( [ 'MaxPulledUid' , 'max_pulled' ] ,
[ 'MaxExpiredFarUid' , 'max_expired' ] ,
[ 'MaxExpiredFarUid' , 'max_expired' ] ,
[ 'MaxPushedUid' , 'max_pushed' ] ) {
[ 'MaxPushedUid' , 'max_pushed' ] ) {
@ -697,33 +717,47 @@ sub cmpstate($$)
my ( $ got , $ want ) = ( $$ ss { $ sn } , $$ ref_ss { $ sn } ) ;
my ( $ got , $ want ) = ( $$ ss { $ sn } , $$ ref_ss { $ sn } ) ;
if ( $ got != $ want ) {
if ( $ got != $ want ) {
print STDERR "Sync state header entry $hn mismatch: got $got, wanted $want\n" ;
print STDERR "Sync state header entry $hn mismatch: got $got, wanted $want\n" ;
return 1 ;
$ ret = 1 ;
}
}
}
}
my $ ref_ents = $$ ref_ss { entries } ;
my $ ref_ents = $$ ref_ss { entries } ;
my $ ents = $$ ss { entries } ;
my $ ents = $$ ss { entries } ;
my $ i = 0 ;
for ( my $ i = 0 ; $ i < @$ ents || $ i < @$ ref_ents ; $ i + + ) {
while ( $ i < @$ ents ) {
my ( $ ent , $ fuid , $ nuid , $ num ) ;
my $ ent = $$ ents [ $ i ] ;
if ( $ i < @$ ents ) {
my $ l = $$ ent [ 0 ] . " " . $$ ent [ 1 ] . " " . $$ ent [ 2 ] ;
$ ent = $$ ents [ $ i ] ;
( $ fuid , $ nuid ) = ( $$ ent [ 0 ] , $$ ent [ 1 ] ) ;
my ( $ fnum , $ nnum ) = ( mapmsg ( $ fuid , $ fbs ) , mapmsg ( $ nuid , $ nbs ) ) ;
if ( $ fnum && $ nnum && $ fnum != $ nnum ) {
print STDERR "Invalid sync state entry $fuid:$nuid:" .
" mismatched subjects (" . mn ( $ fnum ) . ":" . mn ( $ nnum ) . ")\n" ;
return 1 ;
}
$ num = $ fnum || $ nnum ;
}
if ( $ i == @$ ref_ents ) {
if ( $ i == @$ ref_ents ) {
print STDERR "Excess sync state entry: '$l'.\n" ;
print STDERR "Excess sync state entry $fuid:$nuid (" . mn ( $ num ) . ") \n" ;
return 1 ;
return 1 ;
}
}
my $ rent = $$ ref_ents [ $ i ] ;
my $ rent = $$ ref_ents [ $ i ] ;
my $ xl = $$ rent [ 0 ] . " " . $$ rent [ 1 ] . " " . $$ rent [ 2 ] ;
my ( $ rfuid , $ rnuid ) = ( $$ rent [ 0 ] , $$ rent [ 1 ] ) ;
if ( $ l ne $ xl ) {
my $ rnum = mapmsg ( $ rfuid , $ ref_fbs ) || mapmsg ( $ rnuid , $ ref_nbs ) ;
print STDERR "Sync state entry mismatch: '$l' instead of '$xl'.\n" ;
if ( $ i == @$ ents ) {
print STDERR "Missing sync state entry $rfuid:$rnuid (" . mn ( $ rnum ) . ")\n" ;
return 1 ;
return 1 ;
}
}
$ i += 1 ;
if ( $ fuid != $ rfuid || $ nuid != $ rnuid || $ num != $ rnum ) {
}
print STDERR "Unexpected sync state entry:" .
if ( $ i < @$ ref_ents ) {
" got $fuid:$nuid (" . mn ( $ num ) . "), wanted $rfuid:$rnuid (" . mn ( $ rnum ) . ")\n" ;
my $ rent = $$ ref_ents [ $ i ] ;
print STDERR "Missing sync state entry: '" . $$ rent [ 0 ] . " " . $$ rent [ 1 ] . " " . $$ rent [ 2 ] . "'.\n" ;
return 1 ;
return 1 ;
}
}
return 0 ;
if ( $$ ent [ 2 ] ne $$ rent [ 2 ] ) {
print STDERR "Flag mismatch in sync state entry $fuid:$nuid (" . mn ( $ rnum ) . "):" .
" got " . mf ( $$ ent [ 2 ] ) . ", wanted " . mf ( $$ rent [ 2 ] ) . "\n" ;
$ ret = 1 ;
}
}
return $ ret ;
}
}
# \%actual_chan_state, \%reference_chan_state
# \%actual_chan_state, \%reference_chan_state
@ -734,7 +768,7 @@ 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 |= cmpstate ( $$ cs { state } , $$ ref_cs { state } ) ;
$ rslt |= cmpstate ( $ cs , $ ref_cs ) ;
return $ rslt ;
return $ rslt ;
}
}
@ -819,7 +853,7 @@ sub test_impl($$$$)
my ( $ jxc , $ jret ) = runsync ( $ async , "-0 --no-expunge" , "2-replay.log" ) ;
my ( $ jxc , $ jret ) = runsync ( $ async , "-0 --no-expunge" , "2-replay.log" ) ;
my $ jrcs = readstate ( ) if ( ! $ jxc ) ;
my $ jrcs = readstate ( ) if ( ! $ jxc ) ;
if ( $ jxc || cmpstate ( $ jrcs , $$ tx { state } ) ) {
if ( $ jxc || cmpstate ( { far = > $$ tx { far } , near = > $$ tx { near } , state = > $ jrcs } , $ tx ) ) {
print "Journal replay failed.\n" ;
print "Journal replay failed.\n" ;
print "Options:\n" ;
print "Options:\n" ;
print " [ " . join ( ", " , map ( '"' . qm ( $ _ ) . '"' , @$ sfx ) ) . " ], [ \"-0\", \"--no-expunge\" ]\n" ;
print " [ " . join ( ", " , map ( '"' . qm ( $ _ ) . '"' , @$ sfx ) ) . " ], [ \"-0\", \"--no-expunge\" ]\n" ;