~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/tests/cmdline/svntest/actions.py

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
######################################################################
25
25
 
26
26
import os, shutil, re, sys, errno
27
 
import difflib, pprint
 
27
import difflib, pprint, logging
28
28
import xml.parsers.expat
29
29
from xml.dom.minidom import parseString
 
30
if sys.version_info[0] >= 3:
 
31
  # Python >=3.0
 
32
  from io import StringIO
 
33
else:
 
34
  # Python <3.0
 
35
  from cStringIO import StringIO
30
36
 
31
37
import svntest
32
 
from svntest import main, verify, tree, wc
 
38
from svntest import main, verify, tree, wc, sandbox
33
39
from svntest import Failure
34
40
 
 
41
logger = logging.getLogger()
 
42
 
 
43
# (abbreviation)
 
44
Item = svntest.wc.StateItem
 
45
 
 
46
def _log_tree_state(msg, actual, subtree=""):
 
47
  if subtree:
 
48
    subtree += os.sep
 
49
  o = StringIO()
 
50
  o.write(msg + '\n')
 
51
  tree.dump_tree_script(actual, subtree, stream=o)
 
52
  logger.warn(o.getvalue())
 
53
  o.close()
 
54
 
35
55
def no_sleep_for_timestamps():
36
56
  os.environ['SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_SLEEP_FOR_TIMESTAMPS'] = 'yes'
37
57
 
69
89
    # import the greek tree, using l:foo/p:bar
70
90
    ### todo: svn should not be prompting for auth info when using
71
91
    ### repositories with no auth/auth requirements
72
 
    exit_code, output, errput = main.run_svn(None, 'import', '-m',
73
 
                                             'Log message for revision 1.',
74
 
                                             main.greek_dump_dir,
75
 
                                             main.pristine_greek_repos_url)
76
 
 
77
 
    # check for any errors from the import
78
 
    if len(errput):
79
 
      display_lines("Errors during initial 'svn import':",
80
 
                    'STDERR', None, errput)
81
 
      sys.exit(1)
 
92
    _, output, _ = main.run_svn(None, 'import', '-m',
 
93
                                'Log message for revision 1.',
 
94
                                main.greek_dump_dir,
 
95
                                main.pristine_greek_repos_url)
82
96
 
83
97
    # verify the printed output of 'svn import'.
84
98
    lastline = output.pop().strip()
85
99
    match = re.search("(Committed|Imported) revision [0-9]+.", lastline)
86
100
    if not match:
87
 
      print("ERROR:  import did not succeed, while creating greek repos.")
88
 
      print("The final line from 'svn import' was:")
89
 
      print(lastline)
 
101
      logger.error("import did not succeed, while creating greek repos.")
 
102
      logger.error("The final line from 'svn import' was:")
 
103
      logger.error(lastline)
90
104
      sys.exit(1)
91
105
    output_tree = wc.State.from_commit(output)
92
106
 
117
131
  nothing."""
118
132
 
119
133
  if path == main.pristine_greek_repos_dir:
120
 
    print("ERROR:  attempt to overwrite the pristine repos!  Aborting.")
 
134
    logger.error("attempt to overwrite the pristine repos!  Aborting.")
121
135
    sys.exit(1)
122
136
 
123
137
  # create an empty repository at PATH.
129
143
# the `pristine repos' to a new location.
130
144
# Note: make sure setup_pristine_greek_repository was called once before
131
145
# using this function.
132
 
def guarantee_greek_repository(path):
 
146
def guarantee_greek_repository(path, minor_version):
133
147
  """Guarantee that a local svn repository exists at PATH, containing
134
148
  nothing but the greek-tree at revision 1."""
135
149
 
136
150
  if path == main.pristine_greek_repos_dir:
137
 
    print("ERROR:  attempt to overwrite the pristine repos!  Aborting.")
 
151
    logger.error("attempt to overwrite the pristine repos!  Aborting.")
138
152
    sys.exit(1)
139
153
 
140
154
  # copy the pristine repository to PATH.
141
155
  main.safe_rmtree(path)
142
 
  if main.copy_repos(main.pristine_greek_repos_dir, path, 1):
143
 
    print("ERROR:  copying repository failed.")
 
156
  if main.copy_repos(main.pristine_greek_repos_dir, path, 1, 1, minor_version):
 
157
    logger.error("copying repository failed.")
144
158
    sys.exit(1)
145
159
 
146
160
  # make the repos world-writeable, for mod_dav_svn's sake.
316
330
  expected_stderr = []
317
331
  if bypass_prop_validation:
318
332
    exit_code, output, errput = main.run_command_stdin(
319
 
      main.svnadmin_binary, expected_stderr, 0, 1, dump_file_content,
 
333
      main.svnadmin_binary, expected_stderr, 0, True, dump_file_content,
320
334
      'load', '--force-uuid', '--quiet', '--bypass-prop-validation', repo_dir)
321
335
  else:
322
336
    exit_code, output, errput = main.run_command_stdin(
323
 
      main.svnadmin_binary, expected_stderr, 0, 1, dump_file_content,
 
337
      main.svnadmin_binary, expected_stderr, 0, True, dump_file_content,
324
338
      'load', '--force-uuid', '--quiet', repo_dir)
325
339
 
326
340
  verify.verify_outputs("Unexpected stderr output", None, errput,
407
421
                                     % (rev),
408
422
                                     "no-op update")
409
423
 
 
424
def run_and_verify_svnauthz(message, expected_stdout, expected_stderr,
 
425
                            expected_exit, compat_mode, *varargs):
 
426
  """Run svnauthz command and check its output and exit code.
 
427
     If COMPAT_MODE is True then run the command in pre-1.8
 
428
     compatibility mode"""
 
429
 
 
430
  if compat_mode:
 
431
    exit_code, out, err = main.run_svnauthz_validate(*varargs)
 
432
  else:
 
433
    exit_code, out, err = main.run_svnauthz(*varargs)
 
434
 
 
435
  verify.verify_outputs("Unexpected output", out, err,
 
436
                        expected_stdout, expected_stderr)
 
437
  verify.verify_exit_code(message, exit_code, expected_exit)
 
438
  return exit_code, out, err
 
439
 
410
440
######################################################################
411
441
# Subversion Actions
412
442
#
438
468
 
439
469
  if isinstance(output_tree, wc.State):
440
470
    output_tree = output_tree.old_tree()
441
 
  if isinstance(disk_tree, wc.State):
442
 
    disk_tree = disk_tree.old_tree()
443
471
 
444
472
  # Remove dir if it's already there, unless this is a forced checkout.
445
473
  # In that case assume we want to test a forced checkout's toleration
458
486
  try:
459
487
    tree.compare_trees("output", actual, output_tree)
460
488
  except tree.SVNTreeUnequal:
461
 
    print("ACTUAL OUTPUT TREE:")
462
 
    tree.dump_tree_script(actual, wc_dir_name + os.sep)
463
 
    raise
464
 
 
465
 
  # Create a tree by scanning the working copy
466
 
  actual = tree.build_tree_from_wc(wc_dir_name)
467
 
 
468
 
  # Verify expected disk against actual disk.
469
 
  try:
470
 
    tree.compare_trees("disk", actual, disk_tree,
471
 
                       singleton_handler_a, a_baton,
472
 
                       singleton_handler_b, b_baton)
473
 
  except tree.SVNTreeUnequal:
474
 
    print("ACTUAL DISK TREE:")
475
 
    tree.dump_tree_script(actual, wc_dir_name + os.sep)
476
 
    raise
 
489
    _log_tree_state("ACTUAL OUTPUT TREE:", actual, wc_dir_name)
 
490
    raise
 
491
 
 
492
  if disk_tree:
 
493
    verify_disk(wc_dir_name, disk_tree, False,
 
494
                singleton_handler_a, a_baton,
 
495
                singleton_handler_b, b_baton)
477
496
 
478
497
def run_and_verify_checkout(URL, wc_dir_name, output_tree, disk_tree,
479
498
                            singleton_handler_a = None,
523
542
  try:
524
543
    tree.compare_trees("output", actual, output_tree)
525
544
  except tree.SVNTreeUnequal:
526
 
    print("ACTUAL OUTPUT TREE:")
527
 
    tree.dump_tree_script(actual, export_dir_name + os.sep)
 
545
    _log_tree_state("ACTUAL OUTPUT TREE:", actual, export_dir_name)
528
546
    raise
529
547
 
530
548
  # Create a tree by scanning the working copy.  Don't ignore
536
554
  try:
537
555
    tree.compare_trees("disk", actual, disk_tree)
538
556
  except tree.SVNTreeUnequal:
539
 
    print("ACTUAL DISK TREE:")
540
 
    tree.dump_tree_script(actual, export_dir_name + os.sep)
 
557
    _log_tree_state("ACTUAL DISK TREE:", actual, export_dir_name)
541
558
    raise
542
559
 
543
560
 
559
576
    """Assert that changed_paths is the same as this entry's changed_paths
560
577
    Raises svntest.Failure if not.
561
578
    """
562
 
    raise Failure('NOT IMPLEMENTED')
 
579
    if self.changed_paths != changed_paths:
 
580
      raise Failure('\n' + '\n'.join(difflib.ndiff(
 
581
            pprint.pformat(changed_paths).splitlines(),
 
582
            pprint.pformat(self.changed_paths).splitlines())))
563
583
 
564
584
  def assert_revprops(self, revprops):
565
585
    """Assert that the dict revprops is the same as this entry's revprops.
592
612
    self.parser.EndElementHandler = self.handle_end_element
593
613
    self.parser.CharacterDataHandler = self.handle_character_data
594
614
    # Ignore some things.
595
 
    self.ignore_elements('log', 'paths', 'path', 'revprops')
 
615
    self.ignore_elements('log', 'paths', 'revprops')
596
616
    self.ignore_tags('logentry_end', 'author_start', 'date_start', 'msg_start')
597
617
    # internal state
598
618
    self.cdata = []
599
619
    self.property = None
 
620
    self.kind = None
 
621
    self.action = None
600
622
    # the result
601
623
    self.entries = []
602
624
 
640
662
    self.property = attrs['name']
641
663
  def property_end(self):
642
664
    self.entries[-1].revprops[self.property] = self.use_cdata()
 
665
  def path_start(self, attrs):
 
666
    self.kind = attrs['kind']
 
667
    self.action = attrs['action']
 
668
  def path_end(self):
 
669
    self.entries[-1].changed_paths[self.use_cdata()] = [{'kind': self.kind,
 
670
                                                         'action': self.action}]
643
671
 
644
672
def run_and_verify_log_xml(message=None, expected_paths=None,
645
673
                           expected_revprops=None, expected_stdout=None,
735
763
    mergeinfo_output_tree = mergeinfo_output_tree.old_tree()
736
764
  if isinstance(elision_output_tree, wc.State):
737
765
    elision_output_tree = elision_output_tree.old_tree()
738
 
  if isinstance(disk_tree, wc.State):
739
 
    disk_tree = disk_tree.old_tree()
740
 
  if isinstance(status_tree, wc.State):
741
 
    status_tree = status_tree.old_tree()
742
766
 
743
767
  # Verify actual output against expected output.
744
768
  if output_tree:
745
769
    try:
746
770
      tree.compare_trees("output", actual_output, output_tree)
747
771
    except tree.SVNTreeUnequal:
748
 
      print("ACTUAL OUTPUT TREE:")
749
 
      tree.dump_tree_script(actual_output, wc_dir_name + os.sep)
 
772
      _log_tree_state("ACTUAL OUTPUT TREE:", actual_output, wc_dir_name)
750
773
      raise
751
774
 
752
775
  # Verify actual mergeinfo recording output against expected output.
755
778
      tree.compare_trees("mergeinfo_output", actual_mergeinfo_output,
756
779
                         mergeinfo_output_tree)
757
780
    except tree.SVNTreeUnequal:
758
 
      print("ACTUAL MERGEINFO OUTPUT TREE:")
759
 
      tree.dump_tree_script(actual_mergeinfo_output,
760
 
                            wc_dir_name + os.sep)
 
781
      _log_tree_state("ACTUAL MERGEINFO OUTPUT TREE:", actual_mergeinfo_output,
 
782
                      wc_dir_name)
761
783
      raise
762
784
 
763
785
  # Verify actual mergeinfo elision output against expected output.
766
788
      tree.compare_trees("elision_output", actual_elision_output,
767
789
                         elision_output_tree)
768
790
    except tree.SVNTreeUnequal:
769
 
      print("ACTUAL ELISION OUTPUT TREE:")
770
 
      tree.dump_tree_script(actual_elision_output,
771
 
                            wc_dir_name + os.sep)
 
791
      _log_tree_state("ACTUAL ELISION OUTPUT TREE:", actual_elision_output,
 
792
                      wc_dir_name)
772
793
      raise
773
794
 
774
795
  # Create a tree by scanning the working copy, and verify it
775
796
  if disk_tree:
776
 
    actual_disk = tree.build_tree_from_wc(wc_dir_name, check_props)
777
 
    try:
778
 
      tree.compare_trees("disk", actual_disk, disk_tree,
779
 
                         singleton_handler_a, a_baton,
780
 
                         singleton_handler_b, b_baton)
781
 
    except tree.SVNTreeUnequal:
782
 
      print("EXPECTED DISK TREE:")
783
 
      tree.dump_tree_script(disk_tree)
784
 
      print("ACTUAL DISK TREE:")
785
 
      tree.dump_tree_script(actual_disk)
786
 
      raise
 
797
    verify_disk(wc_dir_name, disk_tree, check_props,
 
798
                singleton_handler_a, a_baton,
 
799
                singleton_handler_b, b_baton)
787
800
 
788
801
  # Verify via 'status' command too, if possible.
789
802
  if status_tree:
790
803
    run_and_verify_status(wc_dir_name, status_tree)
791
804
 
792
805
 
793
 
def verify_disk(wc_dir_name, disk_tree, check_props=False):
 
806
def verify_disk(wc_dir_name, disk_tree, check_props=False,
 
807
                singleton_handler_a = None, a_baton = None,
 
808
                singleton_handler_b = None, b_baton = None):
794
809
  """Verify WC_DIR_NAME against DISK_TREE.  If CHECK_PROPS is set,
795
810
  the comparison will examin props.  Returns if successful, raises on
796
811
  failure."""
797
 
  verify_update(None, None, None, wc_dir_name, None, None, None, disk_tree,
798
 
                None, check_props=check_props)
 
812
 
 
813
  if isinstance(disk_tree, wc.State):
 
814
    disk_tree = disk_tree.old_tree()
 
815
 
 
816
  actual_disk = tree.build_tree_from_wc(wc_dir_name, check_props)
 
817
  try:
 
818
    tree.compare_trees("disk", actual_disk, disk_tree,
 
819
                       singleton_handler_a, a_baton,
 
820
                       singleton_handler_b, b_baton)
 
821
  except tree.SVNTreeUnequal:
 
822
    _log_tree_state("EXPECTED DISK TREE:", disk_tree)
 
823
    _log_tree_state("ACTUAL DISK TREE:", actual_disk)
 
824
    raise
 
825
 
799
826
 
800
827
 
801
828
 
819
846
  If ERROR_RE_STRING, the update must exit with error, and the error
820
847
  message must match regular expression ERROR_RE_STRING.
821
848
 
822
 
  Else if ERROR_RE_STRING is None, then:
823
 
 
824
849
  If OUTPUT_TREE is not None, the subcommand output will be verified
825
850
  against OUTPUT_TREE.  If DISK_TREE is not None, the working copy
826
851
  itself will be verified against DISK_TREE.  If STATUS_TREE is not
845
870
 
846
871
  if error_re_string:
847
872
    rm = re.compile(error_re_string)
 
873
    match = None
848
874
    for line in errput:
849
875
      match = rm.search(line)
850
876
      if match:
851
 
        return
852
 
    raise main.SVNUnmatchedError
 
877
        break
 
878
    if not match:
 
879
      raise main.SVNUnmatchedError
853
880
 
854
881
  actual = wc.State.from_checkout(output)
855
882
  verify_update(actual, None, None, wc_dir_name,
1030
1057
    try:
1031
1058
      tree.compare_trees("disk", post_disk, pre_disk)
1032
1059
    except tree.SVNTreeError:
1033
 
      print("=============================================================")
1034
 
      print("Dry-run merge altered working copy")
1035
 
      print("=============================================================")
 
1060
      logger.warn("=============================================================")
 
1061
      logger.warn("Dry-run merge altered working copy")
 
1062
      logger.warn("=============================================================")
1036
1063
      raise
1037
1064
 
1038
1065
 
1096
1123
    out_dry_copy = set(out_dry[:])
1097
1124
 
1098
1125
    if out_copy != out_dry_copy:
1099
 
      print("=============================================================")
1100
 
      print("Merge outputs differ")
1101
 
      print("The dry-run merge output:")
 
1126
      logger.warn("=============================================================")
 
1127
      logger.warn("Merge outputs differ")
 
1128
      logger.warn("The dry-run merge output:")
1102
1129
      for x in out_dry:
1103
 
        sys.stdout.write(x)
1104
 
      print("The full merge output:")
1105
 
      for x in out:
1106
 
        sys.stdout.write(x)
1107
 
      print("=============================================================")
 
1130
        logger.warn(x)
 
1131
      logger.warn("The full merge output:")
 
1132
      for x in merge_diff_out:
 
1133
        logger.warn(x)
 
1134
      logger.warn("=============================================================")
1108
1135
      raise main.SVNUnmatchedError
1109
1136
 
1110
1137
  def missing_skip(a, b):
1111
 
    print("=============================================================")
1112
 
    print("Merge failed to skip: " + a.path)
1113
 
    print("=============================================================")
 
1138
    logger.warn("=============================================================")
 
1139
    logger.warn("Merge failed to skip: %s", a.path)
 
1140
    logger.warn("=============================================================")
1114
1141
    raise Failure
1115
1142
  def extra_skip(a, b):
1116
 
    print("=============================================================")
1117
 
    print("Merge unexpectedly skipped: " + a.path)
1118
 
    print("=============================================================")
 
1143
    logger.warn("=============================================================")
 
1144
    logger.warn("Merge unexpectedly skipped: %s", a.path)
 
1145
    logger.warn("=============================================================")
1119
1146
    raise Failure
1120
1147
 
1121
1148
  myskiptree = tree.build_tree_from_skipped(out)
1125
1152
    tree.compare_trees("skip", myskiptree, skip_tree,
1126
1153
                       extra_skip, None, missing_skip, None)
1127
1154
  except tree.SVNTreeUnequal:
1128
 
    print("ACTUAL SKIP TREE:")
1129
 
    tree.dump_tree_script(myskiptree, dir + os.sep)
 
1155
    _log_tree_state("ACTUAL SKIP TREE:", myskiptree, dir)
1130
1156
    raise
1131
1157
 
1132
1158
  actual_diff = svntest.wc.State.from_checkout(merge_diff_out, False)
1153
1179
  If ERROR_RE_STRING, 'svn patch' must exit with error, and the error
1154
1180
  message must match regular expression ERROR_RE_STRING.
1155
1181
 
1156
 
  Else if ERROR_RE_STRING is None, then:
1157
 
 
1158
1182
  The subcommand output will be verified against OUTPUT_TREE, and the
1159
1183
  working copy itself will be verified against DISK_TREE.  If optional
1160
1184
  STATUS_TREE is given, then 'svn status' output will be compared.
1182
1206
    try:
1183
1207
      tree.compare_trees("disk", post_disk, pre_disk)
1184
1208
    except tree.SVNTreeError:
1185
 
      print("=============================================================")
1186
 
      print("'svn patch --dry-run' altered working copy")
1187
 
      print("=============================================================")
 
1209
      logger.warn("=============================================================")
 
1210
      logger.warn("'svn patch --dry-run' altered working copy")
 
1211
      logger.warn("=============================================================")
1188
1212
      raise
1189
1213
 
1190
1214
  # Update and make a tree of the output.
1201
1225
    if not match:
1202
1226
      raise main.SVNUnmatchedError
1203
1227
  elif err:
1204
 
    print("UNEXPECTED STDERR:")
 
1228
    logger.warn("UNEXPECTED STDERR:")
1205
1229
    for x in err:
1206
 
      sys.stdout.write(x)
 
1230
      logger.warn(x)
1207
1231
    raise verify.SVNUnexpectedStderr
1208
1232
 
1209
1233
  if dry_run and out != out_dry:
1214
1238
                                     '', out_dry_expected, out_dry)
1215
1239
 
1216
1240
  def missing_skip(a, b):
1217
 
    print("=============================================================")
1218
 
    print("'svn patch' failed to skip: " + a.path)
1219
 
    print("=============================================================")
 
1241
    logger.warn("=============================================================")
 
1242
    logger.warn("'svn patch' failed to skip: %s", a.path)
 
1243
    logger.warn("=============================================================")
1220
1244
    raise Failure
1221
1245
  def extra_skip(a, b):
1222
 
    print("=============================================================")
1223
 
    print("'svn patch' unexpectedly skipped: " + a.path)
1224
 
    print("=============================================================")
 
1246
    logger.warn("=============================================================")
 
1247
    logger.warn("'svn patch' unexpectedly skipped: %s", a.path)
 
1248
    logger.warn("=============================================================")
1225
1249
    raise Failure
1226
1250
 
1227
1251
  myskiptree = tree.build_tree_from_skipped(out)
1263
1287
    verify.verify_outputs(None, None, err, None, expected_err)
1264
1288
    return
1265
1289
 
1266
 
  out = sorted([_f for _f in [x.rstrip()[1:] for x in out] if _f])
1267
 
  expected_output.sort()
 
1290
  out = [_f for _f in [x.rstrip()[1:] for x in out] if _f]
1268
1291
  extra_out = []
1269
1292
  if out != expected_output:
1270
1293
    exp_hash = dict.fromkeys(expected_output)
1323
1346
      error_re_string = ".*(" + error_re_string + ")"
1324
1347
    expected_err = verify.RegexOutput(error_re_string, match_all=False)
1325
1348
    verify.verify_outputs(None, None, errput, None, expected_err)
1326
 
    return
1327
1349
  elif errput:
1328
1350
    raise verify.SVNUnexpectedStderr(err)
1329
1351
 
1335
1357
                singleton_handler_b, b_baton,
1336
1358
                check_props)
1337
1359
 
1338
 
def process_output_for_commit(output):
 
1360
def process_output_for_commit(output, error_re_string):
1339
1361
  """Helper for run_and_verify_commit(), also used in the factory."""
1340
1362
  # Remove the final output line, and verify that the commit succeeded.
1341
1363
  lastline = ""
1354
1376
 
1355
1377
    cm = re.compile("(Committed|Imported) revision [0-9]+.")
1356
1378
    match = cm.search(lastline)
1357
 
    if not match:
1358
 
      print("ERROR:  commit did not succeed.")
1359
 
      print("The final line from 'svn ci' was:")
1360
 
      print(lastline)
 
1379
    if not match and not error_re_string:
 
1380
      logger.warn("ERROR:  commit did not succeed.")
 
1381
      logger.warn("The final line from 'svn ci' was:")
 
1382
      logger.warn(lastline)
1361
1383
      raise main.SVNCommitFailure
1362
1384
 
1363
1385
  # The new 'final' line in the output is either a regular line that
1399
1421
 
1400
1422
  if isinstance(output_tree, wc.State):
1401
1423
    output_tree = output_tree.old_tree()
1402
 
  if isinstance(status_tree, wc.State):
1403
 
    status_tree = status_tree.old_tree()
1404
1424
 
1405
1425
  # Commit.
1406
1426
  if '-m' not in args and '-F' not in args:
1413
1433
      error_re_string = ".*(" + error_re_string + ")"
1414
1434
    expected_err = verify.RegexOutput(error_re_string, match_all=False)
1415
1435
    verify.verify_outputs(None, None, errput, None, expected_err)
1416
 
    return
1417
1436
 
1418
1437
  # Else not expecting error:
1419
1438
 
1420
1439
  # Convert the output into a tree.
1421
 
  output = process_output_for_commit(output)
 
1440
  output = process_output_for_commit(output, error_re_string)
1422
1441
  actual = tree.build_tree_from_commit(output)
1423
1442
 
1424
1443
  # Verify actual output against expected output.
1425
 
  try:
1426
 
    tree.compare_trees("output", actual, output_tree)
1427
 
  except tree.SVNTreeError:
1428
 
      verify.display_trees("Output of commit is unexpected",
1429
 
                           "OUTPUT TREE", output_tree, actual)
1430
 
      print("ACTUAL OUTPUT TREE:")
1431
 
      tree.dump_tree_script(actual, wc_dir_name + os.sep)
1432
 
      raise
 
1444
  if output_tree:
 
1445
    try:
 
1446
      tree.compare_trees("output", actual, output_tree)
 
1447
    except tree.SVNTreeError:
 
1448
        verify.display_trees("Output of commit is unexpected",
 
1449
                             "OUTPUT TREE", output_tree, actual)
 
1450
        _log_tree_state("ACTUAL OUTPUT TREE:", actual, wc_dir_name)
 
1451
        raise
1433
1452
 
1434
1453
  # Verify via 'status' command too, if possible.
1435
1454
  if status_tree:
1438
1457
 
1439
1458
# This function always passes '-q' to the status command, which
1440
1459
# suppresses the printing of any unversioned or nonexistent items.
1441
 
def run_and_verify_status(wc_dir_name, output_tree,
 
1460
def run_and_verify_status(wc_dir_name, status_tree,
1442
1461
                          singleton_handler_a = None,
1443
1462
                          a_baton = None,
1444
1463
                          singleton_handler_b = None,
1445
1464
                          b_baton = None):
1446
1465
  """Run 'status' on WC_DIR_NAME and compare it with the
1447
 
  expected OUTPUT_TREE.  SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will
 
1466
  expected STATUS_TREE.  SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will
1448
1467
  be passed to tree.compare_trees - see that function's doc string for
1449
1468
  more details.
1450
1469
  Returns on success, raises on failure."""
1451
1470
 
1452
 
  if isinstance(output_tree, wc.State):
1453
 
    output_state = output_tree
1454
 
    output_tree = output_tree.old_tree()
1455
 
  else:
1456
 
    output_state = None
1457
 
 
1458
1471
  exit_code, output, errput = main.run_svn(None, 'status', '-v', '-u', '-q',
1459
1472
                                           wc_dir_name)
1460
1473
 
1461
 
  actual = tree.build_tree_from_status(output)
 
1474
  actual_status = svntest.wc.State.from_status(output)
1462
1475
 
1463
1476
  # Verify actual output against expected output.
1464
 
  try:
1465
 
    tree.compare_trees("status", actual, output_tree,
1466
 
                       singleton_handler_a, a_baton,
1467
 
                       singleton_handler_b, b_baton)
1468
 
  except tree.SVNTreeError:
1469
 
    verify.display_trees(None, 'STATUS OUTPUT TREE', output_tree, actual)
1470
 
    print("ACTUAL STATUS TREE:")
1471
 
    tree.dump_tree_script(actual, wc_dir_name + os.sep)
1472
 
    raise
 
1477
  if isinstance(status_tree, wc.State):
 
1478
    try:
 
1479
      status_tree.compare_and_display('status', actual_status)
 
1480
    except tree.SVNTreeError:
 
1481
      _log_tree_state("ACTUAL STATUS TREE:", actual_status.old_tree(),
 
1482
                                             wc_dir_name)
 
1483
      raise
 
1484
  else:
 
1485
    actual_status = actual_status.old_tree()
 
1486
    try:
 
1487
      tree.compare_trees("status", actual_status, status_tree,
 
1488
                         singleton_handler_a, a_baton,
 
1489
                         singleton_handler_b, b_baton)
 
1490
    except tree.SVNTreeError:
 
1491
      verify.display_trees(None, 'STATUS OUTPUT TREE', status_tree, actual_status)
 
1492
      _log_tree_state("ACTUAL STATUS TREE:", actual_status, wc_dir_name)
 
1493
      raise
1473
1494
 
1474
1495
  # if we have an output State, and we can/are-allowed to create an
1475
1496
  # entries-based State, then compare the two.
1476
 
  if output_state:
1477
 
    entries_state = wc.State.from_entries(wc_dir_name)
1478
 
    if entries_state:
1479
 
      tweaked = output_state.copy()
 
1497
  if isinstance(status_tree, wc.State):
 
1498
    actual_entries = wc.State.from_entries(wc_dir_name)
 
1499
    if actual_entries:
 
1500
      tweaked = status_tree.copy()
1480
1501
      tweaked.tweak_for_entries_compare()
1481
1502
      try:
1482
 
        tweaked.compare_and_display('entries', entries_state)
 
1503
        tweaked.compare_and_display('entries', actual_entries)
1483
1504
      except tree.SVNTreeUnequal:
1484
1505
        ### do something more
1485
1506
        raise
1492
1513
  expected STATUS_TREE.
1493
1514
  Returns on success, raises on failure."""
1494
1515
 
1495
 
  if isinstance(status_tree, wc.State):
1496
 
    status_tree = status_tree.old_tree()
1497
 
 
1498
1516
  exit_code, output, errput = main.run_svn(None, 'status', '-v',
1499
1517
                                           '-u', wc_dir_name)
1500
1518
 
1501
 
  actual = tree.build_tree_from_status(output)
 
1519
  actual_status = svntest.wc.State.from_status(output)
1502
1520
 
1503
1521
  # Verify actual output against expected output.
1504
 
  try:
1505
 
    tree.compare_trees("UNQUIET STATUS", actual, status_tree)
1506
 
  except tree.SVNTreeError:
1507
 
    print("ACTUAL UNQUIET STATUS TREE:")
1508
 
    tree.dump_tree_script(actual, wc_dir_name + os.sep)
1509
 
    raise
 
1522
  if isinstance(status_tree, wc.State):
 
1523
    try:
 
1524
      status_tree.compare_and_display('unquiet status', actual_status)
 
1525
    except tree.SVNTreeError:
 
1526
      _log_tree_state("ACTUAL STATUS TREE:",
 
1527
                      actual_status.normalize().old_tree(), wc_dir_name)
 
1528
      raise
 
1529
  else:
 
1530
    actual_status = actual_status.old_tree()
 
1531
    try:
 
1532
      tree.compare_trees("UNQUIET STATUS", actual_status, status_tree)
 
1533
    except tree.SVNTreeError:
 
1534
      _log_tree_state("ACTUAL UNQUIET STATUS TREE:", actual_status,
 
1535
                                                     wc_dir_name)
 
1536
      raise
1510
1537
 
1511
1538
def run_and_verify_status_xml(expected_entries = [],
1512
1539
                              *args):
1558
1585
          pprint.pformat(expected_entries).splitlines(),
1559
1586
          pprint.pformat(actual_entries).splitlines())))
1560
1587
 
 
1588
def run_and_verify_inherited_prop_xml(path_or_url,
 
1589
                                      expected_inherited_props,
 
1590
                                      expected_explicit_props,
 
1591
                                      propname=None,
 
1592
                                      peg_rev=None,
 
1593
                                      *args):
 
1594
  """If PROPNAME is None, then call run_and_verify_svn with proplist -v --xml
 
1595
  --show-inherited-props on PATH_OR_URL, otherwise call run_and_verify_svn
 
1596
  with propget PROPNAME --xml --show-inherited-props.
 
1597
 
 
1598
  PATH_OR_URL is pegged at PEG_REV if the latter is not None.  If PEG_REV
 
1599
  is none, then PATH_OR_URL is pegged at HEAD if a url.
 
1600
 
 
1601
  EXPECTED_INHERITED_PROPS is a (possibly empty) dict mapping working copy
 
1602
  paths or URLs to dicts of inherited properties. EXPECTED_EXPLICIT_PROPS is
 
1603
  a (possibly empty) dict of the explicit properties expected on PATH_OR_URL.
 
1604
 
 
1605
  Returns on success, raises on failure if EXPECTED_INHERITED_PROPS or
 
1606
  EXPECTED_EXPLICIT_PROPS don't match the results of proplist/propget.
 
1607
  """
 
1608
 
 
1609
  if peg_rev is None:
 
1610
    if sandbox.is_url(path_or_url):
 
1611
      path_or_url = path_or_url + '@HEAD'
 
1612
  else:
 
1613
    path_or_url = path_or_url + '@' + str(peg_rev)
 
1614
 
 
1615
  if (propname):
 
1616
    exit_code, output, errput = svntest.actions.run_and_verify_svn(
 
1617
      None, None, [], 'propget', propname, '--xml',
 
1618
      '--show-inherited-props', path_or_url, *args)
 
1619
  else:
 
1620
    exit_code, output, errput = svntest.actions.run_and_verify_svn(
 
1621
      None, None, [], 'proplist', '-v', '--xml', '--show-inherited-props',
 
1622
      path_or_url, *args)
 
1623
 
 
1624
  if len(errput) > 0:
 
1625
    raise Failure
 
1626
 
 
1627
  # Props inherited from within the WC are keyed on absolute paths.
 
1628
  expected_iprops = {}
 
1629
  for x in expected_inherited_props:
 
1630
    if sandbox.is_url(x):
 
1631
      expected_iprops[x] = expected_inherited_props[x]
 
1632
    else:
 
1633
      expected_iprops[os.path.abspath(x)] = expected_inherited_props[x]
 
1634
 
 
1635
  actual_iprops = {}
 
1636
  actual_explicit_props = {}
 
1637
 
 
1638
  doc = parseString(''.join(output))
 
1639
  targets = doc.getElementsByTagName('target')
 
1640
  for t in targets:
 
1641
 
 
1642
    # Create actual inherited props.
 
1643
    iprops = t.getElementsByTagName('inherited_property')
 
1644
 
 
1645
    if len(iprops) > 0:
 
1646
      actual_iprops[t.getAttribute('path')]={}
 
1647
 
 
1648
    for i in iprops:
 
1649
      actual_iprops[t.getAttribute('path')][i.getAttribute('name')] = \
 
1650
        i.firstChild.nodeValue
 
1651
 
 
1652
    # Create actual explicit props.
 
1653
    xprops = t.getElementsByTagName('property')
 
1654
 
 
1655
    for x in xprops:
 
1656
      actual_explicit_props[x.getAttribute('name')] = x.firstChild.nodeValue
 
1657
 
 
1658
  if expected_explicit_props != actual_explicit_props:
 
1659
    raise svntest.Failure(
 
1660
      'Actual and expected explicit props do not match\n' +
 
1661
      '\n'.join(difflib.ndiff(
 
1662
      pprint.pformat(expected_explicit_props).splitlines(),
 
1663
      pprint.pformat(actual_explicit_props).splitlines())))
 
1664
 
 
1665
  if expected_iprops != actual_iprops:
 
1666
    raise svntest.Failure(
 
1667
      'Actual and expected inherited props do not match\n' +
 
1668
      '\n'.join(difflib.ndiff(
 
1669
      pprint.pformat(expected_iprops).splitlines(),
 
1670
      pprint.pformat(actual_iprops).splitlines())))
 
1671
 
1561
1672
def run_and_verify_diff_summarize_xml(error_re_string = [],
1562
1673
                                      expected_prefix = None,
1563
1674
                                      expected_paths = [],
1610
1721
      modified_path = modified_path.replace(os.sep, "/")
1611
1722
 
1612
1723
    if modified_path not in expected_paths:
1613
 
      print("ERROR: %s not expected in the changed paths." % modified_path)
 
1724
      logger.warn("ERROR: %s not expected in the changed paths.", modified_path)
1614
1725
      raise Failure
1615
1726
 
1616
1727
    index = expected_paths.index(modified_path)
1622
1733
    actual_prop = path.getAttribute('props')
1623
1734
 
1624
1735
    if expected_item != actual_item:
1625
 
      print("ERROR: expected: %s actual: %s" % (expected_item, actual_item))
 
1736
      logger.warn("ERROR: expected: %s actual: %s", expected_item, actual_item)
1626
1737
      raise Failure
1627
1738
 
1628
1739
    if expected_kind != actual_kind:
1629
 
      print("ERROR: expected: %s actual: %s" % (expected_kind, actual_kind))
 
1740
      logger.warn("ERROR: expected: %s actual: %s", expected_kind, actual_kind)
1630
1741
      raise Failure
1631
1742
 
1632
1743
    if expected_prop != actual_prop:
1633
 
      print("ERROR: expected: %s actual: %s" % (expected_prop, actual_prop))
 
1744
      logger.warn("ERROR: expected: %s actual: %s", expected_prop, actual_prop)
1634
1745
      raise Failure
1635
1746
 
1636
1747
def run_and_verify_diff_summarize(output_tree, *args):
1653
1764
    tree.compare_trees("output", actual, output_tree)
1654
1765
  except tree.SVNTreeError:
1655
1766
    verify.display_trees(None, 'DIFF OUTPUT TREE', output_tree, actual)
1656
 
    print("ACTUAL DIFF OUTPUT TREE:")
1657
 
    tree.dump_tree_script(actual)
 
1767
    _log_tree_state("ACTUAL DIFF OUTPUT TREE:", actual)
1658
1768
    raise
1659
1769
 
1660
1770
def run_and_validate_lock(path, username):
1674
1784
                                              'info','-R',
1675
1785
                                              path)
1676
1786
 
1677
 
  ### TODO: Leverage RegexOuput([...], match_all=True) here.
 
1787
  ### TODO: Leverage RegexOutput([...], match_all=True) here.
1678
1788
  # prepare the regexs to compare against
1679
1789
  token_re = re.compile(".*?Lock Token: opaquelocktoken:.*?", re.DOTALL)
1680
1790
  author_re = re.compile(".*?Lock Owner: %s\n.*?" % username, re.DOTALL)
1697
1807
  # TODO: verify that the status of PATHS changes accordingly.
1698
1808
  if len(args) == 0:
1699
1809
    args = expected_paths
1700
 
  expected_output = verify.UnorderedOutput([
1701
 
    "Resolved conflicted state of '" + path + "'\n" for path in
1702
 
    expected_paths])
 
1810
  expected_output = verify.AlternateOutput([
 
1811
      verify.UnorderedOutput([
 
1812
        "Resolved conflicted state of '" + path + "'\n" for path in
 
1813
        expected_paths]),
 
1814
      verify.UnorderedOutput([
 
1815
        "Breaking move with source path '" + path + "'\n" for path in
 
1816
         expected_paths] + [
 
1817
        "Resolved conflicted state of '" + path + "'\n" for path in
 
1818
        expected_paths]),
 
1819
    ],
 
1820
    match_all=False)
1703
1821
  run_and_verify_svn(None, expected_output, [],
1704
1822
                     cmd, *args)
1705
1823
 
1733
1851
 
1734
1852
 
1735
1853
# This allows a test to *quickly* bootstrap itself.
1736
 
def make_repo_and_wc(sbox, create_wc = True, read_only = False):
 
1854
def make_repo_and_wc(sbox, create_wc = True, read_only = False,
 
1855
                     minor_version = None):
1737
1856
  """Create a fresh 'Greek Tree' repository and check out a WC from it.
1738
1857
 
1739
1858
  If READ_ONLY is False, a dedicated repository will be created, at the path
1748
1867
 
1749
1868
  # Create (or copy afresh) a new repos with a greek tree in it.
1750
1869
  if not read_only:
1751
 
    guarantee_greek_repository(sbox.repo_dir)
 
1870
    guarantee_greek_repository(sbox.repo_dir, minor_version)
1752
1871
 
1753
1872
  if create_wc:
1754
1873
    # Generate the expected output tree.
1819
1938
def hook_failure_message(hook_name):
1820
1939
  """Return the error message that the client prints for failure of the
1821
1940
  specified hook HOOK_NAME. The wording changed with Subversion 1.5."""
1822
 
  if svntest.main.options.server_minor_version < 5:
 
1941
 
 
1942
  # Output depends on the server version, not the repository version.
 
1943
  # This gets the wrong result for modern servers with old format
 
1944
  # repositories.
 
1945
  if svntest.main.options.server_minor_version < 5 and not svntest.main.is_ra_type_file():
1823
1946
    return "'%s' hook failed with error output:\n" % hook_name
1824
1947
  else:
1825
1948
    if hook_name in ["start-commit", "pre-commit"]:
1853
1976
  pre-revprop-change hook script and (if appropriate) making it executable."""
1854
1977
 
1855
1978
  hook_path = main.get_pre_revprop_change_hook_path(repo_dir)
1856
 
  main.create_python_hook_script(hook_path, 'import sys; sys.exit(0)')
 
1979
  main.create_python_hook_script(hook_path, 'import sys; sys.exit(0)',
 
1980
                                 cmd_alternative='@exit 0')
1857
1981
 
1858
1982
def disable_revprop_changes(repo_dir):
1859
1983
  """Disable revprop changes in the repository at REPO_DIR by creating a
1863
1987
  hook_path = main.get_pre_revprop_change_hook_path(repo_dir)
1864
1988
  main.create_python_hook_script(hook_path,
1865
1989
                                 'import sys\n'
1866
 
                                 'sys.stderr.write("pre-revprop-change %s" % " ".join(sys.argv[1:6]))\n'
1867
 
                                 'sys.exit(1)\n')
 
1990
                                 'sys.stderr.write("pre-revprop-change %s" %'
 
1991
                                                  ' " ".join(sys.argv[1:]))\n'
 
1992
                                 'sys.exit(1)\n',
 
1993
                                 cmd_alternative=
 
1994
                                       '@echo pre-revprop-change %* 1>&2\n'
 
1995
                                       '@exit 1\n')
1868
1996
 
1869
1997
def create_failing_post_commit_hook(repo_dir):
1870
1998
  """Create a post-commit hook script in the repository at REPO_DIR that always
1873
2001
  hook_path = main.get_post_commit_hook_path(repo_dir)
1874
2002
  main.create_python_hook_script(hook_path, 'import sys\n'
1875
2003
    'sys.stderr.write("Post-commit hook failed")\n'
1876
 
    'sys.exit(1)')
 
2004
    'sys.exit(1)\n',
 
2005
    cmd_alternative=
 
2006
            '@echo Post-commit hook failed 1>&2\n'
 
2007
            '@exit 1\n')
1877
2008
 
1878
2009
# set_prop can be used for properties with NULL characters which are not
1879
2010
# handled correctly when passed to subprocess.Popen() and values like "*"
1880
2011
# which are not handled correctly on Windows.
1881
 
def set_prop(name, value, path, expected_re_string=None):
 
2012
def set_prop(name, value, path, expected_re_string=None, force=None):
1882
2013
  """Set a property with specified value"""
 
2014
  if not force:
 
2015
    propset = ('propset',)
 
2016
  else:
 
2017
    propset = ('propset', '--force')
1883
2018
  if value and (value[0] == '-' or '\x00' in value or sys.platform == 'win32'):
1884
2019
    from tempfile import mkstemp
1885
2020
    (fd, value_file_path) = mkstemp()
 
2021
    os.close(fd)
1886
2022
    value_file = open(value_file_path, 'wb')
1887
2023
    value_file.write(value)
1888
2024
    value_file.flush()
1889
2025
    value_file.close()
1890
 
    exit_code, out, err = main.run_svn(expected_re_string, 'propset',
1891
 
                                       '-F', value_file_path, name, path)
1892
 
    os.close(fd)
 
2026
    propset += ('-F', value_file_path, name, path)
 
2027
    exit_code, out, err = main.run_svn(expected_re_string, *propset)
1893
2028
    os.remove(value_file_path)
1894
2029
  else:
1895
 
    exit_code, out, err = main.run_svn(expected_re_string, 'propset',
1896
 
                                       name, value, path)
 
2030
    propset += (name, value, path)
 
2031
    exit_code, out, err = main.run_svn(expected_re_string, *propset)
1897
2032
  if expected_re_string:
1898
2033
    if not expected_re_string.startswith(".*"):
1899
2034
      expected_re_string = ".*(" + expected_re_string + ")"
1909
2044
  else:
1910
2045
    revprop_options = []
1911
2046
  # Not using run_svn because binary_mode must be set
1912
 
  exit_code, out, err = main.run_command(main.svn_binary, None, 1, 'pg',
 
2047
  exit_code, out, err = main.run_command(main.svn_binary, None, True, 'pg',
1913
2048
                                         '--strict', name, path,
1914
2049
                                         '--config-dir',
1915
2050
                                         main.default_config_dir,
1917
2052
                                         '--password', main.wc_passwd,
1918
2053
                                         *revprop_options)
1919
2054
  if out != exp_out:
1920
 
    print("svn pg --strict %s output does not match expected." % name)
1921
 
    print("Expected standard output:  %s\n" % exp_out)
1922
 
    print("Actual standard output:  %s\n" % out)
 
2055
    logger.warn("svn pg --strict %s output does not match expected.", name)
 
2056
    logger.warn("Expected standard output:  %s\n", exp_out)
 
2057
    logger.warn("Actual standard output:  %s\n", out)
1923
2058
    raise Failure
1924
2059
 
1925
2060
def fill_file_with_lines(wc_path, line_nbr, line_descrip=None,
2092
2227
  run_and_verify_svn(None, verify.AnyOutput, [], 'update', wc_dir)
2093
2228
 
2094
2229
 
2095
 
def make_deep_trees(base):
2096
 
  """Helper function for deep trees conflicts. Create a set of trees,
2097
 
  each in its own "container" dir. Any conflicts can be tested separately
2098
 
  in each container.
2099
 
  """
2100
 
  j = os.path.join
2101
 
  # Create the container dirs.
2102
 
  F   = j(base, 'F')
2103
 
  D   = j(base, 'D')
2104
 
  DF  = j(base, 'DF')
2105
 
  DD  = j(base, 'DD')
2106
 
  DDF = j(base, 'DDF')
2107
 
  DDD = j(base, 'DDD')
2108
 
  os.makedirs(F)
2109
 
  os.makedirs(j(D, 'D1'))
2110
 
  os.makedirs(j(DF, 'D1'))
2111
 
  os.makedirs(j(DD, 'D1', 'D2'))
2112
 
  os.makedirs(j(DDF, 'D1', 'D2'))
2113
 
  os.makedirs(j(DDD, 'D1', 'D2', 'D3'))
2114
 
 
2115
 
  # Create their files.
2116
 
  alpha = j(F, 'alpha')
2117
 
  beta  = j(DF, 'D1', 'beta')
2118
 
  gamma = j(DDF, 'D1', 'D2', 'gamma')
2119
 
  main.file_append(alpha, "This is the file 'alpha'.\n")
2120
 
  main.file_append(beta, "This is the file 'beta'.\n")
2121
 
  main.file_append(gamma, "This is the file 'gamma'.\n")
2122
 
 
2123
 
 
2124
 
def add_deep_trees(sbox, base_dir_name):
2125
 
  """Prepare a "deep_trees" within a given directory.
2126
 
 
2127
 
  The directory <sbox.wc_dir>/<base_dir_name> is created and a deep_tree
2128
 
  is created within. The items are only added, a commit has to be
2129
 
  called separately, if needed.
2130
 
 
2131
 
  <base_dir_name> will thus be a container for the set of containers
2132
 
  mentioned in make_deep_trees().
2133
 
  """
2134
 
  j = os.path.join
2135
 
  base = j(sbox.wc_dir, base_dir_name)
2136
 
  make_deep_trees(base)
2137
 
  main.run_svn(None, 'add', base)
2138
 
 
2139
 
 
2140
 
Item = wc.StateItem
2141
 
 
2142
 
# initial deep trees state
2143
 
deep_trees_virginal_state = wc.State('', {
2144
 
  'F'               : Item(),
2145
 
  'F/alpha'         : Item("This is the file 'alpha'.\n"),
2146
 
  'D'               : Item(),
2147
 
  'D/D1'            : Item(),
2148
 
  'DF'              : Item(),
2149
 
  'DF/D1'           : Item(),
2150
 
  'DF/D1/beta'      : Item("This is the file 'beta'.\n"),
2151
 
  'DD'              : Item(),
2152
 
  'DD/D1'           : Item(),
2153
 
  'DD/D1/D2'        : Item(),
2154
 
  'DDF'             : Item(),
2155
 
  'DDF/D1'          : Item(),
2156
 
  'DDF/D1/D2'       : Item(),
2157
 
  'DDF/D1/D2/gamma' : Item("This is the file 'gamma'.\n"),
2158
 
  'DDD'             : Item(),
2159
 
  'DDD/D1'          : Item(),
2160
 
  'DDD/D1/D2'       : Item(),
2161
 
  'DDD/D1/D2/D3'    : Item(),
2162
 
  })
2163
 
 
2164
 
 
2165
 
# Many actions on deep trees and their resulting states...
2166
 
 
2167
 
def deep_trees_leaf_edit(base):
2168
 
  """Helper function for deep trees test cases. Append text to files,
2169
 
  create new files in empty directories, and change leaf node properties."""
2170
 
  j = os.path.join
2171
 
  F   = j(base, 'F', 'alpha')
2172
 
  DF  = j(base, 'DF', 'D1', 'beta')
2173
 
  DDF = j(base, 'DDF', 'D1', 'D2', 'gamma')
2174
 
  main.file_append(F, "More text for file alpha.\n")
2175
 
  main.file_append(DF, "More text for file beta.\n")
2176
 
  main.file_append(DDF, "More text for file gamma.\n")
2177
 
  run_and_verify_svn(None, verify.AnyOutput, [],
2178
 
                     'propset', 'prop1', '1', F, DF, DDF)
2179
 
 
2180
 
  D   = j(base, 'D', 'D1')
2181
 
  DD  = j(base, 'DD', 'D1', 'D2')
2182
 
  DDD = j(base, 'DDD', 'D1', 'D2', 'D3')
2183
 
  run_and_verify_svn(None, verify.AnyOutput, [],
2184
 
                     'propset', 'prop1', '1', D, DD, DDD)
2185
 
  D   = j(base, 'D', 'D1', 'delta')
2186
 
  DD  = j(base, 'DD', 'D1', 'D2', 'epsilon')
2187
 
  DDD = j(base, 'DDD', 'D1', 'D2', 'D3', 'zeta')
2188
 
  main.file_append(D, "This is the file 'delta'.\n")
2189
 
  main.file_append(DD, "This is the file 'epsilon'.\n")
2190
 
  main.file_append(DDD, "This is the file 'zeta'.\n")
2191
 
  run_and_verify_svn(None, verify.AnyOutput, [],
2192
 
                     'add', D, DD, DDD)
2193
 
 
2194
 
# deep trees state after a call to deep_trees_leaf_edit
2195
 
deep_trees_after_leaf_edit = wc.State('', {
2196
 
  'F'                 : Item(),
2197
 
  'F/alpha'           : Item("This is the file 'alpha'.\nMore text for file alpha.\n"),
2198
 
  'D'                 : Item(),
2199
 
  'D/D1'              : Item(),
2200
 
  'D/D1/delta'        : Item("This is the file 'delta'.\n"),
2201
 
  'DF'                : Item(),
2202
 
  'DF/D1'             : Item(),
2203
 
  'DF/D1/beta'        : Item("This is the file 'beta'.\nMore text for file beta.\n"),
2204
 
  'DD'                : Item(),
2205
 
  'DD/D1'             : Item(),
2206
 
  'DD/D1/D2'          : Item(),
2207
 
  'DD/D1/D2/epsilon'  : Item("This is the file 'epsilon'.\n"),
2208
 
  'DDF'               : Item(),
2209
 
  'DDF/D1'            : Item(),
2210
 
  'DDF/D1/D2'         : Item(),
2211
 
  'DDF/D1/D2/gamma'   : Item("This is the file 'gamma'.\nMore text for file gamma.\n"),
2212
 
  'DDD'               : Item(),
2213
 
  'DDD/D1'            : Item(),
2214
 
  'DDD/D1/D2'         : Item(),
2215
 
  'DDD/D1/D2/D3'      : Item(),
2216
 
  'DDD/D1/D2/D3/zeta' : Item("This is the file 'zeta'.\n"),
2217
 
  })
2218
 
 
2219
 
 
2220
 
def deep_trees_leaf_del(base):
2221
 
  """Helper function for deep trees test cases. Delete files and empty
2222
 
  dirs."""
2223
 
  j = os.path.join
2224
 
  F   = j(base, 'F', 'alpha')
2225
 
  D   = j(base, 'D', 'D1')
2226
 
  DF  = j(base, 'DF', 'D1', 'beta')
2227
 
  DD  = j(base, 'DD', 'D1', 'D2')
2228
 
  DDF = j(base, 'DDF', 'D1', 'D2', 'gamma')
2229
 
  DDD = j(base, 'DDD', 'D1', 'D2', 'D3')
2230
 
  main.run_svn(None, 'rm', F, D, DF, DD, DDF, DDD)
2231
 
 
2232
 
# deep trees state after a call to deep_trees_leaf_del
2233
 
deep_trees_after_leaf_del = wc.State('', {
2234
 
  'F'               : Item(),
2235
 
  'D'               : Item(),
2236
 
  'DF'              : Item(),
2237
 
  'DF/D1'           : Item(),
2238
 
  'DD'              : Item(),
2239
 
  'DD/D1'           : Item(),
2240
 
  'DDF'             : Item(),
2241
 
  'DDF/D1'          : Item(),
2242
 
  'DDF/D1/D2'       : Item(),
2243
 
  'DDD'             : Item(),
2244
 
  'DDD/D1'          : Item(),
2245
 
  'DDD/D1/D2'       : Item(),
2246
 
  })
2247
 
 
2248
 
# deep trees state after a call to deep_trees_leaf_del with no commit
2249
 
def deep_trees_after_leaf_del_no_ci(wc_dir):
2250
 
  if svntest.main.wc_is_singledb(wc_dir):
2251
 
    return deep_trees_after_leaf_del
2252
 
  else:
2253
 
    return deep_trees_empty_dirs
2254
 
 
2255
 
 
2256
 
def deep_trees_tree_del(base):
2257
 
  """Helper function for deep trees test cases.  Delete top-level dirs."""
2258
 
  j = os.path.join
2259
 
  F   = j(base, 'F', 'alpha')
2260
 
  D   = j(base, 'D', 'D1')
2261
 
  DF  = j(base, 'DF', 'D1')
2262
 
  DD  = j(base, 'DD', 'D1')
2263
 
  DDF = j(base, 'DDF', 'D1')
2264
 
  DDD = j(base, 'DDD', 'D1')
2265
 
  main.run_svn(None, 'rm', F, D, DF, DD, DDF, DDD)
2266
 
 
2267
 
def deep_trees_rmtree(base):
2268
 
  """Helper function for deep trees test cases.  Delete top-level dirs
2269
 
     with rmtree instead of svn del."""
2270
 
  j = os.path.join
2271
 
  F   = j(base, 'F', 'alpha')
2272
 
  D   = j(base, 'D', 'D1')
2273
 
  DF  = j(base, 'DF', 'D1')
2274
 
  DD  = j(base, 'DD', 'D1')
2275
 
  DDF = j(base, 'DDF', 'D1')
2276
 
  DDD = j(base, 'DDD', 'D1')
2277
 
  os.unlink(F)
2278
 
  main.safe_rmtree(D)
2279
 
  main.safe_rmtree(DF)
2280
 
  main.safe_rmtree(DD)
2281
 
  main.safe_rmtree(DDF)
2282
 
  main.safe_rmtree(DDD)
2283
 
 
2284
 
# deep trees state after a call to deep_trees_tree_del
2285
 
deep_trees_after_tree_del = wc.State('', {
2286
 
  'F'                 : Item(),
2287
 
  'D'                 : Item(),
2288
 
  'DF'                : Item(),
2289
 
  'DD'                : Item(),
2290
 
  'DDF'               : Item(),
2291
 
  'DDD'               : Item(),
2292
 
  })
2293
 
 
2294
 
# deep trees state without any files
2295
 
deep_trees_empty_dirs = wc.State('', {
2296
 
  'F'               : Item(),
2297
 
  'D'               : Item(),
2298
 
  'D/D1'            : Item(),
2299
 
  'DF'              : Item(),
2300
 
  'DF/D1'           : Item(),
2301
 
  'DD'              : Item(),
2302
 
  'DD/D1'           : Item(),
2303
 
  'DD/D1/D2'        : Item(),
2304
 
  'DDF'             : Item(),
2305
 
  'DDF/D1'          : Item(),
2306
 
  'DDF/D1/D2'       : Item(),
2307
 
  'DDD'             : Item(),
2308
 
  'DDD/D1'          : Item(),
2309
 
  'DDD/D1/D2'       : Item(),
2310
 
  'DDD/D1/D2/D3'    : Item(),
2311
 
  })
2312
 
 
2313
 
# deep trees state after a call to deep_trees_tree_del with no commit
2314
 
def deep_trees_after_tree_del_no_ci(wc_dir):
2315
 
  if svntest.main.wc_is_singledb(wc_dir):
2316
 
    return deep_trees_after_tree_del
2317
 
  else:
2318
 
    return deep_trees_empty_dirs
2319
 
 
2320
 
def deep_trees_tree_del_repos(base):
2321
 
  """Helper function for deep trees test cases.  Delete top-level dirs,
2322
 
  directly in the repository."""
2323
 
  j = '/'.join
2324
 
  F   = j([base, 'F', 'alpha'])
2325
 
  D   = j([base, 'D', 'D1'])
2326
 
  DF  = j([base, 'DF', 'D1'])
2327
 
  DD  = j([base, 'DD', 'D1'])
2328
 
  DDF = j([base, 'DDF', 'D1'])
2329
 
  DDD = j([base, 'DDD', 'D1'])
2330
 
  main.run_svn(None, 'mkdir', '-m', '', F, D, DF, DD, DDF, DDD)
2331
 
 
2332
 
# Expected merge/update/switch output.
2333
 
 
2334
 
deep_trees_conflict_output = wc.State('', {
2335
 
  'F/alpha'           : Item(status='  ', treeconflict='C'),
2336
 
  'D/D1'              : Item(status='  ', treeconflict='C'),
2337
 
  'DF/D1'             : Item(status='  ', treeconflict='C'),
2338
 
  'DD/D1'             : Item(status='  ', treeconflict='C'),
2339
 
  'DDF/D1'            : Item(status='  ', treeconflict='C'),
2340
 
  'DDD/D1'            : Item(status='  ', treeconflict='C'),
2341
 
  })
2342
 
 
2343
 
deep_trees_conflict_output_skipped = wc.State('', {
2344
 
  'D/D1'              : Item(verb='Skipped'),
2345
 
  'F/alpha'           : Item(verb='Skipped'),
2346
 
  'DD/D1'             : Item(verb='Skipped'),
2347
 
  'DF/D1'             : Item(verb='Skipped'),
2348
 
  'DDD/D1'            : Item(verb='Skipped'),
2349
 
  'DDF/D1'            : Item(verb='Skipped'),
2350
 
  })
2351
 
 
2352
 
# Expected status output after merge/update/switch.
2353
 
 
2354
 
deep_trees_status_local_tree_del = wc.State('', {
2355
 
  ''                  : Item(status='  ', wc_rev=3),
2356
 
  'D'                 : Item(status='  ', wc_rev=3),
2357
 
  'D/D1'              : Item(status='D ', wc_rev=2, treeconflict='C'),
2358
 
  'DD'                : Item(status='  ', wc_rev=3),
2359
 
  'DD/D1'             : Item(status='D ', wc_rev=2, treeconflict='C'),
2360
 
  'DD/D1/D2'          : Item(status='D ', wc_rev=2),
2361
 
  'DDD'               : Item(status='  ', wc_rev=3),
2362
 
  'DDD/D1'            : Item(status='D ', wc_rev=2, treeconflict='C'),
2363
 
  'DDD/D1/D2'         : Item(status='D ', wc_rev=2),
2364
 
  'DDD/D1/D2/D3'      : Item(status='D ', wc_rev=2),
2365
 
  'DDF'               : Item(status='  ', wc_rev=3),
2366
 
  'DDF/D1'            : Item(status='D ', wc_rev=2, treeconflict='C'),
2367
 
  'DDF/D1/D2'         : Item(status='D ', wc_rev=2),
2368
 
  'DDF/D1/D2/gamma'   : Item(status='D ', wc_rev=2),
2369
 
  'DF'                : Item(status='  ', wc_rev=3),
2370
 
  'DF/D1'             : Item(status='D ', wc_rev=2, treeconflict='C'),
2371
 
  'DF/D1/beta'        : Item(status='D ', wc_rev=2),
2372
 
  'F'                 : Item(status='  ', wc_rev=3),
2373
 
  'F/alpha'           : Item(status='D ', wc_rev=2, treeconflict='C'),
2374
 
  })
2375
 
 
2376
 
deep_trees_status_local_leaf_edit = wc.State('', {
2377
 
  ''                  : Item(status='  ', wc_rev=3),
2378
 
  'D'                 : Item(status='  ', wc_rev=3),
2379
 
  'D/D1'              : Item(status=' M', wc_rev=2, treeconflict='C'),
2380
 
  'D/D1/delta'        : Item(status='A ', wc_rev=0),
2381
 
  'DD'                : Item(status='  ', wc_rev=3),
2382
 
  'DD/D1'             : Item(status='  ', wc_rev=2, treeconflict='C'),
2383
 
  'DD/D1/D2'          : Item(status=' M', wc_rev=2),
2384
 
  'DD/D1/D2/epsilon'  : Item(status='A ', wc_rev=0),
2385
 
  'DDD'               : Item(status='  ', wc_rev=3),
2386
 
  'DDD/D1'            : Item(status='  ', wc_rev=2, treeconflict='C'),
2387
 
  'DDD/D1/D2'         : Item(status='  ', wc_rev=2),
2388
 
  'DDD/D1/D2/D3'      : Item(status=' M', wc_rev=2),
2389
 
  'DDD/D1/D2/D3/zeta' : Item(status='A ', wc_rev=0),
2390
 
  'DDF'               : Item(status='  ', wc_rev=3),
2391
 
  'DDF/D1'            : Item(status='  ', wc_rev=2, treeconflict='C'),
2392
 
  'DDF/D1/D2'         : Item(status='  ', wc_rev=2),
2393
 
  'DDF/D1/D2/gamma'   : Item(status='MM', wc_rev=2),
2394
 
  'DF'                : Item(status='  ', wc_rev=3),
2395
 
  'DF/D1'             : Item(status='  ', wc_rev=2, treeconflict='C'),
2396
 
  'DF/D1/beta'        : Item(status='MM', wc_rev=2),
2397
 
  'F'                 : Item(status='  ', wc_rev=3),
2398
 
  'F/alpha'           : Item(status='MM', wc_rev=2, treeconflict='C'),
2399
 
  })
2400
 
 
2401
 
 
2402
 
class DeepTreesTestCase:
2403
 
  """Describes one tree-conflicts test case.
2404
 
  See deep_trees_run_tests_scheme_for_update(), ..._switch(), ..._merge().
2405
 
 
2406
 
  The name field is the subdirectory name in which the test should be run.
2407
 
 
2408
 
  The local_action and incoming_action are the functions to run
2409
 
  to construct the local changes and incoming changes, respectively.
2410
 
  See deep_trees_leaf_edit, deep_trees_tree_del, etc.
2411
 
 
2412
 
  The expected_* and error_re_string arguments are described in functions
2413
 
  run_and_verify_[update|switch|merge]
2414
 
  except expected_info, which is a dict that has path keys with values
2415
 
  that are dicts as passed to run_and_verify_info():
2416
 
    expected_info = {
2417
 
      'F/alpha' : {
2418
 
        'Revision' : '3',
2419
 
        'Tree conflict' :
2420
 
          '^local delete, incoming edit upon update'
2421
 
          + ' Source  left: .file.*/F/alpha@2'
2422
 
          + ' Source right: .file.*/F/alpha@3$',
2423
 
      },
2424
 
      'DF/D1' : {
2425
 
        'Tree conflict' :
2426
 
          '^local delete, incoming edit upon update'
2427
 
          + ' Source  left: .dir.*/DF/D1@2'
2428
 
          + ' Source right: .dir.*/DF/D1@3$',
2429
 
      },
2430
 
      ...
2431
 
    }
2432
 
 
2433
 
  Note: expected_skip is only used in merge, i.e. using
2434
 
  deep_trees_run_tests_scheme_for_merge.
2435
 
  """
2436
 
 
2437
 
  def __init__(self, name, local_action, incoming_action,
2438
 
                expected_output = None, expected_disk = None,
2439
 
                expected_status = None, expected_skip = None,
2440
 
                error_re_string = None,
2441
 
                commit_block_string = ".*remains in conflict.*",
2442
 
                expected_info = None):
2443
 
    self.name = name
2444
 
    self.local_action = local_action
2445
 
    self.incoming_action = incoming_action
2446
 
    self.expected_output = expected_output
2447
 
    self.expected_disk = expected_disk
2448
 
    self.expected_status = expected_status
2449
 
    self.expected_skip = expected_skip
2450
 
    self.error_re_string = error_re_string
2451
 
    self.commit_block_string = commit_block_string
2452
 
    self.expected_info = expected_info
2453
 
 
2454
 
 
2455
 
 
2456
 
def deep_trees_run_tests_scheme_for_update(sbox, greater_scheme):
2457
 
  """
2458
 
  Runs a given list of tests for conflicts occuring at an update operation.
2459
 
 
2460
 
  This function wants to save time and perform a number of different
2461
 
  test cases using just a single repository and performing just one commit
2462
 
  for all test cases instead of one for each test case.
2463
 
 
2464
 
   1) Each test case is initialized in a separate subdir. Each subdir
2465
 
      again contains one set of "deep_trees", being separate container
2466
 
      dirs for different depths of trees (F, D, DF, DD, DDF, DDD).
2467
 
 
2468
 
   2) A commit is performed across all test cases and depths.
2469
 
      (our initial state, -r2)
2470
 
 
2471
 
   3) In each test case subdir (e.g. "local_tree_del_incoming_leaf_edit"),
2472
 
      its *incoming* action is performed (e.g. "deep_trees_leaf_edit"), in
2473
 
      each of the different depth trees (F, D, DF, ... DDD).
2474
 
 
2475
 
   4) A commit is performed across all test cases and depths:
2476
 
      our "incoming" state is "stored away in the repository for now",
2477
 
      -r3.
2478
 
 
2479
 
   5) All test case dirs and contained deep_trees are time-warped
2480
 
      (updated) back to -r2, the initial state containing deep_trees.
2481
 
 
2482
 
   6) In each test case subdir (e.g. "local_tree_del_incoming_leaf_edit"),
2483
 
      its *local* action is performed (e.g. "deep_trees_leaf_del"), in
2484
 
      each of the different depth trees (F, D, DF, ... DDD).
2485
 
 
2486
 
   7) An update to -r3 is performed across all test cases and depths.
2487
 
      This causes tree-conflicts between the "local" state in the working
2488
 
      copy and the "incoming" state from the repository, -r3.
2489
 
 
2490
 
   8) A commit is performed in each separate container, to verify
2491
 
      that each tree-conflict indeed blocks a commit.
2492
 
 
2493
 
  The sbox parameter is just the sbox passed to a test function. No need
2494
 
  to call sbox.build(), since it is called (once) within this function.
2495
 
 
2496
 
  The "table" greater_scheme models all of the different test cases
2497
 
  that should be run using a single repository.
2498
 
 
2499
 
  greater_scheme is a list of DeepTreesTestCase items, which define complete
2500
 
  test setups, so that they can be performed as described above.
2501
 
  """
2502
 
 
2503
 
  j = os.path.join
2504
 
 
2505
 
  if not sbox.is_built():
2506
 
    sbox.build()
2507
 
  wc_dir = sbox.wc_dir
2508
 
 
2509
 
 
2510
 
  # 1) create directories
2511
 
 
2512
 
  for test_case in greater_scheme:
2513
 
    try:
2514
 
      add_deep_trees(sbox, test_case.name)
2515
 
    except:
2516
 
      print("ERROR IN: Tests scheme for update: "
2517
 
          + "while setting up deep trees in '%s'" % test_case.name)
2518
 
      raise
2519
 
 
2520
 
 
2521
 
  # 2) commit initial state
2522
 
 
2523
 
  main.run_svn(None, 'commit', '-m', 'initial state', wc_dir)
2524
 
 
2525
 
 
2526
 
  # 3) apply incoming changes
2527
 
 
2528
 
  for test_case in greater_scheme:
2529
 
    try:
2530
 
      test_case.incoming_action(j(sbox.wc_dir, test_case.name))
2531
 
    except:
2532
 
      print("ERROR IN: Tests scheme for update: "
2533
 
          + "while performing incoming action in '%s'" % test_case.name)
2534
 
      raise
2535
 
 
2536
 
 
2537
 
  # 4) commit incoming changes
2538
 
 
2539
 
  main.run_svn(None, 'commit', '-m', 'incoming changes', wc_dir)
2540
 
 
2541
 
 
2542
 
  # 5) time-warp back to -r2
2543
 
 
2544
 
  main.run_svn(None, 'update', '-r2', wc_dir)
2545
 
 
2546
 
 
2547
 
  # 6) apply local changes
2548
 
 
2549
 
  for test_case in greater_scheme:
2550
 
    try:
2551
 
      test_case.local_action(j(wc_dir, test_case.name))
2552
 
    except:
2553
 
      print("ERROR IN: Tests scheme for update: "
2554
 
          + "while performing local action in '%s'" % test_case.name)
2555
 
      raise
2556
 
 
2557
 
 
2558
 
  # 7) update to -r3, conflicting with incoming changes.
2559
 
  #    A lot of different things are expected.
2560
 
  #    Do separate update operations for each test case.
2561
 
 
2562
 
  for test_case in greater_scheme:
2563
 
    try:
2564
 
      base = j(wc_dir, test_case.name)
2565
 
 
2566
 
      x_out = test_case.expected_output
2567
 
      if x_out != None:
2568
 
        x_out = x_out.copy()
2569
 
        x_out.wc_dir = base
2570
 
 
2571
 
      x_disk = test_case.expected_disk
2572
 
 
2573
 
      x_status = test_case.expected_status
2574
 
      if x_status != None:
2575
 
        x_status.copy()
2576
 
        x_status.wc_dir = base
2577
 
 
2578
 
      run_and_verify_update(base, x_out, x_disk, None,
2579
 
                            error_re_string = test_case.error_re_string)
2580
 
      if x_status:
2581
 
        run_and_verify_unquiet_status(base, x_status)
2582
 
 
2583
 
      x_info = test_case.expected_info or {}
2584
 
      for path in x_info:
2585
 
        run_and_verify_info([x_info[path]], j(base, path))
2586
 
 
2587
 
    except:
2588
 
      print("ERROR IN: Tests scheme for update: "
2589
 
          + "while verifying in '%s'" % test_case.name)
2590
 
      raise
2591
 
 
2592
 
 
2593
 
  # 8) Verify that commit fails.
2594
 
 
2595
 
  for test_case in greater_scheme:
2596
 
    try:
2597
 
      base = j(wc_dir, test_case.name)
2598
 
 
2599
 
      x_status = test_case.expected_status
2600
 
      if x_status != None:
2601
 
        x_status.copy()
2602
 
        x_status.wc_dir = base
2603
 
 
2604
 
      run_and_verify_commit(base, None, x_status,
2605
 
                            test_case.commit_block_string,
2606
 
                            base)
2607
 
    except:
2608
 
      print("ERROR IN: Tests scheme for update: "
2609
 
          + "while checking commit-blocking in '%s'" % test_case.name)
2610
 
      raise
2611
 
 
2612
 
 
2613
 
 
2614
 
def deep_trees_skipping_on_update(sbox, test_case, skip_paths,
2615
 
                                  chdir_skip_paths):
2616
 
  """
2617
 
  Create tree conflicts, then update again, expecting the existing tree
2618
 
  conflicts to be skipped.
2619
 
  SKIP_PATHS is a list of paths, relative to the "base dir", for which
2620
 
  "update" on the "base dir" should report as skipped.
2621
 
  CHDIR_SKIP_PATHS is a list of (target-path, skipped-path) pairs for which
2622
 
  an update of "target-path" (relative to the "base dir") should result in
2623
 
  "skipped-path" (relative to "target-path") being reported as skipped.
2624
 
  """
2625
 
 
2626
 
  """FURTHER_ACTION is a function that will make a further modification to
2627
 
  each target, this being the modification that we expect to be skipped. The
2628
 
  function takes the "base dir" (the WC path to the test case directory) as
2629
 
  its only argument."""
2630
 
  further_action = deep_trees_tree_del_repos
2631
 
 
2632
 
  j = os.path.join
2633
 
  wc_dir = sbox.wc_dir
2634
 
  base = j(wc_dir, test_case.name)
2635
 
 
2636
 
  # Initialize: generate conflicts. (We do not check anything here.)
2637
 
  setup_case = DeepTreesTestCase(test_case.name,
2638
 
                                 test_case.local_action,
2639
 
                                 test_case.incoming_action,
2640
 
                                 None,
2641
 
                                 None,
2642
 
                                 None)
2643
 
  deep_trees_run_tests_scheme_for_update(sbox, [setup_case])
2644
 
 
2645
 
  # Make a further change to each target in the repository so there is a new
2646
 
  # revision to update to. (This is r4.)
2647
 
  further_action(sbox.repo_url + '/' + test_case.name)
2648
 
 
2649
 
  # Update whole working copy, expecting the nodes still in conflict to be
2650
 
  # skipped.
2651
 
 
2652
 
  x_out = test_case.expected_output
2653
 
  if x_out != None:
2654
 
    x_out = x_out.copy()
2655
 
    x_out.wc_dir = base
2656
 
 
2657
 
  x_disk = test_case.expected_disk
2658
 
 
2659
 
  x_status = test_case.expected_status
2660
 
  if x_status != None:
2661
 
    x_status = x_status.copy()
2662
 
    x_status.wc_dir = base
2663
 
    # Account for nodes that were updated by further_action
2664
 
    x_status.tweak('', 'D', 'F', 'DD', 'DF', 'DDD', 'DDF', wc_rev=4)
2665
 
 
2666
 
  run_and_verify_update(base, x_out, x_disk, None,
2667
 
                        error_re_string = test_case.error_re_string)
2668
 
 
2669
 
  run_and_verify_unquiet_status(base, x_status)
2670
 
 
2671
 
  # Try to update each in-conflict subtree. Expect a 'Skipped' output for
2672
 
  # each, and the WC status to be unchanged.
2673
 
  for path in skip_paths:
2674
 
    run_and_verify_update(j(base, path),
2675
 
                          wc.State(base, {path : Item(verb='Skipped')}),
2676
 
                          None, None)
2677
 
 
2678
 
  run_and_verify_unquiet_status(base, x_status)
2679
 
 
2680
 
  # Try to update each in-conflict subtree. Expect a 'Skipped' output for
2681
 
  # each, and the WC status to be unchanged.
2682
 
  # This time, cd to the subdir before updating it.
2683
 
  was_cwd = os.getcwd()
2684
 
  for path, skipped in chdir_skip_paths:
2685
 
    if isinstance(skipped, list):
2686
 
      expected_skip = {}
2687
 
      for p in skipped:
2688
 
        expected_skip[p] = Item(verb='Skipped')
2689
 
    else:
2690
 
      expected_skip = {skipped : Item(verb='Skipped')}
2691
 
    p = j(base, path)
2692
 
    run_and_verify_update(p,
2693
 
                          wc.State(p, expected_skip),
2694
 
                          None, None)
2695
 
  os.chdir(was_cwd)
2696
 
 
2697
 
  run_and_verify_unquiet_status(base, x_status)
2698
 
 
2699
 
  # Verify that commit still fails.
2700
 
  for path, skipped in chdir_skip_paths:
2701
 
 
2702
 
    run_and_verify_commit(j(base, path), None, None,
2703
 
                          test_case.commit_block_string,
2704
 
                          base)
2705
 
 
2706
 
  run_and_verify_unquiet_status(base, x_status)
2707
 
 
2708
 
 
2709
 
def deep_trees_run_tests_scheme_for_switch(sbox, greater_scheme):
2710
 
  """
2711
 
  Runs a given list of tests for conflicts occuring at a switch operation.
2712
 
 
2713
 
  This function wants to save time and perform a number of different
2714
 
  test cases using just a single repository and performing just one commit
2715
 
  for all test cases instead of one for each test case.
2716
 
 
2717
 
   1) Each test case is initialized in a separate subdir. Each subdir
2718
 
      again contains two subdirs: one "local" and one "incoming" for
2719
 
      the switch operation. These contain a set of deep_trees each.
2720
 
 
2721
 
   2) A commit is performed across all test cases and depths.
2722
 
      (our initial state, -r2)
2723
 
 
2724
 
   3) In each test case subdir's incoming subdir, the
2725
 
      incoming actions are performed.
2726
 
 
2727
 
   4) A commit is performed across all test cases and depths. (-r3)
2728
 
 
2729
 
   5) In each test case subdir's local subdir, the local actions are
2730
 
      performed. They remain uncommitted in the working copy.
2731
 
 
2732
 
   6) In each test case subdir's local dir, a switch is performed to its
2733
 
      corresponding incoming dir.
2734
 
      This causes conflicts between the "local" state in the working
2735
 
      copy and the "incoming" state from the incoming subdir (still -r3).
2736
 
 
2737
 
   7) A commit is performed in each separate container, to verify
2738
 
      that each tree-conflict indeed blocks a commit.
2739
 
 
2740
 
  The sbox parameter is just the sbox passed to a test function. No need
2741
 
  to call sbox.build(), since it is called (once) within this function.
2742
 
 
2743
 
  The "table" greater_scheme models all of the different test cases
2744
 
  that should be run using a single repository.
2745
 
 
2746
 
  greater_scheme is a list of DeepTreesTestCase items, which define complete
2747
 
  test setups, so that they can be performed as described above.
2748
 
  """
2749
 
 
2750
 
  j = os.path.join
2751
 
 
2752
 
  if not sbox.is_built():
2753
 
    sbox.build()
2754
 
  wc_dir = sbox.wc_dir
2755
 
 
2756
 
 
2757
 
  # 1) Create directories.
2758
 
 
2759
 
  for test_case in greater_scheme:
2760
 
    try:
2761
 
      base = j(sbox.wc_dir, test_case.name)
2762
 
      os.makedirs(base)
2763
 
      make_deep_trees(j(base, "local"))
2764
 
      make_deep_trees(j(base, "incoming"))
2765
 
      main.run_svn(None, 'add', base)
2766
 
    except:
2767
 
      print("ERROR IN: Tests scheme for switch: "
2768
 
          + "while setting up deep trees in '%s'" % test_case.name)
2769
 
      raise
2770
 
 
2771
 
 
2772
 
  # 2) Commit initial state (-r2).
2773
 
 
2774
 
  main.run_svn(None, 'commit', '-m', 'initial state', wc_dir)
2775
 
 
2776
 
 
2777
 
  # 3) Apply incoming changes
2778
 
 
2779
 
  for test_case in greater_scheme:
2780
 
    try:
2781
 
      test_case.incoming_action(j(sbox.wc_dir, test_case.name, "incoming"))
2782
 
    except:
2783
 
      print("ERROR IN: Tests scheme for switch: "
2784
 
          + "while performing incoming action in '%s'" % test_case.name)
2785
 
      raise
2786
 
 
2787
 
 
2788
 
  # 4) Commit all changes (-r3).
2789
 
 
2790
 
  main.run_svn(None, 'commit', '-m', 'incoming changes', wc_dir)
2791
 
 
2792
 
 
2793
 
  # 5) Apply local changes in their according subdirs.
2794
 
 
2795
 
  for test_case in greater_scheme:
2796
 
    try:
2797
 
      test_case.local_action(j(sbox.wc_dir, test_case.name, "local"))
2798
 
    except:
2799
 
      print("ERROR IN: Tests scheme for switch: "
2800
 
          + "while performing local action in '%s'" % test_case.name)
2801
 
      raise
2802
 
 
2803
 
 
2804
 
  # 6) switch the local dir to the incoming url, conflicting with incoming
2805
 
  #    changes. A lot of different things are expected.
2806
 
  #    Do separate switch operations for each test case.
2807
 
 
2808
 
  for test_case in greater_scheme:
2809
 
    try:
2810
 
      local = j(wc_dir, test_case.name, "local")
2811
 
      incoming = sbox.repo_url + "/" + test_case.name + "/incoming"
2812
 
 
2813
 
      x_out = test_case.expected_output
2814
 
      if x_out != None:
2815
 
        x_out = x_out.copy()
2816
 
        x_out.wc_dir = local
2817
 
 
2818
 
      x_disk = test_case.expected_disk
2819
 
 
2820
 
      x_status = test_case.expected_status
2821
 
      if x_status != None:
2822
 
        x_status.copy()
2823
 
        x_status.wc_dir = local
2824
 
 
2825
 
      run_and_verify_switch(local, local, incoming, x_out, x_disk, None,
2826
 
                            test_case.error_re_string, None, None, None,
2827
 
                            None, False, '--ignore-ancestry')
2828
 
      run_and_verify_unquiet_status(local, x_status)
2829
 
 
2830
 
      x_info = test_case.expected_info or {}
2831
 
      for path in x_info:
2832
 
        run_and_verify_info([x_info[path]], j(local, path))
2833
 
    except:
2834
 
      print("ERROR IN: Tests scheme for switch: "
2835
 
          + "while verifying in '%s'" % test_case.name)
2836
 
      raise
2837
 
 
2838
 
 
2839
 
  # 7) Verify that commit fails.
2840
 
 
2841
 
  for test_case in greater_scheme:
2842
 
    try:
2843
 
      local = j(wc_dir, test_case.name, 'local')
2844
 
 
2845
 
      x_status = test_case.expected_status
2846
 
      if x_status != None:
2847
 
        x_status.copy()
2848
 
        x_status.wc_dir = local
2849
 
 
2850
 
      run_and_verify_commit(local, None, x_status,
2851
 
                            test_case.commit_block_string,
2852
 
                            local)
2853
 
    except:
2854
 
      print("ERROR IN: Tests scheme for switch: "
2855
 
          + "while checking commit-blocking in '%s'" % test_case.name)
2856
 
      raise
2857
 
 
2858
 
 
2859
 
def deep_trees_run_tests_scheme_for_merge(sbox, greater_scheme,
2860
 
                                          do_commit_local_changes,
2861
 
                                          do_commit_conflicts=True,
2862
 
                                          ignore_ancestry=False):
2863
 
  """
2864
 
  Runs a given list of tests for conflicts occuring at a merge operation.
2865
 
 
2866
 
  This function wants to save time and perform a number of different
2867
 
  test cases using just a single repository and performing just one commit
2868
 
  for all test cases instead of one for each test case.
2869
 
 
2870
 
   1) Each test case is initialized in a separate subdir. Each subdir
2871
 
      initially contains another subdir, called "incoming", which
2872
 
      contains a set of deep_trees.
2873
 
 
2874
 
   2) A commit is performed across all test cases and depths.
2875
 
      (a pre-initial state)
2876
 
 
2877
 
   3) In each test case subdir, the "incoming" subdir is copied to "local",
2878
 
      via the `svn copy' command. Each test case's subdir now has two sub-
2879
 
      dirs: "local" and "incoming", initial states for the merge operation.
2880
 
 
2881
 
   4) An update is performed across all test cases and depths, so that the
2882
 
      copies made in 3) are pulled into the wc.
2883
 
 
2884
 
   5) In each test case's "incoming" subdir, the incoming action is
2885
 
      performed.
2886
 
 
2887
 
   6) A commit is performed across all test cases and depths, to commit
2888
 
      the incoming changes.
2889
 
      If do_commit_local_changes is True, this becomes step 7 (swap steps).
2890
 
 
2891
 
   7) In each test case's "local" subdir, the local_action is performed.
2892
 
      If do_commit_local_changes is True, this becomes step 6 (swap steps).
2893
 
      Then, in effect, the local changes are committed as well.
2894
 
 
2895
 
   8) In each test case subdir, the "incoming" subdir is merged into the
2896
 
      "local" subdir.  If ignore_ancestry is True, then the merge is done
2897
 
      with the --ignore-ancestry option, so mergeinfo is neither considered
2898
 
      nor recorded.  This causes conflicts between the "local" state in the
2899
 
      working copy and the "incoming" state from the incoming subdir.
2900
 
 
2901
 
   9) If do_commit_conflicts is True, then a commit is performed in each
2902
 
      separate container, to verify that each tree-conflict indeed blocks
2903
 
      a commit.
2904
 
 
2905
 
  The sbox parameter is just the sbox passed to a test function. No need
2906
 
  to call sbox.build(), since it is called (once) within this function.
2907
 
 
2908
 
  The "table" greater_scheme models all of the different test cases
2909
 
  that should be run using a single repository.
2910
 
 
2911
 
  greater_scheme is a list of DeepTreesTestCase items, which define complete
2912
 
  test setups, so that they can be performed as described above.
2913
 
  """
2914
 
 
2915
 
  j = os.path.join
2916
 
 
2917
 
  if not sbox.is_built():
2918
 
    sbox.build()
2919
 
  wc_dir = sbox.wc_dir
2920
 
 
2921
 
  # 1) Create directories.
2922
 
  for test_case in greater_scheme:
2923
 
    try:
2924
 
      base = j(sbox.wc_dir, test_case.name)
2925
 
      os.makedirs(base)
2926
 
      make_deep_trees(j(base, "incoming"))
2927
 
      main.run_svn(None, 'add', base)
2928
 
    except:
2929
 
      print("ERROR IN: Tests scheme for merge: "
2930
 
          + "while setting up deep trees in '%s'" % test_case.name)
2931
 
      raise
2932
 
 
2933
 
 
2934
 
  # 2) Commit pre-initial state (-r2).
2935
 
 
2936
 
  main.run_svn(None, 'commit', '-m', 'pre-initial state', wc_dir)
2937
 
 
2938
 
 
2939
 
  # 3) Copy "incoming" to "local".
2940
 
 
2941
 
  for test_case in greater_scheme:
2942
 
    try:
2943
 
      base_url = sbox.repo_url + "/" + test_case.name
2944
 
      incoming_url = base_url + "/incoming"
2945
 
      local_url = base_url + "/local"
2946
 
      main.run_svn(None, 'cp', incoming_url, local_url, '-m',
2947
 
                   'copy incoming to local')
2948
 
    except:
2949
 
      print("ERROR IN: Tests scheme for merge: "
2950
 
          + "while copying deep trees in '%s'" % test_case.name)
2951
 
      raise
2952
 
 
2953
 
  # 4) Update to load all of the "/local" subdirs into the working copies.
2954
 
 
2955
 
  try:
2956
 
    main.run_svn(None, 'up', sbox.wc_dir)
2957
 
  except:
2958
 
    print("ERROR IN: Tests scheme for merge: "
2959
 
          + "while updating local subdirs")
2960
 
    raise
2961
 
 
2962
 
 
2963
 
  # 5) Perform incoming actions
2964
 
 
2965
 
  for test_case in greater_scheme:
2966
 
    try:
2967
 
      test_case.incoming_action(j(sbox.wc_dir, test_case.name, "incoming"))
2968
 
    except:
2969
 
      print("ERROR IN: Tests scheme for merge: "
2970
 
          + "while performing incoming action in '%s'" % test_case.name)
2971
 
      raise
2972
 
 
2973
 
 
2974
 
  # 6) or 7) Commit all incoming actions
2975
 
 
2976
 
  if not do_commit_local_changes:
2977
 
    try:
2978
 
      main.run_svn(None, 'ci', '-m', 'Committing incoming actions',
2979
 
                   sbox.wc_dir)
2980
 
    except:
2981
 
      print("ERROR IN: Tests scheme for merge: "
2982
 
          + "while committing incoming actions")
2983
 
      raise
2984
 
 
2985
 
 
2986
 
  # 7) or 6) Perform all local actions.
2987
 
 
2988
 
  for test_case in greater_scheme:
2989
 
    try:
2990
 
      test_case.local_action(j(sbox.wc_dir, test_case.name, "local"))
2991
 
    except:
2992
 
      print("ERROR IN: Tests scheme for merge: "
2993
 
          + "while performing local action in '%s'" % test_case.name)
2994
 
      raise
2995
 
 
2996
 
 
2997
 
  # 6) or 7) Commit all incoming actions
2998
 
 
2999
 
  if do_commit_local_changes:
3000
 
    try:
3001
 
      main.run_svn(None, 'ci', '-m', 'Committing incoming and local actions',
3002
 
                   sbox.wc_dir)
3003
 
    except:
3004
 
      print("ERROR IN: Tests scheme for merge: "
3005
 
          + "while committing incoming and local actions")
3006
 
      raise
3007
 
 
3008
 
 
3009
 
  # 8) Merge all "incoming" subdirs to their respective "local" subdirs.
3010
 
  #    This creates conflicts between the local changes in the "local" wc
3011
 
  #    subdirs and the incoming states committed in the "incoming" subdirs.
3012
 
 
3013
 
  for test_case in greater_scheme:
3014
 
    try:
3015
 
      local = j(sbox.wc_dir, test_case.name, "local")
3016
 
      incoming = sbox.repo_url + "/" + test_case.name + "/incoming"
3017
 
 
3018
 
      x_out = test_case.expected_output
3019
 
      if x_out != None:
3020
 
        x_out = x_out.copy()
3021
 
        x_out.wc_dir = local
3022
 
 
3023
 
      x_disk = test_case.expected_disk
3024
 
 
3025
 
      x_status = test_case.expected_status
3026
 
      if x_status != None:
3027
 
        x_status.copy()
3028
 
        x_status.wc_dir = local
3029
 
 
3030
 
      x_skip = test_case.expected_skip
3031
 
      if x_skip != None:
3032
 
        x_skip.copy()
3033
 
        x_skip.wc_dir = local
3034
 
 
3035
 
      varargs = (local,)
3036
 
      if ignore_ancestry:
3037
 
        varargs = varargs + ('--ignore-ancestry',)
3038
 
 
3039
 
      run_and_verify_merge(local, None, None, incoming, None,
3040
 
                           x_out, None, None, x_disk, None, x_skip,
3041
 
                           test_case.error_re_string,
3042
 
                           None, None, None, None,
3043
 
                           False, False, *varargs)
3044
 
      run_and_verify_unquiet_status(local, x_status)
3045
 
    except:
3046
 
      print("ERROR IN: Tests scheme for merge: "
3047
 
          + "while verifying in '%s'" % test_case.name)
3048
 
      raise
3049
 
 
3050
 
 
3051
 
  # 9) Verify that commit fails.
3052
 
 
3053
 
  if do_commit_conflicts:
3054
 
    for test_case in greater_scheme:
3055
 
      try:
3056
 
        local = j(wc_dir, test_case.name, 'local')
3057
 
 
3058
 
        x_status = test_case.expected_status
3059
 
        if x_status != None:
3060
 
          x_status.copy()
3061
 
          x_status.wc_dir = local
3062
 
 
3063
 
        run_and_verify_commit(local, None, x_status,
3064
 
                              test_case.commit_block_string,
3065
 
                              local)
3066
 
      except:
3067
 
        print("ERROR IN: Tests scheme for merge: "
3068
 
            + "while checking commit-blocking in '%s'" % test_case.name)
3069
 
        raise
3070
 
 
3071