1#!/usr/bin/env python
2#
3#  shelf_tests.py:  testing shelving
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.apache.org for more information.
7#
8# ====================================================================
9#    Licensed to the Apache Software Foundation (ASF) under one
10#    or more contributor license agreements.  See the NOTICE file
11#    distributed with this work for additional information
12#    regarding copyright ownership.  The ASF licenses this file
13#    to you under the Apache License, Version 2.0 (the
14#    "License"); you may not use this file except in compliance
15#    with the License.  You may obtain a copy of the License at
16#
17#      http://www.apache.org/licenses/LICENSE-2.0
18#
19#    Unless required by applicable law or agreed to in writing,
20#    software distributed under the License is distributed on an
21#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22#    KIND, either express or implied.  See the License for the
23#    specific language governing permissions and limitations
24#    under the License.
25######################################################################
26
27# General modules
28import shutil, stat, re, os, logging
29
30logger = logging.getLogger()
31
32# Our testing module
33import svntest
34from svntest import wc
35from svntest.verify import make_diff_header, make_no_diff_deleted_header, \
36                           make_git_diff_header, make_diff_prop_header, \
37                           make_diff_prop_val, make_diff_prop_deleted, \
38                           make_diff_prop_added, make_diff_prop_modified
39
40# (abbreviation)
41Skip = svntest.testcase.Skip_deco
42SkipUnless = svntest.testcase.SkipUnless_deco
43XFail = svntest.testcase.XFail_deco
44Issues = svntest.testcase.Issues_deco
45Issue = svntest.testcase.Issue_deco
46Wimp = svntest.testcase.Wimp_deco
47Item = wc.StateItem
48
49def shelf2_enabled():
50  v = os.getenv('SVN_EXPERIMENTAL_COMMANDS')
51  return v is not None and v.find('shelf2') >= 0
52
53#----------------------------------------------------------------------
54
55def state_from_status(wc_dir,
56                      v=True, u=True, q=True):
57  opts = ()
58  if v:
59    opts += ('-v',)
60  if u:
61    opts += ('-u',)
62  if q:
63    opts += ('-q',)
64  _, output, _ = svntest.main.run_svn(None, 'status', wc_dir, *opts)
65  return svntest.wc.State.from_status(output, wc_dir)
66
67def get_wc_state(wc_dir):
68  """Return a description of the WC state. Include as much info as shelving
69     should be capable of restoring.
70  """
71  return (state_from_status(wc_dir),
72          svntest.wc.State.from_wc(wc_dir, load_props=True),
73          )
74
75def check_wc_state(wc_dir, expected):
76  """Check a description of the WC state. Include as much info as shelving
77     should be capable of restoring.
78  """
79  expect_st, expect_wc = expected
80  actual_st, actual_wc = get_wc_state(wc_dir)
81
82  # Verify actual status against expected status.
83  try:
84    expect_st.compare_and_display('status', actual_st)
85  except svntest.tree.SVNTreeError:
86    svntest.actions._log_tree_state("EXPECT STATUS TREE:", expect_st.old_tree(),
87                                    wc_dir)
88    svntest.actions._log_tree_state("ACTUAL STATUS TREE:", actual_st.old_tree(),
89                                    wc_dir)
90    raise
91
92  # Verify actual WC against expected WC.
93  try:
94    expect_wc.compare_and_display('status', actual_wc)
95  except svntest.tree.SVNTreeError:
96    svntest.actions._log_tree_state("EXPECT WC TREE:", expect_wc.old_tree(),
97                                    wc_dir)
98    svntest.actions._log_tree_state("ACTUAL WC TREE:", actual_wc.old_tree(),
99                                    wc_dir)
100    raise
101
102def shelve_unshelve_verify(sbox, modifier, cannot_shelve=False):
103  """Round-trip: shelve; verify all changes are reverted;
104     unshelve; verify all changes are restored.
105  """
106
107  wc_dir = sbox.wc_dir
108  virginal_state = get_wc_state(wc_dir)
109
110  # Make some changes to the working copy
111  modifier(sbox)
112
113  # Save the modified state
114  modified_state = get_wc_state(wc_dir)
115
116  if cannot_shelve:
117    svntest.actions.run_and_verify_svn(None, '.* could not be shelved.*',
118                                       'x-shelve', 'foo')
119    return
120
121  # Shelve; check there are no longer any modifications
122  svntest.actions.run_and_verify_svn(None, [],
123                                     'x-shelve', 'foo')
124  check_wc_state(wc_dir, virginal_state)
125
126  # List; ensure the shelf is listed
127  expected_output = svntest.verify.RegexListOutput(
128    [r'foo\s*version \d+.*',
129     r' ',
130    ])
131  svntest.actions.run_and_verify_svn(expected_output, [], 'x-shelves')
132
133  # Unshelve; check the original modifications are here again
134  svntest.actions.run_and_verify_svn(None, [],
135                                     'x-unshelve', 'foo')
136  check_wc_state(wc_dir, modified_state)
137
138#----------------------------------------------------------------------
139
140def shelve_unshelve(sbox, modifier, cannot_shelve=False):
141  """Round-trip: build 'sbox'; apply changes by calling 'modifier(sbox)';
142     shelve and unshelve; verify changes are fully reverted and restored.
143  """
144
145  if not sbox.is_built():
146    sbox.build()
147  was_cwd = os.getcwd()
148  os.chdir(sbox.wc_dir)
149  sbox.wc_dir = ''
150
151  shelve_unshelve_verify(sbox, modifier, cannot_shelve)
152
153  os.chdir(was_cwd)
154
155######################################################################
156# Tests
157#
158#   Each test must return on success or raise on failure.
159
160@SkipUnless(shelf2_enabled)
161def shelve_text_mods(sbox):
162  "shelve text mods"
163
164  def modifier(sbox):
165    sbox.simple_append('A/mu', 'appended mu text')
166
167  shelve_unshelve(sbox, modifier)
168
169#----------------------------------------------------------------------
170
171@SkipUnless(shelf2_enabled)
172def shelve_prop_changes(sbox):
173  "shelve prop changes"
174
175  def modifier(sbox):
176    sbox.simple_propset('p', 'v', 'A')
177    sbox.simple_propset('p', 'v', 'A/mu')
178
179  shelve_unshelve(sbox, modifier)
180
181#----------------------------------------------------------------------
182
183@SkipUnless(shelf2_enabled)
184def shelve_adds(sbox):
185  "shelve adds"
186
187  def modifier(sbox):
188    sbox.simple_add_text('A new file\n', 'A/new')
189    sbox.simple_add_text('A new file\n', 'A/new2')
190    sbox.simple_propset('p', 'v', 'A/new2')
191
192  shelve_unshelve(sbox, modifier)
193
194#----------------------------------------------------------------------
195
196@Issue(4709)
197@SkipUnless(shelf2_enabled)
198def shelve_deletes(sbox):
199  "shelve deletes"
200
201  def modifier(sbox):
202    sbox.simple_rm('A/mu')
203
204  shelve_unshelve(sbox, modifier)
205
206#----------------------------------------------------------------------
207
208@SkipUnless(shelf2_enabled)
209def shelve_replace(sbox):
210  "shelve replace"
211
212  def modifier(sbox):
213    sbox.simple_rm('A/mu')
214    sbox.simple_add_text('Replacement\n', 'A/mu')
215    sbox.simple_propset('p', 'v', 'A/mu')
216
217  shelve_unshelve(sbox, modifier)
218
219#----------------------------------------------------------------------
220
221@SkipUnless(shelf2_enabled)
222def shelve_empty_adds(sbox):
223  "shelve empty adds"
224  sbox.build(empty=True)
225
226  def modifier(sbox):
227    sbox.simple_add_text('', 'empty')
228    sbox.simple_add_text('', 'empty-with-prop')
229    sbox.simple_propset('p', 'v', 'empty-with-prop')
230
231  shelve_unshelve(sbox, modifier)
232
233#----------------------------------------------------------------------
234
235@SkipUnless(shelf2_enabled)
236def shelve_empty_deletes(sbox):
237  "shelve empty deletes"
238  sbox.build(empty=True)
239  sbox.simple_add_text('', 'empty')
240  sbox.simple_add_text('', 'empty-with-prop')
241  sbox.simple_propset('p', 'v', 'empty-with-prop')
242  sbox.simple_commit()
243
244  def modifier(sbox):
245    sbox.simple_rm('empty', 'empty-with-prop')
246
247  shelve_unshelve(sbox, modifier)
248
249#----------------------------------------------------------------------
250
251@SkipUnless(shelf2_enabled)
252def shelve_from_inner_path(sbox):
253  "shelve from inner path"
254
255  def modifier(sbox):
256    sbox.simple_append('A/mu', 'appended mu text')
257
258  sbox.build()
259  was_cwd = os.getcwd()
260  os.chdir(sbox.ospath('A'))
261  sbox.wc_dir = '..'
262
263  shelve_unshelve_verify(sbox, modifier)
264
265  os.chdir(was_cwd)
266
267#----------------------------------------------------------------------
268
269def save_revert_restore(sbox, modifier1, modifier2):
270  "Save 2 checkpoints; revert; restore 1st"
271
272  sbox.build()
273  was_cwd = os.getcwd()
274  os.chdir(sbox.wc_dir)
275  sbox.wc_dir = ''
276  wc_dir = ''
277
278  initial_state = get_wc_state(wc_dir)
279
280  # Make some changes to the working copy
281  modifier1(sbox)
282
283  # Remember the modified state
284  modified_state1 = get_wc_state(wc_dir)
285
286  # Save a checkpoint; check nothing changed
287  svntest.actions.run_and_verify_svn(None, [],
288                                     'x-shelf-save', 'foo')
289  check_wc_state(wc_dir, modified_state1)
290
291  # Modify again; remember the state; save a checkpoint
292  modifier2(sbox)
293  modified_state2 = get_wc_state(wc_dir)
294  svntest.actions.run_and_verify_svn(None, [],
295                                     'x-shelf-save', 'foo')
296  check_wc_state(wc_dir, modified_state2)
297
298  # Revert
299  svntest.actions.run_and_verify_svn(None, [],
300                                     'revert', '-R', '.')
301  check_wc_state(wc_dir, initial_state)
302
303  # Restore; check the original modifications are here again
304  svntest.actions.run_and_verify_svn(None, [],
305                                     'x-unshelve', 'foo', '1')
306  check_wc_state(wc_dir, modified_state1)
307
308  os.chdir(was_cwd)
309
310#----------------------------------------------------------------------
311
312@SkipUnless(shelf2_enabled)
313def checkpoint_basic(sbox):
314  "checkpoint basic"
315
316  def modifier1(sbox):
317    sbox.simple_append('A/mu', 'appended mu text\n')
318
319  def modifier2(sbox):
320    sbox.simple_append('iota', 'appended iota text\n')
321    sbox.simple_append('A/mu', 'appended another line\n')
322
323  save_revert_restore(sbox, modifier1, modifier2)
324
325#----------------------------------------------------------------------
326
327@Issue(3747)
328@SkipUnless(shelf2_enabled)
329def shelve_mergeinfo(sbox):
330  "shelve mergeinfo"
331
332  def modifier(sbox):
333    sbox.simple_propset('svn:mergeinfo', '/trunk/A:1-3,10', 'A')
334    sbox.simple_propset('svn:mergeinfo', '/trunk/A/mu:1-3,10', 'A/mu')
335
336  shelve_unshelve(sbox, modifier)
337
338#----------------------------------------------------------------------
339
340@SkipUnless(shelf2_enabled)
341def unshelve_refuses_if_conflicts(sbox):
342  "unshelve refuses if conflicts"
343
344  def modifier1(sbox):
345    sbox.simple_append('alpha', 'A-mod1\nB\nC\nD\n', truncate=True)
346    sbox.simple_append('beta', 'A-mod1\nB\nC\nD\n', truncate=True)
347
348  def modifier2(sbox):
349    sbox.simple_append('beta', 'A-mod2\nB\nC\nD\n', truncate=True)
350
351  sbox.build(empty=True)
352  was_cwd = os.getcwd()
353  os.chdir(sbox.wc_dir)
354  sbox.wc_dir = ''
355  wc_dir = ''
356
357  sbox.simple_add_text('A\nB\nC\nD\n', 'alpha')
358  sbox.simple_add_text('A\nB\nC\nD\n', 'beta')
359  sbox.simple_commit()
360  initial_state = get_wc_state(wc_dir)
361
362  # Make initial mods; remember this modified state
363  modifier1(sbox)
364  modified_state1 = get_wc_state(wc_dir)
365  assert modified_state1 != initial_state
366
367  # Shelve; check there are no longer any local mods
368  svntest.actions.run_and_verify_svn(None, [],
369                                     'x-shelve', 'foo')
370  check_wc_state(wc_dir, initial_state)
371
372  # Make a different local mod that will conflict with the shelf
373  modifier2(sbox)
374  modified_state2 = get_wc_state(wc_dir)
375
376  # Try to unshelve; check it fails with an error about a conflict
377  svntest.actions.run_and_verify_svn(None, '.*[Cc]onflict.*',
378                                     'x-unshelve', 'foo')
379  # Check nothing changed in the attempt
380  check_wc_state(wc_dir, modified_state2)
381
382#----------------------------------------------------------------------
383
384@SkipUnless(shelf2_enabled)
385def shelve_binary_file_mod(sbox):
386  "shelve binary file mod"
387
388  sbox.build(empty=True)
389
390  existing_files = ['A/B/existing']
391  mod_files = ['bin', 'A/B/bin']
392
393  sbox.simple_mkdir('A', 'A/B')
394  for f in existing_files + mod_files:
395    sbox.simple_add_text('\0\1\2\3\4\5', f)
396  sbox.simple_commit()
397
398  def modifier(sbox):
399    for f in mod_files:
400      sbox.simple_append(f, '\6\5\4\3\2\1\0', truncate=True)
401
402  shelve_unshelve(sbox, modifier)
403
404#----------------------------------------------------------------------
405
406@SkipUnless(shelf2_enabled)
407def shelve_binary_file_add(sbox):
408  "shelve binary file add"
409
410  sbox.build(empty=True)
411
412  existing_files = ['A/B/existing']
413  mod_files = ['bin', 'A/B/bin']
414
415  sbox.simple_mkdir('A', 'A/B')
416  for f in existing_files:
417    sbox.simple_add_text('\0\1\2\3\4\5', f)
418  sbox.simple_commit()
419
420  def modifier(sbox):
421    for f in mod_files:
422      sbox.simple_add_text('\0\1\2\3\4\5', f)
423
424  shelve_unshelve(sbox, modifier)
425
426#----------------------------------------------------------------------
427
428@SkipUnless(shelf2_enabled)
429def shelve_binary_file_del(sbox):
430  "shelve binary file del"
431
432  sbox.build(empty=True)
433
434  existing_files = ['A/B/existing']
435  mod_files = ['bin', 'A/B/bin']
436
437  sbox.simple_mkdir('A', 'A/B')
438  for f in existing_files + mod_files:
439    sbox.simple_add_text('\0\1\2\3\4\5', f)
440  sbox.simple_commit()
441
442  def modifier(sbox):
443    for f in mod_files:
444      sbox.simple_rm(f)
445
446  shelve_unshelve(sbox, modifier)
447
448#----------------------------------------------------------------------
449
450@SkipUnless(shelf2_enabled)
451def shelve_binary_file_replace(sbox):
452  "shelve binary file replace"
453
454  sbox.build(empty=True)
455
456  existing_files = ['A/B/existing']
457  mod_files = ['bin', 'A/B/bin']
458
459  sbox.simple_mkdir('A', 'A/B')
460  for f in existing_files + mod_files:
461    sbox.simple_add_text('\0\1\2\3\4\5', f)
462  sbox.simple_commit()
463
464  def modifier(sbox):
465    for f in mod_files:
466      sbox.simple_rm(f)
467      sbox.simple_add_text('\6\5\4\3\2\1\0', f)
468
469  shelve_unshelve(sbox, modifier)
470
471#----------------------------------------------------------------------
472
473@SkipUnless(shelf2_enabled)
474def shelve_with_log_message(sbox):
475  "shelve with log message"
476
477  sbox.build(empty=True)
478  was_cwd = os.getcwd()
479  os.chdir(sbox.wc_dir)
480  sbox.wc_dir = ''
481
482  sbox.simple_add_text('New file', 'f')
483  log_message = 'Log message for foo'
484  svntest.actions.run_and_verify_svn(None, [],
485                                     'x-shelve', 'foo', '-m', log_message)
486  expected_output = svntest.verify.RegexListOutput(
487    ['foo .*',
488     ' ' + log_message
489    ])
490  svntest.actions.run_and_verify_svn(expected_output, [],
491                                     'x-shelf-list')
492
493  os.chdir(was_cwd)
494
495#----------------------------------------------------------------------
496
497def run_and_verify_status(wc_dir_name, status_tree, changelists=[]):
498  """Run 'status' on WC_DIR_NAME and compare it with the
499  expected STATUS_TREE.
500  Returns on success, raises on failure."""
501
502  if not isinstance(status_tree, wc.State):
503    raise TypeError('wc.State tree expected')
504
505  cl_opts = ('--cl=' + cl for cl in changelists)
506  exit_code, output, errput = svntest.main.run_svn(None, 'status', '-q',
507                                                   wc_dir_name, *cl_opts)
508
509  actual_status = svntest.wc.State.from_status(output, wc_dir=wc_dir_name)
510
511  # Verify actual output against expected output.
512  try:
513    status_tree.compare_and_display('status', actual_status)
514  except svntest.tree.SVNTreeError:
515    svntest.actions._log_tree_state("ACTUAL STATUS TREE:", actual_status.old_tree(),
516                                    wc_dir_name)
517    raise
518
519def run_and_verify_shelf_status(wc_dir, expected_status, shelf):
520  run_and_verify_status(wc_dir, expected_status,
521                        changelists=['svn:shelf:' + shelf])
522
523@SkipUnless(shelf2_enabled)
524def shelf_status(sbox):
525  "shelf status"
526
527  sbox.build()
528  was_cwd = os.getcwd()
529  os.chdir(sbox.wc_dir)
530  sbox.wc_dir = ''
531
532  sbox.simple_add_text('New file', 'f')
533  sbox.simple_append('iota', 'New text')
534  sbox.simple_propset('p', 'v', 'A/mu')
535  sbox.simple_rm('A/B/lambda')
536  # Not yet supported:
537  #sbox.simple_rm('A/B/E')
538  expected_status = state_from_status(sbox.wc_dir, v=False, u=False, q=False)
539  run_and_verify_status(sbox.wc_dir, expected_status)
540
541  svntest.actions.run_and_verify_svn(None, [],
542                                     'x-shelve', 'foo')
543  run_and_verify_shelf_status(sbox.wc_dir, expected_status, shelf='foo')
544
545  os.chdir(was_cwd)
546
547#----------------------------------------------------------------------
548
549@SkipUnless(shelf2_enabled)
550def shelve_mkdir(sbox):
551  "shelve mkdir"
552
553  sbox.build()
554
555  def modifier(sbox):
556    sbox.simple_mkdir('D', 'D/D2')
557    sbox.simple_propset('p', 'v', 'D', 'D/D2')
558
559  shelve_unshelve(sbox, modifier, cannot_shelve=True)
560
561#----------------------------------------------------------------------
562
563@SkipUnless(shelf2_enabled)
564def shelve_rmdir(sbox):
565  "shelve rmdir"
566
567  sbox.build()
568  sbox.simple_propset('p', 'v', 'A/C')
569  sbox.simple_commit()
570
571  def modifier(sbox):
572    sbox.simple_rm('A/C', 'A/D/G')
573
574  shelve_unshelve(sbox, modifier, cannot_shelve=True)
575
576#----------------------------------------------------------------------
577
578@SkipUnless(shelf2_enabled)
579def shelve_replace_dir(sbox):
580  "shelve replace dir"
581
582  sbox.build()
583  sbox.simple_propset('p', 'v', 'A/C')
584  sbox.simple_commit()
585
586  def modifier(sbox):
587    sbox.simple_rm('A/C', 'A/D/G')
588    sbox.simple_mkdir('A/C', 'A/C/D2')
589
590  shelve_unshelve(sbox, modifier, cannot_shelve=True)
591
592#----------------------------------------------------------------------
593
594@SkipUnless(shelf2_enabled)
595def shelve_file_copy(sbox):
596  "shelve file copy"
597
598  sbox.build()
599
600  def modifier(sbox):
601    sbox.simple_copy('iota', 'A/ii')
602    sbox.simple_propset('p', 'v', 'A/ii')
603
604  shelve_unshelve(sbox, modifier, cannot_shelve=True)
605
606#----------------------------------------------------------------------
607
608@SkipUnless(shelf2_enabled)
609def shelve_dir_copy(sbox):
610  "shelve dir copy"
611
612  sbox.build()
613
614  def modifier(sbox):
615    sbox.simple_copy('A/B', 'BB')
616    sbox.simple_propset('p', 'v', 'BB')
617
618  shelve_unshelve(sbox, modifier, cannot_shelve=True)
619
620#----------------------------------------------------------------------
621
622@SkipUnless(shelf2_enabled)
623def list_shelves(sbox):
624  "list_shelves"
625
626  sbox.build()
627  was_cwd = os.getcwd()
628  os.chdir(sbox.wc_dir)
629  sbox.wc_dir = ''
630
631  # an empty list
632  svntest.actions.run_and_verify_svn([], [],
633                                     'x-shelf-list', '-q')
634
635  # make two shelves
636  sbox.simple_append('A/mu', 'appended mu text')
637  svntest.actions.run_and_verify_svn(None, [],
638                                     'x-shelf-save', 'foo')
639  sbox.simple_append('A/mu', 'appended more text')
640  svntest.actions.run_and_verify_svn(None, [],
641                                     'x-shelf-save', 'foo', '-m', 'log msg')
642  svntest.actions.run_and_verify_svn(None, [],
643                                     'x-shelf-save', 'bar', '-m', 'log msg')
644
645  # We don't check for time-ordering of the shelves. If we want to do so, we
646  # would need to sleep for timestamps to differ, between creating them.
647
648  # a quiet list
649  expected_out = svntest.verify.UnorderedRegexListOutput(['foo', 'bar'])
650  svntest.actions.run_and_verify_svn(expected_out, [],
651                                     'x-shelf-list', '-q')
652
653  # a detailed list
654  expected_out = svntest.verify.UnorderedRegexListOutput(['foo .* 1 path.*',
655                                                          ' log msg',
656                                                          'bar .* 1 path.*',
657                                                          ' log msg'])
658  svntest.actions.run_and_verify_svn(expected_out, [],
659                                     'x-shelf-list')
660
661  os.chdir(was_cwd)
662
663#----------------------------------------------------------------------
664
665@SkipUnless(shelf2_enabled)
666def refuse_to_shelve_conflict(sbox):
667  "refuse to shelve conflict"
668
669  sbox.build(empty=True)
670  was_cwd = os.getcwd()
671  os.chdir(sbox.wc_dir)
672  sbox.wc_dir = ''
673
674  # create a tree conflict victim at an unversioned path
675  sbox.simple_mkdir('topdir')
676  sbox.simple_commit()
677  sbox.simple_mkdir('topdir/subdir')
678  sbox.simple_commit()
679  sbox.simple_update()
680  sbox.simple_rm('topdir')
681  sbox.simple_commit()
682  sbox.simple_update()
683  svntest.actions.run_and_verify_svn(
684    None, [],
685    'merge', '-c2', '.', '--ignore-ancestry', '--accept', 'postpone')
686  svntest.actions.run_and_verify_svn(
687    None, 'svn: E155015:.*existing.*conflict.*',
688    'merge', '-c1', '.', '--ignore-ancestry', '--accept', 'postpone')
689
690  # attempt to shelve
691  expected_out = svntest.verify.RegexListOutput([
692    r'--- .*',
693    r'--- .*',
694    r'\?     C topdir',
695    r'      > .*',
696    r'      >   not shelved'])
697  svntest.actions.run_and_verify_svn(expected_out,
698                                     '.* 1 path could not be shelved',
699                                     'x-shelf-save', 'foo')
700
701  os.chdir(was_cwd)
702
703#----------------------------------------------------------------------
704
705def unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state):
706  """Run a test scenario in which 'unshelve' needs to merge some shelved
707     changes made by modifier1() with some committed changes made by
708     modifier2(). tweak_expected_state() must produce the expected WC state.
709  """
710  sbox.build()
711  was_cwd = os.getcwd()
712  os.chdir(sbox.wc_dir)
713  sbox.wc_dir = ''
714  wc_dir = sbox.wc_dir
715
716  setup(sbox)
717  sbox.simple_commit()
718  initial_state = get_wc_state(wc_dir)
719
720  # Make some changes to the working copy
721  modifier1(sbox)
722  modified_state = get_wc_state(wc_dir)
723
724  # Shelve; check there are no longer any modifications
725  svntest.actions.run_and_verify_svn(None, [],
726                                     'x-shelve', 'foo')
727  check_wc_state(wc_dir, initial_state)
728
729  # Make a different change, with which we shall merge
730  modifier2(sbox)
731  sbox.simple_commit()
732  modified_state[0].tweak('A/mu', wc_rev='3')
733
734  # Unshelve; check the expected result of the merge
735  svntest.actions.run_and_verify_svn(None, [],
736                                     'x-unshelve', 'foo')
737  tweak_expected_state(modified_state)
738  check_wc_state(wc_dir, modified_state)
739
740  os.chdir(was_cwd)
741
742@SkipUnless(shelf2_enabled)
743def unshelve_text_mod_merge(sbox):
744  "unshelve text mod merge"
745
746  orig_contents='A\nB\nC\nD\nE\n'
747  mod1_contents='A\nBB\nC\nD\nE\n'
748  mod2_contents='A\nB\nC\nDD\nE\n'
749  merged_contents='A\nBB\nC\nDD\nE\n'
750
751  def setup(sbox):
752    sbox.simple_append('A/mu', orig_contents, truncate=True)
753
754  def modifier1(sbox):
755    sbox.simple_append('A/mu', mod1_contents, truncate=True)
756
757  def modifier2(sbox):
758    sbox.simple_append('A/mu', mod2_contents, truncate=True)
759
760  def tweak_expected_state(modified_state):
761    modified_state[1].tweak('A/mu', contents=merged_contents)
762
763  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
764
765#----------------------------------------------------------------------
766
767@SkipUnless(shelf2_enabled)
768def unshelve_text_mod_conflict(sbox):
769  "unshelve text mod conflict"
770
771  orig_contents='A\nB\nC\nD\nE\n'
772  mod1_contents='A\nBB\nC\nD\nE\n'
773  mod2_contents='A\nBCD\nC\nD\nE\n'
774  merged_contents = 'A\n<<<<<<< .working\nBCD\n||||||| .merge-left\nB\n=======\nBB\n>>>>>>> .merge-right\nC\nD\nE\n'
775
776  def setup(sbox):
777    sbox.simple_append('A/mu', orig_contents, truncate=True)
778
779  def modifier1(sbox):
780    sbox.simple_append('A/mu', mod1_contents, truncate=True)
781
782  def modifier2(sbox):
783    sbox.simple_append('A/mu', mod2_contents, truncate=True)
784
785  def tweak_expected_state(modified_state):
786    modified_state[0].tweak('A/mu', status='C ')
787    modified_state[1].tweak('A/mu', contents=merged_contents)
788    modified_state[1].add({
789      'A/mu.merge-left':  Item(contents=orig_contents),
790      'A/mu.merge-right': Item(contents=mod1_contents),
791      'A/mu.working':     Item(contents=mod2_contents),
792      })
793
794  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
795
796#----------------------------------------------------------------------
797
798@SkipUnless(shelf2_enabled)
799def unshelve_undeclared_binary_mod_conflict(sbox):
800  "unshelve undeclared binary mod conflict"
801
802  orig_contents='\1\2\3\4\5'
803  mod1_contents='\1\2\2\3\4\5'
804  mod2_contents='\1\2\3\4\3\4\5'
805  merged_contents = '<<<<<<< .working\n' + mod2_contents + '||||||| .merge-left\n' + orig_contents + '=======\n' + mod1_contents + '>>>>>>> .merge-right\n'
806
807  def setup(sbox):
808    sbox.simple_append('A/mu', orig_contents, truncate=True)
809
810  def modifier1(sbox):
811    sbox.simple_append('A/mu', mod1_contents, truncate=True)
812
813  def modifier2(sbox):
814    sbox.simple_append('A/mu', mod2_contents, truncate=True)
815
816  def tweak_expected_state(modified_state):
817    modified_state[0].tweak('A/mu', status='C ')
818    modified_state[1].tweak('A/mu', contents=merged_contents)
819    modified_state[1].add({
820      'A/mu.merge-left':  Item(contents=orig_contents),
821      'A/mu.merge-right': Item(contents=mod1_contents),
822      'A/mu.working':     Item(contents=mod2_contents),
823      })
824
825  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
826
827#----------------------------------------------------------------------
828
829@SkipUnless(shelf2_enabled)
830def unshelve_binary_mod_conflict(sbox):
831  "unshelve binary mod conflict"
832
833  orig_contents='\1\2\3\4\5'
834  mod1_contents='\1\2\2\3\4\5'
835  mod2_contents='\1\2\3\4\3\4\5'
836
837  def setup(sbox):
838    sbox.simple_append('A/mu', orig_contents, truncate=True)
839    sbox.simple_propset('svn:mime-type', 'application/octet-stream', 'A/mu')
840
841  def modifier1(sbox):
842    sbox.simple_append('A/mu', mod1_contents, truncate=True)
843
844  def modifier2(sbox):
845    sbox.simple_append('A/mu', mod2_contents, truncate=True)
846
847  def tweak_expected_state(modified_state):
848    modified_state[0].tweak('A/mu', status='C ')
849    modified_state[1].tweak('A/mu', contents=mod2_contents)
850    modified_state[1].add({
851      'A/mu.merge-left':  Item(contents=orig_contents),
852      'A/mu.merge-right': Item(contents=mod1_contents),
853      })
854
855  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
856
857#----------------------------------------------------------------------
858
859@SkipUnless(shelf2_enabled)
860def unshelve_text_prop_merge(sbox):
861  "unshelve text prop merge"
862
863  def setup(sbox):
864    sbox.simple_propset('p1', 'v', 'A/mu')
865    sbox.simple_propset('p2', 'v', 'A/mu')
866
867  def modifier1(sbox):
868    sbox.simple_propset('p1', 'changed', 'A/mu')
869
870  def modifier2(sbox):
871    sbox.simple_propset('p2', 'changed', 'A/mu')
872
873  def tweak_expected_state(wc_state):
874    wc_state[1].tweak('A/mu', props={'p1':'changed',
875                                     'p2':'changed'})
876
877  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
878
879#----------------------------------------------------------------------
880
881@SkipUnless(shelf2_enabled)
882def unshelve_text_prop_conflict(sbox):
883  "unshelve text prop conflict"
884
885  orig_contents='A'
886  mod1_contents='B'
887  mod2_contents='C'
888  merged_contents='C'
889  prej_contents='''Trying to change property 'p'
890but the local property value conflicts with the incoming change.
891<<<<<<< (local property value)
892C||||||| (incoming 'changed from' value)
893A=======
894B>>>>>>> (incoming 'changed to' value)
895'''
896
897  def setup(sbox):
898    sbox.simple_propset('p', orig_contents, 'A/mu')
899
900  def modifier1(sbox):
901    sbox.simple_propset('p', mod1_contents, 'A/mu')
902
903  def modifier2(sbox):
904    sbox.simple_propset('p', mod2_contents, 'A/mu')
905
906  def tweak_expected_state(wc_state):
907    wc_state[0].tweak('A/mu', status=' C')
908    wc_state[1].tweak('A/mu', props={'p':merged_contents})
909    wc_state[1].add({
910      'A/mu.prej':     Item(contents=prej_contents),
911      })
912
913  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
914
915#----------------------------------------------------------------------
916
917def run_and_verify_shelf_diff_summarize(output_tree, shelf, *args):
918  """Run 'svn shelf-diff --summarize' with the arguments *ARGS.
919
920  The subcommand output will be verified against OUTPUT_TREE.  Returns
921  on success, raises on failure.
922  """
923
924  if isinstance(output_tree, wc.State):
925    output_tree = output_tree.old_tree()
926
927  exit_code, output, errput = svntest.actions.run_and_verify_svn(
928                                None, [],
929                                'x-shelf-diff', '--summarize', shelf, *args)
930
931  actual = svntest.tree.build_tree_from_diff_summarize(output)
932
933  # Verify actual output against expected output.
934  try:
935    svntest.tree.compare_trees("output", actual, output_tree)
936  except svntest.tree.SVNTreeError:
937    svntest.verify.display_trees(None, 'DIFF OUTPUT TREE', output_tree, actual)
938    raise
939
940# Exercise a very basic case of shelf-diff.
941@SkipUnless(shelf2_enabled)
942def shelf_diff_simple(sbox):
943  "shelf diff simple"
944
945  sbox.build()
946  was_cwd = os.getcwd()
947  os.chdir(sbox.wc_dir)
948  sbox.wc_dir = ''
949  wc_dir = sbox.wc_dir
950
951  def setup(sbox):
952    sbox.simple_propset('p1', 'v', 'A/mu')
953    sbox.simple_propset('p2', 'v', 'A/mu')
954
955  def modifier1(sbox):
956    sbox.simple_append('A/mu', 'New line.\n')
957    sbox.simple_propset('p1', 'changed', 'A/mu')
958
959  setup(sbox)
960  sbox.simple_commit()
961  initial_state = get_wc_state(wc_dir)
962
963  # Make some changes to the working copy
964  modifier1(sbox)
965  modified_state = get_wc_state(wc_dir)
966
967  svntest.actions.run_and_verify_svn(None, [],
968                                     'x-shelf-save', 'foo')
969
970  # basic svn-style diff
971  expected_output = make_diff_header('A/mu', 'revision 2', 'working copy') + [
972                      "@@ -1 +1,2 @@\n",
973                      " This is the file 'mu'.\n",
974                      "+New line.\n",
975                    ] + make_diff_prop_header('A/mu') \
976                    + make_diff_prop_modified('p1', 'v', 'changed')
977  svntest.actions.run_and_verify_svn(expected_output, [],
978                                     'x-shelf-diff', 'foo')
979
980  # basic summary diff
981  expected_diff = svntest.wc.State(wc_dir, {
982    'A/mu':           Item(status='MM'),
983  })
984  run_and_verify_shelf_diff_summarize(expected_diff, 'foo')
985
986
987########################################################################
988# Run the tests
989
990# list all tests here, starting with None:
991test_list = [ None,
992              shelve_text_mods,
993              shelve_prop_changes,
994              shelve_adds,
995              shelve_deletes,
996              shelve_replace,
997              shelve_empty_adds,
998              shelve_empty_deletes,
999              shelve_from_inner_path,
1000              checkpoint_basic,
1001              shelve_mergeinfo,
1002              unshelve_refuses_if_conflicts,
1003              shelve_binary_file_mod,
1004              shelve_binary_file_add,
1005              shelve_binary_file_del,
1006              shelve_binary_file_replace,
1007              shelve_with_log_message,
1008              shelf_status,
1009              shelve_mkdir,
1010              shelve_rmdir,
1011              shelve_replace_dir,
1012              shelve_file_copy,
1013              shelve_dir_copy,
1014              list_shelves,
1015              refuse_to_shelve_conflict,
1016              unshelve_text_mod_merge,
1017              unshelve_text_mod_conflict,
1018              unshelve_undeclared_binary_mod_conflict,
1019              unshelve_binary_mod_conflict,
1020              unshelve_text_prop_merge,
1021              unshelve_text_prop_conflict,
1022              shelf_diff_simple,
1023             ]
1024
1025if __name__ == '__main__':
1026  svntest.main.run_tests(test_list)
1027  # NOTREACHED
1028
1029
1030### End of file.
1031