#!/usr/bin/env python
#
# revert_tests.py: testing 'svn revert'.
#
# Subversion is a tool for revision control.
# See http://subversion.tigris.org for more information.
#
# ====================================================================
# Copyright (c) 2000-2004 CollabNet. All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://subversion.tigris.org/license-1.html.
# If newer versions of this license are posted there, you may use a
# newer version instead, at your option.
#
######################################################################
# General modules
import shutil, string, sys, stat, re, os
# Our testing module
import svntest
from svntest import wc
# (abbreviation)
Skip = svntest.testcase.Skip
XFail = svntest.testcase.XFail
Item = svntest.wc.StateItem
######################################################################
# Helpers
def revert_replacement_with_props(sbox, wc_copy):
"""Helper implementing the core of
revert_{repos,wc}_to_wc_replace_with_props().
Uses a working copy (when wc_copy == True) or a URL (when wc_copy == False)
source to copy from.
"""
sbox.build()
wc_dir = sbox.wc_dir
# Use a temp file to set properties with wildcards in their values
# otherwise Win32/VS2005 will expand them
prop_path = os.path.join(wc_dir, 'proptmp')
svntest.main.file_append (prop_path, '*')
# Set props on file which is copy-source later on
pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
svntest.actions.run_and_verify_svn("", None, [],
'ps', 'phony-prop', '-F', prop_path,
pi_path)
os.remove(prop_path)
svntest.actions.run_and_verify_svn("", None, [],
'ps', 'svn:eol-style', 'LF', rho_path)
# Verify props having been set
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_disk.tweak('A/D/G/pi',
props={ 'phony-prop': '*' })
expected_disk.tweak('A/D/G/rho',
props={ 'svn:eol-style': 'LF' })
actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
svntest.tree.compare_trees(actual_disk, expected_disk.old_tree())
# Commit props
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/pi': Item(verb='Sending'),
'A/D/G/rho': Item(verb='Sending'),
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak(repos_rev='2')
expected_status.tweak('A/D/G/pi', wc_rev='2')
expected_status.tweak('A/D/G/rho', wc_rev='2')
svntest.actions.run_and_verify_commit(wc_dir,
expected_output,
expected_status,
None, None, None, None, None,
wc_dir)
# Bring wc into sync
svntest.actions.run_and_verify_svn("", None, [], 'up', wc_dir)
# File scheduled for deletion
svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
# Status before attempting copies
expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
expected_status.tweak('A/D/G/rho', status='D ')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# The copy shouldn't fail
if wc_copy:
pi_src = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
else:
pi_src = svntest.main.current_repo_url + '/A/D/G/pi'
svntest.actions.run_and_verify_svn("", None, [],
'cp', pi_src, rho_path)
# Verify both content and props have been copied
expected_disk.tweak('A/D/G/rho',
contents="This is the file 'pi'.\n",
props={ 'phony-prop': '*' })
actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
svntest.tree.compare_trees(actual_disk, expected_disk.old_tree())
# Now revert
expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
expected_status.tweak(repos_rev='3')
expected_status.tweak('A/D/G/rho', status=' ', copied=None,
repos_rev='3', wc_rev='3')
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/rho': Item(verb='Replacing'),
})
svntest.actions.run_and_verify_svn("", None, [],
'revert', '-R', wc_dir)
# Check disk status
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_disk.tweak('A/D/G/pi',
props={ 'phony-prop': '*' })
expected_disk.tweak('A/D/G/rho',
props={ 'svn:eol-style': 'LF' })
actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
svntest.tree.compare_trees(actual_disk, expected_disk.old_tree())
######################################################################
# Tests
#
# Each test must return on success or raise on failure.
#----------------------------------------------------------------------
def revert_from_wc_root(sbox):
"revert relative to wc root"
sbox.build()
wc_dir = sbox.wc_dir
saved_dir = os.getcwd()
try:
os.chdir(wc_dir)
# Mostly taken from basic_revert
# Modify some files and props.
beta_path = os.path.join('A', 'B', 'E', 'beta')
gamma_path = os.path.join('A', 'D', 'gamma')
iota_path = 'iota'
rho_path = os.path.join('A', 'D', 'G', 'rho')
zeta_path = os.path.join('A', 'D', 'H', 'zeta')
svntest.main.file_append(beta_path, "Added some text to 'beta'.\n")
svntest.main.file_append(iota_path, "Added some text to 'iota'.\n")
svntest.main.file_append(rho_path, "Added some text to 'rho'.\n")
svntest.main.file_append(zeta_path, "Added some text to 'zeta'.\n")
svntest.actions.run_and_verify_svn("Add command", None, [],
'add', zeta_path)
svntest.actions.run_and_verify_svn("Add prop command", None, [],
'ps', 'random-prop', 'propvalue',
gamma_path)
svntest.actions.run_and_verify_svn("Add prop command", None, [],
'ps', 'random-prop', 'propvalue',
iota_path)
svntest.actions.run_and_verify_svn("Add prop command", None, [],
'ps', 'random-prop', 'propvalue',
'.')
svntest.actions.run_and_verify_svn("Add prop command", None, [],
'ps', 'random-prop', 'propvalue',
'A')
# Verify modified status.
expected_output = svntest.actions.get_virginal_state('', 1)
expected_output.tweak('A/B/E/beta', 'A/D/G/rho', status='M ')
expected_output.tweak('iota', status='MM')
expected_output.tweak('', 'A/D/gamma', 'A', status=' M')
expected_output.add({
'A/D/H/zeta' : Item(status='A ', wc_rev=0),
})
svntest.actions.run_and_verify_status ('', expected_output)
# Run revert
svntest.actions.run_and_verify_svn("Revert command", None, [],
'revert', beta_path)
svntest.actions.run_and_verify_svn("Revert command", None, [],
'revert', gamma_path)
svntest.actions.run_and_verify_svn("Revert command", None, [],
'revert', iota_path)
svntest.actions.run_and_verify_svn("Revert command", None, [],
'revert', rho_path)
svntest.actions.run_and_verify_svn("Revert command", None, [],
'revert', zeta_path)
svntest.actions.run_and_verify_svn("Revert command", None, [],
'revert', '.')
svntest.actions.run_and_verify_svn("Revert command", None, [],
'revert', 'A')
# Verify unmodified status.
expected_output = svntest.actions.get_virginal_state('', 1)
svntest.actions.run_and_verify_status ('', expected_output)
finally:
os.chdir(saved_dir)
def revert_reexpand_keyword(sbox):
"revert reexpands manually contracted keyword"
# This is for issue #1663. The bug is that if the only difference
# between a locally modified working file and the base version of
# same was that the former had a contracted keyword that would be
# expanded in the latter, then 'svn revert' wouldn't notice the
# difference, and therefore wouldn't revert. And why wouldn't it
# notice? Since text bases are always stored with keywords
# contracted, and working files are contracted before comparison
# with text base, there would appear to be no difference when the
# contraction is the only difference. For most commands, this is
# correct -- but revert's job is to restore the working file, not
# the text base.
sbox.build()
wc_dir = sbox.wc_dir
newfile_path = os.path.join(wc_dir, "newfile")
unexpanded_contents = "This is newfile: $Rev$.\n"
# Put an unexpanded keyword into iota.
fp = open(newfile_path, 'w')
fp.write(unexpanded_contents)
fp.close()
# Commit, without svn:keywords property set.
svntest.main.run_svn(None, 'add', newfile_path)
svntest.main.run_svn(None, 'commit', '-m', 'r2', newfile_path)
# Set the property and commit. This should expand the keyword.
svntest.main.run_svn(None, 'propset', 'svn:keywords', 'rev', newfile_path)
svntest.main.run_svn(None, 'commit', '-m', 'r3', newfile_path)
# Verify that the keyword got expanded.
def check_expanded(path):
fp = open(path, 'r')
lines = fp.readlines()
fp.close()
if lines[0] != "This is newfile: $Rev: 3 $.\n":
raise svntest.Failure
check_expanded(newfile_path)
# Now un-expand the keyword again.
fp = open(newfile_path, 'w')
fp.write(unexpanded_contents)
fp.close()
fp = open(newfile_path, 'r')
lines = fp.readlines()
fp.close()
# Revert the file. The keyword should reexpand.
svntest.main.run_svn(None, 'revert', newfile_path)
# Verify that the keyword got re-expanded.
check_expanded(newfile_path)
#----------------------------------------------------------------------
# Regression test for issue #1775:
# Should be able to revert a file with no properties i.e. no prop-base
def revert_replaced_file_without_props(sbox):
"revert a replaced file with no properties"
sbox.build()
wc_dir = sbox.wc_dir
file1_path = os.path.join(wc_dir, 'file1')
# Add a new file, file1, that has no prop-base
svntest.main.file_append(file1_path, "This is the file 'file1' revision 2.")
svntest.actions.run_and_verify_svn(None, None, [], 'add', file1_path)
# commit file1
expected_output = svntest.wc.State(wc_dir, {
'file1' : Item(verb='Adding')
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
expected_status.tweak(wc_rev=1)
expected_status.add({
'file1' : Item(status=' ', wc_rev=2),
})
svntest.actions.run_and_verify_commit (wc_dir, expected_output,
expected_status, None, None,
None, None, None, wc_dir)
# delete file1
svntest.actions.run_and_verify_svn(None, None, [], 'rm', file1_path)
# test that file1 is scheduled for deletion.
expected_status.tweak('file1', status='D ')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# recreate and add file1
svntest.main.file_append(file1_path, "This is the file 'file1' revision 3.")
svntest.actions.run_and_verify_svn(None, None, [], 'add', file1_path)
# Test to see if file1 is schedule for replacement
expected_status.tweak('file1', status='R ')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# revert file1
svntest.actions.run_and_verify_svn(None, ["Reverted '" + file1_path + "'\n"],
[], 'revert', file1_path)
# test that file1 really was reverted
expected_status.tweak('file1', status=' ', wc_rev=2)
svntest.actions.run_and_verify_status(wc_dir, expected_status)
#----------------------------------------------------------------------
# Regression test for issue #876:
# svn revert of an svn move'd file does not revert the file
def revert_moved_file(sbox):
"revert a moved file"
sbox.build()
wc_dir = sbox.wc_dir
iota_path = os.path.join(wc_dir, 'iota')
iota_path_moved = os.path.join(wc_dir, 'iota_moved')
svntest.actions.run_and_verify_svn(None, None, [], 'mv', iota_path,
iota_path_moved)
expected_output = svntest.actions.get_virginal_state(wc_dir, 1)
expected_output.tweak('iota', status='D ')
expected_output.add({
'iota_moved' : Item(status='A ', copied='+', wc_rev='-'),
})
svntest.actions.run_and_verify_status(wc_dir, expected_output)
# now revert the file iota
svntest.actions.run_and_verify_svn(None,
["Reverted '" + iota_path + "'\n"], [], 'revert', iota_path)
# at this point, svn status on iota_path_moved should return nothing
# since it should disappear on reverting the move, and since svn status
# on a non-existent file returns nothing.
svntest.actions.run_and_verify_svn(None, [], [],
'status', '-v', iota_path_moved)
#----------------------------------------------------------------------
# Test for issue 2135
#
# It is like merge_file_replace (in merge_tests.py), but reverts file
# instead of commit.
def revert_file_merge_replace_with_history(sbox):
"revert a merge replacement of file with history"
sbox.build()
wc_dir = sbox.wc_dir
# File scheduled for deletion
rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/D/G/rho', status='D ')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/rho': Item(verb='Deleting'),
})
expected_status.remove('A/D/G/rho')
# Commit rev 2
svntest.actions.run_and_verify_commit(wc_dir,
expected_output,
expected_status,
None, None, None, None, None,
wc_dir)
# create new rho file
fp = open(rho_path, 'w')
fp.write("new rho\n")
fp.close()
# Add the new file
svntest.actions.run_and_verify_svn(None, None, [], 'add', rho_path)
# Commit revsion 3
expected_status.add({
'A/D/G/rho' : Item(status='A ', wc_rev='0')
})
svntest.actions.run_and_verify_status(wc_dir, expected_status)
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/rho': Item(verb='Adding'),
})
svntest.actions.run_and_verify_commit(wc_dir,
expected_output,
None,
None, None, None, None, None,
wc_dir)
# Update working copy
expected_output = svntest.wc.State(wc_dir, {})
expected_disk = svntest.main.greek_state.copy()
expected_disk.tweak('A/D/G/rho', contents='new rho\n' )
expected_status.tweak(wc_rev='3')
expected_status.tweak('A/D/G/rho', status=' ')
svntest.actions.run_and_verify_update(wc_dir,
expected_output,
expected_disk,
expected_status)
# merge changes from r3:1
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/rho': Item(status='A ')
})
expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
expected_skip = wc.State(wc_dir, { })
expected_disk.tweak('A/D/G/rho', contents="This is the file 'rho'.\n")
svntest.actions.run_and_verify_merge(wc_dir, '3', '1',
svntest.main.current_repo_url,
expected_output,
expected_disk,
expected_status,
expected_skip)
# Now revert
svntest.actions.run_and_verify_svn(None,
None,
[], 'revert', rho_path)
# test that rho really was reverted
expected_status.tweak('A/D/G/rho', copied=None, status=' ', wc_rev=3)
svntest.actions.run_and_verify_status(wc_dir, expected_status)
actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
expected_disk.tweak('A/D/G/rho', contents="new rho\n")
svntest.tree.compare_trees(actual_disk, expected_disk.old_tree())
def revert_wc_to_wc_replace_with_props(sbox):
"revert svn cp PATH PATH replace file with props"
revert_replacement_with_props(sbox, 1)
def revert_repos_to_wc_replace_with_props(sbox):
"revert svn cp URL PATH replace file with props"
revert_replacement_with_props(sbox, 0)
def revert_after_second_replace(sbox):
"revert file after second replace"
sbox.build()
wc_dir = sbox.wc_dir
# File scheduled for deletion
rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)
# Status before attempting copy
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/D/G/rho', status='D ')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# Replace file for the first time
pi_src = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
svntest.actions.run_and_verify_svn("", None, [],
'cp', pi_src, rho_path)
expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# Now delete replaced file.
svntest.actions.run_and_verify_svn(None, None, [], 'rm', '--force', rho_path)
# Status should be same as after first delete
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/D/G/rho', status='D ')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# Replace file for the second time
pi_src = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
svntest.actions.run_and_verify_svn("", None, [], 'cp', pi_src, rho_path)
expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# Now revert
svntest.actions.run_and_verify_svn("", None, [],
'revert', '-R', wc_dir)
# Check disk status
expected_disk = svntest.main.greek_state.copy()
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
svntest.tree.compare_trees(actual_disk, expected_disk.old_tree())
#----------------------------------------------------------------------
# Tests for issue #2517.
#
# Manual conflict resolution leads to spurious revert report.
def revert_after_manual_conflict_resolution__text(sbox):
"revert after manual text-conflict resolution"
# Make two working copies
sbox.build()
wc_dir_1 = sbox.wc_dir
wc_dir_2 = sbox.add_wc_path('other')
svntest.actions.duplicate_dir(wc_dir_1, wc_dir_2)
# Cause a (text) conflict
iota_path_1 = os.path.join(wc_dir_1, 'iota')
iota_path_2 = os.path.join(wc_dir_2, 'iota')
svntest.main.file_write(iota_path_1, 'Modified iota text')
svntest.main.file_write(iota_path_2, 'Conflicting iota text')
svntest.main.run_svn(None, 'commit', '-m', 'r2', wc_dir_1)
svntest.main.run_svn(None, 'update', wc_dir_2)
# Resolve the conflict "manually"
svntest.main.file_write(iota_path_2, 'Modified iota text')
os.remove(iota_path_2 + '.mine')
os.remove(iota_path_2 + '.r1')
os.remove(iota_path_2 + '.r2')
# Verify no output from status, diff, or revert
svntest.actions.run_and_verify_svn(None, [], [], "status", wc_dir_2)
svntest.actions.run_and_verify_svn(None, [], [], "diff", wc_dir_2)
svntest.actions.run_and_verify_svn(None, [], [], "revert", "-R", wc_dir_2)
def revert_after_manual_conflict_resolution__prop(sbox):
"revert after manual property-conflict resolution"
# Make two working copies
sbox.build()
wc_dir_1 = sbox.wc_dir
wc_dir_2 = sbox.add_wc_path('other')
svntest.actions.duplicate_dir(wc_dir_1, wc_dir_2)
# Cause a (property) conflict
iota_path_1 = os.path.join(wc_dir_1, 'iota')
iota_path_2 = os.path.join(wc_dir_2, 'iota')
svntest.main.run_svn(None, 'propset', 'foo', '1', iota_path_1)
svntest.main.run_svn(None, 'propset', 'foo', '2', iota_path_2)
svntest.main.run_svn(None, 'commit', '-m', 'r2', wc_dir_1)
svntest.main.run_svn(None, 'update', wc_dir_2)
# Resolve the conflict "manually"
svntest.main.run_svn(None, 'propset', 'foo', '1', iota_path_2)
os.remove(iota_path_2 + '.prej')
# Verify no output from status, diff, or revert
svntest.actions.run_and_verify_svn(None, [], [], "status", wc_dir_2)
svntest.actions.run_and_verify_svn(None, [], [], "diff", wc_dir_2)
svntest.actions.run_and_verify_svn(None, [], [], "revert", "-R", wc_dir_2)
def revert_propset__dir(sbox):
"revert a simple propset on a dir"
sbox.build()
wc_dir = sbox.wc_dir
a_path = os.path.join(wc_dir, 'A')
svntest.main.run_svn(None, 'propset', 'foo', 'x', a_path)
expected_output = re.escape("Reverted '" + a_path + "'")
svntest.actions.run_and_verify_svn(None, expected_output, [], "revert",
a_path)
def revert_propset__file(sbox):
"revert a simple propset on a file"
sbox.build()
wc_dir = sbox.wc_dir
iota_path = os.path.join(wc_dir, 'iota')
svntest.main.run_svn(None, 'propset', 'foo', 'x', iota_path)
expected_output = re.escape("Reverted '" + iota_path + "'")
svntest.actions.run_and_verify_svn(None, expected_output, [], "revert",
iota_path)
def revert_propdel__dir(sbox):
"revert a simple propdel on a dir"
sbox.build()
wc_dir = sbox.wc_dir
a_path = os.path.join(wc_dir, 'A')
svntest.main.run_svn(None, 'propset', 'foo', 'x', a_path)
svntest.main.run_svn(None, 'commit', '-m', 'ps', a_path)
svntest.main.run_svn(None, 'propdel', 'foo', a_path)
expected_output = re.escape("Reverted '" + a_path + "'")
svntest.actions.run_and_verify_svn(None, expected_output, [], "revert",
a_path)
def revert_propdel__file(sbox):
"revert a simple propdel on a file"
sbox.build()
wc_dir = sbox.wc_dir
iota_path = os.path.join(wc_dir, 'iota')
svntest.main.run_svn(None, 'propset', 'foo', 'x', iota_path)
svntest.main.run_svn(None, 'commit', '-m', 'ps', iota_path)
svntest.main.run_svn(None, 'propdel', 'foo', iota_path)
expected_output = re.escape("Reverted '" + iota_path + "'")
svntest.actions.run_and_verify_svn(None, expected_output, [], "revert",
iota_path)
########################################################################
# Run the tests
# list all tests here, starting with None:
test_list = [ None,
revert_from_wc_root,
revert_reexpand_keyword,
revert_replaced_file_without_props,
XFail(revert_moved_file),
revert_wc_to_wc_replace_with_props,
revert_file_merge_replace_with_history,
revert_repos_to_wc_replace_with_props,
revert_after_second_replace,
revert_after_manual_conflict_resolution__text,
revert_after_manual_conflict_resolution__prop,
revert_propset__dir,
revert_propset__file,
revert_propdel__dir,
revert_propdel__file,
]
if __name__ == '__main__':
svntest.main.run_tests(test_list)
# NOTREACHED
### End of file.
syntax highlighted by Code2HTML, v. 0.9.1