[Bps-public-commit] Prophet - A disconnected, replicated p2p database branch, master, updated. 781c9447798e85a03f5ce618052100a22ee07a62

spang at bestpractical.com spang at bestpractical.com
Tue Jan 27 05:25:53 EST 2009


The branch, master has been updated
       via  781c9447798e85a03f5ce618052100a22ee07a62 (commit)
       via  1491c8e757c078494a1ec5df318319c9fc17b5dc (commit)
       via  e6278a5ce606848276ec234f6e46f3b88ff8f81a (commit)
       via  1a555a39533708f33ce4f77675eb80b190918d1d (commit)
       via  20b0b4c6c6271a90038fb0ac0c07fdeb92b93259 (commit)
      from  d68518866547f2795fe6dc38338266d709740969 (commit)

Summary of changes:
 lib/Prophet/CLI/Command/Log.pm |   92 +++++++++++++++++++++++++++++++----
 lib/Prophet/CLI/Dispatcher.pm  |    7 +++
 lib/Prophet/CLIContext.pm      |    2 +-
 lib/Prophet/Replica.pm         |    2 +-
 lib/Prophet/Replica/prophet.pm |    9 +++-
 lib/Prophet/Replica/sqlite.pm  |   10 +++-
 lib/Prophet/Test.pm            |   56 ++++++++++++++++-----
 t/log.t                        |  106 ++++++++++++++++++++++++++++++++++++++++
 8 files changed, 253 insertions(+), 31 deletions(-)
 create mode 100644 t/log.t

- Log -----------------------------------------------------------------
commit 20b0b4c6c6271a90038fb0ac0c07fdeb92b93259
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jan 26 12:53:34 2009 +0200

    allow specifying an endpoint in traverse_changesets

diff --git a/lib/Prophet/Replica.pm b/lib/Prophet/Replica.pm
index 5bc288a..5ef13c2 100644
--- a/lib/Prophet/Replica.pm
+++ b/lib/Prophet/Replica.pm
@@ -724,7 +724,7 @@ sub _read_luid2guid_mappings {
     return \%luid2guid;
 }
 
-=head3 traverse_changesets { after => SEQUENCE_NO, callback => sub {} }
+=head3 traverse_changesets { after => SEQUENCE_NO, until => SEQUENCE_NO, callback => sub {} }
 
 Walk through each changeset in the replica after SEQUENCE_NO, calling the
 C<callback> for each one in turn.
diff --git a/lib/Prophet/Replica/prophet.pm b/lib/Prophet/Replica/prophet.pm
index 9c0f6c7..a0e8f66 100644
--- a/lib/Prophet/Replica/prophet.pm
+++ b/lib/Prophet/Replica/prophet.pm
@@ -635,7 +635,9 @@ sub _get_changeset_index_entry {
 
 =head2 traverse_changesets { after => SEQUENCE_NO, callback => sub { } } 
 
-Walks through all changesets after $after, calling $callback on each.
+Walks through all changesets from $after to $until, calling $callback on each.
+
+If no $until is specified, the latest changeset is assumed.
 
 =cut
 
@@ -648,11 +650,14 @@ sub traverse_changesets {
         @_,
         {   after    => 1,
             callback => 1,
+            until    => 0,
         }
     );
 
     my $first_rev = ( $args{'after'} + 1 ) || 1;
-    my $latest = $self->latest_sequence_no();
+    my $latest = $args{until} ? $args{until} : $self->latest_sequence_no();
+
+    $latest = $self->latest_sequence_no() if $latest > $self->latest_sequence_no();
 
     my $chgidx = $self->_read_changeset_index;
     $self->log_debug("Traversing changesets between $first_rev and $latest");
diff --git a/lib/Prophet/Replica/sqlite.pm b/lib/Prophet/Replica/sqlite.pm
index cff2d35..7c1264f 100644
--- a/lib/Prophet/Replica/sqlite.pm
+++ b/lib/Prophet/Replica/sqlite.pm
@@ -383,10 +383,11 @@ sub _delete_record_props_from_db {
 
 }
 
-=head2 traverse_changesets { after => SEQUENCE_NO, callback => sub { } } 
+=head2 traverse_changesets { after => SEQUENCE_NO, UNTIL => SEQUENCE_NO, callback => sub { } } 
 
-Walks through all changesets after $after, calling $callback on each.
+Walks through all changesets from $after to $until, calling $callback on each.
 
+If no $until is specified, the latest changeset is assumed.
 
 =cut
 
@@ -396,11 +397,14 @@ sub traverse_changesets {
         @_,
         {   after    => 1,
             callback => 1,
+            until    => 0,
         }
     );
 
     my $first_rev = ( $args{'after'} + 1 ) || 1;
-    my $latest = $self->latest_sequence_no();
+    my $latest = $args{until} ? $args{until} : $self->latest_sequence_no();
+
+    $latest = $self->latest_sequence_no() if $latest > $self->latest_sequence_no();
 
     $self->log_debug("Traversing changesets between $first_rev and $latest");
     for my $rev ( $first_rev .. $latest ) {

commit 1a555a39533708f33ce4f77675eb80b190918d1d
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jan 26 19:30:51 2009 +0200

    anchor the end of this regex to avoid incorrect matches on things like '1..10'

diff --git a/lib/Prophet/CLIContext.pm b/lib/Prophet/CLIContext.pm
index ebe2ebc..58a6dd1 100644
--- a/lib/Prophet/CLIContext.pm
+++ b/lib/Prophet/CLIContext.pm
@@ -187,7 +187,7 @@ sub parse_args {
 
     # "ticket show 4" should DWIM and "ticket show --id=4"
     $self->set_arg( id => pop @primary )
-        if @primary && $primary[-1] =~ /^(?:\d+|[0-9a-f]{8}\-)/i;
+        if @primary && $primary[-1] =~ /^(?:\d+|[0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12})$/i;
 
     my $collecting_props = 0;
 

commit e6278a5ce606848276ec234f6e46f3b88ff8f81a
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jan 26 22:21:31 2009 +0200

    tests for log command

diff --git a/t/log.t b/t/log.t
new file mode 100644
index 0000000..61afce0
--- /dev/null
+++ b/t/log.t
@@ -0,0 +1,106 @@
+use warnings;
+use strict;
+use Test::More tests => 22;
+use File::Temp qw'tempdir';
+use Prophet::Test;
+
+# tests for log command
+
+use_ok('Prophet::CLI');
+$ENV{'PROPHET_REPO'} = tempdir( CLEANUP => ! $ENV{PROPHET_DEBUG}  ) . '/repo-' . $$;
+
+my $cli = Prophet::CLI->new();
+my $handle = $cli->handle;
+
+isa_ok( $handle, 'Prophet::Replica', "Got the handle" );
+
+$handle->initialize;
+
+use_ok('Prophet::CLI::Command::Log');
+
+# make some changes so the tests below don't pass just because they always
+# get zero as the latest sequence number
+use_ok('Prophet::Record');
+my $record = Prophet::Record->new( handle => $handle, type => 'Person' );
+my $mao = $record->create( props => { name => 'Mao', age => 0.7, species => 'cat' } );
+$record->set_prop( name => 'age', value => 1 );
+$record->set_prop( name => 'color', value => 'black' );
+
+diag("latest sequence no is ".$handle->latest_sequence_no);
+
+# test the range parsing / setting
+my $log = new Prophet::CLI::Command::Log(handle => $handle, cli => $cli, context => $cli->context );
+
+$log->set_arg('range', '0..20');
+my ($start, $end) = $log->parse_range_arg();
+is($start, 0, '0..20 starts at 0');
+is($end, 20, '0..20 ends at 20');
+
+$log->set_arg('range', '0..LATEST~5');
+($start, $end) = $log->parse_range_arg();
+is($start, 0, '0..LATEST~5 starts at 0');
+is($end, $handle->latest_sequence_no - 5,
+    '0..LATEST~5 ends at latest changeset - 5');
+
+$log->set_arg('range', 'LATEST~8..50');
+($start, $end) = $log->parse_range_arg();
+is($start, $handle->latest_sequence_no - 8, 'LATEST~8..50 starts at latest - 8');
+is($end, 50, 'LATEST~8..50 ends at 50');
+
+$log->set_arg('range', 'LATEST~10..LATEST~5');
+($start, $end) = $log->parse_range_arg();
+is($start, $handle->latest_sequence_no - 10, 'LATEST~10..LATEST~5 starts at latest - 10');
+is($end, $handle->latest_sequence_no - 5, 'LATEST~10..LATEST~5 ends at latest - 5');
+
+$log->set_arg('range', 'LATEST~10');
+($start, $end) = $log->parse_range_arg();
+is($start, $handle->latest_sequence_no - 10, 'LATEST~10 starts at latest - 10');
+is($end, $handle->latest_sequence_no, 'LATEST~10 ends at latest');
+
+$log->set_arg('range', 'LATEST');
+($start, $end) = $log->parse_range_arg();
+is($start, $handle->latest_sequence_no, 'LATEST starts at latest');
+is($end, $handle->latest_sequence_no, 'LATEST ends at latest');
+
+# run the command and test its output
+
+my $replica_uuid = replica_uuid;
+my $first = " $ENV{USER}".' at \d{4}-\d{2}-\d{2} .+	\(1\@'.$replica_uuid.'\)
+ # Person 1 \(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\)
+  \+ "original_replica" set to "'.$replica_uuid.'"
+  \+ "creator" set to "'.$ENV{USER}.'"
+  \+ "name" set to "Mao"
+  \+ "species" set to "cat"
+  \+ "age" set to "0.7"';
+
+my $second = " $ENV{USER}".' at \d{4}-\d{2}-\d{2} .+	\(2\@'.$replica_uuid.'\)
+ # Person 1 \(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\)
+  > "age" changed from "0.7" to "1"\.';
+
+my $third = " $ENV{USER}".' at \d{4}-\d{2}-\d{2} .+	\(3\@'.$replica_uuid.'\)
+ # Person 1 \(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\)
+  \+ "color" set to "black"';
+
+# --all
+my $out = run_command('log', '--all');
+like($out, qr{$first\n\n$second\n\n$third\n\n}, "--all outputs all changes");
+
+# range: digit and LATEST
+$out = run_command('log', '--range=0..LATEST~2');
+like($out, qr{$first\n\n}, "just the first change");
+
+# range: assumed end
+$out = run_command('log', '--range=LATEST~2');
+like($out, qr{$second\n\n$third\n\n}, "last two changes");
+
+# syntactic sugar
+$out = run_command('log', 'LATEST~2');
+like($out, qr{$second\n\n$third\n\n}, "syntactic sugar doesn't change output");
+
+# error -- invalid input
+run_output_matches('prophet', [ 'log', 'invalid' ],
+    [], [ "Invalid range specified.\n" ], "invalid input caught correctly" );
+
+# error -- end is before start
+run_output_matches('prophet', [ 'log', '10..5' ],
+    [], [ "START must be before END in START..END." ], "caught START before END correctly" );

commit 1491c8e757c078494a1ec5df318319c9fc17b5dc
Author: Christine Spang <spang at bestpractical.com>
Date:   Mon Jan 26 22:23:21 2009 +0200

    git-style <since>..<until> ranges in log command

diff --git a/lib/Prophet/CLI/Command/Log.pm b/lib/Prophet/CLI/Command/Log.pm
index e380b37..447b34c 100644
--- a/lib/Prophet/CLI/Command/Log.pm
+++ b/lib/Prophet/CLI/Command/Log.pm
@@ -2,17 +2,38 @@ package Prophet::CLI::Command::Log;
 use Moose;
 extends 'Prophet::CLI::Command';
 
+# Default: last 20 entries.
+# sd log --all                    # show it all (overrides everything else)
+# sd log --range 0..LATEST~5      # shows the first until 5 from the latest
+# sd log --range LATEST~10        # shows last 10 entries
+# sd log --range LATEST           # shows the latest entry
+
+# syntactic sugar in dispatcher:
+#  sd log 0..LATEST~5 => sd log --range 0..LATEST~5
+#  sd log LATEST~10   => sd log --range LATEST~10
+
 sub run {
     my $self   = shift;
+    my $handle = $self->handle;
 
-    $self->validate_args;
+    # --all overrides any other args
+    if ($self->has_arg('all')) {
+        $self->set_arg('range', '0..'.$handle->latest_sequence_no);
+    }
+
+    my ($start, $end) = $self->has_arg('range') ? $self->parse_range_arg() :
+        ($handle->latest_sequence_no - 20, $handle->latest_sequence_no);
+
+    # parse_range returned undef
+    die "Invalid range specified.\n" if !defined($start) || !defined($end);
 
-    my $handle = $self->handle;
-    my $newest = $self->arg('last') || $handle->latest_sequence_no;
-    my $start  = $newest - ( $self->arg('count') || '20' );
     $start = 0 if $start < 0;
+
+    die "START must be before END in START..END.\n" if $end - $start < 0;
+
     $handle->traverse_changesets(
-        after    => $start,
+        after    => $start - 1,
+        until    => $end,
         callback => sub {
             my $changeset = shift;
             $self->handle_changeset($changeset);
@@ -22,14 +43,65 @@ sub run {
 
 }
 
-sub validate_args {
+=head2 parse_range_arg
+
+Parses the string in the 'range' arg into start and end sequence numbers
+and returns them in that order.
+
+Returns undef if the string is malformed.
+
+=cut
+
+sub parse_range_arg {
     my $self = shift;
-    if ($self->has_arg('last') && $self->arg('last') !~ /\d+/) {
-        die "Value passed to --last must be a number.\n";
+    my $range = $self->arg('range');
+
+    # split on .. (denotes range)
+    my @start_and_end = split(/\.\./, $range, 2);
+    my ($start, $end);
+    if (@start_and_end == 1) {
+        # only one delimiter was specified -- this will be the
+        # START; END defaults to the latest
+        $end = $self->handle->latest_sequence_no;
+        $start = $self->_parse_delimiter($start_and_end[0]);
+    } elsif (@start_and_end == 2) {
+        # both delimiters were specified
+        # parse the first one as START
+        $start = $self->_parse_delimiter($start_and_end[0]);
+        # parse the second one as END
+        $end = $self->_parse_delimiter($start_and_end[1]);
+    } else {
+        # something wrong was specified
+        return undef;
     }
-    if ($self->has_arg('count') && $self->arg('count') !~ /\d+/) {
-        die "Value passed to --count must be a number.\n";
+    return ($start, $end);
+}
+
+=head2 _parse_delimiter($delim)
+
+Takes a delimiter string and parses into a sequence number. If
+it is not either an integer number or of the form LATEST~#,
+returns undef (invalid delimiter).
+
+=cut
+
+sub _parse_delimiter {
+    my ($self, $delim) = @_;
+
+    if ($delim =~ m/^\d+$/) {
+        # a sequence number was specified, just use it
+        return $delim;
+    } else {
+        # try to parse what was given as LATEST~#
+        # if it's just LATEST, we want only the last change
+        my $offset;
+        $offset = 0 if $delim eq 'LATEST';
+        (undef, $offset) = split(/~/, $delim, 2) if $delim =~ m/^LATEST~/;
+        return undef unless $offset =~ m/^\d+$/;
+
+        return $self->handle->latest_sequence_no - $offset;
     }
+    return undef;
 }
 
 sub handle_changeset {
diff --git a/lib/Prophet/CLI/Dispatcher.pm b/lib/Prophet/CLI/Dispatcher.pm
index e520006..1cec543 100644
--- a/lib/Prophet/CLI/Dispatcher.pm
+++ b/lib/Prophet/CLI/Dispatcher.pm
@@ -44,6 +44,13 @@ on qr{^(clone|pull) (\S+)$} => sub {
     run($1, $self);
 };
 
+# log range => log --range range
+on qr{log\s*([0-9LATEST.~]+)} => sub {
+    my $self = shift;
+    $self->context->set_arg(range => $1);
+    run('log', $self);
+};
+
 on [ ['create', 'new'] ]         => run_command("Create");
 on [ ['show', 'display'] ]       => run_command("Show");
 on [ ['update', 'edit'] ]        => run_command("Update");

commit 781c9447798e85a03f5ce618052100a22ee07a62
Author: Christine Spang <spang at bestpractical.com>
Date:   Tue Jan 27 12:11:33 2009 +0200

    as it turns out, sorting Regexp objects and expecting them to end up in the same order as the strings they will match turns out to be a bad assumption

diff --git a/lib/Prophet/Test.pm b/lib/Prophet/Test.pm
index 48dbdf3..677fa75 100644
--- a/lib/Prophet/Test.pm
+++ b/lib/Prophet/Test.pm
@@ -262,9 +262,6 @@ sub _mk_cmp_closure {
     }
 }
 
-# factored out so it can be shared between is_script_output
-# and run_script_matches_unordered
-
 # XXX note that this sub doesn't check to make sure we got
 # all the errors we were expecting (there can be more lines
 # in the expected stderr than the received stderr as long
@@ -312,25 +309,56 @@ outputs.
 
 sub run_output_matches_unordered {
     my ($cmd, $args, $stdout, $stderr, $msg) = @_;
+    $stderr ||= [];
 
     my ($val, $out, $err)  = run_script( $cmd, $args );
 
     local $Test::Builder::Level = $Test::Builder::Level + 1;
 
-    # in order to not force an ordering on the output, we sort both
-    # the expected and received output before comparing them
-    my $sorted_exp_out = [sort @$stdout];
-    my $sorted_exp_err = [sort @{$stderr||[]} ];
+    # Check if each line matches a line in the expected output and
+    # delete that line if we have a match. If no match is found,
+    # add an error.
+    my $errors = [];
+    my @lines = split /\n/, $out;
+    OUTPUT: while (my $line = shift @lines) {
+        for my $exp_line (@$stdout) {
+            if ((ref($exp_line) eq 'Regexp' ? ( $line =~ m/$exp_line/ ) :
+                                            ( $line eq $exp_line ))) {
+                # remove the found element from the array of expected output
+                $stdout = [grep { $_ ne $exp_line } @$stdout];
+                next OUTPUT;
+            }
+        }
+        # we didn't find a match
+        push @$errors, "couldn't find match for ($line)\n";
+    }
+
+    # do the same for STDERR
+    @lines = split /\n/, $err;
+    ERROR: while (my $line = shift @lines) {
+        for my $exp_line (@$stderr) {
+            if ((ref($exp_line) eq 'Regexp' ? ( $line =~ m/$exp_line/ ) :
+                                            ( $line eq $exp_line ))) {
+                # remove the found element from the array of expected output
+                $stderr = [grep { $_ ne $exp_line } @$stderr];
+                next ERROR;
+            }
+        }
+        # we didn't find a match
+        push @$errors, "couldn't find match for ($line)\n";
+    }
 
-    # compare and put errors into $error
-    my $error = [];
-    my $check_exp_out = _mk_cmp_closure($sorted_exp_out, $error);
-    my $check_exp_err = _mk_cmp_closure($sorted_exp_err, $error);
+    # add any expected lines that we didn't find to the errors
+    for my $exp_line (@$stdout, @$stderr) {
+        push @$errors, "got nothing, expected: $exp_line";
+    }
 
-    map { $check_exp_out->($_) } sort split(/\n/,$out);
-    map { $check_exp_err->($_) } sort split(/\n/,$err);
+    my $test_name = join( ' ', $msg ? "$msg:" : '', $cmd, @$args );
+    is(scalar(@$errors), 0, $test_name);
 
-    _check_cmp_closure_output($cmd, $msg, $args, $sorted_exp_out, $error);
+    if (@$errors) {
+        diag( "Errors: " . join( "\n", @$errors ) );
+    }
 }
 
 =head2 repo_path_for($username)

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list