~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/tests/cmdline/svntest/mergetrees.py

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#
 
3
#  mergetrees.py:  routines that create merge scenarios
 
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
 
28
import shutil, sys, re, os
 
29
import time
 
30
 
 
31
# Our testing module
 
32
import main, wc, verify, actions, testcase
 
33
 
 
34
from prop_tests import binary_mime_type_on_text_file_warning
 
35
 
 
36
# (abbreviation)
 
37
Item = wc.StateItem
 
38
Skip = testcase.Skip_deco
 
39
SkipUnless = testcase.SkipUnless_deco
 
40
XFail = testcase.XFail_deco
 
41
Issues = testcase.Issues_deco
 
42
Issue = testcase.Issue_deco
 
43
Wimp = testcase.Wimp_deco
 
44
exp_noop_up_out = actions.expected_noop_update_output
 
45
 
 
46
from svntest.main import SVN_PROP_MERGEINFO
 
47
 
 
48
def expected_merge_output(rev_ranges, additional_lines=[], foreign=False,
 
49
                          elides=False, two_url=False, target=None,
 
50
                          text_conflicts=0, prop_conflicts=0, tree_conflicts=0,
 
51
                          text_resolved=0, prop_resolved=0, tree_resolved=0,
 
52
                          skipped_paths=0):
 
53
  """Generate an (inefficient) regex representing the expected merge
 
54
  output and mergeinfo notifications from REV_RANGES and ADDITIONAL_LINES.
 
55
 
 
56
  REV_RANGES is a list of revision ranges for which mergeinfo is being
 
57
  recorded.  Each range is of the form [start, end] (where both START and
 
58
  END are inclusive, unlike in '-rX:Y') or the form [single_rev] (which is
 
59
  like '-c SINGLE_REV').  If REV_RANGES is None then only the standard
 
60
  notification for a 3-way merge is expected.
 
61
 
 
62
  ADDITIONAL_LINES is a list of strings to match the other lines of output;
 
63
  these are basically regular expressions except that backslashes will be
 
64
  escaped herein.  If ADDITIONAL_LINES is a single string, it is interpreted
 
65
  the same as a list containing that string.
 
66
 
 
67
  If ELIDES is true, add to the regex an expression representing elision
 
68
  notification.  If TWO_URL is true, tweak the regex to expect the
 
69
  appropriate mergeinfo notification for a 3-way merge.
 
70
 
 
71
  TARGET is the local path to the target, as it should appear in
 
72
  notifications; if None, it is not checked.
 
73
 
 
74
  TEXT_CONFLICTS, PROP_CONFLICTS, TREE_CONFLICTS and SKIPPED_PATHS specify
 
75
  the number of each kind of conflict to expect.
 
76
  """
 
77
 
 
78
  if rev_ranges is None:
 
79
    lines = [main.merge_notify_line(None, None, False, foreign)]
 
80
  else:
 
81
    lines = []
 
82
    for rng in rev_ranges:
 
83
      start_rev = rng[0]
 
84
      if len(rng) > 1:
 
85
        end_rev = rng[1]
 
86
      else:
 
87
        end_rev = None
 
88
      lines += [main.merge_notify_line(start_rev, end_rev,
 
89
                                               True, foreign, target)]
 
90
      lines += [main.mergeinfo_notify_line(start_rev, end_rev, target)]
 
91
 
 
92
  if (elides):
 
93
    lines += ["--- Eliding mergeinfo from .*\n"]
 
94
 
 
95
  if (two_url):
 
96
    lines += ["--- Recording mergeinfo for merge between repository URLs .*\n"]
 
97
 
 
98
  # Address "The Backslash Plague"
 
99
  #
 
100
  # If ADDITIONAL_LINES are present there are possibly paths in it with
 
101
  # multiple components and on Windows these components are separated with
 
102
  # '\'.  These need to be escaped properly in the regexp for the match to
 
103
  # work correctly.  See http://aspn.activestate.com/ASPN/docs/ActivePython
 
104
  # /2.2/howto/regex/regex.html#SECTION000420000000000000000.
 
105
  if isinstance(additional_lines, str):
 
106
    additional_lines = [additional_lines]
 
107
  if sys.platform == 'win32':
 
108
    additional_lines = [line.replace("\\", "\\\\") for line in additional_lines]
 
109
  lines += additional_lines
 
110
 
 
111
  lines += main.summary_of_conflicts(
 
112
             text_conflicts, prop_conflicts, tree_conflicts,
 
113
             text_resolved, prop_resolved, tree_resolved,
 
114
             skipped_paths,
 
115
             as_regex=True)
 
116
 
 
117
  return "|".join(lines)
 
118
 
 
119
def check_mergeinfo_recursively(root_path, subpaths_mergeinfo):
 
120
  """Check that the mergeinfo properties on and under ROOT_PATH are those in
 
121
     SUBPATHS_MERGEINFO, a {path: mergeinfo-prop-val} dictionary."""
 
122
  expected = verify.UnorderedOutput(
 
123
    [path + ' - ' + subpaths_mergeinfo[path] + '\n'
 
124
     for path in subpaths_mergeinfo])
 
125
  actions.run_and_verify_svn(expected, [],
 
126
                                     'propget', '-R', SVN_PROP_MERGEINFO,
 
127
                                     root_path)
 
128
 
 
129
######################################################################
 
130
#----------------------------------------------------------------------
 
131
def set_up_dir_replace(sbox):
 
132
  """Set up the working copy for directory replace tests, creating
 
133
  directory 'A/B/F/foo' with files 'new file' and 'new file2' within
 
134
  it (r2), and merging 'foo' onto 'C' (r3), then deleting 'A/B/F/foo'
 
135
  (r4)."""
 
136
 
 
137
  sbox.build()
 
138
  wc_dir = sbox.wc_dir
 
139
 
 
140
  C_path = sbox.ospath('A/C')
 
141
  F_path = sbox.ospath('A/B/F')
 
142
  F_url = sbox.repo_url + '/A/B/F'
 
143
 
 
144
  foo_path = os.path.join(F_path, 'foo')
 
145
  new_file = os.path.join(foo_path, "new file")
 
146
  new_file2 = os.path.join(foo_path, "new file 2")
 
147
 
 
148
  # Make directory foo in F, and add some files within it.
 
149
  actions.run_and_verify_svn(None, [], 'mkdir', foo_path)
 
150
  main.file_append(new_file, "Initial text in new file.\n")
 
151
  main.file_append(new_file2, "Initial text in new file 2.\n")
 
152
  main.run_svn(None, "add", new_file)
 
153
  main.run_svn(None, "add", new_file2)
 
154
 
 
155
  # Commit all the new content, creating r2.
 
156
  expected_output = wc.State(wc_dir, {
 
157
    'A/B/F/foo'            : Item(verb='Adding'),
 
158
    'A/B/F/foo/new file'   : Item(verb='Adding'),
 
159
    'A/B/F/foo/new file 2' : Item(verb='Adding'),
 
160
    })
 
161
  expected_status = actions.get_virginal_state(wc_dir, 1)
 
162
  expected_status.add({
 
163
    'A/B/F/foo'             : Item(status='  ', wc_rev=2),
 
164
    'A/B/F/foo/new file'    : Item(status='  ', wc_rev=2),
 
165
    'A/B/F/foo/new file 2'  : Item(status='  ', wc_rev=2),
 
166
    })
 
167
  actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
168
 
 
169
  # Merge foo onto C
 
170
  expected_output = wc.State(C_path, {
 
171
    'foo' : Item(status='A '),
 
172
    'foo/new file'   : Item(status='A '),
 
173
    'foo/new file 2' : Item(status='A '),
 
174
    })
 
175
  expected_mergeinfo_output = wc.State(C_path, {
 
176
    '' : Item(status=' U'),
 
177
    })
 
178
  expected_elision_output = wc.State(C_path, {
 
179
    })
 
180
  expected_disk = wc.State('', {
 
181
    ''               : Item(props={SVN_PROP_MERGEINFO : '/A/B/F:2'}),
 
182
    'foo' : Item(),
 
183
    'foo/new file'   : Item("Initial text in new file.\n"),
 
184
    'foo/new file 2' : Item("Initial text in new file 2.\n"),
 
185
    })
 
186
  expected_status = wc.State(C_path, {
 
187
    ''    : Item(status=' M', wc_rev=1),
 
188
    'foo' : Item(status='A ', wc_rev='-', copied='+'),
 
189
    'foo/new file'   : Item(status='  ', wc_rev='-', copied='+'),
 
190
    'foo/new file 2' : Item(status='  ', wc_rev='-', copied='+'),
 
191
    })
 
192
  expected_skip = wc.State(C_path, { })
 
193
  actions.run_and_verify_merge(C_path, '1', '2', F_url, None,
 
194
                                       expected_output,
 
195
                                       expected_mergeinfo_output,
 
196
                                       expected_elision_output,
 
197
                                       expected_disk,
 
198
                                       expected_status,
 
199
                                       expected_skip,
 
200
                                       check_props=True)
 
201
  # Commit merge of foo onto C, creating r3.
 
202
  expected_output = wc.State(wc_dir, {
 
203
    'A/C'        : Item(verb='Sending'),
 
204
    'A/C/foo'    : Item(verb='Adding'),
 
205
    })
 
206
  expected_status = actions.get_virginal_state(wc_dir, 1)
 
207
  expected_status.add({
 
208
    'A/B/F/foo'  : Item(status='  ', wc_rev=2),
 
209
    'A/C'        : Item(status='  ', wc_rev=3),
 
210
    'A/B/F/foo/new file'      : Item(status='  ', wc_rev=2),
 
211
    'A/B/F/foo/new file 2'    : Item(status='  ', wc_rev=2),
 
212
    'A/C/foo'    : Item(status='  ', wc_rev=3),
 
213
    'A/C/foo/new file'      : Item(status='  ', wc_rev=3),
 
214
    'A/C/foo/new file 2'    : Item(status='  ', wc_rev=3),
 
215
 
 
216
    })
 
217
  actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
218
 
 
219
  # Delete foo on F, creating r4.
 
220
  actions.run_and_verify_svn(None, [], 'rm', foo_path)
 
221
  expected_output = wc.State(wc_dir, {
 
222
    'A/B/F/foo'   : Item(verb='Deleting'),
 
223
    })
 
224
  expected_status = actions.get_virginal_state(wc_dir, 1)
 
225
  expected_status.add({
 
226
    'A/C'         : Item(status='  ', wc_rev=3),
 
227
    'A/C/foo'     : Item(status='  ', wc_rev=3),
 
228
    'A/C/foo/new file'      : Item(status='  ', wc_rev=3),
 
229
    'A/C/foo/new file 2'    : Item(status='  ', wc_rev=3),
 
230
    })
 
231
  actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
232
 
 
233
#----------------------------------------------------------------------
 
234
def set_up_branch(sbox, branch_only = False, nbr_of_branches = 1):
 
235
  '''Starting with standard greek tree, copy 'A' NBR_OF_BRANCHES times
 
236
  to A_COPY, A_COPY_2, A_COPY_3, and so on.  Then, unless BRANCH_ONLY is
 
237
  true, make four modifications (setting file contents to "New content")
 
238
  under A:
 
239
    r(2 + NBR_OF_BRANCHES) - A/D/H/psi
 
240
    r(3 + NBR_OF_BRANCHES) - A/D/G/rho
 
241
    r(4 + NBR_OF_BRANCHES) - A/B/E/beta
 
242
    r(5 + NBR_OF_BRANCHES) - A/D/H/omega
 
243
  Return (expected_disk, expected_status).'''
 
244
 
 
245
  # With the default parameters, the branching looks like this:
 
246
  #
 
247
  #   A         -1-----3-4-5-6--
 
248
  #                \
 
249
  #   A_COPY        2-----------
 
250
 
 
251
  wc_dir = sbox.wc_dir
 
252
 
 
253
  expected_status = actions.get_virginal_state(wc_dir, 1)
 
254
  expected_disk = main.greek_state.copy()
 
255
 
 
256
  def copy_A(dest_name, rev):
 
257
    expected = verify.UnorderedOutput(
 
258
      ["A    " + os.path.join(wc_dir, dest_name, "B") + "\n",
 
259
       "A    " + os.path.join(wc_dir, dest_name, "B", "lambda") + "\n",
 
260
       "A    " + os.path.join(wc_dir, dest_name, "B", "E") + "\n",
 
261
       "A    " + os.path.join(wc_dir, dest_name, "B", "E", "alpha") + "\n",
 
262
       "A    " + os.path.join(wc_dir, dest_name, "B", "E", "beta") + "\n",
 
263
       "A    " + os.path.join(wc_dir, dest_name, "B", "F") + "\n",
 
264
       "A    " + os.path.join(wc_dir, dest_name, "mu") + "\n",
 
265
       "A    " + os.path.join(wc_dir, dest_name, "C") + "\n",
 
266
       "A    " + os.path.join(wc_dir, dest_name, "D") + "\n",
 
267
       "A    " + os.path.join(wc_dir, dest_name, "D", "gamma") + "\n",
 
268
       "A    " + os.path.join(wc_dir, dest_name, "D", "G") + "\n",
 
269
       "A    " + os.path.join(wc_dir, dest_name, "D", "G", "pi") + "\n",
 
270
       "A    " + os.path.join(wc_dir, dest_name, "D", "G", "rho") + "\n",
 
271
       "A    " + os.path.join(wc_dir, dest_name, "D", "G", "tau") + "\n",
 
272
       "A    " + os.path.join(wc_dir, dest_name, "D", "H") + "\n",
 
273
       "A    " + os.path.join(wc_dir, dest_name, "D", "H", "chi") + "\n",
 
274
       "A    " + os.path.join(wc_dir, dest_name, "D", "H", "omega") + "\n",
 
275
       "A    " + os.path.join(wc_dir, dest_name, "D", "H", "psi") + "\n",
 
276
       "Checked out revision " + str(rev - 1) + ".\n",
 
277
       "A         " + os.path.join(wc_dir, dest_name) + "\n"])
 
278
    expected_status.add({
 
279
      dest_name + "/B"         : Item(status='  ', wc_rev=rev),
 
280
      dest_name + "/B/lambda"  : Item(status='  ', wc_rev=rev),
 
281
      dest_name + "/B/E"       : Item(status='  ', wc_rev=rev),
 
282
      dest_name + "/B/E/alpha" : Item(status='  ', wc_rev=rev),
 
283
      dest_name + "/B/E/beta"  : Item(status='  ', wc_rev=rev),
 
284
      dest_name + "/B/F"       : Item(status='  ', wc_rev=rev),
 
285
      dest_name + "/mu"        : Item(status='  ', wc_rev=rev),
 
286
      dest_name + "/C"         : Item(status='  ', wc_rev=rev),
 
287
      dest_name + "/D"         : Item(status='  ', wc_rev=rev),
 
288
      dest_name + "/D/gamma"   : Item(status='  ', wc_rev=rev),
 
289
      dest_name + "/D/G"       : Item(status='  ', wc_rev=rev),
 
290
      dest_name + "/D/G/pi"    : Item(status='  ', wc_rev=rev),
 
291
      dest_name + "/D/G/rho"   : Item(status='  ', wc_rev=rev),
 
292
      dest_name + "/D/G/tau"   : Item(status='  ', wc_rev=rev),
 
293
      dest_name + "/D/H"       : Item(status='  ', wc_rev=rev),
 
294
      dest_name + "/D/H/chi"   : Item(status='  ', wc_rev=rev),
 
295
      dest_name + "/D/H/omega" : Item(status='  ', wc_rev=rev),
 
296
      dest_name + "/D/H/psi"   : Item(status='  ', wc_rev=rev),
 
297
      dest_name                : Item(status='  ', wc_rev=rev)})
 
298
    expected_disk.add({
 
299
      dest_name                : Item(),
 
300
      dest_name + '/B'         : Item(),
 
301
      dest_name + '/B/lambda'  : Item("This is the file 'lambda'.\n"),
 
302
      dest_name + '/B/E'       : Item(),
 
303
      dest_name + '/B/E/alpha' : Item("This is the file 'alpha'.\n"),
 
304
      dest_name + '/B/E/beta'  : Item("This is the file 'beta'.\n"),
 
305
      dest_name + '/B/F'       : Item(),
 
306
      dest_name + '/mu'        : Item("This is the file 'mu'.\n"),
 
307
      dest_name + '/C'         : Item(),
 
308
      dest_name + '/D'         : Item(),
 
309
      dest_name + '/D/gamma'   : Item("This is the file 'gamma'.\n"),
 
310
      dest_name + '/D/G'       : Item(),
 
311
      dest_name + '/D/G/pi'    : Item("This is the file 'pi'.\n"),
 
312
      dest_name + '/D/G/rho'   : Item("This is the file 'rho'.\n"),
 
313
      dest_name + '/D/G/tau'   : Item("This is the file 'tau'.\n"),
 
314
      dest_name + '/D/H'       : Item(),
 
315
      dest_name + '/D/H/chi'   : Item("This is the file 'chi'.\n"),
 
316
      dest_name + '/D/H/omega' : Item("This is the file 'omega'.\n"),
 
317
      dest_name + '/D/H/psi'   : Item("This is the file 'psi'.\n"),
 
318
      })
 
319
 
 
320
    # Make a branch A_COPY to merge into.
 
321
    actions.run_and_verify_svn(expected, [], 'copy',
 
322
                                       sbox.repo_url + "/A",
 
323
                                       os.path.join(wc_dir,
 
324
                                                    dest_name))
 
325
 
 
326
    expected_output = wc.State(wc_dir, {dest_name : Item(verb='Adding')})
 
327
    actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
328
  for i in range(nbr_of_branches):
 
329
    if i == 0:
 
330
      copy_A('A_COPY', i + 2)
 
331
    else:
 
332
      copy_A('A_COPY_' + str(i + 1), i + 2)
 
333
 
 
334
  if branch_only:
 
335
    return expected_disk, expected_status
 
336
 
 
337
  # Make some changes under A which we'll later merge under A_COPY:
 
338
 
 
339
  # r(nbr_of_branches + 2) - modify and commit A/D/H/psi
 
340
  main.file_write(sbox.ospath('A/D/H/psi'),
 
341
                          "New content")
 
342
  expected_output = wc.State(wc_dir, {'A/D/H/psi' : Item(verb='Sending')})
 
343
  expected_status.tweak('A/D/H/psi', wc_rev=nbr_of_branches + 2)
 
344
  actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
345
  expected_disk.tweak('A/D/H/psi', contents="New content")
 
346
 
 
347
  # r(nbr_of_branches + 3) - modify and commit A/D/G/rho
 
348
  main.file_write(sbox.ospath('A/D/G/rho'),
 
349
                          "New content")
 
350
  expected_output = wc.State(wc_dir, {'A/D/G/rho' : Item(verb='Sending')})
 
351
  expected_status.tweak('A/D/G/rho', wc_rev=nbr_of_branches + 3)
 
352
  actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
353
  expected_disk.tweak('A/D/G/rho', contents="New content")
 
354
 
 
355
  # r(nbr_of_branches + 4) - modify and commit A/B/E/beta
 
356
  main.file_write(sbox.ospath('A/B/E/beta'),
 
357
                          "New content")
 
358
  expected_output = wc.State(wc_dir, {'A/B/E/beta' : Item(verb='Sending')})
 
359
  expected_status.tweak('A/B/E/beta', wc_rev=nbr_of_branches + 4)
 
360
  actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
361
  expected_disk.tweak('A/B/E/beta', contents="New content")
 
362
 
 
363
  # r(nbr_of_branches + 5) - modify and commit A/D/H/omega
 
364
  main.file_write(sbox.ospath('A/D/H/omega'),
 
365
                          "New content")
 
366
  expected_output = wc.State(wc_dir, {'A/D/H/omega' : Item(verb='Sending')})
 
367
  expected_status.tweak('A/D/H/omega', wc_rev=nbr_of_branches + 5)
 
368
  actions.run_and_verify_commit(wc_dir, expected_output, expected_status)
 
369
  expected_disk.tweak('A/D/H/omega', contents="New content")
 
370
 
 
371
  return expected_disk, expected_status
 
372
 
 
373
#----------------------------------------------------------------------
 
374
# Helper functions. These take local paths using '/' separators.
 
375
 
 
376
def local_path(path):
 
377
  "Convert a path from '/' separators to the local style."
 
378
  return os.sep.join(path.split('/'))
 
379
 
 
380
def svn_mkfile(path):
 
381
  "Make and add a file with some default content, and keyword expansion."
 
382
  path = local_path(path)
 
383
  dirname, filename = os.path.split(path)
 
384
  main.file_write(path, "This is the file '" + filename + "'.\n" +
 
385
                                "Last changed in '$Revision$'.\n")
 
386
  actions.run_and_verify_svn(None, [], 'add', path)
 
387
  actions.run_and_verify_svn(None, [], 'propset',
 
388
                                     'svn:keywords', 'Revision', path)
 
389
 
 
390
def svn_modfile(path):
 
391
  "Make text and property mods to a WC file."
 
392
  path = local_path(path)
 
393
  main.file_append(path, "An extra line.\n")
 
394
  actions.run_and_verify_svn(None, [], 'propset',
 
395
                                     'newprop', 'v', path)
 
396
 
 
397
def svn_copy(s_rev, path1, path2):
 
398
  "Copy a WC path locally."
 
399
  path1 = local_path(path1)
 
400
  path2 = local_path(path2)
 
401
  actions.run_and_verify_svn(None, [], 'copy', '--parents',
 
402
                                     '-r', s_rev, path1, path2)
 
403
 
 
404
def svn_merge(rev_range, source, target, lines=None, elides=[],
 
405
              text_conflicts=0, prop_conflicts=0, tree_conflicts=0,
 
406
              text_resolved=0, prop_resolved=0, tree_resolved=0,
 
407
              args=[]):
 
408
  """Merge a single change from path SOURCE to path TARGET and verify the
 
409
  output and that there is no error.  (The changes made are not verified.)
 
410
 
 
411
  REV_RANGE is either a number (to cherry-pick that specific change) or a
 
412
  two-element list [X,Y] to pick the revision range '-r(X-1):Y'.
 
413
 
 
414
  LINES is a list of regular expressions to match other lines of output; if
 
415
  LINES is 'None' then match all normal (non-conflicting) merges.
 
416
 
 
417
  ELIDES is a list of paths on which mergeinfo elision should be reported.
 
418
 
 
419
  TEXT_CONFLICTS, PROP_CONFLICTS and TREE_CONFLICTS specify the number of
 
420
  each kind of conflict to expect.
 
421
 
 
422
  ARGS are additional arguments passed to svn merge.
 
423
  """
 
424
 
 
425
  source = local_path(source)
 
426
  target = local_path(target)
 
427
  elides = [local_path(p) for p in elides]
 
428
  if isinstance(rev_range, int):
 
429
    mi_rev_range = [rev_range]
 
430
    rev_arg = '-c' + str(rev_range)
 
431
  else:
 
432
    mi_rev_range = rev_range
 
433
    rev_arg = '-r' + str(rev_range[0] - 1) + ':' + str(rev_range[1])
 
434
  if lines is None:
 
435
    lines = ["(A |D |[UG] | [UG]|[UG][UG])   " + target + ".*\n"]
 
436
  else:
 
437
    # Expect mergeinfo on the target; caller must supply matches for any
 
438
    # subtree mergeinfo paths.
 
439
    lines.append(" [UG]   " + target + "\n")
 
440
  exp_out = expected_merge_output([mi_rev_range], lines, target=target,
 
441
                                  elides=elides,
 
442
                                  text_conflicts=text_conflicts,
 
443
                                  prop_conflicts=prop_conflicts,
 
444
                                  tree_conflicts=tree_conflicts,
 
445
                                  text_resolved=text_resolved,
 
446
                                  prop_resolved=prop_resolved,
 
447
                                  tree_resolved=tree_resolved)
 
448
  actions.run_and_verify_svn(exp_out, [],
 
449
                                     'merge', rev_arg, source, target, *args)
 
450
 
 
451
#----------------------------------------------------------------------
 
452
# Setup helper for issue #4056 and issue #4057 tests.
 
453
def noninheritable_mergeinfo_test_set_up(sbox):
 
454
  '''Starting with standard greek tree, copy 'A' to 'branch' in r2 and
 
455
  then made a file edit to A/B/lambda in r3.
 
456
  Return (expected_output, expected_mergeinfo_output, expected_elision_output,
 
457
          expected_status, expected_disk, expected_skip) for a merge of
 
458
  r3 from ^/A/B to branch/B.'''
 
459
 
 
460
  sbox.build()
 
461
  wc_dir = sbox.wc_dir
 
462
 
 
463
  lambda_path   = sbox.ospath('A/B/lambda')
 
464
  B_branch_path = sbox.ospath('branch/B')
 
465
 
 
466
  # r2 - Branch ^/A to ^/branch.
 
467
  main.run_svn(None, 'copy', sbox.repo_url + '/A',
 
468
                       sbox.repo_url + '/branch', '-m', 'make a branch')
 
469
 
 
470
  # r3 - Make an edit to A/B/lambda.
 
471
  main.file_write(lambda_path, "trunk edit.\n")
 
472
  main.run_svn(None, 'commit', '-m', 'file edit', wc_dir)
 
473
  main.run_svn(None, 'up', wc_dir)
 
474
 
 
475
  expected_output = wc.State(B_branch_path, {
 
476
    'lambda' : Item(status='U '),
 
477
    })
 
478
  expected_mergeinfo_output = wc.State(B_branch_path, {
 
479
    ''       : Item(status=' U'),
 
480
    'lambda' : Item(status=' U'),
 
481
    })
 
482
  expected_elision_output = wc.State(B_branch_path, {
 
483
    'lambda' : Item(status=' U'),
 
484
    })
 
485
  expected_status = wc.State(B_branch_path, {
 
486
    ''        : Item(status=' M'),
 
487
    'lambda'  : Item(status='M '),
 
488
    'E'       : Item(status='  '),
 
489
    'E/alpha' : Item(status='  '),
 
490
    'E/beta'  : Item(status='  '),
 
491
    'F'       : Item(status='  '),
 
492
    })
 
493
  expected_status.tweak(wc_rev='3')
 
494
  expected_disk = wc.State('', {
 
495
    ''          : Item(props={SVN_PROP_MERGEINFO : '/A/B:3'}),
 
496
    'lambda'  : Item("trunk edit.\n"),
 
497
    'E'       : Item(),
 
498
    'E/alpha' : Item("This is the file 'alpha'.\n"),
 
499
    'E/beta'  : Item("This is the file 'beta'.\n"),
 
500
    'F'       : Item(),
 
501
    })
 
502
  expected_skip = wc.State(B_branch_path, {})
 
503
 
 
504
  return expected_output, expected_mergeinfo_output, expected_elision_output, \
 
505
    expected_status, expected_disk, expected_skip
 
506