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
,
65 # rename pi to rho. commit r3.
66 svntest
.main
.run_svn(None, 'mv', pi_path
, rho_path
)
68 # svn cat -r1 rho --> should show pi's contents.
69 svntest
.actions
.run_and_verify_svn(None,
70 [ "This is the file 'pi'.\n"], [],
71 'cat', '-r', '1', rho_path
)
73 expected_output
= svntest
.wc
.State(wc_dir
, {
74 'A/D/G/pi' : Item(verb
='Deleting'),
75 'A/D/G/rho' : Item(verb
='Adding')
77 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
78 expected_status
.remove('A/D/G/pi');
79 expected_status
.tweak('A/D/G/rho', wc_rev
=3)
80 expected_status
.add({ 'A/D/G/bloo' :
81 Item(wc_rev
=2, status
=' ') })
83 svntest
.actions
.run_and_verify_commit(wc_dir
,
89 # update whole wc to HEAD
90 expected_output
= svntest
.wc
.State(wc_dir
, { }) # no output
91 expected_status
.tweak(wc_rev
=3)
92 expected_disk
= svntest
.main
.greek_state
.copy()
93 expected_disk
.remove('A/D/G/pi', 'A/D/G/rho')
95 'A/D/G/rho' : Item("This is the file 'pi'.\n"),
98 'A/D/G/bloo' : Item("This is the file 'rho'.\n"),
100 svntest
.actions
.run_and_verify_update(wc_dir
,
105 # 'svn cat bloo' --> should show rho's contents.
106 svntest
.actions
.run_and_verify_svn(None,
107 [ "This is the file 'rho'.\n"], [],
110 # svn cat -r1 bloo --> should still show rho's contents.
111 svntest
.actions
.run_and_verify_svn(None,
112 [ "This is the file 'rho'.\n"], [],
113 'cat', '-r', '1', bloo_path
)
115 # svn cat -r1 rho --> should show pi's contents.
116 svntest
.actions
.run_and_verify_svn(None,
117 [ "This is the file 'pi'.\n"], [],
118 'cat', '-r', '1', rho_path
)
121 svntest
.actions
.run_and_verify_svn(None, None, [], 'up', '-r', '1', wc_dir
)
122 expected_status
= svntest
.actions
.get_virginal_state(wc_dir
, 1)
123 svntest
.actions
.run_and_verify_status(wc_dir
, expected_status
)
125 # svn cat -rHEAD rho --> should see 'unrelated object' error.
126 svntest
.actions
.run_and_verify_svn("unrelated object",
127 None, svntest
.verify
.AnyOutput
,
128 'cat', '-r', 'HEAD', rho_path
)
130 def cat_avoids_false_identities(sbox
):
131 "verify that 'svn cat' avoids false identities"
138 # Highlight a bug in the client side use of the repository's
139 # location searching algorithm.
141 # The buggy history-following algorithm determines the paths that a
142 # line of history would be *expected to be* found in a given revision,
143 # but doesn't treat copies as gaps in the historical sequence. If
144 # some other object fills those gaps at the same expected path, the
145 # client will find the wrong object.
147 # In the recipe below, iota gets created in r1. In r2, it is
148 # deleted and replaced with an unrelated object at the same path.
149 # In r3, the interloper is deleted. In r4, the original iota is
150 # resurrected via a copy from r1.
153 # o---| o---| o o----->
158 # In a working copy at r4, running
162 # should result in an error, but with the bug it instead cats the r2
165 # To reassure yourself that that's wrong, recall that the above
166 # command is equivalent to
168 # $ svn cat -r2 iota@4
170 # Now do you see the evil that lies within us?
172 iota_path
= os
.path
.join(wc_dir
, 'iota')
173 iota_url
= sbox
.repo_url
+ '/iota'
176 svntest
.main
.run_svn(None, 'del', iota_path
)
177 svntest
.main
.file_append(iota_path
, "YOU SHOULD NOT SEE THIS\n")
178 svntest
.main
.run_svn(None, 'add', iota_path
)
179 svntest
.main
.run_svn(None, 'ci', '-m', 'log msg',
181 svntest
.main
.run_svn(None, 'up', wc_dir
)
184 svntest
.main
.run_svn(None, 'del', iota_path
)
185 svntest
.main
.run_svn(None, 'ci', '-m', 'log msg',
187 svntest
.main
.run_svn(None, 'up', wc_dir
)
190 svntest
.main
.run_svn(None, 'cp', iota_url
+ '@1', wc_dir
)
191 svntest
.main
.run_svn(None, 'ci', '-m', 'log msg',
193 svntest
.main
.run_svn(None, 'up', wc_dir
)
195 # 'svn cat -r2 iota' should error, because the line of history
196 # currently identified by /iota did not exist in r2, even though a
197 # totally unrelated file of the same name did.
198 svntest
.actions
.run_and_verify_svn(None, None, svntest
.verify
.AnyOutput
,
199 'cat', '-r', '2', iota_path
)
202 ########################################################################
205 # list all tests here, starting with None:
208 cat_avoids_false_identities
,
211 if __name__
== '__main__':
212 svntest
.main
.run_tests(test_list
)