1#!/usr/bin/env python
2#
3#  prop_tests.py:  testing versioned properties
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.apache.org for more information.
7#
8# ====================================================================
9#    Licensed to the Apache Software Foundation (ASF) under one
10#    or more contributor license agreements.  See the NOTICE file
11#    distributed with this work for additional information
12#    regarding copyright ownership.  The ASF licenses this file
13#    to you under the Apache License, Version 2.0 (the
14#    "License"); you may not use this file except in compliance
15#    with the License.  You may obtain a copy of the License at
16#
17#      http://www.apache.org/licenses/LICENSE-2.0
18#
19#    Unless required by applicable law or agreed to in writing,
20#    software distributed under the License is distributed on an
21#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22#    KIND, either express or implied.  See the License for the
23#    specific language governing permissions and limitations
24#    under the License.
25######################################################################
26
27# General modules
28import sys, re, os, stat, subprocess, logging
29
30logger = logging.getLogger()
31
32# Our testing module
33import svntest
34
35from svntest.main import SVN_PROP_MERGEINFO
36from svntest.main import SVN_PROP_INHERITABLE_IGNORES
37from svntest import wc
38
39# (abbreviation)
40Skip = svntest.testcase.Skip_deco
41SkipUnless = svntest.testcase.SkipUnless_deco
42XFail = svntest.testcase.XFail_deco
43Issues = svntest.testcase.Issues_deco
44Issue = svntest.testcase.Issue_deco
45Wimp = svntest.testcase.Wimp_deco
46Item = svntest.wc.StateItem
47
48def is_non_posix_and_non_windows_os():
49  """lambda function to skip revprop_change test"""
50  return (not svntest.main.is_posix_os()) and sys.platform != 'win32'
51
52# this is global so other test files can use it
53binary_mime_type_on_text_file_warning = \
54  "svn: warning:.*is a binary mime-type but file.*looks like text.*"
55
56######################################################################
57# Tests
58
59#----------------------------------------------------------------------
60
61def make_local_props(sbox):
62  "write/read props in wc only (ps, pl, pdel, pe)"
63
64  # Bootstrap
65  sbox.build()
66  wc_dir = sbox.wc_dir
67
68  # Add properties to one file and one directory
69  sbox.simple_propset('blue', 'azul', 'A/mu')
70  sbox.simple_propset('green', 'verde', 'A/mu')
71  sbox.simple_propset('editme', 'the foo fighters', 'A/mu')
72  sbox.simple_propset('red', 'rojo', 'A/D/G')
73  sbox.simple_propset('yellow', 'amarillo', 'A/D/G')
74
75  # Make sure they show up as local mods in status
76  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
77  expected_status.tweak('A/mu', status=' M')
78  expected_status.tweak('A/D/G', status=' M')
79
80  svntest.actions.run_and_verify_status(wc_dir, expected_status)
81
82  # Remove one property
83  sbox.simple_propdel('yellow', 'A/D/G')
84
85  svntest.main.use_editor('foo_to_bar')
86  # Edit one property
87  svntest.main.run_svn(None, 'propedit', 'editme',
88                       os.path.join(wc_dir, 'A', 'mu'))
89
90  # What we expect the disk tree to look like:
91  expected_disk = svntest.main.greek_state.copy()
92  expected_disk.tweak('A/mu', props={'blue' : 'azul', 'green' : 'verde',
93                                     'editme' : 'the bar fighters'})
94  expected_disk.tweak('A/D/G', props={'red' : 'rojo'})
95
96  # Read the real disk tree.  Notice we are passing the (normally
97  # disabled) "load props" flag to this routine.  This will run 'svn
98  # proplist' on every item in the working copy!
99  svntest.actions.verify_disk(wc_dir, expected_disk, True)
100
101  # Edit without actually changing the property
102  svntest.main.use_editor('identity')
103  svntest.actions.run_and_verify_svn("No changes to property 'editme' on '.*'",
104                                     [],
105                                     'propedit', 'editme',
106                                     os.path.join(wc_dir, 'A', 'mu'))
107
108
109
110#----------------------------------------------------------------------
111
112def commit_props(sbox):
113  "commit properties"
114
115  # Bootstrap
116  sbox.build()
117  wc_dir = sbox.wc_dir
118
119  # Add a property to a file and a directory
120  sbox.simple_propset('blue', 'azul', 'A/mu')
121  sbox.simple_propset('red', 'rojo', 'A/D/H')
122
123  # Create expected output tree.
124  expected_output = svntest.wc.State(wc_dir, {
125    'A/mu' : Item(verb='Sending'),
126    'A/D/H' : Item(verb='Sending'),
127    })
128
129  # Created expected status tree.
130  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
131  expected_status.tweak('A/mu', 'A/D/H', wc_rev=2, status='  ')
132
133  # Commit the one file.
134  svntest.actions.run_and_verify_commit(wc_dir,
135                                        expected_output,
136                                        expected_status)
137
138
139
140#----------------------------------------------------------------------
141
142@Issue(3951)
143def update_props(sbox):
144  "receive properties via update"
145
146  # Bootstrap
147  sbox.build()
148  wc_dir = sbox.wc_dir
149
150  # Make a backup copy of the working copy
151  wc_backup = sbox.add_wc_path('backup')
152  svntest.actions.duplicate_dir(wc_dir, wc_backup)
153
154  # Add a property to a file and a directory
155  sbox.simple_propset('blue', 'azul', 'A/mu')
156  sbox.simple_propset('red', 'rojo', 'A/D/H')
157
158  # Create expected output tree.
159  expected_output = svntest.wc.State(wc_dir, {
160    'A/mu' : Item(verb='Sending'),
161    'A/D/H' : Item(verb='Sending'),
162    })
163
164  # Created expected status tree.
165  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
166  expected_status.tweak('A/mu', 'A/D/H', wc_rev=2, status='  ')
167
168  # Commit property mods
169  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
170                                        expected_status)
171
172  # Add more properties
173  sbox.simple_propset('blue2', 'azul2', 'A/mu')
174  sbox.simple_propset('red2', 'rojo2', 'A/D/H')
175  expected_status.tweak('A/mu', 'A/D/H', wc_rev=3, status='  ')
176  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
177                                        expected_status)
178
179  # Create expected output tree for an update of the wc_backup.
180  expected_output = svntest.wc.State(wc_backup, {
181    'A/mu' : Item(status=' U'),
182    'A/D/H' : Item(status=' U'),
183    })
184
185  # Create expected disk tree for the update.
186  expected_disk = svntest.main.greek_state.copy()
187  expected_disk.tweak('A/mu', props={'blue' : 'azul'})
188  expected_disk.tweak('A/D/H', props={'red' : 'rojo'})
189
190  # Create expected status tree for the update.
191  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
192  expected_status.tweak('A/mu', 'A/D/H', status='  ')
193
194  # Do the update and check the results in three ways... INCLUDING PROPS
195  # This adds properties to nodes that have none
196  svntest.actions.run_and_verify_update(wc_backup,
197                                        expected_output,
198                                        expected_disk,
199                                        expected_status,
200                                        [], 1,
201                                        '-r', '2', wc_backup)
202
203  # This adds properties to nodes that have properties
204  expected_status.tweak(wc_rev=3)
205  expected_disk.tweak('A/mu', props={'blue'  : 'azul',
206                                     'blue2' : 'azul2'})
207  expected_disk.tweak('A/D/H', props={'red'  : 'rojo',
208                                      'red2' : 'rojo2'})
209  svntest.actions.run_and_verify_update(wc_backup,
210                                        expected_output,
211                                        expected_disk,
212                                        expected_status,
213                                        [], 1,
214                                        '-r', '3', wc_backup)
215
216
217#----------------------------------------------------------------------
218
219def downdate_props(sbox):
220  "receive property changes as part of a downdate"
221
222  # Bootstrap
223  sbox.build()
224  wc_dir = sbox.wc_dir
225
226  mu_path = sbox.ospath('A/mu')
227
228  # Add a property to a file
229  sbox.simple_propset('cash-sound', 'cha-ching!', 'iota')
230
231  # Create expected output tree.
232  expected_output = svntest.wc.State(wc_dir, {
233    'iota' : Item(verb='Sending'),
234    })
235
236  # Created expected status tree.
237  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
238  expected_status.tweak('iota', wc_rev=2, status='  ')
239
240  # Commit the one file.
241  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
242                                        expected_status)
243
244  # Make some mod (something to commit)
245  svntest.main.file_append(mu_path, "some mod")
246
247  # Create expected output tree.
248  expected_output = svntest.wc.State(wc_dir, {
249    'A/mu' : Item(verb='Sending'),
250    })
251
252  # Created expected status tree.
253  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
254  expected_status.tweak('iota', wc_rev=2, status='  ')
255  expected_status.tweak('A/mu', wc_rev=3, status='  ')
256
257  # Commit the one file.
258  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
259                                        expected_status)
260
261  # Create expected output tree for an update.
262  expected_output = svntest.wc.State(wc_dir, {
263    'iota' : Item(status=' U'),
264    'A/mu' : Item(status='U '),
265    })
266
267  # Create expected disk tree for the update.
268  expected_disk = svntest.main.greek_state
269
270  # Create expected status tree for the update.
271  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
272
273  # Do the update and check the results in three ways... INCLUDING PROPS
274  svntest.actions.run_and_verify_update(wc_dir,
275                                        expected_output,
276                                        expected_disk,
277                                        expected_status,
278                                        [], 1,
279                                        '-r', '1', wc_dir)
280
281#----------------------------------------------------------------------
282
283def remove_props(sbox):
284  "commit the removal of props"
285
286  # Bootstrap
287  sbox.build()
288  wc_dir = sbox.wc_dir
289
290  # Add a property to a file
291  sbox.simple_propset('cash-sound', 'cha-ching!', 'iota')
292
293  # Commit the file
294  sbox.simple_commit('iota')
295
296  # Now, remove the property
297  sbox.simple_propdel('cash-sound', 'iota')
298
299  # Create expected output tree.
300  expected_output = svntest.wc.State(wc_dir, {
301    'iota' : Item(verb='Sending'),
302    })
303
304  # Created expected status tree.
305  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
306  expected_status.tweak('iota', wc_rev=3, status='  ')
307
308  # Commit the one file.
309  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
310                                        expected_status)
311
312#----------------------------------------------------------------------
313
314def update_conflict_props(sbox):
315  "update with conflicting props"
316
317  # Bootstrap
318  sbox.build()
319  wc_dir = sbox.wc_dir
320
321  # Add a property to a file and a directory
322  mu_path = sbox.ospath('A/mu')
323  sbox.simple_propset('cash-sound', 'cha-ching!', 'A/mu')
324  A_path = sbox.ospath('A')
325  sbox.simple_propset('foo', 'bar', 'A')
326
327  # Commit the file and directory
328  sbox.simple_commit()
329
330  # Update to rev 1
331  svntest.main.run_svn(None, 'up', '-r', '1', wc_dir)
332
333  # Add conflicting properties
334  sbox.simple_propset('cash-sound', 'beep!', 'A/mu')
335  sbox.simple_propset('foo', 'baz', 'A')
336
337  # Create expected output tree for an update of the wc_backup.
338  expected_output = svntest.wc.State(wc_dir, {
339    'A/mu' : Item(status=' C'),
340    'A' : Item(status=' C'),
341    })
342
343  # Create expected disk tree for the update.
344  expected_disk = svntest.main.greek_state.copy()
345  expected_disk.tweak('A/mu', props={'cash-sound' : 'beep!'})
346  expected_disk.tweak('A', props={'foo' : 'baz'})
347
348  # Create expected status tree for the update.
349  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
350  expected_status.tweak('A/mu', 'A', status=' C')
351
352  extra_files = ['mu.*\.prej', 'dir_conflicts.*\.prej']
353  # Do the update and check the results in three ways... INCLUDING PROPS
354  svntest.actions.run_and_verify_update(wc_dir,
355                                        expected_output,
356                                        expected_disk,
357                                        expected_status,
358                                        check_props=True,
359                                        extra_files=extra_files)
360
361  # Resolve the conflicts
362  svntest.actions.run_and_verify_resolved([mu_path, A_path])
363
364  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
365  expected_status.tweak('A/mu', 'A', status=' M')
366
367  svntest.actions.run_and_verify_status(wc_dir, expected_status)
368
369#----------------------------------------------------------------------
370@Issue(2608)
371def commit_conflict_dirprops(sbox):
372  "commit with conflicting dirprops"
373
374  # Issue #2608: failure to see conflicting dirprops on root of
375  # repository.
376
377  # Bootstrap
378  sbox.build()
379  wc_dir = sbox.wc_dir
380
381  sbox.simple_propset('foo', 'bar', '')
382
383  # Commit the file and directory
384  sbox.simple_commit()
385
386  # Update to rev 1
387  svntest.main.run_svn(None,
388                       'up', '-r', '1', wc_dir)
389
390  # Add conflicting properties
391  sbox.simple_propset('foo', 'eek', '')
392
393  svntest.actions.run_and_verify_commit(wc_dir, None, None,
394                                        ".*[oO]ut[- ]of[- ]date.*")
395
396#----------------------------------------------------------------------
397
398# Issue #742: we used to screw up when committing a file replacement
399# that also had properties.  It was fixed by teaching
400# svn_wc_props_modified_p and svn_wc_transmit_prop_deltas to *ignore*
401# leftover base-props when a file is scheduled for replacement.  (When
402# we svn_wc_add a file, it starts life with no working props.)
403@Issue(742)
404def commit_replacement_props(sbox):
405  "props work when committing a replacement"
406
407  # Bootstrap
408  sbox.build()
409  wc_dir = sbox.wc_dir
410
411  # Add a property to two files
412  iota_path = sbox.ospath('iota')
413  lambda_path = sbox.ospath('A/B/lambda')
414  sbox.simple_propset('cash-sound', 'cha-ching!', 'iota')
415  sbox.simple_propset('boson', 'W', 'A/B/lambda')
416
417  # Commit (### someday use run_and_verify_commit for better coverage)
418  sbox.simple_commit()
419
420  # Schedule both files for deletion
421  sbox.simple_rm('iota', 'A/B/lambda')
422
423  # Now recreate the files, and schedule them for addition.
424  # Poof, the 'new' files don't have any properties at birth.
425  svntest.main.file_append(iota_path, 'iota TNG')
426  svntest.main.file_append(lambda_path, 'lambda TNG')
427  sbox.simple_add('iota', 'A/B/lambda')
428
429  # Sanity check:  the two files should be scheduled for (R)eplacement.
430  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
431  expected_status.tweak('iota', wc_rev=2, status='R ')
432  expected_status.tweak('A/B/lambda', wc_rev=2, status='R ')
433
434  svntest.actions.run_and_verify_status(wc_dir, expected_status)
435
436  # Now add a property to lambda.  Iota still doesn't have any.
437  sbox.simple_propset('capacitor', 'flux', 'A/B/lambda')
438
439  # Commit, with careful output checking.  We're actually going to
440  # scan the working copy for props after the commit.
441
442  expected_output = svntest.wc.State(wc_dir, {
443    'iota' : Item(verb='Replacing'),
444    'A/B/lambda' : Item(verb='Replacing'),
445    })
446
447  # Expected status tree:  lambda has one prop, iota doesn't.
448  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
449  expected_status.tweak('iota', wc_rev=3)
450  expected_status.tweak('A/B/lambda', wc_rev=3, status='  ')
451
452  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
453                                        expected_status)
454
455#----------------------------------------------------------------------
456
457def revert_replacement_props(sbox):
458  "props work when reverting a replacement"
459
460  # Bootstrap
461  sbox.build()
462  wc_dir = sbox.wc_dir
463
464  # Add a property to two files
465  iota_path = sbox.ospath('iota')
466  lambda_path = sbox.ospath('A/B/lambda')
467  sbox.simple_propset('cash-sound', 'cha-ching!', 'iota')
468  sbox.simple_propset('boson', 'W', 'A/B/lambda')
469
470  # Commit rev 2. (### someday use run_and_verify_commit for better coverage)
471  sbox.simple_commit()
472
473  # Schedule both files for deletion
474  sbox.simple_rm('iota', 'A/B/lambda')
475
476  # Now recreate the files, and schedule them for addition.
477  # Poof, the 'new' files don't have any properties at birth.
478  svntest.main.file_append(iota_path, 'iota TNG')
479  svntest.main.file_append(lambda_path, 'lambda TNG')
480  sbox.simple_add('iota', 'A/B/lambda')
481
482  # Sanity check:  the two files should be scheduled for (R)eplacement.
483  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
484  expected_status.tweak('iota', wc_rev=2, status='R ')
485  expected_status.tweak('A/B/lambda', wc_rev=2, status='R ')
486
487  svntest.actions.run_and_verify_status(wc_dir, expected_status)
488
489  # Now add a property to lambda.  Iota still doesn't have any.
490  sbox.simple_propset('capacitor', 'flux', 'A/B/lambda')
491
492  # Now revert both files.
493  sbox.simple_revert('iota', 'A/B/lambda')
494
495  # Do an update; even though the update is really a no-op,
496  # run_and_verify_update has the nice feature of scanning disk as
497  # well as running status.  We want to verify that we truly have a
498  # *pristine* revision 2 tree, with the original rev 2 props, and no
499  # local mods at all.
500
501  expected_output = svntest.wc.State(wc_dir, {
502    })
503
504  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
505  expected_status.tweak('iota', status='  ')
506  expected_status.tweak('A/B/lambda', status='  ')
507
508  expected_disk = svntest.main.greek_state.copy()
509  expected_disk.tweak('iota', props={'cash-sound' : 'cha-ching!'})
510  expected_disk.tweak('A/B/lambda', props={'boson' : 'W'})
511
512  # scan disk for props too.
513  svntest.actions.run_and_verify_update(wc_dir,
514                                        expected_output,
515                                        expected_disk,
516                                        expected_status,
517                                        check_props=True)
518
519#----------------------------------------------------------------------
520@Issues(920,2065)
521def inappropriate_props(sbox):
522  "try to set inappropriate props"
523
524  # Bootstrap
525  sbox.build()
526  wc_dir = sbox.wc_dir
527
528  A_path = sbox.ospath('A')
529  E_path = sbox.ospath('A/B/E')
530  iota_path = sbox.ospath('iota')
531
532  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
533  svntest.actions.run_and_verify_status(wc_dir, expected_status)
534
535  # These should produce an error
536  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput,
537                                     'propset', 'svn:executable', 'on', A_path)
538
539  svntest.actions.run_and_verify_svn(None,
540                                     svntest.verify.AnyOutput, 'propset',
541                                     'svn:keywords', 'LastChangedDate',
542                                     A_path)
543
544  svntest.actions.run_and_verify_svn(None,
545                                     svntest.verify.AnyOutput, 'propset',
546                                     'svn:eol-style', 'native', A_path)
547
548  svntest.actions.run_and_verify_svn(None,
549                                     svntest.verify.AnyOutput, 'propset',
550                                     'svn:eol-style', 'invalid value',
551                                     os.path.join(A_path, 'mu'))
552
553  svntest.actions.run_and_verify_svn(None,
554                                     svntest.verify.AnyOutput, 'propset',
555                                     'svn:mime-type', 'image/png', A_path)
556
557  svntest.actions.run_and_verify_svn(None,
558                                     svntest.verify.AnyOutput, 'propset',
559                                     'svn:ignore', '*.o', iota_path)
560
561  svntest.actions.run_and_verify_svn(None,
562                                     svntest.verify.AnyOutput, 'propset',
563                                     'svn:externals',
564                                     'foo http://host.com/repos', iota_path)
565
566  svntest.actions.run_and_verify_svn(None,
567                                     svntest.verify.AnyOutput, 'propset',
568                                     'svn:author', 'socrates', iota_path)
569
570  svntest.actions.run_and_verify_svn(None,
571                                     svntest.verify.AnyOutput, 'propset',
572                                     'svn:log', 'log message', iota_path)
573
574  svntest.actions.run_and_verify_svn(None,
575                                     svntest.verify.AnyOutput, 'propset',
576                                     'svn:date', 'Tue Jan 19 04:14:07 2038',
577                                     iota_path)
578
579  svntest.actions.run_and_verify_svn(None,
580                                     svntest.verify.AnyOutput, 'propset',
581                                     'svn:original-date',
582                                     'Thu Jan  1 01:00:00 1970', iota_path)
583
584  # Status unchanged
585  svntest.actions.run_and_verify_status(wc_dir, expected_status)
586
587  # Recursive setting of inappropriate dir prop should work on files
588  svntest.actions.run_and_verify_svn(None, [], 'propset', '-R',
589                                     'svn:executable', 'on', E_path)
590
591  expected_status.tweak('A/B/E/alpha', 'A/B/E/beta', status=' M')
592  svntest.actions.run_and_verify_status(wc_dir, expected_status)
593
594  # Issue #920. Don't allow setting of svn:eol-style on binary files or files
595  # with inconsistent eol types.
596
597  path = sbox.ospath('binary')
598  svntest.main.file_append(path, "binary")
599  sbox.simple_add('binary')
600
601  svntest.main.run_svn(binary_mime_type_on_text_file_warning,
602                       'propset', 'svn:mime-type', 'application/octet-stream',
603                       sbox.ospath('binary'))
604
605  svntest.actions.run_and_verify_svn(None,
606                                     svntest.verify.AnyOutput,
607                                     'propset', 'svn:eol-style',
608                                     'CRLF', path)
609
610  path = sbox.ospath('multi-eol')
611  svntest.main.file_append(path, "line1\rline2\n")
612  sbox.simple_add('multi-eol')
613
614  svntest.actions.run_and_verify_svn(None,
615                                     svntest.verify.AnyOutput,
616                                     'propset', 'svn:eol-style',
617                                     'LF', path)
618
619  path = sbox.ospath('backwards-eol')
620  svntest.main.file_append(path, "line1\n\r")
621  sbox.simple_add('backwards-eol')
622
623  svntest.actions.run_and_verify_svn(None,
624                                     svntest.verify.AnyOutput,
625                                     'propset', 'svn:eol-style',
626                                     'native', path)
627
628  path = sbox.ospath('incomplete-eol')
629  svntest.main.file_append(path, "line1\r\n\r")
630  sbox.simple_add('incomplete-eol')
631
632  svntest.actions.run_and_verify_svn(None,
633                                     svntest.verify.AnyOutput,
634                                     'propset', 'svn:eol-style',
635                                     'CR', path)
636
637  # Issue #2065. Do allow setting of svn:eol-style on binary files or files
638  # with inconsistent eol types if --force is passed.
639
640  path = sbox.ospath('binary')
641  svntest.main.file_append(path, "binary")
642  svntest.actions.run_and_verify_svn(None, [],
643                                     'propset', '--force',
644                                     'svn:eol-style', 'CRLF',
645                                     path)
646
647  path = sbox.ospath('multi-eol')
648  svntest.actions.run_and_verify_svn(None, [],
649                                     'propset', '--force',
650                                     'svn:eol-style', 'LF',
651                                     path)
652
653  path = sbox.ospath('backwards-eol')
654  svntest.actions.run_and_verify_svn(None, [],
655                                     'propset', '--force',
656                                     'svn:eol-style', 'native',
657                                     path)
658
659  path = sbox.ospath('incomplete-eol')
660  svntest.actions.run_and_verify_svn(None, [],
661                                     'propset', '--force',
662                                     'svn:eol-style', 'CR',
663                                     path)
664
665  # Prevent setting of svn:mergeinfo prop values that are...
666  path = sbox.ospath('A/D')
667
668  # ...grammatically incorrect
669  svntest.actions.run_and_verify_svn(None,
670                                     "svn: E200020: Pathname not terminated by ':'\n",
671                                     'propset', SVN_PROP_MERGEINFO, '/trunk',
672                                     path)
673  svntest.actions.run_and_verify_svn(None,
674                                     "svn: E200022: Invalid revision number found "
675                                      "parsing 'one'\n",
676                                     'propset', SVN_PROP_MERGEINFO,
677                                     '/trunk:one', path)
678
679  # ...contain overlapping revision ranges of differing inheritability.
680  svntest.actions.run_and_verify_svn(None,
681                                     "svn: E200020: Unable to parse overlapping "
682                                     "revision ranges '9-20\\*' and "
683                                     "'18-22' with different "
684                                     "inheritance types\n",
685                                     'propset', SVN_PROP_MERGEINFO,
686                                     '/branch:5-7,9-20*,18-22', path)
687
688  svntest.actions.run_and_verify_svn(None,
689                                     "svn: E200020: Unable to parse overlapping "
690                                     "revision ranges "
691                                     "(('3' and '3\\*')|('3\\*' and '3')) "
692                                     "with different "
693                                     "inheritance types\n",
694                                     'propset', SVN_PROP_MERGEINFO,
695                                     '/branch:3,3*', path)
696
697  # ...contain revision ranges with start revisions greater than or
698  #    equal to end revisions.
699  svntest.actions.run_and_verify_svn(None,
700                                     "svn: E200020: Unable to parse reversed "
701                                      "revision range '20-5'\n",
702                                     'propset', SVN_PROP_MERGEINFO,
703                                     '/featureX:4,20-5', path)
704
705  # ...contain paths mapped to empty revision ranges
706  svntest.actions.run_and_verify_svn(None,
707                                     "svn: E200020: Mergeinfo for '/trunk' maps to "
708                                      "an empty revision range\n",
709                                     'propset', SVN_PROP_MERGEINFO,
710                                     '/trunk:', path)
711
712  # ...contain non-inheritable ranges when the target is a file.
713  svntest.actions.run_and_verify_svn(None,
714                                     "svn: E200020: Cannot set non-inheritable "
715                                     "mergeinfo on a non-directory*",
716                                     'propset', SVN_PROP_MERGEINFO,
717                                     '/A/D/H/psi:1*', iota_path)
718
719#----------------------------------------------------------------------
720
721# Issue #976.  When copying a file, do not determine svn:executable
722# and svn:mime-type values as though the file is brand new, instead
723# use the copied file's property values.
724@Issue(976)
725def copy_inherits_special_props(sbox):
726  "file copies inherit (not re-derive) special props"
727
728  # Bootstrap
729  sbox.build()
730  wc_dir = sbox.wc_dir
731
732  orig_mime_type = 'image/fake_image'
733
734  # Create two paths
735  new_path1 = sbox.ospath('new_file1.bin')
736  new_path2 = sbox.ospath('new_file2.bin')
737
738  # Create the first path as a binary file.  To have svn treat the
739  # file as binary, have a 0x00 in the file.
740  svntest.main.file_append(new_path1, "binary file\000")
741  sbox.simple_add('new_file1.bin')
742
743  # Add initial svn:mime-type to the file
744  sbox.simple_propset('svn:mime-type', orig_mime_type, 'new_file1.bin')
745
746  # Set the svn:executable property on the file if this is a system
747  # that can handle chmod, in which case svn will turn on the
748  # executable bits on the file.  Then remove the executable bits
749  # manually on the file and see the value of svn:executable in the
750  # copied file.
751  if os.name == 'posix':
752    sbox.simple_propset('svn:executable', 'on', 'new_file1.bin')
753    os.chmod(new_path1, svntest.main.S_ALL_READ | stat.S_IWUSR)
754
755  # Commit the file
756  sbox.simple_commit()
757
758  # Copy the file
759  svntest.main.run_svn(None, 'cp', new_path1, new_path2)
760
761  # Check the svn:mime-type
762  actual_exit, actual_stdout, actual_stderr = svntest.main.run_svn(
763    None, 'pg', 'svn:mime-type', new_path2)
764
765  expected_stdout = [orig_mime_type + '\n']
766  if actual_stdout != expected_stdout:
767    logger.warn("svn pg svn:mime-type output does not match expected.")
768    logger.warn("Expected standard output:  %s\n", expected_stdout)
769    logger.warn("Actual standard output:  %s\n", actual_stdout)
770    raise svntest.verify.SVNUnexpectedOutput
771
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_exit, actual_stdout, actual_stderr = svntest.main.run_svn(
776      None, 'pg', 'svn:executable', new_path2)
777
778    expected_stdout = ['*\n']
779    if actual_stdout != expected_stdout:
780      logger.warn("svn pg svn:executable output does not match expected.")
781      logger.warn("Expected standard output:  %s\n", expected_stdout)
782      logger.warn("Actual standard output:  %s\n", actual_stdout)
783      raise svntest.verify.SVNUnexpectedOutput
784
785#----------------------------------------------------------------------
786# Test for issue #3086 'mod-dav-svn ignores pre-revprop-change failure
787# on revprop delete'
788#
789# If we learn how to write a pre-revprop-change hook for
790# non-Posix platforms, we won't have to skip here:
791@Skip(is_non_posix_and_non_windows_os)
792@Issue(3086)
793def revprop_change(sbox):
794  "set, get, and delete a revprop change"
795
796  sbox.build()
797
798  # First test the error when no revprop-change hook exists.
799  svntest.actions.run_and_verify_svn(None, '.*pre-revprop-change',
800                                     'propset', '--revprop', '-r', '0',
801                                     'cash-sound', 'cha-ching!', sbox.wc_dir)
802
803  # Now test error output from revprop-change hook.
804  svntest.actions.disable_revprop_changes(sbox.repo_dir)
805  svntest.actions.run_and_verify_svn(None, '.*pre-revprop-change.* 0 jrandom cash-sound A',
806                                     'propset', '--revprop', '-r', '0',
807                                     'cash-sound', 'cha-ching!', sbox.wc_dir)
808
809  # Create the revprop-change hook for this test
810  svntest.actions.enable_revprop_changes(sbox.repo_dir)
811
812  svntest.actions.run_and_verify_svn(None, [],
813                                     'propset', '--revprop', '-r', '0',
814                                     'cash-sound', 'cha-ching!', sbox.wc_dir)
815
816  svntest.actions.run_and_verify_svn(None, [],
817                                     'propget', '--revprop', '-r', '0',
818                                     'cash-sound', sbox.wc_dir)
819
820  # Now test that blocking the revprop delete.
821  svntest.actions.disable_revprop_changes(sbox.repo_dir)
822  svntest.actions.run_and_verify_svn(None, '.*pre-revprop-change.* 0 jrandom cash-sound D',
823                                     'propdel', '--revprop', '-r', '0',
824                                     'cash-sound', sbox.wc_dir)
825
826  # Now test actually deleting the revprop.
827  svntest.actions.enable_revprop_changes(sbox.repo_dir)
828  svntest.actions.run_and_verify_svn(None, [],
829                                     'propdel', '--revprop', '-r', '0',
830                                     'cash-sound', sbox.wc_dir)
831
832  # The property should have been deleted.
833  svntest.actions.run_and_verify_svn(None,
834    '.*(E195011|E200017).*cash-sound.*',
835    'propget', '--revprop', '-r', '0', 'cash-sound', sbox.wc_dir)
836
837
838#----------------------------------------------------------------------
839
840def prop_value_conversions(sbox):
841  "some svn: properties should be converted"
842
843  # Bootstrap
844  sbox.build()
845  wc_dir = sbox.wc_dir
846
847  A_path = sbox.ospath('A')
848  B_path = sbox.ospath('A/B')
849  iota_path = sbox.ospath('iota')
850  lambda_path = sbox.ospath('A/B/lambda')
851  mu_path = sbox.ospath('A/mu')
852
853  # Leading and trailing whitespace should be stripped
854  svntest.actions.set_prop('svn:mime-type', ' text/html\n\n', iota_path)
855  svntest.actions.set_prop('svn:mime-type', 'text/html', mu_path)
856
857  # Leading and trailing whitespace should be stripped
858  svntest.actions.set_prop('svn:eol-style', '\nnative\n', iota_path)
859  svntest.actions.set_prop('svn:eol-style', 'native', mu_path)
860
861  # A trailing newline should be added
862  svntest.actions.set_prop('svn:ignore', '*.o\nfoo.c', A_path)
863  svntest.actions.set_prop('svn:ignore', '*.o\nfoo.c\n', B_path)
864
865  # A trailing newline should be added
866  svntest.actions.set_prop('svn:externals', 'foo http://foo.com/repos', A_path)
867  svntest.actions.set_prop('svn:externals', 'foo http://foo.com/repos\n', B_path)
868
869  # Leading and trailing whitespace should be stripped, but not internal
870  # whitespace
871  svntest.actions.set_prop('svn:keywords', ' Rev Date \n', iota_path)
872  svntest.actions.set_prop('svn:keywords', 'Rev  Date', mu_path)
873
874  # svn:executable value should be forced to a '*'
875  svntest.actions.set_prop('svn:executable', 'foo', iota_path)
876  svntest.actions.set_prop('svn:executable', '*', lambda_path)
877  for pval in ('      ', '', 'no', 'off', 'false'):
878    svntest.actions.set_prop('svn:executable', pval, mu_path,
879                             "svn: warning: W125005.*use 'svn propdel'")
880
881  # Anything else should be untouched
882  svntest.actions.set_prop('svn:some-prop', 'bar', lambda_path, force=True)
883  svntest.actions.set_prop('svn:some-prop', ' bar baz', mu_path, force=True)
884  svntest.actions.set_prop('svn:some-prop', 'bar\n', iota_path, force=True)
885  svntest.actions.set_prop('some-prop', 'bar', lambda_path)
886  svntest.actions.set_prop('some-prop', ' bar baz', mu_path)
887  svntest.actions.set_prop('some-prop', 'bar\n', iota_path)
888
889  # NOTE: When writing out multi-line prop values in svn:* props, the
890  # client converts to local encoding and local eol style.
891  # Therefore, the expected output must contain the right kind of eoln
892  # strings. That's why we use os.linesep in the tests below, not just
893  # plain '\n'. The _last_ \n is also from the client, but it's not
894  # part of the prop value and it doesn't get converted in the pipe.
895
896  # Check svn:mime-type
897  svntest.actions.check_prop('svn:mime-type', iota_path, [b'text/html'])
898  svntest.actions.check_prop('svn:mime-type', mu_path, [b'text/html'])
899
900  # Check svn:eol-style
901  svntest.actions.check_prop('svn:eol-style', iota_path, [b'native'])
902  svntest.actions.check_prop('svn:eol-style', mu_path, [b'native'])
903
904  # Check svn:ignore
905  linesep = os.linesep.encode()
906  svntest.actions.check_prop('svn:ignore', A_path,
907                             [b'*.o'+linesep, b'foo.c'+linesep])
908  svntest.actions.check_prop('svn:ignore', B_path,
909                             [b'*.o'+linesep, b'foo.c'+linesep])
910
911  # Check svn:externals
912  svntest.actions.check_prop('svn:externals', A_path,
913                             [b'foo http://foo.com/repos'+linesep])
914  svntest.actions.check_prop('svn:externals', B_path,
915                             [b'foo http://foo.com/repos'+linesep])
916
917  # Check svn:keywords
918  svntest.actions.check_prop('svn:keywords', iota_path, [b'Rev Date'])
919  svntest.actions.check_prop('svn:keywords', mu_path, [b'Rev  Date'])
920
921  # Check svn:executable
922  svntest.actions.check_prop('svn:executable', iota_path, [b'*'])
923  svntest.actions.check_prop('svn:executable', lambda_path, [b'*'])
924  svntest.actions.check_prop('svn:executable', mu_path, [b'*'])
925
926  # Check other props
927  svntest.actions.check_prop('svn:some-prop', lambda_path, [b'bar'])
928  svntest.actions.check_prop('svn:some-prop', mu_path, [b' bar baz'])
929  svntest.actions.check_prop('svn:some-prop', iota_path, [b'bar'+linesep])
930  svntest.actions.check_prop('some-prop', lambda_path, [b'bar'])
931  svntest.actions.check_prop('some-prop', mu_path,[b' bar baz'])
932  svntest.actions.check_prop('some-prop', iota_path, [b'bar\n'])
933
934
935#----------------------------------------------------------------------
936
937def binary_props(sbox):
938  "test binary property support"
939
940  # Bootstrap
941  sbox.build()
942  wc_dir = sbox.wc_dir
943
944  # Make a backup copy of the working copy
945  wc_backup = sbox.add_wc_path('backup')
946  svntest.actions.duplicate_dir(wc_dir, wc_backup)
947
948  # Some path convenience vars.
949  A_path = sbox.ospath('A')
950  B_path = sbox.ospath('A/B')
951  iota_path = sbox.ospath('iota')
952  lambda_path = sbox.ospath('A/B/lambda')
953  mu_path = sbox.ospath('A/mu')
954
955  A_path_bak = sbox.ospath('A', wc_dir=wc_backup)
956  B_path_bak = sbox.ospath('A/B', wc_dir=wc_backup)
957  iota_path_bak = sbox.ospath('iota', wc_dir=wc_backup)
958  lambda_path_bak = sbox.ospath('A/B/lambda', wc_dir=wc_backup)
959  mu_path_bak = sbox.ospath('A/mu', wc_dir=wc_backup)
960
961  # Property value convenience vars.
962  prop_zb   = b"This property has a zer\000 byte."
963  prop_ff   = b"This property has a form\014feed."
964  prop_xml  = b"This property has an <xml> tag."
965  prop_binx = b"This property has an <xml> tag and a zer\000 byte."
966
967  # Set some binary properties.
968  svntest.actions.set_prop('prop_zb', prop_zb, B_path, )
969  svntest.actions.set_prop('prop_ff', prop_ff, iota_path)
970  svntest.actions.set_prop('prop_xml', prop_xml, lambda_path)
971  svntest.actions.set_prop('prop_binx', prop_binx, mu_path)
972  svntest.actions.set_prop('prop_binx', prop_binx, A_path)
973
974  # Create expected output and status trees.
975  expected_output = svntest.wc.State(wc_dir, {
976    'A' : Item(verb='Sending'),
977    'A/B' : Item(verb='Sending'),
978    'iota' : Item(verb='Sending'),
979    'A/B/lambda' : Item(verb='Sending'),
980    'A/mu' : Item(verb='Sending'),
981    })
982  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
983  expected_status.tweak('A', 'A/B', 'iota', 'A/B/lambda', 'A/mu',
984                        wc_rev=2, status='  ')
985
986  # Commit the propsets.
987  svntest.actions.run_and_verify_commit(wc_dir,
988                                        expected_output,
989                                        expected_status)
990
991  # Create expected output, disk, and status trees for an update of
992  # the wc_backup.
993  expected_output = svntest.wc.State(wc_backup, {
994    'A' : Item(status=' U'),
995    'A/B' : Item(status=' U'),
996    'iota' : Item(status=' U'),
997    'A/B/lambda' : Item(status=' U'),
998    'A/mu' : Item(status=' U'),
999    })
1000  expected_disk = svntest.main.greek_state.copy()
1001  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
1002
1003  # Do the update and check the results.
1004  svntest.actions.run_and_verify_update(wc_backup,
1005                                        expected_output,
1006                                        expected_disk,
1007                                        expected_status)
1008
1009  # Now, check those properties.
1010  svntest.actions.check_prop('prop_zb', B_path_bak, [prop_zb])
1011  svntest.actions.check_prop('prop_ff', iota_path_bak, [prop_ff])
1012  svntest.actions.check_prop('prop_xml', lambda_path_bak, [prop_xml])
1013  svntest.actions.check_prop('prop_binx', mu_path_bak, [prop_binx])
1014  svntest.actions.check_prop('prop_binx', A_path_bak, [prop_binx])
1015
1016#----------------------------------------------------------------------
1017
1018# Ensure that each line of output contains the corresponding string of
1019# expected_out, and that errput is empty.
1020def verify_output(expected_out, output, errput):
1021  if errput != []:
1022    logger.warn('Error: stderr:')
1023    logger.warn(errput)
1024    raise svntest.Failure
1025  output.sort()
1026  ln = 0
1027  for line in output:
1028    if line.startswith('DBG:'):
1029      continue
1030    if ((line.find(expected_out[ln]) == -1) or
1031        (line != '' and expected_out[ln] == '')):
1032      logger.warn('Error: expected keywords:  %s', expected_out)
1033      logger.warn('       actual full output: %s', output)
1034      raise svntest.Failure
1035    ln = ln + 1
1036  if ln != len(expected_out):
1037    raise svntest.Failure
1038
1039@Issue(1794)
1040def recursive_base_wc_ops(sbox):
1041  "recursive property operations in BASE and WC"
1042
1043  # Bootstrap
1044  sbox.build()
1045  wc_dir = sbox.wc_dir
1046
1047  # Files with which to test, in alphabetical order
1048  fp_add = sbox.ospath('A/added')
1049  fp_del = sbox.ospath('A/mu')
1050  #fp_keep= sbox.ospath('iota')
1051
1052  # Set up properties
1053  sbox.simple_propset('p', 'old-del', 'A/mu')
1054  sbox.simple_propset('p', 'old-keep', 'iota')
1055  sbox.simple_commit()
1056
1057  svntest.main.file_append(fp_add, 'blah')
1058  sbox.simple_add('A/added')
1059  sbox.simple_propset('p', 'new-add', 'A/added')
1060  sbox.simple_propset('p', 'new-del', 'A/mu')
1061  sbox.simple_propset('p', 'new-keep', 'iota')
1062  svntest.main.run_svn(None, 'del', '--force', fp_del)
1063
1064  # Test recursive proplist
1065  exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '-R',
1066                                                   '-v', wc_dir, '-rBASE')
1067  verify_output([ 'old-del', 'old-keep', 'p', 'p',
1068                  'Properties on ', 'Properties on ' ],
1069                output, errput)
1070  svntest.verify.verify_exit_code(None, exit_code, 0)
1071
1072  exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '-R',
1073                                                   '-v', wc_dir)
1074  verify_output([ 'new-add', 'new-keep', 'p', 'p',
1075                  'Properties on ', 'Properties on ' ],
1076                output, errput)
1077  svntest.verify.verify_exit_code(None, exit_code, 0)
1078
1079  # Test recursive propget
1080  exit_code, output, errput = svntest.main.run_svn(None, 'propget', '-R',
1081                                                   'p', wc_dir, '-rBASE')
1082  verify_output([ 'old-del', 'old-keep' ], output, errput)
1083  svntest.verify.verify_exit_code(None, exit_code, 0)
1084
1085  exit_code, output, errput = svntest.main.run_svn(None, 'propget', '-R',
1086                                                   'p', wc_dir)
1087  verify_output([ 'new-add', 'new-keep' ], output, errput)
1088  svntest.verify.verify_exit_code(None, exit_code, 0)
1089
1090  # Test recursive propset (issue 1794)
1091  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1092  expected_status.tweak('A/mu', status='D ', wc_rev=2)
1093  expected_status.tweak('iota', status=' M', wc_rev=2)
1094  expected_status.add({
1095    'A/added'     : Item(status='A ', wc_rev=0),
1096    })
1097  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1098
1099  svntest.actions.run_and_verify_svn(None, [],
1100                                     'propset', '-R', 'svn:keywords', 'Date',
1101                                     os.path.join(wc_dir, 'A', 'B'))
1102  expected_status.tweak('A/B/lambda', 'A/B/E/alpha', 'A/B/E/beta', status=' M')
1103  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1104
1105#----------------------------------------------------------------------
1106
1107def url_props_ops(sbox):
1108  "property operations on a URL"
1109
1110  # Bootstrap
1111  sbox.build()
1112  wc_dir = sbox.wc_dir
1113
1114  prop1 = 'prop1'
1115  propval1 = 'propval1 is foo'
1116  prop2 = 'prop2'
1117  propval2 = 'propval2'
1118
1119  iota_url = sbox.repo_url + '/iota'
1120  A_url = sbox.repo_url + '/A'
1121
1122  # Add a couple of properties
1123  sbox.simple_propset(prop1, propval1, 'iota')
1124  sbox.simple_propset(prop1, propval1, 'A')
1125
1126  # Commit
1127  sbox.simple_commit()
1128
1129  # Add a few more properties
1130  sbox.simple_propset(prop2, propval2, 'iota')
1131  sbox.simple_propset(prop2, propval2, 'A')
1132
1133  # Commit again
1134  sbox.simple_commit()
1135
1136  # Test propget
1137  svntest.actions.run_and_verify_svn([ propval1 + '\n' ], [],
1138                                     'propget', prop1, iota_url)
1139  svntest.actions.run_and_verify_svn([ propval1 + '\n' ], [],
1140                                     'propget', prop1, A_url)
1141
1142  # Test normal proplist
1143  exit_code, output, errput = svntest.main.run_svn(None,
1144                                                   'proplist', iota_url)
1145  verify_output([ prop1, prop2, 'Properties on ' ],
1146                output, errput)
1147  svntest.verify.verify_exit_code(None, exit_code, 0)
1148
1149  exit_code, output, errput = svntest.main.run_svn(None,
1150                                                   'proplist', A_url)
1151  verify_output([ prop1, prop2, 'Properties on ' ],
1152                output, errput)
1153  svntest.verify.verify_exit_code(None, exit_code, 0)
1154
1155  # Test verbose proplist
1156  exit_code, output, errput = svntest.main.run_svn(None,
1157                                                   'proplist', '-v', iota_url)
1158  verify_output([ propval1, propval2, prop1, prop2,
1159                  'Properties on ' ], output, errput)
1160  svntest.verify.verify_exit_code(None, exit_code, 0)
1161
1162  exit_code, output, errput = svntest.main.run_svn(None,
1163                                                   'proplist', '-v', A_url)
1164  verify_output([ propval1, propval2, prop1, prop2,
1165                  'Properties on ' ], output, errput)
1166  svntest.verify.verify_exit_code(None, exit_code, 0)
1167
1168  # Test propedit
1169  svntest.main.use_editor('foo_to_bar')
1170  propval1 = propval1.replace('foo', 'bar')
1171  svntest.main.run_svn(None,
1172                       'propedit', prop1, '-m', 'editlog', iota_url)
1173  svntest.main.run_svn(None,
1174                       'propedit', prop1, '-m', 'editlog', A_url)
1175  svntest.actions.run_and_verify_svn([ propval1 + '\n' ], [],
1176                                     'propget', prop1, iota_url)
1177  svntest.actions.run_and_verify_svn([ propval1 + '\n' ], [],
1178                                     'propget', prop1, A_url)
1179
1180  # Edit without actually changing the property
1181  svntest.main.use_editor('identity')
1182  svntest.actions.run_and_verify_svn("No changes to property '%s' on '.*'"
1183                                       % prop1,
1184                                     [],
1185                                     'propedit', prop1, '-m', 'nocommit',
1186                                     iota_url)
1187
1188
1189
1190#----------------------------------------------------------------------
1191def removal_schedule_added_props(sbox):
1192  "removal of schedule added file with properties"
1193
1194  sbox.build()
1195
1196  wc_dir = sbox.wc_dir
1197  newfile_path = sbox.ospath('newfile')
1198  file_add_output = ["A         " + newfile_path + "\n"]
1199  propset_output = ["property 'newprop' set on '" + newfile_path + "'\n"]
1200  file_rm_output = ["D         " + newfile_path + "\n"]
1201  propls_output = [
1202     "Properties on '" + newfile_path + "':\n",
1203     "  newprop\n",
1204     "    newvalue\n",
1205                  ]
1206
1207  # create new fs file
1208  open(newfile_path, 'w').close()
1209  # Add it and set a property
1210  svntest.actions.run_and_verify_svn(file_add_output, [], 'add', newfile_path)
1211  svntest.actions.run_and_verify_svn(propset_output, [], 'propset',
1212                                     'newprop', 'newvalue', newfile_path)
1213  svntest.actions.run_and_verify_svn(propls_output, [],
1214                                     'proplist', '-v', newfile_path)
1215  # remove the file
1216  svntest.actions.run_and_verify_svn(file_rm_output, [],
1217                                     'rm', '--force', newfile_path)
1218  # recreate the file and add it again
1219  open(newfile_path, 'w').close()
1220  svntest.actions.run_and_verify_svn(file_add_output, [], 'add', newfile_path)
1221
1222  # Now there should be NO properties leftover...
1223  svntest.actions.run_and_verify_svn([], [],
1224                                     'proplist', '-v', newfile_path)
1225
1226#----------------------------------------------------------------------
1227
1228def update_props_on_wc_root(sbox):
1229  "receive properties on the wc root via update"
1230
1231  # Bootstrap
1232  sbox.build()
1233  wc_dir = sbox.wc_dir
1234
1235  # Make a backup copy of the working copy
1236  wc_backup = sbox.add_wc_path('backup')
1237  svntest.actions.duplicate_dir(wc_dir, wc_backup)
1238
1239  # Add a property to the root folder
1240  sbox.simple_propset('red', 'rojo', '')
1241
1242  # Create expected output tree.
1243  expected_output = svntest.wc.State(wc_dir, {
1244    '' : Item(verb='Sending')
1245    })
1246
1247  # Created expected status tree.
1248  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1249  expected_status.tweak('', wc_rev=2, status='  ')
1250
1251  # Commit the working copy
1252  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1253                                        expected_status)
1254
1255 # Create expected output tree for an update of the wc_backup.
1256  expected_output = svntest.wc.State(wc_backup, {
1257    '' : Item(status=' U'),
1258    })
1259  # Create expected disk tree for the update.
1260  expected_disk = svntest.main.greek_state.copy()
1261  expected_disk.add({
1262    '' : Item(props = {'red' : 'rojo'}),
1263    })
1264  # Create expected status tree for the update.
1265  expected_status = svntest.actions.get_virginal_state(wc_backup, 2)
1266  expected_status.tweak('', status='  ')
1267
1268  # Do the update and check the results in three ways... INCLUDING PROPS
1269  svntest.actions.run_and_verify_update(wc_backup,
1270                                        expected_output,
1271                                        expected_disk,
1272                                        expected_status,
1273                                        check_props=True)
1274
1275# test for issue 2743
1276@Issue(2743)
1277def props_on_replaced_file(sbox):
1278  """test properties on replaced files"""
1279
1280  sbox.build()
1281  wc_dir = sbox.wc_dir
1282
1283  # Add some properties to iota
1284  iota_path = sbox.ospath("iota")
1285  sbox.simple_propset('red', 'rojo', 'iota')
1286  sbox.simple_propset('blue', 'lagoon', 'iota')
1287  sbox.simple_commit()
1288
1289  # replace iota_path
1290  sbox.simple_rm('iota')
1291  svntest.main.file_append(iota_path, "some mod")
1292  sbox.simple_add('iota')
1293
1294  # check that the replaced file has no properties
1295  expected_disk = svntest.main.greek_state.copy()
1296  expected_disk.tweak('iota', contents="some mod")
1297  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
1298
1299  # now add a new property to iota
1300  sbox.simple_propset('red', 'mojo', 'iota')
1301  sbox.simple_propset('groovy', 'baby', 'iota')
1302
1303  # What we expect the disk tree to look like:
1304  expected_disk.tweak('iota', props={'red' : 'mojo', 'groovy' : 'baby'})
1305  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
1306
1307#----------------------------------------------------------------------
1308
1309def depthy_wc_proplist(sbox):
1310  """test proplist at various depths on a wc"""
1311  # Bootstrap.
1312  sbox.build()
1313  wc_dir = sbox.wc_dir
1314
1315  # Set up properties.
1316  sbox.simple_propset('p', 'prop1', '')
1317  sbox.simple_propset('p', 'prop2', 'iota')
1318  sbox.simple_propset('p', 'prop3', 'A')
1319  sbox.simple_propset('p', 'prop4', 'A/mu')
1320
1321  # Commit.
1322  sbox.simple_commit()
1323
1324  # Test depth-empty proplist.
1325  exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1326                                                   '--depth', 'empty',
1327                                                   '-v', wc_dir)
1328  verify_output([ 'prop1', 'p', 'Properties on ' ],
1329                output, errput)
1330  svntest.verify.verify_exit_code(None, exit_code, 0)
1331
1332  # Test depth-files proplist.
1333  exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1334                                                   '--depth', 'files',
1335                                                   '-v', wc_dir)
1336  verify_output([ 'prop1', 'prop2', 'p', 'p',
1337                  'Properties on ', 'Properties on ' ],
1338                output, errput)
1339  svntest.verify.verify_exit_code(None, exit_code, 0)
1340
1341  # Test depth-immediates proplist.
1342  exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '--depth',
1343                                                   'immediates', '-v', wc_dir)
1344  verify_output([ 'prop1', 'prop2', 'prop3' ] +
1345                ['p'] * 3 + ['Properties on '] * 3,
1346                output, errput)
1347  svntest.verify.verify_exit_code(None, exit_code, 0)
1348
1349  # Test depth-infinity proplist.
1350  exit_code, output, errput = svntest.main.run_svn(None, 'proplist', '--depth',
1351                                                   'infinity', '-v', wc_dir)
1352  verify_output([ 'prop1', 'prop2', 'prop3', 'prop4' ] +
1353                ['p'] * 4 + ['Properties on '] * 4,
1354                output, errput)
1355  svntest.verify.verify_exit_code(None, exit_code, 0)
1356
1357#----------------------------------------------------------------------
1358
1359def depthy_url_proplist(sbox):
1360  """test proplist at various depths on a url"""
1361  # Bootstrap.
1362  sbox.build()
1363  repo_url = sbox.repo_url
1364  wc_dir = sbox.wc_dir
1365
1366  # Set up properties.
1367  sbox.simple_propset('p', 'prop1', '')
1368  sbox.simple_propset('p', 'prop2', 'iota')
1369  sbox.simple_propset('p', 'prop3', 'A')
1370  sbox.simple_propset('p', 'prop4', 'A/mu')
1371  sbox.simple_commit()
1372
1373  # Test depth-empty proplist.
1374  exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1375                                                   '--depth', 'empty',
1376                                                   '-v', repo_url)
1377  verify_output([ 'prop1', 'p', 'Properties on '],
1378                output, errput)
1379  svntest.verify.verify_exit_code(None, exit_code, 0)
1380
1381  # Test depth-files proplist.
1382  exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1383                                                   '--depth', 'files',
1384                                                   '-v', repo_url)
1385  verify_output([ 'prop1', 'prop2', 'p', 'p',
1386                 'Properties on ', 'Properties on ' ],
1387                output, errput)
1388  svntest.verify.verify_exit_code(None, exit_code, 0)
1389
1390  # Test depth-immediates proplist.
1391  exit_code, output, errput = svntest.main.run_svn(None, 'proplist',
1392                                                   '--depth', 'immediates',
1393                                                   '-v', repo_url)
1394
1395  verify_output([ 'prop1', 'prop2', 'prop3' ] + ['p'] * 3 +
1396                ['Properties on '] * 3,
1397                output, errput)
1398  svntest.verify.verify_exit_code(None, exit_code, 0)
1399
1400  # Test depth-infinity proplist.
1401  exit_code, output, errput = svntest.main.run_svn(None,
1402                                                   'proplist', '--depth',
1403                                                   'infinity', '-v', repo_url)
1404  verify_output([ 'prop1', 'prop2', 'prop3', 'prop4' ] + ['p'] * 4 +
1405                ['Properties on '] * 4,
1406                output, errput)
1407  svntest.verify.verify_exit_code(None, exit_code, 0)
1408
1409#----------------------------------------------------------------------
1410
1411def invalid_propnames(sbox):
1412  """test prop* handle invalid property names"""
1413  # Bootstrap.
1414  sbox.build()
1415  repo_url = sbox.repo_url
1416  wc_dir = sbox.wc_dir
1417  cwd = os.getcwd()
1418  os.chdir(wc_dir)
1419
1420  propname = chr(8)
1421  propval = 'foo'
1422
1423  expected_stdout = (".*Attempting to delete nonexistent property "
1424                     "'%s'.*" % (propname,))
1425  svntest.actions.run_and_verify_svn(expected_stdout, [],
1426                                     'propdel', propname)
1427  expected_stderr = (".*'%s' is not a valid Subversion"
1428                     ' property name' % (propname,))
1429  svntest.actions.run_and_verify_svn(None, expected_stderr,
1430                                     'propedit', propname)
1431  svntest.actions.run_and_verify_svn(None, expected_stderr,
1432                                     'propget', propname)
1433  svntest.actions.run_and_verify_svn(None, expected_stderr,
1434                                     'propset', propname, propval)
1435
1436  svntest.actions.run_and_verify_svn(None, expected_stderr,
1437                                     'commit', '--with-revprop',
1438                                     '='.join([propname, propval]))
1439  # Now swap them: --with-revprop should accept propname as a property
1440  # value; no concept of validity there.
1441  svntest.actions.run_and_verify_svn([], [],
1442                                     'commit', '--with-revprop',
1443                                     '='.join([propval, propname]))
1444
1445  os.chdir(cwd)
1446
1447@SkipUnless(svntest.main.is_posix_os)
1448@Issue(2581)
1449def perms_on_symlink(sbox):
1450  "propset shouldn't touch symlink perms"
1451  sbox.build()
1452  # We can't just run commands on absolute paths in the usual way
1453  # (e.g., os.path.join(sbox.wc_dir, 'newdir')), because for some
1454  # reason, if the symlink points to newdir as an absolute path, the
1455  # bug doesn't reproduce.  I have no idea why.  Since it does have to
1456  # point to newdir, the only other choice is to have it point to it
1457  # in the same directory, so we have to run the test from inside the
1458  # working copy.
1459  saved_cwd = os.getcwd()
1460  os.chdir(sbox.wc_dir)
1461  try:
1462    svntest.actions.run_and_verify_svn(None, [], 'mkdir', 'newdir')
1463    os.symlink('newdir', 'symlink')
1464    svntest.actions.run_and_verify_svn(None, [], 'add', 'symlink')
1465    old_mode = os.stat('newdir')[stat.ST_MODE]
1466    # The only property on 'symlink' is svn:special, so attempting to remove
1467    # 'svn:executable' should result in an error
1468    expected_stdout = (".*Attempting to delete nonexistent property "
1469                       "'svn:executable'.*")
1470    svntest.actions.run_and_verify_svn(expected_stdout, [], 'propdel',
1471                                     'svn:executable', 'symlink')
1472    new_mode = os.stat('newdir')[stat.ST_MODE]
1473    if not old_mode == new_mode:
1474      # Chmod newdir back, so the test suite can remove this working
1475      # copy when cleaning up later.
1476      os.chmod('newdir', stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
1477      raise svntest.Failure
1478  finally:
1479    os.chdir(saved_cwd)
1480
1481# Use a property with a custom namespace, ie 'ns:prop' or 'mycompany:prop'.
1482def remove_custom_ns_props(sbox):
1483  "remove a property with a custom namespace"
1484
1485  # Bootstrap
1486  sbox.build()
1487  wc_dir = sbox.wc_dir
1488
1489  # Add a property to a file
1490  sbox.simple_propset('ns:cash-sound', 'cha-ching!', 'iota')
1491
1492  # Commit the file
1493  sbox.simple_commit('iota')
1494
1495  # Now, make a backup copy of the working copy
1496  wc_backup = sbox.add_wc_path('backup')
1497  svntest.actions.duplicate_dir(wc_dir, wc_backup)
1498
1499  # Remove the property
1500  sbox.simple_propdel('ns:cash-sound', 'iota')
1501
1502  # Create expected trees.
1503  expected_output = svntest.wc.State(wc_dir, {
1504    'iota' : Item(verb='Sending'),
1505    })
1506  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1507  expected_status.tweak('iota', wc_rev=3, status='  ')
1508
1509  # Commit the one file.
1510  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
1511                                        expected_status)
1512
1513  # Create expected trees for the update.
1514  expected_output = svntest.wc.State(wc_backup, {
1515    'iota' : Item(status=' U'),
1516    })
1517  expected_disk = svntest.main.greek_state.copy()
1518  expected_status = svntest.actions.get_virginal_state(wc_backup, 3)
1519  expected_status.tweak('iota', wc_rev=3, status='  ')
1520
1521  # Do the update and check the results in three ways... INCLUDING PROPS
1522  svntest.actions.run_and_verify_update(wc_backup,
1523                                        expected_output,
1524                                        expected_disk,
1525                                        expected_status,
1526                                        check_props=True)
1527
1528def props_over_time(sbox):
1529  "property retrieval with peg and operative revs"
1530
1531  # Bootstrap
1532  sbox.build()
1533  wc_dir = sbox.wc_dir
1534
1535  # Convenience variables
1536  iota_path = sbox.ospath('iota')
1537  iota_url = sbox.repo_url + '/iota'
1538
1539  # Add/tweak a property 'revision' with value revision-committed to a
1540  # file, commit, and then repeat this a few times.
1541  for rev in range(2, 4):
1542    sbox.simple_propset('revision', str(rev), 'iota')
1543    sbox.simple_commit('iota')
1544
1545  # Backdate to r2 so the defaults for URL- vs. WC-style queries are
1546  # different.
1547  svntest.main.run_svn(None, 'up', '-r2', wc_dir)
1548
1549  # Now, test propget of the property across many combinations of
1550  # pegrevs, operative revs, and wc-path vs. url style input specs.
1551  # NOTE: We're using 0 in these loops to mean "unspecified".
1552  for path in iota_path, iota_url:
1553    for peg_rev in range(0, 4):
1554      for op_rev in range(0, 4):
1555        # Calculate the expected property value.  If there is an
1556        # operative rev, we expect the output to match revisions
1557        # there.  Else, we'll be looking at the peg-rev value.  And if
1558        # neither are supplied, it depends on the path vs. URL
1559        # question.
1560        if op_rev > 1:
1561          expected = str(op_rev)
1562        elif op_rev == 1:
1563          expected = None
1564        else:
1565          if peg_rev > 1:
1566            expected = str(peg_rev)
1567          elif peg_rev == 1:
1568            expected = None
1569          else:
1570            if path == iota_url:
1571              expected = "3" # HEAD
1572            else:
1573              expected = "2" # BASE
1574
1575        peg_path = path + (peg_rev != 0 and '@' + str(peg_rev) or "")
1576
1577        ### Test 'svn propget'
1578        pget_expected = expected
1579        if pget_expected:
1580          pget_expected = [ pget_expected + "\n" ]
1581        expected_err = [] if expected else '.*W200017: Property.*not found.*'
1582        if op_rev != 0:
1583          svntest.actions.run_and_verify_svn(pget_expected, expected_err,
1584                                             'propget', 'revision', peg_path,
1585                                             '-r', str(op_rev))
1586        else:
1587          svntest.actions.run_and_verify_svn(pget_expected, expected_err,
1588                                             'propget', 'revision', peg_path)
1589
1590        ### Test 'svn proplist -v'
1591        if op_rev != 0 or peg_rev != 0:  # a revision-ful query output URLs
1592          path = iota_url
1593        plist_expected = expected
1594        if plist_expected:
1595          plist_expected = [ "Properties on '" + path + "':\n",
1596                             "  revision\n",
1597                             "    " + expected + "\n" ]
1598
1599        if op_rev != 0:
1600          svntest.actions.run_and_verify_svn(plist_expected, [],
1601                                             'proplist', '-v', peg_path,
1602                                             '-r', str(op_rev))
1603        else:
1604          svntest.actions.run_and_verify_svn(plist_expected, [],
1605                                             'proplist', '-v', peg_path)
1606
1607
1608# XFail the same reason revprop_change() is.
1609@SkipUnless(svntest.main.server_enforces_date_syntax)
1610@Issue(3086)
1611def invalid_propvalues(sbox):
1612  "test handling invalid svn:* property values"
1613
1614  sbox.build(create_wc = False)
1615  repo_dir = sbox.repo_dir
1616  repo_url = sbox.repo_url
1617
1618  svntest.actions.enable_revprop_changes(repo_dir)
1619
1620  expected_stderr = '.*unexpected property value.*|.*Bogus date.*'
1621  svntest.actions.run_and_verify_svn([], expected_stderr,
1622                                     'propset', '--revprop', '-r', '0',
1623                                     'svn:date', 'Sat May 10 12:12:31 2008',
1624                                     repo_url)
1625
1626@Issue(3282)
1627def same_replacement_props(sbox):
1628  "commit replacement props when same as old props"
1629  # issue #3282
1630  sbox.build()
1631
1632  foo_path = sbox.ospath('foo')
1633
1634  open(foo_path, 'w').close()
1635  sbox.simple_add('foo')
1636  sbox.simple_propset('someprop', 'someval', 'foo')
1637  sbox.simple_commit('foo')
1638  sbox.simple_rm('foo')
1639
1640  # Now replace 'foo'.
1641  open(foo_path, 'w').close()
1642  sbox.simple_add('foo')
1643
1644  # Set the same property again, with the same value.
1645  sbox.simple_propset('someprop', 'someval', 'foo')
1646  sbox.simple_commit('foo')
1647
1648  # Check if the property made it into the repository.
1649  foo_url = sbox.repo_url + '/foo'
1650  expected_out = [ "Properties on '" + foo_url + "':\n",
1651                   "  someprop\n",
1652                   "    someval\n" ]
1653  svntest.actions.run_and_verify_svn(expected_out, [],
1654                                     'proplist', '-v', foo_url)
1655
1656def added_moved_file(sbox):
1657  "'svn mv added_file' preserves props"
1658
1659  sbox.build()
1660  wc_dir = sbox.wc_dir
1661
1662  # create it
1663  foo_path = sbox.ospath('foo')
1664  foo2_path = sbox.ospath('foo2')
1665  foo2_url = sbox.repo_url + '/foo2'
1666
1667  open(foo_path, 'w').close()
1668
1669  # add it
1670  sbox.simple_add('foo')
1671  sbox.simple_propset('someprop', 'someval', 'foo')
1672
1673  # move it
1674  svntest.main.run_svn(None, 'mv', foo_path, foo2_path)
1675
1676  # should still have the property
1677  svntest.actions.check_prop('someprop', foo2_path, [b'someval'])
1678
1679  # the property should get committed, too
1680  sbox.simple_commit()
1681  svntest.actions.check_prop('someprop', foo2_url, [b'someval'])
1682
1683
1684# Issue 2220, deleting a non-existent property should error
1685@Issue(2220)
1686def delete_nonexistent_property(sbox):
1687  "remove a property which doesn't exist"
1688
1689  # Bootstrap
1690  sbox.build()
1691  wc_dir = sbox.wc_dir
1692
1693  # Remove one property
1694  expected_stdout = ".*Attempting to delete nonexistent property 'yellow'.*"
1695  svntest.actions.run_and_verify_svn(expected_stdout, [],
1696                                     'propdel', 'yellow',
1697                                     os.path.join(wc_dir, 'A', 'D', 'G'))
1698
1699#----------------------------------------------------------------------
1700@Issue(3553)
1701def post_revprop_change_hook(sbox):
1702  "post-revprop-change hook"
1703
1704  sbox.build()
1705  wc_dir = sbox.wc_dir
1706  repo_dir = sbox.repo_dir
1707
1708  # Include a non-XML-safe message to regression-test issue #3553.
1709  error_msg = 'Text with <angle brackets> & ampersand'
1710
1711  svntest.actions.enable_revprop_changes(repo_dir)
1712  svntest.actions.create_failing_hook(repo_dir, 'post-revprop-change',
1713                                      error_msg)
1714
1715  # serf/mod_dav_svn give SVN_ERR_RA_DAV_PROPPATCH_FAILED
1716  # file/svn give SVN_ERR_REPOS_HOOK_FAILURE
1717  expected_error = 'svn: (E175008|E165001).*post-revprop-change hook failed'
1718
1719  svntest.actions.run_and_verify_svn([], expected_error,
1720                                     'ps', '--revprop', '-r0', 'p', 'v',
1721                                     wc_dir)
1722
1723  # Verify change has stuck -- at one time mod_dav_svn would rollback
1724  # revprop changes on post-revprop-change hook errors
1725  svntest.actions.run_and_verify_svn('v', [],
1726                                     'pg', '--revprop', '-r0', 'p',
1727                                     wc_dir)
1728
1729def rm_of_replaced_file(sbox):
1730  """properties after a removal of a replaced file"""
1731
1732  sbox.build()
1733  wc_dir = sbox.wc_dir
1734
1735  # Add some properties to iota and mu
1736  iota_path = sbox.ospath('iota')
1737  sbox.simple_propset('red', 'rojo', 'iota')
1738  sbox.simple_propset('blue', 'lagoon', 'iota')
1739
1740  mu_path = sbox.ospath('A/mu')
1741  sbox.simple_propset('yellow', 'submarine', 'A/mu')
1742  sbox.simple_propset('orange', 'toothpick', 'A/mu')
1743
1744  sbox.simple_commit()
1745
1746  # Copy iota over the top of mu
1747  sbox.simple_rm('A/mu')
1748  svntest.main.run_svn(None, 'cp', iota_path, mu_path)
1749
1750  expected_disk = svntest.main.greek_state.copy()
1751  expected_disk.tweak('iota', props={'red': 'rojo', 'blue': 'lagoon'})
1752  expected_disk.tweak('A/mu', props={'red': 'rojo', 'blue': 'lagoon'},
1753                      contents="This is the file 'iota'.\n")
1754  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
1755
1756  # Remove the copy. This should leave the original locally-deleted mu,
1757  # which should have no properties.
1758  svntest.main.run_svn(None, 'rm', '--force', mu_path)
1759
1760  svntest.actions.run_and_verify_svn(
1761                        [],
1762                        'svn: E200009.*some targets are not versioned.*',
1763                        'proplist', '-v', mu_path)
1764
1765  # Run it again, but ask for the pristine properties, which should
1766  # be mu's original props.
1767  exit_code, output, errput = svntest.main.run_svn(None,
1768                                                   'proplist', '-v',
1769                                                   mu_path + '@base')
1770  expected_output = svntest.verify.UnorderedRegexListOutput([
1771      'Properties on.*',
1772      '  yellow',
1773      '    submarine',
1774      '  orange',
1775      '    toothpick',
1776      ])
1777  svntest.verify.compare_and_display_lines('message', 'label',
1778                                           expected_output, output)
1779  svntest.verify.verify_exit_code(None, exit_code, 0)
1780
1781
1782def prop_reject_grind(sbox):
1783  """grind through all variants of prop rejects"""
1784
1785  sbox.build()
1786  wc_dir = sbox.wc_dir
1787
1788  mu_path = sbox.ospath('A/mu')
1789  mu_prej_path = sbox.ospath('A/mu.prej')
1790
1791  # Create r2 with all the properties we intend to use as incoming-change,
1792  # and as incoming-delete. Also set up our local-edit and local-delete
1793  # properties. We also need some properties that are simply different
1794  # from the incoming properties
1795  sbox.simple_propset('edit.diff', 'repos', 'iota')
1796  sbox.simple_propset('edit.edit', 'repos', 'iota')
1797  sbox.simple_propset('edit.del', 'repos', 'iota')
1798  sbox.simple_propset('edit.add', 'repos', 'iota')
1799  sbox.simple_propset('edit.none', 'repos', 'iota')
1800  sbox.simple_propset('del.edit', 'repos', 'iota')
1801  sbox.simple_propset('del.edit2', 'repos', 'iota')
1802  sbox.simple_propset('del.diff', 'repos', 'iota')
1803  sbox.simple_propset('del.del', 'repos', 'iota')
1804  sbox.simple_propset('del.add', 'repos', 'iota')
1805
1806  sbox.simple_propset('edit.edit', 'local', 'A/mu')
1807  sbox.simple_propset('add.edit', 'local', 'A/mu')
1808  sbox.simple_propset('del.edit', 'local', 'A/mu')
1809  sbox.simple_propset('del.edit2', 'repos', 'A/mu')
1810  sbox.simple_propset('add.del', 'local', 'A/mu')
1811  sbox.simple_propset('edit.del', 'local', 'A/mu')
1812  sbox.simple_propset('del.del', 'local', 'A/mu')
1813  sbox.simple_propset('edit.diff', 'local', 'A/mu')
1814  sbox.simple_propset('add.diff', 'local', 'A/mu')
1815  sbox.simple_propset('del.diff', 'local', 'A/mu')
1816
1817  sbox.simple_commit()
1818
1819  # Create r3 with all the properties that we intend to use as incoming-add,
1820  # and then perform the incoming-edits and incoming-deletes.
1821  sbox.simple_propset('add.add', 'repos', 'iota')
1822  sbox.simple_propset('add.edit', 'repos', 'iota')
1823  sbox.simple_propset('add.del', 'repos', 'iota')
1824  sbox.simple_propset('add.diff', 'repos', 'iota')
1825  sbox.simple_propset('edit.diff', 'repos.changed', 'iota')
1826  sbox.simple_propset('edit.edit', 'repos.changed', 'iota')
1827  sbox.simple_propset('edit.del', 'repos.changed', 'iota')
1828  sbox.simple_propset('edit.add', 'repos.changed', 'iota')
1829  sbox.simple_propset('edit.none', 'repos.changed', 'iota')
1830  sbox.simple_propdel('del.edit', 'iota')
1831  sbox.simple_propdel('del.edit2', 'iota')
1832  sbox.simple_propdel('del.diff', 'iota')
1833  sbox.simple_propdel('del.del', 'iota')
1834  sbox.simple_propdel('del.add', 'iota')
1835  sbox.simple_commit()
1836
1837  # Set up our victim for all the right rejects: local-adds, local-edits,
1838  # and local-deletes.
1839  sbox.simple_propset('edit.add', 'local', 'A/mu')
1840  sbox.simple_propset('add.add', 'local', 'A/mu')
1841  sbox.simple_propset('del.add', 'local', 'A/mu')
1842  sbox.simple_propset('edit.edit', 'local.changed', 'A/mu')
1843  sbox.simple_propset('add.edit', 'local.changed', 'A/mu')
1844  sbox.simple_propset('del.edit', 'local.changed', 'A/mu')
1845  sbox.simple_propset('del.edit2', 'repos.changed', 'A/mu')
1846  sbox.simple_propdel('add.del', 'A/mu')
1847  sbox.simple_propdel('edit.del', 'A/mu')
1848  sbox.simple_propdel('del.del', 'A/mu')
1849
1850  # Now merge r2:3 into the victim to create all variants
1851  svntest.main.run_svn(False, 'merge', '-r2:3', sbox.repo_url + '/iota',
1852                       mu_path)
1853
1854  # Check that A/mu.prej reports the expected conflicts:
1855  expected_prej = [
1856    "Trying to change property 'edit.none'\n"
1857    "but the property does not exist locally.\n"
1858    "<<<<<<< (local property value)\n"
1859    "||||||| (incoming 'changed from' value)\n"
1860    "repos=======\n"
1861    "repos.changed>>>>>>> (incoming 'changed to' value)\n",
1862
1863    "Trying to delete property 'del.del'\n"
1864    "but the property has been locally deleted and had a different value.\n",
1865
1866    "Trying to delete property 'del.edit'\n"
1867    "but the local property value is different.\n"
1868    "<<<<<<< (local property value)\n"
1869    "local.changed||||||| (incoming 'changed from' value)\n"
1870    "repos=======\n"
1871    ">>>>>>> (incoming 'changed to' value)\n",
1872
1873    "Trying to change property 'edit.del'\n"
1874    "but the property has been locally deleted.\n"
1875    "<<<<<<< (local property value)\n"
1876    "||||||| (incoming 'changed from' value)\n"
1877    "repos=======\n"
1878    "repos.changed>>>>>>> (incoming 'changed to' value)\n",
1879
1880    "Trying to change property 'edit.edit'\n"
1881    "but the property has already been locally changed to a different value.\n"
1882    "<<<<<<< (local property value)\n"
1883    "local.changed||||||| (incoming 'changed from' value)\n"
1884    "repos=======\n"
1885    "repos.changed>>>>>>> (incoming 'changed to' value)\n",
1886
1887    "Trying to delete property 'del.edit2'\n"
1888    "but the property has been locally modified.\n"
1889    "<<<<<<< (local property value)\n"
1890    "repos.changed||||||| (incoming 'changed from' value)\n"
1891    "repos=======\n"
1892    ">>>>>>> (incoming 'changed to' value)\n",
1893
1894    "Trying to delete property 'del.add'\n"
1895    "but the property has been locally added.\n"
1896    "<<<<<<< (local property value)\n"
1897    "local||||||| (incoming 'changed from' value)\n"
1898    "repos=======\n"
1899    ">>>>>>> (incoming 'changed to' value)\n",
1900
1901    "Trying to delete property 'del.diff'\n"
1902    "but the local property value is different.\n"
1903    "<<<<<<< (local property value)\n"
1904    "local||||||| (incoming 'changed from' value)\n"
1905    "repos=======\n"
1906    ">>>>>>> (incoming 'changed to' value)\n",
1907
1908    "Trying to change property 'edit.add'\n"
1909    "but the property has been locally added with a different value.\n"
1910    "<<<<<<< (local property value)\n"
1911    "local||||||| (incoming 'changed from' value)\n"
1912    "repos=======\n"
1913    "repos.changed>>>>>>> (incoming 'changed to' value)\n",
1914
1915    "Trying to change property 'edit.diff'\n"
1916    "but the local property value conflicts with the incoming change.\n"
1917    "<<<<<<< (local property value)\n"
1918    "local||||||| (incoming 'changed from' value)\n"
1919    "repos=======\n"
1920    "repos.changed>>>>>>> (incoming 'changed to' value)\n",
1921
1922    "Trying to add new property 'add.add'\n"
1923    "but the property already exists.\n"
1924    "<<<<<<< (local property value)\n"
1925    "local||||||| (incoming 'changed from' value)\n"
1926    "=======\n"
1927    "repos>>>>>>> (incoming 'changed to' value)\n",
1928
1929    "Trying to add new property 'add.diff'\n"
1930    "but the property already exists.\n"
1931    "<<<<<<< (local property value)\n"
1932    "local||||||| (incoming 'changed from' value)\n"
1933    "=======\n"
1934    "repos>>>>>>> (incoming 'changed to' value)\n",
1935
1936    "Trying to add new property 'add.del'\n"
1937    "but the property has been locally deleted.\n"
1938    "Incoming property value:\n"
1939    "repos\n",
1940
1941    "Trying to add new property 'add.edit'\n"
1942    "but the property already exists.\n"
1943    "<<<<<<< (local property value)\n"
1944    "local.changed||||||| (incoming 'changed from' value)\n"
1945    "=======\n"
1946    "repos>>>>>>> (incoming 'changed to' value)\n",
1947  ]
1948
1949  # Get the contents of mu.prej.  The error messages are in the prej file
1950  # but there is no guarantee as to order. So try to locate each message
1951  # in the file individually.
1952  prej_file = open(mu_prej_path, 'r')
1953  n = 0
1954  for message in expected_prej:
1955    prej_file.seek(0)
1956    match = False
1957    i = 0
1958    j = 0
1959    msg_lines = message.split('\n')
1960    for file_line in prej_file:
1961      line = msg_lines[i] + '\n'
1962      match = (line == file_line)
1963      if match:
1964        # The last line in the list is always an empty string.
1965        if msg_lines[i + 1] == "":
1966          #logger.info("found message %i in file at line %i" % (n, j))
1967          break
1968        i += 1
1969      else:
1970        i = 0
1971      j += 1
1972    n += 1
1973    if not match:
1974      raise svntest.main.SVNUnmatchedError(
1975              "Expected mu.prej doesn't match actual mu.prej")
1976
1977def obstructed_subdirs(sbox):
1978  """test properties of obstructed subdirectories"""
1979
1980  sbox.build()
1981  wc_dir = sbox.wc_dir
1982
1983  # at one point during development, obstructed subdirectories threw
1984  # errors trying to fetch property information during 'svn status'.
1985  # this test ensures we won't run into that problem again.
1986
1987  C_path = sbox.ospath('A/C')
1988  sbox.simple_propset('red', 'blue', 'A/C')
1989
1990  expected_disk = svntest.main.greek_state.copy()
1991  expected_disk.tweak('A/C', props={'red': 'blue'})
1992  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
1993
1994  # Remove the subdir from disk, and validate the status
1995  svntest.main.safe_rmtree(C_path)
1996
1997  expected_disk.remove('A/C')
1998  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
1999
2000  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2001  expected_status.tweak('A/C', status='!M', wc_rev='1')
2002
2003  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2004
2005  # Drop an empty file there to obstruct the now-deleted subdir
2006  open(C_path, 'w')
2007
2008  expected_disk.add({'A/C': Item(contents='', props={'red': 'blue'})})
2009  expected_status.tweak('A/C', status='~M', wc_rev='1')
2010
2011  svntest.actions.verify_disk(wc_dir, expected_disk.old_tree(), True)
2012
2013
2014  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2015
2016
2017def atomic_over_ra(sbox):
2018  "test revprop atomicity guarantees of libsvn_ra"
2019
2020  sbox.build(create_wc=False)
2021  repo_url = sbox.repo_url
2022
2023  # From this point on, similar to ../libsvn_fs/fs-test.c:revision_props().
2024  s1 = "violet"
2025  s2 = "wrong value"
2026
2027  # But test "" explicitly, since the RA layers have to marshal "" and <unset>
2028  # differently.
2029  s3 = ""
2030
2031  # Initial state.
2032  svntest.actions.enable_revprop_changes(sbox.repo_dir)
2033  svntest.actions.run_and_verify_svn(None, [],
2034                                     'propset', '--revprop', '-r', '0',
2035                                     'flower', s1, repo_url)
2036
2037  # Helpers.
2038
2039  def expect_old_server_fail(old_value, proposed_value):
2040    # We are setting a (possibly "not present") expectation for the old value,
2041    # so we should fail.
2042    expected_stderr = ".*doesn't advertise.*ATOMIC_REVPROP"
2043    svntest.actions.run_and_verify_atomic_ra_revprop_change(
2044       None, expected_stderr, 1, repo_url, 0, 'flower',
2045       old_value, proposed_value, True)
2046
2047    # The original value is still there.
2048    svntest.actions.check_prop('flower', repo_url, [s1], 0)
2049
2050  def FAILS_WITH_BPV(not_the_old_value, proposed_value):
2051    if svntest.main.server_has_atomic_revprop():
2052      svntest.actions.run_and_verify_atomic_ra_revprop_change(
2053         None, [], 0, repo_url, 0, 'flower',
2054         not_the_old_value, proposed_value, True)
2055    else:
2056      expect_old_server_fail(not_the_old_value, proposed_value)
2057
2058  def PASSES_WITHOUT_BPV(yes_the_old_value, proposed_value):
2059    if svntest.main.server_has_atomic_revprop():
2060      svntest.actions.run_and_verify_atomic_ra_revprop_change(
2061         None, [], 0, repo_url, 0, 'flower',
2062         yes_the_old_value, proposed_value, False)
2063    else:
2064      expect_old_server_fail(yes_the_old_value, proposed_value)
2065
2066  # Value of "flower" is 's1'.
2067  FAILS_WITH_BPV(s2, s1)
2068  FAILS_WITH_BPV(s3, s1)
2069  PASSES_WITHOUT_BPV(s1, s2)
2070
2071  # Value of "flower" is 's2'.
2072  PASSES_WITHOUT_BPV(s2, s3)
2073
2074  # Value of "flower" is 's3'.
2075  FAILS_WITH_BPV(None, s3)
2076  FAILS_WITH_BPV(s1, s3)
2077  PASSES_WITHOUT_BPV(s3, s2)
2078
2079  # Value of "flower" is 's2'.
2080  FAILS_WITH_BPV(None, None)
2081  FAILS_WITH_BPV(s1, None)
2082  FAILS_WITH_BPV(s3, None)
2083  PASSES_WITHOUT_BPV(s2, None)
2084
2085  # Value of "flower" is <not set>.
2086  FAILS_WITH_BPV(s2, s1)
2087  FAILS_WITH_BPV(s3, s1)
2088  PASSES_WITHOUT_BPV(None, s1)
2089
2090  # Value of "flower" is 's1'.
2091  svntest.actions.check_prop('flower', repo_url, [s1.encode()], 0)
2092
2093# Test for issue #3721 'redirection of svn propget output corrupted with
2094# large property values'
2095@Issue(3721)
2096def propget_redirection(sbox):
2097  """pg of large text properties redirects properly"""
2098
2099  sbox.build()
2100  wc_dir = sbox.wc_dir
2101
2102  # Some paths we'll care about
2103  B_path = os.path.join(wc_dir, "A", "B")
2104  C_path = os.path.join(wc_dir, "A", "C")
2105  D_path = os.path.join(wc_dir, "A", "D")
2106
2107  prop_val_file = os.path.join(wc_dir, "prop_val")
2108  redirect_file = os.path.join(wc_dir, "pg.vR.out")
2109
2110  # A 'big' mergeinfo property.  Yes, it is bogus in the sense that
2111  # it refers to non-existent path-revs, but that is not relevant to
2112  # this test.  What matters is that it is a realistic 'big' mergeinfo
2113  # value (it is from Subversion's own 1.6.x branch in fact).
2114  # Also, the syntax is wrong: every path should start with '/';
2115  # Subversion currently silently corrects this.
2116  big_prop_val = "subversion/branches/1.5.x:872364-874936\n"           + \
2117  "subversion/branches/1.5.x-34184:874657-874741\n"                    + \
2118  "subversion/branches/1.5.x-34432:874744-874798\n"                    + \
2119  "subversion/branches/1.5.x-issue3067:872184-872314\n"                + \
2120  "subversion/branches/1.5.x-issue3157:872165-872175\n"                + \
2121  "subversion/branches/1.5.x-issue3174:872178-872348\n"                + \
2122  "subversion/branches/1.5.x-r30215:870310,870312,870319,870362\n"     + \
2123  "subversion/branches/1.5.x-r30756:874853-874870\n"                   + \
2124  "subversion/branches/1.5.x-r30868:870951-870970\n"                   + \
2125  "subversion/branches/1.5.x-r31314:874476-874605\n"                   + \
2126  "subversion/branches/1.5.x-r31516:871592-871649\n"                   + \
2127  "subversion/branches/1.5.x-r32470:872546-872676\n"                   + \
2128  "subversion/branches/1.5.x-r32968:873773-873872\n"                   + \
2129  "subversion/branches/1.5.x-r33447:873527-873547\n"                   + \
2130  "subversion/branches/1.5.x-r33465:873541-873549\n"                   + \
2131  "subversion/branches/1.5.x-r33641:873880-873883\n"                   + \
2132  "subversion/branches/1.5.x-r34050-followups:874639-874686\n"         + \
2133  "subversion/branches/1.5.x-r34487:874562-874581\n"                   + \
2134  "subversion/branches/1.5.x-ra_serf-backports:872354-872626\n"        + \
2135  "subversion/branches/1.5.x-rb-test-fix:874916-874919\n"              + \
2136  "subversion/branches/1.5.x-reintegrate-improvements:874586-874922\n" + \
2137  "subversion/branches/1.5.x-tests-pass:870925-870973\n"               + \
2138  "subversion/branches/dont-save-plaintext-passwords-by-default:"      + \
2139  "870728-871118\n"                                                    + \
2140  "subversion/branches/gnome-keyring:870558-871410\n"                  + \
2141  "subversion/branches/issue-3220-dev:872210-872226\n"                 + \
2142  "subversion/branches/kwallet:870785-871314\n"                        + \
2143  "subversion/branches/log-g-performance:870941-871032\n"              + \
2144  "subversion/branches/r30963-1.5.x:871056-871076\n"                   + \
2145  "subversion/branches/reintegrate-improvements:873853-874164\n"       + \
2146  "subversion/branches/svn-mergeinfo-enhancements:870196\n"            + \
2147  "subversion/branches/svnpatch-diff:871905\n"                         + \
2148  "subversion/trunk:869159-869165,869168-869181,869185,869188,869191," + \
2149  "869200-869201,869203-869207,869209-869224,869227-869238,869240-"    + \
2150  "869244,869248,869250-869260,869262-869263,869265,869267-869268,"    + \
2151  "869272-869280,869282-869325,869328-869330,869335,869341-869347,"    + \
2152  "869351,869354-869355,869358,869361-869377,869379-869381,869383-"    + \
2153  "869417,869419-869422,869432-869453,869455-869466,869471-869473,"    + \
2154  "869475,869483,869486,869488-869489,869491-869497,869499-869500,"    + \
2155  "869503,869506-869508,869510-869521,869523-869540,869542-869552,"    + \
2156  "869556,869558,869560-869561,869563,869565,869567,869570,869572,"    + \
2157  "869582,869601-869602,869605,869607,869613-869614,869616,869618,"    + \
2158  "869620,869625,869627,869630,869633,869639,869641-869643,869645-"    + \
2159  "869652,869655,869657,869665,869668,869674,869677,869681,869685,"    + \
2160  "869687-869688,869693,869697,869699-869700,869704-869708,869716,"    + \
2161  "869719,869722,869724,869730,869733-869734,869737-869740,869745-"    + \
2162  "869746,869751-869754,869766,869812-869813,869815-869818,869820,"    + \
2163  "869825,869837,869841,869843-869844,869858,869860-869861,869871,"    + \
2164  "869875,869889,869895,869898,869902,869907,869909,869926,869928-"    + \
2165  "869929,869931-869933,869942-869943,869950,869952,869957-869958,"    + \
2166  "869969,869972,869974,869988,869994,869996,869999,870004,870013-"    + \
2167  "870014,870016,870024,870032,870036,870039,870041-870043,870054,"    + \
2168  "870060,870068-870071,870078,870083,870094,870104,870124,870127-"    + \
2169  "870128,870133,870135-870136,870141,870144,870148,870160,870172,"    + \
2170  "870175,870191,870198,870203-870204,870211,870219,870225,870233,"    + \
2171  "870235-870236,870254-870255,870259,870307,870311,870313,870320,"    + \
2172  "870323,870330-870331,870352-870353,870355,870359-870360,870371,"    + \
2173  "870373,870378,870393-870395,870402,870409-870410,870414,870416,"    + \
2174  "870421,870436,870442,870447,870449,870452,870454,870466,870476,"    + \
2175  "870481-870483,870486,870500,870502,870505,870513-870518,870522-"    + \
2176  "870523,870527,870529,870534,870536-870538,870540-870541,870543-"    + \
2177  "870548,870554,870556,870561,870563,870584,870590-870592,870594-"    + \
2178  "870595,870597,870618,870620,870622,870625-870626,870641,870647,"    + \
2179  "870657,870665,870671,870681,870702-870703,870706-870708,870717-"    + \
2180  "870718,870727,870730,870737,870740,870742,870752,870758,870800,"    + \
2181  "870809,870815,870817,870820-870825,870830,870834-870836,870850-"    + \
2182  "870851,870853,870859,870861,870886,870894,870916-870918,870942,"    + \
2183  "870945,870957,870962,870970,870979,870981,870989,870996,871003,"    + \
2184  "871005,871009,871011,871023,871033,871035-871038,871041,871060,"    + \
2185  "871078,871080,871092,871097,871099,871105,871107,871120,871123-"    + \
2186  "871127,871130,871133-871135,871140,871149,871155-871156,871160,"    + \
2187  "871162,871164,871181,871191,871199-871200,871205,871211-871212,"    + \
2188  "871215,871219,871225,871227,871229,871231,871236,871270,871273,"    + \
2189  "871277,871283,871297,871302,871306,871308,871315-871320,871323-"    + \
2190  "871325,871333-871335,871345,871347-871350,871354,871357,871361,"    + \
2191  "871363-871366,871374-871375,871377,871382,871385-871388,871391,"    + \
2192  "871408,871411,871422,871435,871441,871443-871444,871465,871470,"    + \
2193  "871472-871476,871481,871489,871499,871501-871502,871505,871508,"    + \
2194  "871520,871523,871525-871527,871538,871542,871544,871547-871549,"    + \
2195  "871556,871559,871562-871563,871578,871581,871587,871589-871597,"    + \
2196  "871608,871613,871616-871617,871620,871624,871649,871668,871675,"    + \
2197  "871677,871693-871694,871696,871704,871732-871733,871744,871747,"    + \
2198  "871759,871762,871766,871769,871793,871796,871799,871801,871811,"    + \
2199  "871813,871821-871826,871831,871843,871860,871880,871891,871894,"    + \
2200  "871899,871907,871911,871926,871928,871933,871935,871941-871942,"    + \
2201  "871947-871949,871958,871974,872000-872001,872003,872005,872018,"    + \
2202  "872022,872038,872065,872068,872086,872091,872093,872097,872103,"    + \
2203  "872112,872130,872154,872157,872206,872216,872218-872219,872227,"    + \
2204  "872234,872238,872243,872253,872255,872259,872261,872278-872279,"    + \
2205  "872281,872310-872311,872362,872404,872416-872417,872429,872431,"    + \
2206  "872434,872439,872450-872453,872468,872470,872477-872478,872483,"    + \
2207  "872490-872491,872495,872515-872516,872518-872519,872537,872541,"    + \
2208  "872544,872565,872568,872571-872573,872584,872596-872597,872612,"    + \
2209  "872619,872624,872632,872656,872670,872706,872710,872713,872717,"    + \
2210  "872746-872748,872777,872780-872782,872791,872804,872813,872845,"    + \
2211  "872864,872870,872872,872947-872948,872961,872974,872981,872985-"    + \
2212  "872987,873004,873042,873049,873051,873076,873087,873090,873096,"    + \
2213  "873098,873100,873183,873186,873192,873195,873210-873211,873247,"    + \
2214  "873252,873256,873259,873275,873286,873288,873343,873379-873381,"    + \
2215  "873443,873521,873538-873539,873714-873715,873718,873733,873745,"    + \
2216  "873751,873767,873778,873781,873849,873856,873862,873914,873940,"    + \
2217  "873947-873948,873975-873976,873987,873998,874026-874027,874075,"    + \
2218  "874077-874078,874124-874125,874127,874156,874159,874161,874165,"    + \
2219  "874168,874170,874184,874189,874204,874223-874224,874245,874258,"    + \
2220  "874262,874270,874292-874297,874300-874301,874303,874305,874316-"    + \
2221  "874318,874330,874363,874380,874405,874421,874441,874459,874467,"    + \
2222  "874473,874497,874506,874545-874546,874561,874566,874568,874580,"    + \
2223  "874619,874621,874634,874636,874659,874673,874681,874727,874730,"    + \
2224  "874743,874765-874767,874806,874816,874848,874868,874888,874896,"    + \
2225  "874909,874912,874996,875051,875069,875129,875132,875134,875137,"    + \
2226  "875151-875153,875186-875188,875190,875235-875237,875242-875243,"    + \
2227  "875249,875388,875393,875406,875411\n"
2228
2229  # Set the 'big' mergeinfo prop on A/B, A/C, and A/D.
2230  svntest.main.file_write(prop_val_file, big_prop_val)
2231
2232  svntest.actions.run_and_verify_svn(None, [], 'propset',
2233                                     SVN_PROP_MERGEINFO, '-F', prop_val_file,
2234                                     B_path)
2235  svntest.actions.run_and_verify_svn(None, [], 'propset',
2236                                     SVN_PROP_MERGEINFO, '-F', prop_val_file,
2237                                     C_path)
2238  svntest.actions.run_and_verify_svn(None, [], 'propset',
2239                                     SVN_PROP_MERGEINFO, '-F', prop_val_file,
2240                                     D_path)
2241  svntest.actions.run_and_verify_svn(None, [], 'ci', '-m',
2242                                     'ps some large svn:mergeinfos', wc_dir)
2243
2244  # Run propget -vR svn:mergeinfo, redirecting the stdout to a file.
2245  arglist = [svntest.main.svn_binary, 'propget', SVN_PROP_MERGEINFO, '-vR',
2246             '--config-dir', svntest.main.default_config_dir, wc_dir]
2247  redir_file = open(redirect_file, 'wb')
2248  pg_proc = subprocess.Popen(arglist, stdout=redir_file)
2249  pg_proc.wait()
2250  redir_file.close()
2251  pg_stdout_redir = open(redirect_file, 'r').readlines()
2252
2253  # Check if the redirected output of svn pg -vR on the root of the WC
2254  # is what we expect.
2255  expected_mergeinfo_displayed = [
2256    '    /' + line for line in big_prop_val.splitlines(True) ]
2257  expected_output = [
2258    "Properties on '" + B_path +  "':\n", # Should ocur only once!
2259    "  svn:mergeinfo\n",
2260    ] + expected_mergeinfo_displayed + [
2261    "Properties on '" + C_path +  "':\n", # Should ocur only once!
2262    "  svn:mergeinfo\n",
2263    ] + expected_mergeinfo_displayed + [
2264    "Properties on '" + D_path +  "':\n", # Should ocur only once!
2265    "  svn:mergeinfo\n",
2266    ] + expected_mergeinfo_displayed
2267  svntest.verify.verify_outputs(
2268    "Redirected pg -vR doesn't match pg -vR stdout",
2269    pg_stdout_redir, None,
2270    svntest.verify.UnorderedOutput(expected_output), None)
2271  # (We want this check to fail if the redirected pg output contains
2272  # unexpected duplicate lines, although this hasn't been observed as
2273  # part of issue #3721.  We used to check separately here because the old
2274  # UnorderedOutput class ignored duplicates but now it detects them.)
2275
2276@Issue(3852)
2277def file_matching_dir_prop_reject(sbox):
2278  "prop conflict for file matching dir prop reject"
2279
2280  sbox.build()
2281  wc_dir = sbox.wc_dir
2282
2283  # Add file with awkward name
2284  svntest.main.file_append(sbox.ospath('A/dir_conflicts'), "some content\n")
2285  svntest.actions.run_and_verify_svn(None, [],
2286                                     'add', sbox.ospath('A/dir_conflicts'))
2287  sbox.simple_propset('prop', 'val1', 'A/dir_conflicts')
2288  sbox.simple_propset('prop', 'val1', 'A')
2289  expected_output = svntest.wc.State(wc_dir, {
2290      'A'               : Item(verb='Sending'),
2291      'A/dir_conflicts' : Item(verb='Adding'),
2292      })
2293  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2294  expected_status.tweak('A', wc_rev=2)
2295  expected_status.add({
2296    'A/dir_conflicts' : Item(status='  ', wc_rev=2),
2297      })
2298  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2299                                        expected_status)
2300
2301  # Modify/commit property change
2302  sbox.simple_propset('prop', 'val2', 'A/dir_conflicts')
2303  sbox.simple_propset('prop', 'val2', 'A')
2304  expected_output = svntest.wc.State(wc_dir, {
2305      'A'               : Item(verb='Sending'),
2306      'A/dir_conflicts' : Item(verb='Sending'),
2307      })
2308  expected_status.tweak('A', 'A/dir_conflicts', wc_rev=3)
2309  svntest.actions.run_and_verify_commit(wc_dir, expected_output,
2310                                        expected_status)
2311
2312  # Local property mod
2313  sbox.simple_propset('prop', 'val3', 'A/dir_conflicts')
2314  sbox.simple_propset('prop', 'val3', 'A')
2315
2316  # Update to trigger property conflicts
2317  expected_disk = svntest.main.greek_state.copy()
2318  expected_disk.add({
2319    'A/dir_conflicts' : Item('some content\n', props = {'prop' : 'val3'}),
2320    })
2321  expected_disk.tweak('A', props={'prop' : 'val3'})
2322  expected_output = svntest.wc.State(wc_dir, {
2323    'A'               : Item(status=' C'),
2324    'A/dir_conflicts' : Item(status=' C'),
2325    })
2326  expected_status.tweak(wc_rev=2)
2327  expected_status.tweak('A', 'A/dir_conflicts', status=' C')
2328
2329  # Conflict: BASE=val2 WORKING=val3 INCOMING_OLD=val2 INCOMING_NEW=val1
2330  extra_files = ['dir_conflicts.prej', 'dir_conflicts.2.prej']
2331  svntest.actions.run_and_verify_update(wc_dir,
2332                                        expected_output,
2333                                        expected_disk,
2334                                        expected_status,
2335                                        [], True,
2336                                        '-r', '2', wc_dir,
2337                                        extra_files=extra_files)
2338
2339  # Revert and update to check that conflict files are removed
2340  svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
2341  expected_status.tweak('A', 'A/dir_conflicts', status='  ')
2342  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2343
2344  expected_output = svntest.wc.State(wc_dir, {
2345    'A'               : Item(status=' U'),
2346    'A/dir_conflicts' : Item(status=' U'),
2347    })
2348  expected_disk.tweak('A', 'A/dir_conflicts', props={'prop' : 'val2'})
2349  expected_status.tweak(wc_rev=3)
2350  svntest.actions.run_and_verify_update(wc_dir,
2351                                        expected_output,
2352                                        expected_disk,
2353                                        expected_status,
2354                                        check_props=True)
2355
2356def pristine_props_listed(sbox):
2357  "check if pristine properties are visible"
2358
2359  sbox.build()
2360  wc_dir = sbox.wc_dir
2361
2362  sbox.simple_propset('prop', 'val', 'A')
2363  sbox.simple_commit()
2364
2365  expected_output = ["Properties on '" + sbox.ospath('A') + "':\n", "  prop\n"]
2366
2367  # Now we see the pristine properties
2368  svntest.actions.run_and_verify_svn(expected_output, [],
2369                                     'proplist', '-R', wc_dir, '-r', 'BASE')
2370
2371  sbox.simple_propset('prop', 'needs-fix', 'A')
2372
2373  # And now we see no property at all
2374  svntest.actions.run_and_verify_svn(expected_output, [],
2375                                     'proplist', '-R', wc_dir, '-r', 'BASE')
2376
2377def create_inherited_ignores_config(sbox):
2378  "create config stuffs for inherited ignores tests"
2379
2380  # contents of the file 'config'
2381  config_contents = '''\
2382[miscellany]
2383global-ignores = *.boo *.goo
2384'''
2385
2386  return sbox.create_config_dir(config_contents)
2387
2388def inheritable_ignores(sbox):
2389  "inheritable ignores with svn:ignores and config"
2390
2391  sbox.build()
2392  wc_dir = sbox.wc_dir
2393
2394  config_dir = create_inherited_ignores_config(sbox)
2395
2396  sbox.simple_propset(SVN_PROP_INHERITABLE_IGNORES, '*.doo', 'A/B')
2397  sbox.simple_propset(SVN_PROP_INHERITABLE_IGNORES, '*.moo', 'A/D')
2398  sbox.simple_propset('svn:ignore', '*.foo', 'A/B/E')
2399  sbox.simple_commit()
2400
2401  # Some directories and files that should always be added because they
2402  # don't match any applicable ignore patterns.
2403  X_dir_path = sbox.ospath('ADD-ME-DIR-X')
2404  Y_dir_path = sbox.ospath('A/ADD-ME-DIR-Y.doo')
2405  Z_dir_path = sbox.ospath('A/D/G/ADD-ME-DIR-Z.doo')
2406  os.mkdir(X_dir_path)
2407  os.mkdir(Y_dir_path)
2408  os.mkdir(Z_dir_path)
2409
2410  # Some directories and files that should be ignored when adding
2411  # because they match an ignore pattern (unless of course they are
2412  # the direct target of an add, which we always add).
2413  boo_dir_path = sbox.ospath('IGNORE-ME-DIR.boo')
2414  goo_dir_path = sbox.ospath('IGNORE-ME-DIR.boo/IGNORE-ME-DIR.goo')
2415  doo_dir_path = sbox.ospath('A/B/IGNORE-ME-DIR.doo')
2416  moo_dir_path = sbox.ospath('A/D/IGNORE-ME-DIR.moo')
2417  foo_dir_path = sbox.ospath('A/B/E/IGNORE-ME-DIR.foo')
2418  os.mkdir(boo_dir_path)
2419  os.mkdir(goo_dir_path)
2420  os.mkdir(doo_dir_path)
2421  os.mkdir(moo_dir_path)
2422  os.mkdir(foo_dir_path)
2423  boo_file_path = sbox.ospath('ADD-ME-DIR-X/ignore-me-file.boo')
2424  goo_file_path = sbox.ospath('A/D/G/ignore-me-file.goo')
2425  doo_file_path = sbox.ospath('A/B/IGNORE-ME-DIR.doo/ignore-me-file.doo')
2426  doo_file2_path = sbox.ospath('A/B/E/ignore-me-file.doo')
2427  moo_file_path = sbox.ospath('A/D/ignore-me-file.moo')
2428  foo_file_path = sbox.ospath('A/B/E/ignore-me-file.foo')
2429  svntest.main.file_write(boo_file_path, 'I should not be versioned!\n')
2430  svntest.main.file_write(goo_file_path, 'I should not be versioned!\n')
2431  svntest.main.file_write(doo_file_path, 'I should not be versioned!\n')
2432  svntest.main.file_write(doo_file2_path, 'I should not be versioned!\n')
2433  svntest.main.file_write(moo_file_path, 'I should not be versioned!\n')
2434  svntest.main.file_write(foo_file_path, 'I should not be versioned!\n')
2435
2436  # Some directories and files that don't match any ignore pattern
2437  # but are located within a subtree that does match and so shouldn't
2438  # be added.
2439  roo_file_path = sbox.ospath('A/B/IGNORE-ME-DIR.doo/ignore-me-file.roo')
2440  svntest.main.file_write(roo_file_path, 'I should not be versioned!\n')
2441
2442  # Check (non-verbose) status with the custom config. We should only see
2443  # the three unversioned directories which don't match any of the ignore
2444  # patterns and aren't proper subtrees of an unversioned or ignored
2445  # subtree.
2446  expected_output = svntest.verify.UnorderedOutput(
2447    ['?       ' + X_dir_path + '\n',
2448     '?       ' + Y_dir_path + '\n',
2449     '?       ' + Z_dir_path + '\n',])
2450  svntest.actions.run_and_verify_svn(expected_output, [], 'st',
2451                                     '--config-dir', config_dir, wc_dir)
2452
2453  # Check status without the custom config.
2454  # Should be the same as above except the *.boo and *.goo paths
2455  # now show up as unversioned '?'.
2456  expected_output = svntest.verify.UnorderedOutput(
2457    ['?       ' + X_dir_path    + '\n',
2458     '?       ' + Y_dir_path    + '\n',
2459     '?       ' + Z_dir_path    + '\n',
2460     '?       ' + boo_dir_path  + '\n',
2461     '?       ' + goo_file_path + '\n',])
2462  svntest.actions.run_and_verify_svn(expected_output, [], 'st', wc_dir)
2463
2464  # Check status with the custom config and --no-ignore.
2465  expected_output = svntest.verify.UnorderedOutput(
2466    ['?       ' + X_dir_path     + '\n',
2467     '?       ' + Y_dir_path     + '\n',
2468     '?       ' + Z_dir_path     + '\n',
2469     'I       ' + boo_dir_path   + '\n',
2470     'I       ' + doo_dir_path   + '\n',
2471     'I       ' + doo_file2_path + '\n',
2472     'I       ' + moo_dir_path   + '\n',
2473     'I       ' + foo_dir_path   + '\n',
2474     'I       ' + goo_file_path  + '\n',
2475     'I       ' + moo_file_path  + '\n',
2476     'I       ' + foo_file_path  + '\n',])
2477  svntest.actions.run_and_verify_svn(expected_output, [], 'st',
2478                                     '--config-dir', config_dir,
2479                                     '--no-ignore', wc_dir)
2480
2481  # Check status without the custom config and --no-ignore.
2482  # Should be the same as above except the *.boo and *.goo paths
2483  # are reported as unversioned '?' rather than ignored 'I'.
2484  expected_output = svntest.verify.UnorderedOutput(
2485    ['?       ' + X_dir_path     + '\n',
2486     '?       ' + Y_dir_path     + '\n',
2487     '?       ' + Z_dir_path     + '\n',
2488     '?       ' + boo_dir_path   + '\n',
2489     'I       ' + doo_dir_path   + '\n',
2490     'I       ' + doo_file2_path + '\n',
2491     'I       ' + moo_dir_path   + '\n',
2492     'I       ' + foo_dir_path   + '\n',
2493     '?       ' + goo_file_path  + '\n',
2494     'I       ' + moo_file_path  + '\n',
2495     'I       ' + foo_file_path  + '\n',])
2496  svntest.actions.run_and_verify_svn(expected_output, [], 'st',
2497                                     '--no-ignore', wc_dir)
2498
2499  # Perform the add with the --force flag, targeting the root of the WC.
2500  ### Note: You have to be inside the working copy or else Subversion
2501  ### will think you're trying to add the working copy to its parent
2502  ### directory, and will (possibly, if the parent directory isn't
2503  ### versioned) fail -- see also schedule_tests.py 11 "'svn add'
2504  ### should traverse already-versioned dirs"
2505  saved_wd = os.getcwd()
2506  os.chdir(sbox.wc_dir)
2507  expected = svntest.verify.UnorderedOutput(
2508    ['A         ' + 'ADD-ME-DIR-X\n',
2509     'A         ' + os.path.join('A', 'ADD-ME-DIR-Y.doo') + '\n',
2510     'A         ' + os.path.join('A', 'D', 'G', 'ADD-ME-DIR-Z.doo') + '\n'])
2511  svntest.actions.run_and_verify_svn(expected,
2512                                     [], 'add', '.', '--force',
2513                                     '--config-dir', config_dir)
2514  os.chdir(saved_wd)
2515
2516  # Now revert and try the add with the --no-ignore flag, nothing should
2517  # be ignored.
2518  svntest.actions.run_and_verify_svn(None, [], 'revert', wc_dir, '-R')
2519  saved_wd = os.getcwd()
2520  os.chdir(sbox.wc_dir)
2521  expected = svntest.verify.UnorderedOutput(
2522    ['A         ' + 'ADD-ME-DIR-X\n',
2523     'A         ' + os.path.join('A', 'ADD-ME-DIR-Y.doo') + '\n',
2524     'A         ' + os.path.join('A', 'D', 'G', 'ADD-ME-DIR-Z.doo') + '\n',
2525     'A         ' + os.path.join('ADD-ME-DIR-X', 'ignore-me-file.boo') + '\n',
2526     'A         ' + 'IGNORE-ME-DIR.boo' + '\n',
2527     'A         ' + os.path.join('IGNORE-ME-DIR.boo',
2528                                 'IGNORE-ME-DIR.goo') + '\n',
2529     'A         ' + os.path.join('A', 'B', 'E', 'IGNORE-ME-DIR.foo') + '\n',
2530     'A         ' + os.path.join('A', 'B', 'E', 'ignore-me-file.foo') + '\n',
2531     'A         ' + os.path.join('A', 'D', 'G', 'ignore-me-file.goo') + '\n',
2532
2533     'A         ' + os.path.join('A', 'B', 'E', 'ignore-me-file.doo') + '\n',
2534     'A         ' + os.path.join('A', 'B', 'IGNORE-ME-DIR.doo') + '\n',
2535     'A         ' + os.path.join('A', 'B', 'IGNORE-ME-DIR.doo',
2536                                 'ignore-me-file.doo') + '\n',
2537     'A         ' + os.path.join('A', 'B', 'IGNORE-ME-DIR.doo',
2538                                 'ignore-me-file.roo') + '\n',
2539     'A         ' + os.path.join('A', 'D', 'IGNORE-ME-DIR.moo') + '\n',
2540     'A         ' + os.path.join('A', 'D', 'ignore-me-file.moo') + '\n'])
2541  svntest.actions.run_and_verify_svn(expected, [], 'add', '.', '--force',
2542                                     '--no-ignore', '--config-dir',
2543                                     config_dir)
2544
2545def almost_known_prop_names(sbox):
2546  "propset with svn: prefix but unknown name"
2547
2548  sbox.build(read_only=True)
2549  wc_dir = sbox.wc_dir
2550  iota_path = sbox.ospath('iota')
2551
2552  # Same prefix, different prop name
2553  svntest.actions.set_prop('svn:exemutable', 'x', iota_path,
2554                           "svn: E195011: 'svn:exemutable' "
2555                           "is not a valid svn: property name")
2556  svntest.actions.set_prop('svn:exemutable', 'x', iota_path, force=True)
2557
2558  # Similar prefix, different prop name
2559  svntest.actions.set_prop('svm:exemutable', 'x', iota_path)
2560
2561  # Similar prefix, same prop name
2562  svntest.actions.set_prop('svm:executable', 'x', iota_path,
2563                           "svn: E195011: 'svm:executable' "
2564                           "is not a valid svn: property name")
2565  svntest.actions.set_prop('svm:executable', 'x', iota_path, force=True)
2566
2567  # Different prefix, same prop name
2568  svntest.actions.set_prop('tsvn:executable', 'x', iota_path)
2569
2570  # Property name is too different to matter
2571  svntest.actions.set_prop('svn:foobar', 'x', iota_path,
2572                           "svn: E195011: 'svn:foobar'"
2573                           " is not a valid svn: property name;"
2574                           " use '--force' to set it")
2575
2576@Issue(3231)
2577def peg_rev_base_working(sbox):
2578  """peg rev @BASE, peg rev @WORKING"""
2579
2580  sbox.build()
2581  wc_dir = sbox.wc_dir
2582
2583  # set up a local prop mod
2584  svntest.actions.set_prop('ordinal', 'ninth\n', sbox.ospath('iota'))
2585  sbox.simple_commit(message='r2')
2586  svntest.actions.set_prop('cardinal', 'nine\n', sbox.ospath('iota'))
2587  svntest.actions.run_and_verify_svn(['ninth\n'], [],
2588                                     'propget', '--no-newline', 'ordinal',
2589                                     sbox.ospath('iota') + '@BASE')
2590
2591@Issue(4415)
2592def xml_unsafe_author(sbox):
2593  "svn:author with XML unsafe chars"
2594
2595  sbox.build()
2596  wc_dir = sbox.wc_dir
2597
2598  svntest.actions.enable_revprop_changes(sbox.repo_dir)
2599
2600  # client sends svn:author (via PROPPATCH for DAV)
2601  svntest.actions.run_and_verify_svn(None, [],
2602                                     'propset', '--revprop', '-r', '1',
2603                                     'svn:author', 'foo\bbar', wc_dir)
2604
2605  # mod_dav_svn sends svn:author (via REPORT for DAV)
2606  sbox.simple_update(revision=0)
2607  sbox.simple_update(revision=1)
2608  expected_info = [{
2609      'Path' : re.escape(wc_dir),
2610      'Repository Root' : sbox.repo_url,
2611      'Repository UUID' : svntest.actions.get_wc_uuid(wc_dir),
2612      'Last Changed Author' : 'foo\bbar',
2613  }]
2614  svntest.actions.run_and_verify_info(expected_info, wc_dir)
2615
2616  # mod_dav_svn sends svn:author (via PROPFIND for DAV)
2617  # Since r1553367 this works correctly on ra_serf, since we now request
2618  # a single property value which skips creating the creator-displayname property
2619  svntest.actions.run_and_verify_svn(['foo\bbar'], [],
2620                                     'propget', '--revprop', '-r', '1',
2621                                     'svn:author', '--no-newline', wc_dir)
2622
2623  # Ensure a stable date
2624  svntest.actions.run_and_verify_svn(None, [],
2625                                     'propset', '--revprop', '-r', '1',
2626                                     'svn:date', '2015-01-01T00:00:00.0Z', wc_dir)
2627
2628  # But a proplist of this property value still fails via DAV.
2629  expected_output = svntest.verify.UnorderedOutput([
2630    'Unversioned properties on revision 1:\n',
2631    '  svn:author\n',
2632    '    foo\bbar\n',
2633    '  svn:date\n',
2634    '    2015-01-01T00:00:00.0Z\n',
2635    '  svn:log\n',
2636    '    Log message for revision 1.\n'
2637  ])
2638  svntest.actions.run_and_verify_svn(expected_output, [],
2639                                     'proplist', '--revprop', '-r', '1', '-v',
2640                                     wc_dir)
2641
2642@Issue(4415)
2643def xml_unsafe_author2(sbox):
2644  "svn:author with XML unsafe chars 2"
2645
2646  sbox.build(create_wc = False)
2647  repo_url = sbox.repo_url
2648
2649  svntest.actions.enable_revprop_changes(sbox.repo_dir)
2650
2651  # client sends svn:author (via PROPPATCH for DAV)
2652  svntest.actions.run_and_verify_svn(None, [],
2653                                     'propset', '--revprop', '-r', '1',
2654                                     'svn:author', 'foo\bbar', repo_url)
2655
2656  # Ensure a stable date
2657  svntest.actions.run_and_verify_svn(None, [],
2658                                     'propset', '--revprop', '-r', '1',
2659                                     'svn:date', '2000-01-01T12:00:00.0Z',
2660                                     repo_url)
2661
2662  if svntest.main.is_ra_type_dav():
2663    # This receives the filtered author (but that is better than an Xml fail)
2664    expected_author = 'foobar'
2665  else:
2666    expected_author = 'foo\bbar'
2667
2668  # Use svn ls in --xml mode to test locale independent output.
2669  expected_output = [
2670    '<?xml version="1.0" encoding="UTF-8"?>\n',
2671    '<lists>\n',
2672    '<list\n',
2673    '   path="%s">\n' % sbox.repo_url,
2674    '<entry\n',
2675    '   kind="dir">\n',
2676    '<name>A</name>\n',
2677    '<commit\n',
2678    '   revision="1">\n',
2679    '<author>%s</author>\n' % expected_author,
2680    '<date>2000-01-01T12:00:00.000000Z</date>\n',
2681    '</commit>\n',
2682    '</entry>\n',
2683    '<entry\n',
2684    '   kind="file">\n',
2685    '<name>iota</name>\n',
2686    '<size>25</size>\n',
2687    '<commit\n',
2688    '   revision="1">\n',
2689    '<author>%s</author>\n' % expected_author,
2690    '<date>2000-01-01T12:00:00.000000Z</date>\n',
2691    '</commit>\n',
2692    '</entry>\n',
2693    '</list>\n',
2694    '</lists>\n'
2695    ]
2696
2697  svntest.actions.run_and_verify_svn(expected_output, [],
2698                                     'ls', '--xml', repo_url)
2699
2700  expected_info = [{
2701      'Repository Root' : sbox.repo_url,
2702      'Last Changed Author' : expected_author,
2703  }]
2704  svntest.actions.run_and_verify_info(expected_info, repo_url)
2705
2706def dir_prop_conflict_details(sbox):
2707  "verify dir property conflict details"
2708
2709  sbox.build()
2710  wc_dir = sbox.wc_dir
2711
2712  # Apply some changes
2713  sbox.simple_propset('svn:mergeinfo', '/B:1', 'A')
2714  sbox.simple_propset('my-prop', 'my-val', 'A')
2715  sbox.simple_commit()
2716
2717  # Revert to r1
2718  sbox.simple_update('', revision=1)
2719
2720  # Apply some incompatible changes
2721  sbox.simple_propset('svn:mergeinfo', '/C:1', 'A')
2722  sbox.simple_propset('my-prop', 'other-val', 'A')
2723
2724  # This should report out of date because there are incompatible property
2725  # changes that can't be merged on the server
2726  svntest.actions.run_and_verify_commit(wc_dir,
2727                                        None,
2728                                        None,
2729                                        '.*[Oo]ut of date.*')
2730
2731  expected_output = svntest.wc.State(wc_dir, {
2732    'A'                 : Item(status=' C'),
2733  })
2734  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
2735  expected_status.tweak('A', status=' C')
2736
2737  svntest.actions.run_and_verify_update(wc_dir,
2738                                        expected_output,
2739                                        None,
2740                                        expected_status,
2741                                        check_props=True)
2742  expected_info = {
2743    'Conflicted Properties' : 'my-prop',
2744    'Conflict Details': re.escape('incoming dir edit upon update'
2745                                           + ' Source  left: (dir) ^/A@1'
2746                                           + ' Source right: (dir) ^/A@2')
2747  }
2748  svntest.actions.run_and_verify_info([expected_info], sbox.path('A'))
2749
2750
2751def iprops_list_abspath(sbox):
2752  "test listing iprops via abspath"
2753
2754  sbox.build()
2755
2756  sbox.simple_propset('im', 'root', '')
2757  sbox.simple_commit()
2758
2759  svntest.actions.run_and_verify_svn(None, [],
2760                                     'switch', '^/A/D', sbox.ospath(''),
2761                                     '--ignore-ancestry')
2762
2763  sbox.simple_propset('im', 'GammA', 'gamma')
2764
2765  expected_output = [
2766    'Inherited properties on \'%s\',\n' % sbox.ospath(''),
2767    'from \'%s\':\n' % sbox.repo_url,
2768    '  im\n',
2769    '    root\n',
2770    'Properties on \'%s\':\n' % sbox.ospath('gamma'),
2771    '  im\n',
2772    '    GammA\n'
2773  ]
2774  svntest.actions.run_and_verify_svn(expected_output, [],
2775                                     'pl', '-R',
2776                                     '--show-inherited-props', '-v',
2777                                     sbox.ospath(''))
2778
2779  expected_output = [
2780    'Inherited properties on \'%s\',\n' % os.path.abspath(sbox.ospath('')),
2781    'from \'%s\':\n' % sbox.repo_url,
2782    '  im\n',
2783    '    root\n',
2784    'Properties on \'%s\':\n' % os.path.abspath(sbox.ospath('gamma')),
2785    '  im\n',
2786    '    GammA\n'
2787  ]
2788  svntest.actions.run_and_verify_svn(expected_output, [],
2789                                     'pl', '-R',
2790                                     '--show-inherited-props', '-v',
2791                                     os.path.abspath(sbox.ospath('')))
2792
2793def wc_propop_on_url(sbox):
2794  "perform wc specific operations on url"
2795
2796  sbox.build(create_wc = False)
2797
2798  svntest.actions.run_and_verify_svn(None, '.*E195000:.*path',
2799                                     'pl', '-r', 'PREV',
2800                                     sbox.repo_url)
2801
2802  svntest.actions.run_and_verify_svn(None, '.*E195000:.*path',
2803                                     'pg', 'my:Q', '-r', 'PREV',
2804                                     sbox.repo_url)
2805
2806def prop_conflict_root(sbox):
2807  """property conflict on wc root"""
2808
2809  sbox.build()
2810  wc_dir = sbox.wc_dir
2811
2812  sbox.simple_propset('propname', 'propval1', '')
2813  sbox.simple_commit()
2814  sbox.simple_propset('propname', 'propval2', '')
2815  sbox.simple_commit()
2816  sbox.simple_update(revision=2)
2817  sbox.simple_propset('propname', 'propvalconflict', '')
2818
2819  expected_output = svntest.wc.State(wc_dir, {
2820    '' : Item(status=' C'),
2821  })
2822  expected_disk = svntest.main.greek_state.copy()
2823  expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
2824  expected_status.tweak('', status=' C')
2825  extra_files = ['dir_conflicts.prej']
2826  svntest.actions.run_and_verify_update(wc_dir,
2827                                        expected_output,
2828                                        expected_disk,
2829                                        expected_status,
2830                                        extra_files=extra_files)
2831
2832########################################################################
2833# Run the tests
2834
2835# list all tests here, starting with None:
2836test_list = [ None,
2837              make_local_props,
2838              commit_props,
2839              update_props,
2840              downdate_props,
2841              remove_props,
2842              update_conflict_props,
2843              commit_conflict_dirprops,
2844              commit_replacement_props,
2845              revert_replacement_props,
2846              inappropriate_props,
2847              copy_inherits_special_props,
2848              revprop_change,
2849              prop_value_conversions,
2850              binary_props,
2851              recursive_base_wc_ops,
2852              url_props_ops,
2853              removal_schedule_added_props,
2854              update_props_on_wc_root,
2855              props_on_replaced_file,
2856              depthy_wc_proplist,
2857              depthy_url_proplist,
2858              invalid_propnames,
2859              perms_on_symlink,
2860              remove_custom_ns_props,
2861              props_over_time,
2862              invalid_propvalues,
2863              same_replacement_props,
2864              added_moved_file,
2865              delete_nonexistent_property,
2866              post_revprop_change_hook,
2867              rm_of_replaced_file,
2868              prop_reject_grind,
2869              obstructed_subdirs,
2870              atomic_over_ra,
2871              propget_redirection,
2872              file_matching_dir_prop_reject,
2873              pristine_props_listed,
2874              inheritable_ignores,
2875              almost_known_prop_names,
2876              peg_rev_base_working,
2877              xml_unsafe_author,
2878              xml_unsafe_author2,
2879              dir_prop_conflict_details,
2880              iprops_list_abspath,
2881              wc_propop_on_url,
2882              prop_conflict_root,
2883             ]
2884
2885if __name__ == '__main__':
2886  svntest.main.run_tests(test_list)
2887  # NOTREACHED
2888
2889
2890### End of file.
2891