1#!/usr/bin/env python
2#
3#  svndumpfilter_tests.py:  testing the 'svndumpfilter' tool.
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 os
29import sys
30import tempfile
31
32# Our testing module
33import svntest
34from svntest.verify import SVNExpectedStdout, SVNExpectedStderr
35
36# Get some helper routines
37from svnadmin_tests import load_and_verify_dumpstream, load_dumpstream
38from svntest.main import run_svn, run_svnadmin
39
40# (abbreviation)
41Skip = svntest.testcase.Skip_deco
42SkipUnless = svntest.testcase.SkipUnless_deco
43XFail = svntest.testcase.XFail_deco
44Issues = svntest.testcase.Issues_deco
45Issue = svntest.testcase.Issue_deco
46Wimp = svntest.testcase.Wimp_deco
47Item = svntest.wc.StateItem
48
49
50######################################################################
51# Helper routines
52
53
54def filter_and_return_output(dump, bufsize=0, *varargs):
55  """Filter the array of lines passed in 'dump' and return the output
56  and errput"""
57
58  if isinstance(dump, str):
59    dump = [ dump ]
60
61  # Does the caller want the stderr?
62  if '-q' in varargs or '--quiet' in varargs:
63      expected_errput = None # Stderr with -q or --quiet is a real error!
64  else:
65      expected_errput = svntest.verify.AnyOutput
66  ## TODO: Should we handle exit_code?
67  exit_code, output, errput = svntest.main.run_command_stdin(
68    svntest.main.svndumpfilter_binary, expected_errput, bufsize, True,
69    dump, *varargs)
70
71  # Since we call svntest.main.run_command_stdin() in binary mode,
72  # normalize the stderr line endings on Windows ourselves.
73  if sys.platform == 'win32':
74    errput = [x.replace('\r\n', '\n') for x in errput]
75
76  return output, errput
77
78
79######################################################################
80# Tests
81
82@Issue(2982)
83def reflect_dropped_renumbered_revs(sbox):
84  "reflect dropped renumbered revs in svn:mergeinfo"
85
86  ## See https://issues.apache.org/jira/browse/SVN-2982. ##
87
88  # Test svndumpfilter with include option
89  sbox.build(empty=True)
90  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
91                                   'svndumpfilter_tests_data',
92                                   'with_merges.dump')
93  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
94
95  filtered_out, filtered_err = filter_and_return_output(
96      dumpfile, 0, "include",
97      "trunk", "branch1",
98      "--skip-missing-merge-sources",
99      "--drop-empty-revs",
100      "--renumber-revs", "--quiet")
101
102  load_dumpstream(sbox, filtered_out, "--ignore-uuid")
103
104  # Verify the svn:mergeinfo properties
105  url = sbox.repo_url
106  expected_output = svntest.verify.UnorderedOutput([
107    url + "/trunk - /branch1:4-5\n",
108    ])
109  svntest.actions.run_and_verify_svn(expected_output, [],
110                                     'propget', 'svn:mergeinfo', '-R',
111                                     sbox.repo_url)
112
113
114  # Test svndumpfilter with exclude option
115  sbox.build(empty=True)
116  filtered_out, filtered_err = filter_and_return_output(
117      dumpfile, 0, "exclude", "branch1",
118      "--skip-missing-merge-sources",
119      "--drop-empty-revs",
120      "--renumber-revs", "--quiet")
121
122  load_dumpstream(sbox, filtered_out, "--ignore-uuid")
123
124  # Verify the svn:mergeinfo properties
125  expected_output = svntest.verify.UnorderedOutput([
126    url + "/trunk - \n",
127    ])
128  svntest.actions.run_and_verify_svn(expected_output, [],
129                                     'propget', 'svn:mergeinfo', '-R',
130                                     sbox.repo_url)
131
132@Issue(3181)
133def svndumpfilter_loses_mergeinfo(sbox):
134  "svndumpfilter loses mergeinfo"
135  #svndumpfilter loses mergeinfo if invoked without --renumber-revs
136
137  ## See https://issues.apache.org/jira/browse/SVN-3181. ##
138
139  sbox.build(empty=True)
140  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
141                                   'svndumpfilter_tests_data',
142                                   'with_merges.dump')
143  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
144
145  filtered_out, filtered_err = filter_and_return_output(dumpfile, 0, "include",
146                                                        "trunk", "branch1",
147                                                        "--quiet")
148  load_dumpstream(sbox, filtered_out)
149
150  # Verify the svn:mergeinfo properties
151  url = sbox.repo_url
152  expected_output = svntest.verify.UnorderedOutput([
153    url + "/trunk - /branch1:4-8\n",
154    ])
155  svntest.actions.run_and_verify_svn(expected_output, [],
156                                     'propget', 'svn:mergeinfo', '-R',
157                                     sbox.repo_url)
158
159
160def _simple_dumpfilter_test(sbox, dumpfile, *dumpargs):
161  """Run svndumpfilter with arguments DUMPARGS, taking input from DUMPFILE.
162     Check that the output consists of the standard Greek tree excluding
163     all paths that start with 'A/B/E', 'A/D/G' or 'A/D/H'."""
164  wc_dir = sbox.wc_dir
165
166  filtered_output, filtered_err = filter_and_return_output(dumpfile, 0,
167                                                           '--quiet',
168                                                           *dumpargs)
169
170  # Setup our expectations
171  load_dumpstream(sbox, filtered_output, '--ignore-uuid')
172  expected_disk = svntest.main.greek_state.copy()
173  expected_disk.remove('A/B/E/alpha')
174  expected_disk.remove('A/B/E/beta')
175  expected_disk.remove('A/B/E')
176  expected_disk.remove('A/D/H/chi')
177  expected_disk.remove('A/D/H/psi')
178  expected_disk.remove('A/D/H/omega')
179  expected_disk.remove('A/D/H')
180  expected_disk.remove('A/D/G/pi')
181  expected_disk.remove('A/D/G/rho')
182  expected_disk.remove('A/D/G/tau')
183  expected_disk.remove('A/D/G')
184
185  expected_output = svntest.wc.State(wc_dir, {
186    'A'           : Item(status='A '),
187    'A/B'         : Item(status='A '),
188    'A/B/lambda'  : Item(status='A '),
189    'A/B/F'       : Item(status='A '),
190    'A/mu'        : Item(status='A '),
191    'A/C'         : Item(status='A '),
192    'A/D'         : Item(status='A '),
193    'A/D/gamma'   : Item(status='A '),
194    'iota'        : Item(status='A '),
195    })
196
197  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
198  expected_status.remove('A/B/E/alpha')
199  expected_status.remove('A/B/E/beta')
200  expected_status.remove('A/B/E')
201  expected_status.remove('A/D/H/chi')
202  expected_status.remove('A/D/H/psi')
203  expected_status.remove('A/D/H/omega')
204  expected_status.remove('A/D/H')
205  expected_status.remove('A/D/G/pi')
206  expected_status.remove('A/D/G/rho')
207  expected_status.remove('A/D/G/tau')
208  expected_status.remove('A/D/G')
209
210  # Check that our paths really were excluded
211  svntest.actions.run_and_verify_update(wc_dir,
212                                        expected_output,
213                                        expected_disk,
214                                        expected_status)
215
216
217@Issue(2697)
218def dumpfilter_with_targets(sbox):
219  "svndumpfilter --targets blah"
220  ## See https://issues.apache.org/jira/browse/SVN-2697. ##
221
222  sbox.build(empty=True)
223
224  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
225                                   'svndumpfilter_tests_data',
226                                   'greek_tree.dump')
227  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
228
229  (fd, targets_file) = tempfile.mkstemp(dir=svntest.main.temp_dir)
230  try:
231    targets = open(targets_file, 'w')
232    targets.write('/A/D/H\n')
233    targets.write('/A/D/G\n')
234    targets.close()
235    _simple_dumpfilter_test(sbox, dumpfile,
236                            'exclude', '/A/B/E', '--targets', targets_file)
237  finally:
238    os.close(fd)
239    os.remove(targets_file)
240
241
242def dumpfilter_with_patterns(sbox):
243  "svndumpfilter --pattern PATH_PREFIX"
244
245  sbox.build(empty=True)
246
247  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
248                                   'svndumpfilter_tests_data',
249                                   'greek_tree.dump')
250  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
251  _simple_dumpfilter_test(sbox, dumpfile,
252                          'exclude', '--pattern', '/A/D/[GH]*', '/A/[B]/E*')
253
254#----------------------------------------------------------------------
255# More testing for issue #3020 'Reflect dropped/renumbered revisions in
256# svn:mergeinfo data during svnadmin load'
257#
258# Specifically, test that svndumpfilter, when used with the
259# --skip-missing-merge-sources option, removes mergeinfo that refers to
260# revisions that are older than the oldest revision in the dump stream.
261@Issue(3020)
262def filter_mergeinfo_revs_outside_of_dump_stream(sbox):
263  "filter mergeinfo revs outside of dump stream"
264
265  sbox.build(empty=True)
266
267  # Load a partial dump into an existing repository.
268  #
269  # Picture == 1k words:
270  #
271  # The dump file we filter in this test, 'mergeinfo_included_partial.dump', is
272  # a dump of r6:HEAD of the following repos:
273  #
274  #                       __________________________________________
275  #                      |                                         |
276  #                      |             ____________________________|_____
277  #                      |            |                            |     |
278  # trunk---r2---r3-----r5---r6-------r8---r9--------------->      |     |
279  #   r1             |        |     |       |                      |     |
280  # initial          |        |     |       |______                |     |
281  # import         copy       |   copy             |            merge   merge
282  #                  |        |     |            merge           (r5)   (r8)
283  #                  |        |     |            (r9)              |     |
284  #                  |        |     |              |               |     |
285  #                  |        |     V              V               |     |
286  #                  |        | branches/B2-------r11---r12---->   |     |
287  #                  |        |     r7              |____|         |     |
288  #                  |        |                        |           |     |
289  #                  |      merge                      |___        |     |
290  #                  |      (r6)                           |       |     |
291  #                  |        |_________________           |       |     |
292  #                  |                          |        merge     |     |
293  #                  |                          |      (r11-12)    |     |
294  #                  |                          |          |       |     |
295  #                  V                          V          V       |     |
296  #              branches/B1-------------------r10--------r13-->   |     |
297  #                  r4                                            |     |
298  #                   |                                            V     V
299  #                  branches/B1/B/E------------------------------r14---r15->
300  #
301  #
302  # The mergeinfo on the complete repos would look like this:
303  #
304  #   Properties on 'branches/B1':
305  #     svn:mergeinfo
306  #       /branches/B2:11-12
307  #       /trunk:6,9
308  #   Properties on 'branches/B1/B/E':
309  #     svn:mergeinfo
310  #       /branches/B2/B/E:11-12
311  #       /trunk/B/E:5-6,8-9
312  #   Properties on 'branches/B2':
313  #     svn:mergeinfo
314  #       /trunk:9
315  #
316  # We will run the partial dump through svndumpfilter using the the
317  # --skip-missing-merge-soruces which should strip out any revisions < 6.
318  # Then we'll load the filtered result into an empty repository.  This
319  # should offset the incoming mergeinfo by -5.  In addition, any mergeinfo
320  # referring to the initial revision in the dump file (r6) should be
321  # removed because the change it refers to (r5:6) is not wholly within the
322  # dumpfile.  The resulting mergeinfo should look like this:
323  #
324  #   Properties on 'branches/B1':
325  #     svn:mergeinfo
326  #       /branches/B2:6-7
327  #       /trunk:4
328  #   Properties on 'branches/B1/B/E':
329  #     svn:mergeinfo
330  #       /branches/B2/B/E:6-7
331  #       /trunk/B/E:3-4
332  #   Properties on 'branches/B2':
333  #     svn:mergeinfo
334  #       /trunk:4
335  partial_dump = os.path.join(os.path.dirname(sys.argv[0]),
336                                   'svndumpfilter_tests_data',
337                                   'mergeinfo_included_partial.dump')
338  partial_dump_contents = svntest.actions.load_dumpfile(partial_dump)
339  filtered_dumpfile2, filtered_out = filter_and_return_output(
340      partial_dump_contents,
341      8192, # Set a sufficiently large bufsize to avoid a deadlock
342      "include", "trunk", "branches",
343      "--skip-missing-merge-sources",
344      "--quiet")
345  load_dumpstream(sbox, filtered_dumpfile2, '--ignore-uuid')
346  # Check the resulting mergeinfo.
347  url = sbox.repo_url + "/branches"
348  expected_output = svntest.verify.UnorderedOutput([
349    url + "/B1 - /branches/B2:6-7\n",
350    "/trunk:4\n",
351    url + "/B2 - /trunk:4\n",
352    url + "/B1/B/E - /branches/B2/B/E:6-7\n",
353    "/trunk/B/E:3-4\n"])
354  svntest.actions.run_and_verify_svn(expected_output, [],
355                                     'propget', 'svn:mergeinfo', '-R',
356                                     sbox.repo_url)
357
358  # Blow away the current repos, create an empty one in its place, and
359  # then load this skeleton repos into the empty target:
360  #
361  #   Projects/       (Added r1)
362  #     README        (Added r2)
363  #     Project-X     (Added r3)
364  #     Project-Y     (Added r4)
365  #     Project-Z     (Added r5)
366  #     docs/         (Added r6)
367  #       README      (Added r6).
368  sbox.build(empty=True)
369  skeleton_location = os.path.join(os.path.dirname(sys.argv[0]),
370                                                  'svnadmin_tests_data',
371                                                  'skeleton_repos.dump')
372  skeleton_dumpfile = svntest.actions.load_dumpfile(skeleton_location)
373  load_dumpstream(sbox, skeleton_dumpfile, '--ignore-uuid')
374  partial_dump2 = os.path.join(os.path.dirname(sys.argv[0]),
375                                   'svndumpfilter_tests_data',
376                                   'mergeinfo_included_partial.dump')
377  partial_dump_contents2 = svntest.actions.load_dumpfile(partial_dump2)
378  # Now use the partial dump file we used above, but this time exclude
379  # the B2 branch.  Load the filtered dump into the /Projects/Project-X
380  # subtree of the skeleton repos.
381  filtered_dumpfile2, filtered_err = filter_and_return_output(
382      partial_dump_contents2,
383      8192, # Set a sufficiently large bufsize to avoid a deadlock
384      "exclude", "branches/B2",
385      "--skip-missing-merge-sources",
386      "--drop-empty-revs",
387      "--renumber-revs")
388
389  # Starting with the same expectation we had when loading into an empty
390  # repository, adjust each revision by +6 to account for the six revision
391  # already present in the target repos, that gives:
392  #
393  #   Properties on 'branches/B1':
394  #     svn:mergeinfo
395  #       /branches/B2:12-13
396  #       /trunk:10
397  #   Properties on 'branches/B1/B/E':
398  #     svn:mergeinfo
399  #       /branches/B2/B/E:12-13
400  #       /trunk/B/E:9-10
401  #   Properties on 'branches/B2':
402  #     svn:mergeinfo
403  #       /trunk:10
404  #
405  # ...But /branches/B2 has been filtered out, so all references to
406  # that branch should be gone, leaving:
407  #
408  #   Properties on 'branches/B1':
409  #     svn:mergeinfo
410  #       /trunk:10
411  #   Properties on 'branches/B1/B/E':
412  #     svn:mergeinfo
413  #       /trunk/B/E:9-10
414  #
415  # ...But wait, there's more!  Because we use the '--drop-empty-revs'
416  # option, when filtering out 'branches/B2' all the revisions that effect
417  # only that branch should be dropped (i.e. original revs r7, r11, and r12).
418  # In and of itself that has no effect, but we also specifiy the
419  # '--renumber-revs' option, so when r7 is dropped, r8 should map to r7,
420  # r9 to r8, and r10 to r9 (and so on).  That should finally leave us with:
421  #
422  #   Properties on 'branches/B1':
423  #     svn:mergeinfo
424  #       /trunk:9
425  #   Properties on 'branches/B1/B/E':
426  #     svn:mergeinfo
427  #       /trunk/B/E:8-9
428  #
429  # This test currently fails with this mergeinfo:
430  #
431  #
432  #
433  #
434  # Check that all the blather above really happens.  First does
435  # svndumpfilter report what we expect to stderr?
436  expected_err = [
437      "Excluding (and dropping empty revisions for) prefixes:\n",
438      "   '/branches/B2'\n",
439      "\n",
440      "Revision 6 committed as 6.\n",
441      "Revision 7 skipped.\n",        # <-- DROP!
442      "Revision 8 committed as 7.\n",
443      "Revision 9 committed as 8.\n",
444      "Revision 10 committed as 9.\n",
445      "Revision 11 skipped.\n",       # <-- DROP!
446      "Revision 12 skipped.\n",       # <-- DROP!
447      "Revision 13 committed as 10.\n",
448      "Revision 14 committed as 11.\n",
449      "Revision 15 committed as 12.\n",
450      "\n",
451      "Dropped 3 revisions.\n",
452      "\n",
453      "Revisions renumbered as follows:\n",
454      "   15 => 12\n",
455      "   14 => 11\n",
456      "   13 => 10\n",
457      "   12 => (dropped)\n", # <-- DROP!
458      "   11 => (dropped)\n", # <-- DROP!
459      "   10 => 9\n",
460      "   9 => 8\n",
461      "   8 => 7\n",
462      "   7 => (dropped)\n",  # <-- DROP!
463      "   6 => 6\n",
464      "\n",
465      "Dropped 2 nodes:\n",
466      "   '/branches/B2'\n",
467      "   '/branches/B2/D/H/chi'\n",
468      "\n"]
469  svntest.verify.verify_outputs(
470      "Actual svndumpfilter stderr does not agree with expected stderr",
471      None, filtered_err, None, expected_err)
472
473  # Now actually load the filtered dump into the skeleton repository
474  # and then check the resulting mergeinfo.
475  load_dumpstream(sbox, filtered_dumpfile2,
476                  '--parent-dir', '/Projects/Project-X', '--ignore-uuid')
477
478  url = sbox.repo_url + "/Projects/Project-X/branches"
479  expected_output = svntest.verify.UnorderedOutput([
480    url + "/B1 - /Projects/Project-X/trunk:9\n",
481    url + "/B1/B/E - /Projects/Project-X/trunk/B/E:8-9\n"])
482  svntest.actions.run_and_verify_svn(expected_output, [],
483                                     'propget', 'svn:mergeinfo', '-R',
484                                     sbox.repo_url)
485
486#----------------------------------------------------------------------
487# More testing for issue #3020 'Reflect dropped/renumbered revisions in
488# svn:mergeinfo data during svnadmin load'
489#
490# Using svndumpfilter with the --drop-empty-revs option, but without the
491# --renumber-revs option, can create a dump with non-contiguous revisions.
492# Such dumps should not interfere with the correct remapping of mergeinfo
493# source revisions.
494@Issue(3020)
495def dropped_but_not_renumbered_empty_revs(sbox):
496  "mergeinfo maps correctly when dropping revs"
497
498  sbox.build(empty=True)
499
500  # The dump file mergeinfo_included_full.dump represents this repository:
501  #
502  #
503  #                       __________________________________________
504  #                      |                                         |
505  #                      |             ____________________________|_____
506  #                      |            |                            |     |
507  # trunk---r2---r3-----r5---r6-------r8---r9--------------->      |     |
508  #   r1             |        |     |       |                      |     |
509  # initial          |        |     |       |______                |     |
510  # import         copy       |   copy             |            merge   merge
511  #                  |        |     |            merge           (r5)   (r8)
512  #                  |        |     |            (r9)              |     |
513  #                  |        |     |              |               |     |
514  #                  |        |     V              V               |     |
515  #                  |        | branches/B2-------r11---r12---->   |     |
516  #                  |        |     r7              |____|         |     |
517  #                  |        |                        |           |     |
518  #                  |      merge                      |___        |     |
519  #                  |      (r6)                           |       |     |
520  #                  |        |_________________           |       |     |
521  #                  |                          |        merge     |     |
522  #                  |                          |      (r11-12)    |     |
523  #                  |                          |          |       |     |
524  #                  V                          V          V       |     |
525  #              branches/B1-------------------r10--------r13-->   |     |
526  #                  r4                                            |     |
527  #                   |                                            V     V
528  #                  branches/B1/B/E------------------------------r14---r15->
529  #
530  #
531  # The mergeinfo on mergeinfo_included_full.dump is:
532  #
533  #   Properties on 'branches/B1':
534  #     svn:mergeinfo
535  #       /branches/B2:11-12
536  #       /trunk:6,9
537  #   Properties on 'branches/B1/B/E':
538  #     svn:mergeinfo
539  #       /branches/B2/B/E:11-12
540  #       /trunk/B/E:5-6,8-9
541  #   Properties on 'branches/B2':
542  #     svn:mergeinfo
543  #       /trunk:9
544  #
545  # Use svndumpfilter to filter mergeinfo_included_full.dump, excluding
546  # branches/B2, while dropping, but not renumbering, empty revisions.
547  #
548  # Load the filtered dump into an empty repository.  Since we are excluding
549  # /branches/B2 and dropping empty revs, revisions 7, 11, and 12 won't be
550  # included in the filtered dump.
551  full_dump = os.path.join(os.path.dirname(sys.argv[0]),
552                                   'svnadmin_tests_data',
553                                   'mergeinfo_included_full.dump')
554  full_dump_contents = svntest.actions.load_dumpfile(full_dump)
555  filtered_dumpfile, filtered_out = filter_and_return_output(
556      full_dump_contents,
557      16384, # Set a sufficiently large bufsize to avoid a deadlock
558      "exclude", "branches/B2",
559      "--skip-missing-merge-sources", "--drop-empty-revs")
560
561  # Now load the filtered dump into an empty repository.
562  load_dumpstream(sbox, filtered_dumpfile, '--ignore-uuid')
563
564  # The mergeinfo in the newly loaded repos should have no references to the
565  # dropped branch and the remaining merge source revs should be remapped to
566  # reflect the fact that the loaded repository no longer has any empty
567  # revisions:
568  #
569  #   Properties on 'branches/B1':
570  #     svn:mergeinfo
571  #       /trunk:6,8
572  #                ^
573  #       With r7 dropped, r9 in the incoming
574  #       dump becomes r8 in the loaded repos.
575  #
576  #   Properties on 'branches/B1/B/E':
577  #     svn:mergeinfo
578  #       /trunk/B/E:5-8
579  #                    ^
580  #       With r7 dropped, r8 and r9 in the incoming
581  #       dump becomes r7 and r8 in the loaded repos.
582
583  # Check the resulting mergeinfo.
584  url = sbox.repo_url + "/branches"
585  expected_output = svntest.verify.UnorderedOutput([
586    url + "/B1 - /trunk:6,8\n",
587    url + "/B1/B/E - /trunk/B/E:5-8\n"])
588  svntest.actions.run_and_verify_svn(expected_output, [],
589                                     'propget', 'svn:mergeinfo', '-R',
590                                     sbox.repo_url)
591
592#----------------------------------------------------------------------
593def match_empty_prefix(sbox):
594  "svndumpfilter with an empty prefix"
595
596  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
597                                   'svndumpfilter_tests_data',
598                                   'greek_tree.dump')
599  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
600
601  def test(sbox, dumpfile, *dumpargs):
602    """Run svndumpfilter with DUMPFILE as the input lines, load
603       the result and check it matches EXPECTED_DISK, EXPECTED_OUTPUT,
604       EXPECTED_STATUS."""
605
606    # Filter the Greek tree dump
607    filtered_output, filtered_err = filter_and_return_output(dumpfile, 0,
608                                                             '--quiet',
609                                                             *dumpargs)
610    if filtered_err:
611      raise verify.UnexpectedStderr(filtered_err)
612
613    # Load the filtered dump into a repo and check the result
614    sbox.build(empty=True)
615    load_dumpstream(sbox, filtered_output, '--ignore-uuid')
616    svntest.actions.run_and_verify_update(sbox.wc_dir,
617                                          expected_output,
618                                          expected_disk,
619                                          expected_status)
620
621  # Test excluding everything
622  expected_disk = svntest.wc.State(sbox.wc_dir, {})
623  expected_output = svntest.wc.State(sbox.wc_dir, {})
624  expected_status = svntest.wc.State(sbox.wc_dir, {
625                      '': Item(status='  ', wc_rev=1) })
626
627  test(sbox, dumpfile, 'exclude', '')
628
629  # Test including everything
630  expected_disk = svntest.main.greek_state.copy()
631  expected_output = svntest.main.greek_state.copy().tweak(status='A ')
632  expected_status = svntest.actions.get_virginal_state(sbox.wc_dir, 1)
633
634  test(sbox, dumpfile, 'include', '', '/A/D/G')
635
636  # Note: We also ought to test the '--pattern' option, including or
637  # excluding a pattern of '*'.  However, passing a wildcard parameter
638  # is troublesome on Windows: it may be expanded, depending on whether
639  # the svndumpfilter executable was linked with 'setargv.obj', and there
640  # doesn't seem to be a consistent way to quote such an argument to
641  # prevent expansion.
642
643@Issue(2760)
644def accepts_deltas(sbox):
645  "accepts deltas in the input"
646  # Accept format v3 (as created by 'svnadmin --deltas' or svnrdump).
647
648  sbox.build(empty=True)
649  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
650                                   'svndumpfilter_tests_data',
651                                   'simple_v3.dump')
652  dump_in = svntest.actions.load_dumpfile(dumpfile_location)
653
654  dump_out, err = filter_and_return_output(dump_in, 0, "include",
655                                                        "trunk", "--quiet")
656
657  expected_revs = [
658    svntest.wc.State('', {
659      'trunk'     : svntest.wc.StateItem(props={'soup': 'No soup for you!'}),
660      'trunk/foo' : svntest.wc.StateItem("This is file 'foo'.\n"),
661      }),
662    svntest.wc.State('', {
663      'trunk'     : svntest.wc.StateItem(props={'soup': 'No soup for you!'}),
664      'trunk/foo' : svntest.wc.StateItem("This is file 'foo'.\n"),
665      }),
666    svntest.wc.State('', {
667      'trunk'     : svntest.wc.StateItem(props={'story': 'Yada yada yada...'}),
668      'trunk/foo' : svntest.wc.StateItem("This is file 'foo'.\n"),
669      }),
670    ]
671
672  load_and_verify_dumpstream(sbox, [], [], expected_revs, True, dump_out,
673                             '--ignore-uuid')
674
675
676
677@Issue(4234)
678def dumpfilter_targets_expect_leading_slash_prefixes(sbox):
679  "dumpfilter targets expect leading '/' in prefixes"
680  ## See https://issues.apache.org/jira/browse/SVN-4234. ##
681
682  sbox.build(empty=True)
683
684  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
685                                   'svndumpfilter_tests_data',
686                                   'greek_tree.dump')
687  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
688
689  (fd, targets_file) = tempfile.mkstemp(dir=svntest.main.temp_dir)
690  try:
691    targets = open(targets_file, 'w')
692
693    # Removing the leading slash in path prefixes should work.
694    targets.write('A/D/H\n')
695    targets.write('A/D/G\n')
696    targets.close()
697    _simple_dumpfilter_test(sbox, dumpfile,
698                            'exclude', '/A/B/E', '--targets', targets_file)
699  finally:
700    os.close(fd)
701    os.remove(targets_file)
702
703@Issue(3681)
704def drop_all_empty_revisions(sbox):
705  "drop all empty revisions except revision 0"
706
707  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
708                                   'svndumpfilter_tests_data',
709                                   'empty_revisions.dump')
710  dump_contents = svntest.actions.load_dumpfile(dumpfile_location)
711
712  filtered_dumpfile, filtered_err = filter_and_return_output(
713      dump_contents,
714      8192, # Set a sufficiently large bufsize to avoid a deadlock
715      "include", "branch1",
716      "--drop-all-empty-revs")
717
718  expected_err = [
719       "Including (and dropping empty revisions for) prefixes:\n",
720       "   '/branch1'\n",
721       "\n",
722       "Revision 0 committed as 0.\n",
723       "Revision 1 skipped.\n",
724       "Revision 2 committed as 2.\n",
725       "Revision 3 skipped.\n",
726       "\n",
727       "Dropped 2 revisions.\n",
728       "\n"]
729
730  svntest.verify.verify_outputs(
731      "Actual svndumpfilter stderr does not agree with expected stderr",
732      None, filtered_err, None, expected_err)
733
734  # Test with --renumber-revs option.
735  filtered_dumpfile, filtered_err = filter_and_return_output(
736      dump_contents,
737      8192, # Set a sufficiently large bufsize to avoid a deadlock
738      "include", "branch1",
739      "--drop-all-empty-revs",
740      "--renumber-revs")
741
742  expected_err = [
743       "Including (and dropping empty revisions for) prefixes:\n",
744       "   '/branch1'\n",
745       "\n",
746       "Revision 0 committed as 0.\n",
747       "Revision 1 skipped.\n",
748       "Revision 2 committed as 1.\n",
749       "Revision 3 skipped.\n",
750       "\n",
751       "Dropped 2 revisions.\n",
752       "\n",
753       "Revisions renumbered as follows:\n",
754       "   3 => (dropped)\n",
755       "   2 => 1\n",
756       "   1 => (dropped)\n",
757       "   0 => 0\n",
758       "\n"]
759
760  svntest.verify.verify_outputs(
761      "Actual svndumpfilter stderr does not agree with expected stderr",
762      None, filtered_err, None, expected_err)
763
764
765########################################################################
766# Run the tests
767
768
769# list all tests here, starting with None:
770test_list = [ None,
771              reflect_dropped_renumbered_revs,
772              svndumpfilter_loses_mergeinfo,
773              dumpfilter_with_targets,
774              dumpfilter_with_patterns,
775              filter_mergeinfo_revs_outside_of_dump_stream,
776              dropped_but_not_renumbered_empty_revs,
777              match_empty_prefix,
778              accepts_deltas,
779              dumpfilter_targets_expect_leading_slash_prefixes,
780              drop_all_empty_revisions,
781              ]
782
783if __name__ == '__main__':
784  svntest.main.run_tests(test_list)
785  # NOTREACHED
786
787
788### End of file.
789