| #!/usr/bin/env python |
| # |
| # changelist_tests.py: testing changelist uses. |
| # |
| # 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 string, sys, os, re |
| |
| # Our testing module |
| import svntest |
| |
| # (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 |
| |
| |
| ###################################################################### |
| # Utilities |
| |
| |
| def mod_all_files(wc_dir, new_text): |
| """Walk over working copy WC_DIR, appending NEW_TEXT to all the |
| files in that tree (but not inside the .svn areas of that tree).""" |
| |
| dot_svn = svntest.main.get_admin_name() |
| for dirpath, dirs, files in os.walk(wc_dir): |
| if dot_svn in dirs: |
| dirs.remove(dot_svn) |
| for name in files: |
| svntest.main.file_append(os.path.join(dirpath, name), new_text) |
| |
| def changelist_all_files(wc_dir, name_func): |
| """Walk over working copy WC_DIR, adding versioned files to |
| changelists named by invoking NAME_FUNC(full-path-of-file) and |
| noting its string return value (or None, if we wish to remove the |
| file from a changelist).""" |
| |
| dot_svn = svntest.main.get_admin_name() |
| for dirpath, dirs, files in os.walk(wc_dir): |
| if dot_svn in dirs: |
| dirs.remove(dot_svn) |
| for name in files: |
| full_path = os.path.join(dirpath, name) |
| clname = name_func(full_path) |
| if not clname: |
| svntest.main.run_svn(None, "changelist", "--remove", full_path) |
| else: |
| svntest.main.run_svn(None, "changelist", clname, full_path) |
| |
| def clname_from_lastchar_cb(full_path): |
| """Callback for changelist_all_files() that returns a changelist |
| name matching the last character in the file's name. For example, |
| after running this on a greek tree where every file has some text |
| modification, 'svn status' shows: |
| |
| --- Changelist 'a': |
| M A/B/lambda |
| M A/B/E/alpha |
| M A/B/E/beta |
| M A/D/gamma |
| M A/D/H/omega |
| M iota |
| |
| --- Changelist 'u': |
| M A/mu |
| M A/D/G/tau |
| |
| --- Changelist 'i': |
| M A/D/G/pi |
| M A/D/H/chi |
| M A/D/H/psi |
| |
| --- Changelist 'o': |
| M A/D/G/rho |
| """ |
| return full_path[-1] |
| |
| |
| # Regular expressions for 'svn changelist' output. |
| _re_cl_rem_pattern = "^D \[(.*)\] (.*)" |
| _re_cl_skip = re.compile("Skipped '(.*)'") |
| _re_cl_add = re.compile("^A \[(.*)\] (.*)") |
| _re_cl_rem = re.compile(_re_cl_rem_pattern) |
| |
| def verify_changelist_output(output, expected_adds=None, |
| expected_removals=None, |
| expected_skips=None): |
| """Compare lines of OUTPUT from 'svn changelist' against |
| EXPECTED_ADDS (a dictionary mapping paths to changelist names), |
| EXPECTED_REMOVALS (a dictionary mapping paths to ... whatever), and |
| EXPECTED_SKIPS (a dictionary mapping paths to ... whatever). |
| |
| EXPECTED_SKIPS is ignored if None.""" |
| |
| num_expected = 0 |
| if expected_adds: |
| num_expected += len(expected_adds) |
| if expected_removals: |
| num_expected += len(expected_removals) |
| if expected_skips: |
| num_expected += len(expected_skips) |
| |
| if not expected_skips: |
| output = [line for line in output if (not _re_cl_skip.match(line))] |
| |
| for line in output: |
| line = line.rstrip() |
| match = _re_cl_rem.match(line) |
| if match \ |
| and expected_removals \ |
| and match.group(2) in expected_removals: |
| continue |
| elif match: |
| raise svntest.Failure("Unexpected changelist removal line: " + line) |
| match = _re_cl_add.match(line) |
| if match \ |
| and expected_adds \ |
| and expected_adds.get(match.group(2)) == match.group(1): |
| continue |
| elif match: |
| raise svntest.Failure("Unexpected changelist add line: " + line) |
| match = _re_cl_skip.match(line) |
| if match \ |
| and expected_skips \ |
| and match.group(2) in expected_skips: |
| continue |
| elif match: |
| raise svntest.Failure("Unexpected changelist skip line: " + line) |
| raise svntest.Failure("Unexpected line: " + line) |
| |
| if len(output) != num_expected: |
| raise svntest.Failure("Unexpected number of 'svn changelist' output " + |
| "lines (%d vs %d)" % (len(output), num_expected)) |
| |
| def verify_pget_output(output, expected_props): |
| """Compare lines of OUTPUT from 'svn propget' against EXPECTED_PROPS |
| (a dictionary mapping paths to property values).""" |
| |
| _re_pget = re.compile('^(.*) - (.*)$') |
| actual_props = {} |
| for line in output: |
| try: |
| path, prop = line.rstrip().split(' - ') |
| except: |
| raise svntest.Failure("Unexpected output line: " + line) |
| actual_props[path] = prop |
| if expected_props != actual_props: |
| raise svntest.Failure("Got unexpected property results\n" |
| "\tExpected: %s\n" |
| "\tActual: %s" % (str(expected_props), |
| str(actual_props))) |
| |
| |
| ###################################################################### |
| # Tests |
| # |
| # Each test must return on success or raise on failure. |
| |
| |
| #---------------------------------------------------------------------- |
| |
| def add_remove_changelists(sbox): |
| "add and remove files from changelists" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| ### 'Skip' notifications |
| |
| def expected_skips_under(*greek_path): |
| "return a dict mapping Greek-tree directories below GREEK_PATH to None" |
| |
| expected_skips = {} |
| for path in expected_skips_all: |
| if path.startswith(os.path.join(wc_dir, *greek_path)): |
| expected_skips[path] = None |
| |
| return expected_skips |
| |
| def all_parents(expected_adds): |
| """return a dict mapping Greek-tree directories above directories in |
| EXPECTED_ADDS to None""" |
| |
| expected_skips = {} |
| for path in expected_adds.keys(): |
| if not os.path.isdir(path): |
| path = os.path.dirname(path) |
| |
| while path != wc_dir: |
| expected_skips[path] = None |
| path = os.path.dirname(path) |
| |
| expected_skips[wc_dir] = None |
| return expected_skips |
| |
| # all dirs in the Greek tree |
| expected_skips_all = dict([(x, None) for x in [ |
| sbox.ospath(''), |
| sbox.ospath('A'), |
| sbox.ospath('A/B'), |
| sbox.ospath('A/B/E'), |
| sbox.ospath('A/B/F'), |
| sbox.ospath('A/C'), |
| sbox.ospath('A/D'), |
| sbox.ospath('A/D/G'), |
| sbox.ospath('A/D/H'), |
| ]]) |
| |
| expected_skips_wc_dir = { wc_dir : None } |
| |
| ### First, we play with just adding to changelists ### |
| |
| # svn changelist foo WC_DIR |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", "foo", |
| wc_dir) |
| verify_changelist_output(output) # nothing expected |
| |
| # svn changelist foo WC_DIR --depth files |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", "foo", |
| "--depth", "files", |
| wc_dir) |
| expected_adds = { |
| os.path.join(wc_dir, 'iota') : 'foo', |
| } |
| verify_changelist_output(output, expected_adds) |
| |
| # svn changelist foo WC_DIR --depth infinity |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", "foo", |
| "--depth", "infinity", |
| wc_dir) |
| expected_adds = { |
| sbox.ospath('A/B/E/alpha') : 'foo', |
| sbox.ospath('A/B/E/beta') : 'foo', |
| sbox.ospath('A/B/lambda') : 'foo', |
| sbox.ospath('A/D/G/pi') : 'foo', |
| sbox.ospath('A/D/G/rho') : 'foo', |
| sbox.ospath('A/D/G/tau') : 'foo', |
| sbox.ospath('A/D/H/chi') : 'foo', |
| sbox.ospath('A/D/H/omega') : 'foo', |
| sbox.ospath('A/D/H/psi') : 'foo', |
| sbox.ospath('A/D/gamma') : 'foo', |
| sbox.ospath('A/mu') : 'foo', |
| } |
| verify_changelist_output(output, expected_adds) |
| |
| ### Now, change some changelists ### |
| |
| # svn changelist bar WC_DIR/A/D --depth infinity |
| exit_code, output, errput = svntest.main.run_svn(".*", "changelist", "bar", |
| "--depth", "infinity", |
| sbox.ospath('A/D')) |
| expected_adds = { |
| sbox.ospath('A/D/G/pi') : 'bar', |
| sbox.ospath('A/D/G/rho') : 'bar', |
| sbox.ospath('A/D/G/tau') : 'bar', |
| sbox.ospath('A/D/H/chi') : 'bar', |
| sbox.ospath('A/D/H/omega') : 'bar', |
| sbox.ospath('A/D/H/psi') : 'bar', |
| sbox.ospath('A/D/gamma') : 'bar', |
| } |
| expected_removals = expected_adds |
| verify_changelist_output(output, expected_adds, expected_removals) |
| |
| # svn changelist baz WC_DIR/A/D/H --depth infinity |
| exit_code, output, errput = svntest.main.run_svn(".*", "changelist", "baz", |
| "--depth", "infinity", |
| sbox.ospath('A/D/H')) |
| expected_adds = { |
| sbox.ospath('A/D/H/chi') : 'baz', |
| sbox.ospath('A/D/H/omega') : 'baz', |
| sbox.ospath('A/D/H/psi') : 'baz', |
| } |
| expected_removals = expected_adds |
| verify_changelist_output(output, expected_adds, expected_removals) |
| |
| ### Now, let's selectively rename some changelists ### |
| |
| # svn changelist foo-rename WC_DIR --depth infinity --changelist foo |
| exit_code, output, errput = svntest.main.run_svn(".*", "changelist", |
| "foo-rename", |
| "--depth", "infinity", |
| "--changelist", "foo", |
| wc_dir) |
| expected_adds = { |
| sbox.ospath('A/B/E/alpha') : 'foo-rename', |
| sbox.ospath('A/B/E/beta') : 'foo-rename', |
| sbox.ospath('A/B/lambda') : 'foo-rename', |
| sbox.ospath('A/mu') : 'foo-rename', |
| sbox.ospath('iota') : 'foo-rename', |
| } |
| expected_removals = expected_adds |
| verify_changelist_output(output, expected_adds, expected_removals) |
| |
| # svn changelist bar WC_DIR --depth infinity |
| # --changelist foo-rename --changelist baz |
| exit_code, output, errput = svntest.main.run_svn( |
| ".*", "changelist", "bar", "--depth", "infinity", |
| "--changelist", "foo-rename", "--changelist", "baz", wc_dir) |
| |
| expected_adds = { |
| sbox.ospath('A/B/E/alpha') : 'bar', |
| sbox.ospath('A/B/E/beta') : 'bar', |
| sbox.ospath('A/B/lambda') : 'bar', |
| sbox.ospath('A/D/H/chi') : 'bar', |
| sbox.ospath('A/D/H/omega') : 'bar', |
| sbox.ospath('A/D/H/psi') : 'bar', |
| sbox.ospath('A/mu') : 'bar', |
| sbox.ospath('iota') : 'bar', |
| } |
| expected_removals = expected_adds |
| verify_changelist_output(output, expected_adds, expected_removals) |
| |
| ### Okay. Time to remove some stuff from changelists now. ### |
| |
| # svn changelist --remove WC_DIR |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", |
| "--remove", wc_dir) |
| verify_changelist_output(output) # nothing expected |
| |
| # svn changelist --remove WC_DIR --depth files |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", |
| "--remove", |
| "--depth", "files", |
| wc_dir) |
| expected_removals = { |
| os.path.join(wc_dir, 'iota') : None, |
| } |
| verify_changelist_output(output, None, expected_removals) |
| |
| # svn changelist --remove WC_DIR --depth infinity |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", |
| "--remove", |
| "--depth", "infinity", |
| wc_dir) |
| expected_removals = { |
| sbox.ospath('A/B/E/alpha') : None, |
| sbox.ospath('A/B/E/beta') : None, |
| sbox.ospath('A/B/lambda') : None, |
| sbox.ospath('A/D/G/pi') : None, |
| sbox.ospath('A/D/G/rho') : None, |
| sbox.ospath('A/D/G/tau') : None, |
| sbox.ospath('A/D/H/chi') : None, |
| sbox.ospath('A/D/H/omega') : None, |
| sbox.ospath('A/D/H/psi') : None, |
| sbox.ospath('A/D/gamma') : None, |
| sbox.ospath('A/mu') : None, |
| } |
| verify_changelist_output(output, None, expected_removals) |
| |
| ### Add files to changelists based on the last character in their names ### |
| |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| ### Now, do selective changelist removal ### |
| |
| # svn changelist --remove WC_DIR --depth infinity --changelist a |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", |
| "--remove", |
| "--depth", "infinity", |
| "--changelist", "a", |
| wc_dir) |
| expected_removals = { |
| sbox.ospath('A/B/E/alpha') : None, |
| sbox.ospath('A/B/E/beta') : None, |
| sbox.ospath('A/B/lambda') : None, |
| sbox.ospath('A/D/H/omega') : None, |
| sbox.ospath('A/D/gamma') : None, |
| sbox.ospath('iota') : None, |
| } |
| verify_changelist_output(output, None, expected_removals) |
| |
| # svn changelist --remove WC_DIR --depth infinity |
| # --changelist i --changelist o |
| exit_code, output, errput = svntest.main.run_svn(None, "changelist", |
| "--remove", |
| "--depth", "infinity", |
| "--changelist", "i", |
| "--changelist", "o", |
| wc_dir) |
| expected_removals = { |
| sbox.ospath('A/D/G/pi') : None, |
| sbox.ospath('A/D/G/rho') : None, |
| sbox.ospath('A/D/H/chi') : None, |
| sbox.ospath('A/D/H/psi') : None, |
| } |
| verify_changelist_output(output, None, expected_removals) |
| |
| #---------------------------------------------------------------------- |
| |
| def commit_one_changelist(sbox): |
| "commit with single --changelist" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Add a line of text to all the versioned files in the tree. |
| mod_all_files(wc_dir, "New text.\n") |
| |
| # Add files to changelists based on the last character in their names. |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| # Now, test a commit that uses a single changelist filter (--changelist a). |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/B/lambda' : Item(verb='Sending'), |
| 'A/B/E/alpha' : Item(verb='Sending'), |
| 'A/B/E/beta' : Item(verb='Sending'), |
| 'A/D/gamma' : Item(verb='Sending'), |
| 'A/D/H/omega' : Item(verb='Sending'), |
| 'iota' : Item(verb='Sending'), |
| }) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/mu', 'A/D/G/tau', 'A/D/G/pi', 'A/D/H/chi', |
| 'A/D/H/psi', 'A/D/G/rho', wc_rev=1, status='M ') |
| expected_status.tweak('iota', 'A/B/lambda', 'A/B/E/alpha', 'A/B/E/beta', |
| 'A/D/gamma', 'A/D/H/omega', wc_rev=2, status=' ') |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, |
| expected_status, |
| [], |
| wc_dir, |
| "--changelist", |
| "a") |
| |
| #---------------------------------------------------------------------- |
| |
| def commit_multiple_changelists(sbox): |
| "commit with multiple --changelist's" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Add a line of text to all the versioned files in the tree. |
| mod_all_files(wc_dir, "New text.\n") |
| |
| # Add files to changelists based on the last character in their names. |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| # Now, test a commit that uses multiple changelist filters |
| # (--changelist=a --changelist=i). |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/B/lambda' : Item(verb='Sending'), |
| 'A/B/E/alpha' : Item(verb='Sending'), |
| 'A/B/E/beta' : Item(verb='Sending'), |
| 'A/D/gamma' : Item(verb='Sending'), |
| 'A/D/H/omega' : Item(verb='Sending'), |
| 'iota' : Item(verb='Sending'), |
| 'A/D/G/pi' : Item(verb='Sending'), |
| 'A/D/H/chi' : Item(verb='Sending'), |
| 'A/D/H/psi' : Item(verb='Sending'), |
| }) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/mu', 'A/D/G/tau', 'A/D/G/rho', |
| wc_rev=1, status='M ') |
| expected_status.tweak('iota', 'A/B/lambda', 'A/B/E/alpha', 'A/B/E/beta', |
| 'A/D/gamma', 'A/D/H/omega', 'A/D/G/pi', 'A/D/H/chi', |
| 'A/D/H/psi', wc_rev=2, status=' ') |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, |
| expected_status, |
| [], |
| wc_dir, |
| "--changelist", "a", |
| "--changelist", "i") |
| |
| #---------------------------------------------------------------------- |
| |
| def info_with_changelists(sbox): |
| "info --changelist" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Add files to changelists based on the last character in their names. |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| # Now, test various combinations of changelist specification and depths. |
| for clname in [['a'], ['i'], ['a', 'i']]: |
| for depth in [None, 'files', 'infinity']: |
| |
| # Figure out what we expect to see in our info output. |
| expected_paths = [] |
| if 'a' in clname: |
| if depth == 'infinity': |
| expected_paths.append('A/B/lambda') |
| expected_paths.append('A/B/E/alpha') |
| expected_paths.append('A/B/E/beta') |
| expected_paths.append('A/D/gamma') |
| expected_paths.append('A/D/H/omega') |
| if depth == 'files' or depth == 'infinity': |
| expected_paths.append('iota') |
| if 'i' in clname: |
| if depth == 'infinity': |
| expected_paths.append('A/D/G/pi') |
| expected_paths.append('A/D/H/chi') |
| expected_paths.append('A/D/H/psi') |
| expected_paths = sorted([os.path.join(wc_dir, x.replace('/', os.sep)) for x in expected_paths]) |
| |
| # Build the command line. |
| args = ['info', wc_dir] |
| for cl in clname: |
| args.append('--changelist') |
| args.append(cl) |
| if depth: |
| args.append('--depth') |
| args.append(depth) |
| |
| # Run 'svn info ...' |
| exit_code, output, errput = svntest.main.run_svn(None, *args) |
| |
| # Filter the output for lines that begin with 'Path:', and |
| # reduce even those lines to just the actual path. |
| paths = sorted([x[6:].rstrip() for x in output if x[:6] == 'Path: ']) |
| |
| # And, compare! |
| if (paths != expected_paths): |
| raise svntest.Failure("Expected paths (%s) and actual paths (%s) " |
| "don't gel" % (str(expected_paths), str(paths))) |
| |
| #---------------------------------------------------------------------- |
| |
| def diff_with_changelists(sbox): |
| "diff --changelist (wc-wc and repos-wc)" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Add a line of text to all the versioned files in the tree. |
| mod_all_files(wc_dir, "New text.\n") |
| |
| # Add files to changelists based on the last character in their names. |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| # Now, test various combinations of changelist specification and depths. |
| for is_repos_wc in [0, 1]: |
| for clname in [['a'], ['i'], ['a', 'i']]: |
| for depth in ['files', 'infinity']: |
| |
| # Figure out what we expect to see in our diff output. |
| expected_paths = [] |
| if 'a' in clname: |
| if depth == 'infinity': |
| expected_paths.append('A/B/lambda') |
| expected_paths.append('A/B/E/alpha') |
| expected_paths.append('A/B/E/beta') |
| expected_paths.append('A/D/gamma') |
| expected_paths.append('A/D/H/omega') |
| if depth == 'files' or depth == 'infinity': |
| expected_paths.append('iota') |
| if 'i' in clname: |
| if depth == 'infinity': |
| expected_paths.append('A/D/G/pi') |
| expected_paths.append('A/D/H/chi') |
| expected_paths.append('A/D/H/psi') |
| expected_paths = sorted([os.path.join(wc_dir, x.replace('/', os.sep)) for x in expected_paths]) |
| |
| # Build the command line. |
| args = ['diff'] |
| for cl in clname: |
| args.append('--changelist') |
| args.append(cl) |
| if depth: |
| args.append('--depth') |
| args.append(depth) |
| if is_repos_wc: |
| args.append('--old') |
| args.append(sbox.repo_url) |
| args.append('--new') |
| args.append(sbox.wc_dir) |
| else: |
| args.append(wc_dir) |
| |
| # Run 'svn diff ...' |
| exit_code, output, errput = svntest.main.run_svn(None, *args) |
| |
| # Filter the output for lines that begin with 'Index:', and |
| # reduce even those lines to just the actual path. |
| paths = sorted([x[7:].rstrip() for x in output if x[:7] == 'Index: ']) |
| |
| # Diff output on Win32 uses '/' path separators. |
| if sys.platform == 'win32': |
| paths = [x.replace('/', os.sep) for x in paths] |
| |
| # And, compare! |
| if (paths != expected_paths): |
| raise svntest.Failure("Expected paths (%s) and actual paths (%s) " |
| "don't gel" |
| % (str(expected_paths), str(paths))) |
| |
| #---------------------------------------------------------------------- |
| |
| def propmods_with_changelists(sbox): |
| "propset/del/get/list --changelist" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Add files to changelists based on the last character in their names. |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| # Set property 'name'='value' on all working copy items. |
| svntest.main.run_svn(None, "pset", "--depth", "infinity", |
| "name", "value", wc_dir) |
| expected_disk = svntest.main.greek_state.copy() |
| expected_disk.add({'' : Item(props={ 'name' : 'value' })}) |
| expected_disk.tweak('A', 'A/B', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta', |
| 'A/B/F', 'A/B/lambda', 'A/C', 'A/D', 'A/D/G', |
| 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau', 'A/D/H', |
| 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi', 'A/D/gamma', |
| 'A/mu', 'iota', props={ 'name' : 'value' }) |
| |
| svntest.actions.verify_disk(wc_dir, expected_disk, True) |
| |
| # Proplist the 'i' changelist |
| exit_code, output, errput = svntest.main.run_svn(None, "proplist", "--depth", |
| "infinity", "--changelist", |
| "i", wc_dir) |
| ### Really simple sanity check on the output of 'proplist'. If we've got |
| ### a proper proplist content checker anywhere, we should probably use it |
| ### instead. |
| if len(output) != 6: |
| raise svntest.Failure |
| |
| # Remove the 'name' property from files in the 'o' and 'i' changelists. |
| svntest.main.run_svn(None, "pdel", "--depth", "infinity", |
| "name", "--changelist", "o", "--changelist", "i", |
| wc_dir) |
| expected_disk.tweak('A/D/G/pi', 'A/D/G/rho', 'A/D/H/chi', 'A/D/H/psi', |
| props={}) |
| svntest.actions.verify_disk(wc_dir, expected_disk, True) |
| |
| # Add 'foo'='bar' property on all files under A/B to depth files and |
| # in changelist 'a'. |
| svntest.main.run_svn(None, "pset", "--depth", "files", |
| "foo", "bar", "--changelist", "a", |
| os.path.join(wc_dir, 'A', 'B')) |
| expected_disk.tweak('A/B/lambda', props={ 'name' : 'value', |
| 'foo' : 'bar' }) |
| svntest.actions.verify_disk(wc_dir, expected_disk, True) |
| |
| # Add 'bloo'='blarg' property to all files in changelist 'a'. |
| svntest.main.run_svn(None, "pset", "--depth", "infinity", |
| "bloo", "blarg", "--changelist", "a", |
| wc_dir) |
| expected_disk.tweak('A/B/lambda', props={ 'name' : 'value', |
| 'foo' : 'bar', |
| 'bloo' : 'blarg' }) |
| expected_disk.tweak('A/B/E/alpha', 'A/B/E/beta', 'A/D/H/omega', 'A/D/gamma', |
| 'iota', props={ 'name' : 'value', |
| 'bloo' : 'blarg' }) |
| svntest.actions.verify_disk(wc_dir, expected_disk, True) |
| |
| # Propget 'name' in files in changelists 'a' and 'i' to depth files. |
| exit_code, output, errput = svntest.main.run_svn(None, "pget", |
| "--depth", "files", "name", |
| "--changelist", "a", |
| "--changelist", "i", |
| wc_dir) |
| verify_pget_output(output, { |
| os.path.join(wc_dir, 'iota') : 'value', |
| }) |
| |
| # Propget 'name' in files in changelists 'a' and 'i' to depth infinity. |
| exit_code, output, errput = svntest.main.run_svn(None, "pget", |
| "--depth", "infinity", |
| "name", |
| "--changelist", "a", |
| "--changelist", "i", |
| wc_dir) |
| verify_pget_output(output, { |
| os.path.join(wc_dir, 'A', 'D', 'gamma') : 'value', |
| os.path.join(wc_dir, 'A', 'B', 'E', 'alpha') : 'value', |
| os.path.join(wc_dir, 'iota') : 'value', |
| os.path.join(wc_dir, 'A', 'B', 'E', 'beta') : 'value', |
| os.path.join(wc_dir, 'A', 'B', 'lambda') : 'value', |
| os.path.join(wc_dir, 'A', 'D', 'H', 'omega') : 'value', |
| }) |
| |
| |
| #---------------------------------------------------------------------- |
| |
| def revert_with_changelists(sbox): |
| "revert --changelist" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Add files to changelists based on the last character in their names. |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| # Add a line of text to all the versioned files in the tree. |
| mod_all_files(wc_dir, "Please, oh please, revert me!\n") |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| expected_status.tweak('A/B/lambda', 'A/B/E/alpha', 'A/B/E/beta', |
| 'A/D/gamma', 'A/D/H/omega', 'iota', 'A/mu', |
| 'A/D/G/tau', 'A/D/G/pi', 'A/D/H/chi', |
| 'A/D/H/psi', 'A/D/G/rho', status='M ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # 'svn revert --changelist a WC_DIR' (without depth, no change expected) |
| svntest.main.run_svn(None, "revert", "--changelist", "a", wc_dir) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # 'svn revert --changelist o --depth files WC_DIR WC_DIR/A/B' (no change) |
| svntest.main.run_svn(None, "revert", "--depth", "files", |
| "--changelist", "o", |
| wc_dir, os.path.join(wc_dir, 'A', 'B')) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # 'svn revert --changelist a --depth files WC_DIR WC_DIR/A/B' |
| # (iota, lambda reverted) |
| svntest.main.run_svn(None, "revert", "--depth", "files", |
| "--changelist", "a", |
| wc_dir, os.path.join(wc_dir, 'A', 'B')) |
| expected_status.tweak('iota', 'A/B/lambda', status=' ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # 'svn revert --changelist a --changelist i --depth infinity WC_DIR' |
| # (alpha, beta, gamma, omega, pi, chi, psi reverted) |
| svntest.main.run_svn(None, "revert", "--depth", "infinity", |
| "--changelist", "a", "--changelist", "i", |
| wc_dir) |
| expected_status.tweak('A/B/E/alpha', 'A/B/E/beta', 'A/D/gamma', |
| 'A/D/H/omega', 'A/D/G/pi', 'A/D/H/chi', |
| 'A/D/H/psi', status=' ') |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # 'svn revert --depth infinity WC_DIR' (back to pristine-ness) |
| svntest.main.run_svn(None, "revert", "--depth", "infinity", |
| wc_dir) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 1) |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| #---------------------------------------------------------------------- |
| |
| def update_with_changelists(sbox): |
| "update --changelist" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| # Add a line of text to all the versioned files in the tree, commit, update. |
| mod_all_files(wc_dir, "Added line.\n") |
| svntest.main.run_svn(None, "commit", "-m", "logmsg", wc_dir) |
| svntest.main.run_svn(None, "update", wc_dir) |
| |
| # Add files to changelists based on the last character in their names. |
| changelist_all_files(wc_dir, clname_from_lastchar_cb) |
| |
| ### Backdate only the files in the 'a' and 'i' changelists at depth |
| ### files under WC_DIR and WC_DIR/A/B. |
| |
| # We expect update to only touch lambda and iota. |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/B/lambda' : Item(status='U '), |
| 'iota' : Item(status='U '), |
| }) |
| |
| # Disk state should have all the files except iota and lambda |
| # carrying new text. |
| expected_disk = svntest.main.greek_state.copy() |
| expected_disk.tweak('A/B/E/alpha', |
| contents="This is the file 'alpha'.\nAdded line.\n") |
| expected_disk.tweak('A/B/E/beta', |
| contents="This is the file 'beta'.\nAdded line.\n") |
| expected_disk.tweak('A/D/gamma', |
| contents="This is the file 'gamma'.\nAdded line.\n") |
| expected_disk.tweak('A/D/H/omega', |
| contents="This is the file 'omega'.\nAdded line.\n") |
| expected_disk.tweak('A/mu', |
| contents="This is the file 'mu'.\nAdded line.\n") |
| expected_disk.tweak('A/D/G/tau', |
| contents="This is the file 'tau'.\nAdded line.\n") |
| expected_disk.tweak('A/D/G/pi', |
| contents="This is the file 'pi'.\nAdded line.\n") |
| expected_disk.tweak('A/D/H/chi', |
| contents="This is the file 'chi'.\nAdded line.\n") |
| expected_disk.tweak('A/D/H/psi', |
| contents="This is the file 'psi'.\nAdded line.\n") |
| expected_disk.tweak('A/D/G/rho', |
| contents="This is the file 'rho'.\nAdded line.\n") |
| |
| # Status is clean, but with iota and lambda at r1 and all else at r2. |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 2) |
| expected_status.tweak('iota', 'A/B/lambda', wc_rev=1) |
| |
| # Update. |
| svntest.actions.run_and_verify_update(wc_dir, |
| expected_output, |
| expected_disk, |
| expected_status, |
| [], True, |
| "-r", "1", |
| "--changelist", "a", |
| "--changelist", "i", |
| "--depth", "files", |
| wc_dir, |
| os.path.join(wc_dir, 'A', 'B')) |
| |
| ### Backdate to depth infinity all changelists "a", "i", and "o" now. |
| |
| # We expect update to only touch all the files ending in 'a', 'i', |
| # and 'o' (except lambda and iota which were previously updated). |
| expected_output = svntest.wc.State(wc_dir, { |
| 'A/D/G/pi' : Item(status='U '), |
| 'A/D/H/chi' : Item(status='U '), |
| 'A/D/H/psi' : Item(status='U '), |
| 'A/D/G/rho' : Item(status='U '), |
| 'A/B/E/alpha' : Item(status='U '), |
| 'A/B/E/beta' : Item(status='U '), |
| 'A/D/gamma' : Item(status='U '), |
| 'A/D/H/omega' : Item(status='U '), |
| }) |
| |
| # Disk state should have only tau and mu carrying new text. |
| expected_disk = svntest.main.greek_state.copy() |
| expected_disk.tweak('A/mu', |
| contents="This is the file 'mu'.\nAdded line.\n") |
| expected_disk.tweak('A/D/G/tau', |
| contents="This is the file 'tau'.\nAdded line.\n") |
| |
| # Status is clean, but with iota and lambda at r1 and all else at r2. |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 2) |
| expected_status.tweak('iota', 'A/B/lambda', 'A/D/G/pi', 'A/D/H/chi', |
| 'A/D/H/psi', 'A/D/G/rho', 'A/B/E/alpha', |
| 'A/B/E/beta', 'A/D/gamma', 'A/D/H/omega', wc_rev=1) |
| |
| # Update. |
| svntest.actions.run_and_verify_update(wc_dir, |
| expected_output, |
| expected_disk, |
| expected_status, |
| [], True, |
| "-r", "1", |
| "--changelist", "a", |
| "--changelist", "i", |
| "--changelist", "o", |
| "--depth", "infinity", |
| wc_dir) |
| |
| def tree_conflicts_and_changelists_on_commit1(sbox): |
| "tree conflicts, changelists and commit" |
| svntest.actions.build_greek_tree_conflicts(sbox) |
| wc_dir = sbox.wc_dir |
| |
| iota = os.path.join(wc_dir, "iota") |
| rho = os.path.join(wc_dir, "A", "D", "G", "rho") |
| |
| # This file will ultimately be committed |
| svntest.main.file_append(iota, "More stuff in iota") |
| |
| # Verify that the commit is blocked when we include a tree-conflicted |
| # item. |
| svntest.main.run_svn(None, "changelist", "list", iota, rho) |
| |
| expected_error = ("svn: E155015: Aborting commit: '.*" + re.escape(rho) |
| + "' remains in .*conflict") |
| |
| svntest.actions.run_and_verify_commit(wc_dir, |
| None, None, |
| expected_error, |
| wc_dir, |
| "--changelist", |
| "list") |
| |
| # Now, test if we can commit iota without those tree-conflicts |
| # getting in the way. |
| svntest.main.run_svn(None, "changelist", "--remove", rho) |
| |
| expected_output = svntest.wc.State(wc_dir, { |
| 'iota' : Item(verb='Sending'), |
| }) |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 2) |
| expected_status.tweak('A/D/G/pi', status='D ', treeconflict='C') |
| expected_status.tweak('A/D/G/tau', status='! ', treeconflict='C', |
| wc_rev=None) |
| expected_status.tweak('A/D/G/rho', status='A ', copied='+', |
| treeconflict='C', wc_rev='-') |
| expected_status.tweak('iota', wc_rev=3, status=' ') |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, |
| expected_status, |
| [], |
| wc_dir, |
| "--changelist", |
| "list") |
| |
| |
| def tree_conflicts_and_changelists_on_commit2(sbox): |
| "more tree conflicts, changelists and commit" |
| |
| sbox.build() |
| wc_dir = sbox.wc_dir |
| |
| iota = os.path.join(wc_dir, "iota") |
| A = os.path.join(wc_dir, "A",) |
| C = os.path.join(A, "C") |
| |
| # Make a tree-conflict on A/C: |
| # Remove it, warp back, add a prop, update. |
| svntest.main.run_svn(None, 'delete', C) |
| |
| expected_output = svntest.verify.RegexOutput( |
| "Deleting.*" + re.escape(C), |
| False) |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'commit', '-m', 'delete A/C', C) |
| |
| expected_output = svntest.verify.RegexOutput( |
| "A.*" + re.escape(C), False) |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'update', C, "-r1") |
| |
| expected_output = svntest.verify.RegexOutput( |
| ".*'propname' set on '" |
| + re.escape(C) + "'", False) |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'propset', 'propname', 'propval', C) |
| |
| expected_output = svntest.verify.RegexOutput( |
| " C " + re.escape(C), False) |
| svntest.actions.run_and_verify_svn(expected_output, [], |
| 'update', wc_dir) |
| |
| |
| expected_status = svntest.actions.get_virginal_state(wc_dir, 2) |
| expected_status.tweak('A/C', status='A ', copied='+', |
| treeconflict='C', wc_rev='-') |
| |
| svntest.actions.run_and_verify_status(wc_dir, expected_status) |
| |
| # So far so good. We have a tree-conflict on an absent dir A/C. |
| |
| # Verify that the current situation does not commit. |
| expected_error = "svn: E155015: Aborting commit:.* remains in .*conflict"; |
| |
| svntest.actions.run_and_verify_commit(wc_dir, |
| None, None, |
| expected_error, |
| wc_dir) |
| |
| # Now try to commit with a changelist, not letting the |
| # tree-conflict get in the way. |
| svntest.main.file_append(iota, "More stuff in iota") |
| svntest.main.run_svn(None, "changelist", "list", iota) |
| |
| expected_output = svntest.wc.State(wc_dir, { |
| 'iota' : Item(verb='Sending'), |
| }) |
| |
| expected_status.tweak('iota', wc_rev=3, status=' ') |
| |
| svntest.actions.run_and_verify_commit(wc_dir, |
| expected_output, |
| expected_status, |
| [], |
| wc_dir, |
| "--changelist", |
| "list") |
| |
| |
| #---------------------------------------------------------------------- |
| |
| def move_keeps_changelist(sbox): |
| "'svn mv' of existing keeps the changelist" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| iota_path = os.path.join(wc_dir, 'iota') |
| iota2_path = iota_path + '2' |
| |
| # 'svn mv' of existing file should *copy* the changelist to the new place |
| svntest.main.run_svn(None, "changelist", 'foo', iota_path) |
| svntest.main.run_svn(None, "rename", iota_path, iota2_path) |
| expected_infos = [ |
| { |
| 'Name' : 'iota', |
| 'Schedule' : 'delete', |
| 'Changelist' : 'foo', |
| }, |
| { |
| 'Name' : 'iota2', |
| 'Schedule' : 'add', |
| 'Changelist' : 'foo', # this line fails the test |
| }, |
| ] |
| svntest.actions.run_and_verify_info(expected_infos, iota_path, iota2_path) |
| |
| def move_added_keeps_changelist(sbox): |
| "'svn mv' of added keeps the changelist" |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| repo_url = sbox.repo_url |
| |
| kappa_path = os.path.join(wc_dir, 'kappa') |
| kappa2_path = kappa_path + '2' |
| |
| # add 'kappa' (do not commit!) |
| svntest.main.file_write(kappa_path, "This is the file 'kappa'.\n") |
| svntest.main.run_svn(None, 'add', kappa_path) |
| |
| # 'svn mv' of added file should *move* the changelist to the new place |
| svntest.main.run_svn(None, "changelist", 'foo', kappa_path) |
| svntest.main.run_svn(None, "rename", kappa_path, kappa2_path) |
| |
| # kappa not under version control |
| svntest.actions.run_and_verify_svnversion(kappa_path, repo_url, |
| [], ".*doesn't exist.*") |
| # kappa2 in a changelist |
| expected_infos = [ |
| { |
| 'Name' : 'kappa2', |
| 'Schedule' : 'add', |
| 'Changelist' : 'foo', # this line fails the test |
| }, |
| ] |
| svntest.actions.run_and_verify_info(expected_infos, kappa2_path) |
| |
| @Issue(3820) |
| def change_to_dir(sbox): |
| "change file in changelist to dir" |
| |
| sbox.build() |
| |
| # No changelist initially |
| expected_infos = [{'Name' : 'mu', 'Changelist' : None}] |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu visible in changelist |
| svntest.actions.run_and_verify_svn(None, [], |
| 'changelist', 'qq', sbox.ospath('A/mu')) |
| expected_infos = [{'Name' : 'mu', 'Changelist' : 'qq'}] |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu still visible after delete |
| svntest.actions.run_and_verify_svn(None, [], 'rm', sbox.ospath('A/mu')) |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu removed from changelist after replace with directory |
| svntest.actions.run_and_verify_svn('^A|' + _re_cl_rem_pattern, [], |
| 'mkdir', sbox.ospath('A/mu')) |
| expected_infos = [{'Changelist' : None}] # No Name for directories? |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| svntest.main.run_svn(None, "commit", "-m", "r2: replace A/mu: file->dir", |
| sbox.ospath('A')) |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| svntest.main.run_svn(None, "update", "-r", "1", sbox.ospath('A')) |
| expected_infos = [{'Name' : 'mu', 'Changelist' : None}] |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu visible in changelist |
| svntest.actions.run_and_verify_svn(None, [], |
| 'changelist', 'qq', sbox.ospath('A/mu')) |
| expected_infos = [{'Name' : 'mu', 'Changelist' : 'qq'}] |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu removed from changelist after replace with dir via merge |
| svntest.main.run_svn(None, "merge", "-c", "2", sbox.ospath('A'), |
| sbox.ospath('A')) |
| expected_infos = [{'Changelist' : None}] # No Name for directories? |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| |
| @Issue(3822) |
| def revert_deleted_in_changelist(sbox): |
| "revert a deleted file in a changelist" |
| |
| sbox.build(read_only = True) |
| |
| # No changelist initially |
| expected_infos = [{'Name' : 'mu', 'Changelist' : None}] |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu visible in changelist |
| svntest.actions.run_and_verify_svn(None, [], |
| 'changelist', 'qq', sbox.ospath('A/mu')) |
| expected_infos = [{'Name' : 'mu', 'Changelist' : 'qq'}] |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu still visible after delete |
| svntest.actions.run_and_verify_svn(None, [], 'rm', sbox.ospath('A/mu')) |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu still visible after revert |
| svntest.actions.run_and_verify_svn(None, [], |
| 'revert', sbox.ospath('A/mu')) |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu still visible after parent delete |
| svntest.actions.run_and_verify_svn(None, [], 'rm', sbox.ospath('A')) |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| # A/mu still visible after revert |
| svntest.actions.run_and_verify_svn(None, [], |
| 'revert', '-R', sbox.ospath('A')) |
| svntest.actions.run_and_verify_info(expected_infos, sbox.ospath('A/mu')) |
| |
| def add_remove_non_existent_target(sbox): |
| "add and remove non-existent target to changelist" |
| |
| sbox.build(read_only = True) |
| wc_dir = sbox.wc_dir |
| bogus_path = os.path.join(wc_dir, 'A', 'bogus') |
| |
| expected_err = "svn: warning: W155010: The node '" + \ |
| re.escape(os.path.abspath(bogus_path)) + \ |
| "' was not found" |
| |
| svntest.actions.run_and_verify_svn(None, expected_err, |
| 'changelist', 'testlist', |
| bogus_path) |
| |
| svntest.actions.run_and_verify_svn(None, expected_err, |
| 'changelist', bogus_path, |
| '--remove') |
| |
| def add_remove_unversioned_target(sbox): |
| "add and remove unversioned target to changelist" |
| |
| sbox.build(read_only = True) |
| unversioned = sbox.ospath('unversioned') |
| svntest.main.file_write(unversioned, "dummy contents", 'w+') |
| |
| expected_err = "svn: warning: W155010: The node '" + \ |
| re.escape(os.path.abspath(unversioned)) + \ |
| "' was not found" |
| |
| svntest.actions.run_and_verify_svn(None, expected_err, |
| 'changelist', 'testlist', |
| unversioned) |
| |
| svntest.actions.run_and_verify_svn(None, expected_err, |
| 'changelist', unversioned, |
| '--remove') |
| |
| @Issue(3985) |
| def readd_after_revert(sbox): |
| "add new file to changelist, revert and readd" |
| sbox.build(read_only = True) |
| |
| dummy = sbox.ospath('dummy') |
| svntest.main.file_write(dummy, "dummy contents") |
| |
| sbox.simple_add('dummy') |
| svntest.actions.run_and_verify_svn(None, [], |
| 'changelist', 'testlist', |
| dummy) |
| |
| sbox.simple_revert('dummy') |
| |
| svntest.main.file_write(dummy, "dummy contents") |
| |
| svntest.actions.run_and_verify_svn(None, [], |
| 'add', dummy) |
| |
| |
| ######################################################################## |
| # Run the tests |
| |
| # list all tests here, starting with None: |
| test_list = [ None, |
| add_remove_changelists, |
| commit_one_changelist, |
| commit_multiple_changelists, |
| info_with_changelists, |
| diff_with_changelists, |
| propmods_with_changelists, |
| revert_with_changelists, |
| update_with_changelists, |
| tree_conflicts_and_changelists_on_commit1, |
| tree_conflicts_and_changelists_on_commit2, |
| move_keeps_changelist, |
| move_added_keeps_changelist, |
| change_to_dir, |
| revert_deleted_in_changelist, |
| add_remove_non_existent_target, |
| add_remove_unversioned_target, |
| readd_after_revert, |
| ] |
| |
| if __name__ == '__main__': |
| svntest.main.run_tests(test_list) |
| # NOTREACHED |
| |
| |
| ### End of file. |