Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / tests / cmdline / prop_tests.py
blob29e8fcec4ff80769e305b25b992918ff0564ae0b
1 #!/usr/bin/env python
3 # prop_tests.py: testing versioned properties
5 # Subversion is a tool for revision control.
6 # See http://subversion.tigris.org for more information.
8 # ====================================================================
9 # Copyright (c) 2000-2004 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 sys, re, os, stat
22 # Our testing module
23 import svntest
25 from svntest.main import SVN_PROP_MERGEINFO
27 # (abbreviation)
28 Skip = svntest.testcase.Skip
29 SkipUnless = svntest.testcase.SkipUnless
30 XFail = svntest.testcase.XFail
31 Item = svntest.wc.StateItem
33 def is_non_posix_and_non_windows_os():
34 """lambda function to skip revprop_change test"""
35 return (not svntest.main.is_posix_os()) and sys.platform != 'win32'
37 ######################################################################
38 # Tests
40 #----------------------------------------------------------------------
42 def make_local_props(sbox):
43 "write/read props in wc only (ps, pl, pdel, pe)"
45 # Bootstrap
46 sbox.build()
47 wc_dir = sbox.wc_dir
49 # Add properties to one file and one directory
50 svntest.main.run_svn(None, 'propset', 'blue', 'azul',
51 os.path.join(wc_dir, 'A', 'mu'))
52 svntest.main.run_svn(None, 'propset', 'green', 'verde',
53 os.path.join(wc_dir, 'A', 'mu'))
54 svntest.main.run_svn(None, 'propset', 'editme', 'the foo fighters',
55 os.path.join(wc_dir, 'A', 'mu'))
56 svntest.main.run_svn(None, 'propset', 'red', 'rojo',
57 os.path.join(wc_dir, 'A', 'D', 'G'))
58 svntest.main.run_svn(None, 'propset', 'yellow', 'amarillo',
59 os.path.join(wc_dir, 'A', 'D', 'G'))
61 # Make sure they show up as local mods in status
62 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
63 expected_status.tweak('A/mu', status=' M')
64 expected_status.tweak('A/D/G', status=' M')
66 svntest.actions.run_and_verify_status(wc_dir, expected_status)
68 # Remove one property
69 svntest.main.run_svn(None, 'propdel', 'yellow',
70 os.path.join(wc_dir, 'A', 'D', 'G'))
72 svntest.main.use_editor('foo_to_bar')
73 # Edit one property
74 svntest.main.run_svn(None, 'propedit', 'editme',
75 os.path.join(wc_dir, 'A', 'mu'))
77 # What we expect the disk tree to look like:
78 expected_disk = svntest.main.greek_state.copy()
79 expected_disk.tweak('A/mu', props={'blue' : 'azul', 'green' : 'verde',
80 'editme' : 'the bar fighters'})
81 expected_disk.tweak('A/D/G', props={'red' : 'rojo'})
83 # Read the real disk tree. Notice we are passing the (normally
84 # disabled) "load props" flag to this routine. This will run 'svn
85 # proplist' on every item in the working copy!
86 actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
88 # Compare actual vs. expected disk trees.
89 svntest.tree.compare_trees("disk", actual_disk_tree,
90 expected_disk.old_tree())
92 # Edit without actually changing the property
93 svntest.main.use_editor('identity')
94 svntest.actions.run_and_verify_svn(None,
95 "No changes to property 'editme' on '.*'",
96 [],
97 'propedit', 'editme',
98 os.path.join(wc_dir, 'A', 'mu'))
102 #----------------------------------------------------------------------
104 def commit_props(sbox):
105 "commit properties"
107 # Bootstrap
108 sbox.build()
109 wc_dir = sbox.wc_dir
111 # Add a property to a file and a directory
112 mu_path = os.path.join(wc_dir, 'A', 'mu')
113 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
114 svntest.main.run_svn(None, 'propset', 'blue', 'azul', mu_path)
115 svntest.main.run_svn(None, 'propset', 'red', 'rojo', H_path)
117 # Create expected output tree.
118 expected_output = svntest.wc.State(wc_dir, {
119 'A/mu' : Item(verb='Sending'),
120 'A/D/H' : Item(verb='Sending'),
123 # Created expected status tree.
124 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
125 expected_status.tweak('A/mu', 'A/D/H', wc_rev=2, status=' ')
127 # Commit the one file.
128 svntest.actions.run_and_verify_commit(wc_dir,
129 expected_output,
130 expected_status,
131 None,
132 wc_dir)
136 #----------------------------------------------------------------------
138 def update_props(sbox):
139 "receive properties via update"
141 # Bootstrap
142 sbox.build()
143 wc_dir = sbox.wc_dir
145 # Make a backup copy of the working copy
146 wc_backup = sbox.add_wc_path('backup')
147 svntest.actions.duplicate_dir(wc_dir, wc_backup)
149 # Add a property to a file and a directory
150 mu_path = os.path.join(wc_dir, 'A', 'mu')
151 H_path = os.path.join(wc_dir, 'A', 'D', 'H')
152 svntest.main.run_svn(None, 'propset', 'blue', 'azul', mu_path)
153 svntest.main.run_svn(None, 'propset', 'red', 'rojo', H_path)
155 # Create expected output tree.
156 expected_output = svntest.wc.State(wc_dir, {
157 'A/mu' : Item(verb='Sending'),
158 'A/D/H' : Item(verb='Sending'),
161 # Created expected status tree.
162 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
163 expected_status.tweak('A/mu', 'A/D/H', wc_rev=2, status=' ')
165 # Commit the one file.
166 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
167 expected_status,
168 None, wc_dir)
170 # Overwrite mu_path and H_path to refer to the backup copies from
171 # here on out.
172 mu_path = os.path.join(wc_backup, 'A', 'mu')
173 H_path = os.path.join(wc_backup, 'A', 'D', 'H')
175 # Create expected output tree for an update of the wc_backup.
176 expected_output = svntest.wc.State(wc_backup, {
177 'A/mu' : Item(status=' U'),
178 'A/D/H' : Item(status=' U'),
181 # Create expected disk tree for the update.
182 expected_disk = svntest.main.greek_state.copy()
183 expected_disk.tweak('A/mu', props={'blue' : 'azul'})
184 expected_disk.tweak('A/D/H', props={'red' : 'rojo'})
186 # Create expected status tree for the update.
187 expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
188 expected_status.tweak('A/mu', 'A/D/H', status=' ')
190 # Do the update and check the results in three ways... INCLUDING PROPS
191 svntest.actions.run_and_verify_update(wc_backup,
192 expected_output,
193 expected_disk,
194 expected_status,
195 None, None, None, None, None, 1)
197 #----------------------------------------------------------------------
199 def downdate_props(sbox):
200 "receive property changes as part of a downdate"
202 # Bootstrap
203 sbox.build()
204 wc_dir = sbox.wc_dir
206 iota_path = os.path.join(wc_dir, 'iota')
207 mu_path = os.path.join(wc_dir, 'A', 'mu')
209 # Add a property to a file
210 svntest.main.run_svn(None, 'propset', 'cash-sound', 'cha-ching!', iota_path)
212 # Create expected output tree.
213 expected_output = svntest.wc.State(wc_dir, {
214 'iota' : Item(verb='Sending'),
217 # Created expected status tree.
218 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
219 expected_status.tweak('iota', wc_rev=2, status=' ')
221 # Commit the one file.
222 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
223 expected_status,
224 None, wc_dir)
226 # Make some mod (something to commit)
227 svntest.main.file_append(mu_path, "some mod")
229 # Create expected output tree.
230 expected_output = svntest.wc.State(wc_dir, {
231 'A/mu' : Item(verb='Sending'),
234 # Created expected status tree.
235 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
236 expected_status.tweak('iota', wc_rev=2, status=' ')
237 expected_status.tweak('A/mu', wc_rev=3, status=' ')
239 # Commit the one file.
240 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
241 expected_status,
242 None, wc_dir)
244 # Create expected output tree for an update.
245 expected_output = svntest.wc.State(wc_dir, {
246 'iota' : Item(status=' U'),
247 'A/mu' : Item(status='U '),
250 # Create expected disk tree for the update.
251 expected_disk = svntest.main.greek_state
253 # Create expected status tree for the update.
254 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
256 # Do the update and check the results in three ways... INCLUDING PROPS
257 svntest.actions.run_and_verify_update(wc_dir,
258 expected_output,
259 expected_disk,
260 expected_status,
261 None, None, None, None, None, 1,
262 '-r', '1', wc_dir)
264 #----------------------------------------------------------------------
266 def remove_props(sbox):
267 "commit the removal of props"
269 # Bootstrap
270 sbox.build()
271 wc_dir = sbox.wc_dir
273 # Add a property to a file
274 iota_path = os.path.join(wc_dir, 'iota')
275 svntest.main.run_svn(None, 'propset', 'cash-sound', 'cha-ching!', iota_path)
277 # Commit the file
278 svntest.main.run_svn(None,
279 'ci', '-m', 'logmsg', iota_path)
281 # Now, remove the property
282 svntest.main.run_svn(None, 'propdel', 'cash-sound', iota_path)
284 # Create expected output tree.
285 expected_output = svntest.wc.State(wc_dir, {
286 'iota' : Item(verb='Sending'),
289 # Created expected status tree.
290 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
291 expected_status.tweak('iota', wc_rev=3, status=' ')
293 # Commit the one file.
294 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
295 expected_status,
296 None, wc_dir)
298 #----------------------------------------------------------------------
300 def update_conflict_props(sbox):
301 "update with conflicting props"
303 # Bootstrap
304 sbox.build()
305 wc_dir = sbox.wc_dir
307 # Add a property to a file and a directory
308 mu_path = os.path.join(wc_dir, 'A', 'mu')
309 svntest.main.run_svn(None, 'propset', 'cash-sound', 'cha-ching!', mu_path)
310 A_path = os.path.join(wc_dir, 'A')
311 svntest.main.run_svn(None, 'propset', 'foo', 'bar', A_path)
313 # Commit the file and directory
314 svntest.main.run_svn(None,
315 'ci', '-m', 'logmsg', wc_dir)
317 # Update to rev 1
318 svntest.main.run_svn(None, 'up', '-r', '1', wc_dir)
320 # Add conflicting properties
321 svntest.main.run_svn(None, 'propset', 'cash-sound', 'beep!', mu_path)
322 svntest.main.run_svn(None, 'propset', 'foo', 'baz', A_path)
324 # Create expected output tree for an update of the wc_backup.
325 expected_output = svntest.wc.State(wc_dir, {
326 'A/mu' : Item(status=' C'),
327 'A' : Item(status=' C'),
330 # Create expected disk tree for the update.
331 expected_disk = svntest.main.greek_state.copy()
332 expected_disk.tweak('A/mu', props={'cash-sound' : 'beep!'})
333 expected_disk.tweak('A', props={'foo' : 'baz'})
335 # Create expected status tree for the update.
336 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
337 expected_status.tweak('A/mu', 'A', status=' C')
339 extra_files = ['mu.*\.prej', 'dir_conflicts.*\.prej']
340 # Do the update and check the results in three ways... INCLUDING PROPS
341 svntest.actions.run_and_verify_update(wc_dir,
342 expected_output,
343 expected_disk,
344 expected_status,
345 None,
346 svntest.tree.detect_conflict_files,
347 extra_files,
348 None, None, 1)
350 if len(extra_files) != 0:
351 print "didn't get expected conflict files"
352 raise svntest.verify.SVNUnexpectedOutput
354 # Resolve the conflicts
355 svntest.main.run_svn(None, 'resolved', mu_path)
356 svntest.main.run_svn(None, 'resolved', A_path)
358 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
359 expected_status.tweak('A/mu', 'A', status=' M')
361 svntest.actions.run_and_verify_status(wc_dir, expected_status)
363 #----------------------------------------------------------------------
364 def commit_conflict_dirprops(sbox):
365 "commit with conflicting dirprops"
367 # Issue #2608: failure to see conflicting dirprops on root of
368 # repository.
370 # Bootstrap
371 sbox.build()
372 wc_dir = sbox.wc_dir
374 svntest.main.run_svn(None, 'propset', 'foo', 'bar', wc_dir)
376 # Commit the file and directory
377 svntest.main.run_svn(None,
378 'ci', '-m', 'r2', wc_dir)
380 # Update to rev 1
381 svntest.main.run_svn(None,
382 'up', '-r', '1', wc_dir)
384 # Add conflicting properties
385 svntest.main.run_svn(None, 'propset', 'foo', 'eek', wc_dir)
387 svntest.actions.run_and_verify_commit(wc_dir, None, None,
388 "out[- ]of[- ]date",
389 wc_dir)
391 #----------------------------------------------------------------------
393 # Issue #742: we used to screw up when committing a file replacement
394 # that also had properties. It was fixed by teaching
395 # svn_wc_props_modified_p and svn_wc_transmit_prop_deltas to *ignore*
396 # leftover base-props when a file is scheduled for replacement. (When
397 # we svn_wc_add a file, it starts life with no working props.)
399 def commit_replacement_props(sbox):
400 "props work when committing a replacement"
402 # Bootstrap
403 sbox.build()
404 wc_dir = sbox.wc_dir
406 # Add a property to two files
407 iota_path = os.path.join(wc_dir, 'iota')
408 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
409 svntest.main.run_svn(None, 'propset', 'cash-sound', 'cha-ching!', iota_path)
410 svntest.main.run_svn(None, 'propset', 'boson', 'W', lambda_path)
412 # Commit (### someday use run_and_verify_commit for better coverage)
413 svntest.actions.run_and_verify_svn("Error in property commit",
414 None, [],
415 'ci', '-m', 'logmsg', wc_dir)
417 # Schedule both files for deletion
418 svntest.main.run_svn(None, 'rm', iota_path, lambda_path)
420 # Now recreate the files, and schedule them for addition.
421 # Poof, the 'new' files don't have any properties at birth.
422 svntest.main.file_append(iota_path, 'iota TNG')
423 svntest.main.file_append(lambda_path, 'lambda TNG')
424 svntest.main.run_svn(None, 'add', iota_path, lambda_path)
426 # Sanity check: the two files should be scheduled for (R)eplacement.
427 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
428 expected_status.tweak('iota', wc_rev=2, status='R ')
429 expected_status.tweak('A/B/lambda', wc_rev=2, status='R ')
431 svntest.actions.run_and_verify_status(wc_dir, expected_status)
433 # Now add a property to lambda. Iota still doesn't have any.
434 svntest.main.run_svn(None, 'propset', 'capacitor', 'flux', lambda_path)
436 # Commit, with careful output checking. We're actually going to
437 # scan the working copy for props after the commit.
439 expected_output = svntest.wc.State(wc_dir, {
440 'iota' : Item(verb='Replacing'),
441 'A/B/lambda' : Item(verb='Replacing'),
444 # Expected status tree: lambda has one prop, iota doesn't.
445 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
446 expected_status.tweak('iota', wc_rev=3)
447 expected_status.tweak('A/B/lambda', wc_rev=3, status=' ')
449 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
450 expected_status,
451 None, wc_dir)
453 #----------------------------------------------------------------------
455 def revert_replacement_props(sbox):
456 "props work when reverting a replacement"
458 # Bootstrap
459 sbox.build()
460 wc_dir = sbox.wc_dir
462 # Add a property to two files
463 iota_path = os.path.join(wc_dir, 'iota')
464 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
465 svntest.main.run_svn(None, 'propset', 'cash-sound', 'cha-ching!', iota_path)
466 svntest.main.run_svn(None, 'propset', 'boson', 'W', lambda_path)
468 # Commit rev 2. (### someday use run_and_verify_commit for better coverage)
469 svntest.actions.run_and_verify_svn("Error in property commit", None, [],
470 'ci', '-m', 'logmsg', wc_dir)
472 # Schedule both files for deletion
473 svntest.main.run_svn(None, 'rm', iota_path, lambda_path)
475 # Now recreate the files, and schedule them for addition.
476 # Poof, the 'new' files don't have any properties at birth.
477 svntest.main.file_append(iota_path, 'iota TNG')
478 svntest.main.file_append(lambda_path, 'lambda TNG')
479 svntest.main.run_svn(None, 'add', iota_path, lambda_path)
481 # Sanity check: the two files should be scheduled for (R)eplacement.
482 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
483 expected_status.tweak('iota', wc_rev=2, status='R ')
484 expected_status.tweak('A/B/lambda', wc_rev=2, status='R ')
486 svntest.actions.run_and_verify_status(wc_dir, expected_status)
488 # Now add a property to lambda. Iota still doesn't have any.
489 svntest.main.run_svn(None, 'propset', 'capacitor', 'flux', lambda_path)
491 # Now revert both files.
492 svntest.main.run_svn(None, 'revert', iota_path, lambda_path)
494 # Do an update; even though the update is really a no-op,
495 # run_and_verify_update has the nice feature of scanning disk as
496 # well as running status. We want to verify that we truly have a
497 # *pristine* revision 2 tree, with the original rev 2 props, and no
498 # local mods at all.
500 expected_output = svntest.wc.State(wc_dir, {
503 expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
504 expected_status.tweak('iota', status=' ')
505 expected_status.tweak('A/B/lambda', status=' ')
507 expected_disk = svntest.main.greek_state.copy()
508 expected_disk.tweak('iota', props={'cash-sound' : 'cha-ching!'})
509 expected_disk.tweak('A/B/lambda', props={'boson' : 'W'})
511 # scan disk for props too.
512 svntest.actions.run_and_verify_update(wc_dir,
513 expected_output,
514 expected_disk,
515 expected_status,
516 None, None, None, None, None,
519 #----------------------------------------------------------------------
521 def inappropriate_props(sbox):
522 "try to set inappropriate props"
524 # Bootstrap
525 sbox.build()
526 wc_dir = sbox.wc_dir
528 A_path = os.path.join(wc_dir, 'A')
529 E_path = os.path.join(wc_dir, 'A', 'B', 'E')
530 iota_path = os.path.join(wc_dir, 'iota')
532 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
533 svntest.actions.run_and_verify_status(wc_dir, expected_status)
535 # These should produce an error
536 svntest.actions.run_and_verify_svn('Illegal target',
537 None, svntest.verify.AnyOutput,
538 'propset', 'svn:executable', 'on', A_path)
540 svntest.actions.run_and_verify_svn('Illegal target', None,
541 svntest.verify.AnyOutput, 'propset',
542 'svn:keywords', 'LastChangedDate',
543 A_path)
545 svntest.actions.run_and_verify_svn('Illegal target', None,
546 svntest.verify.AnyOutput, 'propset',
547 'svn:eol-style', 'native', A_path)
549 svntest.actions.run_and_verify_svn('Invalid svn:eol-style', None,
550 svntest.verify.AnyOutput, 'propset',
551 'svn:eol-style', 'invalid value',
552 os.path.join(A_path, 'mu'))
554 svntest.actions.run_and_verify_svn('Illegal target', None,
555 svntest.verify.AnyOutput, 'propset',
556 'svn:mime-type', 'image/png', A_path)
558 svntest.actions.run_and_verify_svn('Illegal target', None,
559 svntest.verify.AnyOutput, 'propset',
560 'svn:ignore', '*.o', iota_path)
562 svntest.actions.run_and_verify_svn('Illegal target', None,
563 svntest.verify.AnyOutput, 'propset',
564 'svn:externals',
565 'foo http://host.com/repos', iota_path)
567 svntest.actions.run_and_verify_svn('Illegal target', None,
568 svntest.verify.AnyOutput, 'propset',
569 'svn:author', 'socrates', iota_path)
571 svntest.actions.run_and_verify_svn('Illegal target', None,
572 svntest.verify.AnyOutput, 'propset',
573 'svn:log', 'log message', iota_path)
575 svntest.actions.run_and_verify_svn('Illegal target', None,
576 svntest.verify.AnyOutput, 'propset',
577 'svn:date', 'Tue Jan 19 04:14:07 2038',
578 iota_path)
580 svntest.actions.run_and_verify_svn('Illegal target', None,
581 svntest.verify.AnyOutput, 'propset',
582 'svn:original-date',
583 'Thu Jan 1 01:00:00 1970', iota_path)
585 # Status unchanged
586 svntest.actions.run_and_verify_status(wc_dir, expected_status)
588 # Recursive setting of inappropriate dir prop should work on files
589 svntest.actions.run_and_verify_svn(None, None, [], 'propset', '-R',
590 'svn:executable', 'on', E_path)
592 expected_status.tweak('A/B/E/alpha', 'A/B/E/beta', status=' M')
593 svntest.actions.run_and_verify_status(wc_dir, expected_status)
595 # Issue #920. Don't allow setting of svn:eol-style on binary files or files
596 # with inconsistent eol types.
598 path = os.path.join(wc_dir, 'binary')
599 svntest.main.file_append(path, "binary")
600 svntest.main.run_svn(None, 'add', path)
602 svntest.main.run_svn(None, 'propset', 'svn:mime-type',
603 'application/octet-stream', path)
605 svntest.actions.run_and_verify_svn('Illegal target', None,
606 svntest.verify.AnyOutput,
607 'propset', 'svn:eol-style',
608 'CRLF', path)
610 path = os.path.join(wc_dir, 'multi-eol')
611 svntest.main.file_append(path, "line1\rline2\n")
612 svntest.main.run_svn(None, 'add', path)
614 svntest.actions.run_and_verify_svn('Illegal target', None,
615 svntest.verify.AnyOutput,
616 'propset', 'svn:eol-style',
617 'LF', path)
619 path = os.path.join(wc_dir, 'backwards-eol')
620 svntest.main.file_append(path, "line1\n\r")
621 svntest.main.run_svn(None, 'add', path)
623 svntest.actions.run_and_verify_svn('Illegal target', None,
624 svntest.verify.AnyOutput,
625 'propset', 'svn:eol-style',
626 'native', path)
628 path = os.path.join(wc_dir, 'incomplete-eol')
629 svntest.main.file_append(path, "line1\r\n\r")
630 svntest.main.run_svn(None, 'add', path)
632 svntest.actions.run_and_verify_svn('Illegal target', None,
633 svntest.verify.AnyOutput,
634 'propset', 'svn:eol-style',
635 'CR', path)
637 # Issue #2065. Do allow setting of svn:eol-style on binary files or files
638 # with inconsistent eol types if --force is passed.
640 path = os.path.join(wc_dir, 'binary')
641 svntest.main.file_append(path, "binary")
642 svntest.actions.run_and_verify_svn(None, None, [],
643 'propset', '--force',
644 'svn:eol-style', 'CRLF',
645 path)
647 path = os.path.join(wc_dir, 'multi-eol')
648 svntest.actions.run_and_verify_svn(None, None, [],
649 'propset', '--force',
650 'svn:eol-style', 'LF',
651 path)
653 path = os.path.join(wc_dir, 'backwards-eol')
654 svntest.actions.run_and_verify_svn(None, None, [],
655 'propset', '--force',
656 'svn:eol-style', 'native',
657 path)
659 path = os.path.join(wc_dir, 'incomplete-eol')
660 svntest.actions.run_and_verify_svn(None, None, [],
661 'propset', '--force',
662 'svn:eol-style', 'CR',
663 path)
665 # Prevent setting of svn:mergeinfo prop values that are...
666 path = os.path.join(wc_dir, 'A', 'D')
668 # ...grammatically incorrect
669 svntest.actions.run_and_verify_svn('illegal grammar', None,
670 "svn: Pathname not terminated by ':'\n",
671 'propset', SVN_PROP_MERGEINFO, '/trunk',
672 path)
673 svntest.actions.run_and_verify_svn('illegal grammar', None,
674 "svn: Invalid revision number found "
675 "parsing 'one'\n",
676 'propset', SVN_PROP_MERGEINFO,
677 '/trunk:one', path)
679 # ...contain overlapping revision ranges
680 svntest.actions.run_and_verify_svn('overlapping ranges', None,
681 "svn: Parsing of overlapping revision "
682 "ranges '9-20' and '18-22' is not "
683 "supported\n",
684 'propset', SVN_PROP_MERGEINFO,
685 '/branch:5-7,9-20,18-22', path)
687 svntest.actions.run_and_verify_svn('overlapping ranges', None,
688 "svn: Parsing of overlapping revision "
689 "ranges '3' and '3' is not supported\n",
690 'propset', SVN_PROP_MERGEINFO,
691 '/branch:3,3', path)
693 # ...contain unordered revision ranges
694 svntest.actions.run_and_verify_svn('unordered ranges', None,
695 "svn: Unable to parse unordered "
696 "revision ranges '5' and '2-3'\n",
697 'propset', SVN_PROP_MERGEINFO,
698 '/featureX:5,2-3,9', path)
700 # ...contain revision ranges with start revisions greater than or
701 # equal to end revisions.
702 svntest.actions.run_and_verify_svn('range start >= range end', None,
703 "svn: Unable to parse reversed "
704 "revision range '20-5'\n",
705 'propset', SVN_PROP_MERGEINFO,
706 '/featureX:4,20-5', path)
708 # ...contain paths mapped to empty revision ranges
709 svntest.actions.run_and_verify_svn('empty ranges', None,
710 "svn: Mergeinfo for '/trunk' maps to "
711 "an empty revision range\n",
712 'propset', SVN_PROP_MERGEINFO,
713 '/trunk:', path)
715 #----------------------------------------------------------------------
717 # Issue #976. When copying a file, do not determine svn:executable
718 # and svn:mime-type values as though the file is brand new, instead
719 # use the copied file's property values.
721 def copy_inherits_special_props(sbox):
722 "file copies inherit (not re-derive) special props"
724 # Bootstrap
725 sbox.build()
726 wc_dir = sbox.wc_dir
728 orig_mime_type = 'image/fake_image'
730 # Create two paths
731 new_path1 = os.path.join(wc_dir, 'new_file1.bin')
732 new_path2 = os.path.join(wc_dir, 'new_file2.bin')
734 # Create the first path as a binary file. To have svn treat the
735 # file as binary, have a 0x00 in the file.
736 svntest.main.file_append(new_path1, "binary file\000")
737 svntest.main.run_svn(None, 'add', new_path1)
739 # Add initial svn:mime-type to the file
740 svntest.main.run_svn(None, 'propset', 'svn:mime-type', orig_mime_type,
741 new_path1)
743 # Set the svn:executable property on the file if this is a system
744 # that can handle chmod, in which case svn will turn on the
745 # executable bits on the file. Then remove the executable bits
746 # manually on the file and see the value of svn:executable in the
747 # copied file.
748 if os.name == 'posix':
749 svntest.main.run_svn(None, 'propset', 'svn:executable', 'on', new_path1)
750 os.chmod(new_path1, 0644)
752 # Commit the file
753 svntest.main.run_svn(None,
754 'ci', '-m', 'create file and set svn:mime-type',
755 wc_dir)
757 # Copy the file
758 svntest.main.run_svn(None, 'cp', new_path1, new_path2)
760 # Check the svn:mime-type
761 actual_stdout, actual_stderr = svntest.main.run_svn(None,
762 'pg',
763 'svn:mime-type',
764 new_path2)
765 expected_stdout = [orig_mime_type + '\n']
766 if actual_stdout != expected_stdout:
767 print "svn pg svn:mime-type output does not match expected."
768 print "Expected standard output: ", expected_stdout, "\n"
769 print "Actual standard output: ", actual_stdout, "\n"
770 raise svntest.verify.SVNUnexpectedOutput
772 # Check the svn:executable value.
773 # The value of the svn:executable property is now always forced to '*'
774 if os.name == 'posix':
775 actual_stdout, actual_stderr = svntest.main.run_svn(None,
776 'pg',
777 'svn:executable',
778 new_path2)
779 expected_stdout = ['*\n']
780 if actual_stdout != expected_stdout:
781 print "svn pg svn:executable output does not match expected."
782 print "Expected standard output: ", expected_stdout, "\n"
783 print "Actual standard output: ", actual_stdout, "\n"
784 raise svntest.verify.SVNUnexpectedOutput
786 #----------------------------------------------------------------------
788 def revprop_change(sbox):
789 "set, get, and delete a revprop change"
791 sbox.build()
793 # First test the error when no revprop-change hook exists.
794 svntest.actions.run_and_verify_svn(None, None, '.*pre-revprop-change',
795 'propset', '--revprop', '-r', '0',
796 'cash-sound', 'cha-ching!', sbox.wc_dir)
798 # Now test error output from revprop-change hook.
799 svntest.actions.disable_revprop_changes(sbox.repo_dir)
800 svntest.actions.run_and_verify_svn(None, None, '.*pre-revprop-change.* 0 jrandom cash-sound A',
801 'propset', '--revprop', '-r', '0',
802 'cash-sound', 'cha-ching!', sbox.wc_dir)
804 # Create the revprop-change hook for this test
805 svntest.actions.enable_revprop_changes(sbox.repo_dir)
807 svntest.actions.run_and_verify_svn(None, None, [],
808 'propset', '--revprop', '-r', '0',
809 'cash-sound', 'cha-ching!', sbox.wc_dir)
811 svntest.actions.run_and_verify_svn(None, None, [],
812 'propget', '--revprop', '-r', '0',
813 'cash-sound', sbox.wc_dir)
815 # Now test that blocking the revprop delete.
816 svntest.actions.disable_revprop_changes(sbox.repo_dir)
817 svntest.actions.run_and_verify_svn(None, None, '.*pre-revprop-change.* 0 jrandom cash-sound D',
818 'propdel', '--revprop', '-r', '0',
819 'cash-sound', sbox.wc_dir)
821 # Now test actually deleting the revprop.
822 svntest.actions.enable_revprop_changes(sbox.repo_dir)
823 svntest.actions.run_and_verify_svn(None, None, [],
824 'propdel', '--revprop', '-r', '0',
825 'cash-sound', sbox.wc_dir)
827 actual_stdout, actual_stderr = svntest.main.run_svn(None,
828 'pg', '--revprop',
829 '-r', '0',
830 'cash-sound',
831 sbox.wc_dir)
833 # The property should have been deleted.
834 regex = 'cha-ching'
835 for line in actual_stdout:
836 if re.match(regex, line):
837 raise svntest.Failure
840 #----------------------------------------------------------------------
842 def prop_value_conversions(sbox):
843 "some svn: properties should be converted"
845 # Bootstrap
846 sbox.build()
847 wc_dir = sbox.wc_dir
849 A_path = os.path.join(wc_dir, 'A')
850 B_path = os.path.join(wc_dir, 'A', 'B')
851 iota_path = os.path.join(wc_dir, 'iota')
852 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
853 mu_path = os.path.join(wc_dir, 'A', 'mu')
855 # We'll use a file to set the prop values, so that weird characters
856 # in the props don't confuse the shell.
857 propval_path = os.path.join(wc_dir, 'propval.tmp')
858 propval_file = open(propval_path, 'wb')
860 def set_prop(name, value, path, valf=propval_file, valp=propval_path,
861 expected_error=None):
862 valf.seek(0)
863 valf.truncate(0)
864 valf.write(value)
865 valf.flush()
866 svntest.main.run_svn(expected_error, 'propset', '-F', valp, name, path)
868 # Leading and trailing whitespace should be stripped
869 set_prop('svn:mime-type', ' text/html\n\n', iota_path)
870 set_prop('svn:mime-type', 'text/html', mu_path)
872 # Leading and trailing whitespace should be stripped
873 set_prop('svn:eol-style', '\nnative\n', iota_path)
874 set_prop('svn:eol-style', 'native', mu_path)
876 # A trailing newline should be added
877 set_prop('svn:ignore', '*.o\nfoo.c', A_path)
878 set_prop('svn:ignore', '*.o\nfoo.c\n', B_path)
880 # A trailing newline should be added
881 set_prop('svn:externals', 'foo http://foo.com/repos', A_path)
882 set_prop('svn:externals', 'foo http://foo.com/repos\n', B_path)
884 # Leading and trailing whitespace should be stripped, but not internal
885 # whitespace
886 set_prop('svn:keywords', ' Rev Date \n', iota_path)
887 set_prop('svn:keywords', 'Rev Date', mu_path)
889 # svn:executable value should be forced to a '*'
890 set_prop('svn:executable', 'foo', iota_path)
891 set_prop('svn:executable', '*', lambda_path)
892 for pval in (' ', '', 'no', 'off', 'false'):
893 set_prop('svn:executable', pval, mu_path, propval_file, propval_path,
894 ["svn: warning: To turn off the svn:executable property, "
895 "use 'svn propdel';\n",
896 "setting the property to '" + pval +
897 "' will not turn it off.\n"])
899 # Anything else should be untouched
900 set_prop('svn:some-prop', 'bar', lambda_path)
901 set_prop('svn:some-prop', ' bar baz', mu_path)
902 set_prop('svn:some-prop', 'bar\n', iota_path)
903 set_prop('some-prop', 'bar', lambda_path)
904 set_prop('some-prop', ' bar baz', mu_path)
905 set_prop('some-prop', 'bar\n', iota_path)
907 # Close and remove the prop value file
908 propval_file.close()
909 os.unlink(propval_path)
911 # NOTE: When writing out multi-line prop values in svn:* props, the
912 # client converts to local encoding and local eoln style.
913 # Therefore, the expected output must contain the right kind of eoln
914 # strings. That's why we use os.linesep in the tests below, not just
915 # plain '\n'. The _last_ \n is also from the client, but it's not
916 # part of the prop value and it doesn't get converted in the pipe.
918 # Check svn:mime-type
919 svntest.actions.check_prop('svn:mime-type', iota_path, ['text/html'])
920 svntest.actions.check_prop('svn:mime-type', mu_path, ['text/html'])
922 # Check svn:eol-style
923 svntest.actions.check_prop('svn:eol-style', iota_path, ['native'])
924 svntest.actions.check_prop('svn:eol-style', mu_path, ['native'])
926 # Check svn:ignore
927 svntest.actions.check_prop('svn:ignore', A_path,
928 ['*.o'+os.linesep, 'foo.c'+os.linesep])
929 svntest.actions.check_prop('svn:ignore', B_path,
930 ['*.o'+os.linesep, 'foo.c'+os.linesep])
932 # Check svn:externals
933 svntest.actions.check_prop('svn:externals', A_path,
934 ['foo http://foo.com/repos'+os.linesep])
935 svntest.actions.check_prop('svn:externals', B_path,
936 ['foo http://foo.com/repos'+os.linesep])
938 # Check svn:keywords
939 svntest.actions.check_prop('svn:keywords', iota_path, ['Rev Date'])
940 svntest.actions.check_prop('svn:keywords', mu_path, ['Rev Date'])
942 # Check svn:executable
943 svntest.actions.check_prop('svn:executable', iota_path, ['*'])
944 svntest.actions.check_prop('svn:executable', lambda_path, ['*'])
945 svntest.actions.check_prop('svn:executable', mu_path, ['*'])
947 # Check other props
948 svntest.actions.check_prop('svn:some-prop', lambda_path, ['bar'])
949 svntest.actions.check_prop('svn:some-prop', mu_path, [' bar baz'])
950 svntest.actions.check_prop('svn:some-prop', iota_path, ['bar'+os.linesep])
951 svntest.actions.check_prop('some-prop', lambda_path, ['bar'])
952 svntest.actions.check_prop('some-prop', mu_path,[' bar baz'])
953 svntest.actions.check_prop('some-prop', iota_path, ['bar\n'])
956 #----------------------------------------------------------------------
958 def binary_props(sbox):
959 "test binary property support"
961 # Bootstrap
962 sbox.build()
963 wc_dir = sbox.wc_dir
965 # Make a backup copy of the working copy
966 wc_backup = sbox.add_wc_path('backup')
967 svntest.actions.duplicate_dir(wc_dir, wc_backup)
969 # Some path convenience vars.
970 A_path = os.path.join(wc_dir, 'A')
971 B_path = os.path.join(wc_dir, 'A', 'B')
972 iota_path = os.path.join(wc_dir, 'iota')
973 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
974 mu_path = os.path.join(wc_dir, 'A', 'mu')
975 A_path_bak = os.path.join(wc_backup, 'A')
976 B_path_bak = os.path.join(wc_backup, 'A', 'B')
977 iota_path_bak = os.path.join(wc_backup, 'iota')
978 lambda_path_bak = os.path.join(wc_backup, 'A', 'B', 'lambda')
979 mu_path_bak = os.path.join(wc_backup, 'A', 'mu')
981 # Property value convenience vars.
982 prop_zb = "This property has a zer\000 byte."
983 prop_ff = "This property has a form\014feed."
984 prop_xml = "This property has an <xml> tag."
985 prop_binx = "This property has an <xml> tag and a zer\000 byte."
987 # Set some binary properties.
988 propval_path = os.path.join(wc_dir, 'propval.tmp')
989 propval_file = open(propval_path, 'wb')
991 def set_prop(name, value, path, valf=propval_file, valp=propval_path):
992 valf.seek(0)
993 valf.truncate(0)
994 valf.write(value)
995 valf.flush()
996 svntest.main.run_svn(None, 'propset', '-F', valp, name, path)
998 set_prop('prop_zb', prop_zb, B_path)
999 set_prop('prop_ff', prop_ff, iota_path)
1000 set_prop('prop_xml', prop_xml, lambda_path)
1001 set_prop('prop_binx', prop_binx, mu_path)
1002 set_prop('prop_binx', prop_binx, A_path)
1004 # Create expected output and status trees.
1005 expected_output = svntest.wc.State(wc_dir, {
1006 'A' : Item(verb='Sending'),
1007 'A/B' : Item(verb='Sending'),
1008 'iota' : Item(verb='Sending'),
1009 'A/B/lambda' : Item(verb='Sending'),
1010 'A/mu' : Item(verb='Sending'),
1012 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1013 expected_status.tweak('A', 'A/B', 'iota', 'A/B/lambda', 'A/mu',
1014 wc_rev=2, status=' ')
1016 # Commit the propsets.
1017 svntest.actions.run_and_verify_commit(wc_dir,
1018 expected_output,
1019 expected_status,
1020 None,
1021 wc_dir)
1023 # Create expected output, disk, and status trees for an update of
1024 # the wc_backup.
1025 expected_output = svntest.wc.State(wc_backup, {
1026 'A' : Item(status=' U'),
1027 'A/B' : Item(status=' U'),
1028 'iota' : Item(status=' U'),
1029 'A/B/lambda' : Item(status=' U'),
1030 'A/mu' : Item(status=' U'),
1032 expected_disk = svntest.main.greek_state.copy()
1033 expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
1035 # Do the update and check the results.
1036 svntest.actions.run_and_verify_update(wc_backup,
1037 expected_output,
1038 expected_disk,
1039 expected_status,
1040 None, None, None, None, None, 0)
1042 # Now, check those properties.
1043 svntest.actions.check_prop('prop_zb', B_path_bak, [prop_zb])
1044 svntest.actions.check_prop('prop_ff', iota_path_bak, [prop_ff])
1045 svntest.actions.check_prop('prop_xml', lambda_path_bak, [prop_xml])
1046 svntest.actions.check_prop('prop_binx', mu_path_bak, [prop_binx])
1047 svntest.actions.check_prop('prop_binx', A_path_bak, [prop_binx])
1049 #----------------------------------------------------------------------
1051 # Ensure that each line of output contains the corresponding string of
1052 # expected_out, and that errput is empty.
1053 def verify_output(expected_out, output, errput):
1054 if errput != []:
1055 print 'Error: stderr:'
1056 print errput
1057 raise svntest.Failure
1058 output.sort()
1059 ln = 0
1060 for line in output:
1061 if ((line.find(expected_out[ln]) == -1) or
1062 (line != '' and expected_out[ln] == '')):
1063 print 'Error: expected keywords: ', expected_out
1064 print ' actual full output:', output
1065 raise svntest.Failure
1066 ln = ln + 1
1068 def recursive_base_wc_ops(sbox):
1069 "recursive property operations in BASE and WC"
1071 # Bootstrap
1072 sbox.build()
1073 wc_dir = sbox.wc_dir
1075 # Files with which to test, in alphabetical order
1076 f_add = os.path.join('A', 'added')
1077 f_del = os.path.join('A', 'mu')
1078 f_keep= os.path.join('iota')
1079 fp_add = os.path.join(wc_dir, f_add)
1080 fp_del = os.path.join(wc_dir, f_del)
1081 fp_keep= os.path.join(wc_dir, f_keep)
1083 # Set up properties
1084 svntest.main.run_svn(None, 'propset', 'p', 'old-del', fp_del)
1085 svntest.main.run_svn(None, 'propset', 'p', 'old-keep',fp_keep)
1086 svntest.main.run_svn(None,
1087 'commit', '-m', '', wc_dir)
1088 svntest.main.file_append(fp_add, 'blah')
1089 svntest.main.run_svn(None, 'add', fp_add)
1090 svntest.main.run_svn(None, 'propset', 'p', 'new-add', fp_add)
1091 svntest.main.run_svn(None, 'propset', 'p', 'new-del', fp_del)
1092 svntest.main.run_svn(None, 'propset', 'p', 'new-keep',fp_keep)
1093 svntest.main.run_svn(None, 'del', '--force', fp_del)
1095 # Test recursive proplist
1096 output, errput = svntest.main.run_svn(None, 'proplist', '-R', '-v', wc_dir,
1097 '-rBASE')
1098 verify_output([ 'old-del', 'old-keep', 'Properties on ', 'Properties on ' ],
1099 output, errput)
1101 output, errput = svntest.main.run_svn(None, 'proplist', '-R', '-v', wc_dir)
1102 verify_output([ 'new-add', 'new-keep', 'Properties on ', 'Properties on ' ],
1103 output, errput)
1105 # Test recursive propget
1106 output, errput = svntest.main.run_svn(None, 'propget', '-R', 'p', wc_dir,
1107 '-rBASE')
1108 verify_output([ 'old-del', 'old-keep' ], output, errput)
1110 output, errput = svntest.main.run_svn(None, 'propget', '-R', 'p', wc_dir)
1111 verify_output([ 'new-add', 'new-keep' ], output, errput)
1113 # Test recursive propset (issue 1794)
1114 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1115 expected_status.tweak('A/mu', status='D ', wc_rev=2)
1116 expected_status.tweak('iota', status=' M', wc_rev=2)
1117 expected_status.add({
1118 'A/added' : Item(status='A ', wc_rev=0),
1120 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1122 svntest.actions.run_and_verify_svn(None, None, [],
1123 'propset', '-R', 'svn:keywords', 'Date',
1124 os.path.join(wc_dir, 'A', 'B'))
1125 expected_status.tweak('A/B/lambda', 'A/B/E/alpha', 'A/B/E/beta', status=' M')
1126 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1128 #----------------------------------------------------------------------
1130 def url_props_ops(sbox):
1131 "property operations on an URL"
1133 # Bootstrap
1134 sbox.build()
1135 wc_dir = sbox.wc_dir
1137 prop1 = 'prop1'
1138 propval1 = 'propval1 is foo'
1139 prop2 = 'prop2'
1140 propval2 = 'propval2'
1142 iota_path = os.path.join(sbox.wc_dir, 'iota')
1143 iota_url = sbox.repo_url + '/iota'
1144 A_path = os.path.join(sbox.wc_dir, 'A')
1145 A_url = sbox.repo_url + '/A'
1147 # Add a couple of properties
1148 svntest.main.run_svn(None, 'propset', prop1, propval1, iota_path)
1149 svntest.main.run_svn(None, 'propset', prop1, propval1, A_path)
1151 # Commit
1152 svntest.main.run_svn(None,
1153 'ci', '-m', 'logmsg', sbox.wc_dir)
1155 # Add a few more properties
1156 svntest.main.run_svn(None, 'propset', prop2, propval2, iota_path)
1157 svntest.main.run_svn(None, 'propset', prop2, propval2, A_path)
1159 # Commit again
1160 svntest.main.run_svn(None,
1161 'ci', '-m', 'logmsg', sbox.wc_dir)
1163 # Test propget
1164 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1165 'propget', prop1, iota_url)
1166 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1167 'propget', prop1, A_url)
1169 # Test normal proplist
1170 output, errput = svntest.main.run_svn(None,
1171 'proplist', iota_url)
1172 verify_output([ prop1, prop2, 'Properties on ' ],
1173 output, errput)
1175 output, errput = svntest.main.run_svn(None,
1176 'proplist', A_url)
1177 verify_output([ prop1, prop2, 'Properties on ' ],
1178 output, errput)
1180 # Test verbose proplist
1181 output, errput = svntest.main.run_svn(None,
1182 'proplist', '-v', iota_url)
1183 verify_output([ prop1 + ' : ' + propval1, prop2 + ' : ' + propval2,
1184 'Properties on ' ], output, errput)
1186 output, errput = svntest.main.run_svn(None,
1187 'proplist', '-v', A_url)
1188 verify_output([ prop1 + ' : ' + propval1, prop2 + ' : ' + propval2,
1189 'Properties on ' ], output, errput)
1191 # Test propedit
1192 svntest.main.use_editor('foo_to_bar')
1193 propval1 = propval1.replace('foo', 'bar')
1194 svntest.main.run_svn(None,
1195 'propedit', prop1, '-m', 'editlog', iota_url)
1196 svntest.main.run_svn(None,
1197 'propedit', prop1, '-m', 'editlog', A_url)
1198 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1199 'propget', prop1, iota_url)
1200 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1201 'propget', prop1, A_url)
1203 # Edit without actually changing the property
1204 svntest.main.use_editor('identity')
1205 svntest.actions.run_and_verify_svn(None,
1206 "No changes to property '%s' on '.*'"
1207 % prop1,
1209 'propedit', prop1, '-m', 'nocommit',
1210 iota_url)
1214 #----------------------------------------------------------------------
1215 def removal_schedule_added_props(sbox):
1216 "removal of schedule added file with properties"
1218 sbox.build()
1220 wc_dir = sbox.wc_dir
1221 newfile_path = os.path.join(wc_dir, 'newfile')
1222 file_add_output = ["A " + newfile_path + "\n"]
1223 propset_output = ["property 'newprop' set on '" + newfile_path + "'\n"]
1224 file_rm_output = ["D " + newfile_path + "\n"]
1225 propls_output = [
1226 "Properties on '" + newfile_path + "':\n",
1227 " newprop : newvalue\n",
1230 # create new fs file
1231 open(newfile_path, 'w').close()
1232 # Add it and set a property
1233 svntest.actions.run_and_verify_svn(None, file_add_output, [], 'add', newfile_path)
1234 svntest.actions.run_and_verify_svn(None, propset_output, [], 'propset',
1235 'newprop', 'newvalue', newfile_path)
1236 svntest.actions.run_and_verify_svn(None, propls_output, [],
1237 'proplist', '-v', newfile_path)
1238 # remove the file
1239 svntest.actions.run_and_verify_svn(None, file_rm_output, [],
1240 'rm', '--force', newfile_path)
1241 # recreate the file and add it again
1242 open(newfile_path, 'w').close()
1243 svntest.actions.run_and_verify_svn(None, file_add_output, [], 'add', newfile_path)
1245 # Now there should be NO properties leftover...
1246 svntest.actions.run_and_verify_svn(None, [], [],
1247 'proplist', '-v', newfile_path)
1249 #----------------------------------------------------------------------
1251 def update_props_on_wc_root(sbox):
1252 "receive properties on the wc root via update"
1254 # Bootstrap
1255 sbox.build()
1256 wc_dir = sbox.wc_dir
1258 # Make a backup copy of the working copy
1259 wc_backup = sbox.add_wc_path('backup')
1260 svntest.actions.duplicate_dir(wc_dir, wc_backup)
1262 # Add a property to the root folder
1263 svntest.main.run_svn(None, 'propset', 'red', 'rojo', wc_dir)
1265 # Create expected output tree.
1266 expected_output = svntest.wc.State(wc_dir, {
1267 '' : Item(verb='Sending')
1270 # Created expected status tree.
1271 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1272 expected_status.tweak('', wc_rev=2, status=' ')
1274 # Commit the working copy
1275 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1276 expected_status,
1277 None, wc_dir)
1279 # Create expected output tree for an update of the wc_backup.
1280 expected_output = svntest.wc.State(wc_backup, {
1281 '' : Item(status=' U'),
1283 # Create expected disk tree for the update.
1284 expected_disk = svntest.main.greek_state.copy()
1285 expected_disk.add({
1286 '' : Item(props = {'red' : 'rojo'}),
1288 # Create expected status tree for the update.
1289 expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
1290 expected_status.tweak('', status=' ')
1292 # Do the update and check the results in three ways... INCLUDING PROPS
1293 svntest.actions.run_and_verify_update(wc_backup,
1294 expected_output,
1295 expected_disk,
1296 expected_status,
1297 None, None, None, None, None, 1)
1299 # test for issue 2743
1300 def props_on_replaced_file(sbox):
1301 """test properties on replaced files"""
1303 sbox.build()
1304 wc_dir = sbox.wc_dir
1306 # Add some properties to iota
1307 iota_path = os.path.join(wc_dir, "iota")
1308 svntest.main.run_svn(None, 'propset', 'red', 'rojo', iota_path)
1309 svntest.main.run_svn(None, 'propset', 'blue', 'lagoon', iota_path)
1310 svntest.main.run_svn(None,
1311 'ci', '-m', 'log message', wc_dir)
1313 # replace iota_path
1314 svntest.main.run_svn(None, 'rm', iota_path)
1315 svntest.main.file_append(iota_path, "some mod")
1316 svntest.main.run_svn(None, 'add', iota_path)
1318 # check that the replaced file has no properties
1319 expected_disk = svntest.main.greek_state.copy()
1320 expected_disk.tweak('iota', contents="some mod")
1321 actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
1322 svntest.tree.compare_trees("disk", actual_disk_tree,
1323 expected_disk.old_tree())
1325 # now add a new property to iota
1326 svntest.main.run_svn(None, 'propset', 'red', 'mojo', iota_path)
1327 svntest.main.run_svn(None, 'propset', 'groovy', 'baby', iota_path)
1329 # What we expect the disk tree to look like:
1330 expected_disk.tweak('iota', props={'red' : 'mojo', 'groovy' : 'baby'})
1331 actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
1332 svntest.tree.compare_trees("disk", actual_disk_tree,
1333 expected_disk.old_tree())
1335 #----------------------------------------------------------------------
1337 def depthy_wc_proplist(sbox):
1338 """test proplist at various depths on a wc"""
1339 # Bootstrap.
1340 sbox.build()
1341 wc_dir = sbox.wc_dir
1343 A_path = os.path.join(wc_dir, 'A')
1344 iota_path = os.path.join(wc_dir, 'iota')
1345 mu_path = os.path.join(A_path, 'mu')
1347 # Set up properties.
1348 svntest.main.run_svn(None, 'propset', 'p', 'prop1', wc_dir)
1349 svntest.main.run_svn(None, 'propset', 'p', 'prop2', iota_path)
1350 svntest.main.run_svn(None, 'propset', 'p', 'prop3', A_path)
1351 svntest.main.run_svn(None, 'propset', 'p', 'prop4', mu_path)
1353 # Commit.
1354 svntest.main.run_svn(None,
1355 'ci', '-m', 'log message', wc_dir)
1357 # Test depth-empty proplist.
1358 output, errput = svntest.main.run_svn(None, 'proplist', '--depth', 'empty',
1359 '-v', wc_dir)
1360 verify_output([ 'prop1', 'Properties on ' ],
1361 output, errput)
1363 # Test depth-files proplist.
1364 output, errput = svntest.main.run_svn(None, 'proplist', '--depth', 'files',
1365 '-v', wc_dir)
1366 verify_output([ 'prop1', 'prop2', 'Properties on ', 'Properties on ' ],
1367 output, errput)
1369 # Test depth-immediates proplist.
1370 output, errput = svntest.main.run_svn(None, 'proplist', '--depth',
1371 'immediates', '-v', wc_dir)
1372 verify_output([ 'prop1', 'prop2', 'prop3' ] + ['Properties on '] * 3,
1373 output, errput)
1375 # Test depth-infinity proplist.
1376 output, errput = svntest.main.run_svn(None, 'proplist', '--depth',
1377 'infinity', '-v', wc_dir)
1378 verify_output([ 'prop1', 'prop2', 'prop3', 'prop4' ] + ['Properties on '] * 4,
1379 output, errput)
1381 #----------------------------------------------------------------------
1383 def depthy_url_proplist(sbox):
1384 """test proplist at various depths on a url"""
1385 # Bootstrap.
1386 sbox.build()
1387 repo_url = sbox.repo_url
1388 wc_dir = sbox.wc_dir
1390 A_path = os.path.join(wc_dir, 'A')
1391 iota_path = os.path.join(wc_dir, 'iota')
1392 mu_path = os.path.join(A_path, 'mu')
1394 # Set up properties.
1395 svntest.main.run_svn(None, 'propset', 'p', 'prop1', wc_dir)
1396 svntest.main.run_svn(None, 'propset', 'p', 'prop2', iota_path)
1397 svntest.main.run_svn(None, 'propset', 'p', 'prop3', A_path)
1398 svntest.main.run_svn(None, 'propset', 'p', 'prop4', mu_path)
1400 # Test depth-empty proplist.
1401 output, errput = svntest.main.run_svn(None,
1402 'proplist', '--depth', 'empty',
1403 '-v', repo_url)
1404 verify_output([ 'prop1', 'Properties on ' ],
1405 output, errput)
1407 # Test depth-files proplist.
1408 output, errput = svntest.main.run_svn(None,
1409 'proplist', '--depth', 'files',
1410 '-v', repo_url)
1411 verify_output([ 'prop1', 'prop2', 'Properties on ', 'Properties on ' ],
1412 output, errput)
1414 # Test depth-immediates proplist.
1415 output, errput = svntest.main.run_svn(None,
1416 'proplist', '--depth',
1417 'immediates', '-v', repo_url)
1418 verify_output([ 'prop1', 'prop2', 'prop3' ] + ['Properties on '] * 3,
1419 output, errput)
1421 # Test depth-infinity proplist.
1422 output, errput = svntest.main.run_svn(None,
1423 'proplist', '--depth',
1424 'infinity', '-v', repo_url)
1425 verify_output([ 'prop1', 'prop2', 'prop3', 'prop4' ] + ['Properties on '] * 4,
1426 output, errput)
1428 #----------------------------------------------------------------------
1430 def invalid_propnames(sbox):
1431 """test prop* handle invalid property names"""
1432 # Bootstrap.
1433 sbox.build()
1434 repo_url = sbox.repo_url
1435 wc_dir = sbox.wc_dir
1436 cwd = os.getcwd()
1437 os.chdir(wc_dir)
1439 propname = chr(8)
1440 propval = 'foo'
1442 expected_stdout = ["property '%s' deleted from '.'.\n" % (propname,)]
1443 svntest.actions.run_and_verify_svn(None, expected_stdout, [],
1444 'propdel', propname)
1445 expected_stderr = (".*'%s' is not a valid Subversion"
1446 ' property name' % (propname,))
1447 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1448 'propedit', propname)
1449 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1450 'propget', propname)
1451 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1452 'propset', propname, propval)
1454 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1455 'commit', '--with-revprop',
1456 '='.join([propname, propval]))
1457 # Now swap them: --with-revprop should accept propname as a property
1458 # value; no concept of validity there.
1459 svntest.actions.run_and_verify_svn(None, [], [],
1460 'commit', '--with-revprop',
1461 '='.join([propval, propname]))
1463 os.chdir(cwd)
1465 def perms_on_symlink(sbox):
1466 "issue #2581: propset shouldn't touch symlink perms"
1467 sbox.build()
1468 # We can't just run commands on absolute paths in the usual way
1469 # (e.g., os.path.join(sbox.wc_dir, 'newdir')), because for some
1470 # reason, if the symlink points to newdir as an absolute path, the
1471 # bug doesn't reproduce. I have no idea why. Since it does have to
1472 # point to newdir, the only other choice is to have it point to it
1473 # in the same directory, so we have to run the test from inside the
1474 # working copy.
1475 saved_cwd = os.getcwd()
1476 os.chdir(sbox.wc_dir)
1477 try:
1478 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', 'newdir')
1479 os.symlink('newdir', 'symlink')
1480 svntest.actions.run_and_verify_svn(None, None, [], 'add', 'symlink')
1481 old_mode = os.stat('newdir')[stat.ST_MODE]
1482 svntest.actions.run_and_verify_svn(None, None, [], 'propdel',
1483 'svn:executable', 'symlink')
1484 new_mode = os.stat('newdir')[stat.ST_MODE]
1485 if not old_mode == new_mode:
1486 # Chmod newdir back, so the test suite can remove this working
1487 # copy when cleaning up later.
1488 os.chmod('newdir', stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
1489 raise svntest.Failure
1490 finally:
1491 os.chdir(saved_cwd)
1493 # Use a property with a custom namespace, ie 'ns:prop' or 'mycompany:prop'.
1494 def remove_custom_ns_props(sbox):
1495 "remove a property with a custom namespace"
1497 # Bootstrap
1498 sbox.build()
1499 wc_dir = sbox.wc_dir
1501 # Add a property to a file
1502 iota_path = os.path.join(wc_dir, 'iota')
1503 svntest.main.run_svn(None, 'propset', 'ns:cash-sound', 'cha-ching!', iota_path)
1505 # Commit the file
1506 svntest.main.run_svn(None,
1507 'ci', '-m', 'logmsg', iota_path)
1509 # Now, make a backup copy of the working copy
1510 wc_backup = sbox.add_wc_path('backup')
1511 svntest.actions.duplicate_dir(wc_dir, wc_backup)
1513 # Remove the property
1514 svntest.main.run_svn(None, 'propdel', 'ns:cash-sound', iota_path)
1516 # Create expected trees.
1517 expected_output = svntest.wc.State(wc_dir, {
1518 'iota' : Item(verb='Sending'),
1520 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1521 expected_status.tweak('iota', wc_rev=3, status=' ')
1523 # Commit the one file.
1524 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1525 expected_status,
1526 None, wc_dir)
1528 # Create expected trees for the update.
1529 expected_output = svntest.wc.State(wc_backup, {
1530 'iota' : Item(status=' U'),
1532 expected_disk = svntest.main.greek_state.copy()
1533 expected_status = svntest.actions.get_virginal_state(wc_backup, 3)
1534 expected_status.tweak('iota', wc_rev=3, status=' ')
1536 # Do the update and check the results in three ways... INCLUDING PROPS
1537 svntest.actions.run_and_verify_update(wc_backup,
1538 expected_output,
1539 expected_disk,
1540 expected_status,
1541 None, None, None, None, None, 1)
1543 def props_over_time(sbox):
1544 "property retrieval with peg and operative revs"
1546 # Bootstrap
1547 sbox.build()
1548 wc_dir = sbox.wc_dir
1550 # Convenience variables
1551 iota_path = os.path.join(wc_dir, 'iota')
1552 iota_url = sbox.repo_url + '/iota'
1554 # Add/tweak a property 'revision' with value revision-committed to a
1555 # file, commit, and then repeat this a few times.
1556 for rev in range(2, 4):
1557 svntest.main.run_svn(None, 'propset', 'revision', str(rev), iota_path)
1558 svntest.main.run_svn(None, 'ci', '-m', 'logmsg', iota_path)
1560 # Backdate to r2 so the defaults for URL- vs. WC-style queries are
1561 # different.
1562 svntest.main.run_svn(None, 'up', '-r2', wc_dir)
1564 # Now, test propget of the property across many combinations of
1565 # pegrevs, operative revs, and wc-path vs. url style input specs.
1566 # NOTE: We're using 0 in these loops to mean "unspecified".
1567 for path in iota_path, iota_url:
1568 for peg_rev in range(0, 4):
1569 for op_rev in range(0, 4):
1570 # Calculate the expected property value. If there is an
1571 # operative rev, we expect the output to match revisions
1572 # there. Else, we'll be looking at the peg-rev value. And if
1573 # neither are supplied, it depends on the path vs. URL
1574 # question.
1575 if op_rev > 1:
1576 expected = str(op_rev)
1577 elif op_rev == 1:
1578 expected = None
1579 else:
1580 if peg_rev > 1:
1581 expected = str(peg_rev)
1582 elif peg_rev == 1:
1583 expected = None
1584 else:
1585 if path == iota_url:
1586 expected = "3" # HEAD
1587 else:
1588 expected = "2" # BASE
1590 peg_path = path + (peg_rev != 0 and '@' + str(peg_rev) or "")
1592 ### Test 'svn propget'
1593 pget_expected = expected
1594 if pget_expected:
1595 pget_expected = [ pget_expected + "\n" ]
1596 if op_rev != 0:
1597 svntest.actions.run_and_verify_svn(None, pget_expected, [],
1598 'propget', 'revision', peg_path,
1599 '-r', str(op_rev))
1600 else:
1601 svntest.actions.run_and_verify_svn(None, pget_expected, [],
1602 'propget', 'revision', peg_path)
1604 ### Test 'svn proplist -v'
1605 if op_rev != 0 or peg_rev != 0: # a revision-ful query output URLs
1606 path = iota_url
1607 plist_expected = expected
1608 if plist_expected:
1609 plist_expected = [ "Properties on '" + path + "':\n",
1610 " revision : " + expected + "\n" ]
1612 if op_rev != 0:
1613 svntest.actions.run_and_verify_svn(None, plist_expected, [],
1614 'proplist', '-v', peg_path,
1615 '-r', str(op_rev))
1616 else:
1617 svntest.actions.run_and_verify_svn(None, plist_expected, [],
1618 'proplist', '-v', peg_path)
1620 ########################################################################
1621 # Run the tests
1623 # list all tests here, starting with None:
1624 test_list = [ None,
1625 make_local_props,
1626 commit_props,
1627 update_props,
1628 downdate_props,
1629 remove_props,
1630 update_conflict_props,
1631 commit_conflict_dirprops,
1632 commit_replacement_props,
1633 revert_replacement_props,
1634 inappropriate_props,
1635 copy_inherits_special_props,
1636 # If we learn how to write a pre-revprop-change hook for
1637 # non-Posix platforms, we won't have to skip here:
1638 # TODO(epg): Removed Skip as long as we have this XFail
1639 # because I couldn't get Skip and XFail to interact
1640 # properly (it kept showing the failure and then
1641 # printing PASS instead of XFAIL).
1642 #Skip(revprop_change, is_non_posix_and_non_windows_os),
1643 XFail(revprop_change, svntest.main.is_ra_type_dav),
1644 prop_value_conversions,
1645 binary_props,
1646 recursive_base_wc_ops,
1647 url_props_ops,
1648 removal_schedule_added_props,
1649 update_props_on_wc_root,
1650 props_on_replaced_file,
1651 depthy_wc_proplist,
1652 depthy_url_proplist,
1653 invalid_propnames,
1654 SkipUnless(perms_on_symlink, svntest.main.is_posix_os),
1655 remove_custom_ns_props,
1656 props_over_time,
1659 if __name__ == '__main__':
1660 svntest.main.run_tests(test_list)
1661 # NOTREACHED
1664 ### End of file.