blob: 31588e2f781f02b558189bf1406f80e8b64abe1a [file] [log] [blame]
#!/usr/bin/env python
#
# history_tests.py: testing history-tracing code
#
# Subversion is a tool for revision control.
# See http://subversion.apache.org for more information.
#
# ====================================================================
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
######################################################################
# General modules
import os
# Our testing module
import svntest
from svntest import wc
# (abbreviation)
Skip = svntest.testcase.Skip_deco
SkipUnless = svntest.testcase.SkipUnless_deco
XFail = svntest.testcase.XFail_deco
Issues = svntest.testcase.Issues_deco
Issue = svntest.testcase.Issue_deco
Wimp = svntest.testcase.Wimp_deco
Item = wc.StateItem
######################################################################
# Tests
#
# Each test must return on success or raise on failure.
#----------------------------------------------------------------------
def cat_traces_renames(sbox):
"verify that 'svn cat' traces renames"
sbox.build()
wc_dir = sbox.wc_dir
rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
bloo_path = os.path.join(wc_dir, 'A', 'D', 'G', 'bloo')
# rename rho to bloo. commit r2.
svntest.main.run_svn(None, 'mv', rho_path, bloo_path)
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/rho' : Item(verb='Deleting'),
'A/D/G/bloo' : Item(verb='Adding')
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.remove('A/D/G/rho')
expected_status.add({ 'A/D/G/bloo' :
Item(wc_rev=2, status=' ') })
svntest.actions.run_and_verify_commit(wc_dir,
expected_output,
expected_status)
# rename pi to rho. commit r3.
svntest.main.run_svn(None, 'mv', pi_path, rho_path)
# svn cat -r1 rho --> should show pi's contents.
svntest.actions.run_and_verify_svn([ "This is the file 'pi'.\n"], [],
'cat', '-r', '1', rho_path)
expected_output = svntest.wc.State(wc_dir, {
'A/D/G/pi' : Item(verb='Deleting'),
'A/D/G/rho' : Item(verb='Adding')
})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.remove('A/D/G/pi')
expected_status.tweak('A/D/G/rho', wc_rev=3)
expected_status.add({ 'A/D/G/bloo' :
Item(wc_rev=2, status=' ') })
svntest.actions.run_and_verify_commit(wc_dir,
expected_output,
expected_status)
# update whole wc to HEAD
expected_output = svntest.wc.State(wc_dir, { }) # no output
expected_status.tweak(wc_rev=3)
expected_disk = svntest.main.greek_state.copy()
expected_disk.remove('A/D/G/pi', 'A/D/G/rho')
expected_disk.add({
'A/D/G/rho' : Item("This is the file 'pi'.\n"),
})
expected_disk.add({
'A/D/G/bloo' : Item("This is the file 'rho'.\n"),
})
svntest.actions.run_and_verify_update(wc_dir,
expected_output,
expected_disk,
expected_status)
# 'svn cat bloo' --> should show rho's contents.
svntest.actions.run_and_verify_svn([ "This is the file 'rho'.\n"], [],
'cat', bloo_path)
# svn cat -r1 bloo --> should still show rho's contents.
svntest.actions.run_and_verify_svn([ "This is the file 'rho'.\n"], [],
'cat', '-r', '1', bloo_path)
# svn cat -r1 rho --> should show pi's contents.
svntest.actions.run_and_verify_svn([ "This is the file 'pi'.\n"], [],
'cat', '-r', '1', rho_path)
# svn up -r1
svntest.actions.run_and_verify_svn(None, [], 'up', '-r', '1', wc_dir)
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
svntest.actions.run_and_verify_status(wc_dir, expected_status)
# svn cat -rHEAD rho --> should see 'unrelated object' error.
svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput,
'cat', '-r', 'HEAD', rho_path)
@Issue(1970)
def cat_avoids_false_identities(sbox):
"verify that 'svn cat' avoids false identities"
sbox.build()
wc_dir = sbox.wc_dir
# Issue #1970
#
# Highlight a bug in the client side use of the repository's
# location searching algorithm.
#
# The buggy history-following algorithm determines the paths that a
# line of history would be *expected to be* found in a given revision,
# but doesn't treat copies as gaps in the historical sequence. If
# some other object fills those gaps at the same expected path, the
# client will find the wrong object.
#
# In the recipe below, iota gets created in r1. In r2, it is
# deleted and replaced with an unrelated object at the same path.
# In r3, the interloper is deleted. In r4, the original iota is
# resurrected via a copy from r1.
#
# ,- - - - - - --.
# o---| o---| o o----->
#
# | | | |
# r1 r2 r3 r4
#
# In a working copy at r4, running
#
# $ svn cat -r2 iota
#
# should result in an error, but with the bug it instead cats the r2
# interloper.
#
# To reassure yourself that that's wrong, recall that the above
# command is equivalent to
#
# $ svn cat -r2 iota@4
#
# Now do you see the evil that lies within us?
iota_path = os.path.join(wc_dir, 'iota')
iota_url = sbox.repo_url + '/iota'
# r2
svntest.main.run_svn(None, 'del', iota_path)
svntest.main.file_append(iota_path, "YOU SHOULD NOT SEE THIS\n")
svntest.main.run_svn(None, 'add', iota_path)
sbox.simple_commit(message='log msg')
svntest.main.run_svn(None, 'up', wc_dir)
# r3
svntest.main.run_svn(None, 'del', iota_path)
sbox.simple_commit(message='log msg')
svntest.main.run_svn(None, 'up', wc_dir)
# r4
svntest.main.run_svn(None, 'cp', iota_url + '@1', wc_dir)
sbox.simple_commit(message='log msg')
svntest.main.run_svn(None, 'up', wc_dir)
# 'svn cat -r2 iota' should error, because the line of history
# currently identified by /iota did not exist in r2, even though a
# totally unrelated file of the same name did.
svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput,
'cat', '-r', '2', iota_path)
########################################################################
# Run the tests
# list all tests here, starting with None:
test_list = [ None,
cat_traces_renames,
cat_avoids_false_identities,
]
if __name__ == '__main__':
svntest.main.run_tests(test_list)
# NOTREACHED
### End of file.