3 # history_tests.py: testing history-tracing code
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-2007 CollabNet. All rights reserved.
11 # This software is licensed as described in the file COPYING, which
12 # you should have received as part of this distribution. The terms
13 # are also available at http://subversion.tigris.org/license-1.html.
14 # If newer versions of this license are posted there, you may use a
15 # newer version instead, at your option.
17 ######################################################################
24 from svntest
import wc
27 Skip
= svntest
.testcase
.Skip
28 XFail
= svntest
.testcase
.XFail
31 ######################################################################
34 # Each test must return on success or raise on failure.
36 #----------------------------------------------------------------------
38 def cat_traces_renames(sbox
):
39 "verify that 'svn cat' traces renames"
43 rho_path
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'rho')
44 pi_path
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'pi')
45 bloo_path
= os
.path
.join(wc_dir
, 'A', 'D', 'G', 'bloo')
47 # rename rho to bloo. commit r2.
48 svntest
.main
.run_svn(None, 'mv', rho_path
, bloo_path
)
50 expected_output
= svntest
.wc
.State(wc_dir
, {
51 'A/D/G/rho' : Item(verb
='Deleting'),
52 'A/D/G/bloo' : Item(verb
='Adding')
54 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
55 expected_status
.remove('A/D/G/rho');
56 expected_status
.add({ 'A/D/G/bloo' :
57 Item(wc_rev
=2, status
=' ') })
59 svntest
.actions
.run_and_verify_commit(wc_dir
,
67 # rename pi to rho. commit r3.
68 svntest
.main
.run_svn(None, 'mv', pi_path
, rho_path
)
70 # svn cat -r1 rho --> should show pi's contents.
71 svntest
.actions
.run_and_verify_svn(None,
72 [ "This is the file 'pi'.\n"], [],
73 'cat', '-r', '1', rho_path
)
75 expected_output
= svntest
.wc
.State(wc_dir
, {
76 'A/D/G/pi' : Item(verb
='Deleting'),
77 'A/D/G/rho' : Item(verb
='Adding')
79 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
80 expected_status
.remove('A/D/G/pi');
81 expected_status
.tweak('A/D/G/rho', wc_rev
=3)
82 expected_status
.add({ 'A/D/G/bloo' :
83 Item(wc_rev
=2, status
=' ') })
85 svntest
.actions
.run_and_verify_commit(wc_dir
,
93 # update whole wc to HEAD
94 expected_output
= svntest
.wc
.State(wc_dir
, { }) # no output
95 expected_status
.tweak(wc_rev
=3)
96 expected_disk
= svntest
.main
.greek_state
.copy()
97 expected_disk
.remove('A/D/G/pi', 'A/D/G/rho')
99 'A/D/G/rho' : Item("This is the file 'pi'.\n"),
102 'A/D/G/bloo' : Item("This is the file 'rho'.\n"),
104 svntest
.actions
.run_and_verify_update(wc_dir
,
109 # 'svn cat bloo' --> should show rho's contents.
110 svntest
.actions
.run_and_verify_svn(None,
111 [ "This is the file 'rho'.\n"], [],
114 # svn cat -r1 bloo --> should still show rho's contents.
115 svntest
.actions
.run_and_verify_svn(None,
116 [ "This is the file 'rho'.\n"], [],
117 'cat', '-r', '1', bloo_path
)
119 # svn cat -r1 rho --> should show pi's contents.
120 svntest
.actions
.run_and_verify_svn(None,
121 [ "This is the file 'pi'.\n"], [],
122 'cat', '-r', '1', rho_path
)
125 svntest
.actions
.run_and_verify_svn(None, None, [], 'up', '-r', '1', wc_dir
)
126 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
127 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
129 # svn cat -rHEAD rho --> should see 'unrelated object' error.
130 svntest
.actions
.run_and_verify_svn("unrelated object",
131 None, svntest
.verify
.AnyOutput
,
132 'cat', '-r', 'HEAD', rho_path
)
134 def cat_avoids_false_identities(sbox
):
135 "verify that 'svn cat' avoids false identities"
142 # Highlight a bug in the client side use of the repository's
143 # location searching algorithm.
145 # The buggy history-following algorithm determines the paths that a
146 # line of history would be *expected to be* found in a given revision,
147 # but doesn't treat copies as gaps in the historical sequence. If
148 # some other object fills those gaps at the same expected path, the
149 # client will find the wrong object.
151 # In the recipe below, iota gets created in r1. In r2, it is
152 # deleted and replaced with an unrelated object at the same path.
153 # In r3, the interloper is deleted. In r4, the original iota is
154 # resurrected via a copy from r1.
157 # o---| o---| o o----->
162 # In a working copy at r4, running
166 # should result in an error, but with the bug it instead cats the r2
169 # To reassure yourself that that's wrong, recall that the above
170 # command is equivalent to
172 # $ svn cat -r2 iota@4
174 # Now do you see the evil that lies within us?
176 iota_path
= os
.path
.join(wc_dir
, 'iota')
177 iota_url
= sbox
.repo_url
+ '/iota'
180 svntest
.main
.run_svn(None, 'del', iota_path
)
181 svntest
.main
.file_append(iota_path
, "YOU SHOULD NOT SEE THIS\n")
182 svntest
.main
.run_svn(None, 'add', iota_path
)
183 svntest
.main
.run_svn(None, 'ci', '-m', 'log msg',
185 svntest
.main
.run_svn(None, 'up', wc_dir
)
188 svntest
.main
.run_svn(None, 'del', iota_path
)
189 svntest
.main
.run_svn(None, 'ci', '-m', 'log msg',
191 svntest
.main
.run_svn(None, 'up', wc_dir
)
194 svntest
.main
.run_svn(None, 'cp', iota_url
+ '@1', wc_dir
)
195 svntest
.main
.run_svn(None, 'ci', '-m', 'log msg',
197 svntest
.main
.run_svn(None, 'up', wc_dir
)
199 # 'svn cat -r2 iota' should error, because the line of history
200 # currently identified by /iota did not exist in r2, even though a
201 # totally unrelated file of the same name did.
202 svntest
.actions
.run_and_verify_svn(None, None, svntest
.verify
.AnyOutput
,
203 'cat', '-r', '2', iota_path
)
206 ########################################################################
209 # list all tests here, starting with None:
212 cat_avoids_false_identities
,
215 if __name__
== '__main__':
216 svntest
.main
.run_tests(test_list
)