| #!/usr/bin/env python |
| # |
| # shelf_tests.py: testing shelving |
| # |
| # Subversion is a tool for revision control. |
| # See http://subversion.apache.org for more information. |
| # |
| # ==================================================================== |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| ###################################################################### |
| |
| # General modules |
| import shutil, stat, re, os, logging |
| |
| logger = logging.getLogger() |
| |
| # Our testing module |
| import svntest |
| from svntest import wc |
| from svntest.verify import make_diff_header, make_no_diff_deleted_header, \ |
| make_git_diff_header, make_diff_prop_header, \ |
| make_diff_prop_val, make_diff_prop_deleted, \ |
| make_diff_prop_added, make_diff_prop_modified |
| |
| # (abbreviation) |
| Skip = svntest.testcase.Skip_deco |
| SkipUnless = svntest.testcase.SkipUnless_deco |
| XFail = svntest.testcase.XFail_deco |
| Issues = svntest.testcase.Issues_deco |
| Issue = svntest.testcase.Issue_deco |
| Wimp = svntest.testcase.Wimp_deco |
| Item = wc.StateItem |
| |
| def shelf2_enabled(): |
| v = os.getenv('SVN_EXPERIMENTAL_COMMANDS') |
| return v is not None and v.find('shelf2') >= 0 |
| |
| #---------------------------------------------------------------------- |
| |
| def state_from_status(wc_dir, |
| v=True, u=True, q=True): |
| opts = () |
| if v: |
| opts += ('-v',) |
| if u: |
| opts += ('-u',) |
| if q: |
| opts += ('-q',) |
| _, output, _ = svntest.main.run_svn(None, 'status', wc_dir, *opts) |
| return svntest.wc.State.from_status(output, wc_dir) |
| |
| def get_wc_state(wc_dir): |
| """Return a description of the WC state. Include as much info as shelving |
| should be capable of restoring. |
| """ |
| return (state_from_status(wc_dir), |
| svntest.wc.State.from_wc(wc_dir, load_props=True), |
| ) |
| |
| def check_wc_state(wc_dir, expected): |
| """Check a description of the WC state. Include as much info as shelving |
| should be capable of restoring. |
| """ |
| expect_st, expect_wc = expected |
| actual_st, actual_wc = get_wc_state(wc_dir) |
| |
| # Verify actual status against expected status. |
| try: |
| expect_st.compare_and_display('status', actual_st) |
| except svntest.tree.SVNTreeError: |
| svntest.actions._log_tree_state("EXPECT STATUS TREE:", expect_st.old_tree(), |
| wc_dir) |
| svntest.actions._log_tree_state("ACTUAL STATUS TREE:", actual_st.old_tree(), |
| wc_dir) |
| raise |
| |
| # Verify actual WC against expected WC. |
| try: |
| expect_wc.compare_and_display('status', actual_wc) |
| except svntest.tree.SVNTreeError: |
| svntest.actions._log_tree_state("EXPECT WC TREE:", expect_wc.old_tree(), |
| wc_dir) |
| svntest.actions._log_tree_state("ACTUAL WC TREE:", actual_wc.old_tree(), |
| wc_dir) |
| raise |
| |
| def shelve_unshelve_verify(sbox, modifier, cannot_shelve=False): |
| """Round-trip: shelve; verify all changes are reverted; |
| unshelve; verify all changes are restored. |
| """ |
| |
| wc_dir = sbox.wc_dir |
| virginal_state = get_wc_state(wc_dir) |
| |
| # Make some changes to the working copy |
| modifier(sbox) |
| |
| # Save the modified state |
| modified_state = get_wc_state(wc_dir) |
| |
| if cannot_shelve: |
| svntest.actions.run_and_verify_svn(None, '.* could not be shelved.*', |
| 'x-shelve', 'foo') |
| return |
| |
| # Shelve; check there are no longer any modifications |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelve', 'foo') |
| check_wc_state(wc_dir, virginal_state) |
| |
| # List; ensure the shelf is listed |
| expected_output = svntest.verify.RegexListOutput( |
| [r'foo\s*version \d+.*', |
| r' ', |
| ]) |
| svntest.actions.run_and_verify_svn(expected_output, [], 'x-shelves') |
| |
| # Unshelve; check the original modifications are here again |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-unshelve', 'foo') |
| check_wc_state(wc_dir, modified_state) |
| |
| #---------------------------------------------------------------------- |
| |
| def shelve_unshelve(sbox, modifier, cannot_shelve=False): |
| """Round-trip: build 'sbox'; apply changes by calling 'modifier(sbox)'; |
| shelve and unshelve; verify changes are fully reverted and restored. |
| """ |
| |
| if not sbox.is_built(): |
| sbox.build() |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| |
| shelve_unshelve_verify(sbox, modifier, cannot_shelve) |
| |
| os.chdir(was_cwd) |
| |
| ###################################################################### |
| # Tests |
| # |
| # Each test must return on success or raise on failure. |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_text_mods(sbox): |
| "shelve text mods" |
| |
| def modifier(sbox): |
| sbox.simple_append('A/mu', 'appended mu text') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_prop_changes(sbox): |
| "shelve prop changes" |
| |
| def modifier(sbox): |
| sbox.simple_propset('p', 'v', 'A') |
| sbox.simple_propset('p', 'v', 'A/mu') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_adds(sbox): |
| "shelve adds" |
| |
| def modifier(sbox): |
| sbox.simple_add_text('A new file\n', 'A/new') |
| sbox.simple_add_text('A new file\n', 'A/new2') |
| sbox.simple_propset('p', 'v', 'A/new2') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @Issue(4709) |
| @SkipUnless(shelf2_enabled) |
| def shelve_deletes(sbox): |
| "shelve deletes" |
| |
| def modifier(sbox): |
| sbox.simple_rm('A/mu') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_replace(sbox): |
| "shelve replace" |
| |
| def modifier(sbox): |
| sbox.simple_rm('A/mu') |
| sbox.simple_add_text('Replacement\n', 'A/mu') |
| sbox.simple_propset('p', 'v', 'A/mu') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_empty_adds(sbox): |
| "shelve empty adds" |
| sbox.build(empty=True) |
| |
| def modifier(sbox): |
| sbox.simple_add_text('', 'empty') |
| sbox.simple_add_text('', 'empty-with-prop') |
| sbox.simple_propset('p', 'v', 'empty-with-prop') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_empty_deletes(sbox): |
| "shelve empty deletes" |
| sbox.build(empty=True) |
| sbox.simple_add_text('', 'empty') |
| sbox.simple_add_text('', 'empty-with-prop') |
| sbox.simple_propset('p', 'v', 'empty-with-prop') |
| sbox.simple_commit() |
| |
| def modifier(sbox): |
| sbox.simple_rm('empty', 'empty-with-prop') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_from_inner_path(sbox): |
| "shelve from inner path" |
| |
| def modifier(sbox): |
| sbox.simple_append('A/mu', 'appended mu text') |
| |
| sbox.build() |
| was_cwd = os.getcwd() |
| os.chdir(sbox.ospath('A')) |
| sbox.wc_dir = '..' |
| |
| shelve_unshelve_verify(sbox, modifier) |
| |
| os.chdir(was_cwd) |
| |
| #---------------------------------------------------------------------- |
| |
| def save_revert_restore(sbox, modifier1, modifier2): |
| "Save 2 checkpoints; revert; restore 1st" |
| |
| sbox.build() |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| wc_dir = '' |
| |
| initial_state = get_wc_state(wc_dir) |
| |
| # Make some changes to the working copy |
| modifier1(sbox) |
| |
| # Remember the modified state |
| modified_state1 = get_wc_state(wc_dir) |
| |
| # Save a checkpoint; check nothing changed |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelf-save', 'foo') |
| check_wc_state(wc_dir, modified_state1) |
| |
| # Modify again; remember the state; save a checkpoint |
| modifier2(sbox) |
| modified_state2 = get_wc_state(wc_dir) |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelf-save', 'foo') |
| check_wc_state(wc_dir, modified_state2) |
| |
| # Revert |
| svntest.actions.run_and_verify_svn(None, [], |
| 'revert', '-R', '.') |
| check_wc_state(wc_dir, initial_state) |
| |
| # Restore; check the original modifications are here again |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-unshelve', 'foo', '1') |
| check_wc_state(wc_dir, modified_state1) |
| |
| os.chdir(was_cwd) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def checkpoint_basic(sbox): |
| "checkpoint basic" |
| |
| def modifier1(sbox): |
| sbox.simple_append('A/mu', 'appended mu text\n') |
| |
| def modifier2(sbox): |
| sbox.simple_append('iota', 'appended iota text\n') |
| sbox.simple_append('A/mu', 'appended another line\n') |
| |
| save_revert_restore(sbox, modifier1, modifier2) |
| |
| #---------------------------------------------------------------------- |
| |
| @Issue(3747) |
| @SkipUnless(shelf2_enabled) |
| def shelve_mergeinfo(sbox): |
| "shelve mergeinfo" |
| |
| def modifier(sbox): |
| sbox.simple_propset('svn:mergeinfo', '/trunk/A:1-3,10', 'A') |
| sbox.simple_propset('svn:mergeinfo', '/trunk/A/mu:1-3,10', 'A/mu') |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def unshelve_refuses_if_conflicts(sbox): |
| "unshelve refuses if conflicts" |
| |
| def modifier1(sbox): |
| sbox.simple_append('alpha', 'A-mod1\nB\nC\nD\n', truncate=True) |
| sbox.simple_append('beta', 'A-mod1\nB\nC\nD\n', truncate=True) |
| |
| def modifier2(sbox): |
| sbox.simple_append('beta', 'A-mod2\nB\nC\nD\n', truncate=True) |
| |
| sbox.build(empty=True) |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| wc_dir = '' |
| |
| sbox.simple_add_text('A\nB\nC\nD\n', 'alpha') |
| sbox.simple_add_text('A\nB\nC\nD\n', 'beta') |
| sbox.simple_commit() |
| initial_state = get_wc_state(wc_dir) |
| |
| # Make initial mods; remember this modified state |
| modifier1(sbox) |
| modified_state1 = get_wc_state(wc_dir) |
| assert modified_state1 != initial_state |
| |
| # Shelve; check there are no longer any local mods |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelve', 'foo') |
| check_wc_state(wc_dir, initial_state) |
| |
| # Make a different local mod that will conflict with the shelf |
| modifier2(sbox) |
| modified_state2 = get_wc_state(wc_dir) |
| |
| # Try to unshelve; check it fails with an error about a conflict |
| svntest.actions.run_and_verify_svn(None, '.*[Cc]onflict.*', |
| 'x-unshelve', 'foo') |
| # Check nothing changed in the attempt |
| check_wc_state(wc_dir, modified_state2) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_binary_file_mod(sbox): |
| "shelve binary file mod" |
| |
| sbox.build(empty=True) |
| |
| existing_files = ['A/B/existing'] |
| mod_files = ['bin', 'A/B/bin'] |
| |
| sbox.simple_mkdir('A', 'A/B') |
| for f in existing_files + mod_files: |
| sbox.simple_add_text('\0\1\2\3\4\5', f) |
| sbox.simple_commit() |
| |
| def modifier(sbox): |
| for f in mod_files: |
| sbox.simple_append(f, '\6\5\4\3\2\1\0', truncate=True) |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_binary_file_add(sbox): |
| "shelve binary file add" |
| |
| sbox.build(empty=True) |
| |
| existing_files = ['A/B/existing'] |
| mod_files = ['bin', 'A/B/bin'] |
| |
| sbox.simple_mkdir('A', 'A/B') |
| for f in existing_files: |
| sbox.simple_add_text('\0\1\2\3\4\5', f) |
| sbox.simple_commit() |
| |
| def modifier(sbox): |
| for f in mod_files: |
| sbox.simple_add_text('\0\1\2\3\4\5', f) |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_binary_file_del(sbox): |
| "shelve binary file del" |
| |
| sbox.build(empty=True) |
| |
| existing_files = ['A/B/existing'] |
| mod_files = ['bin', 'A/B/bin'] |
| |
| sbox.simple_mkdir('A', 'A/B') |
| for f in existing_files + mod_files: |
| sbox.simple_add_text('\0\1\2\3\4\5', f) |
| sbox.simple_commit() |
| |
| def modifier(sbox): |
| for f in mod_files: |
| sbox.simple_rm(f) |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_binary_file_replace(sbox): |
| "shelve binary file replace" |
| |
| sbox.build(empty=True) |
| |
| existing_files = ['A/B/existing'] |
| mod_files = ['bin', 'A/B/bin'] |
| |
| sbox.simple_mkdir('A', 'A/B') |
| for f in existing_files + mod_files: |
| sbox.simple_add_text('\0\1\2\3\4\5', f) |
| sbox.simple_commit() |
| |
| def modifier(sbox): |
| for f in mod_files: |
| sbox.simple_rm(f) |
| sbox.simple_add_text('\6\5\4\3\2\1\0', f) |
| |
| shelve_unshelve(sbox, modifier) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_with_log_message(sbox): |
| "shelve with log message" |
| |
| sbox.build(empty=True) |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| |
| sbox.simple_add_text('New file', 'f') |
| log_message = 'Log message for foo' |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelve', 'foo', '-m', log_message) |
| expected_output = svntest.verify.RegexListOutput( |
| ['foo .*', |
| ' ' + log_message |
| ]) |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'x-shelf-list') |
| |
| os.chdir(was_cwd) |
| |
| #---------------------------------------------------------------------- |
| |
| def run_and_verify_status(wc_dir_name, status_tree, changelists=[]): |
| """Run 'status' on WC_DIR_NAME and compare it with the |
| expected STATUS_TREE. |
| Returns on success, raises on failure.""" |
| |
| if not isinstance(status_tree, wc.State): |
| raise TypeError('wc.State tree expected') |
| |
| cl_opts = ('--cl=' + cl for cl in changelists) |
| exit_code, output, errput = svntest.main.run_svn(None, 'status', '-q', |
| wc_dir_name, *cl_opts) |
| |
| actual_status = svntest.wc.State.from_status(output, wc_dir=wc_dir_name) |
| |
| # Verify actual output against expected output. |
| try: |
| status_tree.compare_and_display('status', actual_status) |
| except svntest.tree.SVNTreeError: |
| svntest.actions._log_tree_state("ACTUAL STATUS TREE:", actual_status.old_tree(), |
| wc_dir_name) |
| raise |
| |
| def run_and_verify_shelf_status(wc_dir, expected_status, shelf): |
| run_and_verify_status(wc_dir, expected_status, |
| changelists=['svn:shelf:' + shelf]) |
| |
| @SkipUnless(shelf2_enabled) |
| def shelf_status(sbox): |
| "shelf status" |
| |
| sbox.build() |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| |
| sbox.simple_add_text('New file', 'f') |
| sbox.simple_append('iota', 'New text') |
| sbox.simple_propset('p', 'v', 'A/mu') |
| sbox.simple_rm('A/B/lambda') |
| # Not yet supported: |
| #sbox.simple_rm('A/B/E') |
| expected_status = state_from_status(sbox.wc_dir, v=False, u=False, q=False) |
| run_and_verify_status(sbox.wc_dir, expected_status) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelve', 'foo') |
| run_and_verify_shelf_status(sbox.wc_dir, expected_status, shelf='foo') |
| |
| os.chdir(was_cwd) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_mkdir(sbox): |
| "shelve mkdir" |
| |
| sbox.build() |
| |
| def modifier(sbox): |
| sbox.simple_mkdir('D', 'D/D2') |
| sbox.simple_propset('p', 'v', 'D', 'D/D2') |
| |
| shelve_unshelve(sbox, modifier, cannot_shelve=True) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_rmdir(sbox): |
| "shelve rmdir" |
| |
| sbox.build() |
| sbox.simple_propset('p', 'v', 'A/C') |
| sbox.simple_commit() |
| |
| def modifier(sbox): |
| sbox.simple_rm('A/C', 'A/D/G') |
| |
| shelve_unshelve(sbox, modifier, cannot_shelve=True) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_replace_dir(sbox): |
| "shelve replace dir" |
| |
| sbox.build() |
| sbox.simple_propset('p', 'v', 'A/C') |
| sbox.simple_commit() |
| |
| def modifier(sbox): |
| sbox.simple_rm('A/C', 'A/D/G') |
| sbox.simple_mkdir('A/C', 'A/C/D2') |
| |
| shelve_unshelve(sbox, modifier, cannot_shelve=True) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_file_copy(sbox): |
| "shelve file copy" |
| |
| sbox.build() |
| |
| def modifier(sbox): |
| sbox.simple_copy('iota', 'A/ii') |
| sbox.simple_propset('p', 'v', 'A/ii') |
| |
| shelve_unshelve(sbox, modifier, cannot_shelve=True) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def shelve_dir_copy(sbox): |
| "shelve dir copy" |
| |
| sbox.build() |
| |
| def modifier(sbox): |
| sbox.simple_copy('A/B', 'BB') |
| sbox.simple_propset('p', 'v', 'BB') |
| |
| shelve_unshelve(sbox, modifier, cannot_shelve=True) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def list_shelves(sbox): |
| "list_shelves" |
| |
| sbox.build() |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| |
| # an empty list |
| svntest.actions.run_and_verify_svn([], [], |
| 'x-shelf-list', '-q') |
| |
| # make two shelves |
| sbox.simple_append('A/mu', 'appended mu text') |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelf-save', 'foo') |
| sbox.simple_append('A/mu', 'appended more text') |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelf-save', 'foo', '-m', 'log msg') |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelf-save', 'bar', '-m', 'log msg') |
| |
| # We don't check for time-ordering of the shelves. If we want to do so, we |
| # would need to sleep for timestamps to differ, between creating them. |
| |
| # a quiet list |
| expected_out = svntest.verify.UnorderedRegexListOutput(['foo', 'bar']) |
| svntest.actions.run_and_verify_svn(expected_out, [], |
| 'x-shelf-list', '-q') |
| |
| # a detailed list |
| expected_out = svntest.verify.UnorderedRegexListOutput(['foo .* 1 path.*', |
| ' log msg', |
| 'bar .* 1 path.*', |
| ' log msg']) |
| svntest.actions.run_and_verify_svn(expected_out, [], |
| 'x-shelf-list') |
| |
| os.chdir(was_cwd) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def refuse_to_shelve_conflict(sbox): |
| "refuse to shelve conflict" |
| |
| sbox.build(empty=True) |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| |
| # create a tree conflict victim at an unversioned path |
| sbox.simple_mkdir('topdir') |
| sbox.simple_commit() |
| sbox.simple_mkdir('topdir/subdir') |
| sbox.simple_commit() |
| sbox.simple_update() |
| sbox.simple_rm('topdir') |
| sbox.simple_commit() |
| sbox.simple_update() |
| svntest.actions.run_and_verify_svn( |
| None, [], |
| 'merge', '-c2', '.', '--ignore-ancestry', '--accept', 'postpone') |
| svntest.actions.run_and_verify_svn( |
| None, 'svn: E155015:.*existing.*conflict.*', |
| 'merge', '-c1', '.', '--ignore-ancestry', '--accept', 'postpone') |
| |
| # attempt to shelve |
| expected_out = svntest.verify.RegexListOutput([ |
| r'--- .*', |
| r'--- .*', |
| r'\? C topdir', |
| r' > .*', |
| r' > not shelved']) |
| svntest.actions.run_and_verify_svn(expected_out, |
| '.* 1 path could not be shelved', |
| 'x-shelf-save', 'foo') |
| |
| os.chdir(was_cwd) |
| |
| #---------------------------------------------------------------------- |
| |
| def unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state): |
| """Run a test scenario in which 'unshelve' needs to merge some shelved |
| changes made by modifier1() with some committed changes made by |
| modifier2(). tweak_expected_state() must produce the expected WC state. |
| """ |
| sbox.build() |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| wc_dir = sbox.wc_dir |
| |
| setup(sbox) |
| sbox.simple_commit() |
| initial_state = get_wc_state(wc_dir) |
| |
| # Make some changes to the working copy |
| modifier1(sbox) |
| modified_state = get_wc_state(wc_dir) |
| |
| # Shelve; check there are no longer any modifications |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelve', 'foo') |
| check_wc_state(wc_dir, initial_state) |
| |
| # Make a different change, with which we shall merge |
| modifier2(sbox) |
| sbox.simple_commit() |
| modified_state[0].tweak('A/mu', wc_rev='3') |
| |
| # Unshelve; check the expected result of the merge |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-unshelve', 'foo') |
| tweak_expected_state(modified_state) |
| check_wc_state(wc_dir, modified_state) |
| |
| os.chdir(was_cwd) |
| |
| @SkipUnless(shelf2_enabled) |
| def unshelve_text_mod_merge(sbox): |
| "unshelve text mod merge" |
| |
| orig_contents='A\nB\nC\nD\nE\n' |
| mod1_contents='A\nBB\nC\nD\nE\n' |
| mod2_contents='A\nB\nC\nDD\nE\n' |
| merged_contents='A\nBB\nC\nDD\nE\n' |
| |
| def setup(sbox): |
| sbox.simple_append('A/mu', orig_contents, truncate=True) |
| |
| def modifier1(sbox): |
| sbox.simple_append('A/mu', mod1_contents, truncate=True) |
| |
| def modifier2(sbox): |
| sbox.simple_append('A/mu', mod2_contents, truncate=True) |
| |
| def tweak_expected_state(modified_state): |
| modified_state[1].tweak('A/mu', contents=merged_contents) |
| |
| unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def unshelve_text_mod_conflict(sbox): |
| "unshelve text mod conflict" |
| |
| orig_contents='A\nB\nC\nD\nE\n' |
| mod1_contents='A\nBB\nC\nD\nE\n' |
| mod2_contents='A\nBCD\nC\nD\nE\n' |
| merged_contents = 'A\n<<<<<<< .working\nBCD\n||||||| .merge-left\nB\n=======\nBB\n>>>>>>> .merge-right\nC\nD\nE\n' |
| |
| def setup(sbox): |
| sbox.simple_append('A/mu', orig_contents, truncate=True) |
| |
| def modifier1(sbox): |
| sbox.simple_append('A/mu', mod1_contents, truncate=True) |
| |
| def modifier2(sbox): |
| sbox.simple_append('A/mu', mod2_contents, truncate=True) |
| |
| def tweak_expected_state(modified_state): |
| modified_state[0].tweak('A/mu', status='C ') |
| modified_state[1].tweak('A/mu', contents=merged_contents) |
| modified_state[1].add({ |
| 'A/mu.merge-left': Item(contents=orig_contents), |
| 'A/mu.merge-right': Item(contents=mod1_contents), |
| 'A/mu.working': Item(contents=mod2_contents), |
| }) |
| |
| unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def unshelve_undeclared_binary_mod_conflict(sbox): |
| "unshelve undeclared binary mod conflict" |
| |
| orig_contents='\1\2\3\4\5' |
| mod1_contents='\1\2\2\3\4\5' |
| mod2_contents='\1\2\3\4\3\4\5' |
| merged_contents = '<<<<<<< .working\n' + mod2_contents + '||||||| .merge-left\n' + orig_contents + '=======\n' + mod1_contents + '>>>>>>> .merge-right\n' |
| |
| def setup(sbox): |
| sbox.simple_append('A/mu', orig_contents, truncate=True) |
| |
| def modifier1(sbox): |
| sbox.simple_append('A/mu', mod1_contents, truncate=True) |
| |
| def modifier2(sbox): |
| sbox.simple_append('A/mu', mod2_contents, truncate=True) |
| |
| def tweak_expected_state(modified_state): |
| modified_state[0].tweak('A/mu', status='C ') |
| modified_state[1].tweak('A/mu', contents=merged_contents) |
| modified_state[1].add({ |
| 'A/mu.merge-left': Item(contents=orig_contents), |
| 'A/mu.merge-right': Item(contents=mod1_contents), |
| 'A/mu.working': Item(contents=mod2_contents), |
| }) |
| |
| unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def unshelve_binary_mod_conflict(sbox): |
| "unshelve binary mod conflict" |
| |
| orig_contents='\1\2\3\4\5' |
| mod1_contents='\1\2\2\3\4\5' |
| mod2_contents='\1\2\3\4\3\4\5' |
| |
| def setup(sbox): |
| sbox.simple_append('A/mu', orig_contents, truncate=True) |
| sbox.simple_propset('svn:mime-type', 'application/octet-stream', 'A/mu') |
| |
| def modifier1(sbox): |
| sbox.simple_append('A/mu', mod1_contents, truncate=True) |
| |
| def modifier2(sbox): |
| sbox.simple_append('A/mu', mod2_contents, truncate=True) |
| |
| def tweak_expected_state(modified_state): |
| modified_state[0].tweak('A/mu', status='C ') |
| modified_state[1].tweak('A/mu', contents=mod2_contents) |
| modified_state[1].add({ |
| 'A/mu.merge-left': Item(contents=orig_contents), |
| 'A/mu.merge-right': Item(contents=mod1_contents), |
| }) |
| |
| unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def unshelve_text_prop_merge(sbox): |
| "unshelve text prop merge" |
| |
| def setup(sbox): |
| sbox.simple_propset('p1', 'v', 'A/mu') |
| sbox.simple_propset('p2', 'v', 'A/mu') |
| |
| def modifier1(sbox): |
| sbox.simple_propset('p1', 'changed', 'A/mu') |
| |
| def modifier2(sbox): |
| sbox.simple_propset('p2', 'changed', 'A/mu') |
| |
| def tweak_expected_state(wc_state): |
| wc_state[1].tweak('A/mu', props={'p1':'changed', |
| 'p2':'changed'}) |
| |
| unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state) |
| |
| #---------------------------------------------------------------------- |
| |
| @SkipUnless(shelf2_enabled) |
| def unshelve_text_prop_conflict(sbox): |
| "unshelve text prop conflict" |
| |
| orig_contents='A' |
| mod1_contents='B' |
| mod2_contents='C' |
| merged_contents='C' |
| prej_contents='''Trying to change property 'p' |
| but the local property value conflicts with the incoming change. |
| <<<<<<< (local property value) |
| C||||||| (incoming 'changed from' value) |
| A======= |
| B>>>>>>> (incoming 'changed to' value) |
| ''' |
| |
| def setup(sbox): |
| sbox.simple_propset('p', orig_contents, 'A/mu') |
| |
| def modifier1(sbox): |
| sbox.simple_propset('p', mod1_contents, 'A/mu') |
| |
| def modifier2(sbox): |
| sbox.simple_propset('p', mod2_contents, 'A/mu') |
| |
| def tweak_expected_state(wc_state): |
| wc_state[0].tweak('A/mu', status=' C') |
| wc_state[1].tweak('A/mu', props={'p':merged_contents}) |
| wc_state[1].add({ |
| 'A/mu.prej': Item(contents=prej_contents), |
| }) |
| |
| unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state) |
| |
| #---------------------------------------------------------------------- |
| |
| def run_and_verify_shelf_diff_summarize(output_tree, shelf, *args): |
| """Run 'svn shelf-diff --summarize' with the arguments *ARGS. |
| |
| The subcommand output will be verified against OUTPUT_TREE. Returns |
| on success, raises on failure. |
| """ |
| |
| if isinstance(output_tree, wc.State): |
| output_tree = output_tree.old_tree() |
| |
| exit_code, output, errput = svntest.actions.run_and_verify_svn( |
| None, [], |
| 'x-shelf-diff', '--summarize', shelf, *args) |
| |
| actual = svntest.tree.build_tree_from_diff_summarize(output) |
| |
| # Verify actual output against expected output. |
| try: |
| svntest.tree.compare_trees("output", actual, output_tree) |
| except svntest.tree.SVNTreeError: |
| svntest.verify.display_trees(None, 'DIFF OUTPUT TREE', output_tree, actual) |
| raise |
| |
| # Exercise a very basic case of shelf-diff. |
| @SkipUnless(shelf2_enabled) |
| def shelf_diff_simple(sbox): |
| "shelf diff simple" |
| |
| sbox.build() |
| was_cwd = os.getcwd() |
| os.chdir(sbox.wc_dir) |
| sbox.wc_dir = '' |
| wc_dir = sbox.wc_dir |
| |
| def setup(sbox): |
| sbox.simple_propset('p1', 'v', 'A/mu') |
| sbox.simple_propset('p2', 'v', 'A/mu') |
| |
| def modifier1(sbox): |
| sbox.simple_append('A/mu', 'New line.\n') |
| sbox.simple_propset('p1', 'changed', 'A/mu') |
| |
| setup(sbox) |
| sbox.simple_commit() |
| initial_state = get_wc_state(wc_dir) |
| |
| # Make some changes to the working copy |
| modifier1(sbox) |
| modified_state = get_wc_state(wc_dir) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'x-shelf-save', 'foo') |
| |
| # basic svn-style diff |
| expected_output = make_diff_header('A/mu', 'revision 2', 'working copy') + [ |
| "@@ -1 +1,2 @@\n", |
| " This is the file 'mu'.\n", |
| "+New line.\n", |
| ] + make_diff_prop_header('A/mu') \ |
| + make_diff_prop_modified('p1', 'v', 'changed') |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'x-shelf-diff', 'foo') |
| |
| # basic summary diff |
| expected_diff = svntest.wc.State(wc_dir, { |
| 'A/mu': Item(status='MM'), |
| }) |
| run_and_verify_shelf_diff_summarize(expected_diff, 'foo') |
| |
| |
| ######################################################################## |
| # Run the tests |
| |
| # list all tests here, starting with None: |
| test_list = [ None, |
| shelve_text_mods, |
| shelve_prop_changes, |
| shelve_adds, |
| shelve_deletes, |
| shelve_replace, |
| shelve_empty_adds, |
| shelve_empty_deletes, |
| shelve_from_inner_path, |
| checkpoint_basic, |
| shelve_mergeinfo, |
| unshelve_refuses_if_conflicts, |
| shelve_binary_file_mod, |
| shelve_binary_file_add, |
| shelve_binary_file_del, |
| shelve_binary_file_replace, |
| shelve_with_log_message, |
| shelf_status, |
| shelve_mkdir, |
| shelve_rmdir, |
| shelve_replace_dir, |
| shelve_file_copy, |
| shelve_dir_copy, |
| list_shelves, |
| refuse_to_shelve_conflict, |
| unshelve_text_mod_merge, |
| unshelve_text_mod_conflict, |
| unshelve_undeclared_binary_mod_conflict, |
| unshelve_binary_mod_conflict, |
| unshelve_text_prop_merge, |
| unshelve_text_prop_conflict, |
| shelf_diff_simple, |
| ] |
| |
| if __name__ == '__main__': |
| svntest.main.run_tests(test_list) |
| # NOTREACHED |
| |
| |
| ### End of file. |