Fix compiler warning due to missing function prototype.
[svn.git] / subversion / tests / cmdline / prop_tests.py
blobbaa079f3623ff7eb79693650d930f35ac9380978
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 "[oO]ut[- ]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_exit, actual_stdout, actual_stderr = svntest.main.run_svn(
762 None, 'pg', 'svn:mime-type', new_path2)
764 expected_stdout = [orig_mime_type + '\n']
765 if actual_stdout != expected_stdout:
766 print "svn pg svn:mime-type output does not match expected."
767 print "Expected standard output: ", expected_stdout, "\n"
768 print "Actual standard output: ", actual_stdout, "\n"
769 raise svntest.verify.SVNUnexpectedOutput
771 # Check the svn:executable value.
772 # The value of the svn:executable property is now always forced to '*'
773 if os.name == 'posix':
774 actual_exit, actual_stdout, actual_stderr = svntest.main.run_svn(
775 None, 'pg', 'svn:executable', new_path2)
777 expected_stdout = ['*\n']
778 if actual_stdout != expected_stdout:
779 print "svn pg svn:executable output does not match expected."
780 print "Expected standard output: ", expected_stdout, "\n"
781 print "Actual standard output: ", actual_stdout, "\n"
782 raise svntest.verify.SVNUnexpectedOutput
784 #----------------------------------------------------------------------
786 def revprop_change(sbox):
787 "set, get, and delete a revprop change"
789 sbox.build()
791 # First test the error when no revprop-change hook exists.
792 svntest.actions.run_and_verify_svn(None, None, '.*pre-revprop-change',
793 'propset', '--revprop', '-r', '0',
794 'cash-sound', 'cha-ching!', sbox.wc_dir)
796 # Now test error output from revprop-change hook.
797 svntest.actions.disable_revprop_changes(sbox.repo_dir)
798 svntest.actions.run_and_verify_svn(None, None, '.*pre-revprop-change.* 0 jrandom cash-sound A',
799 'propset', '--revprop', '-r', '0',
800 'cash-sound', 'cha-ching!', sbox.wc_dir)
802 # Create the revprop-change hook for this test
803 svntest.actions.enable_revprop_changes(sbox.repo_dir)
805 svntest.actions.run_and_verify_svn(None, None, [],
806 'propset', '--revprop', '-r', '0',
807 'cash-sound', 'cha-ching!', sbox.wc_dir)
809 svntest.actions.run_and_verify_svn(None, None, [],
810 'propget', '--revprop', '-r', '0',
811 'cash-sound', sbox.wc_dir)
813 # Now test that blocking the revprop delete.
814 svntest.actions.disable_revprop_changes(sbox.repo_dir)
815 svntest.actions.run_and_verify_svn(None, None, '.*pre-revprop-change.* 0 jrandom cash-sound D',
816 'propdel', '--revprop', '-r', '0',
817 'cash-sound', sbox.wc_dir)
819 # Now test actually deleting the revprop.
820 svntest.actions.enable_revprop_changes(sbox.repo_dir)
821 svntest.actions.run_and_verify_svn(None, None, [],
822 'propdel', '--revprop', '-r', '0',
823 'cash-sound', sbox.wc_dir)
825 actual_exit, actual_stdout, actual_stderr = svntest.main.run_svn(
826 None, 'pg', '--revprop', '-r', '0', 'cash-sound', sbox.wc_dir)
828 # The property should have been deleted.
829 regex = 'cha-ching'
830 for line in actual_stdout:
831 if re.match(regex, line):
832 raise svntest.Failure
835 #----------------------------------------------------------------------
837 def prop_value_conversions(sbox):
838 "some svn: properties should be converted"
840 # Bootstrap
841 sbox.build()
842 wc_dir = sbox.wc_dir
844 A_path = os.path.join(wc_dir, 'A')
845 B_path = os.path.join(wc_dir, 'A', 'B')
846 iota_path = os.path.join(wc_dir, 'iota')
847 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
848 mu_path = os.path.join(wc_dir, 'A', 'mu')
850 # We'll use a file to set the prop values, so that weird characters
851 # in the props don't confuse the shell.
852 propval_path = os.path.join(wc_dir, 'propval.tmp')
853 propval_file = open(propval_path, 'wb')
855 def set_prop(name, value, path, valf=propval_file, valp=propval_path,
856 expected_error=None):
857 valf.seek(0)
858 valf.truncate(0)
859 valf.write(value)
860 valf.flush()
861 svntest.main.run_svn(expected_error, 'propset', '-F', valp, name, path)
863 # Leading and trailing whitespace should be stripped
864 set_prop('svn:mime-type', ' text/html\n\n', iota_path)
865 set_prop('svn:mime-type', 'text/html', mu_path)
867 # Leading and trailing whitespace should be stripped
868 set_prop('svn:eol-style', '\nnative\n', iota_path)
869 set_prop('svn:eol-style', 'native', mu_path)
871 # A trailing newline should be added
872 set_prop('svn:ignore', '*.o\nfoo.c', A_path)
873 set_prop('svn:ignore', '*.o\nfoo.c\n', B_path)
875 # A trailing newline should be added
876 set_prop('svn:externals', 'foo http://foo.com/repos', A_path)
877 set_prop('svn:externals', 'foo http://foo.com/repos\n', B_path)
879 # Leading and trailing whitespace should be stripped, but not internal
880 # whitespace
881 set_prop('svn:keywords', ' Rev Date \n', iota_path)
882 set_prop('svn:keywords', 'Rev Date', mu_path)
884 # svn:executable value should be forced to a '*'
885 set_prop('svn:executable', 'foo', iota_path)
886 set_prop('svn:executable', '*', lambda_path)
887 for pval in (' ', '', 'no', 'off', 'false'):
888 set_prop('svn:executable', pval, mu_path, propval_file, propval_path,
889 ["svn: warning: To turn off the svn:executable property, "
890 "use 'svn propdel';\n",
891 "setting the property to '" + pval +
892 "' will not turn it off.\n"])
894 # Anything else should be untouched
895 set_prop('svn:some-prop', 'bar', lambda_path)
896 set_prop('svn:some-prop', ' bar baz', mu_path)
897 set_prop('svn:some-prop', 'bar\n', iota_path)
898 set_prop('some-prop', 'bar', lambda_path)
899 set_prop('some-prop', ' bar baz', mu_path)
900 set_prop('some-prop', 'bar\n', iota_path)
902 # Close and remove the prop value file
903 propval_file.close()
904 os.unlink(propval_path)
906 # NOTE: When writing out multi-line prop values in svn:* props, the
907 # client converts to local encoding and local eoln style.
908 # Therefore, the expected output must contain the right kind of eoln
909 # strings. That's why we use os.linesep in the tests below, not just
910 # plain '\n'. The _last_ \n is also from the client, but it's not
911 # part of the prop value and it doesn't get converted in the pipe.
913 # Check svn:mime-type
914 svntest.actions.check_prop('svn:mime-type', iota_path, ['text/html'])
915 svntest.actions.check_prop('svn:mime-type', mu_path, ['text/html'])
917 # Check svn:eol-style
918 svntest.actions.check_prop('svn:eol-style', iota_path, ['native'])
919 svntest.actions.check_prop('svn:eol-style', mu_path, ['native'])
921 # Check svn:ignore
922 svntest.actions.check_prop('svn:ignore', A_path,
923 ['*.o'+os.linesep, 'foo.c'+os.linesep])
924 svntest.actions.check_prop('svn:ignore', B_path,
925 ['*.o'+os.linesep, 'foo.c'+os.linesep])
927 # Check svn:externals
928 svntest.actions.check_prop('svn:externals', A_path,
929 ['foo http://foo.com/repos'+os.linesep])
930 svntest.actions.check_prop('svn:externals', B_path,
931 ['foo http://foo.com/repos'+os.linesep])
933 # Check svn:keywords
934 svntest.actions.check_prop('svn:keywords', iota_path, ['Rev Date'])
935 svntest.actions.check_prop('svn:keywords', mu_path, ['Rev Date'])
937 # Check svn:executable
938 svntest.actions.check_prop('svn:executable', iota_path, ['*'])
939 svntest.actions.check_prop('svn:executable', lambda_path, ['*'])
940 svntest.actions.check_prop('svn:executable', mu_path, ['*'])
942 # Check other props
943 svntest.actions.check_prop('svn:some-prop', lambda_path, ['bar'])
944 svntest.actions.check_prop('svn:some-prop', mu_path, [' bar baz'])
945 svntest.actions.check_prop('svn:some-prop', iota_path, ['bar'+os.linesep])
946 svntest.actions.check_prop('some-prop', lambda_path, ['bar'])
947 svntest.actions.check_prop('some-prop', mu_path,[' bar baz'])
948 svntest.actions.check_prop('some-prop', iota_path, ['bar\n'])
951 #----------------------------------------------------------------------
953 def binary_props(sbox):
954 "test binary property support"
956 # Bootstrap
957 sbox.build()
958 wc_dir = sbox.wc_dir
960 # Make a backup copy of the working copy
961 wc_backup = sbox.add_wc_path('backup')
962 svntest.actions.duplicate_dir(wc_dir, wc_backup)
964 # Some path convenience vars.
965 A_path = os.path.join(wc_dir, 'A')
966 B_path = os.path.join(wc_dir, 'A', 'B')
967 iota_path = os.path.join(wc_dir, 'iota')
968 lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
969 mu_path = os.path.join(wc_dir, 'A', 'mu')
970 A_path_bak = os.path.join(wc_backup, 'A')
971 B_path_bak = os.path.join(wc_backup, 'A', 'B')
972 iota_path_bak = os.path.join(wc_backup, 'iota')
973 lambda_path_bak = os.path.join(wc_backup, 'A', 'B', 'lambda')
974 mu_path_bak = os.path.join(wc_backup, 'A', 'mu')
976 # Property value convenience vars.
977 prop_zb = "This property has a zer\000 byte."
978 prop_ff = "This property has a form\014feed."
979 prop_xml = "This property has an <xml> tag."
980 prop_binx = "This property has an <xml> tag and a zer\000 byte."
982 # Set some binary properties.
983 propval_path = os.path.join(wc_dir, 'propval.tmp')
984 propval_file = open(propval_path, 'wb')
986 def set_prop(name, value, path, valf=propval_file, valp=propval_path):
987 valf.seek(0)
988 valf.truncate(0)
989 valf.write(value)
990 valf.flush()
991 svntest.main.run_svn(None, 'propset', '-F', valp, name, path)
993 set_prop('prop_zb', prop_zb, B_path)
994 set_prop('prop_ff', prop_ff, iota_path)
995 set_prop('prop_xml', prop_xml, lambda_path)
996 set_prop('prop_binx', prop_binx, mu_path)
997 set_prop('prop_binx', prop_binx, A_path)
999 # Create expected output and status trees.
1000 expected_output = svntest.wc.State(wc_dir, {
1001 'A' : Item(verb='Sending'),
1002 'A/B' : Item(verb='Sending'),
1003 'iota' : Item(verb='Sending'),
1004 'A/B/lambda' : Item(verb='Sending'),
1005 'A/mu' : Item(verb='Sending'),
1007 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1008 expected_status.tweak('A', 'A/B', 'iota', 'A/B/lambda', 'A/mu',
1009 wc_rev=2, status=' ')
1011 # Commit the propsets.
1012 svntest.actions.run_and_verify_commit(wc_dir,
1013 expected_output,
1014 expected_status,
1015 None,
1016 wc_dir)
1018 # Create expected output, disk, and status trees for an update of
1019 # the wc_backup.
1020 expected_output = svntest.wc.State(wc_backup, {
1021 'A' : Item(status=' U'),
1022 'A/B' : Item(status=' U'),
1023 'iota' : Item(status=' U'),
1024 'A/B/lambda' : Item(status=' U'),
1025 'A/mu' : Item(status=' U'),
1027 expected_disk = svntest.main.greek_state.copy()
1028 expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
1030 # Do the update and check the results.
1031 svntest.actions.run_and_verify_update(wc_backup,
1032 expected_output,
1033 expected_disk,
1034 expected_status,
1035 None, None, None, None, None, 0)
1037 # Now, check those properties.
1038 svntest.actions.check_prop('prop_zb', B_path_bak, [prop_zb])
1039 svntest.actions.check_prop('prop_ff', iota_path_bak, [prop_ff])
1040 svntest.actions.check_prop('prop_xml', lambda_path_bak, [prop_xml])
1041 svntest.actions.check_prop('prop_binx', mu_path_bak, [prop_binx])
1042 svntest.actions.check_prop('prop_binx', A_path_bak, [prop_binx])
1044 #----------------------------------------------------------------------
1046 # Ensure that each line of output contains the corresponding string of
1047 # expected_out, and that errput is empty.
1048 def verify_output(expected_out, output, errput):
1049 if errput != []:
1050 print 'Error: stderr:'
1051 print errput
1052 raise svntest.Failure
1053 output.sort()
1054 ln = 0
1055 for line in output:
1056 if ((line.find(expected_out[ln]) == -1) or
1057 (line != '' and expected_out[ln] == '')):
1058 print 'Error: expected keywords: ', expected_out
1059 print ' actual full output:', output
1060 raise svntest.Failure
1061 ln = ln + 1
1063 def recursive_base_wc_ops(sbox):
1064 "recursive property operations in BASE and WC"
1066 # Bootstrap
1067 sbox.build()
1068 wc_dir = sbox.wc_dir
1070 # Files with which to test, in alphabetical order
1071 f_add = os.path.join('A', 'added')
1072 f_del = os.path.join('A', 'mu')
1073 f_keep= os.path.join('iota')
1074 fp_add = os.path.join(wc_dir, f_add)
1075 fp_del = os.path.join(wc_dir, f_del)
1076 fp_keep= os.path.join(wc_dir, f_keep)
1078 # Set up properties
1079 svntest.main.run_svn(None, 'propset', 'p', 'old-del', fp_del)
1080 svntest.main.run_svn(None, 'propset', 'p', 'old-keep',fp_keep)
1081 svntest.main.run_svn(None,
1082 'commit', '-m', '', wc_dir)
1083 svntest.main.file_append(fp_add, 'blah')
1084 svntest.main.run_svn(None, 'add', fp_add)
1085 svntest.main.run_svn(None, 'propset', 'p', 'new-add', fp_add)
1086 svntest.main.run_svn(None, 'propset', 'p', 'new-del', fp_del)
1087 svntest.main.run_svn(None, 'propset', 'p', 'new-keep',fp_keep)
1088 svntest.main.run_svn(None, 'del', '--force', fp_del)
1090 # Test recursive proplist
1091 exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '-R',
1092 '-v', wc_dir, '-rBASE')
1093 verify_output([ 'old-del', 'old-keep', 'Properties on ', 'Properties on ' ],
1094 output, errput)
1095 svntest.verify.verify_exit_code(None, exit_code, 0)
1097 exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '-R',
1098 '-v', wc_dir)
1099 verify_output([ 'new-add', 'new-keep', 'Properties on ', 'Properties on ' ],
1100 output, errput)
1101 svntest.verify.verify_exit_code(None, exit_code, 0)
1103 # Test recursive propget
1104 exit_code, output, errput = svntest.main.run_svn(None, 'propget', '-R',
1105 'p', wc_dir, '-rBASE')
1106 verify_output([ 'old-del', 'old-keep' ], output, errput)
1107 svntest.verify.verify_exit_code(None, exit_code, 0)
1109 exit_code, output, errput = svntest.main.run_svn(None, 'propget', '-R',
1110 'p', wc_dir)
1111 verify_output([ 'new-add', 'new-keep' ], output, errput)
1112 svntest.verify.verify_exit_code(None, exit_code, 0)
1114 # Test recursive propset (issue 1794)
1115 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1116 expected_status.tweak('A/mu', status='D ', wc_rev=2)
1117 expected_status.tweak('iota', status=' M', wc_rev=2)
1118 expected_status.add({
1119 'A/added' : Item(status='A ', wc_rev=0),
1121 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1123 svntest.actions.run_and_verify_svn(None, None, [],
1124 'propset', '-R', 'svn:keywords', 'Date',
1125 os.path.join(wc_dir, 'A', 'B'))
1126 expected_status.tweak('A/B/lambda', 'A/B/E/alpha', 'A/B/E/beta', status=' M')
1127 svntest.actions.run_and_verify_status(wc_dir, expected_status)
1129 #----------------------------------------------------------------------
1131 def url_props_ops(sbox):
1132 "property operations on an URL"
1134 # Bootstrap
1135 sbox.build()
1136 wc_dir = sbox.wc_dir
1138 prop1 = 'prop1'
1139 propval1 = 'propval1 is foo'
1140 prop2 = 'prop2'
1141 propval2 = 'propval2'
1143 iota_path = os.path.join(sbox.wc_dir, 'iota')
1144 iota_url = sbox.repo_url + '/iota'
1145 A_path = os.path.join(sbox.wc_dir, 'A')
1146 A_url = sbox.repo_url + '/A'
1148 # Add a couple of properties
1149 svntest.main.run_svn(None, 'propset', prop1, propval1, iota_path)
1150 svntest.main.run_svn(None, 'propset', prop1, propval1, A_path)
1152 # Commit
1153 svntest.main.run_svn(None,
1154 'ci', '-m', 'logmsg', sbox.wc_dir)
1156 # Add a few more properties
1157 svntest.main.run_svn(None, 'propset', prop2, propval2, iota_path)
1158 svntest.main.run_svn(None, 'propset', prop2, propval2, A_path)
1160 # Commit again
1161 svntest.main.run_svn(None,
1162 'ci', '-m', 'logmsg', sbox.wc_dir)
1164 # Test propget
1165 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1166 'propget', prop1, iota_url)
1167 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1168 'propget', prop1, A_url)
1170 # Test normal proplist
1171 exit_code, output, errput = svntest.main.run_svn(None,
1172 'proplist', iota_url)
1173 verify_output([ prop1, prop2, 'Properties on ' ],
1174 output, errput)
1175 svntest.verify.verify_exit_code(None, exit_code, 0)
1177 exit_code, output, errput = svntest.main.run_svn(None,
1178 'proplist', A_url)
1179 verify_output([ prop1, prop2, 'Properties on ' ],
1180 output, errput)
1181 svntest.verify.verify_exit_code(None, exit_code, 0)
1183 # Test verbose proplist
1184 exit_code, output, errput = svntest.main.run_svn(None,
1185 'proplist', '-v', iota_url)
1186 verify_output([ prop1 + ' : ' + propval1, prop2 + ' : ' + propval2,
1187 'Properties on ' ], output, errput)
1188 svntest.verify.verify_exit_code(None, exit_code, 0)
1190 exit_code, output, errput = svntest.main.run_svn(None,
1191 'proplist', '-v', A_url)
1192 verify_output([ prop1 + ' : ' + propval1, prop2 + ' : ' + propval2,
1193 'Properties on ' ], output, errput)
1194 svntest.verify.verify_exit_code(None, exit_code, 0)
1196 # Test propedit
1197 svntest.main.use_editor('foo_to_bar')
1198 propval1 = propval1.replace('foo', 'bar')
1199 svntest.main.run_svn(None,
1200 'propedit', prop1, '-m', 'editlog', iota_url)
1201 svntest.main.run_svn(None,
1202 'propedit', prop1, '-m', 'editlog', A_url)
1203 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1204 'propget', prop1, iota_url)
1205 svntest.actions.run_and_verify_svn(None, [ propval1 + '\n' ], [],
1206 'propget', prop1, A_url)
1208 # Edit without actually changing the property
1209 svntest.main.use_editor('identity')
1210 svntest.actions.run_and_verify_svn(None,
1211 "No changes to property '%s' on '.*'"
1212 % prop1,
1214 'propedit', prop1, '-m', 'nocommit',
1215 iota_url)
1219 #----------------------------------------------------------------------
1220 def removal_schedule_added_props(sbox):
1221 "removal of schedule added file with properties"
1223 sbox.build()
1225 wc_dir = sbox.wc_dir
1226 newfile_path = os.path.join(wc_dir, 'newfile')
1227 file_add_output = ["A " + newfile_path + "\n"]
1228 propset_output = ["property 'newprop' set on '" + newfile_path + "'\n"]
1229 file_rm_output = ["D " + newfile_path + "\n"]
1230 propls_output = [
1231 "Properties on '" + newfile_path + "':\n",
1232 " newprop : newvalue\n",
1235 # create new fs file
1236 open(newfile_path, 'w').close()
1237 # Add it and set a property
1238 svntest.actions.run_and_verify_svn(None, file_add_output, [], 'add', newfile_path)
1239 svntest.actions.run_and_verify_svn(None, propset_output, [], 'propset',
1240 'newprop', 'newvalue', newfile_path)
1241 svntest.actions.run_and_verify_svn(None, propls_output, [],
1242 'proplist', '-v', newfile_path)
1243 # remove the file
1244 svntest.actions.run_and_verify_svn(None, file_rm_output, [],
1245 'rm', '--force', newfile_path)
1246 # recreate the file and add it again
1247 open(newfile_path, 'w').close()
1248 svntest.actions.run_and_verify_svn(None, file_add_output, [], 'add', newfile_path)
1250 # Now there should be NO properties leftover...
1251 svntest.actions.run_and_verify_svn(None, [], [],
1252 'proplist', '-v', newfile_path)
1254 #----------------------------------------------------------------------
1256 def update_props_on_wc_root(sbox):
1257 "receive properties on the wc root via update"
1259 # Bootstrap
1260 sbox.build()
1261 wc_dir = sbox.wc_dir
1263 # Make a backup copy of the working copy
1264 wc_backup = sbox.add_wc_path('backup')
1265 svntest.actions.duplicate_dir(wc_dir, wc_backup)
1267 # Add a property to the root folder
1268 svntest.main.run_svn(None, 'propset', 'red', 'rojo', wc_dir)
1270 # Create expected output tree.
1271 expected_output = svntest.wc.State(wc_dir, {
1272 '' : Item(verb='Sending')
1275 # Created expected status tree.
1276 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1277 expected_status.tweak('', wc_rev=2, status=' ')
1279 # Commit the working copy
1280 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1281 expected_status,
1282 None, wc_dir)
1284 # Create expected output tree for an update of the wc_backup.
1285 expected_output = svntest.wc.State(wc_backup, {
1286 '' : Item(status=' U'),
1288 # Create expected disk tree for the update.
1289 expected_disk = svntest.main.greek_state.copy()
1290 expected_disk.add({
1291 '' : Item(props = {'red' : 'rojo'}),
1293 # Create expected status tree for the update.
1294 expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
1295 expected_status.tweak('', status=' ')
1297 # Do the update and check the results in three ways... INCLUDING PROPS
1298 svntest.actions.run_and_verify_update(wc_backup,
1299 expected_output,
1300 expected_disk,
1301 expected_status,
1302 None, None, None, None, None, 1)
1304 # test for issue 2743
1305 def props_on_replaced_file(sbox):
1306 """test properties on replaced files"""
1308 sbox.build()
1309 wc_dir = sbox.wc_dir
1311 # Add some properties to iota
1312 iota_path = os.path.join(wc_dir, "iota")
1313 svntest.main.run_svn(None, 'propset', 'red', 'rojo', iota_path)
1314 svntest.main.run_svn(None, 'propset', 'blue', 'lagoon', iota_path)
1315 svntest.main.run_svn(None,
1316 'ci', '-m', 'log message', wc_dir)
1318 # replace iota_path
1319 svntest.main.run_svn(None, 'rm', iota_path)
1320 svntest.main.file_append(iota_path, "some mod")
1321 svntest.main.run_svn(None, 'add', iota_path)
1323 # check that the replaced file has no properties
1324 expected_disk = svntest.main.greek_state.copy()
1325 expected_disk.tweak('iota', contents="some mod")
1326 actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
1327 svntest.tree.compare_trees("disk", actual_disk_tree,
1328 expected_disk.old_tree())
1330 # now add a new property to iota
1331 svntest.main.run_svn(None, 'propset', 'red', 'mojo', iota_path)
1332 svntest.main.run_svn(None, 'propset', 'groovy', 'baby', iota_path)
1334 # What we expect the disk tree to look like:
1335 expected_disk.tweak('iota', props={'red' : 'mojo', 'groovy' : 'baby'})
1336 actual_disk_tree = svntest.tree.build_tree_from_wc(wc_dir, 1)
1337 svntest.tree.compare_trees("disk", actual_disk_tree,
1338 expected_disk.old_tree())
1340 #----------------------------------------------------------------------
1342 def depthy_wc_proplist(sbox):
1343 """test proplist at various depths on a wc"""
1344 # Bootstrap.
1345 sbox.build()
1346 wc_dir = sbox.wc_dir
1348 A_path = os.path.join(wc_dir, 'A')
1349 iota_path = os.path.join(wc_dir, 'iota')
1350 mu_path = os.path.join(A_path, 'mu')
1352 # Set up properties.
1353 svntest.main.run_svn(None, 'propset', 'p', 'prop1', wc_dir)
1354 svntest.main.run_svn(None, 'propset', 'p', 'prop2', iota_path)
1355 svntest.main.run_svn(None, 'propset', 'p', 'prop3', A_path)
1356 svntest.main.run_svn(None, 'propset', 'p', 'prop4', mu_path)
1358 # Commit.
1359 svntest.main.run_svn(None,
1360 'ci', '-m', 'log message', wc_dir)
1362 # Test depth-empty proplist.
1363 exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1364 '--depth', 'empty',
1365 '-v', wc_dir)
1366 verify_output([ 'prop1', 'Properties on ' ],
1367 output, errput)
1368 svntest.verify.verify_exit_code(None, exit_code, 0)
1370 # Test depth-files proplist.
1371 exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1372 '--depth', 'files',
1373 '-v', wc_dir)
1374 verify_output([ 'prop1', 'prop2', 'Properties on ', 'Properties on ' ],
1375 output, errput)
1376 svntest.verify.verify_exit_code(None, exit_code, 0)
1378 # Test depth-immediates proplist.
1379 exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '--depth',
1380 'immediates', '-v', wc_dir)
1381 verify_output([ 'prop1', 'prop2', 'prop3' ] + ['Properties on '] * 3,
1382 output, errput)
1383 svntest.verify.verify_exit_code(None, exit_code, 0)
1385 # Test depth-infinity proplist.
1386 exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '--depth',
1387 'infinity', '-v', wc_dir)
1388 verify_output([ 'prop1', 'prop2', 'prop3', 'prop4' ] + ['Properties on '] * 4,
1389 output, errput)
1390 svntest.verify.verify_exit_code(None, exit_code, 0)
1392 #----------------------------------------------------------------------
1394 def depthy_url_proplist(sbox):
1395 """test proplist at various depths on a url"""
1396 # Bootstrap.
1397 sbox.build()
1398 repo_url = sbox.repo_url
1399 wc_dir = sbox.wc_dir
1401 A_path = os.path.join(wc_dir, 'A')
1402 iota_path = os.path.join(wc_dir, 'iota')
1403 mu_path = os.path.join(A_path, 'mu')
1405 # Set up properties.
1406 svntest.main.run_svn(None, 'propset', 'p', 'prop1', wc_dir)
1407 svntest.main.run_svn(None, 'propset', 'p', 'prop2', iota_path)
1408 svntest.main.run_svn(None, 'propset', 'p', 'prop3', A_path)
1409 svntest.main.run_svn(None, 'propset', 'p', 'prop4', mu_path)
1411 # Test depth-empty proplist.
1412 exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1413 '--depth', 'empty',
1414 '-v', repo_url)
1415 verify_output([ 'prop1', 'Properties on ' ],
1416 output, errput)
1417 svntest.verify.verify_exit_code(None, exit_code, 0)
1419 # Test depth-files proplist.
1420 exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1421 '--depth', 'files',
1422 '-v', repo_url)
1423 verify_output([ 'prop1', 'prop2', 'Properties on ', 'Properties on ' ],
1424 output, errput)
1425 svntest.verify.verify_exit_code(None, exit_code, 0)
1427 # Test depth-immediates proplist.
1428 exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1429 '--depth', 'immediates',
1430 '-v', repo_url)
1432 verify_output([ 'prop1', 'prop2', 'prop3' ] + ['Properties on '] * 3,
1433 output, errput)
1434 svntest.verify.verify_exit_code(None, exit_code, 0)
1436 # Test depth-infinity proplist.
1437 exit_code, output, errput = svntest.main.run_svn(None,
1438 'proplist', '--depth',
1439 'infinity', '-v', repo_url)
1440 verify_output([ 'prop1', 'prop2', 'prop3', 'prop4' ] + ['Properties on '] * 4,
1441 output, errput)
1442 svntest.verify.verify_exit_code(None, exit_code, 0)
1444 #----------------------------------------------------------------------
1446 def invalid_propnames(sbox):
1447 """test prop* handle invalid property names"""
1448 # Bootstrap.
1449 sbox.build()
1450 repo_url = sbox.repo_url
1451 wc_dir = sbox.wc_dir
1452 cwd = os.getcwd()
1453 os.chdir(wc_dir)
1455 propname = chr(8)
1456 propval = 'foo'
1458 expected_stdout = ["property '%s' deleted from '.'.\n" % (propname,)]
1459 svntest.actions.run_and_verify_svn(None, expected_stdout, [],
1460 'propdel', propname)
1461 expected_stderr = (".*'%s' is not a valid Subversion"
1462 ' property name' % (propname,))
1463 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1464 'propedit', propname)
1465 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1466 'propget', propname)
1467 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1468 'propset', propname, propval)
1470 svntest.actions.run_and_verify_svn(None, None, expected_stderr,
1471 'commit', '--with-revprop',
1472 '='.join([propname, propval]))
1473 # Now swap them: --with-revprop should accept propname as a property
1474 # value; no concept of validity there.
1475 svntest.actions.run_and_verify_svn(None, [], [],
1476 'commit', '--with-revprop',
1477 '='.join([propval, propname]))
1479 os.chdir(cwd)
1481 def perms_on_symlink(sbox):
1482 "issue #2581: propset shouldn't touch symlink perms"
1483 sbox.build()
1484 # We can't just run commands on absolute paths in the usual way
1485 # (e.g., os.path.join(sbox.wc_dir, 'newdir')), because for some
1486 # reason, if the symlink points to newdir as an absolute path, the
1487 # bug doesn't reproduce. I have no idea why. Since it does have to
1488 # point to newdir, the only other choice is to have it point to it
1489 # in the same directory, so we have to run the test from inside the
1490 # working copy.
1491 saved_cwd = os.getcwd()
1492 os.chdir(sbox.wc_dir)
1493 try:
1494 svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', 'newdir')
1495 os.symlink('newdir', 'symlink')
1496 svntest.actions.run_and_verify_svn(None, None, [], 'add', 'symlink')
1497 old_mode = os.stat('newdir')[stat.ST_MODE]
1498 svntest.actions.run_and_verify_svn(None, None, [], 'propdel',
1499 'svn:executable', 'symlink')
1500 new_mode = os.stat('newdir')[stat.ST_MODE]
1501 if not old_mode == new_mode:
1502 # Chmod newdir back, so the test suite can remove this working
1503 # copy when cleaning up later.
1504 os.chmod('newdir', stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
1505 raise svntest.Failure
1506 finally:
1507 os.chdir(saved_cwd)
1509 # Use a property with a custom namespace, ie 'ns:prop' or 'mycompany:prop'.
1510 def remove_custom_ns_props(sbox):
1511 "remove a property with a custom namespace"
1513 # Bootstrap
1514 sbox.build()
1515 wc_dir = sbox.wc_dir
1517 # Add a property to a file
1518 iota_path = os.path.join(wc_dir, 'iota')
1519 svntest.main.run_svn(None, 'propset', 'ns:cash-sound', 'cha-ching!', iota_path)
1521 # Commit the file
1522 svntest.main.run_svn(None,
1523 'ci', '-m', 'logmsg', iota_path)
1525 # Now, make a backup copy of the working copy
1526 wc_backup = sbox.add_wc_path('backup')
1527 svntest.actions.duplicate_dir(wc_dir, wc_backup)
1529 # Remove the property
1530 svntest.main.run_svn(None, 'propdel', 'ns:cash-sound', iota_path)
1532 # Create expected trees.
1533 expected_output = svntest.wc.State(wc_dir, {
1534 'iota' : Item(verb='Sending'),
1536 expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1537 expected_status.tweak('iota', wc_rev=3, status=' ')
1539 # Commit the one file.
1540 svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1541 expected_status,
1542 None, wc_dir)
1544 # Create expected trees for the update.
1545 expected_output = svntest.wc.State(wc_backup, {
1546 'iota' : Item(status=' U'),
1548 expected_disk = svntest.main.greek_state.copy()
1549 expected_status = svntest.actions.get_virginal_state(wc_backup, 3)
1550 expected_status.tweak('iota', wc_rev=3, status=' ')
1552 # Do the update and check the results in three ways... INCLUDING PROPS
1553 svntest.actions.run_and_verify_update(wc_backup,
1554 expected_output,
1555 expected_disk,
1556 expected_status,
1557 None, None, None, None, None, 1)
1559 def props_over_time(sbox):
1560 "property retrieval with peg and operative revs"
1562 # Bootstrap
1563 sbox.build()
1564 wc_dir = sbox.wc_dir
1566 # Convenience variables
1567 iota_path = os.path.join(wc_dir, 'iota')
1568 iota_url = sbox.repo_url + '/iota'
1570 # Add/tweak a property 'revision' with value revision-committed to a
1571 # file, commit, and then repeat this a few times.
1572 for rev in range(2, 4):
1573 svntest.main.run_svn(None, 'propset', 'revision', str(rev), iota_path)
1574 svntest.main.run_svn(None, 'ci', '-m', 'logmsg', iota_path)
1576 # Backdate to r2 so the defaults for URL- vs. WC-style queries are
1577 # different.
1578 svntest.main.run_svn(None, 'up', '-r2', wc_dir)
1580 # Now, test propget of the property across many combinations of
1581 # pegrevs, operative revs, and wc-path vs. url style input specs.
1582 # NOTE: We're using 0 in these loops to mean "unspecified".
1583 for path in iota_path, iota_url:
1584 for peg_rev in range(0, 4):
1585 for op_rev in range(0, 4):
1586 # Calculate the expected property value. If there is an
1587 # operative rev, we expect the output to match revisions
1588 # there. Else, we'll be looking at the peg-rev value. And if
1589 # neither are supplied, it depends on the path vs. URL
1590 # question.
1591 if op_rev > 1:
1592 expected = str(op_rev)
1593 elif op_rev == 1:
1594 expected = None
1595 else:
1596 if peg_rev > 1:
1597 expected = str(peg_rev)
1598 elif peg_rev == 1:
1599 expected = None
1600 else:
1601 if path == iota_url:
1602 expected = "3" # HEAD
1603 else:
1604 expected = "2" # BASE
1606 peg_path = path + (peg_rev != 0 and '@' + str(peg_rev) or "")
1608 ### Test 'svn propget'
1609 pget_expected = expected
1610 if pget_expected:
1611 pget_expected = [ pget_expected + "\n" ]
1612 if op_rev != 0:
1613 svntest.actions.run_and_verify_svn(None, pget_expected, [],
1614 'propget', 'revision', peg_path,
1615 '-r', str(op_rev))
1616 else:
1617 svntest.actions.run_and_verify_svn(None, pget_expected, [],
1618 'propget', 'revision', peg_path)
1620 ### Test 'svn proplist -v'
1621 if op_rev != 0 or peg_rev != 0: # a revision-ful query output URLs
1622 path = iota_url
1623 plist_expected = expected
1624 if plist_expected:
1625 plist_expected = [ "Properties on '" + path + "':\n",
1626 " revision : " + expected + "\n" ]
1628 if op_rev != 0:
1629 svntest.actions.run_and_verify_svn(None, plist_expected, [],
1630 'proplist', '-v', peg_path,
1631 '-r', str(op_rev))
1632 else:
1633 svntest.actions.run_and_verify_svn(None, plist_expected, [],
1634 'proplist', '-v', peg_path)
1636 ########################################################################
1637 # Run the tests
1639 # list all tests here, starting with None:
1640 test_list = [ None,
1641 make_local_props,
1642 commit_props,
1643 update_props,
1644 downdate_props,
1645 remove_props,
1646 update_conflict_props,
1647 commit_conflict_dirprops,
1648 commit_replacement_props,
1649 revert_replacement_props,
1650 inappropriate_props,
1651 copy_inherits_special_props,
1652 # If we learn how to write a pre-revprop-change hook for
1653 # non-Posix platforms, we won't have to skip here:
1654 # TODO(epg): Removed Skip as long as we have this XFail
1655 # because I couldn't get Skip and XFail to interact
1656 # properly (it kept showing the failure and then
1657 # printing PASS instead of XFAIL).
1658 #Skip(revprop_change, is_non_posix_and_non_windows_os),
1659 XFail(revprop_change, svntest.main.is_ra_type_dav),
1660 prop_value_conversions,
1661 binary_props,
1662 recursive_base_wc_ops,
1663 url_props_ops,
1664 removal_schedule_added_props,
1665 update_props_on_wc_root,
1666 props_on_replaced_file,
1667 depthy_wc_proplist,
1668 depthy_url_proplist,
1669 invalid_propnames,
1670 SkipUnless(perms_on_symlink, svntest.main.is_posix_os),
1671 remove_custom_ns_props,
1672 props_over_time,
1675 if __name__ == '__main__':
1676 svntest.main.run_tests(test_list)
1677 # NOTREACHED
1680 ### End of file.