Fix compiler warning due to missing function prototype.
[svn.git] / subversion / tests / cmdline / merge_authz_tests.py
blob915cd8f082d024b5b24bdae1a9dfc2bace0a2470
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 set_up_branch
34 from merge_tests import shorten_path_kludge
36 from svntest.main import SVN_PROP_MERGEINFO
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 issues
54 # #2893 - Handle merge info for portions of a tree not checked out due
55 # to insufficient authz.
57 # #2997 - If skipped paths come first in operative merge mergeinfo
58 # is incomplete
60 # #2829 - Improve handling for skipped paths encountered during a merge.
61 # This is *not* a full test of issue #2829, see also merge_tests.py,
62 # search for "2829". This tests the problem where a merge adds a path
63 # with a missing sibling and so needs its own explicit mergeinfo.
64 def mergeinfo_and_skipped_paths(sbox):
65 "skipped paths get overriding mergeinfo"
67 # Test that we override the mergeinfo for child paths which weren't
68 # actually merged because they were skipped.
70 # This test covers paths skipped because:
72 # 1) The source of a merge is inaccessible due to authz restrictions.
73 # 2) Destination of merge is inaccessible due to authz restrictions.
74 # 3) Source *and* destination of merge is inaccessible due to authz
75 # restrictions.
76 # 4) File path is versioned but is missing from disk due to OS deletion.
77 # This isn't technically part of issue #2893 but we handle this case
78 # and it didn't warrant its own test).
80 # Eventually we should also test(?):
82 # 5) Dir path is versioned but is missing from disk due to an OS deletion.
84 sbox.build()
85 wc_dir = sbox.wc_dir
86 wc_disk, wc_status = set_up_branch(sbox, False, 3)
88 # Create a restrictive authz where part of the merge source and part
89 # of the target are inaccesible.
90 write_restrictive_svnserve_conf(sbox.repo_dir)
91 write_authz_file(sbox, {"/" : svntest.main.wc_author +"=rw",
92 # Make a directory in the merge source inaccessible.
93 "/A/B/E" : svntest.main.wc_author + "=",
94 # Make a file and dir in the merge destination
95 # inaccessible.
96 "/A_COPY_2/D/H/psi" : svntest.main.wc_author + "=",
97 "/A_COPY_2/D/G" : svntest.main.wc_author + "=",
98 # Make the source and destination inaccessible.
99 "/A_COPY_3/B/E" : svntest.main.wc_author + "=",
102 # Checkout just the branch under the newly restricted authz.
103 wc_restricted = sbox.add_wc_path('restricted')
104 svntest.actions.run_and_verify_svn(None, None, [], 'checkout',
105 sbox.repo_url,
106 wc_restricted)
108 # Some paths we'll use in the second WC.
109 A_COPY_path = os.path.join(wc_restricted, "A_COPY")
110 A_COPY_2_path = os.path.join(wc_restricted, "A_COPY_2")
111 A_COPY_2_H_path = os.path.join(wc_restricted, "A_COPY_2", "D", "H")
112 A_COPY_3_path = os.path.join(wc_restricted, "A_COPY_3")
113 omega_path = os.path.join(wc_restricted, "A_COPY", "D", "H", "omega")
114 zeta_path = os.path.join(wc_dir, "A", "D", "H", "zeta")
116 # Restrict access to some more of the merge destination the
117 # old fashioned way, delete it via the OS.
118 ### TODO: Delete a versioned directory?
119 os.remove(omega_path)
121 # Merge r4:8 into the restricted WC's A_COPY.
123 # Search for the comment entitled "The Merge Kluge" elsewhere in
124 # this file, to understand why we shorten and chdir() below.
126 # We expect A_COPY/B/E to be skipped because we can't access the source
127 # and A_COPY/D/H/omega because it is missing. Since we have A_COPY/B/E
128 # we should override it's inherited mergeinfo, giving it just what it
129 # inherited from A_COPY before the merge. omega is missing, but since
130 # it is a file we can record the fact that it is missing in its parent
131 # directory A_COPY/D/H.
132 short_path = shorten_path_kludge(A_COPY_path)
133 expected_output = wc.State(short_path, {
134 'D/G/rho' : Item(status='U '),
135 'D/H/psi' : Item(status='U '),
137 expected_status = wc.State(short_path, {
138 '' : Item(status=' M', wc_rev=8),
139 'D/H/chi' : Item(status=' ', wc_rev=8),
140 'D/H/psi' : Item(status='M ', wc_rev=8),
141 'D/H/omega' : Item(status='!M', wc_rev=8),
142 'D/H' : Item(status=' ', wc_rev=8),
143 'D/G/pi' : Item(status=' ', wc_rev=8),
144 'D/G/rho' : Item(status='M ', wc_rev=8),
145 'D/G/tau' : Item(status=' ', wc_rev=8),
146 'D/G' : Item(status=' ', wc_rev=8),
147 'D/gamma' : Item(status=' ', wc_rev=8),
148 'D' : Item(status=' ', wc_rev=8),
149 'B/lambda' : Item(status=' ', wc_rev=8),
150 'B/E' : Item(status=' M', wc_rev=8),
151 'B/E/alpha' : Item(status=' ', wc_rev=8),
152 'B/E/beta' : Item(status=' ', wc_rev=8),
153 'B/F' : Item(status=' ', wc_rev=8),
154 'B' : Item(status=' ', wc_rev=8),
155 'mu' : Item(status=' ', wc_rev=8),
156 'C' : Item(status=' ', wc_rev=8),
158 expected_disk = wc.State('', {
159 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
160 'D/H/psi' : Item("New content"),
161 'D/H/chi' : Item("This is the file 'chi'.\n"),
162 # 'D/H/omega' : run_and_verify_merge() doesn't support checking
163 # the props on a missing path, so we do that
164 # manually (see below).
165 'D/H' : Item(),
166 'D/G/pi' : Item("This is the file 'pi'.\n"),
167 'D/G/rho' : Item("New content"),
168 'D/G/tau' : Item("This is the file 'tau'.\n"),
169 'D/G' : Item(),
170 'D/gamma' : Item("This is the file 'gamma'.\n"),
171 'D' : Item(),
172 'B/lambda' : Item("This is the file 'lambda'.\n"),
173 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
174 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
175 'B/E/beta' : Item("This is the file 'beta'.\n"),
176 'B/F' : Item(),
177 'B' : Item(),
178 'mu' : Item("This is the file 'mu'.\n"),
179 'C' : Item(),
181 expected_skip = wc.State(short_path, {
182 'D/H/omega' : Item(),
183 'B/E' : Item(),
185 saved_cwd = os.getcwd()
186 os.chdir(svntest.main.work_dir)
187 svntest.actions.run_and_verify_merge(short_path, '4', '8',
188 sbox.repo_url + \
189 '/A',
190 expected_output,
191 expected_disk,
192 expected_status,
193 expected_skip,
194 None, None, None, None,
195 None, 1)
196 os.chdir(saved_cwd)
198 # Manually check the props on A_COPY/D/H/omega.
199 svntest.actions.run_and_verify_svn(None,
200 ["Properties on '" + omega_path + "':\n",
201 ' ' + SVN_PROP_MERGEINFO + ' : ' +
202 '\n'],
203 [], 'pl', '-vR', omega_path)
205 # Merge r4:8 into the restricted WC's A_COPY_2.
207 # As before we expect A_COPY_2/B/E to be skipped because we can't access the
208 # source but now the destination paths A_COPY_2/D/G, A_COPY_2/D/G/rho, and
209 # A_COPY_2/D/H/psi should also be skipped because our test user doesn't have
210 # access.
212 # After the merge the parents of the missing dest paths, A_COPY_2/D and
213 # A_COPY_2/D/H get non-inheritable mergeinfo. Those parents children that
214 # *are* present, A_COPY_2/D/gamma, A_COPY_2/D/H/chi, and A_COPY_2/D/H/omega
215 # get their own mergeinfo. Note that A_COPY_2/D/H is both the parent of
216 # a missing child and the sibling of missing child, but the former always
217 # takes precedence in terms of getting *non*-inheritable mergeinfo.
218 short_path = shorten_path_kludge(A_COPY_2_path)
219 expected_output = wc.State(short_path, {
220 'D/H/omega' : Item(status='U '),
222 expected_status = wc.State(short_path, {
223 '' : Item(status=' M', wc_rev=8),
224 'D/H/chi' : Item(status=' M', wc_rev=8),
225 'D/H/omega' : Item(status='MM', wc_rev=8),
226 'D/H' : Item(status=' M', wc_rev=8),
227 'D/gamma' : Item(status=' M', wc_rev=8),
228 'D' : Item(status=' M', wc_rev=8),
229 'B/lambda' : Item(status=' ', wc_rev=8),
230 'B/E' : Item(status=' M', wc_rev=8),
231 'B/E/alpha' : Item(status=' ', wc_rev=8),
232 'B/E/beta' : Item(status=' ', wc_rev=8),
233 'B/F' : Item(status=' ', wc_rev=8),
234 'B' : Item(status=' ', wc_rev=8),
235 'mu' : Item(status=' ', wc_rev=8),
236 'C' : Item(status=' ', wc_rev=8),
238 expected_disk = wc.State('', {
239 '' : Item(props={SVN_PROP_MERGEINFO : '/A:5-8'}),
240 'D/H/omega' : Item("New content",
241 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5-8'}),
242 'D/H/chi' : Item("This is the file 'chi'.\n",
243 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5-8'}),
244 'D/H' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5-8*'}),
245 'D/gamma' : Item("This is the file 'gamma'.\n",
246 props={SVN_PROP_MERGEINFO : '/A/D/gamma:5-8'}),
247 'D' : Item(props={SVN_PROP_MERGEINFO : '/A/D:5-8*'}),
248 'B/lambda' : Item("This is the file 'lambda'.\n"),
249 'B/E' : Item(props={SVN_PROP_MERGEINFO : ''}),
250 'B/E/alpha' : Item("This is the file 'alpha'.\n"),
251 'B/E/beta' : Item("This is the file 'beta'.\n"),
252 'B/F' : Item(),
253 'B' : Item(),
254 'mu' : Item("This is the file 'mu'.\n"),
255 'C' : Item(),
257 expected_skip = wc.State(short_path, {
258 'D/G' : Item(),
259 'D/G/rho' : Item(),
260 'D/H/psi' : Item(),
261 'B/E' : Item(),
263 saved_cwd = os.getcwd()
264 os.chdir(svntest.main.work_dir)
265 svntest.actions.run_and_verify_merge(short_path, '4', '8',
266 sbox.repo_url + \
267 '/A',
268 expected_output,
269 expected_disk,
270 expected_status,
271 expected_skip,
272 None, None, None, None,
273 None, 1, 0)
274 os.chdir(saved_cwd)
276 # Merge r5:7 into the restricted WC's A_COPY_3.
278 # Again A_COPY_3/B/E should be skipped, but because we can't access the
279 # source *or* the destination we expect its parent A_COPY_3/B to get
280 # non-inheritable mergeinfo and its two existing siblings, A_COPY_3/B/F
281 # and A_COPY_3/B/lambda to get their own mergeinfo.
282 short_path = shorten_path_kludge(A_COPY_3_path)
283 expected_output = wc.State(short_path, {
284 'D/G/rho' : Item(status='U '),
286 expected_status = wc.State(short_path, {
287 '' : Item(status=' M', wc_rev=8),
288 'D/H/chi' : Item(status=' ', wc_rev=8),
289 'D/H/omega' : Item(status=' ', wc_rev=8),
290 'D/H/psi' : Item(status=' ', wc_rev=8),
291 'D/H' : Item(status=' ', wc_rev=8),
292 'D/gamma' : Item(status=' ', wc_rev=8),
293 'D' : Item(status=' ', wc_rev=8),
294 'D/G' : Item(status=' ', wc_rev=8),
295 'D/G/pi' : Item(status=' ', wc_rev=8),
296 'D/G/rho' : Item(status='M ', wc_rev=8),
297 'D/G/tau' : Item(status=' ', wc_rev=8),
298 'B/lambda' : Item(status=' M', wc_rev=8),
299 'B/F' : Item(status=' M', wc_rev=8),
300 'B' : Item(status=' M', wc_rev=8),
301 'mu' : Item(status=' ', wc_rev=8),
302 'C' : Item(status=' ', wc_rev=8),
304 expected_disk = wc.State('', {
305 '' : Item(props={SVN_PROP_MERGEINFO : '/A:6-7'}),
306 'D/H/omega' : Item("This is the file 'omega'.\n"),
307 'D/H/chi' : Item("This is the file 'chi'.\n"),
308 'D/H/psi' : Item("This is the file 'psi'.\n"),
309 'D/H' : Item(),
310 'D/gamma' : Item("This is the file 'gamma'.\n"),
311 'D' : Item(),
312 'D/G' : Item(),
313 'D/G/pi' : Item("This is the file 'pi'.\n"),
314 'D/G/rho' : Item("New content"),
315 'D/G/tau' : Item("This is the file 'tau'.\n"),
316 'B/lambda' : Item("This is the file 'lambda'.\n",
317 props={SVN_PROP_MERGEINFO : '/A/B/lambda:6-7'}),
318 'B/F' : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:6-7'}),
319 'B' : Item(props={SVN_PROP_MERGEINFO : '/A/B:6-7*'}),
320 'mu' : Item("This is the file 'mu'.\n"),
321 'C' : Item(),
323 expected_skip = wc.State(short_path, {'B/E' : Item()})
324 saved_cwd = os.getcwd()
325 os.chdir(svntest.main.work_dir)
326 svntest.actions.run_and_verify_merge(short_path, '5', '7',
327 sbox.repo_url + \
328 '/A',
329 expected_output,
330 expected_disk,
331 expected_status,
332 expected_skip,
333 None, None, None, None,
334 None, 1, 0)
335 os.chdir(saved_cwd)
336 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive',
337 wc_restricted)
339 # Test issue #2997. If a merge requires two separate editor drives and the
340 # first is non-operative we should still update the mergeinfo to reflect
341 # this.
343 # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H. r5 gets merged first
344 # but is a no-op, r8 get's merged next and is operative so the mergeinfo
345 # should be updated to reflect both merges.
346 short_path = shorten_path_kludge(A_COPY_2_H_path)
347 expected_output = wc.State(short_path, {
348 'omega' : Item(status='U '),
350 expected_status = wc.State(short_path, {
351 '' : Item(status=' M', wc_rev=8),
352 'chi' : Item(status=' M', wc_rev=8),
353 'omega' : Item(status='MM', wc_rev=8),
355 expected_disk = wc.State('', {
356 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:5*,8*'}),
357 'omega' : Item("New content",
358 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:5,8'}),
359 'chi' : Item("This is the file 'chi'.\n",
360 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:5,8'}),
362 expected_skip = wc.State(short_path, {'psi' : Item()})
363 saved_cwd = os.getcwd()
364 os.chdir(svntest.main.work_dir)
365 svntest.actions.run_and_verify_merge(short_path, '4', '5',
366 sbox.repo_url + \
367 '/A/D/H',
368 expected_output,
369 expected_disk,
370 expected_status,
371 expected_skip,
372 None, None, None, None,
373 None, 1, 0, '-c5', '-c8')
375 os.chdir(saved_cwd)
377 # Test issue #2829 'Improve handling for skipped paths encountered
378 # during a merge'
380 # Revert previous changes to restricted WC
381 svntest.actions.run_and_verify_svn(None, None, [], 'revert', '--recursive',
382 wc_restricted)
383 # Add new path 'A/D/H/zeta'
384 svntest.main.file_write(zeta_path, "This is the file 'zeta'.\n")
385 svntest.actions.run_and_verify_svn(None, None, [], 'add', zeta_path)
386 expected_output = wc.State(wc_dir, {'A/D/H/zeta' : Item(verb='Adding')})
387 wc_status.add({'A/D/H/zeta' : Item(status=' ', wc_rev=9)})
388 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
389 wc_status, None, wc_dir)
391 # Merge -r7:9 to the restricted WC's A_COPY_2/D/H.
393 # r9 adds a path, 'A_COPY_2/D/H/zeta', which has a parent with
394 # non-inheritable mergeinfo (due to the fact 'A_COPY_2/D/H/psi' is missing).
395 # 'A_COPY_2/D/H/zeta' must therefore get its own explicit mergeinfo from
396 # this merge.
397 short_path = shorten_path_kludge(A_COPY_2_H_path)
398 expected_output = wc.State(short_path, {
399 'omega' : Item(status='U '),
400 'zeta' : Item(status='A '),
402 expected_status = wc.State(short_path, {
403 '' : Item(status=' M', wc_rev=8),
404 'chi' : Item(status=' M', wc_rev=8),
405 'omega' : Item(status='MM', wc_rev=8),
406 'zeta' : Item(status='A ', copied='+', wc_rev='-'),
408 expected_disk = wc.State('', {
409 '' : Item(props={SVN_PROP_MERGEINFO : '/A/D/H:8-9*'}),
410 'omega' : Item("New content",
411 props={SVN_PROP_MERGEINFO : '/A/D/H/omega:8-9'}),
412 'chi' : Item("This is the file 'chi'.\n",
413 props={SVN_PROP_MERGEINFO : '/A/D/H/chi:8-9'}),
414 'zeta' : Item("This is the file 'zeta'.\n",
415 props={SVN_PROP_MERGEINFO : '/A/D/H/zeta:8-9'}),
417 expected_skip = wc.State(short_path, {})
418 saved_cwd = os.getcwd()
419 os.chdir(svntest.main.work_dir)
420 svntest.actions.run_and_verify_merge(short_path, '7', '9',
421 sbox.repo_url + \
422 '/A/D/H',
423 expected_output,
424 expected_disk,
425 expected_status,
426 expected_skip,
427 None, None, None, None,
428 None, 1, 0)
429 os.chdir(saved_cwd)
431 ########################################################################
432 # Run the tests
435 # list all tests here, starting with None:
436 test_list = [ None,
437 SkipUnless(Skip(mergeinfo_and_skipped_paths,
438 svntest.main.is_ra_type_file),
439 svntest.main.server_has_mergeinfo),
442 if __name__ == '__main__':
443 svntest.main.run_tests(test_list, serial_only = True)
444 # NOTREACHED
447 ### End of file.