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