blob: 91cedc8d46a0466c6e7cf4904a457c8f343b9dda [file] [log] [blame]
#!/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 select_paths(target_path, depth, changelists, name_func):
"""Return the subset of paths found on disk at TARGET_PATH, to a depth
of DEPTH, that match CHANGELISTS.
NAME_FUNC, rather than the working copy, determines what
changelist each path is associated with.
Returned paths are relative to the CWD.
### Only the paths of files are returned.
"""
dot_svn = svntest.main.get_admin_name()
for dirpath, dirs, files in os.walk(target_path):
# prepare to return paths relative to WC_DIR
if dot_svn in dirs:
dirs.remove(dot_svn)
if not changelists: # When changelists support dirs, add: "or name_func(name) in changelists"
yield os.path.normpath(dirpath)
if depth == 'empty':
dirs[:] = [] # process no subdirs
continue # nor files
for name in files:
if not changelists or name_func(name) in changelists:
yield os.path.normpath(os.path.join(dirpath, name))
if depth == 'files':
dirs[:] = [] # process no subdirs
if depth == 'immediates':
depth = 'empty' # process subdirs, but no files nor dirs in them
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)))
#----------------------------------------------------------------------
@XFail()
@Issue(4826)
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")
# Also make a property modification on each directory.
svntest.main.run_svn(None, 'propset', 'p', 'v', '-R', 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 is_repos_wc in [0, 1]:
for clname in [['a'], ['a', 'i'], []]:
for depth in ['empty', 'files', 'immediates', 'infinity', None]:
for subdir in ['.', 'A', 'A/D']:
# Figure out what we expect to see in our diff output.
expected_paths = sorted(select_paths(sbox.ospath(subdir), depth, clname, clname_from_lastchar_cb))
# 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 + '/' + subdir)
args.append('--new')
args.append(os.path.join(wc_dir, subdir))
else:
args.append(os.path.join(wc_dir, subdir))
# 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)
#----------------------------------------------------------------------
# A wc-wc diff returned no results if changelists were specified and the
# diff target dir was not the WC root.
@Issue(4822)
def diff_with_changelists_subdir(sbox):
"diff --changelist (wc-wc) in subdir of WC"
sbox.build()
wc_dir = sbox.wc_dir
expected_paths = sbox.ospaths(['A/D/gamma'])
subdir = 'A/D'
clname = 'a'
for path in expected_paths:
svntest.main.file_append(path, "New text.\n")
svntest.main.run_svn(None, "changelist", clname, *expected_paths)
# Run 'svn diff ...'
exit_code, output, errput = svntest.main.run_svn(None,
'diff', '--changelist', clname,
sbox.ospath(subdir))
# 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)))
########################################################################
# 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,
diff_with_changelists_subdir,
]
if __name__ == '__main__':
svntest.main.run_tests(test_list)
# NOTREACHED
### End of file.