source: valtobtest/subversion-1.6.2/subversion/tests/cmdline/commit_tests.py @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

  • Property svn:executable set to *
File size: 102.0 KB
Line 
1#!/usr/bin/env python
2#
3#  commit_tests.py:  testing fancy commit cases.
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.tigris.org for more information.
7#
8# ====================================================================
9# Copyright (c) 2000-2008 CollabNet.  All rights reserved.
10#
11# This software is licensed as described in the file COPYING, which
12# you should have received as part of this distribution.  The terms
13# are also available at http://subversion.tigris.org/license-1.html.
14# If newer versions of this license are posted there, you may use a
15# newer version instead, at your option.
16#
17######################################################################
18
19# General modules
20import sys, os, re
21
22# Our testing module
23import svntest
24from svntest import wc
25
26# (abbreviation)
27Skip = svntest.testcase.Skip
28SkipUnless = svntest.testcase.SkipUnless
29XFail = svntest.testcase.XFail
30Item = svntest.wc.StateItem
31
32from svntest.main import server_has_revprop_commit, \
33    server_gets_client_capabilities
34from svntest.actions import inject_conflict_into_wc
35
36######################################################################
37# Utilities
38#
39
40def is_non_posix_os_or_cygwin_platform():
41  return (not svntest.main.is_posix_os()) or sys.platform == 'cygwin'
42
43def get_standard_state(wc_dir):
44  """Return a status list reflecting the local mods made by
45  make_standard_slew_of_changes()."""
46
47  state = svntest.actions.get_virginal_state(wc_dir, 1)
48
49  state.tweak('', 'A/D', 'A/D/G/pi', status=' M')
50  state.tweak('A/B/lambda', status='M ')
51  state.tweak('A/B/E', 'A/D/H/chi', status='R ')
52  state.tweak('A/B/E/alpha', 'A/B/E/beta', 'A/C', 'A/D/gamma',
53              'A/D/G/rho', status='D ')
54  state.tweak('A/D/H/omega', status='MM')
55
56  # New things
57  state.add({
58    'Q' : Item(status='A ', wc_rev=0),
59    'Q/floo' : Item(status='A ', wc_rev=0),
60    'A/D/H/gloo' : Item(status='A ', wc_rev=0),
61    'A/B/E/bloo' : Item(status='A ', wc_rev=0),
62    })
63
64  return state
65
66
67def make_standard_slew_of_changes(wc_dir):
68  """Make a specific set of local mods to WC_DIR.  These will be used
69  by every commit-test.  Verify the 'svn status' output, and return the
70  (pre-commit) status tree."""
71
72  # Cache current working directory, move into wc_dir
73  was_cwd = os.getcwd()
74  os.chdir(wc_dir)
75
76  # Add a directory
77  os.mkdir('Q')
78  svntest.main.run_svn(None, 'add', 'Q')
79
80  # Remove two directories
81  svntest.main.run_svn(None, 'rm', os.path.join('A', 'B', 'E'))
82  svntest.main.run_svn(None, 'rm', os.path.join('A', 'C'))
83
84  # Replace one of the removed directories
85  svntest.main.run_svn(None, 'add', os.path.join('A', 'B', 'E'))
86
87  # Make property mods to two directories
88  svntest.main.run_svn(None, 'propset', 'foo', 'bar', os.curdir)
89  svntest.main.run_svn(None, 'propset', 'foo2', 'bar2', os.path.join('A', 'D'))
90
91  # Add three files
92  svntest.main.file_append(os.path.join('A', 'B', 'E', 'bloo'), "hi")
93  svntest.main.file_append(os.path.join('A', 'D', 'H', 'gloo'), "hello")
94  svntest.main.file_append(os.path.join('Q', 'floo'), "yo")
95  svntest.main.run_svn(None, 'add', os.path.join('A', 'B', 'E', 'bloo'))
96  svntest.main.run_svn(None, 'add', os.path.join('A', 'D', 'H', 'gloo'))
97  svntest.main.run_svn(None, 'add', os.path.join('Q', 'floo'))
98
99  # Remove three files
100  svntest.main.run_svn(None, 'rm', os.path.join('A', 'D', 'G', 'rho'))
101  svntest.main.run_svn(None, 'rm', os.path.join('A', 'D', 'H', 'chi'))
102  svntest.main.run_svn(None, 'rm', os.path.join('A', 'D', 'gamma'))
103
104  # Replace one of the removed files
105  svntest.main.file_append(os.path.join('A', 'D', 'H', 'chi'), "chi")
106  svntest.main.run_svn(None, 'add', os.path.join('A', 'D', 'H', 'chi'))
107
108  # Make textual mods to two files
109  svntest.main.file_append(os.path.join('A', 'B', 'lambda'), "new ltext")
110  svntest.main.file_append(os.path.join('A', 'D', 'H', 'omega'), "new otext")
111
112  # Make property mods to three files
113  svntest.main.run_svn(None, 'propset', 'blue', 'azul',
114                       os.path.join('A', 'D', 'H', 'omega'))
115  svntest.main.run_svn(None, 'propset', 'green', 'verde',
116                       os.path.join('Q', 'floo'))
117  svntest.main.run_svn(None, 'propset', 'red', 'rojo',
118                       os.path.join('A', 'D', 'G', 'pi'))
119
120  # Restore the CWD.
121  os.chdir(was_cwd)
122
123  # Build an expected status tree.
124  expected_status = get_standard_state(wc_dir)
125
126  # Verify status -- all local mods should be present.
127  svntest.actions.run_and_verify_status(wc_dir, expected_status)
128
129  return expected_status
130
131
132######################################################################
133# Tests
134#
135#   Each test must return on success or raise on failure.
136
137
138#----------------------------------------------------------------------
139
140def commit_one_file(sbox):
141  "commit one file"
142
143  sbox.build()
144  wc_dir = sbox.wc_dir
145
146  expected_status = make_standard_slew_of_changes(wc_dir)
147
148  omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
149
150  # Create expected state.
151  expected_output = svntest.wc.State(wc_dir, {
152    'A/D/H/omega' : Item(verb='Sending'),
153    })
154  expected_status.tweak('A/D/H/omega', wc_rev=2, status='  ')
155
156  # Commit the one file.
157  svntest.actions.run_and_verify_commit(wc_dir,
158                                        expected_output,
159                                        expected_status,
160                                        None,
161                                        omega_path)
162
163
164#----------------------------------------------------------------------
165
166def commit_one_new_file(sbox):
167  "commit one newly added file"
168
169  sbox.build()
170  wc_dir = sbox.wc_dir
171
172  expected_status = make_standard_slew_of_changes(wc_dir)
173
174  gloo_path = os.path.join(wc_dir, 'A', 'D', 'H', 'gloo')
175
176  # Create expected state.
177  expected_output = svntest.wc.State(wc_dir, {
178    'A/D/H/gloo' : Item(verb='Adding'),
179    })
180  expected_status.tweak('A/D/H/gloo', wc_rev=2, status='  ')
181
182  # Commit the one file.
183  svntest.actions.run_and_verify_commit(wc_dir,
184                                        expected_output,
185                                        expected_status,
186                                        None,
187                                        gloo_path)
188
189
190#----------------------------------------------------------------------
191
192def commit_one_new_binary_file(sbox):
193  "commit one newly added binary file"
194
195  sbox.build()
196  wc_dir = sbox.wc_dir
197
198  expected_status = make_standard_slew_of_changes(wc_dir)
199
200  gloo_path = os.path.join(wc_dir, 'A', 'D', 'H', 'gloo')
201  svntest.main.run_svn(None, 'propset', 'svn:mime-type',
202                       'application/octet-stream', gloo_path)
203
204  # Create expected state.
205  expected_output = svntest.wc.State(wc_dir, {
206    'A/D/H/gloo' : Item(verb='Adding  (bin)'),
207    })
208  expected_status.tweak('A/D/H/gloo', wc_rev=2, status='  ')
209
210  # Commit the one file.
211  svntest.actions.run_and_verify_commit(wc_dir,
212                                        expected_output,
213                                        expected_status,
214                                        None,
215                                        gloo_path)
216
217
218#----------------------------------------------------------------------
219
220def commit_multiple_targets(sbox):
221  "commit multiple targets"
222
223  sbox.build()
224  wc_dir = sbox.wc_dir
225
226  # This test will commit three targets:  psi, B, and pi.  In that order.
227
228  # Make local mods to many files.
229  AB_path = os.path.join(wc_dir, 'A', 'B')
230  lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
231  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
232  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
233  omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
234  psi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'psi')
235  svntest.main.file_append(lambda_path, 'new appended text for lambda')
236  svntest.main.file_append(rho_path, 'new appended text for rho')
237  svntest.main.file_append(pi_path, 'new appended text for pi')
238  svntest.main.file_append(omega_path, 'new appended text for omega')
239  svntest.main.file_append(psi_path, 'new appended text for psi')
240
241  # Just for kicks, add a property to A/D/G as well.  We'll make sure
242  # that it *doesn't* get committed.
243  ADG_path = os.path.join(wc_dir, 'A', 'D', 'G')
244  svntest.main.run_svn(None, 'propset', 'foo', 'bar', ADG_path)
245
246  # Create expected output tree for 'svn ci'.  We should see changes
247  # only on these three targets, no others.
248  expected_output = svntest.wc.State(wc_dir, {
249    'A/D/H/psi' : Item(verb='Sending'),
250    'A/B/lambda' : Item(verb='Sending'),
251    'A/D/G/pi' : Item(verb='Sending'),
252    })
253
254  # Create expected status tree; all local revisions should be at 1,
255  # but our three targets should be at 2.
256  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
257  expected_status.tweak('A/D/H/psi', 'A/B/lambda', 'A/D/G/pi', wc_rev=2)
258
259  # rho and omega should still display as locally modified:
260  expected_status.tweak('A/D/G/rho', 'A/D/H/omega', status='M ')
261
262  # A/D/G should still have a local property set, too.
263  expected_status.tweak('A/D/G', status=' M')
264
265  svntest.actions.run_and_verify_commit(wc_dir,
266                                        expected_output,
267                                        expected_status,
268                                        None,
269                                        psi_path, AB_path, pi_path)
270
271#----------------------------------------------------------------------
272
273
274def commit_multiple_targets_2(sbox):
275  "commit multiple targets, 2nd variation"
276
277  sbox.build()
278  wc_dir = sbox.wc_dir
279
280  # This test will commit four targets:  psi, B, omega and pi.  In that order.
281
282  # Make local mods to many files.
283  AB_path = os.path.join(wc_dir, 'A', 'B')
284  lambda_path = os.path.join(wc_dir, 'A', 'B', 'lambda')
285  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
286  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
287  omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
288  psi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'psi')
289  svntest.main.file_append(lambda_path, 'new appended text for lambda')
290  svntest.main.file_append(rho_path, 'new appended text for rho')
291  svntest.main.file_append(pi_path, 'new appended text for pi')
292  svntest.main.file_append(omega_path, 'new appended text for omega')
293  svntest.main.file_append(psi_path, 'new appended text for psi')
294
295  # Just for kicks, add a property to A/D/G as well.  We'll make sure
296  # that it *doesn't* get committed.
297  ADG_path = os.path.join(wc_dir, 'A', 'D', 'G')
298  svntest.main.run_svn(None, 'propset', 'foo', 'bar', ADG_path)
299
300  # Created expected output tree for 'svn ci'.  We should see changes
301  # only on these three targets, no others.
302  expected_output = svntest.wc.State(wc_dir, {
303    'A/D/H/psi' : Item(verb='Sending'),
304    'A/B/lambda' : Item(verb='Sending'),
305    'A/D/H/omega' : Item(verb='Sending'),
306    'A/D/G/pi' : Item(verb='Sending'),
307    })
308
309  # Create expected status tree; all local revisions should be at 1,
310  # but our four targets should be at 2.
311  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
312  expected_status.tweak('A/D/H/psi', 'A/B/lambda', 'A/D/G/pi', 'A/D/H/omega',
313                        wc_rev=2)
314
315  # rho should still display as locally modified:
316  expected_status.tweak('A/D/G/rho', status='M ')
317
318  # A/D/G should still have a local property set, too.
319  expected_status.tweak('A/D/G', status=' M')
320
321  svntest.actions.run_and_verify_commit(wc_dir,
322                                        expected_output,
323                                        expected_status,
324                                        None,
325                                        psi_path, AB_path,
326                                        omega_path, pi_path)
327
328#----------------------------------------------------------------------
329
330def commit_inclusive_dir(sbox):
331  "commit wc_dir/A/D -- includes D. (anchor=A, tgt=D)"
332
333  sbox.build()
334  wc_dir = sbox.wc_dir
335
336  expected_status = make_standard_slew_of_changes(wc_dir)
337
338  D_path = os.path.join(wc_dir, 'A', 'D')
339
340  # Create expected state.
341  expected_output = svntest.wc.State(wc_dir, {
342    'A/D' : Item(verb='Sending'),
343    'A/D/G/pi' : Item(verb='Sending'),
344    'A/D/G/rho' : Item(verb='Deleting'),
345    'A/D/H/gloo' : Item(verb='Adding'),
346    'A/D/H/chi' : Item(verb='Replacing'),
347    'A/D/H/omega' : Item(verb='Sending'),
348    'A/D/gamma' : Item(verb='Deleting'),
349    })
350
351  expected_status.remove('A/D/G/rho', 'A/D/gamma')
352  expected_status.tweak('A/D', 'A/D/G/pi', 'A/D/H/omega',
353                        wc_rev=2, status='  ')
354  expected_status.tweak('A/D/H/chi', 'A/D/H/gloo', wc_rev=2, status='  ')
355
356  # Commit the one file.
357  svntest.actions.run_and_verify_commit(wc_dir,
358                                        expected_output,
359                                        expected_status,
360                                        None,
361                                        D_path)
362
363#----------------------------------------------------------------------
364
365def commit_top_dir(sbox):
366  "commit wc_dir -- (anchor=wc_dir, tgt={})"
367
368  sbox.build()
369  wc_dir = sbox.wc_dir
370
371  expected_status = make_standard_slew_of_changes(wc_dir)
372
373  # Create expected state.
374  expected_output = svntest.wc.State(wc_dir, {
375    '' : Item(verb='Sending'),
376    'Q' : Item(verb='Adding'),
377    'Q/floo' : Item(verb='Adding'),
378    'A/B/E' : Item(verb='Replacing'),
379    'A/B/E/bloo' : Item(verb='Adding'),
380    'A/B/lambda' : Item(verb='Sending'),
381    'A/C' : Item(verb='Deleting'),
382    'A/D' : Item(verb='Sending'),
383    'A/D/G/pi' : Item(verb='Sending'),
384    'A/D/G/rho' : Item(verb='Deleting'),
385    'A/D/H/gloo' : Item(verb='Adding'),
386    'A/D/H/chi' : Item(verb='Replacing'),
387    'A/D/H/omega' : Item(verb='Sending'),
388    'A/D/gamma' : Item(verb='Deleting'),
389    })
390
391  expected_status.remove('A/D/G/rho', 'A/D/gamma', 'A/C',
392                         'A/B/E/alpha', 'A/B/E/beta')
393  expected_status.tweak('A/D', 'A/D/G/pi', 'A/D/H/omega', 'Q/floo', '',
394                        wc_rev=2, status='  ')
395  expected_status.tweak('A/D/H/chi', 'Q', 'A/B/E', 'A/B/E/bloo', 'A/B/lambda',
396                        'A/D/H/gloo', wc_rev=2, status='  ')
397
398  # Commit the one file.
399  svntest.actions.run_and_verify_commit(wc_dir,
400                                        expected_output,
401                                        expected_status,
402                                        None,
403                                        wc_dir)
404
405#----------------------------------------------------------------------
406
407# Regression test for bug reported by Jon Trowbridge:
408#
409#    From: Jon Trowbridge <trow@ximian.com>
410#    Subject:  svn segfaults if you commit a file that hasn't been added
411#    To: dev@subversion.tigris.org
412#    Date: 17 Jul 2001 03:20:55 -0500
413#    Message-Id: <995358055.16975.5.camel@morimoto>
414#
415#    The problem is that report_single_mod in libsvn_wc/adm_crawler.c is
416#    called with its entry parameter as NULL, but the code doesn't
417#    check that entry is non-NULL before trying to dereference it.
418#
419# This bug never had an issue number.
420#
421def commit_unversioned_thing(sbox):
422  "committing unversioned object produces error"
423
424  sbox.build()
425  wc_dir = sbox.wc_dir
426
427  # Create an unversioned file in the wc.
428  svntest.main.file_append(os.path.join(wc_dir, 'blorg'), "nothing to see")
429
430  # Commit a non-existent file and *expect* failure:
431  svntest.actions.run_and_verify_commit(wc_dir,
432                                        None,
433                                        None,
434                                        "is not under version control",
435                                        os.path.join(wc_dir,'blorg'))
436
437#----------------------------------------------------------------------
438
439# regression test for bug #391
440
441def nested_dir_replacements(sbox):
442  "replace two nested dirs, verify empty contents"
443
444  sbox.build()
445  wc_dir = sbox.wc_dir
446
447  # Delete and re-add A/D (a replacement), and A/D/H (another replace).
448  svntest.main.run_svn(None, 'rm', os.path.join(wc_dir, 'A', 'D'))
449  svntest.main.run_svn(None, 'add', '--depth=empty',
450                       os.path.join(wc_dir, 'A', 'D'))
451  svntest.main.run_svn(None, 'add', '--depth=empty',
452                       os.path.join(wc_dir, 'A', 'D', 'H'))
453
454  # For kicks, add new file A/D/bloo.
455  svntest.main.file_append(os.path.join(wc_dir, 'A', 'D', 'bloo'), "hi")
456  svntest.main.run_svn(None, 'add', os.path.join(wc_dir, 'A', 'D', 'bloo'))
457
458  # Verify pre-commit status:
459  #
460  #    - A/D and A/D/H should both be scheduled as "R" at rev 1
461  #         (rev 1 because they both existed before at rev 1)
462  #
463  #    - A/D/bloo scheduled as "A" at rev 0
464  #         (rev 0 because it did not exist before)
465  #
466  #    - ALL other children of A/D scheduled as "D" at rev 1
467
468  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
469  expected_status.tweak('A/D', 'A/D/H', status='R ', wc_rev=1)
470  expected_status.add({
471    'A/D/bloo' : Item(status='A ', wc_rev=0),
472    })
473  expected_status.tweak('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau',
474                        'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi', 'A/D/gamma',
475                        status='D ')
476
477  svntest.actions.run_and_verify_status(wc_dir, expected_status)
478
479  # Build expected post-commit trees:
480
481  # Create expected output tree.
482  expected_output = svntest.wc.State(wc_dir, {
483    'A/D' : Item(verb='Replacing'),
484    'A/D/H' : Item(verb='Adding'),
485    'A/D/bloo' : Item(verb='Adding'),
486    })
487
488  # Created expected status tree.
489  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
490  expected_status.tweak('A/D', 'A/D/H', wc_rev=2)
491  expected_status.add({
492    'A/D/bloo' : Item(status='  ', wc_rev=2),
493    })
494  expected_status.remove('A/D/G', 'A/D/G/pi', 'A/D/G/rho', 'A/D/G/tau',
495                        'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi', 'A/D/gamma')
496
497  # Commit from the top of the working copy and verify output & status.
498  svntest.actions.run_and_verify_commit(wc_dir,
499                                        expected_output,
500                                        expected_status,
501                                        None,
502                                        wc_dir)
503
504#----------------------------------------------------------------------
505
506# Testing part 1 of the "Greg Hudson" problem -- specifically, that
507# our use of the "existence=deleted" flag is working properly in cases
508# where the parent directory's revision lags behind a deleted child's
509# revision.
510
511def hudson_part_1(sbox):
512  "hudson prob 1.0:  delete file, commit, update"
513
514  sbox.build()
515  wc_dir = sbox.wc_dir
516
517  # Remove gamma from the working copy.
518  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
519  svntest.main.run_svn(None, 'rm', gamma_path)
520
521  # Create expected commit output.
522  expected_output = svntest.wc.State(wc_dir, {
523    'A/D/gamma' : Item(verb='Deleting'),
524    })
525
526  # After committing, status should show no sign of gamma.
527  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
528  expected_status.remove('A/D/gamma')
529
530  # Commit the deletion of gamma and verify.
531  svntest.actions.run_and_verify_commit(wc_dir,
532                                        expected_output,
533                                        expected_status,
534                                        None, wc_dir)
535
536  # Now gamma should be marked as `deleted' under the hood.  When we
537  # update, we should no output, and a perfect, virginal status list
538  # at revision 2.  (The `deleted' entry should be removed.)
539
540  # Expected output of update:  nothing.
541  expected_output = svntest.wc.State(wc_dir, {})
542
543  # Expected disk tree:  everything but gamma
544  expected_disk = svntest.main.greek_state.copy()
545  expected_disk.remove('A/D/gamma')
546
547  # Expected status after update:  totally clean revision 2, minus gamma.
548  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
549  expected_status.remove('A/D/gamma')
550
551  svntest.actions.run_and_verify_update(wc_dir,
552                                        expected_output,
553                                        expected_disk,
554                                        expected_status)
555
556
557#----------------------------------------------------------------------
558
559# Testing part 1 of the "Greg Hudson" problem -- variation on previous
560# test, removing a directory instead of a file this time.
561
562def hudson_part_1_variation_1(sbox):
563  "hudson prob 1.1:  delete dir, commit, update"
564
565  sbox.build()
566  wc_dir = sbox.wc_dir
567
568  # Remove H from the working copy.
569  H_path = os.path.join(wc_dir, 'A', 'D', 'H')
570  svntest.main.run_svn(None, 'rm', H_path)
571
572  # Create expected commit output.
573  expected_output = svntest.wc.State(wc_dir, {
574    'A/D/H' : Item(verb='Deleting'),
575    })
576
577  # After committing, status should show no sign of H or its contents
578  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
579  expected_status.remove('A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi')
580
581  # Commit the deletion of H and verify.
582  svntest.actions.run_and_verify_commit(wc_dir,
583                                        expected_output,
584                                        expected_status,
585                                        None, wc_dir)
586
587  # Now H should be marked as `deleted' under the hood.  When we
588  # update, we should no see output, and a perfect, virginal status
589  # list at revision 2.  (The `deleted' entry should be removed.)
590
591  # Expected output of update:  H gets a no-op deletion.
592  expected_output = svntest.wc.State(wc_dir, {})
593
594  # Expected disk tree:  everything except files in H
595  expected_disk = svntest.main.greek_state.copy()
596  expected_disk.remove('A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi')
597
598  # Expected status after update:  totally clean revision 2, minus H.
599  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
600  expected_status.remove('A/D/H', 'A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi')
601
602  svntest.actions.run_and_verify_update(wc_dir,
603                                        expected_output,
604                                        expected_disk,
605                                        expected_status)
606
607#----------------------------------------------------------------------
608
609# Testing part 1 of the "Greg Hudson" problem -- variation 2.  In this
610# test, we make sure that a file that is BOTH `deleted' and scheduled
611# for addition can be correctly committed & merged.
612
613def hudson_part_1_variation_2(sbox):
614  "hudson prob 1.2:  delete, commit, re-add, commit"
615
616  sbox.build()
617  wc_dir = sbox.wc_dir
618
619  # Remove gamma from the working copy.
620  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
621  svntest.main.run_svn(None, 'rm', gamma_path)
622
623  # Create expected commit output.
624  expected_output = svntest.wc.State(wc_dir, {
625    'A/D/gamma' : Item(verb='Deleting'),
626    })
627
628  # After committing, status should show no sign of gamma.
629  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
630  expected_status.remove('A/D/gamma')
631
632  # Commit the deletion of gamma and verify.
633  svntest.actions.run_and_verify_commit(wc_dir,
634                                        expected_output,
635                                        expected_status,
636                                        None, wc_dir)
637
638  # Now gamma should be marked as `deleted' under the hood.
639  # Go ahead and re-add gamma, so that is *also* scheduled for addition.
640  svntest.main.file_append(gamma_path, "added gamma")
641  svntest.main.run_svn(None, 'add', gamma_path)
642
643  # For sanity, examine status: it should show a revision 2 tree with
644  # gamma scheduled for addition.
645  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
646  expected_status.tweak('A/D/gamma', wc_rev=0, status='A ')
647
648  svntest.actions.run_and_verify_status(wc_dir, expected_status)
649
650  # Create expected commit output.
651  expected_output = svntest.wc.State(wc_dir, {
652    'A/D/gamma' : Item(verb='Adding'),
653    })
654
655  # After committing, status should show only gamma at revision 3.
656  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
657  expected_status.tweak('A/D/gamma', wc_rev=3)
658
659  svntest.actions.run_and_verify_commit(wc_dir,
660                                        expected_output,
661                                        expected_status,
662                                        None, wc_dir)
663
664
665#----------------------------------------------------------------------
666
667# Testing part 2 of the "Greg Hudson" problem.
668#
669# In this test, we make sure that we're UNABLE to commit a propchange
670# on an out-of-date directory.
671
672def hudson_part_2(sbox):
673  "hudson prob 2.0:  prop commit on old dir fails"
674
675  sbox.build()
676  wc_dir = sbox.wc_dir
677
678  # Remove gamma from the working copy.
679  D_path = os.path.join(wc_dir, 'A', 'D')
680  gamma_path = os.path.join(wc_dir, 'A', 'D', 'gamma')
681  svntest.main.run_svn(None, 'rm', gamma_path)
682
683  # Create expected commit output.
684  expected_output = svntest.wc.State(wc_dir, {
685    'A/D/gamma' : Item(verb='Deleting'),
686    })
687
688  # After committing, status should show no sign of gamma.
689  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
690  expected_status.remove('A/D/gamma')
691
692  # Commit the deletion of gamma and verify.
693  svntest.actions.run_and_verify_commit(wc_dir,
694                                        expected_output,
695                                        expected_status,
696                                        None, wc_dir)
697
698  # Now gamma should be marked as `deleted' under the hood, at
699  # revision 2.  Meanwhile, A/D is still lagging at revision 1.
700
701  # Make a propchange on A/D
702  svntest.main.run_svn(None, 'ps', 'foo', 'bar', D_path)
703
704  # Commit and *expect* a repository Merge failure:
705  svntest.actions.run_and_verify_commit(wc_dir,
706                                        None,
707                                        None,
708                                        "[Oo]ut.of.date",
709                                        wc_dir)
710
711#----------------------------------------------------------------------
712
713# Test a possible regression in our 'deleted' post-commit handling.
714#
715# This test moves files from one subdir to another, commits, then
716# updates the empty directory.  Nothing should be printed, assuming
717# all the moved files are properly marked as 'deleted' and reported to
718# the server.
719
720def hudson_part_2_1(sbox):
721  "hudson prob 2.1:  move files, update empty dir"
722
723  sbox.build()
724  wc_dir = sbox.wc_dir
725
726  # Move all the files in H to G
727  H_path = os.path.join(wc_dir, 'A', 'D', 'H')
728  G_path = os.path.join(wc_dir, 'A', 'D', 'G')
729  chi_path = os.path.join(H_path, 'chi')
730  psi_path = os.path.join(H_path, 'psi')
731  omega_path = os.path.join(H_path, 'omega')
732
733  svntest.main.run_svn(None, 'mv', chi_path, G_path)
734  svntest.main.run_svn(None, 'mv', psi_path, G_path)
735  svntest.main.run_svn(None, 'mv', omega_path, G_path)
736
737  # Create expected commit output.
738  expected_output = svntest.wc.State(wc_dir, {
739    'A/D/H/chi' : Item(verb='Deleting'),
740    'A/D/H/omega' : Item(verb='Deleting'),
741    'A/D/H/psi' : Item(verb='Deleting'),
742    'A/D/G/chi' : Item(verb='Adding'),
743    'A/D/G/omega' : Item(verb='Adding'),
744    'A/D/G/psi' : Item(verb='Adding'),
745    })
746
747  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
748  expected_status.remove('A/D/H/chi')
749  expected_status.remove('A/D/H/omega')
750  expected_status.remove('A/D/H/psi')
751  expected_status.add({ 'A/D/G/chi' :
752                        Item(wc_rev=2, status='  ') })
753  expected_status.add({ 'A/D/G/omega' :
754                        Item(wc_rev=2, status='  ') })
755  expected_status.add({ 'A/D/G/psi' :
756                        Item(wc_rev=2, status='  ') })
757
758  svntest.actions.run_and_verify_commit(wc_dir,
759                                        expected_output,
760                                        expected_status,
761                                        None, wc_dir)
762
763  # Now, assuming all three files in H are marked as 'deleted', an
764  # update of H should print absolutely nothing.
765  expected_output = svntest.wc.State(wc_dir, { })
766
767  # Reuse expected_status
768  expected_status.tweak(wc_rev=2)
769
770  expected_disk = svntest.main.greek_state.copy()
771  expected_disk.remove('A/D/H/chi', 'A/D/H/omega', 'A/D/H/psi')
772  expected_disk.add({
773    'A/D/G/chi' : Item("This is the file 'chi'.\n"),
774    })
775  expected_disk.add({
776    'A/D/G/omega' : Item("This is the file 'omega'.\n"),
777    })
778  expected_disk.add({
779    'A/D/G/psi' : Item("This is the file 'psi'.\n"),
780    })
781
782  svntest.actions.run_and_verify_update(wc_dir,
783                                        expected_output,
784                                        expected_disk,
785                                        expected_status)
786
787#----------------------------------------------------------------------
788
789def hook_test(sbox):
790  "hook testing"
791
792  sbox.build()
793
794  # Get paths to the working copy and repository
795  wc_dir = sbox.wc_dir
796  repo_dir = sbox.repo_dir
797
798  # Create a hook that appends its name to a log file.
799  hook_format = """import sys
800fp = open(sys.argv[1] + '/hooks.log', 'a')
801fp.write("%s\\n")
802fp.close()"""
803
804  # Setup the hook configs to log data to a file
805  start_commit_hook = svntest.main.get_start_commit_hook_path(repo_dir)
806  svntest.main.create_python_hook_script(start_commit_hook,
807                                         hook_format % "start_commit_hook")
808
809  pre_commit_hook = svntest.main.get_pre_commit_hook_path(repo_dir)
810  svntest.main.create_python_hook_script(pre_commit_hook,
811                                         hook_format % "pre_commit_hook")
812
813  post_commit_hook = svntest.main.get_post_commit_hook_path(repo_dir)
814  svntest.main.create_python_hook_script(post_commit_hook,
815                                         hook_format % "post_commit_hook")
816
817  # Modify iota just so there is something to commit.
818  iota_path = os.path.join(wc_dir, "iota")
819  svntest.main.file_append(iota_path, "More stuff in iota")
820
821  # Commit, no output expected.
822  svntest.actions.run_and_verify_svn(None, [], [],
823                                     'ci', '--quiet',
824                                     '-m', 'log msg', wc_dir)
825
826  # Now check the logfile
827  expected_data = [ 'start_commit_hook\n', 'pre_commit_hook\n', 'post_commit_hook\n' ]
828
829  logfilename = os.path.join(repo_dir, "hooks.log")
830  if os.path.exists(logfilename):
831    fp = open(logfilename)
832  else:
833    raise svntest.verify.SVNUnexpectedOutput("hook logfile %s not found")\
834                                             % logfilename
835
836  actual_data = fp.readlines()
837  fp.close()
838  os.unlink(logfilename)
839  svntest.verify.compare_and_display_lines('wrong hook logfile content',
840                                           'STDERR',
841                                           expected_data, actual_data)
842
843#----------------------------------------------------------------------
844
845# Regression test for bug #469, whereby merge() was once reporting
846# erroneous conflicts due to Ancestor < Target < Source, in terms of
847# node-rev-id parentage.
848
849def merge_mixed_revisions(sbox):
850  "commit mixed-rev wc (no erroneous merge error)"
851
852  sbox.build()
853  wc_dir = sbox.wc_dir
854
855  # Make some convenient paths.
856  iota_path = os.path.join(wc_dir, 'iota')
857  H_path = os.path.join(wc_dir, 'A', 'D', 'H')
858  chi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'chi')
859  omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
860
861  # Here's the reproduction formula, in 5 parts.
862  # Hoo, what a buildup of state!
863
864  # 1. echo "moo" >> iota; echo "moo" >> A/D/H/chi; svn ci
865  svntest.main.file_append(iota_path, "moo")
866  svntest.main.file_append(chi_path, "moo")
867
868  expected_output = svntest.wc.State(wc_dir, {
869    'iota' : Item(verb='Sending'),
870    'A/D/H/chi' : Item(verb='Sending'),
871    })
872
873  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
874  expected_status.tweak('iota', 'A/D/H/chi', wc_rev=2)
875
876  svntest.actions.run_and_verify_commit(wc_dir,
877                                        expected_output,
878                                        expected_status,
879                                        None, wc_dir)
880
881
882  # 2. svn up A/D/H
883  expected_status = svntest.wc.State(wc_dir, {
884    'A/D/H' : Item(status='  ', wc_rev=2),
885    'A/D/H/chi' : Item(status='  ', wc_rev=2),
886    'A/D/H/omega' : Item(status='  ', wc_rev=2),
887    'A/D/H/psi' : Item(status='  ', wc_rev=2),
888    })
889  expected_disk = svntest.wc.State('', {
890    'omega' : Item("This is the file 'omega'.\n"),
891    'chi' : Item("This is the file 'chi'.\nmoo"),
892    'psi' : Item("This is the file 'psi'.\n"),
893    })
894  expected_output = svntest.wc.State(wc_dir, { })
895  svntest.actions.run_and_verify_update(H_path,
896                                        expected_output,
897                                        expected_disk,
898                                        expected_status)
899
900
901  # 3. echo "moo" >> iota; svn ci iota
902  svntest.main.file_append(iota_path, "moo2")
903  expected_output = svntest.wc.State(wc_dir, {
904    'iota' : Item(verb='Sending'),
905    })
906  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
907  expected_status.tweak('A/D/H', 'A/D/H/omega', 'A/D/H/chi', 'A/D/H/psi',
908                        wc_rev=2)
909  expected_status.tweak('iota', wc_rev=3)
910
911  svntest.actions.run_and_verify_commit(wc_dir,
912                                        expected_output,
913                                        expected_status,
914                                        None, wc_dir)
915
916
917  # 4. echo "moo" >> A/D/H/chi; svn ci A/D/H/chi
918  svntest.main.file_append(chi_path, "moo3")
919  expected_output = svntest.wc.State(wc_dir, {
920    'A/D/H/chi' : Item(verb='Sending'),
921    })
922  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
923  expected_status.tweak('A/D/H/chi', wc_rev=4)
924  expected_status.tweak('A/D/H', 'A/D/H/omega', 'A/D/H/psi', wc_rev=2)
925  expected_status.tweak('iota', wc_rev=3)
926  svntest.actions.run_and_verify_commit(wc_dir,
927                                        expected_output,
928                                        expected_status,
929                                        None, wc_dir)
930
931  # 5. echo "moo" >> iota; svn ci iota
932  svntest.main.file_append(iota_path, "moomoo")
933  expected_output = svntest.wc.State(wc_dir, {
934    'iota' : Item(verb='Sending'),
935    })
936  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
937  expected_status.tweak('A/D/H', 'A/D/H/omega', 'A/D/H/psi', wc_rev=2)
938  expected_status.tweak('A/D/H/chi', wc_rev=4)
939  expected_status.tweak('iota', wc_rev=5)
940  svntest.actions.run_and_verify_commit(wc_dir,
941                                        expected_output,
942                                        expected_status,
943                                        None, wc_dir)
944
945  # At this point, here is what our tree should look like:
946  # _    1       (     5)  working_copies/commit_tests-10
947  # _    1       (     5)  working_copies/commit_tests-10/A
948  # _    1       (     5)  working_copies/commit_tests-10/A/B
949  # _    1       (     5)  working_copies/commit_tests-10/A/B/E
950  # _    1       (     5)  working_copies/commit_tests-10/A/B/E/alpha
951  # _    1       (     5)  working_copies/commit_tests-10/A/B/E/beta
952  # _    1       (     5)  working_copies/commit_tests-10/A/B/F
953  # _    1       (     5)  working_copies/commit_tests-10/A/B/lambda
954  # _    1       (     5)  working_copies/commit_tests-10/A/C
955  # _    1       (     5)  working_copies/commit_tests-10/A/D
956  # _    1       (     5)  working_copies/commit_tests-10/A/D/G
957  # _    1       (     5)  working_copies/commit_tests-10/A/D/G/pi
958  # _    1       (     5)  working_copies/commit_tests-10/A/D/G/rho
959  # _    1       (     5)  working_copies/commit_tests-10/A/D/G/tau
960  # _    2       (     5)  working_copies/commit_tests-10/A/D/H
961  # _    4       (     5)  working_copies/commit_tests-10/A/D/H/chi
962  # _    2       (     5)  working_copies/commit_tests-10/A/D/H/omega
963  # _    2       (     5)  working_copies/commit_tests-10/A/D/H/psi
964  # _    1       (     5)  working_copies/commit_tests-10/A/D/gamma
965  # _    1       (     5)  working_copies/commit_tests-10/A/mu
966  # _    5       (     5)  working_copies/commit_tests-10/iota
967
968  # At this point, we're ready to modify omega and iota, and commit
969  # from the top.  We should *not* get a conflict!
970
971  svntest.main.file_append(iota_path, "finalmoo")
972  svntest.main.file_append(omega_path, "finalmoo")
973
974  expected_output = svntest.wc.State(wc_dir, {
975    'iota' : Item(verb='Sending'),
976    'A/D/H/omega' : Item(verb='Sending'),
977    })
978  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
979  expected_status.tweak('iota', 'A/D/H/omega', wc_rev=6)
980  expected_status.tweak('A/D/H', 'A/D/H/psi', wc_rev=2)
981  expected_status.tweak('A/D/H/chi', wc_rev=4)
982  svntest.actions.run_and_verify_commit(wc_dir,
983                                        expected_output,
984                                        expected_status,
985                                        None, wc_dir)
986
987#----------------------------------------------------------------------
988
989def commit_uri_unsafe(sbox):
990  "commit files and dirs with URI-unsafe characters"
991
992  sbox.build()
993  wc_dir = sbox.wc_dir
994
995  # Note: on Windows, files can't have angle brackets in them, so we
996  # don't tests that case.
997  if svntest.main.windows or sys.platform == 'cygwin':
998    angle_name = '_angle_'
999    nasty_name = '#![]{}()__%'
1000  else:
1001    angle_name = '<angle>'
1002    nasty_name = '#![]{}()<>%'
1003
1004  # Make some convenient paths.
1005  hash_dir = os.path.join(wc_dir, '#hash#')
1006  nasty_dir = os.path.join(wc_dir, nasty_name)
1007  space_path = os.path.join(wc_dir, 'A', 'D', 'space path')
1008  bang_path = os.path.join(wc_dir, 'A', 'D', 'H', 'bang!')
1009  bracket_path = os.path.join(wc_dir, 'A', 'D', 'H', 'bra[ket')
1010  brace_path = os.path.join(wc_dir, 'A', 'D', 'H', 'bra{e')
1011  angle_path = os.path.join(wc_dir, 'A', 'D', 'H', angle_name)
1012  paren_path = os.path.join(wc_dir, 'A', 'D', 'pare)(theses')
1013  percent_path = os.path.join(wc_dir, '#hash#', 'percen%')
1014  nasty_path = os.path.join(wc_dir, 'A', nasty_name)
1015
1016  os.mkdir(hash_dir)
1017  os.mkdir(nasty_dir)
1018  svntest.main.file_append(space_path, "This path has a space in it.")
1019  svntest.main.file_append(bang_path, "This path has a bang in it.")
1020  svntest.main.file_append(bracket_path, "This path has a bracket in it.")
1021  svntest.main.file_append(brace_path, "This path has a brace in it.")
1022  svntest.main.file_append(angle_path, "This path has angle brackets in it.")
1023  svntest.main.file_append(paren_path, "This path has parentheses in it.")
1024  svntest.main.file_append(percent_path, "This path has a percent in it.")
1025  svntest.main.file_append(nasty_path, "This path has all sorts of ick in it.")
1026
1027  add_list = [hash_dir,
1028              nasty_dir, # not xml-safe
1029              space_path,
1030              bang_path,
1031              bracket_path,
1032              brace_path,
1033              angle_path, # not xml-safe
1034              paren_path,
1035              percent_path,
1036              nasty_path, # not xml-safe
1037              ]
1038  for item in add_list:
1039    svntest.main.run_svn(None, 'add', '--depth=empty', item)
1040
1041  expected_output = svntest.wc.State(wc_dir, {
1042    '#hash#' : Item(verb='Adding'),
1043    nasty_name : Item(verb='Adding'),
1044    'A/D/space path' : Item(verb='Adding'),
1045    'A/D/H/bang!' : Item(verb='Adding'),
1046    'A/D/H/bra[ket' : Item(verb='Adding'),
1047    'A/D/H/bra{e' : Item(verb='Adding'),
1048    'A/D/H/' + angle_name : Item(verb='Adding'),
1049    'A/D/pare)(theses' : Item(verb='Adding'),
1050    '#hash#/percen%' : Item(verb='Adding'),
1051    'A/' + nasty_name : Item(verb='Adding'),
1052    })
1053
1054  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1055
1056  # Items in our add list will be at rev 2
1057  for item in expected_output.desc.keys():
1058    expected_status.add({ item : Item(wc_rev=2, status='  ') })
1059
1060  svntest.actions.run_and_verify_commit(wc_dir,
1061                                        expected_output,
1062                                        expected_status,
1063                                        None, wc_dir)
1064
1065
1066#----------------------------------------------------------------------
1067
1068def commit_deleted_edited(sbox):
1069  "commit deleted yet edited files"
1070
1071  sbox.build()
1072  wc_dir = sbox.wc_dir
1073
1074  # Make some convenient paths.
1075  iota_path = os.path.join(wc_dir, 'iota')
1076  mu_path = os.path.join(wc_dir, 'A', 'mu')
1077
1078  # Edit the files.
1079  svntest.main.file_append(iota_path, "This file has been edited.")
1080  svntest.main.file_append(mu_path, "This file has been edited.")
1081
1082  # Schedule the files for removal.
1083  svntest.main.run_svn(None, 'remove', '--force', iota_path)
1084  svntest.main.run_svn(None, 'remove', '--force', mu_path)
1085
1086  # Make our output list
1087  expected_output = svntest.wc.State(wc_dir, {
1088    'iota' : Item(verb='Deleting'),
1089    'A/mu' : Item(verb='Deleting'),
1090    })
1091
1092  # Items in the status list are all at rev 1, except the two things
1093  # we changed...but then, they don't exist at all.
1094  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1095  expected_status.remove('iota', 'A/mu')
1096
1097  svntest.actions.run_and_verify_commit(wc_dir,
1098                                        expected_output,
1099                                        expected_status,
1100                                        None, wc_dir)
1101
1102#----------------------------------------------------------------------
1103
1104def commit_in_dir_scheduled_for_addition(sbox):
1105  "commit a file inside dir scheduled for addition"
1106
1107  sbox.build()
1108  wc_dir = sbox.wc_dir
1109
1110  A_path = os.path.join(wc_dir, 'A')
1111  Z_path = os.path.join(wc_dir, 'Z')
1112  mu_path = os.path.join(wc_dir, 'Z', 'mu')
1113
1114  svntest.main.run_svn(None, 'move', A_path, Z_path)
1115
1116  # Commit a copied thing inside an added-with-history directory,
1117  # expecting a specific error to occur!
1118  svntest.actions.run_and_verify_commit(wc_dir,
1119                                        None,
1120                                        None,
1121                                        "unversioned",
1122                                        mu_path)
1123
1124  Q_path = os.path.join(wc_dir, 'Q')
1125  bloo_path = os.path.join(Q_path, 'bloo')
1126
1127  os.mkdir(Q_path)
1128  svntest.main.file_append(bloo_path, "New contents.")
1129  svntest.main.run_svn(None, 'add', Q_path)
1130
1131  # Commit a regular added thing inside an added directory,
1132  # expecting a specific error to occur!
1133  svntest.actions.run_and_verify_commit(wc_dir,
1134                                        None,
1135                                        None,
1136                                        "not under version control",
1137                                        bloo_path)
1138
1139#----------------------------------------------------------------------
1140
1141# Does this make sense now that deleted files are always removed from the wc?
1142def commit_rmd_and_deleted_file(sbox):
1143  "commit deleted (and missing) file"
1144
1145  sbox.build()
1146  wc_dir = sbox.wc_dir
1147  mu_path = os.path.join(wc_dir, 'A', 'mu')
1148
1149  # 'svn remove' mu
1150  svntest.main.run_svn(None, 'rm', mu_path)
1151
1152  # Commit, hoping to see no errors
1153  svntest.actions.run_and_verify_svn("Output on stderr where none expected",
1154                                     svntest.verify.AnyOutput, [],
1155                                     'commit', '-m', 'logmsg', mu_path)
1156
1157#----------------------------------------------------------------------
1158
1159# Issue #644 which failed over ra_neon.
1160def commit_add_file_twice(sbox):
1161  "issue 644 attempt to add a file twice"
1162
1163  sbox.build()
1164  wc_dir = sbox.wc_dir
1165
1166  # Create a file
1167  gloo_path = os.path.join(wc_dir, 'A', 'D', 'H', 'gloo')
1168  svntest.main.file_append(gloo_path, "hello")
1169  svntest.main.run_svn(None, 'add', gloo_path)
1170
1171  # Create expected output tree.
1172  expected_output = svntest.wc.State(wc_dir, {
1173    'A/D/H/gloo' : Item(verb='Adding'),
1174    })
1175
1176  # Created expected status tree.
1177  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1178  expected_status.add({
1179    'A/D/H/gloo' : Item(status='  ', wc_rev=2),
1180    })
1181
1182  # Commit should succeed
1183  svntest.actions.run_and_verify_commit(wc_dir,
1184                                        expected_output,
1185                                        expected_status,
1186                                        None,
1187                                        wc_dir)
1188
1189  # Update to state before commit
1190  svntest.main.run_svn(None, 'up', '-r', '1', wc_dir)
1191
1192  # Create the file again
1193  svntest.main.file_append(gloo_path, "hello")
1194  svntest.main.run_svn(None, 'add', gloo_path)
1195
1196  # Commit and *expect* a failure:
1197  svntest.actions.run_and_verify_commit(wc_dir,
1198                                        None,
1199                                        None,
1200                                        "already exists",
1201                                        wc_dir)
1202
1203#----------------------------------------------------------------------
1204
1205# There was a problem that committing from a directory that had a
1206# longer name than the working copy directory caused the commit notify
1207# messages to display truncated/random filenames.
1208
1209def commit_from_long_dir(sbox):
1210  "commit from a dir with a longer name than the wc"
1211
1212  sbox.build()
1213  wc_dir = sbox.wc_dir
1214
1215  was_dir = os.getcwd()
1216  abs_wc_dir = os.path.realpath(os.path.join(was_dir, wc_dir))
1217
1218  # something to commit
1219  svntest.main.file_append(os.path.join(wc_dir, 'iota'), "modified iota")
1220
1221  # Create expected output tree.
1222  expected_output = svntest.wc.State('', {
1223    'iota' : Item(verb='Sending'),
1224    })
1225
1226  # Any length name was enough to provoke the original bug, but
1227  # keeping its length less than that of the filename 'iota' avoided
1228  # random behaviour, but still caused the test to fail
1229  extra_name = 'xx'
1230
1231  os.chdir(wc_dir)
1232  os.mkdir(extra_name)
1233  os.chdir(extra_name)
1234
1235  svntest.actions.run_and_verify_commit(abs_wc_dir,
1236                                        expected_output,
1237                                        None,
1238                                        None,
1239                                        abs_wc_dir)
1240
1241#----------------------------------------------------------------------
1242
1243def commit_with_lock(sbox):
1244  "try to commit when directory is locked"
1245
1246  sbox.build()
1247  # modify gamma and lock its directory
1248  wc_dir = sbox.wc_dir
1249
1250  D_path = os.path.join(wc_dir, 'A', 'D')
1251  gamma_path = os.path.join(D_path, 'gamma')
1252  svntest.main.file_append(gamma_path, "modified gamma")
1253  svntest.actions.lock_admin_dir(D_path)
1254
1255  # this commit should fail
1256  svntest.actions.run_and_verify_commit(wc_dir,
1257                                        None,
1258                                        None,
1259                                        'svn: Working copy \'.*\' locked',
1260                                        wc_dir)
1261
1262  # unlock directory
1263  svntest.actions.run_and_verify_svn("Output on stderr where none expected",
1264                                     [], [],
1265                                     'cleanup', D_path)
1266
1267  # this commit should succeed
1268  expected_output = svntest.wc.State(wc_dir, {
1269    'A/D/gamma' : Item(verb='Sending'),
1270    })
1271  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1272  expected_status.tweak('A/D/gamma', wc_rev=2)
1273  svntest.actions.run_and_verify_commit(wc_dir,
1274                                        expected_output,
1275                                        expected_status,
1276                                        None,
1277                                        wc_dir)
1278
1279#----------------------------------------------------------------------
1280
1281# Explicitly commit the current directory.  This did at one point fail
1282# in post-commit processing due to a path canonicalization problem.
1283
1284def commit_current_dir(sbox):
1285  "commit the current directory"
1286
1287  sbox.build()
1288
1289  wc_dir = sbox.wc_dir
1290  svntest.main.run_svn(None, 'propset', 'pname', 'pval', wc_dir)
1291
1292  was_cwd = os.getcwd()
1293
1294  os.chdir(wc_dir)
1295
1296  expected_output = svntest.wc.State('.', {
1297    '.' : Item(verb='Sending'),
1298    })
1299  svntest.actions.run_and_verify_commit('.',
1300                                        expected_output,
1301                                        None,
1302                                        None,
1303                                        '.')
1304  os.chdir(was_cwd)
1305
1306  # I can't get the status check to work as part of run_and_verify_commit.
1307  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1308  expected_status.tweak('', wc_rev=2, status='  ')
1309  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1310
1311#----------------------------------------------------------------------
1312
1313# Check that the pending txn gets removed from the repository after
1314# a failed commit.
1315
1316def failed_commit(sbox):
1317  "commit with conflicts and check txn in repo"
1318
1319  sbox.build()
1320  wc_dir = sbox.wc_dir
1321
1322  # Make the other working copy
1323  other_wc_dir = sbox.add_wc_path('other')
1324  svntest.actions.duplicate_dir(wc_dir, other_wc_dir)
1325
1326  # Make different changes in the two working copies
1327  iota_path = os.path.join(wc_dir, "iota")
1328  svntest.main.file_append(iota_path, "More stuff in iota")
1329
1330  other_iota_path = os.path.join(other_wc_dir, "iota")
1331  svntest.main.file_append(other_iota_path, "More different stuff in iota")
1332
1333  # Commit both working copies. The second commit should fail.
1334  svntest.actions.run_and_verify_svn("Output on stderr where none expected",
1335                                     svntest.verify.AnyOutput, [],
1336                                     'commit', '-m', 'log', wc_dir)
1337
1338  svntest.actions.run_and_verify_svn("Output on stderr expected",
1339                                     None, svntest.verify.AnyOutput,
1340                                     'commit', '-m', 'log', other_wc_dir)
1341
1342  # Now list the txns in the repo. The list should be empty.
1343  exit_code, output, errput = svntest.main.run_svnadmin('lstxns',
1344                                                        sbox.repo_dir)
1345  svntest.verify.compare_and_display_lines(
1346    "Error running 'svnadmin lstxns'.",
1347    'STDERR', [], errput)
1348  svntest.verify.compare_and_display_lines(
1349    "Output of 'svnadmin lstxns' is unexpected.",
1350    'STDOUT', [], output)
1351
1352#----------------------------------------------------------------------
1353
1354# Commit from multiple working copies is not yet supported.  At
1355# present an error is generated and none of the working copies change.
1356# Related to issue 959, this test here doesn't use svn:externals but the
1357# behaviour needs to be considered.
1358
1359def commit_multiple_wc(sbox):
1360  "attempted commit from multiple wc fails"
1361
1362  sbox.build()
1363  wc_dir = sbox.wc_dir
1364
1365  # Checkout a second working copy
1366  wc2_dir = os.path.join(wc_dir, 'A', 'wc2')
1367  url = sbox.repo_url
1368  svntest.actions.run_and_verify_svn("Output on stderr where none expected",
1369                                     svntest.verify.AnyOutput, [],
1370                                     'checkout',
1371                                     url, wc2_dir)
1372
1373  # Modify both working copies
1374  mu_path = os.path.join(wc_dir, 'A', 'mu')
1375  svntest.main.file_append(mu_path, 'appended mu text')
1376  lambda2_path = os.path.join(wc2_dir, 'A', 'B', 'lambda')
1377  svntest.main.file_append(lambda2_path, 'appended lambda2 text')
1378
1379  # Verify modified status
1380  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1381  expected_status.tweak('A/mu', status='M ')
1382  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1383  expected_status2 = svntest.actions.get_virginal_state(wc2_dir, 1)
1384  expected_status2.tweak('A/B/lambda', status='M ')
1385  svntest.actions.run_and_verify_status(wc2_dir, expected_status2)
1386
1387  # Commit should fail, even though one target is a "child" of the other.
1388  svntest.actions.run_and_verify_svn("Unexpectedly not locked",
1389                                     None, svntest.verify.AnyOutput,
1390                                     'commit', '-m', 'log',
1391                                     wc_dir, wc2_dir)
1392
1393  # Verify status unchanged
1394  svntest.actions.run_and_verify_status(wc_dir, expected_status)
1395  svntest.actions.run_and_verify_status(wc2_dir, expected_status2)
1396
1397
1398def commit_nonrecursive(sbox):
1399  "commit named targets with -N (issues #1195, #1239)"
1400
1401  sbox.build()
1402  wc_dir = sbox.wc_dir
1403
1404  ### Note: the original recipes used 'add -N'.  These days, we use
1405  ### --depth={empty,files}, and both the code and the comments below
1406  ### have been adjusted to reflect this.
1407
1408  #####################################################
1409  ### Issue #1195:
1410  ###
1411  ### 1. Create these directories and files:
1412  ###
1413  ###    file1
1414  ###    dir1
1415  ###    dir1/file2
1416  ###    dir1/file3
1417  ###    dir1/dir2
1418  ###    dir1/dir2/file4
1419  ###
1420  ### 2. run 'svn add --depth=empty <all of the above>'
1421  ###
1422  ### 3. run 'svn ci -N <all of the above>'
1423  ###
1424  ### (The bug was that only 4 entities would get committed, when it
1425  ### should be 6: dir2/ and file4 were left out.)
1426
1427  # These paths are relative to the top of the test's working copy.
1428  file1_path = 'file1'
1429  dir1_path  = 'dir1'
1430  file2_path = os.path.join('dir1', 'file2')
1431  file3_path = os.path.join('dir1', 'file3')
1432  dir2_path  = os.path.join('dir1', 'dir2')
1433  file4_path = os.path.join('dir1', 'dir2', 'file4')
1434
1435  # Create the new files and directories.
1436  svntest.main.file_append(os.path.join(wc_dir, file1_path), 'this is file1')
1437  os.mkdir(os.path.join(wc_dir, dir1_path))
1438  svntest.main.file_append(os.path.join(wc_dir, file2_path), 'this is file2')
1439  svntest.main.file_append(os.path.join(wc_dir, file3_path), 'this is file3')
1440  os.mkdir(os.path.join(wc_dir, dir2_path))
1441  svntest.main.file_append(os.path.join(wc_dir, file4_path), 'this is file4')
1442
1443  # Add them to version control.
1444  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
1445                                     'add', '--depth=empty',
1446                                     os.path.join(wc_dir, file1_path),
1447                                     os.path.join(wc_dir, dir1_path),
1448                                     os.path.join(wc_dir, file2_path),
1449                                     os.path.join(wc_dir, file3_path),
1450                                     os.path.join(wc_dir, dir2_path),
1451                                     os.path.join(wc_dir, file4_path))
1452
1453  # Commit.  We should see all 6 items (2 dirs, 4 files) get sent.
1454  expected_output = svntest.wc.State(
1455    wc_dir,
1456    { file1_path                    : Item(verb='Adding'),
1457      dir1_path                     : Item(verb='Adding'),
1458      file2_path                    : Item(verb='Adding'),
1459      file3_path                    : Item(verb='Adding'),
1460      dir2_path                     : Item(verb='Adding'),
1461      file4_path                    : Item(verb='Adding'),
1462      }
1463    )
1464
1465  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1466  expected_status.add({
1467    file1_path   : Item(status='  ', wc_rev=2),
1468    dir1_path    : Item(status='  ', wc_rev=2),
1469    file2_path   : Item(status='  ', wc_rev=2),
1470    file3_path   : Item(status='  ', wc_rev=2),
1471    dir2_path    : Item(status='  ', wc_rev=2),
1472    file4_path   : Item(status='  ', wc_rev=2),
1473    })
1474
1475  svntest.actions.run_and_verify_commit(wc_dir,
1476                                        expected_output,
1477                                        expected_status,
1478                                        None,
1479                                        '-N',
1480                                        os.path.join(wc_dir, file1_path),
1481                                        os.path.join(wc_dir, dir1_path),
1482                                        os.path.join(wc_dir, file2_path),
1483                                        os.path.join(wc_dir, file3_path),
1484                                        os.path.join(wc_dir, dir2_path),
1485                                        os.path.join(wc_dir, file4_path))
1486
1487  #######################################################################
1488  ###
1489  ### There's some complex history here; please bear with me.
1490  ###
1491  ### First there was issue #1239, which had the following recipe:
1492  ###
1493  ###    1. Create these directories and files:
1494  ###
1495  ###       dirA
1496  ###       dirA/fileA
1497  ###       dirA/fileB
1498  ###       dirA/dirB
1499  ###       dirA/dirB/fileC
1500  ###       dirA/dirB/nocommit
1501  ###
1502  ###    2. run 'svn add --depth=empty <all of the above>'
1503  ###
1504  ###    3. run 'svn ci -N <all but nocommit>'
1505  ###
1506  ###    (In this recipe, 'add -N' has been changed to 'add --depth...',
1507  ###     but 'ci -N' has been left as-is, for reasons explained below.)
1508  ###
1509  ### Issue #1239 claimed a two-part bug: that step 3 would try to
1510  ### commit the file `nocommit' when it shouldn't, and that it would
1511  ### get an error anyway:
1512  ###
1513  ###       Adding         wc/dirA
1514  ###       Adding         wc/dirA/fileA
1515  ###       Adding         wc/dirA/fileB
1516  ###       Adding         wc/dirA/dirB
1517  ###       Adding         wc/dirA/dirB/nocommit
1518  ###       Adding         wc/dirA/dirB/fileC
1519  ###       Transmitting file data ....svn: A problem occurred; \
1520  ###          see later errors for details
1521  ###       svn: Commit succeeded, but other errors follow:
1522  ###       svn: Problem running log
1523  ###       svn: Error bumping revisions post-commit (details follow):
1524  ###       svn: in directory
1525  ###       'F:/Programmation/Projets/subversion/svnant/test/wc/dirA'
1526  ###       svn: start_handler: error processing command 'committed' in
1527  ###       'F:/Programmation/Projets/subversion/svnant/test/wc/dirA'
1528  ###       svn: Working copy not locked
1529  ###       svn: directory not locked
1530  ###       (F:/Programmation/Projets/subversion/svnant/test/wc)
1531  ###
1532  ### However, this was all in the days before --depth, and depended
1533  ### on an idiosyncratic interpretation of -N, one which required
1534  ### commit to behave differently from other commands taking -N.
1535  ###
1536  ### These days, -N should be equivalent to --depth=files in almost
1537  ### all cases.  There are some exceptions (e.g., status), and commit
1538  ### is one of them: 'commit -N' means 'commit --depth=empty'.
1539  ###
1540  ### The original implementation, as well as this test, mistakenly
1541  ### mapped 'commit -N' to 'commit --depth=files'; that was a bug that
1542  ### made 'svn ci -N' incompatible with 1.4 and earlier versions.
1543  ###
1544  ### See also 'commit_propmods_with_depth_empty' in depth_tests.py .
1545
1546  # Now add these directories and files, except the last:
1547  dirA_path  = 'dirA'
1548  fileA_path = os.path.join('dirA', 'fileA')
1549  fileB_path = os.path.join('dirA', 'fileB')
1550  dirB_path  = os.path.join('dirA', 'dirB')
1551  nope_1_path = os.path.join(dirB_path, 'nope_1')
1552  nope_2_path = os.path.join(dirB_path, 'nope_2')
1553
1554  # Create the new files and directories.
1555  os.mkdir(os.path.join(wc_dir, dirA_path))
1556  svntest.main.file_append(os.path.join(wc_dir, fileA_path), 'fileA')
1557  svntest.main.file_append(os.path.join(wc_dir, fileB_path), 'fileB')
1558  os.mkdir(os.path.join(wc_dir, dirB_path))
1559  svntest.main.file_append(os.path.join(wc_dir, nope_1_path), 'nope_1')
1560  svntest.main.file_append(os.path.join(wc_dir, nope_2_path), 'nope_2')
1561
1562  # Add them to version control.
1563  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
1564                                     'add', '-N',
1565                                     os.path.join(wc_dir, dirA_path),
1566                                     os.path.join(wc_dir, fileA_path),
1567                                     # don't add fileB
1568                                     os.path.join(wc_dir, dirB_path),
1569                                     os.path.join(wc_dir, nope_1_path),
1570                                     # don't add nope_2
1571                                     )
1572
1573  expected_output = svntest.wc.State(
1574    wc_dir,
1575    { dirA_path  : Item(verb='Adding'),
1576      # no children!
1577      }
1578    )
1579
1580  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1581
1582  # Expect the leftovers from the first part of the test.
1583  expected_status.add({
1584    file1_path : Item(status='  ', wc_rev=2),
1585    dir1_path  : Item(status='  ', wc_rev=2),
1586    file2_path : Item(status='  ', wc_rev=2),
1587    file3_path : Item(status='  ', wc_rev=2),
1588    dir2_path  : Item(status='  ', wc_rev=2),
1589    file4_path : Item(status='  ', wc_rev=2),
1590    })
1591
1592  # Expect some commits and some non-commits from this part of the test.
1593  expected_status.add({
1594    dirA_path     : Item(status='  ', wc_rev=3),
1595    fileA_path    : Item(status='A ', wc_rev=0),
1596    # no fileB
1597    dirB_path     : Item(status='A ', wc_rev=0),
1598    nope_1_path   : Item(status='A ', wc_rev=0),
1599    # no nope_2
1600    })
1601
1602  svntest.actions.run_and_verify_commit(wc_dir,
1603                                        expected_output,
1604                                        expected_status,
1605                                        None,
1606                                        '-N', os.path.join(wc_dir, dirA_path))
1607
1608#----------------------------------------------------------------------
1609# Regression for #1017: ra_neon was allowing the deletion of out-of-date
1610# files or dirs, which majorly violates Subversion's semantics.
1611# An out-of-date error should be raised if the object to be committed has
1612# already been deleted or modified in the repo.
1613
1614def commit_out_of_date_deletions(sbox):
1615  "commit deletion of out-of-date file or dir"
1616
1617  # Path           WC 1    WC backup
1618  # ===========    ====    =========
1619  # A/C            pset    del
1620  # A/I            del     pset
1621  # A/B/F          del     del
1622  # A/D/H/omega    text    del
1623  # A/B/E/alpha    pset    del
1624  # A/D/H/chi      del     text
1625  # A/B/E/beta     del     pset
1626  # A/D/H/psi      del     del
1627
1628  sbox.build()
1629  wc_dir = sbox.wc_dir
1630
1631  # Need another empty dir
1632  I_path = os.path.join(wc_dir, 'A', 'I')
1633  os.mkdir(I_path)
1634  svntest.main.run_svn(None, 'add', I_path)
1635  svntest.main.run_svn(None, 'ci', '-m', 'prep', wc_dir)
1636  svntest.main.run_svn(None, 'up', wc_dir)
1637
1638  # Make a backup copy of the working copy
1639  wc_backup = sbox.add_wc_path('backup')
1640  svntest.actions.duplicate_dir(wc_dir, wc_backup)
1641
1642  # Edits in wc 1
1643  C_path = os.path.join(wc_dir, 'A', 'C')
1644  omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
1645  alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
1646  svntest.main.run_svn(None, 'propset', 'fooprop', 'foopropval', C_path)
1647  svntest.main.file_append(omega_path, 'appended omega text')
1648  svntest.main.run_svn(None, 'propset', 'fooprop', 'foopropval', alpha_path)
1649
1650  # Deletions in wc 1
1651  I_path = os.path.join(wc_dir, 'A', 'I')
1652  F_path = os.path.join(wc_dir, 'A', 'B', 'F')
1653  chi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'chi')
1654  beta_path = os.path.join(wc_dir, 'A', 'B', 'E', 'beta')
1655  psi_path = os.path.join(wc_dir, 'A', 'D', 'H', 'psi')
1656  svntest.main.run_svn(None, 'rm', I_path, F_path, chi_path, beta_path,
1657                       psi_path)
1658
1659  # Commit in wc 1
1660  expected_output = svntest.wc.State(wc_dir, {
1661      'A/C' : Item(verb='Sending'),
1662      'A/I' : Item(verb='Deleting'),
1663      'A/B/F' : Item(verb='Deleting'),
1664      'A/D/H/omega' : Item(verb='Sending'),
1665      'A/B/E/alpha' : Item(verb='Sending'),
1666      'A/D/H/chi' : Item(verb='Deleting'),
1667      'A/B/E/beta' : Item(verb='Deleting'),
1668      'A/D/H/psi' : Item(verb='Deleting'),
1669      })
1670  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
1671  expected_status.tweak('A/C', 'A/D/H/omega', 'A/B/E/alpha', wc_rev=3,
1672                        status='  ')
1673  expected_status.remove('A/B/F', 'A/D/H/chi', 'A/B/E/beta', 'A/D/H/psi')
1674  commit = svntest.actions.run_and_verify_commit
1675  commit(wc_dir, expected_output, expected_status, None, wc_dir)
1676
1677  # Edits in wc backup
1678  I_path = os.path.join(wc_backup, 'A', 'I')
1679  chi_path = os.path.join(wc_backup, 'A', 'D', 'H', 'chi')
1680  beta_path = os.path.join(wc_backup, 'A', 'B', 'E','beta')
1681  svntest.main.run_svn(None, 'propset', 'fooprop', 'foopropval', I_path)
1682  svntest.main.file_append(chi_path, 'appended chi text')
1683  svntest.main.run_svn(None, 'propset', 'fooprop', 'foopropval', beta_path)
1684
1685  # Deletions in wc backup
1686  C_path = os.path.join(wc_backup, 'A', 'C')
1687  F_path = os.path.join(wc_backup, 'A', 'B', 'F')
1688  omega_path = os.path.join(wc_backup, 'A', 'D', 'H', 'omega')
1689  alpha_path = os.path.join(wc_backup, 'A', 'B', 'E', 'alpha')
1690  psi_path = os.path.join(wc_backup, 'A', 'D', 'H', 'psi')
1691  svntest.main.run_svn(None, 'rm', C_path, F_path, omega_path, alpha_path,
1692                       psi_path)
1693
1694  # A commit of any one of these files or dirs should fail
1695  error_re = "out of date"
1696  commit(wc_backup, None, None, error_re, C_path)
1697  commit(wc_backup, None, None, error_re, I_path)
1698  commit(wc_backup, None, None, error_re, F_path)
1699  commit(wc_backup, None, None, error_re, omega_path)
1700  commit(wc_backup, None, None, error_re, alpha_path)
1701  commit(wc_backup, None, None, error_re, chi_path)
1702  commit(wc_backup, None, None, error_re, beta_path)
1703  commit(wc_backup, None, None, error_re, psi_path)
1704
1705def commit_with_bad_log_message(sbox):
1706  "commit with a log message containing bad data"
1707
1708  sbox.build()
1709  wc_dir = sbox.wc_dir
1710
1711  iota_path = os.path.join(wc_dir, 'iota')
1712  log_msg_path = os.path.join(wc_dir, 'log-message')
1713
1714  # Make a random change, so there's something to commit.
1715  svntest.main.file_append(iota_path, 'fish')
1716
1717  # Create a log message containing a zero-byte.
1718  svntest.main.file_append(log_msg_path, '\x00')
1719
1720  # Commit and expect an error.
1721  svntest.actions.run_and_verify_commit(wc_dir,
1722                                        None, None,
1723                                        "contains a zero byte",
1724                                        '-F', log_msg_path,
1725                                        iota_path)
1726
1727def commit_with_mixed_line_endings(sbox):
1728  "commit with log message with mixed EOL"
1729
1730  sbox.build()
1731  wc_dir = sbox.wc_dir
1732
1733  expected_status = make_standard_slew_of_changes(wc_dir)
1734
1735  iota_path = os.path.join(wc_dir, 'iota')
1736  log_msg_path = os.path.join(wc_dir, 'log-message')
1737
1738  # Make a random change, so there's something to commit.
1739  svntest.main.file_append(iota_path, 'kebab')
1740
1741  # Create a log message containing a zero-byte.
1742  svntest.main.file_append(log_msg_path, "test\nthis\n\rcase\r\n--This line, and those below, will be ignored--\n")
1743
1744  # Commit and expect an error.
1745  svntest.actions.run_and_verify_commit(wc_dir,
1746                                        None, None,
1747                                        "Error normalizing log message to internal format",
1748                                        '-F', log_msg_path,
1749                                        iota_path)
1750
1751def commit_with_mixed_line_endings_in_ignored_part(sbox):
1752  "commit with log message with mixed EOL in tail"
1753
1754  sbox.build()
1755  wc_dir = sbox.wc_dir
1756
1757  expected_status = make_standard_slew_of_changes(wc_dir)
1758
1759  iota_path = os.path.join(wc_dir, 'iota')
1760  log_msg_path = os.path.join(wc_dir, 'log-message')
1761
1762  # Make a random change, so there's something to commit.
1763  svntest.main.file_append(iota_path, 'cheeseburger')
1764
1765  # Create a log message containing a zero-byte.
1766  svntest.main.file_append(log_msg_path, "test\n--This line, and those below, will be ignored--\nfoo\r\nbar\nbaz\n\r")
1767
1768  # Create expected state.
1769  expected_output = svntest.wc.State(wc_dir, {
1770    'iota' : Item(verb='Sending'),
1771    })
1772  expected_status.tweak('iota', wc_rev=2, status='  ')
1773
1774  # Commit the one file.
1775  svntest.actions.run_and_verify_commit(wc_dir,
1776                                        expected_output,
1777                                        expected_status,
1778                                        None,
1779                                        iota_path)
1780
1781def from_wc_top_with_bad_editor(sbox):
1782  "commit with invalid external editor cmd"
1783
1784  # Shortly after revision 5407, Vladimir Prus posted this bug recipe:
1785  #
1786  #   #!/bin/bash
1787  #   cd /tmp
1788  #   rm -rf repo wc
1789  #   svnadmin create repo
1790  #   svn mkdir file:///tmp/repo/foo -m ""
1791  #   svn co file:///tmp/repo/foo wc
1792  #   cd wc
1793  #   svn ps svn:externals "lib http://something.org/lib" .
1794  #   svn ci
1795  #
1796  # The final 'svn ci' would seg fault because of a problem in
1797  # calculating the paths to insert in the initial log message that
1798  # gets passed to the editor.
1799  #
1800  # So this regression test is primarily about making sure the seg
1801  # fault is gone, and only secondarily about testing that we get the
1802  # expected error from passing a bad editor cmd to Subversion.
1803
1804  sbox.build()
1805  wc_dir = sbox.wc_dir
1806
1807  svntest.actions.run_and_verify_svn("Unexpected failure from propset.",
1808                                     svntest.verify.AnyOutput, [],
1809                                     'pset', 'fish', 'food', wc_dir)
1810  os.chdir(wc_dir)
1811  exit_code, out, err = svntest.actions.run_and_verify_svn(
1812    "Commit succeeded when should have failed.",
1813    None, svntest.verify.AnyOutput,
1814    'ci', '--editor-cmd', 'no_such-editor')
1815
1816  err = " ".join(map(str.strip, err))
1817  if not (re.match(".*no_such-editor.*", err)
1818          and re.match(".*Commit failed.*", err)):
1819    print("Commit failed, but not in the way expected.")
1820    raise svntest.Failure
1821
1822
1823def mods_in_schedule_delete(sbox):
1824  "commit with mods in schedule delete"
1825
1826  sbox.build()
1827  wc_dir = sbox.wc_dir
1828
1829  # Schedule a delete, then put in local mods
1830  C_path = os.path.join(wc_dir, 'A', 'C')
1831  svntest.actions.run_and_verify_svn(None, svntest.verify.AnyOutput, [],
1832                                     'rm', C_path)
1833  foo_path = os.path.join(C_path, 'foo')
1834  foo_contents = 'zig\nzag\n'
1835  svntest.main.file_append(foo_path, foo_contents)
1836
1837  # Commit should succeed
1838  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
1839  expected_status.remove('A/C')
1840  expected_output = svntest.wc.State(wc_dir, {
1841    'A/C' : Item(verb='Deleting'),
1842    })
1843  svntest.actions.run_and_verify_commit(wc_dir,
1844                                        expected_output, expected_status,
1845                                        None, wc_dir)
1846
1847  # Unversioned file still exists
1848  actual_contents = svntest.main.file_read(foo_path)
1849  if actual_contents != foo_contents:
1850    raise svntest.Failure
1851
1852
1853#----------------------------------------------------------------------
1854
1855def tab_test(sbox):
1856  "tabs in paths"
1857  # For issue #1954.
1858
1859  sbox.build()
1860  wc_dir = sbox.wc_dir
1861
1862  tab_file = os.path.join(wc_dir, 'A', "tab\tfile")
1863  tab_dir  = os.path.join(wc_dir, 'A', "tab\tdir")
1864  source_url = sbox.repo_url + "/source_dir"
1865  tab_url = sbox.repo_url + "/tab%09dir"
1866
1867  svntest.main.file_append(tab_file, "This file has a tab in it.")
1868  os.mkdir(tab_dir)
1869
1870  def match_bad_tab_path(path, errlines):
1871    match_re = ".*: Invalid control character '0x09' in path .*"
1872    for line in errlines:
1873      if re.match (match_re, line):
1874        break
1875    else:
1876      raise svntest.Failure("Failed to find match_re in " + str(errlines))
1877
1878  # add file to wc
1879  exit_code, outlines, errlines = svntest.main.run_svn(1, 'add', tab_file)
1880  match_bad_tab_path(tab_file, errlines)
1881
1882  # add dir to wc
1883  exit_code, outlines, errlines = svntest.main.run_svn(1, 'add', tab_dir)
1884  match_bad_tab_path(tab_dir, errlines)
1885
1886  # mkdir URL
1887  exit_code, outlines, errlines = svntest.main.run_svn(1, 'mkdir',
1888                                                       '-m', 'msg', tab_url)
1889  match_bad_tab_path(tab_dir, errlines)
1890
1891  # copy URL
1892  svntest.main.run_svn(1,
1893                       'mkdir', '-m', 'msg', source_url)
1894  exit_code, outlines, errlines = svntest.main.run_svn(1, 'copy',
1895                                                       '-m', 'msg',
1896                                                       source_url, tab_url)
1897  match_bad_tab_path(tab_dir, errlines)
1898
1899  # mv URL
1900  exit_code, outlines, errlines = svntest.main.run_svn(1, 'mv', '-m', 'msg',
1901                                                       source_url, tab_url)
1902  match_bad_tab_path(tab_dir, errlines)
1903
1904#----------------------------------------------------------------------
1905
1906def local_mods_are_not_commits(sbox):
1907  "local ops should not be treated like commits"
1908
1909  # For issue #2285.
1910  #
1911  # Some commands can run on either a URL or a local path.  These
1912  # commands take a log message, intended for the URL case.
1913  # Therefore, they should make sure that getting a log message for
1914  # a local operation errors (because not committing).
1915  #
1916  # This is in commit_tests.py because the unifying theme is that
1917  # commits are *not* happening.  And because there was no better
1918  # place to put it :-).
1919
1920  sbox.build()
1921  wc_dir = sbox.wc_dir
1922  expected_error = '.*Local, non-commit operations do not take a log message.*'
1923
1924  # copy wc->wc
1925  svntest.actions.run_and_verify_svn(None, None, expected_error,
1926                                     'cp', '-m', 'log msg',
1927                                     os.path.join(wc_dir, 'iota'),
1928                                     os.path.join(wc_dir, 'iota2'))
1929
1930  # copy repos->wc
1931  svntest.actions.run_and_verify_svn(None, None, expected_error,
1932                                     'cp', '-m', 'log msg',
1933                                     sbox.repo_url + "/iota",
1934                                     os.path.join(wc_dir, 'iota2'))
1935
1936  # delete
1937  svntest.actions.run_and_verify_svn(None, None, expected_error,
1938                                     'rm', '-m', 'log msg',
1939                                     os.path.join(wc_dir, 'A', 'D', 'gamma'))
1940
1941  # mkdir
1942  svntest.actions.run_and_verify_svn(None, None, expected_error,
1943                                     'mkdir', '-m', 'log msg',
1944                                     os.path.join(wc_dir, 'newdir'))
1945
1946  # rename
1947  svntest.actions.run_and_verify_svn(None, None, expected_error,
1948                                     'cp', '-m', 'log msg',
1949                                     os.path.join(wc_dir, 'A', 'mu'),
1950                                     os.path.join(wc_dir, 'A', 'yu'))
1951
1952# Helper for hook tests: returns the "hook failed" line, with precise
1953# wording that changed with Subversion 1.5.
1954def hook_failure_message(hookname):
1955  if svntest.main.server_minor_version < 5:
1956    return "'%s' hook failed with error output:\n" % hookname
1957  else:
1958    if hookname in ["start-commit", "pre-commit"]:
1959      action = "Commit"
1960    elif hookname == "pre-revprop-change":
1961      action = "Revprop change"
1962    elif hookname == "pre-lock":
1963      action = "Lock"
1964    elif hookname == "pre-unlock":
1965      action = "Unlock"
1966    else:
1967      action = None
1968    if action is None:
1969      message = "%s hook failed (exit code 1)" % (hookname,)
1970    else:
1971      message = "%s blocked by %s hook (exit code 1)" % (action, hookname)
1972    return message + " with output:\n"
1973
1974
1975#----------------------------------------------------------------------
1976# Test if the post-commit error message is returned back to the svn
1977# client and is displayed as a warning.
1978#
1979def post_commit_hook_test(sbox):
1980  "post commit hook failure case testing"
1981
1982  sbox.build()
1983
1984  # Get paths to the working copy and repository
1985  wc_dir = sbox.wc_dir
1986  repo_dir = sbox.repo_dir
1987
1988  # Disable commits
1989  svntest.actions.create_failing_post_commit_hook(repo_dir)
1990
1991  # Modify iota just so there is something to commit.
1992  iota_path = os.path.join(wc_dir, "iota")
1993  svntest.main.file_append(iota_path, "lakalakalakalaka")
1994
1995  # Now, commit and examine the output (we happen to know that the
1996  # filesystem will report an absolute path because that's the way the
1997  # filesystem is created by this test suite.
1998  expected_output = [ "Sending        "+ iota_path + "\n",
1999                      "Transmitting file data .\n",
2000                      "Committed revision 2.\n",
2001                      "\n",
2002                      "Warning: " + hook_failure_message('post-commit'),
2003                      "Post-commit hook failed\n",
2004                    ]
2005
2006  svntest.actions.run_and_verify_svn(None, expected_output, [],
2007                                     'ci', '-m', 'log msg', iota_path)
2008
2009#----------------------------------------------------------------------
2010# Commit two targets non-recursively, but both targets should be the
2011# same folder (in multiple variations). Test that svn handles this correctly.
2012def commit_same_folder_in_targets(sbox):
2013  "commit two targets, both the same folder"
2014
2015  sbox.build()
2016  wc_dir = sbox.wc_dir
2017
2018  iota_path = os.path.join(wc_dir, 'iota')
2019
2020  svntest.main.file_append(iota_path, "added extra line to file iota")
2021
2022  # Create expected output tree.
2023  expected_output = svntest.wc.State(wc_dir, {
2024    'iota' : Item(verb='Sending'),
2025    })
2026
2027  # Created expected status tree.
2028  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2029  expected_status.tweak('iota', wc_rev=2)
2030
2031  # Commit the wc_dir and iota.
2032  svntest.actions.run_and_verify_commit(wc_dir,
2033                                        expected_output,
2034                                        expected_status,
2035                                        None,
2036                                        '-N',
2037                                        wc_dir,
2038                                        iota_path)
2039
2040#----------------------------------------------------------------------
2041# test for issue 2459: verify that commit fails when a file with mixed
2042# eol-styles is included, and show an error message which includes the
2043# filename.
2044def commit_inconsistent_eol(sbox):
2045  "commit files with inconsistent eol should fail"
2046
2047  sbox.build()
2048  wc_dir = sbox.wc_dir
2049
2050  iota_path = os.path.join(wc_dir, 'iota')
2051  mu_path = os.path.join(wc_dir, 'A', 'mu')
2052
2053  svntest.main.run_svn(None, 'propset', 'svn:eol-style', 'native', iota_path)
2054  svntest.main.file_append_binary(iota_path,
2055                                  "added extra line to file iota\012"
2056                                  "added extra line to file iota\015")
2057  svntest.main.file_append(mu_path, "added extra line to file mu\n"
2058                                    "added extra line to file mu\n")
2059
2060  expected_err = ".*iota.*"
2061
2062  svntest.actions.run_and_verify_svn(None, None, expected_err,
2063                                     'commit', '-m', 'log message',
2064                                     wc_dir)
2065
2066
2067def mkdir_with_revprop(sbox):
2068  "set revision props during remote mkdir"
2069
2070  sbox.build()
2071  remote_dir = sbox.repo_url + "/dir"
2072
2073  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
2074                                     '--with-revprop', 'bug=42', remote_dir)
2075
2076  expected = svntest.verify.UnorderedOutput(
2077                  ['Unversioned properties on revision 2:\n',
2078                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2079                   '  bug\n'])
2080  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2081                                     '--revprop', '-r', 2, sbox.repo_url)
2082  svntest.actions.run_and_verify_svn(None, '42', [], 'propget', 'bug',
2083                                     '--revprop', '-r', 2, sbox.repo_url)
2084
2085
2086def delete_with_revprop(sbox):
2087  "set revision props during remote delete"
2088
2089  sbox.build()
2090  remote_dir = sbox.repo_url + "/dir"
2091  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
2092                                     remote_dir)
2093
2094  svntest.actions.run_and_verify_svn(None, None, [], 'delete', '-m', 'msg',
2095                                     '--with-revprop', 'bug=52', remote_dir)
2096
2097  expected = svntest.verify.UnorderedOutput(
2098                  ['Unversioned properties on revision 3:\n',
2099                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2100                   '  bug\n'])
2101  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2102                                     '--revprop', '-r', 3, sbox.repo_url)
2103  svntest.actions.run_and_verify_svn(None, '52', [], 'propget', 'bug',
2104                                     '--revprop', '-r', 3, sbox.repo_url)
2105
2106
2107def commit_with_revprop(sbox):
2108  "set revision props during commit"
2109
2110  sbox.build()
2111  wc_dir = sbox.wc_dir
2112  expected_status = make_standard_slew_of_changes(wc_dir)
2113
2114  omega_path = os.path.join(wc_dir, 'A', 'D', 'H', 'omega')
2115  gloo_path = os.path.join(wc_dir, 'A', 'D', 'H', 'gloo')
2116  expected_output = svntest.wc.State(wc_dir, {
2117    'A/D/H/omega' : Item(verb='Sending'),
2118    'A/D/H/gloo' : Item(verb='Adding'),
2119    })
2120
2121  expected_status.tweak('A/D/H/omega', wc_rev=2, status='  ')
2122  expected_status.tweak('A/D/H/gloo', wc_rev=2, status='  ')
2123
2124  svntest.actions.run_and_verify_commit(wc_dir,
2125                                        expected_output,
2126                                        expected_status,
2127                                        None,
2128                                        '-m', 'msg',
2129                                        '--with-revprop', 'bug=62',
2130                                        omega_path, gloo_path)
2131
2132  expected = svntest.verify.UnorderedOutput(
2133                  ['Unversioned properties on revision 2:\n',
2134                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2135                   '  bug\n'])
2136  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2137                                     '--revprop', '-r', 2, sbox.repo_url)
2138  svntest.actions.run_and_verify_svn(None, '62', [], 'propget', 'bug',
2139                                     '--revprop', '-r', 2, sbox.repo_url)
2140
2141
2142def import_with_revprop(sbox):
2143  "set revision props during import"
2144
2145  sbox.build()
2146  local_dir = os.path.join(sbox.wc_dir, 'folder')
2147  local_file = os.path.join(sbox.wc_dir, 'folder', 'file')
2148  os.mkdir(local_dir)
2149  svntest.main.file_write(local_file, "xxxx")
2150
2151  svntest.actions.run_and_verify_svn(None, None, [], 'import', '-m', 'msg',
2152                                     '--with-revprop', 'bug=72', local_dir,
2153                                     sbox.repo_url)
2154
2155  expected = svntest.verify.UnorderedOutput(
2156                  ['Unversioned properties on revision 2:\n',
2157                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2158                   '  bug\n'])
2159  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2160                                     '--revprop', '-r', 2, sbox.repo_url)
2161  svntest.actions.run_and_verify_svn(None, '72', [], 'propget', 'bug',
2162                                     '--revprop', '-r', 2, sbox.repo_url)
2163
2164
2165def copy_R2R_with_revprop(sbox):
2166  "set revision props during repos-to-repos copy"
2167
2168  sbox.build()
2169  remote_dir1 = sbox.repo_url + "/dir1"
2170  remote_dir2 = sbox.repo_url + "/dir2"
2171  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
2172                                     remote_dir1)
2173
2174  svntest.actions.run_and_verify_svn(None, None, [], 'copy', '-m', 'msg',
2175                                     '--with-revprop', 'bug=82', remote_dir1,
2176                                     remote_dir2)
2177
2178  expected = svntest.verify.UnorderedOutput(
2179                  ['Unversioned properties on revision 3:\n',
2180                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2181                   '  bug\n'])
2182  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2183                                     '--revprop', '-r', 3, sbox.repo_url)
2184  svntest.actions.run_and_verify_svn(None, '82', [], 'propget', 'bug',
2185                                     '--revprop', '-r', 3, sbox.repo_url)
2186
2187
2188def copy_WC2R_with_revprop(sbox):
2189  "set revision props during wc-to-repos copy"
2190
2191  sbox.build()
2192  remote_dir = sbox.repo_url + "/dir"
2193  local_dir = os.path.join(sbox.wc_dir, 'folder')
2194  svntest.actions.run_and_verify_svn(None, None, [],
2195                                     'mkdir', local_dir)
2196
2197  svntest.actions.run_and_verify_svn(None, None, [], 'copy', '-m', 'msg',
2198                                     '--with-revprop', 'bug=92', local_dir,
2199                                     remote_dir)
2200
2201  expected = svntest.verify.UnorderedOutput(
2202                  ['Unversioned properties on revision 2:\n',
2203                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2204                   '  bug\n'])
2205  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2206                                     '--revprop', '-r', 2, sbox.repo_url)
2207  svntest.actions.run_and_verify_svn(None, '92', [], 'propget', 'bug',
2208                                     '--revprop', '-r', 2, sbox.repo_url)
2209
2210
2211def move_R2R_with_revprop(sbox):
2212  "set revision props during repos-to-repos move"
2213
2214  sbox.build()
2215  remote_dir1 = sbox.repo_url + "/dir1"
2216  remote_dir2 = sbox.repo_url + "/dir2"
2217  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
2218                                     remote_dir1)
2219
2220  svntest.actions.run_and_verify_svn(None, None, [], 'move', '-m', 'msg',
2221                                     '--with-revprop', 'bug=102', remote_dir1,
2222                                     remote_dir2)
2223
2224  expected = svntest.verify.UnorderedOutput(
2225                  ['Unversioned properties on revision 3:\n',
2226                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2227                   '  bug\n'])
2228  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2229                                     '--revprop', '-r', 3, sbox.repo_url)
2230  svntest.actions.run_and_verify_svn(None, '102', [], 'propget', 'bug',
2231                                     '--revprop', '-r', 3, sbox.repo_url)
2232
2233
2234def propedit_with_revprop(sbox):
2235  "set revision props during remote property edit"
2236
2237  sbox.build()
2238  svntest.main.use_editor('append_foo')
2239
2240  svntest.actions.run_and_verify_svn(None, None, [], 'propedit', '-m', 'msg',
2241                                     '--with-revprop', 'bug=112', 'prop',
2242                                     sbox.repo_url)
2243
2244  expected = svntest.verify.UnorderedOutput(
2245                  ['Unversioned properties on revision 2:\n',
2246                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2247                   '  bug\n'])
2248  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2249                                     '--revprop', '-r', 2, sbox.repo_url)
2250  svntest.actions.run_and_verify_svn(None, '112', [], 'propget', 'bug',
2251                                     '--revprop', '-r', 2, sbox.repo_url)
2252
2253
2254def set_multiple_props_with_revprop(sbox):
2255  "set multiple revision props during remote mkdir"
2256
2257  sbox.build()
2258  remote_dir = sbox.repo_url + "/dir"
2259
2260  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
2261                                     '--with-revprop', 'bug=32',
2262                                     '--with-revprop', 'ref=22', remote_dir)
2263
2264  expected = svntest.verify.UnorderedOutput(
2265                  ['Unversioned properties on revision 2:\n',
2266                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2267                   '  bug\n', '  ref\n'])
2268  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2269                                     '--revprop', '-r', 2, sbox.repo_url)
2270  svntest.actions.run_and_verify_svn(None, '32', [], 'propget', 'bug',
2271                                     '--revprop', '-r', 2, sbox.repo_url)
2272  svntest.actions.run_and_verify_svn(None, '22', [], 'propget', 'ref',
2273                                     '--revprop', '-r', 2, sbox.repo_url)
2274
2275
2276def use_empty_value_in_revprop_pair(sbox):
2277  "set revprop without value ('') during remote mkdir"
2278
2279  sbox.build()
2280  remote_dir = sbox.repo_url + "/dir"
2281
2282  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
2283                                     '--with-revprop', 'bug=',
2284                                     '--with-revprop', 'ref=', remote_dir)
2285
2286  expected = svntest.verify.UnorderedOutput(
2287                  ['Unversioned properties on revision 2:\n',
2288                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2289                   '  bug\n', '  ref\n'])
2290  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2291                                     '--revprop', '-r', 2, sbox.repo_url)
2292  svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'bug',
2293                                     '--revprop', '-r', 2, sbox.repo_url)
2294  svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'ref',
2295                                     '--revprop', '-r', 2, sbox.repo_url)
2296
2297
2298def no_equals_in_revprop_pair(sbox):
2299  "set revprop without '=' during remote mkdir"
2300
2301  sbox.build()
2302  remote_dir = sbox.repo_url + "/dir"
2303  svntest.actions.run_and_verify_svn(None, None, [], 'mkdir', '-m', 'msg',
2304                                     '--with-revprop', 'bug',
2305                                     '--with-revprop', 'ref', remote_dir)
2306
2307  expected = svntest.verify.UnorderedOutput(
2308                  ['Unversioned properties on revision 2:\n',
2309                   '  svn:author\n','  svn:date\n',  '  svn:log\n',
2310                   '  bug\n', '  ref\n'])
2311  svntest.actions.run_and_verify_svn(None, expected, [], 'proplist',
2312                                     '--revprop', '-r', 2, sbox.repo_url)
2313  svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'bug',
2314                                     '--revprop', '-r', 2, sbox.repo_url)
2315  svntest.actions.run_and_verify_svn(None, '', [], 'propget', 'ref',
2316                                     '--revprop', '-r', 2, sbox.repo_url)
2317
2318
2319def set_invalid_revprops(sbox):
2320  "set invalid revision props during remote mkdir"
2321
2322  sbox.build()
2323  remote_dir = sbox.repo_url + "/dir"
2324  # Try to set svn: revprops.
2325  expected = '.*Standard properties can\'t.*'
2326  svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
2327                                     '--with-revprop', 'svn:author=42', remote_dir)
2328  svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
2329                                     '--with-revprop', 'svn:log=42', remote_dir)
2330  svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
2331                                     '--with-revprop', 'svn:date=42', remote_dir)
2332  svntest.actions.run_and_verify_svn(None, [], expected, 'mkdir', '-m', 'msg',
2333                                     '--with-revprop', 'svn:foo=bar', remote_dir)
2334
2335  # Empty revprop pair.
2336  svntest.actions.run_and_verify_svn(None, [],
2337                                     'svn: Revision property pair is empty',
2338                                     'mkdir', '-m', 'msg',
2339                                     '--with-revprop', '',
2340                                     remote_dir)
2341
2342#----------------------------------------------------------------------
2343
2344def start_commit_hook_test(sbox):
2345  "start-commit hook failure case testing"
2346
2347  sbox.build()
2348
2349  # Get paths to the working copy and repository
2350  wc_dir = sbox.wc_dir
2351  repo_dir = sbox.repo_dir
2352
2353  # Create a hook that outputs a message to stderr and returns exit code 1
2354  hook_code = """import sys
2355sys.stderr.write("Start-commit hook failed")
2356sys.exit(1)"""
2357
2358  # Setup the hook configs to log data to a file
2359  start_commit_hook = svntest.main.get_start_commit_hook_path(repo_dir)
2360  svntest.main.create_python_hook_script(start_commit_hook, hook_code)
2361
2362  # Modify iota just so there is something to commit.
2363  iota_path = os.path.join(wc_dir, "iota")
2364  svntest.main.file_append(iota_path, "More stuff in iota")
2365
2366  # Commit, expect error code 1
2367  exit_code, actual_stdout, actual_stderr = svntest.main.run_svn(
2368    1, 'ci', '--quiet', '-m', 'log msg', wc_dir)
2369
2370  # No stdout expected
2371  svntest.verify.compare_and_display_lines('Start-commit hook test',
2372                                           'STDOUT', [], actual_stdout)
2373
2374  # Compare only the last two lines of stderr since the preceding ones
2375  # contain source code file and line numbers.
2376  if len(actual_stderr) > 2:
2377    actual_stderr = actual_stderr[-2:]
2378  expected_stderr = [ "svn: " + hook_failure_message('start-commit'),
2379                      "Start-commit hook failed\n"
2380                    ]
2381  svntest.verify.compare_and_display_lines('Start-commit hook test',
2382                                           'STDERR',
2383                                           expected_stderr, actual_stderr)
2384
2385#----------------------------------------------------------------------
2386
2387def pre_commit_hook_test(sbox):
2388  "pre-commit hook failure case testing"
2389
2390  sbox.build()
2391
2392  # Get paths to the working copy and repository
2393  wc_dir = sbox.wc_dir
2394  repo_dir = sbox.repo_dir
2395
2396  # Create a hook that outputs a message to stderr and returns exit code 1
2397  hook_code = """import sys
2398sys.stderr.write("Pre-commit hook failed")
2399sys.exit(1)"""
2400
2401  # Setup the hook configs to log data to a file
2402  pre_commit_hook = svntest.main.get_pre_commit_hook_path(repo_dir)
2403  svntest.main.create_python_hook_script(pre_commit_hook, hook_code)
2404
2405  # Modify iota just so there is something to commit.
2406  iota_path = os.path.join(wc_dir, "iota")
2407  svntest.main.file_append(iota_path, "More stuff in iota")
2408
2409  # Commit, expect error code 1
2410  exit_code, actual_stdout, actual_stderr = svntest.main.run_svn(
2411    1, 'ci', '--quiet', '-m', 'log msg', wc_dir)
2412
2413  # No stdout expected
2414  svntest.verify.compare_and_display_lines('Pre-commit hook test',
2415                                           'STDOUT', [], actual_stdout)
2416
2417  # Compare only the last two lines of stderr since the preceding ones
2418  # contain source code file and line numbers.
2419  if len(actual_stderr) > 2:
2420    actual_stderr = actual_stderr[-2:]
2421  expected_stderr = [ "svn: " + hook_failure_message('pre-commit'),
2422                      "Pre-commit hook failed\n"
2423                    ]
2424  svntest.verify.compare_and_display_lines('Pre-commit hook test',
2425                                           'STDERR',
2426                                           expected_stderr, actual_stderr)
2427
2428#----------------------------------------------------------------------
2429
2430def versioned_log_message(sbox):
2431  "'svn commit -F foo' when foo is a versioned file"
2432
2433  sbox.build()
2434
2435  os.chdir(sbox.wc_dir)
2436
2437  iota_path = os.path.join('iota')
2438  mu_path = os.path.join('A', 'mu')
2439  log_path = os.path.join('A', 'D', 'H', 'omega')
2440
2441  svntest.main.file_append(iota_path, "2")
2442
2443  # try to check in a change using a versioned file as your log entry.
2444  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
2445                                     'ci', '-F', log_path)
2446
2447  # force it.  should not produce any errors.
2448  svntest.actions.run_and_verify_svn(None, None, [],
2449                                     'ci', '-F', log_path, '--force-log')
2450
2451  svntest.main.file_append(mu_path, "2")
2452
2453  # try the same thing, but specifying the file to commit explicitly.
2454  svntest.actions.run_and_verify_svn(None, None, svntest.verify.AnyOutput,
2455                                     'ci', '-F', log_path, mu_path)
2456
2457  # force it...  should succeed.
2458  svntest.actions.run_and_verify_svn(None, None, [],
2459                                     'ci',
2460                                     '-F', log_path,
2461                                     '--force-log', mu_path)
2462
2463#----------------------------------------------------------------------
2464
2465def changelist_near_conflict(sbox):
2466  "'svn commit --changelist=foo' above a conflict"
2467
2468  sbox.build()
2469
2470  wc_dir = sbox.wc_dir
2471  iota_path = os.path.join(wc_dir, "iota")
2472  mu_path = os.path.join(wc_dir, "A", "mu")
2473  gloo_path = os.path.join(wc_dir, "A", "D", "H", "gloo")
2474
2475  expected_status = make_standard_slew_of_changes(wc_dir)
2476
2477  # Create a changelist.
2478  changelist_name = "logical-changeset"
2479  svntest.actions.run_and_verify_svn(None, None, [],
2480                                     "changelist", changelist_name,
2481                                     mu_path, gloo_path)
2482
2483  # Create a conflict (making r2 in the process).
2484  inject_conflict_into_wc(sbox, 'iota', iota_path,
2485                          None, expected_status, 2)
2486
2487  # Commit the changelist.
2488  expected_output = svntest.wc.State(wc_dir, {
2489    "A/D/H/gloo" : Item(verb='Adding'),
2490    })
2491  expected_status.tweak("A/D/H/gloo", wc_rev=3, status="  ")
2492  svntest.actions.run_and_verify_commit(wc_dir,
2493                                        expected_output,
2494                                        expected_status,
2495                                        None,
2496                                        "--changelist=" + changelist_name,
2497                                        "-m", "msg", wc_dir)
2498
2499
2500#----------------------------------------------------------------------
2501
2502def commit_out_of_date_file(sbox):
2503  "try to commit a file that is out-of-date"
2504
2505  sbox.build()
2506  wc_dir = sbox.wc_dir
2507
2508  # Make a backup copy of the working copy
2509  wc_backup = sbox.add_wc_path('backup')
2510  svntest.actions.duplicate_dir(wc_dir, wc_backup)
2511
2512  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
2513  backup_pi_path = os.path.join(wc_backup, 'A', 'D', 'G', 'pi')
2514
2515  svntest.main.file_append(pi_path, "new line\n")
2516  expected_output = svntest.wc.State(wc_dir, {
2517    "A/D/G/pi" : Item(verb='Sending'),
2518    })
2519  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
2520  expected_status.tweak("A/D/G/pi", wc_rev=2, status="  ")
2521  svntest.actions.run_and_verify_commit(wc_dir,
2522                                        expected_output,
2523                                        expected_status,
2524                                        None,
2525                                        "-m", "log message", wc_dir)
2526
2527  svntest.main.file_append(backup_pi_path, "hello")
2528  expected_err = ".*(pi.*out of date|Out of date.*pi).*"
2529  svntest.actions.run_and_verify_svn(None, None, expected_err,
2530                                     'commit', '-m', 'log message',
2531                                     wc_backup)
2532
2533def start_commit_detect_capabilities(sbox):
2534  "start-commit hook sees client capabilities"  # Issue #2991
2535  sbox.build()
2536  wc_dir = sbox.wc_dir
2537  repos_dir = sbox.repo_dir
2538
2539  # Create a start-commit hook that detects the "mergeinfo" capability.
2540  hook_text = "import sys\n"                                 + \
2541              "fp = open(sys.argv[1] + '/hooks.log', 'w')\n" + \
2542              "caps = sys.argv[3].split(':')\n"              + \
2543              "if 'mergeinfo' in caps:\n"                    + \
2544              "  fp.write('yes')\n"                          + \
2545              "else:\n"                                      + \
2546              "  fp.write('no')\n"                           + \
2547              "fp.close()\n"
2548
2549  start_commit_hook = svntest.main.get_start_commit_hook_path(repos_dir)
2550  svntest.main.create_python_hook_script(start_commit_hook, hook_text)
2551
2552  # Commit something.
2553  iota_path = os.path.join(wc_dir, "iota")
2554  svntest.main.file_append(iota_path, "More stuff in iota")
2555  svntest.actions.run_and_verify_svn(None, [], [], 'ci', '--quiet',
2556                                     '-m', 'log msg', wc_dir)
2557
2558  # Check that "mergeinfo" was detected.
2559  log_path = os.path.join(repos_dir, "hooks.log")
2560  if os.path.exists(log_path):
2561    data = open(log_path).read()
2562    os.unlink(log_path)
2563  else:
2564    raise svntest.verify.SVNUnexpectedOutput("'%s' not found") % log_path
2565  if data != 'yes':
2566    raise svntest.Failure
2567
2568def commit_url(sbox):
2569  "'svn commit SOME_URL' should error"
2570  sbox.build()
2571  wc_dir = sbox.wc_dir
2572  repos_url = sbox.repo_url
2573
2574  # Commit directly to a URL
2575  svntest.actions.run_and_verify_commit(None,
2576                                        None,
2577                                        None,
2578                                        "Must give local path",
2579                                        repos_url)
2580
2581# Test for issue #3198
2582def commit_added_missing(sbox):
2583  "commit a missing to-be-added file should fail"
2584
2585  sbox.build()
2586  wc_dir = sbox.wc_dir
2587  mu_path = os.path.join(wc_dir, 'A', 'mu')
2588  a_path = os.path.join(wc_dir, 'A', 'a.txt')
2589  b_path = os.path.join(wc_dir, 'A', 'b.txt')
2590
2591  # Make two copies of mu: a and b
2592  svntest.main.run_svn(None, 'cp', mu_path, a_path)
2593  svntest.main.run_svn(None, 'cp', mu_path, b_path)
2594
2595  # remove b, make it missing
2596  os.remove(b_path)
2597
2598  # Commit, hoping to see an error
2599  svntest.actions.run_and_verify_svn("Commit should have failed",
2600                                     [], ".* is scheduled for addition, but is missing",
2601                                     'commit', '-m', 'logmsg', wc_dir)
2602
2603#----------------------------------------------------------------------
2604
2605# Helper for commit-failure tests
2606def commit_fails_at_path(path, wc_dir, error_re):
2607  svntest.actions.run_and_verify_commit(wc_dir,
2608                                        None,
2609                                        None,
2610                                        error_re,
2611                                        path)
2612
2613def tree_conflicts_block_commit(sbox):
2614  "tree conflicts block commit"
2615
2616  # Commit is not allowed in a directory containing tree conflicts.
2617  # This test corresponds to use cases 1-3 (with file victims) in
2618  # notes/tree-conflicts/use-cases.txt.
2619
2620  svntest.actions.build_greek_tree_conflicts(sbox)
2621  wc_dir = sbox.wc_dir
2622  A = os.path.join(wc_dir, 'A')
2623  D = os.path.join(wc_dir, 'A', 'D')
2624  G = os.path.join(wc_dir, 'A', 'D', 'G')
2625
2626  error_re = "remains in conflict"
2627  commit_fails_at_path(wc_dir, wc_dir, error_re)
2628  commit_fails_at_path(A, A, error_re)
2629  commit_fails_at_path(D, D, error_re)
2630  commit_fails_at_path(G, G, error_re)
2631  commit_fails_at_path(os.path.join(G, 'pi'), G, error_re)
2632
2633
2634def tree_conflicts_resolved(sbox):
2635  "tree conflicts resolved"
2636
2637  # Commit is allowed after tree conflicts are resolved.
2638  # This test corresponds to use cases 1-3 in
2639  # notes/tree-conflicts/use-cases.txt.
2640
2641  svntest.actions.build_greek_tree_conflicts(sbox)
2642  wc_dir = sbox.wc_dir
2643
2644  # Duplicate wc for tests
2645  wc_dir_2 = sbox.add_wc_path('2')
2646  svntest.actions.duplicate_dir(wc_dir, wc_dir_2)
2647
2648  # Mark the tree conflict victims as resolved
2649  G = os.path.join(wc_dir, 'A', 'D', 'G')
2650  victims = [ os.path.join(G, v) for v in ['pi', 'rho', 'tau'] ]
2651  svntest.actions.run_and_verify_resolved(victims)
2652
2653  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
2654  expected_status.tweak('A/D/G/pi',  status='D ')
2655  expected_status.tweak('A/D/G/rho', status='A ', copied='+', wc_rev='-')
2656  expected_status.remove('A/D/G/tau')
2657
2658  svntest.actions.run_and_verify_status(wc_dir, expected_status)
2659
2660  # Recursively resolved in parent directory -- expect same result
2661  G2 = os.path.join(wc_dir_2, 'A', 'D', 'G')
2662  victims = [ os.path.join(G2, v) for v in ['pi', 'rho', 'tau'] ]
2663  svntest.actions.run_and_verify_resolved(victims, G2, '-R')
2664
2665  expected_status.wc_dir = wc_dir_2
2666  svntest.actions.run_and_verify_status(wc_dir_2, expected_status)
2667
2668
2669########################################################################
2670# Run the tests
2671
2672# list all tests here, starting with None:
2673test_list = [ None,
2674              commit_one_file,
2675              commit_one_new_file,
2676              commit_one_new_binary_file,
2677              commit_multiple_targets,
2678              commit_multiple_targets_2,
2679              commit_inclusive_dir,
2680              commit_top_dir,
2681              commit_unversioned_thing,
2682              nested_dir_replacements,
2683              hudson_part_1,
2684              hudson_part_1_variation_1,
2685              hudson_part_1_variation_2,
2686              hudson_part_2,
2687              hudson_part_2_1,
2688              hook_test,
2689              merge_mixed_revisions,
2690              commit_uri_unsafe,
2691              commit_deleted_edited,
2692              commit_in_dir_scheduled_for_addition,
2693              commit_rmd_and_deleted_file,
2694              commit_add_file_twice,
2695              commit_from_long_dir,
2696              commit_with_lock,
2697              commit_current_dir,
2698              commit_multiple_wc,
2699              commit_nonrecursive,
2700              failed_commit,
2701              XFail(commit_out_of_date_deletions, svntest.main.is_ra_type_svn),
2702              commit_with_bad_log_message,
2703              commit_with_mixed_line_endings,
2704              commit_with_mixed_line_endings_in_ignored_part,
2705              from_wc_top_with_bad_editor,
2706              mods_in_schedule_delete,
2707              Skip(tab_test, is_non_posix_os_or_cygwin_platform),
2708              local_mods_are_not_commits,
2709              post_commit_hook_test,
2710              commit_same_folder_in_targets,
2711              commit_inconsistent_eol,
2712              SkipUnless(mkdir_with_revprop, server_has_revprop_commit),
2713              SkipUnless(delete_with_revprop, server_has_revprop_commit),
2714              SkipUnless(commit_with_revprop, server_has_revprop_commit),
2715              SkipUnless(import_with_revprop, server_has_revprop_commit),
2716              SkipUnless(copy_R2R_with_revprop, server_has_revprop_commit),
2717              SkipUnless(copy_WC2R_with_revprop, server_has_revprop_commit),
2718              SkipUnless(move_R2R_with_revprop, server_has_revprop_commit),
2719              SkipUnless(propedit_with_revprop, server_has_revprop_commit),
2720              SkipUnless(set_multiple_props_with_revprop,
2721                         server_has_revprop_commit),
2722              SkipUnless(use_empty_value_in_revprop_pair,
2723                         server_has_revprop_commit),
2724              SkipUnless(no_equals_in_revprop_pair, server_has_revprop_commit),
2725              SkipUnless(set_invalid_revprops, server_has_revprop_commit),
2726              start_commit_hook_test,
2727              pre_commit_hook_test,
2728              versioned_log_message,
2729              changelist_near_conflict,
2730              commit_out_of_date_file,
2731              SkipUnless(start_commit_detect_capabilities,
2732                         server_gets_client_capabilities),
2733              commit_url,
2734              commit_added_missing,
2735              tree_conflicts_block_commit,
2736              tree_conflicts_resolved,
2737             ]
2738
2739if __name__ == '__main__':
2740  svntest.main.run_tests(test_list)
2741  # NOTREACHED
2742
2743
2744### End of file.
Note: See TracBrowser for help on using the repository browser.