From 4b0afa7d6cda9092c2b7e24eadc5df6f8708ad80 Mon Sep 17 00:00:00 2001 From: Mark Glines Date: Thu, 29 Jul 2010 07:31:25 -0400 Subject: [PATCH] Handle revision strings with leading "r"s. (RT #57837) --- lib/App/SVN/Bisect.pm | 5 + t/02_revisions_with_leading_rs.t | 301 +++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 t/02_revisions_with_leading_rs.t diff --git a/lib/App/SVN/Bisect.pm b/lib/App/SVN/Bisect.pm index d15a1ae..e5d9cb7 100644 --- a/lib/App/SVN/Bisect.pm +++ b/lib/App/SVN/Bisect.pm @@ -128,6 +128,7 @@ sub start { $$self{config}{orig} = $self->find_cur(); my $max = $self->find_max(); if(defined($$self{args}{Max})) { + $$self{args}{Max} = substr($$self{args}{Max},1) if substr($$self{args}{Max},0,1) eq 'r'; $$self{config}{max} = $$self{args}{Max}; die("Given 'max' value is greater than the working directory maximum $max!\n") if $$self{config}{max} > $max; @@ -221,6 +222,7 @@ sub skip { my @rev = @_; @rev = $$self{config}{cur} unless scalar @rev; foreach my $rev (@rev) { + $rev = substr($rev, 1) if substr($rev, 0, 1) eq 'r'; die("\"$rev\" is not a revision or is out of range.\n") unless exists($$self{config}{extant}{$rev}); $$self{config}{skip}{$rev} = 1; @@ -243,6 +245,7 @@ sub unskip { my @rev = @_; die("Usage: unskip \n") unless scalar @rev; foreach my $rev (@rev) { + $rev = substr($rev, 1) if substr($rev, 0, 1) eq 'r'; die("\"$rev\" is not a revision or is out of range.\n") unless exists($$self{config}{extant}{$rev}); delete($$self{config}{skip}{$rev}); @@ -504,6 +507,8 @@ sub ready { my $self = shift; return 0 unless defined $$self{config}{min}; return 0 unless defined $$self{config}{max}; + $$self{config}{min} = substr($$self{config}{min},1) if substr($$self{config}{min},0,1) eq 'r'; + $$self{config}{max} = substr($$self{config}{max},1) if substr($$self{config}{max},0,1) eq 'r'; $$self{config}{extant} = $self->fetch_log_revs() unless defined $$self{config}{extant}; return 1; diff --git a/t/02_revisions_with_leading_rs.t b/t/02_revisions_with_leading_rs.t new file mode 100644 index 0000000..1802c70 --- /dev/null +++ b/t/02_revisions_with_leading_rs.t @@ -0,0 +1,301 @@ +use strict; +use warnings; + +use File::Temp qw(tempdir); +use Cwd qw(getcwd); +use Test::More; +use Test::Exception; +use Test::Output; +use App::SVN::Bisect; +use File::Spec::Functions; + +my $tests; +BEGIN { $tests = 0; }; +plan tests => $tests; + +my $curdir = getcwd(); +my $tempdir = tempdir( CLEANUP => 1 ); +chdir($tempdir); +mkdir(".svn"); + +package test; +use Test::More; +our @ISA = qw(test2); +sub cmd { + my ($self, $cmd) = @_; + $$self{cmds} = [] unless exists $$self{cmds}; + push(@{$$self{cmds}}, $cmd); + return $$self{rvs}{$cmd} if exists $$self{rvs}{$cmd}; + return ''; +} + +sub stdout { + my ($self, @lines) = @_; + my $text = join("", @lines); + @lines = split(/[\r\n]+/, $text); + $$self{stdout} = [] unless exists $$self{stdout}; + push(@{$$self{stdout}}, @lines); +} + +sub exit { + my $self = shift; + die("exit"); +} + +package test2; +use Test::More; +our @ISA = qw(App::SVN::Bisect); +sub cmd { + my ($self, $cmd) = @_; + $$self{cmds} = [] unless exists $$self{cmds}; + push(@{$$self{cmds}}, $cmd); + return $$self{rvs}{$cmd} if exists $$self{rvs}{$cmd}; + return ''; +} + +sub exit { + my $self = shift; + die("exit"); +} + +package main; + +# constructor +throws_ok(sub { test->new() }, qr/specify an action/, "no Action"); +throws_ok(sub { test->new(Action => 'unknown') }, qr/Unknown action/, "bad Action"); +throws_ok(sub { test->new(Action => 'good') }, qr/not in progress/, "bad environment"); +BEGIN { $tests += 3; }; + + +my $test_responses = { + "svn info" => < < <new(Action => "start", Verbose => 0); +ok(defined($bisect), "new() returns an object"); +is(ref($bisect), "test", "new() blesses object into specified class"); +ok(!-f catfile(".svn", "bisect.yaml"), "metadata file not created yet"); +BEGIN { $tests += 3; }; + +# test readiness +ok(!$bisect->ready, "not ready yet"); +$$bisect{config}{min} = 'r0'; +ok(!$bisect->ready, "still not ready"); +$$bisect{config}{max} = 'r31'; +ok($bisect->ready , "ready now"); +BEGIN { $tests += 3; }; + +# test "start" +$bisect = test->new(Action => "start", Min => 'r0', Max => 'r35', Verbose => 0); +$$bisect{rvs} = $test_responses; +throws_ok(sub { $bisect->do_something_intelligent() }, qr/working directory maximum/, "Max exceeds log"); +$bisect = test->new(Action => "start", Min => 'r0', Max => 'r18', Verbose => 0); +$$bisect{rvs} = $test_responses; +lives_ok(sub { $bisect->do_something_intelligent() }, "Max in range lives"); +unlink(catfile(".svn", "bisect.yaml")); +$bisect = test->new(Action => "start", Min => 'r0', Max => 'r31', Verbose => 0); +$$bisect{rvs} = $test_responses; +$bisect->do_something_intelligent(); +ok(-f catfile(".svn", "bisect.yaml"), "metadata file created"); +is($$bisect{config}{max}, 31, "biggest svn revision was autodetected"); +is($$bisect{config}{min}, 0 , "minimum is 0 by default"); +is($$bisect{config}{orig},16, "Last Changed Rev: is parsed correctly"); +is($$bisect{config}{cur}, 15, "first step: test r15"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r15/, "Choosing r15"); +$bisect = test->new(Action => "start", Min => 'r0', Verbose => 0); +throws_ok(sub { $bisect->do_something_intelligent() }, qr/already in progress/, "re-start"); +BEGIN { $tests += 10; }; + +# test "skip" and "unskip" +$bisect = test->new(Action => "skip", Min => 'r0', Verbose => 0); +$$bisect{rvs} = $test_responses; +throws_ok(sub {$bisect->do_something_intelligent("r3") }, qr/out of range/, "invalid input"); +$bisect->do_something_intelligent("r15"); +is($$bisect{config}{cur}, 16, "next step: test r16"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r16/, "Choosing r16"); +$bisect = test->new(Action => "unskip", Min => 'r0', Verbose => 0); +$$bisect{rvs} = $test_responses; +throws_ok(sub {$bisect->do_something_intelligent() }, qr/Usage/, "missing param"); +throws_ok(sub {$bisect->do_something_intelligent("r3") }, qr/out of range/, "invalid input"); +$bisect->do_something_intelligent("r15"); +is($$bisect{config}{cur}, 15, "first step: test r15"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r15/, "Choosing r15"); +$bisect = test->new(Action => "skip", Min => 0, Verbose => 0); +$$bisect{rvs} = $test_responses; +$bisect->do_something_intelligent(); +is($$bisect{config}{cur}, 16, "next step: test r16"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r16/, "Choosing r16"); +BEGIN { $tests += 12; }; + +# test "view" +$bisect = test->new(Action => "view", Min => 'r0', Max => 'r31', Verbose => 0); +$$bisect{rvs} = $test_responses; +throws_ok( sub { $bisect->do_something_intelligent() }, qr/exit/, "normal exit"); +is(scalar @{$$bisect{stdout}}, 6, "6 lines written"); +is(join("\n", @{$$bisect{stdout}}, ""), <new(Action => "after", Min => 'r0', Verbose => 0); +$$bisect{rvs} = $test_responses; +throws_ok(sub {$bisect->do_something_intelligent("r3") }, qr/out of range/, "invalid input"); +$bisect->do_something_intelligent("r16"); +is($$bisect{config}{cur}, 8, "next step: test r8"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r8/, "Choosing r8"); +$bisect = test->new(Action => "after", Min => 'r0', Verbose => 0); +$$bisect{config}{cur} = 16; +$$bisect{rvs} = $test_responses; +$bisect->do_something_intelligent(); +is($$bisect{config}{cur}, 8, "next step: test r8"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r8/, "Choosing r8"); +BEGIN { $tests += 7; }; + +# test "before" +$bisect = test->new(Action => "before", Min => 'r0', Verbose => 0); +$$bisect{rvs} = $test_responses; +throws_ok(sub {$bisect->do_something_intelligent("r3") }, qr/out of range/, "invalid input"); +$bisect = test->new(Action => "before", Min => 'r0', Verbose => 0); +$$bisect{rvs} = $test_responses; +$bisect->do_something_intelligent("r8"); +is($$bisect{config}{cur}, 12, "next step: test r12"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r12/, "Choosing r12"); +$bisect = test->new(Action => "before", Min => 'r0', Verbose => 0); +$$bisect{config}{cur} = 8; +$$bisect{rvs} = $test_responses; +$bisect->do_something_intelligent(); +is($$bisect{config}{cur}, 12, "next step: test r12"); +is(scalar @{$$bisect{stdout}}, 1, "1 line written"); +like($$bisect{stdout}[0], qr/Choosing r12/, "Choosing r12"); +BEGIN { $tests += 7; }; + +# test endgame with skipped revs +$bisect = test->new(Action => "skip", Min => 'r0', Verbose => 0); +$$bisect{config}{cur} = 12; +$$bisect{rvs} = $test_responses; +$bisect->do_something_intelligent(); +is($$bisect{config}{cur}, 16, "next step: test r16"); +is(scalar @{$$bisect{stdout}}, 2, "2 lines written"); +like($$bisect{stdout}[0], qr/This is the end of the road/, "road end"); +like($$bisect{stdout}[1], qr/ 2 skipped revs preceding/, "counted skips"); +BEGIN { $tests += 4; }; + +# test "reset" +ok(-f catfile(".svn", "bisect.yaml"), "metadata file still exists"); +$bisect = test->new(Action => "reset", Min => 'r0', Verbose => 0); +$$bisect{rvs} = $test_responses; +$bisect->do_something_intelligent(); +is(scalar @{$$bisect{stdout}}, 1, "1 line of output"); +ok(!-f catfile(".svn", "bisect.yaml"), "metadata file removed"); +BEGIN { $tests += 3; }; + +# test "help" +$bisect = test->new(Action => "help"); +$$bisect{rvs} = $test_responses; +throws_ok(sub {$bisect->do_something_intelligent() }, qr/exit/, "help runs"); +is(scalar @{$$bisect{stdout}}, 12, "several lines written"); +like($$bisect{stdout}[0], qr/Usage:/, "first line is a Usage:"); +throws_ok(sub {$bisect->do_something_intelligent('_') }, qr/exit/, "help runs"); +is(scalar @{$$bisect{stdout}}, 24, "several lines written"); +like($$bisect{stdout}[12], qr/Usage:/, "first line is a Usage:"); +throws_ok(sub {$bisect->do_something_intelligent('nonexistent') }, qr/No known help topic/, "help dies"); +BEGIN { $tests += 7; }; + + +# test ->cmd() +$? = 0; +$$bisect{stdout} = []; +my $version = eval { App::SVN::Bisect::cmd($bisect, "svn --version") }; +SKIP: { + skip "no svn command found!", 4 if $?; + + like($version, qr/Subversion/, "svn --version output matches /Subversion/"); + throws_ok(sub { App::SVN::Bisect::cmd($bisect, "svn --unknown-arg 2>/dev/null") }, + qr/exit/, "handles error"); + is(scalar @{$$bisect{stdout}}, 2, "two lines written"); + like($$bisect{stdout}[1], qr/Please fix that/, "informative message"); +}; +BEGIN { $tests += 4; }; + + +# test ->find_max() +is($bisect->find_max(), 31, 'find_max'); +$$bisect{rvs}{'svn log -q -rHEAD:PREV'} = ''; +throws_ok(sub { $bisect->find_max() }, qr/Cannot find/, 'find_max barfs'); +BEGIN { $tests += 2; }; + + +# test ->find_cur() +is($bisect->find_cur(), 16, 'find_cur'); +$$bisect{rvs}{'svn info'} = ''; +throws_ok(sub { $bisect->find_cur() }, qr/Cannot find/, 'find_cur barfs'); +BEGIN { $tests += 2; }; + + +# test ->stdout() +$bisect = test2->new(Action => "help", Verbose => 1); +stdout_like(sub { eval { $bisect->do_something_intelligent() } }, qr/^Usage:/, "stdout"); +BEGIN { $tests += 1; }; + + +# test ->verbose() +stdout_like(sub { $bisect->verbose("foo bar") }, qr/^foo bar$/, "verbose"); +BEGIN { $tests += 1; }; + + +# test ->exit() +chdir $curdir; +App::SVN::Bisect->exit(0); +exit(1);