1#!/usr/bin/env python
2#
3#  resolve_tests.py:  testing 'svn resolve'
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.apache.org for more information.
7#
8# ====================================================================
9#    Licensed to the Apache Software Foundation (ASF) under one
10#    or more contributor license agreements.  See the NOTICE file
11#    distributed with this work for additional information
12#    regarding copyright ownership.  The ASF licenses this file
13#    to you under the Apache License, Version 2.0 (the
14#    "License"); you may not use this file except in compliance
15#    with the License.  You may obtain a copy of the License at
16#
17#      http://www.apache.org/licenses/LICENSE-2.0
18#
19#    Unless required by applicable law or agreed to in writing,
20#    software distributed under the License is distributed on an
21#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22#    KIND, either express or implied.  See the License for the
23#    specific language governing permissions and limitations
24#    under the License.
25######################################################################
26
27# General modules
28import shutil, sys, re, os, stat
29import time
30
31# Our testing module
32import svntest
33from svntest import wc
34
35# (abbreviation)
36Item = wc.StateItem
37Skip = svntest.testcase.Skip_deco
38SkipUnless = svntest.testcase.SkipUnless_deco
39XFail = svntest.testcase.XFail_deco
40Issues = svntest.testcase.Issues_deco
41Issue = svntest.testcase.Issue_deco
42Wimp = svntest.testcase.Wimp_deco
43
44from svntest.mergetrees import set_up_branch
45from svntest.mergetrees import expected_merge_output
46
47
48######################################################################
49# Tests
50#
51#   Each test must return on success or raise on failure.
52
53#----------------------------------------------------------------------
54# 'svn resolve --accept [ base | mine-full | theirs-full ]' was segfaulting
55# on 1.6.x.  Prior to this test, the bug was only caught by the Ruby binding
56# tests, see http://svn.haxx.se/dev/archive-2010-01/0088.shtml.
57def automatic_conflict_resolution(sbox):
58  "resolve -R --accept [base | mf | tf]"
59
60  sbox.build()
61  wc_dir = sbox.wc_dir
62
63  # Some paths we'll care about
64  A_COPY_path   = os.path.join(wc_dir, "A_COPY")
65  psi_COPY_path = os.path.join(wc_dir, "A_COPY", "D", "H", "psi")
66
67  # Branch A to A_COPY in r2, then make some changes under 'A' in r3-6.
68  wc_disk, wc_status = set_up_branch(sbox)
69
70  # Make a change on the A_COPY branch such that a subsequent merge
71  # conflicts.
72  svntest.main.file_write(psi_COPY_path, "Branch content.\n")
73  svntest.actions.run_and_verify_svn(None, [],
74                                     'commit', '-m', 'log msg', wc_dir)
75  def do_text_conflicting_merge():
76    svntest.actions.run_and_verify_svn(None, [],
77                                       'revert', '--recursive', A_COPY_path)
78    svntest.actions.run_and_verify_svn(
79      expected_merge_output([[3]], [
80        "C    %s\n" % psi_COPY_path,
81        " U   %s\n" % A_COPY_path],
82        target=A_COPY_path, text_conflicts=1),
83      [], 'merge', '-c3', '--allow-mixed-revisions',
84      sbox.repo_url + '/A',
85      A_COPY_path)
86
87  # Test 'svn resolve -R --accept base'
88  do_text_conflicting_merge()
89  svntest.actions.run_and_verify_resolve([psi_COPY_path],
90                                         '-R', '--accept', 'base',
91                                         A_COPY_path)
92  wc_disk.tweak('A_COPY/D/H/psi', contents="This is the file 'psi'.\n")
93  svntest.actions.verify_disk(wc_dir, wc_disk)
94
95  # Test 'svn resolve -R --accept mine-full'
96  do_text_conflicting_merge()
97  svntest.actions.run_and_verify_resolve([psi_COPY_path],
98                                         '-R', '--accept', 'mine-full',
99                                         A_COPY_path)
100  wc_disk.tweak('A_COPY/D/H/psi', contents="Branch content.\n")
101  svntest.actions.verify_disk(wc_dir, wc_disk)
102
103  # Test 'svn resolve -R --accept theirs-full'
104  do_text_conflicting_merge()
105  svntest.actions.run_and_verify_resolve([psi_COPY_path],
106                                         '-R', '--accept', 'tf',
107                                         A_COPY_path)
108  wc_disk.tweak('A_COPY/D/H/psi', contents="New content")
109  svntest.actions.verify_disk(wc_dir, wc_disk)
110
111#----------------------------------------------------------------------
112# Test for issue #3707 'property conflicts not handled correctly by
113# svn resolve'.
114@Issue(3707)
115def prop_conflict_resolution(sbox):
116  "resolving prop conflicts"
117
118  sbox.build()
119  wc_dir = sbox.wc_dir
120
121  # Some paths we'll care about
122  iota_path  = os.path.join(wc_dir, "iota")
123  mu_path    = os.path.join(wc_dir, "A", "mu")
124  gamma_path = os.path.join(wc_dir, "A", "D", "gamma")
125  psi_path   = os.path.join(wc_dir, "A", "D", "H", "psi")
126
127  # r2 - Set property 'propname:propval' on iota, A/mu, and A/D/gamma.
128  svntest.actions.run_and_verify_svn(None, [],
129                                     'ps', 'propname', 'propval',
130                                     iota_path, mu_path, gamma_path)
131  svntest.actions.run_and_verify_svn(None, [], 'commit',
132                                     '-m', 'create some new properties',
133                                     wc_dir)
134
135  # r3 - Make some changes to the props from r2:
136  #
137  #   iota      : Delete property 'propname'
138  #   A/mu      : Change property 'propname' to 'incoming-conflict'
139  #   A/D/gamma : Change property 'propname' to 'incoming-no-conflict'
140  svntest.actions.run_and_verify_svn(None, [],
141                                     'pd', 'propname', iota_path)
142  svntest.actions.run_and_verify_svn(None, [],
143                                     'ps', 'propname', 'incoming-conflict',
144                                     mu_path)
145  svntest.actions.run_and_verify_svn(None, [],
146                                     'ps', 'propname', 'incoming-no-conflict',
147                                     gamma_path)
148  svntest.actions.run_and_verify_svn(None, [],
149                                     'commit', '-m', 'delete a property',
150                                     wc_dir)
151
152  def do_prop_conflicting_up_and_resolve(resolve_accept,
153                                         resolved_deleted_prop_val_output,
154                                         resolved_edited_prop_val_output):
155
156    """Revert the WC, update it to r2, and set the following properties:
157
158    iota      : 'propname' = 'local_edit'
159                'newprop'  = 'new-val-no-incoming'
160    A/mu      : 'propname' = 'local_edit'
161    A/D/gamma : 'propname' = 'incoming-no-conflict'
162    A/D/H/psi : 'newprop'  = 'new-val-no-incoming'
163
164    Update the WC, postponing conflicts, then run svn resolve -R
165    --accept=RESOLVE_ACCEPT.
166
167    Using svn propget, check that the resolution results in the following
168    properties:
169
170    iota      : 'propname' = RESOLVED_DELETED_PROP_VAL_OUTPUT
171                'newprop'  = 'new-val-no-incoming'
172    A/mu      : 'propname' = RESOLVED_EDITED_PROP_VAL_OUTPUT
173    A/D/gamma : 'propname' = 'incoming-no-conflict'
174    A/D/H/psi : 'newprop'  = 'new-val-no-incoming'
175
176    RESOLVED_DELETED_PROP_VAL_OUTPUT and RESOLVED_EDITED_PROP_VAL_OUTPUT
177    both follow the rules for the expected_stdout arg to
178    run_and_verify_svn2()"""
179
180    svntest.actions.run_and_verify_svn(None, [],
181                                       'revert', '--recursive', wc_dir)
182    svntest.actions.run_and_verify_svn(None, [], 'up', '-r2', wc_dir)
183
184    # Set some properties that will conflict when we update.
185    svntest.actions.run_and_verify_svn(None, [], 'ps',
186                                       'propname', 'local_edit',
187                                       iota_path, mu_path)
188
189    # Set a property that should always merge cleanly with the update.
190    svntest.actions.run_and_verify_svn(None, [], 'ps',
191                                       'propname', 'incoming-no-conflict',
192                                       gamma_path)
193
194    # Set a property that has no update coming.
195    svntest.actions.run_and_verify_svn(None, [], 'ps',
196                                       'newprop', 'new-val-no-incoming',
197                                       psi_path,
198                                       iota_path)
199
200    # Update, postponing all conflict resolution.
201    svntest.actions.run_and_verify_svn(None, [], 'up',
202                                       '--accept=postpone', wc_dir)
203    svntest.actions.run_and_verify_resolve([iota_path, mu_path], '-R',
204                                           '--accept', resolve_accept, wc_dir)
205    if resolved_deleted_prop_val_output:
206      expected_deleted_stderr = []
207    else:
208      expected_deleted_stderr = '.*W200017: Property.*not found'
209
210    svntest.actions.run_and_verify_svn(
211      resolved_deleted_prop_val_output, expected_deleted_stderr,
212      'pg', 'propname', iota_path)
213    svntest.actions.run_and_verify_svn(
214      ['new-val-no-incoming\n'], [], 'pg', 'newprop', iota_path)
215    svntest.actions.run_and_verify_svn(
216      resolved_edited_prop_val_output, [], 'pg', 'propname', mu_path)
217    svntest.actions.run_and_verify_svn(
218      ['incoming-no-conflict\n'], [], 'pg', 'propname', gamma_path)
219    svntest.actions.run_and_verify_svn(
220      ['new-val-no-incoming\n'], [], 'pg', 'newprop', psi_path)
221
222  # Test how svn resolve deals with prop conflicts and other local
223  # prop changes:
224  #
225  #   1) 'iota' - An incoming prop delete on a local prop modification.
226  #   2) 'A/mu' - An incoming prop edit on a local prop modification.
227  #   3) 'A/D/gamma' - An local, non-conflicted prop edit
228  #
229  # Previously this failed because svn resolve --accept=[theirs-conflict |
230  # theirs-full] removed the conflicts, but didn't install 'their' version
231  # of the conflicted properties.
232  do_prop_conflicting_up_and_resolve('mine-full',
233                                     ['local_edit\n'],
234                                     ['local_edit\n'])
235  do_prop_conflicting_up_and_resolve('mine-conflict',
236                                     ['local_edit\n'],
237                                     ['local_edit\n'])
238  do_prop_conflicting_up_and_resolve('working',
239                                    ['local_edit\n'],
240                                     ['local_edit\n'])
241  do_prop_conflicting_up_and_resolve('theirs-conflict',
242                                     [], # Prop deleted
243                                     ['incoming-conflict\n'])
244  do_prop_conflicting_up_and_resolve('theirs-full',
245                                     [], # Prop deleted
246                                     ['incoming-conflict\n'])
247
248#----------------------------------------------------------------------
249@SkipUnless(svntest.main.is_posix_os)
250def auto_resolve_executable_file(sbox):
251  "resolve file with executable bit set"
252  sbox.build()
253  wc_dir = sbox.wc_dir
254
255  # Mark iota as executable
256  sbox.simple_propset("svn:executable", '*', 'iota')
257  sbox.simple_commit() # r2
258
259  # Make a change to iota in r3
260  svntest.main.file_write(sbox.ospath('iota'), "boo\n")
261  sbox.simple_commit() # r3
262
263  # Update back to r2, and tweak iota to provoke a text conflict
264  sbox.simple_update(revision=2)
265  svntest.main.file_write(sbox.ospath('iota'), "bzzt\n")
266
267  # Get permission bits of iota
268  mode = os.stat(sbox.ospath('iota'))[stat.ST_MODE]
269
270  # Update back to r3, and auto-resolve the text conflict.
271  svntest.main.run_svn(False, 'update', wc_dir, '--accept', 'theirs-full')
272
273  # permission bits of iota should be unaffected
274  if mode != os.stat(sbox.ospath('iota'))[stat.ST_MODE]:
275    raise svntest.Failure
276
277#----------------------------------------------------------------------
278def resolved_on_wc_root(sbox):
279  "resolved on working copy root"
280
281  sbox.build()
282  wc = sbox.wc_dir
283
284  i = os.path.join(wc, 'iota')
285  B = os.path.join(wc, 'A', 'B')
286  g = os.path.join(wc, 'A', 'D', 'gamma')
287
288  # Create some conflicts...
289  # Commit mods
290  svntest.main.file_append(i, "changed iota.\n")
291  svntest.main.file_append(g, "changed gamma.\n")
292  svntest.actions.run_and_verify_svn(None, [],
293                                     'propset', 'foo', 'foo-val', B)
294
295  expected_output = svntest.wc.State(wc, {
296      'iota'              : Item(verb='Sending'),
297      'A/B'               : Item(verb='Sending'),
298      'A/D/gamma'         : Item(verb='Sending'),
299    })
300
301  expected_status = svntest.actions.get_virginal_state(wc, 1)
302  expected_status.tweak('iota', 'A/B', 'A/D/gamma', wc_rev = 2)
303
304  svntest.actions.run_and_verify_commit(wc,
305                                        expected_output,
306                                        expected_status)
307
308  # Go back to rev 1
309  expected_output = svntest.wc.State(wc, {
310    'iota'              : Item(status='U '),
311    'A/B'               : Item(status=' U'),
312    'A/D/gamma'         : Item(status='U '),
313  })
314  expected_status = svntest.actions.get_virginal_state(wc, 1)
315  expected_disk = svntest.main.greek_state.copy()
316  svntest.actions.run_and_verify_update(wc,
317                                        expected_output,
318                                        expected_disk,
319                                        expected_status,
320                                        [], False,
321                                        '-r1', wc)
322
323  # Deletions so that the item becomes unversioned and
324  # will have a tree-conflict upon update.
325  svntest.actions.run_and_verify_svn(None, [],
326                                     'rm', i, B, g)
327
328  # Update so that conflicts appear
329  expected_output = svntest.wc.State(wc, {
330    'iota'              : Item(status='  ', treeconflict='C'),
331    'A/B'               : Item(status='  ', treeconflict='C'),
332    'A/D/gamma'         : Item(status='  ', treeconflict='C'),
333  })
334
335  expected_disk = svntest.main.greek_state.copy()
336  expected_disk.remove('iota',
337                       'A/B',
338                       'A/B/lambda',
339                       'A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
340                       'A/B/F',
341                       'A/D/gamma')
342
343  expected_status = svntest.actions.get_virginal_state(wc, 2)
344  expected_status.tweak('iota', 'A/B', 'A/D/gamma',
345                        status='D ', treeconflict='C')
346  expected_status.tweak('A/B/lambda', 'A/B/E', 'A/B/E/alpha', 'A/B/E/beta',
347                        'A/B/F', status='D ')
348  svntest.actions.run_and_verify_update(wc,
349                                        expected_output,
350                                        expected_disk,
351                                        None,
352                                        [], False,
353                                        wc)
354  svntest.actions.run_and_verify_unquiet_status(wc, expected_status)
355
356  # Resolve recursively
357  svntest.actions.run_and_verify_resolved([i, B, g], '--depth=infinity', wc)
358
359  expected_status.tweak('iota', 'A/B', 'A/D/gamma', treeconflict=None)
360  svntest.actions.run_and_verify_unquiet_status(wc, expected_status)
361
362#----------------------------------------------------------------------
363@SkipUnless(svntest.main.server_has_mergeinfo)
364def resolved_on_deleted_item(sbox):
365  "resolved on deleted item"
366
367  sbox.build()
368  wc = sbox.wc_dir
369
370  A = os.path.join(wc, 'A',)
371  B = os.path.join(wc, 'A', 'B')
372  g = os.path.join(wc, 'A', 'D', 'gamma')
373  A2 = os.path.join(wc, 'A2')
374  B2 = os.path.join(A2, 'B')
375  g2 = os.path.join(A2, 'D', 'gamma')
376
377  A_url = sbox.repo_url + '/A'
378  A2_url = sbox.repo_url + '/A2'
379
380  # make a copy of A
381  svntest.actions.run_and_verify_svn(None, [],
382                                     'cp', A_url, A2_url, '-m', 'm')
383
384  expected_output = svntest.wc.State(wc, {
385    'A2'                : Item(status='A '),
386    'A2/B'              : Item(status='A '),
387    'A2/B/lambda'       : Item(status='A '),
388    'A2/B/E'            : Item(status='A '),
389    'A2/B/E/alpha'      : Item(status='A '),
390    'A2/B/E/beta'       : Item(status='A '),
391    'A2/B/F'            : Item(status='A '),
392    'A2/mu'             : Item(status='A '),
393    'A2/C'              : Item(status='A '),
394    'A2/D'              : Item(status='A '),
395    'A2/D/gamma'        : Item(status='A '),
396    'A2/D/G'            : Item(status='A '),
397    'A2/D/G/pi'         : Item(status='A '),
398    'A2/D/G/rho'        : Item(status='A '),
399    'A2/D/G/tau'        : Item(status='A '),
400    'A2/D/H'            : Item(status='A '),
401    'A2/D/H/chi'        : Item(status='A '),
402    'A2/D/H/omega'      : Item(status='A '),
403    'A2/D/H/psi'        : Item(status='A '),
404  })
405
406  expected_disk = svntest.main.greek_state.copy()
407  expected_disk.add({
408    'A2/mu'             : Item(contents="This is the file 'mu'.\n"),
409    'A2/D/gamma'        : Item(contents="This is the file 'gamma'.\n"),
410    'A2/D/H/psi'        : Item(contents="This is the file 'psi'.\n"),
411    'A2/D/H/omega'      : Item(contents="This is the file 'omega'.\n"),
412    'A2/D/H/chi'        : Item(contents="This is the file 'chi'.\n"),
413    'A2/D/G/rho'        : Item(contents="This is the file 'rho'.\n"),
414    'A2/D/G/pi'         : Item(contents="This is the file 'pi'.\n"),
415    'A2/D/G/tau'        : Item(contents="This is the file 'tau'.\n"),
416    'A2/B/lambda'       : Item(contents="This is the file 'lambda'.\n"),
417    'A2/B/F'            : Item(),
418    'A2/B/E/beta'       : Item(contents="This is the file 'beta'.\n"),
419    'A2/B/E/alpha'      : Item(contents="This is the file 'alpha'.\n"),
420    'A2/C'              : Item(),
421  })
422
423  expected_status = svntest.actions.get_virginal_state(wc, 2)
424  expected_status.add({
425    'A2'                : Item(),
426    'A2/B'              : Item(),
427    'A2/B/lambda'       : Item(),
428    'A2/B/E'            : Item(),
429    'A2/B/E/alpha'      : Item(),
430    'A2/B/E/beta'       : Item(),
431    'A2/B/F'            : Item(),
432    'A2/mu'             : Item(),
433    'A2/C'              : Item(),
434    'A2/D'              : Item(),
435    'A2/D/gamma'        : Item(),
436    'A2/D/G'            : Item(),
437    'A2/D/G/pi'         : Item(),
438    'A2/D/G/rho'        : Item(),
439    'A2/D/G/tau'        : Item(),
440    'A2/D/H'            : Item(),
441    'A2/D/H/chi'        : Item(),
442    'A2/D/H/omega'      : Item(),
443    'A2/D/H/psi'        : Item(),
444  })
445  expected_status.tweak(status='  ', wc_rev='2')
446
447  svntest.actions.run_and_verify_update(wc,
448                                        expected_output,
449                                        expected_disk,
450                                        expected_status,
451                                        [], False,
452                                        wc)
453
454  # Create some conflicts...
455
456  # Modify the paths in the one directory.
457  svntest.actions.run_and_verify_svn(None, [],
458                                     'propset', 'foo', 'foo-val', B)
459  svntest.main.file_append(g, "Modified gamma.\n")
460
461  expected_output = svntest.wc.State(wc, {
462      'A/B'               : Item(verb='Sending'),
463      'A/D/gamma'         : Item(verb='Sending'),
464    })
465
466  expected_status.tweak('A/B', 'A/D/gamma', wc_rev='3')
467
468  svntest.actions.run_and_verify_commit(wc,
469                                        expected_output,
470                                        expected_status)
471
472  # Delete the paths in the second directory.
473  svntest.actions.run_and_verify_svn(None, [],
474                                     'rm', B2, g2)
475
476  expected_output = svntest.wc.State(wc, {
477      'A2/B'              : Item(verb='Deleting'),
478      'A2/D/gamma'        : Item(verb='Deleting'),
479    })
480
481  expected_status.remove('A2/B', 'A2/B/lambda',
482                         'A2/B/E', 'A2/B/E/alpha', 'A2/B/E/beta',
483                         'A2/B/F',
484                         'A2/D/gamma')
485
486  svntest.actions.run_and_verify_commit(wc,
487                                        expected_output,
488                                        expected_status,
489                                        [],
490                                        A2)
491
492  # Now merge A to A2, creating conflicts...
493
494  expected_output = svntest.wc.State(A2, {
495      'B'                 : Item(status='  ', treeconflict='C'),
496      'D/gamma'           : Item(status='  ', treeconflict='C'),
497    })
498  expected_mergeinfo_output = svntest.wc.State(A2, {
499      '' : Item(status=' U')
500    })
501  expected_elision_output = svntest.wc.State(A2, {
502    })
503  expected_disk = svntest.wc.State('', {
504      'mu'                : Item(contents="This is the file 'mu'.\n"),
505      'D'                 : Item(),
506      'D/H'               : Item(),
507      'D/H/psi'           : Item(contents="This is the file 'psi'.\n"),
508      'D/H/omega'         : Item(contents="This is the file 'omega'.\n"),
509      'D/H/chi'           : Item(contents="This is the file 'chi'.\n"),
510      'D/G'               : Item(),
511      'D/G/rho'           : Item(contents="This is the file 'rho'.\n"),
512      'D/G/pi'            : Item(contents="This is the file 'pi'.\n"),
513      'D/G/tau'           : Item(contents="This is the file 'tau'.\n"),
514      'C'                 : Item(),
515    })
516
517  expected_skip = svntest.wc.State(wc, {
518    })
519
520  expected_status = svntest.wc.State(A2, {
521    ''                  : Item(status=' M', wc_rev='2'),
522    'D'                 : Item(status='  ', wc_rev='2'),
523    'D/gamma'           : Item(status='! ', treeconflict='C'),
524    'D/G'               : Item(status='  ', wc_rev='2'),
525    'D/G/pi'            : Item(status='  ', wc_rev='2'),
526    'D/G/rho'           : Item(status='  ', wc_rev='2'),
527    'D/G/tau'           : Item(status='  ', wc_rev='2'),
528    'D/H'               : Item(status='  ', wc_rev='2'),
529    'D/H/chi'           : Item(status='  ', wc_rev='2'),
530    'D/H/omega'         : Item(status='  ', wc_rev='2'),
531    'D/H/psi'           : Item(status='  ', wc_rev='2'),
532    'B'                 : Item(status='! ', treeconflict='C'),
533    'mu'                : Item(status='  ', wc_rev='2'),
534    'C'                 : Item(status='  ', wc_rev='2'),
535  })
536
537  svntest.actions.run_and_verify_merge(A2, None, None, A_url, None,
538                                       expected_output,
539                                       expected_mergeinfo_output,
540                                       expected_elision_output,
541                                       expected_disk, None, expected_skip,
542                                       [], dry_run = False)
543  svntest.actions.run_and_verify_unquiet_status(A2, expected_status)
544
545  # Now resolve by recursing on the working copy root.
546  svntest.actions.run_and_verify_resolved([B2, g2], '--depth=infinity', wc)
547
548  expected_status.remove('B', 'D/gamma')
549  svntest.actions.run_and_verify_unquiet_status(A2, expected_status)
550
551#----------------------------------------------------------------------
552
553def theirs_conflict_in_subdir(sbox):
554  "resolve to 'theirs-conflict' in sub-directory"
555
556  sbox.build()
557  wc = sbox.wc_dir
558  wc2 = sbox.add_wc_path('wc2')
559  svntest.actions.duplicate_dir(sbox.wc_dir, wc2)
560
561  alpha_path = os.path.join(wc, 'A', 'B', 'E', 'alpha')
562  alpha_path2 = os.path.join(wc2, 'A', 'B', 'E', 'alpha')
563
564  svntest.main.file_append(alpha_path, "Modified alpha.\n")
565  sbox.simple_commit(message='logmsg')
566
567  svntest.main.file_append(alpha_path2, "Modified alpha, too.\n")
568  svntest.main.run_svn(None, 'up', wc2)
569
570  svntest.actions.run_and_verify_resolve([alpha_path2],
571                                         '--accept=theirs-conflict',
572                                         alpha_path2)
573
574#----------------------------------------------------------------------
575
576# Regression test for issue #4238 "merge -cA,B with --accept option aborts
577# if rA conflicts".
578@Issue(4238)
579def multi_range_merge_with_accept(sbox):
580  "multi range merge with --accept keeps going"
581
582  sbox.build()
583  os.chdir(sbox.wc_dir)
584  sbox.wc_dir = ''
585
586  # Commit some changes
587  for c in [2, 3, 4]:
588    svntest.main.file_append('iota', 'Change ' + str(c) + '\n')
589    sbox.simple_commit()
590
591  sbox.simple_update(revision=1)
592
593  # The bug: with a request to merge -c4 then -c3, it merges -c4 which
594  # conflicts then auto-resolves the conflict, then errors out with
595  # 'svn: E155035: Can't merge into conflicted node 'iota'.
596  # ### We need more checking of the result to make this test robust, since
597  #     it may not always just error out.
598  svntest.main.run_svn(None, 'merge', '-c4,3', '^/iota', 'iota',
599                       '--accept=theirs-conflict')
600
601#----------------------------------------------------------------------
602
603# Test for issue #4647 'auto resolution mine-full fails on binary file'
604@Issue(4647)
605def automatic_binary_conflict_resolution(sbox):
606  "resolve -R --accept [base | mf | tf] binary file"
607
608  sbox.build()
609  wc_dir = sbox.wc_dir
610
611  # Some paths we'll care about
612  A_COPY_path = os.path.join(wc_dir, "A_COPY")
613
614  # Add a binary file to the project in revision 2.
615  theta_contents = open(os.path.join(sys.path[0], "theta.bin"), 'rb').read()
616  theta_path = sbox.ospath('A/theta')
617  svntest.main.file_write(theta_path, theta_contents, 'wb')
618  svntest.main.run_svn(None, 'add', theta_path)
619  svntest.main.run_svn(None, 'commit', '-m', 'log msg', wc_dir)
620
621  # Branch A to A_COPY in revision 3.
622  svntest.main.run_svn(None, 'copy',  wc_dir + "/A",  A_COPY_path)
623  svntest.main.run_svn(None, 'commit', '-m', 'log msg', wc_dir)
624
625  # Modify the binary file on trunk and in the branch, so that both versions
626  # differ.
627  theta_branch_path = sbox.ospath('A_COPY/theta')
628  svntest.main.file_append_binary(theta_path, theta_contents)
629  svntest.main.run_svn(None, 'commit', '-m', 'log msg', wc_dir)
630  svntest.main.file_append_binary(theta_branch_path, theta_contents)
631  svntest.main.file_append_binary(theta_branch_path, theta_contents)
632  svntest.main.run_svn(None, 'commit', '-m', 'log msg', wc_dir)
633
634  # Run an svn update now to prevent mixed-revision working copy [1:4] error.
635  svntest.main.run_svn(None, 'update', wc_dir)
636
637
638  def do_binary_conflicting_merge():
639    svntest.actions.run_and_verify_svn(None, [],
640                                       'revert', '--recursive', A_COPY_path)
641    svntest.main.run_svn(None, 'merge', sbox.repo_url + "/A/theta",
642                          wc_dir + "/A_COPY/theta")
643
644  # Test 'svn resolve -R --accept base'
645  # Regression until r1758160
646  do_binary_conflicting_merge()
647  svntest.actions.run_and_verify_resolve([theta_branch_path],
648                                         '-R', '--accept', 'base',
649                                         A_COPY_path)
650
651  # Test 'svn resolve -R --accept theirs-full'
652  do_binary_conflicting_merge()
653  svntest.actions.run_and_verify_resolve([theta_branch_path],
654                                         '-R', '--accept', 'tf',
655                                         A_COPY_path)
656
657  # Test 'svn resolve -R --accept working'
658  # Equivalent to 'svn resolved'
659  do_binary_conflicting_merge()
660  svntest.actions.run_and_verify_resolve([theta_branch_path],
661                                         '-R', '--accept', 'working',
662                                         A_COPY_path)
663
664  # Test 'svn resolve -R --accept mine-full'
665  # There is no '.mine' for binary file conflicts. Same handling as 'working'
666  do_binary_conflicting_merge()
667  svntest.actions.run_and_verify_resolve([theta_branch_path],
668                                         '-R', '--accept', 'mine-full',
669                                         A_COPY_path)
670
671########################################################################
672# Run the tests
673
674# list all tests here, starting with None:
675test_list = [ None,
676              automatic_conflict_resolution,
677              prop_conflict_resolution,
678              auto_resolve_executable_file,
679              resolved_on_wc_root,
680              resolved_on_deleted_item,
681              theirs_conflict_in_subdir,
682              multi_range_merge_with_accept,
683              automatic_binary_conflict_resolution,
684             ]
685
686if __name__ == '__main__':
687  svntest.main.run_tests(test_list)
688  # NOTREACHED
689
690### End of file.
691