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 shelf3_enabled():
50  v = os.getenv('SVN_EXPERIMENTAL_COMMANDS')
51  return v is not None and v.find('shelf3') >= 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  # Diff; ensure something comes out and it doesn't crash
134  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
135                                     'x-shelf-diff', 'foo')
136  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
137                                     'x-shelf-diff', '--summarize', 'foo')
138
139  # Unshelve; check the original modifications are here again
140  svntest.actions.run_and_verify_svn(None, [],
141                                     'x-unshelve', 'foo')
142  check_wc_state(wc_dir, modified_state)
143
144#----------------------------------------------------------------------
145
146def shelve_unshelve(sbox, modifier, cannot_shelve=False):
147  """Round-trip: build 'sbox'; apply changes by calling 'modifier(sbox)';
148     shelve and unshelve; verify changes are fully reverted and restored.
149  """
150
151  if not sbox.is_built():
152    sbox.build()
153  was_cwd = os.getcwd()
154  os.chdir(sbox.wc_dir)
155  sbox.wc_dir = ''
156
157  shelve_unshelve_verify(sbox, modifier, cannot_shelve)
158
159  os.chdir(was_cwd)
160
161######################################################################
162# Tests
163#
164#   Each test must return on success or raise on failure.
165
166@SkipUnless(shelf3_enabled)
167def shelve_text_mods(sbox):
168  "shelve text mods"
169
170  def modifier(sbox):
171    sbox.simple_append('A/mu', 'appended mu text')
172
173  shelve_unshelve(sbox, modifier)
174
175#----------------------------------------------------------------------
176
177@SkipUnless(shelf3_enabled)
178def shelve_prop_changes(sbox):
179  "shelve prop changes"
180
181  def modifier(sbox):
182    sbox.simple_propset('p', 'v', 'A')
183    sbox.simple_propset('p', 'v', 'A/mu')
184
185  shelve_unshelve(sbox, modifier)
186
187#----------------------------------------------------------------------
188
189@SkipUnless(shelf3_enabled)
190def shelve_adds(sbox):
191  "shelve adds"
192
193  def modifier(sbox):
194    sbox.simple_add_text('A new file\n', 'A/new')
195    sbox.simple_add_text('A new file\n', 'A/new2')
196    sbox.simple_propset('p', 'v', 'A/new2')
197
198  shelve_unshelve(sbox, modifier)
199
200#----------------------------------------------------------------------
201
202@Issue(4709)
203@SkipUnless(shelf3_enabled)
204def shelve_deletes(sbox):
205  "shelve deletes"
206
207  def modifier(sbox):
208    sbox.simple_rm('A/mu')
209
210  shelve_unshelve(sbox, modifier)
211
212#----------------------------------------------------------------------
213
214@SkipUnless(shelf3_enabled)
215def shelve_replace(sbox):
216  "shelve replace"
217
218  def modifier(sbox):
219    sbox.simple_rm('A/mu')
220    sbox.simple_add_text('Replacement\n', 'A/mu')
221    sbox.simple_propset('p', 'v', 'A/mu')
222
223  shelve_unshelve(sbox, modifier)
224
225#----------------------------------------------------------------------
226
227@SkipUnless(shelf3_enabled)
228def shelve_empty_adds(sbox):
229  "shelve empty adds"
230  sbox.build(empty=True)
231
232  def modifier(sbox):
233    sbox.simple_add_text('', 'empty')
234    sbox.simple_add_text('', 'empty-with-prop')
235    sbox.simple_propset('p', 'v', 'empty-with-prop')
236
237  shelve_unshelve(sbox, modifier)
238
239#----------------------------------------------------------------------
240
241@SkipUnless(shelf3_enabled)
242def shelve_empty_deletes(sbox):
243  "shelve empty deletes"
244  sbox.build(empty=True)
245  sbox.simple_add_text('', 'empty')
246  sbox.simple_add_text('', 'empty-with-prop')
247  sbox.simple_propset('p', 'v', 'empty-with-prop')
248  sbox.simple_commit()
249
250  def modifier(sbox):
251    sbox.simple_rm('empty', 'empty-with-prop')
252
253  shelve_unshelve(sbox, modifier)
254
255#----------------------------------------------------------------------
256
257@SkipUnless(shelf3_enabled)
258def shelve_from_inner_path(sbox):
259  "shelve from inner path"
260
261  def modifier(sbox):
262    sbox.simple_append('A/mu', 'appended mu text')
263
264  sbox.build()
265  was_cwd = os.getcwd()
266  os.chdir(sbox.ospath('A'))
267  sbox.wc_dir = '..'
268
269  shelve_unshelve_verify(sbox, modifier)
270
271  os.chdir(was_cwd)
272
273#----------------------------------------------------------------------
274
275def save_revert_restore(sbox, modifier1, modifier2):
276  "Save 2 checkpoints; revert; restore 1st"
277
278  sbox.build()
279  was_cwd = os.getcwd()
280  os.chdir(sbox.wc_dir)
281  sbox.wc_dir = ''
282  wc_dir = ''
283
284  initial_state = get_wc_state(wc_dir)
285
286  # Make some changes to the working copy
287  modifier1(sbox)
288
289  # Remember the modified state
290  modified_state1 = get_wc_state(wc_dir)
291
292  # Save a checkpoint; check nothing changed
293  svntest.actions.run_and_verify_svn(None, [],
294                                     'x-shelf-save', 'foo')
295  check_wc_state(wc_dir, modified_state1)
296
297  # Modify again; remember the state; save a checkpoint
298  modifier2(sbox)
299  modified_state2 = get_wc_state(wc_dir)
300  svntest.actions.run_and_verify_svn(None, [],
301                                     'x-shelf-save', 'foo')
302  check_wc_state(wc_dir, modified_state2)
303
304  # Revert
305  svntest.actions.run_and_verify_svn(None, [],
306                                     'revert', '-R', '.')
307  check_wc_state(wc_dir, initial_state)
308
309  # Restore; check the original modifications are here again
310  svntest.actions.run_and_verify_svn(None, [],
311                                     'x-unshelve', 'foo', '1')
312  check_wc_state(wc_dir, modified_state1)
313
314  os.chdir(was_cwd)
315
316#----------------------------------------------------------------------
317
318@SkipUnless(shelf3_enabled)
319def checkpoint_basic(sbox):
320  "checkpoint basic"
321
322  def modifier1(sbox):
323    sbox.simple_append('A/mu', 'appended mu text\n')
324
325  def modifier2(sbox):
326    sbox.simple_append('iota', 'appended iota text\n')
327    sbox.simple_append('A/mu', 'appended another line\n')
328
329  save_revert_restore(sbox, modifier1, modifier2)
330
331#----------------------------------------------------------------------
332
333@Issue(3747)
334@SkipUnless(shelf3_enabled)
335def shelve_mergeinfo(sbox):
336  "shelve mergeinfo"
337
338  def modifier(sbox):
339    sbox.simple_propset('svn:mergeinfo', '/trunk/A:1-3,10', 'A')
340    sbox.simple_propset('svn:mergeinfo', '/trunk/A/mu:1-3,10', 'A/mu')
341
342  shelve_unshelve(sbox, modifier)
343
344#----------------------------------------------------------------------
345
346@SkipUnless(shelf3_enabled)
347def unshelve_refuses_if_conflicts(sbox):
348  "unshelve refuses if conflicts"
349
350  def modifier1(sbox):
351    sbox.simple_append('alpha', 'A-mod1\nB\nC\nD\n', truncate=True)
352    sbox.simple_append('beta', 'A-mod1\nB\nC\nD\n', truncate=True)
353
354  def modifier2(sbox):
355    sbox.simple_append('beta', 'A-mod2\nB\nC\nD\n', truncate=True)
356
357  sbox.build(empty=True)
358  was_cwd = os.getcwd()
359  os.chdir(sbox.wc_dir)
360  sbox.wc_dir = ''
361  wc_dir = ''
362
363  sbox.simple_add_text('A\nB\nC\nD\n', 'alpha')
364  sbox.simple_add_text('A\nB\nC\nD\n', 'beta')
365  sbox.simple_commit()
366  initial_state = get_wc_state(wc_dir)
367
368  # Make initial mods; remember this modified state
369  modifier1(sbox)
370  modified_state1 = get_wc_state(wc_dir)
371  assert modified_state1 != initial_state
372
373  # Shelve; check there are no longer any local mods
374  svntest.actions.run_and_verify_svn(None, [],
375                                     'x-shelve', 'foo')
376  check_wc_state(wc_dir, initial_state)
377
378  # Make a different local mod that will conflict with the shelf
379  modifier2(sbox)
380  modified_state2 = get_wc_state(wc_dir)
381
382  # Try to unshelve; check it fails with an error about a conflict
383  svntest.actions.run_and_verify_svn(None, '.*[Cc]onflict.*',
384                                     'x-unshelve', 'foo')
385  # Check nothing changed in the attempt
386  check_wc_state(wc_dir, modified_state2)
387
388#----------------------------------------------------------------------
389
390@SkipUnless(shelf3_enabled)
391def shelve_binary_file_mod(sbox):
392  "shelve binary file mod"
393
394  sbox.build(empty=True)
395
396  existing_files = ['A/B/existing']
397  mod_files = ['bin', 'A/B/bin']
398
399  sbox.simple_mkdir('A', 'A/B')
400  for f in existing_files + mod_files:
401    sbox.simple_add_text('\0\1\2\3\4\5', f)
402  sbox.simple_commit()
403
404  def modifier(sbox):
405    for f in mod_files:
406      sbox.simple_append(f, '\6\5\4\3\2\1\0', truncate=True)
407
408  shelve_unshelve(sbox, modifier)
409
410#----------------------------------------------------------------------
411
412@SkipUnless(shelf3_enabled)
413def shelve_binary_file_add(sbox):
414  "shelve binary file add"
415
416  sbox.build(empty=True)
417
418  existing_files = ['A/B/existing']
419  mod_files = ['bin', 'A/B/bin']
420
421  sbox.simple_mkdir('A', 'A/B')
422  for f in existing_files:
423    sbox.simple_add_text('\0\1\2\3\4\5', f)
424  sbox.simple_commit()
425
426  def modifier(sbox):
427    for f in mod_files:
428      sbox.simple_add_text('\0\1\2\3\4\5', f)
429
430  shelve_unshelve(sbox, modifier)
431
432#----------------------------------------------------------------------
433
434@SkipUnless(shelf3_enabled)
435def shelve_binary_file_del(sbox):
436  "shelve binary file del"
437
438  sbox.build(empty=True)
439
440  existing_files = ['A/B/existing']
441  mod_files = ['bin', 'A/B/bin']
442
443  sbox.simple_mkdir('A', 'A/B')
444  for f in existing_files + mod_files:
445    sbox.simple_add_text('\0\1\2\3\4\5', f)
446  sbox.simple_commit()
447
448  def modifier(sbox):
449    for f in mod_files:
450      sbox.simple_rm(f)
451
452  shelve_unshelve(sbox, modifier)
453
454#----------------------------------------------------------------------
455
456@SkipUnless(shelf3_enabled)
457def shelve_binary_file_replace(sbox):
458  "shelve binary file replace"
459
460  sbox.build(empty=True)
461
462  existing_files = ['A/B/existing']
463  mod_files = ['bin', 'A/B/bin']
464
465  sbox.simple_mkdir('A', 'A/B')
466  for f in existing_files + mod_files:
467    sbox.simple_add_text('\0\1\2\3\4\5', f)
468  sbox.simple_commit()
469
470  def modifier(sbox):
471    for f in mod_files:
472      sbox.simple_rm(f)
473      sbox.simple_add_text('\6\5\4\3\2\1\0', f)
474
475  shelve_unshelve(sbox, modifier)
476
477#----------------------------------------------------------------------
478
479@SkipUnless(shelf3_enabled)
480def shelve_with_log_message(sbox):
481  "shelve with log message"
482
483  sbox.build(empty=True)
484  was_cwd = os.getcwd()
485  os.chdir(sbox.wc_dir)
486  sbox.wc_dir = ''
487
488  sbox.simple_add_text('New file', 'f')
489  log_message = 'Log message for foo'
490  svntest.actions.run_and_verify_svn(None, [],
491                                     'x-shelve', 'foo', '-m', log_message)
492  expected_output = svntest.verify.RegexListOutput(
493    ['foo .*',
494     ' ' + log_message
495    ])
496  svntest.actions.run_and_verify_svn(expected_output, [],
497                                     'x-shelf-list')
498
499  os.chdir(was_cwd)
500
501#----------------------------------------------------------------------
502
503def run_and_verify_status(wc_dir_name, status_tree, changelists=[]):
504  """Run 'status' on WC_DIR_NAME and compare it with the
505  expected STATUS_TREE.
506  Returns on success, raises on failure."""
507
508  if not isinstance(status_tree, wc.State):
509    raise TypeError('wc.State tree expected')
510
511  cl_opts = ('--cl=' + cl for cl in changelists)
512  exit_code, output, errput = svntest.main.run_svn(None, 'status', '-q',
513                                                   wc_dir_name, *cl_opts)
514
515  actual_status = svntest.wc.State.from_status(output, wc_dir=wc_dir_name)
516
517  # Verify actual output against expected output.
518  try:
519    status_tree.compare_and_display('status', actual_status)
520  except svntest.tree.SVNTreeError:
521    svntest.actions._log_tree_state("ACTUAL STATUS TREE:", actual_status.old_tree(),
522                                    wc_dir_name)
523    raise
524
525def run_and_verify_shelf_status(wc_dir, expected_status, shelf):
526  run_and_verify_status(wc_dir, expected_status,
527                        changelists=['svn:shelf:' + shelf])
528
529@SkipUnless(shelf3_enabled)
530def shelf_status(sbox):
531  "shelf status"
532
533  sbox.build()
534  was_cwd = os.getcwd()
535  os.chdir(sbox.wc_dir)
536  sbox.wc_dir = ''
537
538  sbox.simple_add_text('New file', 'f')
539  sbox.simple_append('iota', 'New text')
540  sbox.simple_propset('p', 'v', 'A/mu')
541  sbox.simple_rm('A/B/lambda')
542  # Not yet supported:
543  #sbox.simple_rm('A/B/E')
544  expected_status = state_from_status(sbox.wc_dir, v=False, u=False, q=False)
545  run_and_verify_status(sbox.wc_dir, expected_status)
546
547  svntest.actions.run_and_verify_svn(None, [],
548                                     'x-shelve', 'foo')
549  run_and_verify_shelf_status(sbox.wc_dir, expected_status, shelf='foo')
550
551  os.chdir(was_cwd)
552
553#----------------------------------------------------------------------
554
555@XFail()
556@SkipUnless(shelf3_enabled)
557def shelve_mkdir(sbox):
558  "shelve mkdir"
559
560  sbox.build()
561
562  def modifier(sbox):
563    sbox.simple_mkdir('D', 'D/D2')
564    sbox.simple_propset('p', 'v', 'D', 'D/D2')
565
566  shelve_unshelve(sbox, modifier)
567
568#----------------------------------------------------------------------
569
570@SkipUnless(shelf3_enabled)
571def shelve_rmdir(sbox):
572  "shelve rmdir"
573
574  sbox.build()
575  sbox.simple_propset('p', 'v', 'A/C')
576  sbox.simple_commit()
577
578  def modifier(sbox):
579    sbox.simple_rm('A/C', 'A/D/G')
580
581  shelve_unshelve(sbox, modifier)
582
583#----------------------------------------------------------------------
584
585@XFail()
586@SkipUnless(shelf3_enabled)
587def shelve_replace_dir(sbox):
588  "shelve replace dir"
589
590  sbox.build()
591  sbox.simple_propset('p', 'v', 'A/C')
592  sbox.simple_commit()
593
594  def modifier(sbox):
595    sbox.simple_rm('A/C', 'A/D/G')
596    sbox.simple_mkdir('A/C', 'A/C/D2')
597
598  shelve_unshelve(sbox, modifier)
599
600#----------------------------------------------------------------------
601
602@SkipUnless(shelf3_enabled)
603def shelve_file_copy(sbox):
604  "shelve file copy"
605
606  sbox.build()
607
608  def modifier(sbox):
609    sbox.simple_copy('iota', 'A/ii')
610    sbox.simple_propset('p', 'v', 'A/ii')
611
612  shelve_unshelve(sbox, modifier)
613
614#----------------------------------------------------------------------
615
616@SkipUnless(shelf3_enabled)
617def shelve_dir_copy(sbox):
618  "shelve dir copy"
619
620  sbox.build()
621
622  def modifier(sbox):
623    sbox.simple_copy('A/B', 'BB')
624    sbox.simple_propset('p', 'v', 'BB')
625
626  shelve_unshelve(sbox, modifier)
627
628#----------------------------------------------------------------------
629
630@SkipUnless(shelf3_enabled)
631def list_shelves(sbox):
632  "list_shelves"
633
634  sbox.build()
635  was_cwd = os.getcwd()
636  os.chdir(sbox.wc_dir)
637  sbox.wc_dir = ''
638
639  # an empty list
640  svntest.actions.run_and_verify_svn([], [],
641                                     'x-shelf-list', '-q')
642
643  # make two shelves
644  sbox.simple_append('A/mu', 'appended mu text')
645  svntest.actions.run_and_verify_svn(None, [],
646                                     'x-shelf-save', 'foo')
647  sbox.simple_append('A/mu', 'appended more text')
648  svntest.actions.run_and_verify_svn(None, [],
649                                     'x-shelf-save', 'foo', '-m', 'log msg')
650  svntest.actions.run_and_verify_svn(None, [],
651                                     'x-shelf-save', 'bar', '-m', 'log msg')
652
653  # We don't check for time-ordering of the shelves. If we want to do so, we
654  # would need to sleep for timestamps to differ, between creating them.
655
656  # a quiet list
657  expected_out = svntest.verify.UnorderedRegexListOutput(['foo', 'bar'])
658  svntest.actions.run_and_verify_svn(expected_out, [],
659                                     'x-shelf-list', '-q')
660
661  # a detailed list
662  expected_out = svntest.verify.UnorderedRegexListOutput(['foo .* 1 path.*',
663                                                          ' log msg',
664                                                          'bar .* 1 path.*',
665                                                          ' log msg'])
666  svntest.actions.run_and_verify_svn(expected_out, [],
667                                     'x-shelf-list')
668
669  os.chdir(was_cwd)
670
671#----------------------------------------------------------------------
672
673@SkipUnless(shelf3_enabled)
674def refuse_to_shelve_conflict(sbox):
675  "refuse to shelve conflict"
676
677  sbox.build(empty=True)
678  was_cwd = os.getcwd()
679  os.chdir(sbox.wc_dir)
680  sbox.wc_dir = ''
681
682  # create a conflict
683  sbox.simple_mkdir('topdir')
684  sbox.simple_commit()
685  sbox.simple_update()
686  svntest.actions.run_and_verify_svn(
687    None, [],
688    'merge', '-c1', '.', '--ignore-ancestry', '--accept', 'postpone')
689  # check that we did create a conflict
690  svntest.actions.run_and_verify_svn(
691    None, 'svn: E155035:.*conflict.*',
692    'merge', '-c1', '.', '--ignore-ancestry', '--accept', 'postpone')
693
694  # attempt to shelve
695  expected_err = "svn: E155015: .* '.*topdir' remains in conflict"
696  svntest.actions.run_and_verify_svn(None, expected_err,
697                                     'x-shelf-save', 'foo')
698
699  os.chdir(was_cwd)
700
701#----------------------------------------------------------------------
702
703def unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state):
704  """Run a test scenario in which 'unshelve' needs to merge some shelved
705     changes made by modifier1() with some committed changes made by
706     modifier2(). tweak_expected_state() must produce the expected WC state.
707  """
708  sbox.build()
709  was_cwd = os.getcwd()
710  os.chdir(sbox.wc_dir)
711  sbox.wc_dir = ''
712  wc_dir = sbox.wc_dir
713
714  setup(sbox)
715  sbox.simple_commit()
716  initial_state = get_wc_state(wc_dir)
717
718  # Make some changes to the working copy
719  modifier1(sbox)
720  modified_state = get_wc_state(wc_dir)
721
722  # Shelve; check there are no longer any modifications
723  svntest.actions.run_and_verify_svn(None, [],
724                                     'x-shelve', 'foo')
725  check_wc_state(wc_dir, initial_state)
726
727  # Make a different change, with which we shall merge
728  modifier2(sbox)
729  sbox.simple_commit()
730  modified_state[0].tweak('A/mu', wc_rev='3')
731
732  # Unshelve; check the expected result of the merge
733  svntest.actions.run_and_verify_svn(None, [],
734                                     'x-unshelve', 'foo')
735  tweak_expected_state(modified_state)
736  check_wc_state(wc_dir, modified_state)
737
738  os.chdir(was_cwd)
739
740@XFail()
741@SkipUnless(shelf3_enabled)
742def unshelve_text_mod_merge(sbox):
743  "unshelve text mod merge"
744
745  orig_contents='A\nB\nC\nD\nE\n'
746  mod1_contents='A\nBB\nC\nD\nE\n'
747  mod2_contents='A\nB\nC\nDD\nE\n'
748  merged_contents='A\nBB\nC\nDD\nE\n'
749
750  def setup(sbox):
751    sbox.simple_append('A/mu', orig_contents, truncate=True)
752
753  def modifier1(sbox):
754    sbox.simple_append('A/mu', mod1_contents, truncate=True)
755
756  def modifier2(sbox):
757    sbox.simple_append('A/mu', mod2_contents, truncate=True)
758
759  def tweak_expected_state(modified_state):
760    modified_state[1].tweak('A/mu', contents=merged_contents)
761
762  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
763
764#----------------------------------------------------------------------
765
766@XFail()
767@SkipUnless(shelf3_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@XFail()
799@SkipUnless(shelf3_enabled)
800def unshelve_undeclared_binary_mod_conflict(sbox):
801  "unshelve undeclared binary mod conflict"
802
803  orig_contents='\1\2\3\4\5'
804  mod1_contents='\1\2\2\3\4\5'
805  mod2_contents='\1\2\3\4\3\4\5'
806  merged_contents = '<<<<<<< .working\n' + mod2_contents + '||||||| .merge-left\n' + orig_contents + '=======\n' + mod1_contents + '>>>>>>> .merge-right\n'
807
808  def setup(sbox):
809    sbox.simple_append('A/mu', orig_contents, truncate=True)
810
811  def modifier1(sbox):
812    sbox.simple_append('A/mu', mod1_contents, truncate=True)
813
814  def modifier2(sbox):
815    sbox.simple_append('A/mu', mod2_contents, truncate=True)
816
817  def tweak_expected_state(modified_state):
818    modified_state[0].tweak('A/mu', status='C ')
819    modified_state[1].tweak('A/mu', contents=merged_contents)
820    modified_state[1].add({
821      'A/mu.merge-left':  Item(contents=orig_contents),
822      'A/mu.merge-right': Item(contents=mod1_contents),
823      'A/mu.working':     Item(contents=mod2_contents),
824      })
825
826  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
827
828#----------------------------------------------------------------------
829
830@XFail()
831@SkipUnless(shelf3_enabled)
832def unshelve_binary_mod_conflict(sbox):
833  "unshelve binary mod conflict"
834
835  orig_contents='\1\2\3\4\5'
836  mod1_contents='\1\2\2\3\4\5'
837  mod2_contents='\1\2\3\4\3\4\5'
838
839  def setup(sbox):
840    sbox.simple_append('A/mu', orig_contents, truncate=True)
841    sbox.simple_propset('svn:mime-type', 'application/octet-stream', 'A/mu')
842
843  def modifier1(sbox):
844    sbox.simple_append('A/mu', mod1_contents, truncate=True)
845
846  def modifier2(sbox):
847    sbox.simple_append('A/mu', mod2_contents, truncate=True)
848
849  def tweak_expected_state(modified_state):
850    modified_state[0].tweak('A/mu', status='C ')
851    modified_state[1].tweak('A/mu', contents=mod2_contents)
852    modified_state[1].add({
853      'A/mu.merge-left':  Item(contents=orig_contents),
854      'A/mu.merge-right': Item(contents=mod1_contents),
855      })
856
857  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
858
859#----------------------------------------------------------------------
860
861@SkipUnless(shelf3_enabled)
862def unshelve_text_prop_merge(sbox):
863  "unshelve text prop merge"
864
865  def setup(sbox):
866    sbox.simple_propset('p1', 'v', 'A/mu')
867    sbox.simple_propset('p2', 'v', 'A/mu')
868
869  def modifier1(sbox):
870    sbox.simple_propset('p1', 'changed', 'A/mu')
871
872  def modifier2(sbox):
873    sbox.simple_propset('p2', 'changed', 'A/mu')
874
875  def tweak_expected_state(wc_state):
876    wc_state[1].tweak('A/mu', props={'p1':'changed',
877                                     'p2':'changed'})
878
879  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
880
881#----------------------------------------------------------------------
882
883@XFail()
884@SkipUnless(shelf3_enabled)
885def unshelve_text_prop_conflict(sbox):
886  "unshelve text prop conflict"
887
888  orig_contents='A'
889  mod1_contents='B'
890  mod2_contents='C'
891  merged_contents='C'
892  prej_contents='''Trying to change property 'p'
893but the local property value conflicts with the incoming change.
894<<<<<<< (local property value)
895C||||||| (incoming 'changed from' value)
896A=======
897B>>>>>>> (incoming 'changed to' value)
898'''
899
900  def setup(sbox):
901    sbox.simple_propset('p', orig_contents, 'A/mu')
902
903  def modifier1(sbox):
904    sbox.simple_propset('p', mod1_contents, 'A/mu')
905
906  def modifier2(sbox):
907    sbox.simple_propset('p', mod2_contents, 'A/mu')
908
909  def tweak_expected_state(wc_state):
910    wc_state[0].tweak('A/mu', status=' C')
911    wc_state[1].tweak('A/mu', props={'p':merged_contents})
912    wc_state[1].add({
913      'A/mu.prej':     Item(contents=prej_contents),
914      })
915
916  unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
917
918#----------------------------------------------------------------------
919
920def run_and_verify_shelf_diff_summarize(output_tree, shelf, *args):
921  """Run 'svn shelf-diff --summarize' with the arguments *ARGS.
922
923  The subcommand output will be verified against OUTPUT_TREE.  Returns
924  on success, raises on failure.
925  """
926
927  if isinstance(output_tree, wc.State):
928    output_tree = output_tree.old_tree()
929
930  exit_code, output, errput = svntest.actions.run_and_verify_svn(
931                                None, [],
932                                'x-shelf-diff', '--summarize', shelf, *args)
933
934  actual = svntest.tree.build_tree_from_diff_summarize(output)
935
936  # Verify actual output against expected output.
937  try:
938    svntest.tree.compare_trees("output", actual, output_tree)
939  except svntest.tree.SVNTreeError:
940    svntest.verify.display_trees(None, 'DIFF OUTPUT TREE', output_tree, actual)
941    raise
942
943#----------------------------------------------------------------------
944
945# Exercise a very basic case of shelf-diff.
946@SkipUnless(shelf3_enabled)
947def shelf_diff_simple(sbox):
948  "shelf diff simple"
949
950  sbox.build()
951  was_cwd = os.getcwd()
952  os.chdir(sbox.wc_dir)
953  sbox.wc_dir = ''
954  wc_dir = sbox.wc_dir
955
956  def setup(sbox):
957    sbox.simple_propset('p1', 'v', 'A/mu')
958    sbox.simple_propset('p2', 'v', 'A/mu')
959
960  def modifier1(sbox):
961    sbox.simple_rm('A/B/lambda')
962    sbox.simple_add_text('This is a new file.\n', 'A/B/new')
963    sbox.simple_append('A/mu', 'New line.\n')
964    sbox.simple_propset('p1', 'changed', 'A/mu')
965
966  setup(sbox)
967  sbox.simple_commit()
968  initial_state = get_wc_state(wc_dir)
969
970  # Make some changes to the working copy
971  modifier1(sbox)
972  modified_state = get_wc_state(wc_dir)
973
974  svntest.actions.run_and_verify_svn(None, [],
975                                     'x-shelf-save', 'foo')
976
977  # basic svn-style diff
978  expected_output = make_diff_header('A/B/lambda', 'revision 2', 'nonexistent') + [
979                      "@@ -1 +0,0 @@\n",
980                      "-This is the file 'lambda'.\n"
981                    ]
982  expected_output += make_diff_header('A/B/new', 'nonexistent', 'working copy') + [
983                      "@@ -0,0 +1 @@\n",
984                      "+This is a new file.\n"
985                    ]
986  expected_output += make_diff_header('A/mu', 'revision 2', 'working copy') + [
987                      "@@ -1 +1,2 @@\n",
988                      " This is the file 'mu'.\n",
989                      "+New line.\n",
990                    ] + make_diff_prop_header('A/mu') \
991                    + make_diff_prop_modified('p1', 'v', 'changed')
992  svntest.actions.run_and_verify_svn(expected_output, [],
993                                     'x-shelf-diff', 'foo')
994
995  # basic summary diff
996  expected_diff = svntest.wc.State(wc_dir, {
997    'A/B/lambda':     Item(status='D '),
998    'A/B/new':        Item(status='A '),
999    'A/mu':           Item(status='MM'),
1000  })
1001  run_and_verify_shelf_diff_summarize(expected_diff, 'foo')
1002
1003#----------------------------------------------------------------------
1004
1005@XFail()
1006@Issue(4827)
1007@SkipUnless(shelf3_enabled)
1008def shelve_with_kw_translation(sbox):
1009  "shelve with kw translation"
1010  sbox.build(empty=True)
1011  sbox.simple_add_text('$Rev$\n', 'file')
1012  sbox.simple_propset('svn:keywords', 'rev', 'file')
1013  sbox.simple_commit()
1014  sbox.simple_update()
1015
1016  def modifier(sbox):
1017    sbox.simple_append('file', 'New line\n')
1018
1019  shelve_unshelve(sbox, modifier)
1020
1021
1022########################################################################
1023# Run the tests
1024
1025# list all tests here, starting with None:
1026test_list = [ None,
1027              shelve_text_mods,
1028              shelve_prop_changes,
1029              shelve_adds,
1030              shelve_deletes,
1031              shelve_replace,
1032              shelve_empty_adds,
1033              shelve_empty_deletes,
1034              shelve_from_inner_path,
1035              checkpoint_basic,
1036              shelve_mergeinfo,
1037              unshelve_refuses_if_conflicts,
1038              shelve_binary_file_mod,
1039              shelve_binary_file_add,
1040              shelve_binary_file_del,
1041              shelve_binary_file_replace,
1042              shelve_with_log_message,
1043              shelf_status,
1044              shelve_mkdir,
1045              shelve_rmdir,
1046              shelve_replace_dir,
1047              shelve_file_copy,
1048              shelve_dir_copy,
1049              list_shelves,
1050              refuse_to_shelve_conflict,
1051              unshelve_text_mod_merge,
1052              unshelve_text_mod_conflict,
1053              unshelve_undeclared_binary_mod_conflict,
1054              unshelve_binary_mod_conflict,
1055              unshelve_text_prop_merge,
1056              unshelve_text_prop_conflict,
1057              shelf_diff_simple,
1058              shelve_with_kw_translation,
1059             ]
1060
1061if __name__ == '__main__':
1062  svntest.main.run_tests(test_list)
1063  # NOTREACHED
1064
1065
1066### End of file.
1067