subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stef...@apache.org
Subject svn commit: r1687045 [31/31] - in /subversion/branches/svn-mergeinfo-normalizer: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ doc/ subversion/bindings/javahl/ subversion/bindings/javahl/native/ subversion/bindings/javahl/nativ...
Date Tue, 23 Jun 2015 12:55:47 GMT
Modified: subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport.pl?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport.pl (original)
+++ subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport.pl Tue Jun 23 12:55:43
2015
@@ -3,7 +3,18 @@ use warnings;
 use strict;
 use feature qw/switch say/;
 
-#no warnings 'experimental::smartmatch';
+use v5.10.0; # needed for $^V
+
+# The given/when smartmatch facility, introduced in Perl v5.10, was made
+# experimental and "subject to change" in v5.18 (see perl5180delta).  Every
+# use of it now triggers a warning.
+#
+# As of Perl v5.20.1, the semantics of given/when provided by Perl are
+# compatible with those expected by the script, so disable the warning for
+# those Perls.  But don't try to disable the the warning category on Perls
+# that don't know that category, since that breaks compilation.
+no if (v5.17.0 le $^V and $^V le v5.20.1),
+   warnings => 'experimental::smartmatch';
 
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
@@ -28,6 +39,8 @@ use Term::ReadKey qw/ReadMode ReadKey/;
 use File::Basename qw/basename dirname/;
 use File::Copy qw/copy move/;
 use File::Temp qw/tempfile/;
+use IO::Select ();
+use IPC::Open3 qw/open3/;
 use POSIX qw/ctermid strftime isprint isspace/;
 use Text::Wrap qw/wrap/;
 use Tie::File ();
@@ -122,6 +135,39 @@ $SVNq = "$SVN -q ";
 $SVNq =~ s/-q// if $DEBUG;
 
 
+my $BACKPORT_OPTIONS_HELP = <<EOF;
+y:   Run a merge.  It will not be committed.
+     WARNING: This will run 'update' and 'revert -R ./'.
+l:   Show logs for the entries being nominated.
+v:   Show the full entry (the prompt only shows an abridged version).
+q:   Quit the "for each entry" loop.  If you have entered any votes or
+     approvals, you will be prompted to commit them.
+±1:  Enter a +1 or -1 vote
+     You will be prompted to commit your vote at the end.
+±0:  Enter a +0 or -0 vote
+     You will be prompted to commit your vote at the end.
+a:   Move the entry to the "Approved changes" section.
+     When both approving and voting on an entry, approve first: for example,
+     to enter a third +1 vote, type "a" "+" "1".
+e:   Edit the entry in \$EDITOR, which is '$EDITOR'.
+     You will be prompted to commit your edits at the end.
+N:   Move to the next entry.  Do not prompt for the current entry again, even
+     in future runs, unless the STATUS nomination has been modified (e.g.,
+     revisions added, justification changed) in the repository.
+     (This is a local action that will not affect other people or bots.)
+ :   Move to the next entry.  Prompt for the current entry again in the next
+     run of backport.pl. 
+     (That's a space character, ASCII 0x20.)
+?:   Display this list.
+EOF
+
+my $BACKPORT_OPTIONS_MERGE_OPTIONS_HELP = <<EOF;
+y:   Open a shell.
+d:   View a diff.
+N:   Move to the next entry.
+?:   Display this list.
+EOF
+
 sub backport_usage {
   my $basename = basename $0;
   print <<EOF;
@@ -151,30 +197,11 @@ sense of "match" is either substring (fg
 In interactive mode (the default), you will be prompted once per STATUS entry.
 At a prompt, you have the following options:
 
-y:   Run a merge.  It will not be committed.
-     WARNING: This will run 'update' and 'revert -R ./'.
-l:   Show logs for the entries being nominated.
-v:   Show the full entry (the prompt only shows an abridged version).
-q:   Quit the "for each nomination" loop.
-±1:  Enter a +1 or -1 vote
-     You will be prompted to commit your vote at the end.
-±0:  Enter a +0 or -0 vote
-     You will be prompted to commit your vote at the end.
-a:   Move the entry to the "Approved changes" section.
-     When both approving and voting on an entry, approve first: for example,
-     to enter a third +1 vote, type "a" "+" "1".
-e:   Edit the entry in $EDITOR.
-     You will be prompted to commit your edits at the end.
-N:   Move to the next entry.  Cache the entry in '$STATEFILE' and do not
-     prompt for it again (even across runs) until it is changed.
- :   Move to the next entry, without adding the current one to the cache.
-     (That's a space character, ASCII 0x20.)
+$BACKPORT_OPTIONS_HELP
 
 After running a merge, you have the following options:
 
-y:   Open a shell.
-d:   View a diff.
-N:   Move to the next entry.
+$BACKPORT_OPTIONS_MERGE_OPTIONS_HELP
 
 To commit a merge, you have two options: either answer 'y' to the second prompt
 to open a shell, and manually run 'svn commit' therein; or set \$MAY_COMMIT=1
@@ -201,8 +228,9 @@ is not parsed; only its presence or abse
 
 Both batch modes also perform a basic sanity-check on entries that declare
 backport branches (via the "Branch:" header): if a backport branch is used, but
-at least one of the revisions enumerated in the entry title had not been merged
-from $TRUNK to the branch root, the hourly bot will turn red and 
+at least one of the revisions enumerated in the entry title had neither been
+merged from $TRUNK to the branch root, nor been committed
+directly to the backport branch, the hourly bot will turn red and 
 nightly bot will skip the entry and email its admins.  (The nightly bot does
 not email the list on failure, since it doesn't use buildbot.)
 
@@ -318,9 +346,46 @@ sub my_tempfile {
   return ($fh, $fn);
 }
 
+# The first argument is a shell script.  Run it and return the shell's
+# exit code, and stdout and stderr as references to arrays of lines.
+sub run_in_shell($) {
+  my $script = shift;
+  my $pid = open3 \*SHELL_IN, \*SHELL_OUT, \*SHELL_ERR, qw#/bin/sh#;
+  # open3 raises exception when it fails; no need to error check
+
+  print SHELL_IN $script;
+  close SHELL_IN;
+
+  # Read loop: tee stdout,stderr to arrays.
+  my $select = IO::Select->new(\*SHELL_OUT, \*SHELL_ERR);
+  my (@readable, $outlines, $errlines);
+  while (@readable = $select->can_read) {
+    for my $fh (@readable) {
+      my $line = <$fh>;
+      $select->remove($fh) if eof $fh or not defined $line;
+      next unless defined $line;
+
+      if ($fh == \*SHELL_OUT) {
+        push @$outlines, $line;
+        print STDOUT $line;
+      }
+      if ($fh == \*SHELL_ERR) {
+        push @$errlines, $line;
+        print STDERR $line;
+      }
+    }
+  }
+  waitpid $pid, 0; # sets $?
+  return $?, $outlines, $errlines;
+}
+
 
+# EXPECTED_ERROR_P is subref called with EXIT_CODE, OUTLINES, ERRLINES,
+# expected to return TRUE if the error should be considered fatal (cause
+# backport.pl to exit non-zero) or not.  It may be undef for default behaviour.
 sub merge {
-  my %entry = @_;
+  my %entry = %{ +shift };
+  my $expected_error_p = shift // sub { 0 }; # by default, errors are unexpected
   my $parno = $entry{parno} - scalar grep { $_->{parno} < $entry{parno} } @MERGES_TODAY;
 
   my ($logmsg_fh, $logmsg_filename) = my_tempfile();
@@ -366,7 +431,7 @@ fi
 $SVNq up
 $SVNq merge @mergeargs
 if [ "`$SVN status -q | wc -l`" -eq 1 ]; then
-  if [ -n "`$SVN diff | perl -lne 'print if s/^(Added|Deleted|Modified): //' | grep -vx svn:mergeinfo`"
]; then
+  if [ -z "`$SVN diff | perl -lne 'print if s/^(Added|Deleted|Modified): //' | grep -vx svn:mergeinfo`"
]; then
     # This check detects STATUS entries that name non-^/subversion/ revnums.
     # ### Q: What if we actually commit a mergeinfo fix to trunk and then want
     # ###    to backport it?
@@ -434,15 +499,26 @@ EOF
   revert(verbose => 0, discard_STATUS => $MAY_COMMIT);
 
   $MERGED_SOMETHING++;
-  open SHELL, '|-', qw#/bin/sh# or die "$! (in '$entry{header}')";
-  print SHELL $script;
-  close SHELL or do {
-    warn "$0: sh($?): $! (in '$entry{header}')";
-    die "Lost track of paragraph numbers; aborting" if $MAY_COMMIT
-  };
-  $ERRORS{$entry{id}} = [\%entry, "sh($?): $!"] if $?;
+  my ($exit_code, $outlines, $errlines) = run_in_shell $script;
+  unless ($! == 0) {
+    die "system() failed to spawn subshell ($!); aborting";
+  }
+  unless ($exit_code == 0) {
+    warn "$0: subshell exited with code $exit_code (in '$entry{header}') "
+        ."(maybe due to 'set -e'?)";
+
+    # If we're committing, don't attempt to guess the problem and gracefully
+    # continue; just abort.
+    if ($MAY_COMMIT) {
+      die "Lost track of paragraph numbers; aborting";
+    }
 
-  unlink $logmsg_filename unless $? or $!;
+    # Record the error, unless the caller wants not to.
+    $ERRORS{$entry{id}} = [\%entry, "subshell exited with code $exit_code"]
+      unless $expected_error_p->($exit_code, $outlines, $errlines);
+  }
+
+  unlink $logmsg_filename unless $exit_code;
 }
 
 # Input formats:
@@ -502,7 +578,7 @@ sub parse_entry {
   # summary
   do {
     push @logsummary, shift
-  } until $_[0] =~ /^\s*[][\w]+:/ or not defined $_[0];
+  } until $_[0] =~ /^\s*[A-Z][][\w]*:/ or not defined $_[0];
 
   # votes
   unshift @votes, pop until $_[-1] =~ /^\s*Votes:/ or not defined $_[-1];
@@ -689,7 +765,7 @@ sub vote {
         "Approve $_->{entry}->{header}."
       } @votesarray;
     (@sentences == 1)
-    ? $sentences[0]
+    ? "* STATUS: $sentences[0]"
     : "* STATUS:\n" . join "", map "  $_\n", @sentences;
   };
 
@@ -845,7 +921,8 @@ sub validate_branch_contains_named_revis
 
   my $shell_escaped_branch = shell_escape($entry{branch});
   %present = do {
-    my @present = `$SVN mergeinfo --show-revs=merged -- $TRUNK $BRANCHES/$shell_escaped_branch`;
+    my @present = `$SVN mergeinfo --show-revs=merged -- $TRUNK $BRANCHES/$shell_escaped_branch
&&
+                   $SVN mergeinfo --show-revs=eligible -- $BRANCHES/$shell_escaped_branch`;
     chomp @present;
     @present = map /(\d+)/g, @present;
     map +($_ => 1), @present;
@@ -881,7 +958,7 @@ sub handle_entry {
     unless (@vetoes) {
       if ($MAY_COMMIT and $in_approved) {
         # svn-role mode
-        merge %entry if validate_branch_contains_named_revisions %entry;
+        merge \%entry if validate_branch_contains_named_revisions %entry;
       } elsif (!$MAY_COMMIT) {
         # Scan-for-conflicts mode
 
@@ -890,7 +967,14 @@ sub handle_entry {
         # block.
         validate_branch_contains_named_revisions %entry;
 
-        merge %entry;
+        # E155015 is SVN_ERR_WC_FOUND_CONFLICT
+        my $expected_error_p = sub {
+          my ($exit_code, $outlines, $errlines) = @_;
+          ($exit_code == 0)
+            or
+          (grep /svn: E155015:/, @$errlines)
+        };
+        merge \%entry, ($entry{depends} ? $expected_error_p : undef);
 
         my $output = `$SVN status`;
 
@@ -947,13 +1031,13 @@ sub handle_entry {
     # See above for why the while(1).
     QUESTION: while (1) {
     my $key = $entry{digest};
-    given (prompt 'Run a merge? [y,l,v,±1,±0,q,e,a, ,N] ',
+    given (prompt 'Run a merge? [y,l,v,±1,±0,q,e,a, ,N,?] ',
                    verbose => 1, extra => qr/[+-]/) {
       when (/^y/i) {
-        #validate_branch_contains_named_revisions %entry;
-        merge %entry;
+        # TODO: validate_branch_contains_named_revisions %entry;
+        merge \%entry;
         while (1) {
-          given (prompt "Shall I open a subshell? [ydN] ", verbose => 1) {
+          given (prompt "Shall I open a subshell? [ydN?] ", verbose => 1) {
             when (/^y/i) {
               # TODO: if $MAY_COMMIT, save the log message to a file (say,
               #       backport.logmsg in the wcroot).
@@ -965,6 +1049,10 @@ sub handle_entry {
                 or warn "diff failed ($?): $!";
               next;
             }
+            when (/^[?]/i) {
+              print $BACKPORT_OPTIONS_MERGE_OPTIONS_HELP;
+              next;
+            }
             when (/^N/i) {
               # fall through.
             }
@@ -1028,6 +1116,10 @@ sub handle_entry {
       when (/^\x20/) {
         last PROMPT; # Fall off the end of the given/when block.
       }
+      when (/^[?]/i) {
+        print $BACKPORT_OPTIONS_HELP;
+        next QUESTION;
+      }
       default {
         say "Please use one of the options in brackets (q to quit)!";
         next QUESTION;
@@ -1163,7 +1255,7 @@ sub nominate_main {
   }
 
   my @lines;
-  warn "Wrapping [$logmsg]\n";
+  warn "Wrapping [$logmsg]\n" if $DEBUG;
   push @lines, wrap " * ", ' 'x3, join ', ', map "r$_", @revnums;
   push @lines, wrap ' 'x3, ' 'x3, split /\n/, $logmsg;
   push @lines, "   Justification:";
@@ -1195,7 +1287,7 @@ sub nominate_main {
   # Done!
   system "$SVN diff -- $STATUS";
   if (prompt "Commit this nomination? ") {
-    system "$SVN commit -m 'Nominate r$revnums[0].' -- $STATUS";
+    system "$SVN commit -m '* STATUS: Nominate r$revnums[0].' -- $STATUS";
     exit $?;
   }
   elsif (!$had_local_mods or prompt "Revert STATUS (destroying local mods)? ") {

Modified: subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport_tests.py?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport_tests.py (original)
+++ subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport_tests.py Tue Jun 23 12:55:43
2015
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # py:encoding=utf-8
 #
-#  backport_tests.py:  Test backport.pl
+#  backport_tests.py:  Test backport.pl or backport.py
 #
 #  Subversion is a tool for revision control.
 #  See http://subversion.apache.org for more information.
@@ -25,6 +25,25 @@
 #    under the License.
 ######################################################################
 
+# We'd like to test backport.pl and backport.py the same way, and to reuse
+# the svntest Python harness.  Since the latter standardizes argv parsing,
+# we can't use argv to determine whether .py or .pl should be tested.  Thus,
+# we implement the tests themselves in this file, while two driver files
+# (backport_tests_pl.py and backport_tests_py.py) invoke this file set
+# to run either backport-suite implementation.
+#
+# ### Note: the two driver scripts use the same repository names in
+# ### svn-test-work.  This is not ideal, but hopefully acceptable
+# ### temporarily until we switch over to backport.py and remove backport.pl.
+# ###
+# ### See svntest.testcase.FunctionTestCase.get_sandbox_name().
+try:
+  run_backport, run_conflicter
+except NameError:
+  raise Exception("Failure: %s should not be run directly, or the wrapper "
+                  "does not define both run_backport() and run_conflicter()"
+                  % __file__)
+
 # General modules
 import contextlib
 import functools
@@ -58,8 +77,6 @@ Wimp = svntest.testcase.Wimp_deco
 ######################################################################
 # Helper functions
 
-BACKPORT_PL = os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                           'backport.pl'))
 STATUS = 'branch/STATUS'
 
 class BackportTest(object):
@@ -110,7 +127,8 @@ class BackportTest(object):
       verify_backport(sbox, expected_dump_file, self.uuid)
     return wrapped_test_func
 
-def make_entry(revisions=None, logsummary=None, notes=None, branch=None, votes=None):
+def make_entry(revisions=None, logsummary=None, notes=None, branch=None,
+               depends=None, votes=None):
   assert revisions
   if logsummary is None:
     logsummary = "default logsummary"
@@ -122,6 +140,7 @@ def make_entry(revisions=None, logsummar
     'logsummary': logsummary,
     'notes': notes,
     'branch': branch,
+    'depends': depends,
     'votes': votes,
   }
 
@@ -143,6 +162,9 @@ def serialize_entry(entry):
     # branch
     '   Branch: %s\n' % (entry['branch'],) if entry['branch'] else '',
 
+    # depends
+    '   Depends: %s\n' % (entry['depends'],) if entry['depends'] else '',
+
     # votes
     '   Votes:\n',
     ''.join('     '
@@ -154,12 +176,15 @@ def serialize_entry(entry):
   ])
 
 def serialize_STATUS(approveds,
+                     candidates=[],
                      serialize_entry=serialize_entry):
   """Construct and return the contents of a STATUS file.
 
   APPROVEDS is an iterable of ENTRY dicts.  The dicts are defined
   to have the following keys: 'revisions', a list of revision numbers (ints);
   'logsummary'; and 'votes', a dict mapping ±1/±0 (int) to list of voters.
+
+  CANDIDATES is like APPROVEDS, except added to a different section of the file.
   """
 
   strings = []
@@ -168,6 +193,8 @@ def serialize_STATUS(approveds,
   strings.append("Candidate changes:\n")
   strings.append("==================\n\n")
 
+  strings.extend(map(serialize_entry, candidates))
+
   strings.append("Random new subheading:\n")
   strings.append("======================\n\n")
 
@@ -181,22 +208,6 @@ def serialize_STATUS(approveds,
 
   return "".join(strings)
 
-def run_backport(sbox, error_expected=False, extra_env=[]):
-  """Run backport.pl.  EXTRA_ENV is a list of key=value pairs (str) to set in
-  the child's environment.  ERROR_EXPECTED is propagated to run_command()."""
-  # TODO: if the test is run in verbose mode, pass DEBUG=1 in the environment,
-  #       and pass error_expected=True to run_command() to not croak on
-  #       stderr output from the child (because it uses 'sh -x').
-  args = [
-    '/usr/bin/env',
-    'SVN=' + svntest.main.svn_binary,
-    'YES=1', 'MAY_COMMIT=1', 'AVAILID=jrandom',
-  ] + list(extra_env) + [
-    'perl', BACKPORT_PL,
-  ]
-  with chdir(sbox.ospath('branch')):
-    return svntest.main.run_command(args[0], error_expected, False, *(args[1:]))
-
 def verify_backport(sbox, expected_dump_file, uuid):
   """Compare the contents of the SBOX repository with EXPECTED_DUMP_FILE.
   Set the UUID of SBOX to UUID beforehand.
@@ -220,7 +231,7 @@ def verify_backport(sbox, expected_dump_
   src_dump = svntest.actions.run_and_verify_dump(sbox.repo_dir)
 
   svntest.verify.compare_dump_files(
-    "Dump files", "DUMP", src_dump, dest_dump)
+    "Dump files", "DUMP", dest_dump, src_dump)
 
 ######################################################################
 # Tests
@@ -371,18 +382,20 @@ def backport_conflicts_detection(sbox):
   sbox.simple_commit(message="Conflicting change on iota")
 
   # r7: nominate r4, but without the requisite --accept
-  approved_entries = [
+  candidate_entries = [
     make_entry([4], notes="This will conflict."),
   ]
-  sbox.simple_append(STATUS, serialize_STATUS(approved_entries))
+  sbox.simple_append(STATUS, serialize_STATUS([], candidate_entries))
   sbox.simple_commit(message='Nominate r4')
 
   # Run it.
-  exit_code, output, errput = run_backport(sbox, True,
-                                           # Choose conflicts mode:
-                                           ["MAY_COMMIT=0"])
+  exit_code, output, errput = run_conflicter(sbox, True)
 
-  # Verify
+  # Verify the conflict is detected.
+  expected_output = svntest.verify.RegexOutput(
+    'Index: iota',
+    match_all=False,
+  )
   expected_errput = (
     r'(?ms)' # re.MULTILINE | re.DOTALL
     r'.*Warning summary.*'
@@ -396,9 +409,25 @@ def backport_conflicts_detection(sbox):
                       ],
                       match_all=False)
   svntest.verify.verify_outputs(None, output, errput,
-                                svntest.verify.AnyOutput, expected_errput)
+                                expected_output, expected_errput)
   svntest.verify.verify_exit_code(None, exit_code, 1)
 
+  ## Now, let's test the "Depends:" annotation silences the error.
+
+  # Re-nominate.
+  approved_entries = [
+    make_entry([4], depends="World peace."),
+  ]
+  sbox.simple_append(STATUS, serialize_STATUS(approved_entries), truncate=True)
+  sbox.simple_commit(message='Re-nominate r4')
+
+  # Detect conflicts.
+  exit_code, output, errput = run_conflicter(sbox)
+
+  # Verify stdout.  (exit_code and errput were verified by run_conflicter().)
+  svntest.verify.verify_outputs(None, output, errput,
+                                "Conflicts found.*, as expected.", [])
+
 
 #----------------------------------------------------------------------
 @BackportTest(None) # would be 000000000007
@@ -455,6 +484,168 @@ def backport_branch_contains(sbox):
 
 
 #----------------------------------------------------------------------
+@BackportTest(None) # would be 000000000008
+def backport_double_conflict(sbox):
+  "two-revisioned entry with two conflicts"
+
+  # r6: conflicting change on branch
+  sbox.simple_append('branch/iota', 'Conflicts with first change')
+  sbox.simple_commit(message="Conflicting change on iota")
+
+  # r7: further conflicting change to same file
+  sbox.simple_update()
+  sbox.simple_append('subversion/trunk/iota', 'Third line\n')
+  sbox.simple_commit(message="iota's third line")
+
+  # r8: nominate
+  approved_entries = [
+    make_entry([4,7], depends="World peace.")
+  ]
+  sbox.simple_append(STATUS, serialize_STATUS(approved_entries))
+  sbox.simple_commit(message='Nominate the r4 group')
+
+  # Run it, in conflicts mode.
+  exit_code, output, errput = run_conflicter(sbox, True)
+
+  # Verify the failure mode: "merge conflict" error on stderr, but backport.pl
+  # itself exits with code 0, since conflicts were confined to Depends:-ed
+  # entries.
+  #
+  # The error only happens with multi-pass merges where the first pass
+  # conflicts and the second pass touches the conflict victim.
+  #
+  # The error would be:
+  #    subversion/libsvn_client/merge.c:5499: (apr_err=SVN_ERR_WC_FOUND_CONFLICT)
+  #    svn: E155015: One or more conflicts were produced while merging r3:4
+  #    into '/tmp/stw/working_copies/backport_tests-8/branch' -- resolve all
+  #    conflicts and rerun the merge to apply the remaining unmerged revisions
+  #    ...
+  #    Warning summary
+  #    ===============
+  #    
+  #    r4 (default logsummary): subshell exited with code 256
+  # And backport.pl would exit with exit code 1.
+
+  expected_output = 'Conflicts found.*, as expected.'
+  expected_errput = svntest.verify.RegexOutput(
+      ".*svn: E155015:.*", # SVN_ERR_WC_FOUND_CONFLICT
+      match_all=False,
+  )
+  svntest.verify.verify_outputs(None, output, errput,
+                                expected_output, expected_errput)
+  svntest.verify.verify_exit_code(None, exit_code, 0)
+  if any("Warning summary" in line for line in errput):
+    raise svntest.verify.SVNUnexpectedStderr(errput)
+
+  ## Now, let's ensure this does get detected if not silenced.
+  # r9: Re-nominate
+  approved_entries = [
+    make_entry([4,7]) # no depends=
+  ]
+  sbox.simple_append(STATUS, serialize_STATUS(approved_entries), truncate=True)
+  sbox.simple_commit(message='Re-nominate the r4 group')
+
+  exit_code, output, errput = run_conflicter(sbox, True)
+
+  ## An unexpected non-zero exit code is treated as a fatal error.
+  # [1-9]\d+ matches non-zero exit codes
+  expected_stdout = None
+  expected_errput = r'r4 .*: subshell exited with code (?:[1-9]\d+)' \
+                   r"|.*subprocess.CalledProcessError.*'merge'.*exit status 1"
+  svntest.verify.verify_exit_code(None, exit_code, 1)
+  svntest.verify.verify_outputs(None, output, errput,
+                                expected_stdout, expected_errput)
+
+
+
+#----------------------------------------------------------------------
+@BackportTest('76cee987-25c9-4d6c-ad40-000000000009')
+def backport_branch_with_original_revision(sbox):
+  "branch with original revision"
+
+  # r6: conflicting change on branch
+  sbox.simple_append('branch/iota', 'Conflicts with first change')
+  sbox.simple_commit(message="Conflicting change on iota")
+
+  # r7: backport branch
+  sbox.simple_update()
+  sbox.simple_copy('branch', 'subversion/branches/r4')
+  sbox.simple_commit(message='Create a backport branch')
+
+  # r8: merge into backport branch
+  sbox.simple_update()
+  svntest.main.run_svn(None, 'merge', '--record-only', '-c4',
+                       '^/subversion/trunk', sbox.ospath('subversion/branches/r4'))
+  sbox.simple_mkdir('subversion/branches/r4/A_resolved')
+  sbox.simple_append('subversion/branches/r4/iota', "resolved\n", truncate=1)
+  sbox.simple_commit(message='Conflict resolution via mkdir')
+
+  # r9: original revision on branch
+  sbox.simple_update()
+  sbox.simple_mkdir('subversion/branches/r4/dir-created-on-backport-branch')
+  sbox.simple_commit(message='An original revision on the backport branch')
+
+  # r10: nominate the branch with r9 listed
+  approved_entries = [
+    make_entry([4, 9], branch="r4")
+  ]
+  sbox.simple_append(STATUS, serialize_STATUS(approved_entries))
+  sbox.simple_commit(message='Nominate r4+r9')
+
+  # r11, r12: Run it.
+  run_backport(sbox)
+
+
+#----------------------------------------------------------------------
+@BackportTest(None)
+def backport_otherproject_change(sbox):
+  "inoperative revision"
+
+  # r6: a change outside ^/subversion
+  sbox.simple_mkdir('elsewhere')
+  sbox.simple_commit()
+
+  # r7: Nominate r6 by mistake
+  approved_entries = [
+    make_entry([6])
+  ]
+  sbox.simple_append(STATUS, serialize_STATUS(approved_entries))
+  sbox.simple_commit(message='Nominate r6 by mistake')
+
+  # Run it.
+  exit_code, output, errput = run_backport(sbox, error_expected=True)
+
+  # Verify no commit occurred.
+  svntest.actions.run_and_verify_svnlook(["7\n"], [],
+                                         'youngest', sbox.repo_dir)
+
+  # Verify the failure mode.
+  expected_stdout = None
+  expected_stderr = ".*only svn:mergeinfo changes.*"
+  if exit_code == 0:
+    # Can't use verify_exit_code() since the exact code used varies.
+    raise svntest.Failure("exit_code should be non-zero")
+  svntest.verify.verify_outputs(None, output, errput,
+                                expected_stdout, expected_stderr)
+
+#----------------------------------------------------------------------
+@BackportTest(None)
+def backport_STATUS_mods(sbox):
+  "local mods to STATUS"
+
+  # Introduce a local mod.
+  sbox.simple_append(STATUS, "\n")
+
+  exit_code, output, errput = run_backport(sbox, error_expected=True)
+  expected_stdout = None
+  expected_stderr = ".*Local mods.*STATUS.*"
+  if exit_code == 0:
+    # Can't use verify_exit_code() since the exact code used varies.
+    raise svntest.Failure("exit_code should be non-zero")
+  svntest.verify.verify_outputs(None, output, errput,
+                                expected_stdout, expected_stderr)
+
+#----------------------------------------------------------------------
 
 ########################################################################
 # Run the tests
@@ -468,6 +659,10 @@ test_list = [ None,
               backport_multirevisions,
               backport_conflicts_detection,
               backport_branch_contains,
+              backport_double_conflict,
+              backport_branch_with_original_revision,
+              backport_otherproject_change,
+              backport_STATUS_mods,
               # When adding a new test, include the test number in the last
               # 6 bytes of the UUID.
              ]

Propchange: subversion/branches/svn-mergeinfo-normalizer/tools/dist/backport_tests.py
            ('svn:executable' removed)

Modified: subversion/branches/svn-mergeinfo-normalizer/tools/dist/release.py
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/tools/dist/release.py?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/tools/dist/release.py (original)
+++ subversion/branches/svn-mergeinfo-normalizer/tools/dist/release.py Tue Jun 23 12:55:43
2015
@@ -87,12 +87,12 @@ tool_versions = {
   'trunk' : {
             'autoconf' : '2.69',
             'libtool'  : '2.4.3',
-            'swig'     : '3.0.0',
+            'swig'     : '2.0.12',
   },
   '1.9' : {
             'autoconf' : '2.69',
             'libtool'  : '2.4.3',
-            'swig'     : '3.0.0'
+            'swig'     : '2.0.12'
   },
   '1.8' : {
             'autoconf' : '2.69',
@@ -421,6 +421,29 @@ def compare_changes(repos, branch, revis
       logging.warning('CHANGES has unmerged revisions: %s' %
                       stdout.replace("\n", " "))
 
+
+_current_year = str(datetime.datetime.now().year)
+_copyright_re = re.compile(r'Copyright (?:\(C\) )?(?P<year>[0-9]+)'
+                           r' The Apache Software Foundation',
+                           re.MULTILINE)
+
+def check_copyright_year(repos, branch, revision):
+    def check_file(branch_relpath):
+        file_url = (repos + '/' + branch + '/'
+                    + branch_relpath + '@' + str(revision))
+        cat_cmd = ['svn', 'cat', file_url]
+        stdout = subprocess.check_output(cat_cmd)
+        m = _copyright_re.search(stdout)
+        if m:
+            year = m.group('year')
+        else:
+            year = None
+        if year != _current_year:
+            logging.warning('Copyright year in ' + branch_relpath
+                            + ' is not the current year')
+    check_file('NOTICE')
+    check_file('subversion/libsvn_subr/version.c')
+
 def roll_tarballs(args):
     'Create the release artifacts.'
 
@@ -432,6 +455,8 @@ def roll_tarballs(args):
     logging.info('Rolling release %s from branch %s@%d' % (args.version,
                                                            branch, args.revnum))
 
+    check_copyright_year(repos, args.branch, args.revnum)
+
     # Ensure we've got the appropriate rolling dependencies available
     autoconf = AutoconfDep(args.base_dir, False, args.verbose,
                          tool_versions[args.version.branch]['autoconf'])

Modified: subversion/branches/svn-mergeinfo-normalizer/win-tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/win-tests.py?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/win-tests.py (original)
+++ subversion/branches/svn-mergeinfo-normalizer/win-tests.py Tue Jun 23 12:55:43 2015
@@ -83,6 +83,13 @@ def _usage_exit():
   print("  --disable-http-v2      : Do not advertise support for HTTPv2 on server")
   print("  --disable-bulk-updates : Disable bulk updates on HTTP server")
   print("  --ssl-cert             : Path to SSL server certificate to trust.")
+  print("  --exclusive-wc-locks   : Enable exclusive working copy locks")
+  print("  --memcached-dir=DIR    : Run memcached from dir")
+  print("  --memcached-server=    : Enable usage of the specified memcached server")
+  print("              <url:port>")
+  print("  --skip-c-tests         : Skip all C tests")
+  print("  --dump-load-cross-check: Run the dump load cross check after every test")
+
   print("  --javahl               : Run the javahl tests instead of the normal tests")
   print("  --swig=language        : Run the swig perl/python/ruby tests instead of")
   print("                           the normal tests")
@@ -127,7 +134,9 @@ opts, args = my_getopt(sys.argv[1:], 'hr
                         'list', 'enable-sasl', 'bin=', 'parallel',
                         'config-file=', 'server-minor-version=', 'log-level=',
                         'log-to-stdout', 'mode-filter=', 'milestone-filter=',
-                        'ssl-cert='])
+                        'ssl-cert=', 'exclusive-wc-locks', 'memcached-server=',
+                        'skip-c-tests', 'dump-load-cross-check', 'memcached-dir=',
+                        ])
 if len(args) > 1:
   print('Warning: non-option arguments after the first one will be ignored')
 
@@ -162,6 +171,12 @@ mode_filter=None
 tests_to_run = []
 log_level = None
 ssl_cert = None
+exclusive_wc_locks = None
+run_memcached = None
+memcached_server = None
+memcached_dir = None
+skip_c_tests = None
+dump_load_cross_check = None
 
 for opt, val in opts:
   if opt in ('-h', '--help'):
@@ -238,6 +253,17 @@ for opt, val in opts:
     log_level = val
   elif opt == '--ssl-cert':
     ssl_cert = val
+  elif opt == '--exclusive-wc-locks':
+    exclusive_wc_locks = 1
+  elif opt == '--memcached-server':
+    memcached_server = val
+  elif opt == '--skip-c-tests':
+    skip_c_tests = 1
+  elif opt == '--dump-load-cross-check':
+    dump_load_cross_check = 1
+  elif opt == '--memcached-dir':
+    memcached_dir = val
+    run_memcached = 1
 
 # Calculate the source and test directory names
 abs_srcdir = os.path.abspath("")
@@ -569,6 +595,8 @@ class Httpd:
                                     'jrandom', 'rayjandom'])
     os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp',  self.httpd_users,
                                     'jconstant', 'rayjandom'])
+    os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp',  self.httpd_users,
+                                    '__dumpster__', '__loadster__'])
 
   def _create_mime_types_file(self):
     "Create empty mime.types file"
@@ -635,6 +663,10 @@ class Httpd:
     else:
       self._start_daemon()
 
+    # Avoid output from starting and preparing between test results
+    sys.stderr.flush()
+    sys.stdout.flush()
+
   def stop(self):
     if self.service:
       self._stop_service()
@@ -672,6 +704,45 @@ class Httpd:
         pass
     print('Httpd.stop_daemon not implemented')
 
+class Memcached:
+  "Run memcached for tests"
+  def __init__(self, abs_memcached_dir, memcached_server):
+    self.name = 'memcached.exe'
+
+    self.memcached_host, self.memcached_port = memcached_server.split(':')
+    self.memcached_dir = abs_memcached_dir
+
+    self.proc = None
+    self.path = os.path.join(self.memcached_dir, self.name)
+
+    self.memcached_args = [
+                            self.name,
+                            '-p', self.memcached_port,
+                            '-l', self.memcached_host
+                          ]
+
+  def __del__(self):
+    "Stop memcached when the object is deleted"
+    self.stop()
+
+  def start(self):
+    "Start memcached as daemon"
+    print('Starting %s as daemon' % self.name)
+    print(self.memcached_args)
+    self.proc = subprocess.Popen([self.path] + self.memcached_args)
+
+  def stop(self):
+    "Stop memcached"
+    if self.proc is not None:
+      try:
+        print('Stopping %s' % self.name)
+        self.proc.poll();
+        if self.proc.returncode is None:
+          self.proc.kill();
+        return
+      except AttributeError:
+        pass
+
 # Move the binaries to the test directory
 create_target_dir(abs_builddir)
 locate_libs()
@@ -692,10 +763,15 @@ create_target_dir(CMDLINE_TEST_SCRIPT_NA
 abs_builddir = fix_case(abs_builddir)
 
 daemon = None
+memcached = None
 # Run the tests
 
 # No need to start any servers if we are only listing the tests.
 if not list_tests:
+  if run_memcached:
+    memcached = Memcached(memcached_dir, memcached_server)
+    memcached.start()
+
   if run_svnserve:
     daemon = Svnserve(svnserve_args, objdir, abs_objdir, abs_builddir)
 
@@ -759,7 +835,11 @@ if not test_javahl and not test_swig:
                              fsfs_sharding, fsfs_packing,
                              list_tests, svn_bin, mode_filter,
                              milestone_filter,
-                             set_log_level=log_level, ssl_cert=ssl_cert)
+                             set_log_level=log_level, ssl_cert=ssl_cert,
+                             exclusive_wc_locks=exclusive_wc_locks,
+                             memcached_server=memcached_server,
+                             skip_c_tests=skip_c_tests,
+                             dump_load_cross_check=dump_load_cross_check)
   old_cwd = os.getcwd()
   try:
     os.chdir(abs_builddir)
@@ -792,6 +872,9 @@ elif test_javahl:
     if (objdir == 'Debug'):
       args = args + ('-Xcheck:jni',)
 
+    if cleanup:
+      args = args + ('-Dtest.cleanup=1',)
+
     args = args + (
             '-Dtest.rootdir=' + os.path.join(abs_builddir, 'javahl'),
             '-Dtest.srcdir=' + os.path.join(abs_srcdir,
@@ -976,6 +1059,9 @@ elif test_swig == 'ruby':
 if daemon:
   del daemon
 
+if memcached:
+  del memcached
+
 # Remove the execs again
 for tgt in copied_execs:
   try:



Mime
View raw message