In the command-line client, forbid
[svn.git] / subversion / tests / cmdline / merge_authz_tests.py
blobe3ac51f09028023f4e27f6a2fe16883fad00b93f
1 #!/usr/bin/env python
3 # merge_authz_tests.py: merge tests that need to write an authz file
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 ######################################################################
19 # General modules
20 import shutil, sys, re, os
21 import time
23 # Our testing module
24 import svntest
25 from svntest import wc
27 # (abbreviation)
28 Item = wc.StateItem
29 XFail = svntest.testcase.XFail
30 Skip = svntest.testcase.Skip
31 SkipUnless = svntest.testcase.SkipUnless
33 from merge_tests import setup_branch
34 from merge_tests import shorten_path_kludge
36 from svntest.main import SVN_PROP_MERGE_INFO
37 from svntest.main import write_restrictive_svnserve_conf
38 from svntest.main import write_authz_file
39 from svntest.main import server_has_mergeinfo
40 from svntest.actions import fill_file_with_lines
41 from svntest.actions import make_conflict_marker_text
42 from svntest.actions import inject_conflict_into_expected_state
44 ######################################################################
45 # Tests
47 # Each test must return on success or raise on failure.
50 #----------------------------------------------------------------------
52 # Test for issue #2893
53 # Handle merge info for portions of a tree not checked out due to
54 # insufficient authz.
55 def mergeinfo_and_skipped_paths(sbox):
56 "skipped paths get overriding mergeinfo"
58 # Test that we override the mergeinfo for child paths which weren't
59 # actually merged because they were skipped.
61 # This test covers paths skipped because:
63 # 1) The source of a merge is inaccessible due to authz restrictions.
64 # 2) Destination of merge is inaccessible due to authz restrictions.
65 # 3) Source *and* destination of merge is inaccessible due to authz
66 # restrictions.
67 # 4) File path is versioned but is missing from disk due to OS deletion.
68 # This isn't technically part of issue #2893 but we handle this case
69 # and it didn't warrant its own test).
71 # Eventually we should also test(?):
73 # 5) Dir path is versioned but is missing from disk due to an OS deletion.
75 sbox.build()
76 wc_dir = sbox.wc_dir
77 wc_disk, wc_status = setup_branch(sbox, False, 3)
79 # Create a restrictive authz where part of the merge source and part
80 # of the target are inaccesible.
81 write_restrictive_svnserve_conf(sbox.repo_dir)
82 write_authz_file(sbox, {"/" : svntest.main.wc_author +"=rw",
83 # Make a directory in the merge source inaccessible.
84 "/A/B/E" : svntest.main.wc_author + "=",
85 # Make a file and dir in the merge destination
86 # inaccessible.
87 "/A_COPY_2/D/H/psi" : svntest.main.wc_author + "=",
88 "/A_COPY_2/D/G" : svntest.main.wc_author + "=",
89 # Make the source and destination inaccessible.
90 "/A_COPY_3/B/E" : svntest.main.wc_author + "=",
93 # Checkout just the branch under the newly restricted authz.
94 wc_restricted = sbox.add_wc_path('restricted')
95 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
96 sbox.repo_url,
97 wc_restricted)
99 # Some paths we'll use in the second WC.
100 A_COPY_path = os.path.join(wc_restricted, "A_COPY")
101 A_COPY_2_path = os.path.join(wc_restricted, "A_COPY_2")
102 A_COPY_3_path = os.path.join(wc_restricted, "A_COPY_3")
103 omega_path = os.path.join(wc_restricted, "A_COPY", "D", "H", "omega")
105 # Restrict access to some more of the merge destination the
106 # old fashioned way, delete it via the OS.
107 ### TODO: Delete a versioned directory?
108 os.remove(omega_path)
110 # Merge r4:8 into the restricted WC's A_COPY.
112 # Search for the comment entitled "The Merge Kluge" elsewhere in
113 # this file, to understand why we shorten and chdir() below.
115 # We expect A_COPY/B/E to be skipped because we can't access the source
116 # and A_COPY/D/H/omega because it is missing. Since we have A_COPY/B/E
117 # we should override it's inherited mergeinfo, giving it just what it
118 # inherited from A_COPY before the merge. omega is missing, but since
119 # it is a file we can record the fact that it is missing in its parent
120 # directory A_COPY/D/H.
121 short_path = shorten_path_kludge(A_COPY_path)
122 expected_output = wc.State(short_path, {
123 'D/G/rho' : Item(status='U '),
124 'D/H/psi' : Item(status='U '),
126 expected_status = wc.State(short_path, {
127 '' : Item(status=' M', wc_rev=8),
128 'D/H/chi' : Item(status=' ', wc_rev=8),
129 'D/H/psi' : Item(status='M ', wc_rev=8),
130 'D/H/omega' : Item(status='!M', wc_rev=8),
131 'D/H' : Item(status=' ', wc_rev=8),
132 'D/G/pi' : Item(status=' ', wc_rev=8),
133 'D/G/rho' : Item(status='M ', wc_rev=8),
134 'D/G/tau' : Item(status=' ', wc_rev=8),
135 'D/G' : Item(status=' ', wc_rev=8),
136 'D/gamma' : Item(status=' ', wc_rev=8),
137 'D' : Item(status=' ', wc_rev=8),
138 'B/lambda' : Item(status=' ', wc_rev=8),
139 'B/E' : Item(status=' M', wc_rev=8),
140 'B/E/alpha' : Item(status=' ', wc_rev=8),
141 'B/E/beta' : Item(status=' ', wc_rev=8),
142 'B/F' : Item(status=' ', wc_rev=8),
143 'B' : Item(status=' ', wc_rev=8),
144 'mu' : Item(status=' ', wc_rev=8),
145 'C' : Item(status=' ', wc_rev=8),
147 expected_disk = wc.State('', {
148 '' : Item(props={SVN_PROP_MERGE_INFO : '/A:5-8'}),
149 'D/H/psi' : Item("New content"),
150 'D/H/chi' : Item("This is the file 'chi'.\n"),
151 # 'D/H/omega' : run_and_verify_merge() doesn't support checking
152 # the props on a missing path, so we do that
153 # manually (see below).
154 'D/H' : Item(),
155 'D/G/pi' : Item("This is the file 'pi'.\n"),
156 'D/G/rho' : Item("New content"),
157 'D/G/tau' : Item("This is the file 'tau'.\n"),
158 'D/G' : Item(),
159 'D/gamma' : Item("This is the file 'gamma'.\n"),
160 'D' : Item(),
161 'B/lambda' : Item("This is the file 'lambda'.\n"),
162 'B/E' : Item(props={SVN_PROP_MERGE_INFO : ''}),
163 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
164 'B/E/beta' : Item("This is the file 'beta'.\n"),
165 'B/F' : Item(),
166 'B' : Item(),
167 'mu' : Item("This is the file 'mu'.\n"),
168 'C' : Item(),
170 expected_skip = wc.State(short_path, {
171 'D/H/omega' : Item(),
172 'B/E' : Item(),
174 saved_cwd = os.getcwd()
175 os.chdir(svntest.main.work_dir)
176 svntest.actions.run_and_verify_merge(short_path, '4', '8',
177 sbox.repo_url + \
178 '/A',
179 expected_output,
180 expected_disk,
181 expected_status,
182 expected_skip,
183 None, None, None, None,
184 None, 1)
185 os.chdir(saved_cwd)
187 # Manually check the props on A_COPY/D/H/omega.
188 svntest.actions.run_and_verify_svn(None,
189 ["Properties on '" + omega_path + "':\n",
190 ' ' + SVN_PROP_MERGE_INFO + ' : ' +
191 '\n'],
192 [], 'pl', '-vR', omega_path)
194 # Merge r4:8 into the restricted WC's A_COPY_2.
196 # As before we expect A_COPY_2/B/E to be skipped because we can't access the
197 # source but now the destination paths A_COPY_2/D/G, A_COPY_2/D/G/rho, and
198 # A_COPY_2/D/H/psi should also be skipped because our test user doesn't have
199 # access.
201 # After the merge the parents of the missing dest paths, A_COPY_2/D and
202 # A_COPY_2/D/H get non-inheritable mergeinfo. Those parents children that
203 # *are* present, A_COPY_2/D/gamma, A_COPY_2/D/H/chi, and A_COPY_2/D/H/omega
204 # get their own mergeinfo. Note that A_COPY_2/D/H is both the parent of
205 # a missing child and the sibling of missing child, but the former always
206 # takes precedence in terms of getting *non*-inheritable mergeinfo.
207 short_path = shorten_path_kludge(A_COPY_2_path)
208 expected_output = wc.State(short_path, {
209 'D/H/omega' : Item(status='U '),
211 expected_status = wc.State(short_path, {
212 '' : Item(status=' M', wc_rev=8),
213 'D/H/chi' : Item(status=' M', wc_rev=8),
214 'D/H/omega' : Item(status='MM', wc_rev=8),
215 'D/H' : Item(status=' M', wc_rev=8),
216 'D/gamma' : Item(status=' M', wc_rev=8),
217 'D' : Item(status=' M', wc_rev=8),
218 'B/lambda' : Item(status=' ', wc_rev=8),
219 'B/E' : Item(status=' M', wc_rev=8),
220 'B/E/alpha' : Item(status=' ', wc_rev=8),
221 'B/E/beta' : Item(status=' ', wc_rev=8),
222 'B/F' : Item(status=' ', wc_rev=8),
223 'B' : Item(status=' ', wc_rev=8),
224 'mu' : Item(status=' ', wc_rev=8),
225 'C' : Item(status=' ', wc_rev=8),
227 expected_disk = wc.State('', {
228 '' : Item(props={SVN_PROP_MERGE_INFO : '/A:5-8'}),
229 'D/H/omega' : Item("New content",
230 props={SVN_PROP_MERGE_INFO : '/A/D/H/omega:5-8'}),
231 'D/H/chi' : Item("This is the file 'chi'.\n",
232 props={SVN_PROP_MERGE_INFO : '/A/D/H/chi:5-8'}),
233 'D/H' : Item(props={SVN_PROP_MERGE_INFO : '/A/D/H:5-8*'}),
234 'D/gamma' : Item("This is the file 'gamma'.\n",
235 props={SVN_PROP_MERGE_INFO : '/A/D/gamma:5-8'}),
236 'D' : Item(props={SVN_PROP_MERGE_INFO : '/A/D:5-8*'}),
237 'B/lambda' : Item("This is the file 'lambda'.\n"),
238 'B/E' : Item(props={SVN_PROP_MERGE_INFO : ''}),
239 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
240 'B/E/beta' : Item("This is the file 'beta'.\n"),
241 'B/F' : Item(),
242 'B' : Item(),
243 'mu' : Item("This is the file 'mu'.\n"),
244 'C' : Item(),
246 expected_skip = wc.State(short_path, {
247 'D/G' : Item(),
248 'D/G/rho' : Item(),
249 'D/H/psi' : Item(),
250 'B/E' : Item(),
252 saved_cwd = os.getcwd()
253 os.chdir(svntest.main.work_dir)
254 svntest.actions.run_and_verify_merge(short_path, '4', '8',
255 sbox.repo_url + \
256 '/A',
257 expected_output,
258 expected_disk,
259 expected_status,
260 expected_skip,
261 None, None, None, None,
262 None, 1, 0)
263 os.chdir(saved_cwd)
265 # Merge r5:7 into the restricted WC's A_COPY_3.
267 # Again A_COPY_3/B/E should be skipped, but because we can't access the
268 # source *or* the destination we expect its parent A_COPY_3/B to get
269 # non-inheritable mergeinfo and its two existing siblings, A_COPY_3/B/F
270 # and A_COPY_3/B/lambda to get their own mergeinfo.
271 short_path = shorten_path_kludge(A_COPY_3_path)
272 expected_output = wc.State(short_path, {
273 'D/G/rho' : Item(status='U '),
275 expected_status = wc.State(short_path, {
276 '' : Item(status=' M', wc_rev=8),
277 'D/H/chi' : Item(status=' ', wc_rev=8),
278 'D/H/omega' : Item(status=' ', wc_rev=8),
279 'D/H/psi' : Item(status=' ', wc_rev=8),
280 'D/H' : Item(status=' ', wc_rev=8),
281 'D/gamma' : Item(status=' ', wc_rev=8),
282 'D' : Item(status=' ', wc_rev=8),
283 'D/G' : Item(status=' ', wc_rev=8),
284 'D/G/pi' : Item(status=' ', wc_rev=8),
285 'D/G/rho' : Item(status='M ', wc_rev=8),
286 'D/G/tau' : Item(status=' ', wc_rev=8),
287 'B/lambda' : Item(status=' M', wc_rev=8),
288 'B/F' : Item(status=' M', wc_rev=8),
289 'B' : Item(status=' M', wc_rev=8),
290 'mu' : Item(status=' ', wc_rev=8),
291 'C' : Item(status=' ', wc_rev=8),
293 expected_disk = wc.State('', {
294 '' : Item(props={SVN_PROP_MERGE_INFO : '/A:6-7'}),
295 'D/H/omega' : Item("This is the file 'omega'.\n"),
296 'D/H/chi' : Item("This is the file 'chi'.\n"),
297 'D/H/psi' : Item("This is the file 'psi'.\n"),
298 'D/H' : Item(),
299 'D/gamma' : Item("This is the file 'gamma'.\n"),
300 'D' : Item(),
301 'D/G' : Item(),
302 'D/G/pi' : Item("This is the file 'pi'.\n"),
303 'D/G/rho' : Item("New content"),
304 'D/G/tau' : Item("This is the file 'tau'.\n"),
305 'B/lambda' : Item("This is the file 'lambda'.\n",
306 props={SVN_PROP_MERGE_INFO : '/A/B/lambda:6-7'}),
307 'B/F' : Item(props={SVN_PROP_MERGE_INFO : '/A/B/F:6-7'}),
308 'B' : Item(props={SVN_PROP_MERGE_INFO : '/A/B:6-7*'}),
309 'mu' : Item("This is the file 'mu'.\n"),
310 'C' : Item(),
312 expected_skip = wc.State(short_path, {'B/E' : Item()})
313 saved_cwd = os.getcwd()
314 os.chdir(svntest.main.work_dir)
315 svntest.actions.run_and_verify_merge(short_path, '5', '7',
316 sbox.repo_url + \
317 '/A',
318 expected_output,
319 expected_disk,
320 expected_status,
321 expected_skip,
322 None, None, None, None,
323 None, 1, 0)
324 os.chdir(saved_cwd)
326 ########################################################################
327 # Run the tests
330 # list all tests here, starting with None:
331 test_list = [ None,
332 Skip(mergeinfo_and_skipped_paths, svntest.main.is_ra_type_file),
335 if __name__ == '__main__':
336 svntest.main.run_tests(test_list, serial_only = True)
337 # NOTREACHED
340 ### End of file.