| #!/usr/bin/env python |
| # |
| # stat_tests.py: testing the svn stat command |
| # |
| # 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 os |
| import re |
| import time |
| import datetime |
| import logging |
| |
| logger = logging.getLogger() |
| |
| # Our testing module |
| import svntest |
| from svntest import wc |
| |
| # (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 = svntest.wc.StateItem |
| UnorderedOutput = svntest.verify.UnorderedOutput |
| |
| |
| |
| ###################################################################### |
| # Tests |
| # |
| # Each test must return on success or raise on failure. |
| |
| #---------------------------------------------------------------------- |
| |
| def status_unversioned_file_in_current_dir(sbox): |
| "status on unversioned file in current directory" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| svntest.main.file_append('foo', 'a new file') |
| |
| svntest.actions.run_and_verify_svn([ "? foo\n" ], [], |
| 'stat', 'foo') |
| |
| #---------------------------------------------------------------------- |
| # Regression for issue #590 |
| @Issue(590) |
| def status_update_with_nested_adds(sbox): |
| "run 'status -u' when nested additions are pending" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Make a backup copy of the working copy |
| wc_backup = sbox.add_wc_path('backup') |
| svntest.actions.duplicate_dir(wc_dir, wc_backup) |
| |
| # Create newdir and newfile |
| newdir_path = sbox.ospath('newdir') |
| newfile_path = sbox.ospath('newdir/newfile') |
| os.makedirs(newdir_path) |
| svntest.main.file_append(newfile_path, 'new text') |
| |
| # Schedule newdir and newfile for addition (note that the add is recursive) |
| svntest.main.run_svn(None, 'add', newdir_path) |
| |
| # Created expected output tree for commit |
| expected_output = svntest.wc.State(wc_dir, { |
| 'newdir' : Item(verb='Adding'), |
| 'newdir/newfile' : Item(verb='Adding'), |
| }) |
| |
| # Create expected status tree; all local revisions should be at 1, |
| # but newdir and newfile should be at revision 2. |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.add({ |
| 'newdir' : Item(status=' ', wc_rev=2), |
| 'newdir/newfile' : Item(status=' ', wc_rev=2), |
| }) |
| |
| # Commit. |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status) |
| |
| # Now we go to the backup working copy, still at revision 1. |
| # We will run 'svn st -u', and make sure that newdir/newfile is reported |
| # as a nonexistent (but pending) path. |
| expected_status = svntest.actions.get_virginal_state(wc_backup, 1) |
| expected_status.add({ |
| 'newdir' : Item(status=' '), |
| 'newdir/newfile' : Item(status=' '), |
| }) |
| svntest.actions.run_and_verify_unquiet_status(wc_backup, |
| expected_status) |
| |
| # At one time an obstructing 'newdir' caused a SEGV on 'newdir/newfile' |
| os.makedirs(os.path.join(wc_backup, 'newdir')) |
| expected_status.tweak('newdir', status='? ') |
| svntest.actions.run_and_verify_unquiet_status(wc_backup, |
| expected_status) |
| |
| #---------------------------------------------------------------------- |
| |
| # svn status -vN should include all entries in a directory |
| def status_shows_all_in_current_dir(sbox): |
| "status -vN shows all items in current directory" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| exit_code, output, err = svntest.actions.run_and_verify_svn(None, [], |
| 'stat', '-vN') |
| |
| if (len(output) != len(os.listdir("."))): |
| raise svntest.Failure |
| |
| |
| #---------------------------------------------------------------------- |
| @Issue(2127) |
| def status_missing_file(sbox): |
| "status with a versioned file missing" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| os.remove('iota') |
| |
| exit_code, output, err = svntest.actions.run_and_verify_svn(None, [], |
| 'status') |
| for line in output: |
| if not re.match("! +iota", line): |
| raise svntest.Failure |
| |
| # This invocation is for issue #2127. |
| exit_code, output, err = svntest.actions.run_and_verify_svn(None, [], |
| 'status', '-u', |
| 'iota') |
| found_it = 0 |
| for line in output: |
| if re.match("! +1 +iota", line): |
| found_it = 1 |
| if not found_it: |
| raise svntest.Failure |
| |
| #---------------------------------------------------------------------- |
| |
| def status_type_change(sbox): |
| "status on versioned items whose type has changed" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| # First replace a versioned dir with a file and a versioned file |
| # with a versioned dir. |
| os.rename('iota', 'was_iota') |
| os.rename('A', 'iota') |
| os.rename('was_iota', 'A') |
| |
| expected_output = [ |
| '~ A\n', |
| '! A/mu\n', |
| '! A/B\n', |
| '! A/B/lambda\n', |
| '! A/B/E\n', |
| '! A/B/E/alpha\n', |
| '! A/B/E/beta\n', |
| '! A/B/F\n', |
| '! A/C\n', |
| '! A/D\n', |
| '! A/D/gamma\n', |
| '! A/D/G\n', |
| '! A/D/G/rho\n', |
| '! A/D/G/pi\n', |
| '! A/D/G/tau\n', |
| '! A/D/H\n', |
| '! A/D/H/chi\n', |
| '! A/D/H/omega\n', |
| '! A/D/H/psi\n', |
| '~ iota\n', |
| ] |
| |
| expected_output = [s.replace('/', os.path.sep) for s in expected_output] |
| |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), |
| [], 'status') |
| |
| # Now change the file that is obstructing the versioned dir into an |
| # unversioned dir. |
| os.remove('A') |
| os.mkdir('A') |
| |
| # A is a directory again, so it is no longer missing, but it's |
| # descendants are |
| expected_output = [ |
| '! A/mu\n', |
| '! A/B\n', |
| '! A/B/lambda\n', |
| '! A/B/E\n', |
| '! A/B/E/alpha\n', |
| '! A/B/E/beta\n', |
| '! A/B/F\n', |
| '! A/C\n', |
| '! A/D\n', |
| '! A/D/gamma\n', |
| '! A/D/G\n', |
| '! A/D/G/rho\n', |
| '! A/D/G/pi\n', |
| '! A/D/G/tau\n', |
| '! A/D/H\n', |
| '! A/D/H/chi\n', |
| '! A/D/H/omega\n', |
| '! A/D/H/psi\n', |
| '~ iota\n', |
| ] |
| # Fix separator for Windows |
| expected_output = [s.replace('/', os.path.sep) for s in expected_output] |
| |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), |
| [], 'status') |
| |
| # Now change the versioned dir that is obstructing the file into an |
| # unversioned dir. |
| svntest.main.safe_rmtree('iota') |
| os.mkdir('iota') |
| |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), |
| [], 'status') |
| |
| #---------------------------------------------------------------------- |
| @SkipUnless(svntest.main.is_posix_os) |
| def status_type_change_to_symlink(sbox): |
| "status on versioned items replaced by symlinks" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| # "broken" symlinks |
| os.remove('iota') |
| os.symlink('foo', 'iota') |
| svntest.main.safe_rmtree('A/D') |
| os.symlink('bar', 'A/D') |
| |
| expected_output = [ |
| '~ A/D\n', |
| '! A/D/gamma\n', |
| '! A/D/G\n', |
| '! A/D/G/rho\n', |
| '! A/D/G/pi\n', |
| '! A/D/G/tau\n', |
| '! A/D/H\n', |
| '! A/D/H/chi\n', |
| '! A/D/H/omega\n', |
| '! A/D/H/psi\n', |
| '~ iota\n', |
| ] |
| |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), |
| [], 'status') |
| |
| # "valid" symlinks |
| os.remove('iota') |
| os.remove('A/D') |
| os.symlink('A/mu', 'iota') |
| os.symlink('C', 'A/D') |
| |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), |
| [], 'status') |
| |
| #---------------------------------------------------------------------- |
| # Regression test for revision 3686. |
| |
| def status_with_new_files_pending(sbox): |
| "status -u with new files in the repository" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| svntest.main.file_append('newfile', 'this is a new file') |
| svntest.main.run_svn(None, 'add', 'newfile') |
| svntest.main.run_svn(None, |
| 'ci', '-m', 'logmsg') |
| svntest.main.run_svn(None, |
| 'up', '-r', '1') |
| |
| exit_code, output, err = svntest.actions.run_and_verify_svn(None, [], |
| 'status', '-u') |
| |
| # The bug fixed in revision 3686 was a segmentation fault. |
| # TODO: Check exit code. |
| # In the meantime, no output means there was a problem. |
| for line in output: |
| if line.find('newfile') != -1: |
| break |
| else: |
| raise svntest.Failure |
| |
| #---------------------------------------------------------------------- |
| |
| def status_for_unignored_file(sbox): |
| "status for unignored file and directory" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| # use a temp file to set properties with wildcards in their values |
| # otherwise Win32/VS2005 will expand them |
| svntest.main.file_append('proptmp', 'new*') |
| svntest.main.file_append('newfile', 'this is a new file') |
| os.makedirs('newdir') |
| svntest.main.run_svn(None, 'propset', 'svn:ignore', '-F', 'proptmp', '.') |
| os.remove('proptmp') |
| |
| # status on the directory with --no-ignore |
| expected = svntest.verify.UnorderedOutput( |
| ['I newdir\n', |
| 'I newfile\n', |
| ' M .\n']) |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| 'status', '--no-ignore', '.') |
| |
| # status specifying the file explicitly on the command line |
| expected = svntest.verify.UnorderedOutput( |
| ['I newdir\n', |
| 'I newfile\n']) |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| 'status', 'newdir', 'newfile') |
| |
| #---------------------------------------------------------------------- |
| |
| def status_for_nonexistent_file(sbox): |
| "status on missing and unversioned file" |
| |
| sbox.build(read_only = True) |
| |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| exit_code, output, err = svntest.actions.run_and_verify_svn( |
| None, [], 'status', 'nonexistent-file') |
| |
| # there should *not* be a status line printed for the nonexistent file |
| for line in output: |
| if re.match(" +nonexistent-file", line): |
| raise svntest.Failure |
| |
| #---------------------------------------------------------------------- |
| |
| def status_nonrecursive_update_different_cwd(sbox): |
| "status -v -N -u from different current directories" |
| |
| # check combination of status -u and -N |
| # create A/C/J in repository |
| # create A/C/K in working copy |
| # check status output with -u and -N on target C |
| # check status output with -u and -N on target . (in C) |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| J_url = sbox.repo_url + '/A/C/J' |
| K_path = os.path.join(wc_dir, 'A', 'C', 'K' ) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'mkdir', '-m', 'rev 2', J_url) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'mkdir', K_path) |
| |
| os.chdir(wc_dir) |
| |
| expected_output = [ |
| ' * %s\n' % os.path.join("C", "J"), |
| 'A - ? ? %s\n' % os.path.join("C", "K"), |
| ' * 1 1 jrandom C\n', |
| 'Status against revision: 2\n' ] |
| |
| os.chdir('A') |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), |
| [], |
| 'status', '-v', '-N', '-u', 'C') |
| |
| expected_output = [ |
| ' * J\n', |
| 'A - ? ? K\n', |
| ' * 1 1 jrandom .\n', |
| 'Status against revision: 2\n'] |
| |
| os.chdir('C') |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected_output), |
| [], |
| 'status', '-v', '-N', '-u', '.') |
| |
| |
| #---------------------------------------------------------------------- |
| |
| def status_file_needs_update(sbox): |
| "status -u indicates out-of-dateness" |
| |
| # See this thread: |
| # |
| # http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=27975 |
| # |
| # Basically, Andreas was seeing inconsistent results depending on |
| # whether or not he accompanied 'svn status -u' with '-v': |
| # |
| # % svn st -u |
| # Head revision: 67 |
| # % |
| # |
| # ...and yet... |
| # |
| # % svn st -u -v |
| # 56 6 k cron-daily.pl |
| # * 56 44 k crontab.root |
| # 56 6 k gmls-lR.pl |
| # Head revision: 67 |
| # % |
| # |
| # The first status should show the asterisk, too. There was never |
| # any issue for this bug, so this comment and the thread are your |
| # audit trail :-). |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| other_wc = sbox.add_wc_path('other') |
| |
| svntest.actions.duplicate_dir(wc_dir, other_wc) |
| |
| was_cwd = os.getcwd() |
| |
| os.chdir(wc_dir) |
| svntest.main.file_append('crontab.root', 'New file crontab.root.\n') |
| svntest.main.run_svn(None, 'add', 'crontab.root') |
| svntest.main.run_svn(None, |
| 'ci', '-m', 'log msg') |
| os.chdir(was_cwd) |
| os.chdir(other_wc) |
| svntest.main.run_svn(None, |
| 'up') |
| |
| os.chdir(was_cwd) |
| os.chdir(wc_dir) |
| svntest.main.file_append('crontab.root', 'New line in crontab.root.\n') |
| svntest.main.run_svn(None, |
| 'ci', '-m', 'log msg') |
| |
| # The `svntest.actions.run_and_verify_*_status' routines all pass |
| # the -v flag, which we don't want, as this bug never appeared when |
| # -v was passed. So we run status by hand: |
| os.chdir(was_cwd) |
| exit_code, out, err = svntest.actions.run_and_verify_svn(None, [], |
| 'status', '-u', |
| other_wc) |
| |
| for line in out: |
| if re.match("\\s+\\*.*crontab\\.root$", line): |
| break |
| else: |
| raise svntest.Failure |
| |
| #---------------------------------------------------------------------- |
| |
| def status_uninvited_parent_directory(sbox): |
| "status -u on outdated, added file shows only that" |
| |
| # To reproduce, check out working copies wc1 and wc2, then do: |
| # |
| # $ cd wc1 |
| # $ echo "new file" >> newfile |
| # $ svn add newfile |
| # $ svn ci -m 'log msg' |
| # |
| # $ cd ../wc2 |
| # $ echo "new file" >> newfile |
| # $ svn add newfile |
| # |
| # $ cd .. |
| # $ svn st wc2/newfile |
| # |
| # You *should* get one line of status output, for newfile. The bug |
| # is that you get two instead, one for newfile, and one for its |
| # parent directory, wc2/. |
| # |
| # This bug was originally discovered during investigations into |
| # issue #1042, "fixed" in revision 4181, then later the fix was |
| # reverted because it caused other status problems (see the test |
| # status_file_needs_update(), which fails when 4181 is present). |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| other_wc = sbox.add_wc_path('other') |
| |
| svntest.actions.duplicate_dir(wc_dir, other_wc) |
| |
| was_cwd = os.getcwd() |
| |
| os.chdir(wc_dir) |
| svntest.main.file_append('newfile', 'New file.\n') |
| svntest.main.run_svn(None, 'add', 'newfile') |
| svntest.main.run_svn(None, |
| 'ci', '-m', 'log msg') |
| |
| os.chdir(was_cwd) |
| os.chdir(other_wc) |
| svntest.main.file_append('newfile', 'New file.\n') |
| svntest.main.run_svn(None, 'add', 'newfile') |
| |
| os.chdir(was_cwd) |
| |
| # We don't want a full status tree here, just one line (or two, if |
| # the bug is present). So run status by hand: |
| os.chdir(was_cwd) |
| exit_code, out, err = svntest.actions.run_and_verify_svn( |
| None, [], |
| 'status', '-u', os.path.join(other_wc, 'newfile')) |
| |
| for line in out: |
| # The "/?" is just to allow for an optional trailing slash. |
| if re.match("\\s+\\*.*\.other/?$", line): |
| raise svntest.Failure |
| |
| @Issue(1289) |
| def status_on_forward_deletion(sbox): |
| "status -u on working copy deleted in HEAD" |
| # See issue #1289. |
| sbox.build(create_wc = False) |
| wc_dir = sbox.wc_dir |
| |
| top_url = sbox.repo_url |
| A_url = top_url + '/A' |
| |
| svntest.main.run_svn(None, |
| 'rm', '-m', 'Remove A.', A_url) |
| |
| svntest.main.safe_rmtree(wc_dir) |
| os.mkdir(wc_dir) |
| |
| os.chdir(wc_dir) |
| |
| svntest.main.run_svn(None, |
| 'co', '-r1', top_url + "@1", 'wc') |
| # If the bug is present, this will error with |
| # |
| # subversion/libsvn_wc/lock.c:513: (apr_err=155005) |
| # svn: Working copy not locked |
| # svn: directory '' not locked |
| # |
| svntest.actions.run_and_verify_svn(None, [], 'st', '-u', 'wc') |
| |
| # Try again another way; the error would look like this: |
| # |
| # subversion/libsvn_repos/delta.c:207: (apr_err=160005) |
| # svn: Invalid filesystem path syntax |
| # svn: svn_repos_dir_delta: invalid editor anchoring; at least \ |
| # one of the input paths is not a directory and there was \ |
| # no source entry. |
| # |
| # (Dang! Hope a user never has to see that :-) ). |
| # |
| svntest.main.safe_rmtree('wc') |
| svntest.main.run_svn(None, |
| 'co', '-r1', A_url + "@1", 'wc') |
| svntest.actions.run_and_verify_svn(None, [], 'st', '-u', 'wc') |
| |
| #---------------------------------------------------------------------- |
| |
| def get_last_changed_date(path): |
| "get the Last Changed Date for path using svn info" |
| exit_code, out, err = svntest.actions.run_and_verify_svn(None, [], |
| 'info', path) |
| for line in out: |
| if re.match("^Last Changed Date", line): |
| return line |
| logger.warn("Didn't find Last Changed Date for %s", path) |
| raise svntest.Failure |
| |
| # Helper for timestamp_behaviour test |
| def get_text_timestamp(path): |
| "get the text-time for path using svn info" |
| exit_code, out, err = svntest.actions.run_and_verify_svn(None, [], |
| 'info', path) |
| for line in out: |
| if re.match("^Text Last Updated", line): |
| return line |
| logger.warn("Didn't find text-time for %s", path) |
| raise svntest.Failure("didn't find text-time") |
| |
| def no_text_timestamp(path): |
| "ensure no text-time for path using svn info" |
| exit_code, out, err = svntest.actions.run_and_verify_svn(None, [], |
| 'info', path) |
| for line in out: |
| if re.match("^Text Last Updated", line): |
| logger.warn("Found text-time for %s", path) |
| raise svntest.Failure("found text-time") |
| |
| # Helper for timestamp_behaviour test |
| def text_time_behaviour(wc_dir, wc_path, status_path, expected_status, cmd): |
| "text-time behaviour" |
| |
| # Pristine text and text-time |
| fp = open(wc_path, 'rb') |
| pre_text = fp.readlines() |
| pre_text_time = get_text_timestamp(wc_path) |
| |
| # Modifying the text does not affect text-time |
| svntest.main.file_append(wc_path, "some mod") |
| expected_status.tweak(status_path, status='M ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| text_time = get_text_timestamp(wc_path) |
| if text_time != pre_text_time: |
| raise svntest.Failure |
| |
| # Manually reverting the text does not affect the text-time |
| fp = open(wc_path, 'wb') |
| fp.writelines(pre_text) |
| fp.close() |
| expected_status.tweak(status_path, status=' ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| text_time = get_text_timestamp(wc_path) |
| if text_time != pre_text_time: |
| raise svntest.Failure |
| |
| # revert/cleanup change the text-time even though the text doesn't change |
| if cmd == 'cleanup': |
| svntest.actions.run_and_verify_svn(None, [], cmd, wc_dir) |
| else: |
| svntest.actions.run_and_verify_svn(None, [], cmd, wc_path) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| text_time = get_text_timestamp(wc_path) |
| if text_time == pre_text_time: |
| raise svntest.Failure |
| |
| # Is this really a status test? I'm not sure, but I don't know where |
| # else to put it. |
| @Issue(3773) |
| def timestamp_behaviour(sbox): |
| "timestamp behaviour" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| A_path = sbox.ospath('A') |
| iota_path = sbox.ospath('iota') |
| |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # Sleep to ensure timestamps change |
| time.sleep(1.1) |
| |
| # Check behaviour of revert on text-time |
| text_time_behaviour(wc_dir, iota_path, 'iota', expected_status, 'revert') |
| |
| # Sleep to ensure timestamps change |
| time.sleep(1.1) |
| |
| # Check behaviour of cleanup on text-time |
| text_time_behaviour(wc_dir, iota_path, 'iota', expected_status, 'cleanup') |
| |
| # Create a config to enable use-commit-times |
| config_contents = '''\ |
| [auth] |
| password-stores = |
| |
| [miscellany] |
| use-commit-times = yes |
| ''' |
| config_dir = sbox.create_config_dir(config_contents) |
| |
| other_wc = sbox.add_wc_path('other') |
| svntest.actions.run_and_verify_svn(None, [], |
| 'co', sbox.repo_url, |
| other_wc, |
| '--config-dir', config_dir) |
| |
| other_iota_path = os.path.join(other_wc, 'iota') |
| iota_text_timestamp = get_text_timestamp(other_iota_path) |
| iota_last_changed = get_last_changed_date(other_iota_path) |
| if (iota_text_timestamp[17] != ':' or |
| iota_text_timestamp[17:] != iota_last_changed[17:]): |
| raise svntest.Failure |
| |
| # remove iota, run an update to restore it, and check the times |
| os.remove(other_iota_path) |
| expected_output = svntest.wc.State(other_wc, { |
| 'iota': Item(verb='Restored'), |
| }) |
| expected_disk = svntest.main.greek_state.copy() |
| expected_status = svntest.actions.get_virginal_state(other_wc, 1) |
| svntest.actions.run_and_verify_update(other_wc, expected_output, |
| expected_disk, expected_status, |
| [], False, |
| other_wc, '--config-dir', config_dir) |
| iota_text_timestamp = get_text_timestamp(other_iota_path) |
| if (iota_text_timestamp[17] != ':' or |
| iota_text_timestamp[17:] != iota_last_changed[17:]): |
| raise svntest.Failure |
| |
| iota_ts = iota_text_timestamp[19:44] |
| |
| class TZ(datetime.tzinfo): |
| "A tzinfo to convert a time to iota's timezone." |
| def utcoffset(self, dt): |
| offset = (int(iota_ts[21:23]) * 60 + int(iota_ts[23:25])) |
| if iota_ts[20] == '-': |
| return datetime.timedelta(minutes=-offset) |
| return datetime.timedelta(minutes=offset) |
| def dst(self, dt): |
| return datetime.timedelta(0) |
| |
| # get the timestamp on the file. whack any microseconds value, as svn |
| # doesn't record to that precision. we also use the TZ class to shift |
| # the timestamp into the same timezone as the expected timestamp. |
| mtime = datetime.datetime.fromtimestamp(os.path.getmtime(other_iota_path), |
| TZ()).replace(microsecond=0) |
| fmt = mtime.isoformat(' ') |
| |
| # iota_ts looks like: 2009-04-13 14:30:57 +0200 |
| # fmt looks like: 2009-04-13 14:30:57+02:00 |
| if (fmt[:19] != iota_ts[:19] |
| or fmt[19:22] != iota_ts[20:23] |
| or fmt[23:25] != iota_ts[23:25]): |
| # NOTE: the two strings below won't *exactly* match (see just above), |
| # but the *numeric* portions of them should. |
| logger.warn("File timestamp on 'iota' does not match.") |
| logger.warn(" EXPECTED: %s", iota_ts) |
| logger.warn(" ACTUAL: %s", fmt) |
| raise svntest.Failure |
| |
| #---------------------------------------------------------------------- |
| |
| @Issues(1617,2030) |
| def status_on_unversioned_dotdot(sbox): |
| "status on '..' where '..' is not versioned" |
| # See issue #1617 (and #2030). |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| new_dir = sbox.ospath('new') |
| new_sub = sbox.ospath('new/sub') |
| new_subsub = sbox.ospath('new/sub/sub') |
| os.mkdir(new_dir) |
| os.mkdir(new_sub) |
| os.mkdir(new_subsub) |
| |
| os.chdir(new_subsub) |
| svntest.actions.run_and_verify_svn2(None, |
| "svn: warning: W155(010|007):.*'.*'.*not", |
| 0, 'st', '..') |
| |
| #---------------------------------------------------------------------- |
| |
| @Issue(2122) |
| def status_on_partially_nonrecursive_wc(sbox): |
| "status -u in partially non-recursive wc" |
| # Based on issue #2122. |
| # |
| # $ svn co -N -r 213 svn://svn.debian.org/pkg-kde . |
| # A README |
| # Checked out revision 213. |
| # |
| # $ svn up -r 213 scripts www |
| # [ List of scripts/* files.] |
| # Updated to revision 213. |
| # [ List of www/* files.] |
| # Updated to revision 213. |
| # |
| # $ svn st -u |
| # * 213 www/IGNORE-ME |
| # * 213 www |
| # svn: subversion/libsvn_wc/status.c:910: tweak_statushash: \ |
| # Assertion `repos_text_status == svn_wc_status_added' failed. \ |
| # Aborted (core dumped) |
| # |
| # You might think that the intermediate "svn up -r 213 scripts www" |
| # step is unnecessary, but when I tried eliminating it, I got |
| # |
| # $ svn st -u |
| # subversion/libsvn_wc/lock.c:642: (apr_err=155005) |
| # svn: Working copy 'www' not locked |
| # $ |
| # |
| # instead of the assertion error. |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| top_url = sbox.repo_url |
| A_url = top_url + '/A' |
| D_url = top_url + '/A/D' |
| G_url = top_url + '/A/D/G' |
| H_url = top_url + '/A/D/H' |
| rho = sbox.ospath('A/D/G/rho') |
| |
| # Commit a change to A/D/G/rho. This will be our equivalent of |
| # whatever change it was that happened between r213 and HEAD in the |
| # reproduction recipe. For us, it's r2. |
| svntest.main.file_append(rho, 'Whan that Aprille with his shoores soote\n') |
| svntest.main.run_svn(None, |
| 'ci', '-m', 'log msg', rho) |
| |
| # Make the working copy weird in the right way, then try status -u. |
| D_wc = sbox.add_wc_path('D') |
| svntest.main.run_svn(None, |
| 'co', '-r1', '-N', D_url, D_wc) |
| |
| os.chdir(D_wc) |
| svntest.main.run_svn(None, |
| 'up', '-r1', 'H') |
| svntest.main.run_svn(None, |
| 'st', '-u') |
| |
| |
| def missing_dir_in_anchor(sbox): |
| "a missing dir in the anchor" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| foo_path = sbox.ospath('foo') |
| svntest.actions.run_and_verify_svn(None, [], 'mkdir', foo_path) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.add({ |
| 'foo' : Item(status='A ', wc_rev=0), |
| }) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # At one point this caused a "foo not locked" error |
| svntest.main.safe_rmtree(foo_path) |
| expected_status.tweak('foo', status='! ', entry_status='A ', |
| wc_rev='-', entry_rev='0') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| |
| def status_in_xml(sbox): |
| "status output in XML format" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| file_name = "iota" |
| file_path = os.path.join(wc_dir, file_name) |
| svntest.main.file_append(file_path, "test status --xml\n") |
| |
| # Retrieve last changed date from svn log |
| exit_code, output, error = svntest.actions.run_and_verify_svn( |
| None, [], 'log', file_path, '--xml', '-rHEAD') |
| |
| info_msg = "<date>" |
| for line in output: |
| if line.find(info_msg) >= 0: |
| time_str = line[:len(line)] |
| break |
| else: |
| raise svntest.Failure |
| |
| expected_entries = {file_path : {'wcprops' : 'none', |
| 'wcitem' : 'modified', |
| 'wcrev' : '1', |
| 'crev' : '1', |
| 'author' : svntest.main.wc_author}} |
| |
| svntest.actions.run_and_verify_status_xml(expected_entries, file_path, '-u') |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'cp', '-m', 'repo-to-repo copy', |
| sbox.repo_url + '/iota', |
| sbox.repo_url + '/iota2') |
| |
| file_path = sbox.ospath('iota2') |
| |
| expected_entries = {file_path : {'wcprops' : 'none', |
| 'wcitem' : 'none', |
| 'rprops' : 'none', |
| 'ritem' : 'added'}} |
| |
| svntest.actions.run_and_verify_status_xml(expected_entries, file_path, '-u') |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'rm', '-m', 'repo delete', |
| sbox.repo_url + '/A/B/E/alpha') |
| |
| expected_entries = {sbox.ospath('A/B/E/alpha') |
| : {'wcprops' : 'none', |
| 'wcitem' : 'normal', |
| 'wcrev' : '1', |
| 'crev' : '1', |
| 'author' : svntest.main.wc_author, |
| 'rprops' : 'none', |
| 'ritem' : 'deleted'}} |
| |
| svntest.actions.run_and_verify_status_xml(expected_entries, |
| sbox.ospath('A/B/E/alpha'), '-u') |
| |
| #---------------------------------------------------------------------- |
| |
| def status_ignored_dir(sbox): |
| "status on ignored directory" |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| new_dir = sbox.ospath('dir.o') |
| new_dir_url = sbox.repo_url + "/dir.o" |
| |
| svntest.actions.run_and_verify_svn(["Committing transaction...\n", |
| "Committed revision 2.\n"], [], |
| 'mkdir', new_dir_url, '-m', 'msg') |
| |
| # Make a dir that is ignored by the default ignore patterns. |
| os.mkdir(new_dir) |
| |
| # run_and_verify_status doesn't handle this weird kind of entry. |
| svntest.actions.run_and_verify_svn(['I * ' + new_dir + "\n", |
| ' * 1 ' + wc_dir + "\n", |
| 'Status against revision: 2\n'], [], |
| "status", "-u", wc_dir) |
| |
| #---------------------------------------------------------------------- |
| |
| def status_unversioned_dir_in_wc(sbox): |
| "status on unversioned dir in working copy" |
| sbox.build(read_only = True) |
| |
| # Create two unversioned directories within the test working copy |
| path = sbox.ospath('1/2') |
| os.makedirs(path) |
| |
| expected_err = "svn: warning: (W155007|W155010): .*'.*(/|\\\\)" + \ |
| os.path.basename(path) + \ |
| "' was not found" |
| svntest.actions.run_and_verify_svn2([], expected_err, 0, |
| "status", path) |
| |
| #---------------------------------------------------------------------- |
| |
| def status_missing_dir(sbox): |
| "status with a versioned directory missing" |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| a_d_g = sbox.ospath('A/D/G') |
| |
| # ok, blow away the A/D/G directory |
| svntest.main.safe_rmtree(a_d_g) |
| |
| expected = [ |
| '! A/D/G\n', |
| '! A/D/G/rho\n', |
| '! A/D/G/pi\n', |
| '! A/D/G/tau\n', |
| ] |
| expected = [ s.replace('A/D/G', a_d_g).replace('/', os.path.sep) |
| for s in expected ] |
| |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected), [], |
| "status", wc_dir) |
| |
| expected = [ |
| "! 1 " + a_d_g + "\n", |
| "! 1 " + os.path.join(a_d_g, "rho") + "\n", |
| "! 1 " + os.path.join(a_d_g, "pi") + "\n", |
| "! 1 " + os.path.join(a_d_g, "tau") + "\n", |
| "Status against revision: 1\n" |
| ] |
| |
| # now run status -u, we should be able to do this without crashing |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected), [], |
| "status", "-u", wc_dir) |
| |
| # Finally run an explicit status request directly on the missing directory. |
| expected = [ |
| "! A/D/G\n", |
| "! A/D/G/rho\n", |
| "! A/D/G/pi\n", |
| "! A/D/G/tau\n", |
| ] |
| expected = [ s.replace('A/D/G', a_d_g).replace('/', os.path.sep) |
| for s in expected ] |
| |
| svntest.actions.run_and_verify_svn(UnorderedOutput(expected), [], |
| "status", a_d_g) |
| |
| def status_add_plus_conflict(sbox): |
| "status on conflicted added file" |
| sbox.build() |
| svntest.actions.do_sleep_for_timestamps() |
| |
| wc_dir = sbox.wc_dir |
| |
| branch_url = sbox.repo_url + '/branch' |
| trunk_url = sbox.repo_url + '/trunk' |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'mkdir', '-m', 'rev 2', |
| branch_url, trunk_url) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'update', wc_dir) |
| |
| branch_file = sbox.ospath('branch/file') |
| |
| svntest.main.file_write(branch_file, "line 1\nline2\nline3\n", 'wb+') |
| |
| svntest.actions.run_and_verify_svn(None, [], 'add', branch_file) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'commit', |
| branch_file, '-m', 'rev 3') |
| |
| svntest.main.file_write(branch_file, "line 1\nline3\n", 'wb') |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'commit', |
| branch_file, '-m', 'rev 4') |
| |
| svntest.main.file_write(branch_file, "line 1\nline2\n", 'wb') |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'commit', |
| branch_file, '-m', 'rev 5') |
| |
| trunk_dir = sbox.ospath('trunk') |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'merge', |
| branch_url, '-r', '2:3', trunk_dir) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'merge', |
| branch_url, '-r', '4:5', trunk_dir) |
| |
| lines = [ |
| "? " + sbox.ospath('trunk/file.merge-left.r4') + "\n", |
| "? " + sbox.ospath('trunk/file.merge-right.r5') + "\n", |
| "? " + sbox.ospath('trunk/file.working') + "\n", |
| "C + " + sbox.ospath('trunk/file') + "\n", |
| ] + svntest.main.summary_of_conflicts(text_conflicts=1) |
| if svntest.main.server_has_mergeinfo(): |
| lines.append(" M " + sbox.ospath('trunk') + "\n") |
| |
| expected_output = svntest.verify.UnorderedOutput(lines) |
| |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'status', wc_dir) |
| |
| #---------------------------------------------------------------------- |
| |
| def inconsistent_eol(sbox): |
| "status with inconsistent eol style" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| iota_path = sbox.ospath('iota') |
| |
| svntest.main.file_write(iota_path, "line 1\nline 2\n", "wb") |
| |
| svntest.actions.run_and_verify_svn("property 'svn:eol-style' set on.*iota", |
| [], |
| 'propset', 'svn:eol-style', 'native', |
| sbox.ospath('iota')) |
| |
| expected_output = svntest.wc.State(wc_dir, { |
| 'iota' : Item(verb='Sending'), |
| }) |
| |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('iota', wc_rev=2) |
| |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status) |
| |
| # Make the eol style inconsistent and verify that status says nothing. |
| svntest.main.file_write(iota_path, "line 1\nline 2\r\n", "wb") |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #2533 |
| @Issue(2533) |
| def status_update_with_incoming_props(sbox): |
| "run 'status -u' variations w/ incoming propchanges" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = sbox.ospath('A') |
| |
| # Add a property to the root folder and a subdir |
| svntest.main.run_svn(None, 'propset', 'red', 'rojo', wc_dir) |
| svntest.main.run_svn(None, 'propset', 'black', 'bobo', A_path) |
| |
| # Create expected output tree. |
| expected_output = svntest.wc.State(wc_dir, { |
| '' : Item(verb='Sending'), |
| 'A' : Item(verb='Sending'), |
| }) |
| |
| # Created expected status tree. |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('', wc_rev=2, status=' ') |
| expected_status.tweak('A', wc_rev=2, status=' ') |
| |
| # Commit the working copy |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status) |
| |
| # Create expected trees for an update to revision 1. |
| expected_output = svntest.wc.State(wc_dir, { |
| '' : Item(status=' U'), |
| 'A' : Item(status=' U'), |
| }) |
| expected_disk = svntest.main.greek_state.copy() |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| |
| # Do the update and check the results in three ways... INCLUDING PROPS |
| svntest.actions.run_and_verify_update(wc_dir, |
| expected_output, |
| expected_disk, |
| expected_status, |
| [], True, |
| '-r', '1', wc_dir) |
| |
| # Can't use run_and_verify_status here because the out-of-date |
| # information in the status output isn't copied in the status tree. |
| expected = svntest.verify.UnorderedOutput( |
| [" * 1 " + A_path + "\n", |
| " * 1 " + wc_dir + "\n", |
| "Status against revision: 2\n" ]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", |
| wc_dir) |
| |
| expected = svntest.verify.UnorderedOutput( |
| [" 1 1 jrandom " + |
| sbox.ospath('iota') + "\n", |
| " * 1 1 jrandom " + A_path + "\n", |
| " * 1 1 jrandom " + wc_dir + "\n", |
| "Status against revision: 2\n" ]) |
| |
| svntest.actions.run_and_verify_svn(expected, [], |
| "status", "-uvN", |
| wc_dir) |
| |
| # Retrieve last changed date from svn log |
| exit_code, output, error = svntest.actions.run_and_verify_svn(None, [], |
| 'log', wc_dir, |
| '--xml', '-r1') |
| |
| info_msg = "<date>" |
| for line in output: |
| if line.find(info_msg) >= 0: |
| time_str = line[:len(line)] |
| break |
| else: |
| raise svntest.Failure |
| |
| expected_entries ={wc_dir : {'wcprops' : 'none', |
| 'wcitem' : 'normal', |
| 'wcrev' : '1', |
| 'crev' : '1', |
| 'author' : svntest.main.wc_author, |
| 'rprops' : 'modified', |
| 'ritem' : 'none'}, |
| A_path : {'wcprops' : 'none', |
| 'wcitem' : 'normal', |
| 'wcrev' : '1', |
| 'crev' : '1', |
| 'author' : svntest.main.wc_author, |
| 'rprops' : 'modified', |
| 'ritem' : 'none'}, |
| } |
| |
| svntest.actions.run_and_verify_status_xml(expected_entries, wc_dir, '-uN') |
| |
| # more incoming prop updates. |
| def status_update_verbose_with_incoming_props(sbox): |
| "run 'status -uv' w/ incoming propchanges" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = sbox.ospath('A') |
| D_path = os.path.join(A_path, 'D') |
| B_path = os.path.join(A_path, 'B') |
| E_path = os.path.join(A_path, 'B', 'E') |
| G_path = os.path.join(A_path, 'D', 'G') |
| H_path = os.path.join(A_path, 'D', 'H') |
| # Add a property to the root folder and a subdir |
| svntest.main.run_svn(None, 'propset', 'red', 'rojo', D_path) |
| svntest.main.run_svn(None, 'propset', 'black', 'bobo', E_path) |
| svntest.main.run_svn(None, 'propset', 'black', 'bobo', wc_dir) |
| |
| # Create expected output tree. |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/D' : Item(verb='Sending'), |
| 'A/B/E' : Item(verb='Sending'), |
| '' : Item(verb='Sending'), |
| }) |
| # Created expected status tree. |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/D', wc_rev=2, status=' ') |
| expected_status.tweak('A/B/E', wc_rev=2, status=' ') |
| expected_status.tweak('', wc_rev=2, status=' ') |
| |
| # Commit the working copy |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status) |
| |
| # Create expected trees for an update to revision 1. |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/D' : Item(status=' U'), |
| 'A/B/E' : Item(status=' U'), |
| '' : Item(status=' U'), |
| }) |
| expected_disk = svntest.main.greek_state.copy() |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| |
| # Do the update and check the results in three ways... INCLUDING PROPS |
| svntest.actions.run_and_verify_update(wc_dir, |
| expected_output, |
| expected_disk, |
| expected_status, |
| [], True, |
| '-r', '1', wc_dir) |
| |
| # Can't use run_and_verify_status here because the out-of-date |
| # information in the status output isn't copied in the status tree. |
| common = " 1 1 jrandom " |
| expected = svntest.verify.UnorderedOutput( |
| [" " + common + os.path.join(E_path, 'alpha') + "\n", |
| " " + common + os.path.join(E_path, 'beta') + "\n", |
| " *" + common + os.path.join(E_path) + "\n", |
| " " + common + os.path.join(B_path, 'lambda') + "\n", |
| " " + common + os.path.join(B_path, 'F') + "\n", |
| " " + common + B_path + "\n", |
| " " + common + os.path.join(G_path, 'pi') + "\n", |
| " " + common + os.path.join(G_path, 'rho') + "\n", |
| " " + common + os.path.join(G_path, 'tau') + "\n", |
| " " + common + G_path + "\n", |
| " " + common + os.path.join(H_path, 'chi') + "\n", |
| " " + common + os.path.join(H_path, 'omega') + "\n", |
| " " + common + os.path.join(H_path, 'psi') + "\n", |
| " " + common + H_path + "\n", |
| " " + common + os.path.join(D_path, 'gamma') + "\n", |
| " *" + common + D_path + "\n", |
| " " + common + os.path.join(A_path, 'mu') + "\n", |
| " " + common + os.path.join(A_path, 'C') + "\n", |
| " " + common + A_path + "\n", |
| " " + common + sbox.ospath('iota') + "\n", |
| " *" + common + wc_dir + "\n", |
| "Status against revision: 2\n" ]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-uv", wc_dir) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #2468 |
| @Issue(2468) |
| def status_nonrecursive_update(sbox): |
| "run 'status -uN' with incoming changes" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = sbox.ospath('A') |
| D_path = os.path.join(A_path, 'D') |
| mu_path = os.path.join(A_path, 'mu') |
| gamma_path = os.path.join(D_path, 'gamma') |
| |
| # Change files in A and D and commit |
| svntest.main.file_append(mu_path, "new line of text") |
| svntest.main.file_append(gamma_path, "new line of text") |
| |
| # Create expected trees for commit |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/mu' : Item(verb='Sending'), |
| 'A/D/gamma' : Item(verb='Sending') |
| }) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/mu', wc_rev=2, status=' ') |
| expected_status.tweak('A/D/gamma', wc_rev=2, status=' ') |
| |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status) |
| |
| # Create expected trees for an update to revision 1. |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/mu' : Item(status='U '), |
| 'A/D/gamma' : Item(status='U '), |
| }) |
| expected_disk = svntest.main.greek_state.copy() |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| |
| # Do the update and check the results in three ways |
| svntest.actions.run_and_verify_update(wc_dir, |
| expected_output, |
| expected_disk, |
| expected_status, |
| [], False, |
| '-r', '1', wc_dir) |
| |
| # Check the remote status of folder A (non-recursively) |
| xout = [" * 1 " + sbox.ospath('A/mu') + "\n", |
| "Status against revision: 2\n" ] |
| |
| svntest.actions.run_and_verify_svn(xout, |
| [], |
| "status", "-uN", A_path) |
| |
| def change_files(wc_dir, files): |
| """Make a basic change to the files. |
| files = a list of paths relative to the wc root directory |
| """ |
| |
| for file in files: |
| filepath = os.path.join(wc_dir, file) |
| svntest.main.file_append(filepath, "new line of text") |
| |
| def change_files_and_commit(wc_dir, files, baserev=1): |
| """Make a basic change to the files and commit them. |
| files = a list of paths relative to the wc root directory |
| """ |
| |
| change_files(wc_dir, files) |
| |
| # Prepare expected trees for commit |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/mu' : Item(verb='Sending'), |
| 'A/D/gamma' : Item(verb='Sending') |
| }) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| |
| commitrev = baserev + 1 |
| for file in files: |
| expected_output.add({file : Item(verb='Sending')}) |
| expected_status.tweak(file, wc_rev=commitrev, status=' ') |
| |
| svntest.actions.run_and_verify_commit(wc_dir, expected_output, |
| expected_status) |
| |
| def status_depth_local(sbox): |
| "run 'status --depth=X' with local changes" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| A_path = sbox.ospath('A') |
| D_path = os.path.join(A_path, 'D') |
| |
| mu_path = os.path.join(A_path, 'mu') |
| gamma_path = os.path.join(D_path, 'gamma') |
| |
| # make some changes to the greek tree |
| change_files(wc_dir, ['A/mu', 'A/D/gamma']) |
| svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', A_path) |
| svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', D_path) |
| |
| # for all the possible types of depth, check the status |
| |
| # depth=empty |
| expected = svntest.verify.UnorderedOutput( |
| [" M %s\n" % A_path]) |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "--depth=empty", A_path) |
| |
| # depth=files |
| expected = svntest.verify.UnorderedOutput( |
| [" M %s\n" % A_path, |
| "M %s\n" % mu_path]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "--depth=files", A_path) |
| |
| # depth=immediates |
| expected = svntest.verify.UnorderedOutput( |
| [" M %s\n" % A_path, |
| " M %s\n" % D_path, |
| "M %s\n" % mu_path]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "--depth=immediates", A_path) |
| |
| # depth=infinity (the default) |
| expected = svntest.verify.UnorderedOutput( |
| [" M %s\n" % A_path, |
| " M %s\n" % D_path, |
| "M %s\n" % mu_path, |
| "M %s\n" % gamma_path]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "--depth=infinity", A_path) |
| |
| def status_depth_update(sbox): |
| "run 'status --depth=X -u' with incoming changes" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = sbox.ospath('A') |
| D_path = os.path.join(A_path, 'D') |
| |
| mu_path = os.path.join(A_path, 'mu') |
| gamma_path = os.path.join(D_path, 'gamma') |
| |
| # add some files, change directory properties |
| change_files_and_commit(wc_dir, ['A/mu', 'A/D/gamma']) |
| svntest.main.run_svn(None, 'up', wc_dir) |
| svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', A_path) |
| svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', D_path) |
| sbox.simple_commit(message='log message') |
| |
| # update to r1 |
| svntest.main.run_svn(None, 'up', '-r', '1', wc_dir) |
| |
| # for all the possible types of depth, check the status |
| |
| # depth=empty |
| expected = svntest.verify.UnorderedOutput( |
| [" * 1 %s\n" % A_path, |
| "Status against revision: 3\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=empty", A_path) |
| |
| # depth=files |
| expected = svntest.verify.UnorderedOutput( |
| [" * 1 %s\n" % mu_path, |
| " * 1 %s\n" % A_path, |
| "Status against revision: 3\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=files", |
| A_path) |
| |
| # depth=immediates |
| expected = svntest.verify.UnorderedOutput( |
| [" * 1 %s\n" % A_path, |
| " * 1 %s\n" % D_path, |
| " * 1 %s\n" % mu_path, |
| "Status against revision: 3\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=immediates", |
| A_path) |
| |
| # depth=infinity (the default) |
| expected = svntest.verify.UnorderedOutput( |
| [" * 1 %s\n" % A_path, |
| " * 1 %s\n" % D_path, |
| " * 1 %s\n" % mu_path, |
| " * 1 %s\n" % gamma_path, |
| "Status against revision: 3\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=infinity", |
| A_path) |
| |
| |
| #---------------------------------------------------------------------- |
| def status_depth_update_local_modifications(sbox): |
| "run 'status --depth=X -u' with local changes" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = sbox.ospath('A') |
| D_path = os.path.join(A_path, 'D') |
| |
| mu_path = os.path.join(A_path, 'mu') |
| gamma_path = os.path.join(D_path, 'gamma') |
| |
| svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', A_path) |
| svntest.main.run_svn(None, 'propset', '--force', 'svn:test', 'value', D_path) |
| |
| svntest.main.file_append(mu_path, 'modified') |
| svntest.main.file_append(gamma_path, 'modified') |
| |
| # depth=empty |
| expected = svntest.verify.UnorderedOutput( |
| [" M 1 %s\n" % A_path, |
| "Status against revision: 1\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=empty", A_path) |
| |
| expected = svntest.verify.UnorderedOutput( |
| ["M 1 %s\n" % mu_path, |
| "Status against revision: 1\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=empty", mu_path) |
| |
| # depth=files |
| expected = svntest.verify.UnorderedOutput( |
| ["M 1 %s\n" % mu_path, |
| " M 1 %s\n" % A_path, |
| "Status against revision: 1\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=files", |
| A_path) |
| |
| # depth=immediates |
| expected = svntest.verify.UnorderedOutput( |
| [" M 1 %s\n" % A_path, |
| " M 1 %s\n" % D_path, |
| "M 1 %s\n" % mu_path, |
| "Status against revision: 1\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=immediates", |
| A_path) |
| |
| # depth=infinity (the default) |
| expected = svntest.verify.UnorderedOutput( |
| [" M 1 %s\n" % A_path, |
| " M 1 %s\n" % D_path, |
| "M 1 %s\n" % mu_path, |
| "M 1 %s\n" % gamma_path, |
| "Status against revision: 1\n"]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "--depth=infinity", |
| A_path) |
| |
| #---------------------------------------------------------------------- |
| # Test for issue #2420 |
| @Issue(2420) |
| def status_dash_u_deleted_directories(sbox): |
| "run 'status -u' with locally deleted directories" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| A_path = sbox.ospath('A') |
| B_path = os.path.join(A_path, 'B') |
| |
| # delete the B directory |
| svntest.actions.run_and_verify_svn(None, [], |
| 'rm', B_path) |
| |
| # now run status -u on B and its children |
| was_cwd = os.getcwd() |
| |
| os.chdir(A_path) |
| |
| # check status -u of B |
| expected = svntest.verify.UnorderedOutput( |
| ["D 1 1 jrandom %s\n" % \ |
| "B", |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("B", "lambda"), |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("B", "E"), |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("B", "E", "alpha"), |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("B", "E", "beta"), |
| "D 1 1 jrandom %s\n" % |
| os.path.join("B", "F"), |
| "Status against revision: 1\n" ]) |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "-v", "B") |
| |
| expected = \ |
| ["D 1 %s\n" % "B", |
| "Status against revision: 1\n" ] |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "B") |
| |
| |
| # again, but now from inside B, should give the same output |
| if not os.path.exists('B'): |
| os.mkdir('B') |
| os.chdir("B") |
| expected = svntest.verify.UnorderedOutput( |
| ["D 1 1 jrandom %s\n" % \ |
| ".", |
| "D 1 1 jrandom %s\n" % \ |
| "lambda", |
| "D 1 1 jrandom %s\n" % \ |
| "E", |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("E", "alpha"), |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("E", "beta"), |
| "D 1 1 jrandom %s\n" % \ |
| "F", |
| "Status against revision: 1\n" ]) |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "-v", ".") |
| |
| expected = \ |
| ["D 1 %s\n" % ".", |
| "Status against revision: 1\n" ] |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", ".") |
| |
| # check status -u of B/E |
| expected = svntest.verify.UnorderedOutput( |
| ["D 1 1 jrandom %s\n" % \ |
| os.path.join("B", "E"), |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("B", "E", "alpha"), |
| "D 1 1 jrandom %s\n" % \ |
| os.path.join("B", "E", "beta"), |
| "Status against revision: 1\n" ]) |
| |
| os.chdir(was_cwd) |
| os.chdir(A_path) |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", "-v", |
| os.path.join("B", "E")) |
| |
| |
| expected = [ "Status against revision: 1\n" ] |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u", |
| os.path.join("B", "E")) |
| |
| #---------------------------------------------------------------------- |
| |
| # Test for issue #2737: show obstructed status for versioned directories |
| # replaced by local directories. |
| @Issue(2737) |
| def status_dash_u_type_change(sbox): |
| "status -u on versioned items whose type changed" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.chdir(wc_dir) |
| |
| # Change the versioned file iota into an unversioned dir. |
| os.remove('iota') |
| os.mkdir('iota') |
| |
| xout = ["~ 1 iota\n", |
| "Status against revision: 1\n" ] |
| |
| svntest.actions.run_and_verify_svn(xout, |
| [], |
| "status", "-u") |
| |
| # Change the versioned directory A into an unversioned dir. |
| svntest.main.safe_rmtree('A') |
| os.mkdir('A') |
| |
| output = [ |
| "! 1 A/mu\n", |
| "! 1 A/B\n", |
| "! 1 A/B/lambda\n", |
| "! 1 A/B/E\n", |
| "! 1 A/B/E/alpha\n", |
| "! 1 A/B/E/beta\n", |
| "! 1 A/B/F\n", |
| "! 1 A/C\n", |
| "! 1 A/D\n", |
| "! 1 A/D/gamma\n", |
| "! 1 A/D/G\n", |
| "! 1 A/D/G/rho\n", |
| "! 1 A/D/G/pi\n", |
| "! 1 A/D/G/tau\n", |
| "! 1 A/D/H\n", |
| "! 1 A/D/H/chi\n", |
| "! 1 A/D/H/omega\n", |
| "! 1 A/D/H/psi\n", |
| "~ 1 iota\n", |
| "Status against revision: 1\n" |
| ] |
| |
| expected = svntest.verify.UnorderedOutput( |
| [s.replace('/', os.path.sep) |
| for s in output]) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-u") |
| |
| #---------------------------------------------------------------------- |
| |
| def status_with_tree_conflicts(sbox): |
| "status with tree conflicts" |
| |
| # Status messages reflecting tree conflict status. |
| # These tests correspond to use cases 1-3 in |
| # notes/tree-conflicts/use-cases.txt. |
| |
| svntest.actions.build_greek_tree_conflicts(sbox) |
| wc_dir = sbox.wc_dir |
| G = sbox.ospath('A/D/G') |
| pi = os.path.join(G, 'pi') |
| rho = os.path.join(G, 'rho') |
| tau = os.path.join(G, 'tau') |
| |
| # check status of G |
| expected = svntest.verify.UnorderedOutput( |
| ["A + C %s\n" % rho, |
| " > local file edit, incoming file delete or move upon update\n", |
| "D C %s\n" % pi, |
| " > local file delete, incoming file edit upon update\n", |
| "! C %s\n" % tau, |
| " > local file delete, incoming file delete or move upon update\n", |
| ] + svntest.main.summary_of_conflicts(tree_conflicts=3)) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", G) |
| |
| # check status of G, with -v |
| expected = svntest.verify.UnorderedOutput( |
| [" 2 2 jrandom %s\n" % G, |
| "D C 2 2 jrandom %s\n" % pi, |
| " > local file delete, incoming file edit upon update\n", |
| "A + C - 1 jrandom %s\n" % rho, |
| " > local file edit, incoming file delete or move upon update\n", |
| "! C %s\n" % tau, |
| " > local file delete, incoming file delete or move upon update\n", |
| ] + svntest.main.summary_of_conflicts(tree_conflicts=3)) |
| |
| svntest.actions.run_and_verify_svn(expected, |
| [], |
| "status", "-v", G) |
| |
| # check status of G, with -xml |
| exit_code, output, error = svntest.main.run_svn(None, 'status', G, '--xml', |
| '-v') |
| |
| should_be_victim = { |
| G: False, |
| pi: True, |
| rho: True, |
| tau: True, |
| } |
| |
| real_entry_count = 0 |
| output_str = r"".join(output) |
| # skip the first string, which contains only 'status' and 'target' elements |
| entries = output_str.split("<entry")[1:] |
| |
| for entry in entries: |
| # get the entry's path |
| m = re.search('path="([^"]+)"', entry) |
| if m: |
| real_entry_count += 1 |
| path = m.group(1) |
| # check if the path should be a victim |
| m = re.search('tree-conflicted="true"', entry) |
| if (m is None) and should_be_victim[path]: |
| logger.warn("ERROR: expected '%s' to be a tree conflict victim.", path) |
| logger.warn("ACTUAL STATUS OUTPUT:") |
| logger.warn(output_str) |
| raise svntest.Failure |
| if m and not should_be_victim[path]: |
| logger.warn("ERROR: did NOT expect '%s' to be a tree conflict victim.", |
| path) |
| logger.warn("ACTUAL STATUS OUTPUT:") |
| logger.warn(output_str) |
| raise svntest.Failure |
| |
| if real_entry_count != len(should_be_victim): |
| logger.warn("ERROR: 'status --xml' output is incomplete.") |
| raise svntest.Failure |
| |
| |
| #---------------------------------------------------------------------- |
| # Regression for issue #3742 |
| @Issue(3742) |
| def status_nested_wc_old_format(sbox): |
| "status on wc with nested old-format wc" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| os.mkdir(sbox.ospath('subdir')) |
| os.mkdir(sbox.ospath('subdir/.svn')) |
| svntest.main.file_append(sbox.ospath('subdir/.svn/format'), |
| '10\n') # format 10 was the Subversion 1.6 format |
| os.chdir(wc_dir) |
| svntest.actions.run_and_verify_svn([ "? subdir\n" ], [], 'st') |
| |
| #---------------------------------------------------------------------- |
| # Regression test for issue #3855 "status doesn't show 'K' on a locked |
| # deleted node". |
| @Issue(3855) |
| def status_locked_deleted(sbox): |
| "status with locked deleted file" |
| |
| sbox.build() |
| iota_path = sbox.ospath('iota') |
| |
| sbox.simple_rm('iota') |
| svntest.actions.run_and_verify_svn(None, [], 'lock', |
| os.path.join(sbox.wc_dir, 'iota')) |
| svntest.actions.run_and_verify_svn(['D K %s\n' % iota_path], [], |
| 'status', iota_path) |
| |
| @Issue(3774) |
| def wc_wc_copy_timestamp(sbox): |
| "timestamp on wc-wc copies" |
| |
| sbox.build(read_only=True) |
| wc_dir = sbox.wc_dir |
| |
| time.sleep(1.1) |
| svntest.main.file_append(sbox.ospath('A/D/H/psi'), 'modified\n') |
| svntest.actions.run_and_verify_svn(None, [], 'copy', |
| sbox.ospath('A/D/H'), |
| sbox.ospath('A/D/H2')) |
| |
| expected_output = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_output.tweak('A/D/H/psi', status='M ') |
| expected_output.add({ |
| 'A/D/H2' : Item(status='A ', copied='+', wc_rev='-'), |
| 'A/D/H2/chi' : Item(status=' ', copied='+', wc_rev='-'), |
| 'A/D/H2/omega' : Item(status=' ', copied='+', wc_rev='-'), |
| 'A/D/H2/psi' : Item(status='M ', copied='+', wc_rev='-'), |
| }) |
| svntest.actions.run_and_verify_status(wc_dir, expected_output) |
| |
| # Since copied chi is unmodified the text_timestamp should "match" |
| # the working file but it's not easy to confirm that directly. We |
| # can confirm that the copied is different from the source. |
| chi_src_timestamp = get_text_timestamp(sbox.ospath('A/D/H/chi')) |
| chi_dst_timestamp1 = get_text_timestamp(sbox.ospath('A/D/H2/chi')) |
| if chi_src_timestamp == chi_dst_timestamp1: |
| raise svntest.Failure("chi timestamps should be different") |
| |
| # Since copied psi is modified the text_timestamp should not "match" |
| # the working file, again difficult to confirm directly. It happens |
| # that the current implementation leaves it equal to the source. |
| psi_src_timestamp = get_text_timestamp(sbox.ospath('A/D/H/psi')) |
| psi_dst_timestamp = get_text_timestamp(sbox.ospath('A/D/H2/psi')) |
| if psi_src_timestamp != psi_dst_timestamp: |
| raise svntest.Failure("psi timestamps should be the same") |
| |
| # Cleanup repairs timestamps, so this should be a no-op. |
| svntest.actions.run_and_verify_svn(None, [], 'cleanup', wc_dir) |
| chi_dst_timestamp2 = get_text_timestamp(sbox.ospath('A/D/H2/chi')) |
| if chi_dst_timestamp2 != chi_dst_timestamp1: |
| raise svntest.Failure("chi timestamps should be the same") |
| |
| svntest.actions.run_and_verify_status(wc_dir, expected_output) |
| |
| @Issue(3908) |
| def wclock_status(sbox): |
| "verbose/non-verbose on locked working copy" |
| |
| sbox.build(read_only=True) |
| wc_dir = sbox.wc_dir |
| |
| # Recursive lock |
| svntest.actions.lock_admin_dir(sbox.ospath('A/D'), True) |
| |
| # Verbose status |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/D', 'A/D/G', 'A/D/H', locked='L') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # Non-verbose status |
| expected_output = svntest.verify.UnorderedOutput([ |
| ' L %s\n' % sbox.ospath(path) for path in ['A/D', |
| 'A/D/G', |
| 'A/D/H'] |
| ]) |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'status', wc_dir) |
| |
| # Second non-recursive lock |
| svntest.actions.lock_admin_dir(sbox.ospath('A/B')) |
| |
| expected_status.tweak('A/B', locked='L') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| expected_output = svntest.verify.UnorderedOutput([ |
| ' L %s\n' % sbox.ospath(path) for path in ['A/B', |
| 'A/D', |
| 'A/D/G', |
| 'A/D/H'] |
| ]) |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'status', wc_dir) |
| |
| |
| @Issue(4072) |
| @XFail() |
| def modified_modulo_translation(sbox): |
| "modified before translation, unmodified after" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # iota is a shell script. |
| sbox.simple_propset('svn:eol-style', 'LF', 'iota') |
| sbox.simple_commit() |
| |
| # CRLF it. |
| with open(sbox.ospath('iota'), 'wb') as f: |
| f.write("This is the file 'iota'.\r\n") |
| |
| # Run status. Expect some output. |
| # TODO: decide how such files should show in the output; whether they |
| # always show, or only with some --flag; and adjust this accordingly. |
| svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], |
| 'status', wc_dir) |
| |
| # Expect the file to be renormalized (to LF) after a revert. |
| sbox.simple_revert('iota') |
| svntest.actions.run_and_verify_svn([], [], 'status', wc_dir) |
| |
| def status_not_present(sbox): |
| "no status on not-present and excluded nodes" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # iota is a shell script. |
| sbox.simple_rm('iota', 'A/C') |
| svntest.main.run_svn(None, 'up', '--set-depth', 'exclude', |
| sbox.ospath('A/mu'), sbox.ospath('A/B')) |
| sbox.simple_commit() |
| |
| svntest.actions.run_and_verify_svn([], [],'status', |
| sbox.ospath('iota'), |
| sbox.ospath('A/B'), |
| sbox.ospath('A/C'), |
| sbox.ospath('A/mu'), |
| sbox.ospath('no-file')) |
| |
| # Skip this test is a .svn dir exists in the root directory |
| @Skip(lambda: os.path.exists("/%s" % svntest.main.get_admin_name())) |
| def status_unversioned_dir(sbox): |
| "status on unversioned dir" |
| sbox.build(read_only = True, create_wc = False) |
| |
| # Run svn status on "/", which we assume exists and isn't a WC. |
| # This should work on UNIX-like systems and Windows systems |
| expected_err = "svn: warning: W1550(07|10): .*'.*(/|\\\\)" + \ |
| "' is not a working copy" |
| svntest.actions.run_and_verify_svn2([], expected_err, 0, |
| "status", "/") |
| |
| def status_case_changed(sbox): |
| "status reporting on case changed nodes directly" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| |
| os.rename(sbox.ospath('iota'), sbox.ospath('iOTA')) |
| |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.add({ |
| 'iOTA' : Item(status='? '), |
| }) |
| expected_status.tweak('iota', status='! ') |
| |
| # First run status on the directory |
| svntest.actions.run_and_verify_unquiet_status(wc_dir, |
| expected_status) |
| |
| # Now on the missing iota directly, which should give the same |
| # result, even on case insenstive filesystems |
| expected_status = svntest.wc.State(wc_dir, { |
| 'iota' : Item(status='! ', wc_rev=1), |
| }) |
| svntest.actions.run_and_verify_unquiet_status(sbox.ospath('iota'), |
| expected_status) |
| |
| # And on the unversioned iOTA |
| expected_status = svntest.wc.State(wc_dir, { |
| 'iOTA' : Item(status='? '), |
| }) |
| svntest.actions.run_and_verify_unquiet_status(sbox.ospath('iOTA'), |
| expected_status) |
| |
| |
| def move_update_timestamps(sbox): |
| "timestamp behaviour for move-update" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| sbox.simple_append('A/B/E/beta', 'X\nY\nZ\n', truncate=True) |
| sbox.simple_commit() |
| sbox.simple_append('A/B/E/alpha', 'modified alpha') |
| sbox.simple_append('A/B/E/beta', 'XX\nY\nZ\n', truncate=True) |
| sbox.simple_commit() |
| sbox.simple_update('', 2) |
| |
| sbox.simple_append('A/B/E/beta', 'local beta') |
| src_time = get_text_timestamp(sbox.ospath('A/B/E/alpha')) |
| sbox.simple_move("A/B/E", "A/B/E2") |
| alpha_dst_time = get_text_timestamp(sbox.ospath('A/B/E2/alpha')) |
| beta_dst_time = get_text_timestamp(sbox.ospath('A/B/E2/beta')) |
| if src_time != alpha_dst_time: |
| raise svntest.Failure("move failed to copy timestamp") |
| |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/B/E' : Item(status=' ', treeconflict='C'), |
| 'A/B/E/alpha' : Item(status=' ', treeconflict='U'), |
| 'A/B/E/beta' : Item(status=' ', treeconflict='U'), |
| }) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 3) |
| expected_status.tweak('A/B/E', |
| status='D ', treeconflict='C', moved_to='A/B/E2') |
| expected_status.tweak('A/B/E/alpha', 'A/B/E/beta', status='D ') |
| expected_status.add({ |
| 'A/B/E2' : Item(status='A ', wc_rev='-', copied='+', |
| moved_from='A/B/E'), |
| 'A/B/E2/alpha' : Item(status=' ', wc_rev='-', copied='+'), |
| 'A/B/E2/beta' : Item(status='M ', wc_rev='-', copied='+'), |
| }) |
| expected_disk = svntest.main.greek_state.copy() |
| expected_disk.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta') |
| expected_disk.add({ |
| 'A/B/E2' : Item(), |
| 'A/B/E2/alpha' : Item("This is the file 'alpha'.\n"), |
| 'A/B/E2/beta' : Item("X\nY\nZ\nlocal beta"), |
| }) |
| svntest.actions.run_and_verify_update(wc_dir, |
| expected_output, |
| expected_disk, |
| expected_status) |
| |
| time.sleep(1.1) |
| svntest.actions.run_and_verify_svn(None, [], |
| 'resolve', |
| '--accept=mine-conflict', |
| sbox.ospath('A/B/E')) |
| expected_status.tweak('A/B/E', treeconflict=None) |
| expected_status.tweak('A/B/E2/beta', status='M ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| expected_disk.tweak('A/B/E2/beta', contents="XX\nY\nZ\nlocal beta") |
| expected_disk.tweak('A/B/E2/alpha', contents="This is the file 'alpha'.\nmodified alpha") |
| svntest.actions.verify_disk(wc_dir, expected_disk) |
| |
| # alpha is pristine so gets a new timestamp |
| new_time = get_text_timestamp(sbox.ospath('A/B/E2/alpha')) |
| if new_time == alpha_dst_time: |
| raise svntest.Failure("move failed to update timestamp") |
| |
| # beta is modified so timestamp is removed |
| no_text_timestamp(sbox.ospath('A/B/E2/beta')) |
| |
| @Issue(4398) |
| def status_path_handling(sbox): |
| "relative/absolute path handling" |
| |
| sbox.build(read_only=True) |
| |
| # target is a relative path to a subdir |
| wc_dir = sbox.wc_dir |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # target is an absolute path to a subdir |
| cwd = os.getcwd() |
| abs_wc_dir = os.path.join(cwd, wc_dir) |
| expected_status = svntest.actions.get_virginal_state(abs_wc_dir, 1) |
| svntest.actions.run_and_verify_status(abs_wc_dir, expected_status) |
| |
| # target is an absolute path to a parent dir |
| os.chdir(sbox.ospath('A/B')) |
| expected_status = svntest.actions.get_virginal_state(abs_wc_dir, 1) |
| svntest.actions.run_and_verify_status(abs_wc_dir, expected_status) |
| |
| # target is a relative path to a parent dir |
| rel_wc_dir = os.path.join('..', '..') |
| expected_status = svntest.actions.get_virginal_state(rel_wc_dir, 1) |
| svntest.actions.run_and_verify_status(rel_wc_dir, expected_status) |
| |
| def status_move_missing_direct(sbox): |
| "move information when status is called directly" |
| |
| sbox.build() |
| sbox.simple_copy('A', 'Z') |
| sbox.simple_commit('') |
| sbox.simple_update('') |
| |
| sbox.simple_move('Z', 'ZZ') |
| sbox.simple_move('A', 'Z') |
| sbox.simple_move('Z/B', 'ZB') |
| sbox.simple_mkdir('Z/B') |
| sbox.simple_move('ZB/E', 'Z/B/E') |
| |
| # Somehow 'svn status' now shows different output for 'ZB/E' |
| # when called directly and via an ancestor, as this handles |
| # multi-layer in a different way |
| |
| # Note that the status output may change over different Subversion revisions, |
| # but the status on a node should be identical anyway 'svn status' is called |
| # on it. |
| |
| expected_output = [ |
| 'A + %s\n' % sbox.ospath('ZB'), |
| ' > moved from %s\n' % os.path.join('..', 'Z', 'B'), |
| 'D + %s\n' % sbox.ospath('ZB/E'), |
| ' > moved to %s\n' % os.path.join('..', 'Z', 'B', 'E'), |
| ] |
| svntest.actions.run_and_verify_svn(expected_output, [], 'status', |
| sbox.ospath('ZB'), '--depth', 'immediates') |
| |
| # And calling svn status on just 'ZB/E' should have the same result for this node |
| # except that we calculate the relative path from a different base |
| expected_output = [ |
| 'D + %s\n' % sbox.ospath('ZB/E'), |
| ' > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'), |
| ] |
| svntest.actions.run_and_verify_svn(expected_output, [], 'status', |
| sbox.ospath('ZB/E'), '--depth', 'empty') |
| |
| def status_move_missing_direct_base(sbox): |
| "move when status is called directly with base" |
| |
| sbox.build() |
| sbox.simple_copy('A', 'Z') |
| sbox.simple_mkdir('Q') |
| sbox.simple_mkdir('Q/ZB') |
| sbox.simple_mkdir('Q/ZB/E') |
| sbox.simple_commit('') |
| sbox.simple_update('') |
| |
| sbox.simple_rm('Q') |
| sbox.simple_mkdir('Q') |
| |
| sbox.simple_move('Z', 'ZZ') |
| sbox.simple_move('A', 'Z') |
| sbox.simple_move('Z/B', 'Q/ZB') |
| sbox.simple_mkdir('Z/B') |
| sbox.simple_move('Q/ZB/E', 'Z/B/E') |
| |
| # Somehow 'svn status' now shows different output for 'Q/ZB/E' |
| # when called directly and via an ancestor, as this handles |
| # multi-layer in a different way |
| |
| # Note that the status output may change over different Subversion revisions, |
| # but the status on a node should be identical anyway 'svn status' is called |
| # on it. |
| |
| # This test had a different result as status_move_missing_direct at the time of |
| # writing this test. |
| |
| expected_output = [ |
| 'A + %s\n' % sbox.ospath('Q/ZB'), |
| ' > moved from %s\n' % os.path.join('..', '..', 'Z', 'B'), |
| 'D + %s\n' % sbox.ospath('Q/ZB/E'), |
| ' > moved to %s\n' % os.path.join('..', '..', 'Z', 'B', 'E'), |
| ] |
| svntest.actions.run_and_verify_svn(expected_output, [], 'status', |
| sbox.ospath('Q/ZB'), '--depth', 'immediates') |
| |
| # And calling svn status on just 'ZB/E' should have the same result for this node, |
| # except that the moved_to information is calculated from the node itself |
| expected_output = [ |
| 'D + %s\n' % sbox.ospath('Q/ZB/E'), |
| ' > moved to %s\n' % os.path.join('..', '..', '..', 'Z', 'B', 'E'), |
| ] |
| svntest.actions.run_and_verify_svn(expected_output, [], 'status', |
| sbox.ospath('Q/ZB/E'), '--depth', 'empty') |
| |
| def status_missing_conflicts(sbox): |
| "status missing certain conflicts" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| sbox.simple_propset('q', 'r', 'A/B/E/alpha', 'A/B/E/beta') |
| sbox.simple_commit() |
| |
| sbox.simple_move('A/B/E/alpha', 'alpha') |
| sbox.simple_move('A/B/E/beta', 'beta') |
| |
| sbox.simple_rm('A/B/E') |
| |
| svntest.main.run_svn(False, 'update', sbox.ospath('A/B/E'), '-r', '1', |
| '--accept=postpone') |
| |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/B/E', status='D ', treeconflict='C', wc_rev=1) |
| expected_status.tweak('A/B/E/alpha', status='D ', treeconflict='C', wc_rev=1, |
| moved_to='alpha') |
| expected_status.tweak('A/B/E/beta', status='D ', treeconflict='C', wc_rev=1, |
| moved_to='beta') |
| expected_status.add({ |
| 'alpha' : Item(status='A ', copied='+', moved_from='A/B/E/alpha', wc_rev='-'), |
| 'beta' : Item(status='A ', copied='+', moved_from='A/B/E/beta', wc_rev='-') |
| }) |
| |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| expected_info = [ |
| { |
| 'Tree conflict': 'local file moved away, incoming file edit upon update.*' |
| }, |
| { |
| 'Tree conflict': 'local file moved away, incoming file edit upon update.*' |
| } |
| ] |
| svntest.actions.run_and_verify_info(expected_info, |
| sbox.ospath('A/B/E/alpha'), |
| sbox.ospath('A/B/E/beta')) |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'resolve', '--accept=mine-conflict', |
| '--depth=empty', sbox.ospath('A/B/E')) |
| expected_status.tweak('A/B/E', treeconflict=None) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # Now replace with directory |
| sbox.simple_mkdir('A/B/E') |
| expected_status.tweak('A/B/E', status='R ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| svntest.actions.run_and_verify_info(expected_info, |
| sbox.ospath('A/B/E/alpha'), |
| sbox.ospath('A/B/E/beta')) |
| |
| #Recreate scenario for file |
| sbox.simple_rm('A/B/E', 'alpha', 'beta') |
| svntest.actions.run_and_verify_svn(None, [], |
| 'revert', '-R', sbox.ospath('A/B/E')) |
| |
| sbox.simple_update('A/B/E', revision=2) |
| |
| sbox.simple_move('A/B/E/alpha', 'alpha') |
| sbox.simple_move('A/B/E/beta', 'beta') |
| |
| sbox.simple_rm('A/B/E') |
| svntest.main.run_svn(False, 'update', sbox.ospath('A/B/E'), '-r', '1', |
| '--accept=postpone') |
| svntest.actions.run_and_verify_svn(None, [], |
| 'resolve', '--accept=mine-conflict', |
| '--depth=empty', sbox.ospath('A/B/E')) |
| |
| sbox.simple_append('A/B/E', 'something') |
| expected_status.tweak('A/B/E', status='D ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| sbox.simple_add('A/B/E') |
| |
| # In the entries world A/B/E doesn't have children.. |
| expected_status.tweak('A/B/E', status='R ', entry_kind='file') |
| |
| # Tree conflicts still in db |
| svntest.actions.run_and_verify_info(expected_info, |
| sbox.ospath('A/B/E/alpha'), |
| sbox.ospath('A/B/E/beta')) |
| |
| # But not in status! |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| |
| |
| |
| ######################################################################## |
| # Run the tests |
| |
| |
| # list all tests here, starting with None: |
| test_list = [ None, |
| status_unversioned_file_in_current_dir, |
| status_update_with_nested_adds, |
| status_shows_all_in_current_dir, |
| status_missing_file, |
| status_type_change, |
| status_type_change_to_symlink, |
| status_with_new_files_pending, |
| status_for_unignored_file, |
| status_for_nonexistent_file, |
| status_file_needs_update, |
| status_uninvited_parent_directory, |
| status_on_forward_deletion, |
| timestamp_behaviour, |
| status_on_unversioned_dotdot, |
| status_on_partially_nonrecursive_wc, |
| missing_dir_in_anchor, |
| status_in_xml, |
| status_ignored_dir, |
| status_unversioned_dir_in_wc, |
| status_missing_dir, |
| status_nonrecursive_update_different_cwd, |
| status_add_plus_conflict, |
| inconsistent_eol, |
| status_update_with_incoming_props, |
| status_update_verbose_with_incoming_props, |
| status_nonrecursive_update, |
| status_dash_u_deleted_directories, |
| status_depth_local, |
| status_depth_update, |
| status_depth_update_local_modifications, |
| status_dash_u_type_change, |
| status_with_tree_conflicts, |
| status_nested_wc_old_format, |
| status_locked_deleted, |
| wc_wc_copy_timestamp, |
| wclock_status, |
| modified_modulo_translation, |
| status_not_present, |
| status_unversioned_dir, |
| status_case_changed, |
| move_update_timestamps, |
| status_path_handling, |
| status_move_missing_direct, |
| status_move_missing_direct_base, |
| status_missing_conflicts, |
| ] |
| |
| if __name__ == '__main__': |
| svntest.main.run_tests(test_list) |
| # NOTREACHED |
| |
| |
| ### End of file. |