1#!/usr/bin/perl
2use Test::More;
3#use Data::Dumper;
4use SVN::Notify;
5use Cwd;
6use Config;
7my $SECURE_PERL_PATH = $Config{perlpath};
8if ($^O ne 'VMS') {
9    $SECURE_PERL_PATH.= $Config{_exe}
10	unless $SECURE_PERL_PATH =~ m/$Config{_exe}$/i;
11}
12my $PWD = getcwd;
13my $USER = $ENV{USER};
14my $SVNLOOK  = $ENV{SVNLOOK}  || SVN::Notify->find_exe('svnlook');
15my $SVNADMIN = $ENV{SVNADMIN} || SVN::Notify->find_exe('svnadmin');
16
17if ( !defined($SVNLOOK) ) {
18    plan skip_all => "Cannot find svnlook!\n".
19    "Please start the tests like this:\n".
20    "  SVNLOOK=/path/to/svnlook make test";
21}
22elsif ( !defined($SVNADMIN) ) {
23    plan skip_all => "Cannot find svnadmin!\n".
24    "Please start the tests like this:\n".
25    "  SVNADMIN=/path/to/svnadmin make test";
26}
27else {
28    plan no_plan;
29}
30
31my $repos_path = "$PWD/t/test-repos";
32
33my $wc_map = {
34    'wc-trunk' =>
35    	{
36	    path => 'project1/trunk',
37	    base_rev => 1,
38	    command => 'update',
39	},
40    'wc-tag' =>
41        {
42	    path => 'project1/tags/TRUNK-1135534439',
43	    base_rev => 3,
44	    command => 'switch',
45	    switch_rev => 7,
46	    switch_path => 'project1/tags/TRUNK-1135538253',
47	    tag_regex => 'TRUNK-',
48	},
49    'wc-branch' =>
50    	{
51	    path => 'project1/branches/branch1',
52	    base_rev => 4,
53	    command => 'update',
54	},
55};
56
57my $wc_rsync_map;
58foreach my $key ( keys %$wc_map ) {
59    $wc_rsync_map->{$key.'-r'} = $wc_map->{$key};
60}
61
62my ($repos_history, $changes);
63my $eval;
64while (<DATA>) {
65    $eval .= $_;
66}
67eval $eval;
68
69my $maxrev = 7; # change this later to be the actual number of revs
70
71sub reset_all_tests {
72    create_test_repos();
73    create_test_wcs();
74    reset_test_wcs();
75}
76
77# Create a repository fill it with sample values the first time through
78sub create_test_repos {
79    unless ( -d $repos_path ) {
80	system(<<"") == 0 or die "system failed: $?";
81$SVNADMIN create $repos_path
82
83	system(<<"") == 0 or die "system failed: $?";
84$SVNADMIN load --quiet $repos_path < ${repos_path}.dump
85
86    }
87}
88
89# Create test WC's before proceeding with tests the first time
90sub create_test_wcs {
91    unless ( -d "$PWD/t/wc-trunk" ) {
92	foreach my $wc ( keys %{$wc_map} ) {
93	    my $path = $wc_map->{$wc}->{'path'};
94	    my $rev = $wc_map->{$wc}->{'base_rev'};
95
96	    my $cmd = "svn checkout -q -r$rev ".
97		"file://$repos_path/$path $PWD/t/$wc";
98	    system($cmd) == 0 or die "system failed: $?";
99	}
100    }
101}
102
103# Reset the working copies
104sub reset_test_wcs {
105    foreach my $wc ( keys %{$wc_map} ) {
106	my $path = $wc_map->{$wc}->{'path'};
107	my $rev = $wc_map->{$wc}->{'base_rev'};
108	my $command = $wc_map->{$wc}->{'command'};
109
110	my $cmd = "svn $command -q -r$rev ";
111	$cmd .= "file://$repos_path/$path "
112	   if ( $command =~ /switch/ ); # accomodate older svn's
113	$cmd .= "$PWD/t/$wc";
114	system($cmd) == 0 or die "system failed: $?";
115    }
116}
117
118sub run_tests {
119    my $command = shift;
120    my $TESTER;
121    my $rsync_test = 0;
122
123    for (my $rev = 1; $rev <= $maxrev; $rev++) {
124	foreach my $wc ( keys %{$wc_map} ) {
125	    next unless $rev >= $wc_map->{$wc}->{'base_rev'};
126	    my %args = @_;
127	    # Common to all tests
128	    $args{'repos-path'} = $repos_path;
129	    $args{'handler'} = defined $args{'ssh-host'}
130	                       ? 'Mirror::SSH'
131			       : defined $args{'rsync-host'}
132			       ? 'Mirror::Rsync'
133			       : 'Mirror';
134
135
136	    $args{'to'}       = "$PWD/t/$wc";
137	    $args{'revision'} = $rev;
138
139	    my $path = $wc_map->{$wc}->{'path'};
140	    my $change = $changes->[$rev]->{$path}
141	    	if exists $changes->[$rev]->{$path};
142	    next unless $change;
143
144	    # special case the switched directories
145	    if ( $wc_map->{$wc}->{'command'} eq 'switch'
146		&& $rev >= $wc_map->{$wc}->{'switch_rev'} ) {
147		$path = $wc_map->{$wc}->{'switch_path'};
148		$args{'tag-regex'} = $wc_map->{$wc}->{'tag_regex'};
149	    }
150
151	    # need to specify destination for rsync tests
152	    if ( defined $args{'rsync-host'} ) {
153		$rsync_test = 1;
154		$args{'rsync-dest'} = "$PWD/t/$wc\-r";
155	    }
156
157	    _test(
158		$change,
159		$path,
160		$command,
161		%args
162	    );
163	}
164	_compare_directories($rev, $wc_map);
165
166	if ($rsync_test) {
167	    _compare_directories($rev, $wc_rsync_map);
168	    $rsync_test = 0;
169	}
170    }
171
172}
173
174sub _test {
175    my ($expected, $prefix, $command, %args) = @_;
176    my $test = {};
177
178    open $TESTER, '-|', _build_command($command, %args);
179    while (<$TESTER>) {
180	chomp;
181	next if ( /^Updating '.+':/ );
182	if ( /^At revision (\d+)\./ ) {
183	    ok ( $1 == $args{revision} , "No changes in $prefix at revision: "
184	    	. $args{revision} );
185	    last; # no need to read any more input
186	}
187	elsif ( /^Updated to revision (\d+)\./ ) {
188	    ok ( $1 == $args{revision} , "Updated $prefix to correct revision: "
189	    	. $args{revision} );
190	}
191	else {
192	    my ($status, $target) = split;
193	    $test->{$prefix.'/'.$target} = $status;
194	}
195    }
196    close $TESTER;
197    is_deeply(
198    	$test,
199	$expected,
200	"Correct files updated in $prefix at rev: " . $args{revision}
201    ) if scalar(keys %$test) > 0;
202}
203
204sub _build_command {
205    my ($command, %args) = @_;
206    my @commandline = split " ", $command;
207
208    if ( $command =~ /svnnotify/ ) {
209	# hate to hardcode this, but what else can we do
210	foreach my $key ( keys(%args) ) {
211	    push @commandline, "\-\-$key", $args{$key};
212	}
213    }
214    else {
215	push @commandline, $args{'repos-path'}, $args{'revision'};
216    }
217    return @commandline;
218}
219
220sub _compare_directories {
221    my ($rev, $wc_hash) = @_;
222    my $history = $repos_history->[$rev];
223    my $this_rev = {};
224
225    foreach my $wc ( keys %$wc_hash ) {
226	next unless $rev >= $wc_hash->{$wc}->{'base_rev'};
227	my $subhistory = _expand_path($history, $wc_hash->{$wc}->{'path'});
228	$this_rev = _scan_dir("t/$wc");
229	is_deeply(
230	    $subhistory,
231	    $this_rev,
232	    "Directories are consistent at rev: $rev"
233	);
234    }
235}
236
237sub _expand_path {
238    my ($tree, $path) = @_;
239    my @paths = split('/',$path);
240    my $eval = "\$tree->{'".join("'}->{'", @paths)."'}";
241    return eval $eval;
242}
243
244sub _scan_dir {
245    my ($dir) = @_;
246    my $fsize;
247    my $this_rev = {};
248
249    opendir my($DIR), $dir;
250    my @directory = grep !/^\..*/, readdir $DIR;
251    closedir $DIR;
252
253    foreach my $file ( @directory ) {
254	if ( -d "$dir/$file" ) {
255	    $this_rev->{$file} = _scan_dir( "$dir/$file" );
256	}
257	elsif ( ( -f "$dir/$file" )  && ( my $size = -s "$dir/$file" ) ) {
258	    $this_rev->{$file} = $size;
259	}
260    }
261    return defined $this_rev ? $this_rev : {};
262}
263
2641; # magic return
265__DATA__
266$repos_history = [
267  {},
268  {
269    'project2' => {
270      'trunk' => {},
271      'branches' => {},
272      'tags' => {}
273    },
274    'project1' => {
275      'trunk' => {},
276      'branches' => {},
277      'tags' => {}
278    }
279  },
280  {
281    'project2' => {
282      'trunk' => {},
283      'branches' => {},
284      'tags' => {}
285    },
286    'project1' => {
287      'trunk' => {
288        'file1' => '6',
289        'dir1' => {
290          'file2' => '6'
291        }
292      },
293      'branches' => {},
294      'tags' => {}
295    }
296  },
297  {
298    'project2' => {
299      'trunk' => {},
300      'branches' => {},
301      'tags' => {}
302    },
303    'project1' => {
304      'trunk' => {
305        'file1' => '6',
306        'dir1' => {
307          'file2' => '6'
308        }
309      },
310      'branches' => {},
311      'tags' => {
312        'TRUNK-1135534439' => {
313          'file1' => '6',
314          'dir1' => {
315            'file2' => '6'
316          }
317        }
318      }
319    }
320  },
321  {
322    'project2' => {
323      'trunk' => {},
324      'branches' => {},
325      'tags' => {}
326    },
327    'project1' => {
328      'trunk' => {
329        'file1' => '6',
330        'dir1' => {
331          'file2' => '6'
332        }
333      },
334      'branches' => {
335        'branch1' => {
336          'file1' => '6',
337          'dir1' => {
338            'file2' => '6'
339          }
340        }
341      },
342      'tags' => {
343        'TRUNK-1135534439' => {
344          'file1' => '6',
345          'dir1' => {
346            'file2' => '6'
347          }
348        }
349      }
350    }
351  },
352  {
353    'project2' => {
354      'trunk' => {},
355      'branches' => {},
356      'tags' => {}
357    },
358    'project1' => {
359      'trunk' => {
360        'file1' => '6',
361        'dir1' => {
362          'file2' => '6'
363        }
364      },
365      'branches' => {
366        'branch1' => {
367          'file1' => '6',
368          'file3' => '6',
369          'dir2' => {
370            'file4' => '6'
371          },
372          'dir1' => {
373            'file2' => '6'
374          }
375        }
376      },
377      'tags' => {
378        'TRUNK-1135534439' => {
379          'file1' => '6',
380          'dir1' => {
381            'file2' => '6'
382          }
383        }
384      }
385    }
386  },
387  {
388    'project2' => {
389      'trunk' => {},
390      'branches' => {},
391      'tags' => {}
392    },
393    'project1' => {
394      'trunk' => {
395        'file1' => '6',
396        'file3' => '6',
397        'dir2' => {
398          'file4' => '6'
399        },
400        'dir1' => {
401          'file2' => '6'
402        }
403      },
404      'branches' => {
405        'branch1' => {
406          'file1' => '6',
407          'file3' => '6',
408          'dir2' => {
409            'file4' => '6'
410          },
411          'dir1' => {
412            'file2' => '6'
413          }
414        }
415      },
416      'tags' => {
417        'TRUNK-1135534439' => {
418          'file1' => '6',
419          'dir1' => {
420            'file2' => '6'
421          }
422        }
423      }
424    }
425  },
426  {
427    'project2' => {
428      'trunk' => {},
429      'branches' => {},
430      'tags' => {}
431    },
432    'project1' => {
433      'trunk' => {
434        'file1' => '6',
435        'file3' => '6',
436        'dir2' => {
437          'file4' => '6'
438        },
439        'dir1' => {
440          'file2' => '6'
441        }
442      },
443      'branches' => {
444        'branch1' => {
445          'file1' => '6',
446          'file3' => '6',
447          'dir2' => {
448            'file4' => '6'
449          },
450          'dir1' => {
451            'file2' => '6'
452          }
453        }
454      },
455      'tags' => {
456        'TRUNK-1135538253' => {
457          'file1' => '6',
458          'file3' => '6',
459          'dir2' => {
460            'file4' => '6'
461          },
462          'dir1' => {
463            'file2' => '6'
464          }
465        },
466        'TRUNK-1135534439' => {
467          'file1' => '6',
468          'dir1' => {
469            'file2' => '6'
470          }
471        }
472      }
473    }
474  },
475  {
476    'project2' => {
477      'trunk' => {
478        'dir3' => {
479          'file6' => '6'
480        },
481        'file5' => '6',
482        'dir4' => {
483          'file7' => '6',
484          'file8' => '6'
485        }
486      },
487      'branches' => {},
488      'tags' => {}
489    },
490    'project1' => {
491      'trunk' => {
492        'file1' => '6',
493        'file3' => '6',
494        'dir2' => {
495          'file4' => '6'
496        },
497        'dir1' => {
498          'file2' => '6'
499        }
500      },
501      'branches' => {
502        'branch1' => {
503          'file1' => '6',
504          'file3' => '6',
505          'dir2' => {
506            'file4' => '6'
507          },
508          'dir1' => {
509            'file2' => '6'
510          }
511        }
512      },
513      'tags' => {
514        'TRUNK-1135538253' => {
515          'file1' => '6',
516          'file3' => '6',
517          'dir2' => {
518            'file4' => '6'
519          },
520          'dir1' => {
521            'file2' => '6'
522          }
523        },
524        'TRUNK-1135534439' => {
525          'file1' => '6',
526          'dir1' => {
527            'file2' => '6'
528          }
529        }
530      }
531    }
532  },
533  {
534    'project2' => {
535      'trunk' => {
536        'dir3' => {
537          'file6' => '6'
538        },
539        'file5' => '6',
540        'dir4' => {
541          'file7' => '6',
542          'file8' => '6'
543        }
544      },
545      'branches' => {},
546      'tags' => {
547        'TRUNK-1135538991' => {
548          'dir3' => {
549            'file6' => '6'
550          },
551          'file5' => '6',
552          'dir4' => {
553            'file7' => '6',
554            'file8' => '6'
555          }
556        }
557      }
558    },
559    'project1' => {
560      'trunk' => {
561        'file1' => '6',
562        'file3' => '6',
563        'dir2' => {
564          'file4' => '6'
565        },
566        'dir1' => {
567          'file2' => '6'
568        }
569      },
570      'branches' => {
571        'branch1' => {
572          'file1' => '6',
573          'file3' => '6',
574          'dir2' => {
575            'file4' => '6'
576          },
577          'dir1' => {
578            'file2' => '6'
579          }
580        }
581      },
582      'tags' => {
583        'TRUNK-1135538253' => {
584          'file1' => '6',
585          'file3' => '6',
586          'dir2' => {
587            'file4' => '6'
588          },
589          'dir1' => {
590            'file2' => '6'
591          }
592        },
593        'TRUNK-1135534439' => {
594          'file1' => '6',
595          'dir1' => {
596            'file2' => '6'
597          }
598        }
599      }
600    }
601  },
602  {
603    'project2' => {
604      'trunk' => {
605        'dir3' => {
606          'file6' => '6'
607        },
608        'file5' => '6',
609        'dir4' => {
610          'file7' => '6',
611          'file8' => '6'
612        }
613      },
614      'branches' => {
615        'branch2' => {
616          'dir3' => {
617            'file6' => '6'
618          },
619          'file5' => '6',
620          'dir4' => {
621            'file7' => '6',
622            'file8' => '6'
623          }
624        }
625      },
626      'tags' => {
627        'TRUNK-1135538991' => {
628          'dir3' => {
629            'file6' => '6'
630          },
631          'file5' => '6',
632          'dir4' => {
633            'file7' => '6',
634            'file8' => '6'
635          }
636        }
637      }
638    },
639    'project1' => {
640      'trunk' => {
641        'file1' => '6',
642        'file3' => '6',
643        'dir2' => {
644          'file4' => '6'
645        },
646        'dir1' => {
647          'file2' => '6'
648        }
649      },
650      'branches' => {
651        'branch1' => {
652          'file1' => '6',
653          'file3' => '6',
654          'dir2' => {
655            'file4' => '6'
656          },
657          'dir1' => {
658            'file2' => '6'
659          }
660        }
661      },
662      'tags' => {
663        'TRUNK-1135538253' => {
664          'file1' => '6',
665          'file3' => '6',
666          'dir2' => {
667            'file4' => '6'
668          },
669          'dir1' => {
670            'file2' => '6'
671          }
672        },
673        'TRUNK-1135534439' => {
674          'file1' => '6',
675          'dir1' => {
676            'file2' => '6'
677          }
678        }
679      }
680    }
681  },
682  {
683    'project2' => {
684      'trunk' => {
685        'dir3' => {
686          'file6' => '6'
687        },
688        'file5' => '6',
689        'dir4' => {
690          'file7' => '6',
691          'file8' => '6'
692        }
693      },
694      'branches' => {
695        'branch2' => {
696          'file5.new' => '6',
697          'dir5' => {
698            'file6' => '6'
699          },
700          'dir4' => {
701            'file7' => '6',
702            'file8' => '6'
703          }
704        }
705      },
706      'tags' => {
707        'TRUNK-1135538991' => {
708          'dir3' => {
709            'file6' => '6'
710          },
711          'file5' => '6',
712          'dir4' => {
713            'file7' => '6',
714            'file8' => '6'
715          }
716        }
717      }
718    },
719    'project1' => {
720      'trunk' => {
721        'file1' => '6',
722        'file3' => '6',
723        'dir2' => {
724          'file4' => '6'
725        },
726        'dir1' => {
727          'file2' => '6'
728        }
729      },
730      'branches' => {
731        'branch1' => {
732          'file1' => '6',
733          'file3' => '6',
734          'dir2' => {
735            'file4' => '6'
736          },
737          'dir1' => {
738            'file2' => '6'
739          }
740        }
741      },
742      'tags' => {
743        'TRUNK-1135538253' => {
744          'file1' => '6',
745          'file3' => '6',
746          'dir2' => {
747            'file4' => '6'
748          },
749          'dir1' => {
750            'file2' => '6'
751          }
752        },
753        'TRUNK-1135534439' => {
754          'file1' => '6',
755          'dir1' => {
756            'file2' => '6'
757          }
758        }
759      }
760    }
761  },
762  {
763    'project2' => {
764      'trunk' => {
765        'dir3' => {
766          'file6' => '6'
767        },
768        'file5' => '6',
769        'dir4' => {
770          'file7' => '6',
771          'file8' => '6'
772        }
773      },
774      'branches' => {
775        'branch2' => {
776          'file5.new' => '6',
777          'dir5' => {
778            'file6' => '6'
779          },
780          'dir4' => {
781            'file7' => '6',
782            'file8' => '6'
783          }
784        }
785      },
786      'tags' => {
787        'TRUNK-1135539568' => {
788          'dir3' => {
789            'file6' => '6'
790          },
791          'file5' => '6',
792          'dir4' => {
793            'file7' => '6',
794            'file8' => '6'
795          }
796        },
797        'TRUNK-1135538991' => {
798          'dir3' => {
799            'file6' => '6'
800          },
801          'file5' => '6',
802          'dir4' => {
803            'file7' => '6',
804            'file8' => '6'
805          }
806        }
807      }
808    },
809    'project1' => {
810      'trunk' => {
811        'file1' => '6',
812        'file3' => '6',
813        'dir2' => {
814          'file4' => '6'
815        },
816        'dir1' => {
817          'file2' => '6'
818        }
819      },
820      'branches' => {
821        'branch1' => {
822          'file1' => '6',
823          'file3' => '6',
824          'dir2' => {
825            'file4' => '6'
826          },
827          'dir1' => {
828            'file2' => '6'
829          }
830        }
831      },
832      'tags' => {
833        'TRUNK-1135538253' => {
834          'file1' => '6',
835          'file3' => '6',
836          'dir2' => {
837            'file4' => '6'
838          },
839          'dir1' => {
840            'file2' => '6'
841          }
842        },
843        'TRUNK-1135534439' => {
844          'file1' => '6',
845          'dir1' => {
846            'file2' => '6'
847          }
848        }
849      }
850    }
851  }
852];
853$changes = [
854  {},
855  { '' =>
856      {
857	'project2' => 'A',
858	'project1' => 'A',
859	'project2/tags' => 'A',
860	'project1/trunk' => 'A',
861	'project2/branches' => 'A',
862	'project2/trunk' => 'A',
863	'project1/branches' => 'A',
864	'project1/tags' => 'A'
865      },
866  },
867  {
868    'project1/trunk' =>
869      {
870	'project1/trunk/file1' => 'A',
871	'project1/trunk/dir1/file2' => 'A',
872	'project1/trunk/dir1' => 'A'
873      },
874  },
875  {
876    'project1/trunk' => {},
877    'project1/tags/TRUNK-1135534439' =>
878      {
879	'project1/tags/TRUNK-1135534439/file1' => 'A',
880	'project1/tags/TRUNK-1135534439/dir1/file2' => 'A',
881	'project1/tags/TRUNK-1135534439/dir1' => 'A'
882      },
883  },
884  {
885    'project1/trunk' => {},
886    'project1/tags/TRUNK-1135534439' => {},
887    'project1/branches/branch1',
888      {
889	'project1/branches/branch1/file1' => 'A',
890	'project1/branches/branch1/dir1/file2' => 'A',
891	'project1/branches/branch1/dir1' => 'A'
892      },
893  },
894  { 'project1/branches/branch1' =>
895      {
896	'project1/branches/branch1/dir2/file4' => 'A',
897	'project1/branches/branch1/file3' => 'A',
898	'project1/branches/branch1/dir2' => 'A'
899      },
900  },
901  { 'project1/trunk' =>
902      {
903	'project1/trunk/file3' => 'A',
904	'project1/trunk/dir2' => 'A',
905	'project1/trunk/dir2/file4' => 'A'
906      },
907  },
908  { 'project1/tags/TRUNK-1135538253' =>
909      {
910	'project1/tags/TRUNK-1135538253/file3' => 'A',
911	'project1/tags/TRUNK-1135538253/dir2' => 'A',
912	'project1/tags/TRUNK-1135538253/dir2/file4' => 'A'
913      },
914  },
915  { 'project2/trunk' =>
916      {
917	'project2/trunk/dir4/file7' => 'A',
918	'project2/trunk/dir4/file8' => 'A',
919	'project2/trunk/dir3/file6' => 'A',
920	'project2/trunk/dir3' => 'A',
921	'project2/trunk/file5' => 'A',
922	'project2/trunk/dir4' => 'A'
923      },
924  },
925  { 'project2/tags/TRUNK-1135538991' =>
926      {
927	'project2/tags/TRUNK-1135538991/dir4/file7' => 'A',
928	'project2/tags/TRUNK-1135538991/dir4/file8' => 'A',
929	'project2/tags/TRUNK-1135538991/dir3/file6' => 'A',
930	'project2/tags/TRUNK-1135538991/dir3' => 'A',
931	'project2/tags/TRUNK-1135538991/file5' => 'A',
932	'project2/tags/TRUNK-1135538991/dir4' => 'A'
933      },
934  },
935  { 'project2/branches/branch2' =>
936      {
937	'project2/branches/branch2' => 'A'
938      },
939  },
940  { 'project2/branches/branch2' =>
941      {
942	'project2/branches/branch2/dir5' => 'A',
943	'project2/branches/branch2/file5' => 'D',
944	'project2/branches/branch2/dir3' => 'D',
945	'project2/branches/branch2/file5.new' => 'A'
946      },
947  },
948  { 'project2/tags/TRUNK-1135539568' =>
949      {
950	'project2/tags/TRUNK-1135539568' => 'A'
951      }
952  },
953];
954