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

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import re, sys
28
28
from difflib import unified_diff, ndiff
29
29
import pprint
 
30
import logging
30
31
 
31
32
import svntest
32
33
 
 
34
logger = logging.getLogger()
 
35
 
33
36
 
34
37
######################################################################
35
38
# Exception types
93
96
    raise SVNIncorrectDatatype("Unexpected type for '%s' data" % output_type)
94
97
  return expected
95
98
 
96
 
class ExpectedOutput:
97
 
  """Contains expected output, and performs comparisons."""
98
 
 
99
 
  is_regex = False
100
 
  is_unordered = False
101
 
 
102
 
  def __init__(self, output, match_all=True):
103
 
    """Initialize the expected output to OUTPUT which is a string, or a list
104
 
    of strings, or None meaning an empty list. If MATCH_ALL is True, the
105
 
    expected strings will be matched with the actual strings, one-to-one, in
106
 
    the same order. If False, they will be matched with a subset of the
107
 
    actual strings, one-to-one, in the same order, ignoring any other actual
108
 
    strings among the matching ones."""
109
 
    self.output = output
 
99
class ExpectedOutput(object):
 
100
  """Matches an ordered list of lines.
 
101
 
 
102
     If MATCH_ALL is True, the expected lines must match all the actual
 
103
     lines, one-to-one, in the same order.  If MATCH_ALL is False, the
 
104
     expected lines must match a subset of the actual lines, one-to-one,
 
105
     in the same order, ignoring any other actual lines among the
 
106
     matching ones.
 
107
  """
 
108
 
 
109
  def __init__(self, expected, match_all=True):
 
110
    """Initialize the expected output to EXPECTED which is a string, or
 
111
       a list of strings.
 
112
    """
 
113
    assert expected is not None
 
114
    self.expected = expected
110
115
    self.match_all = match_all
111
116
 
112
117
  def __str__(self):
113
 
    return str(self.output)
 
118
    return str(self.expected)
114
119
 
115
120
  def __cmp__(self, other):
116
 
    raise Exception('badness')
117
 
 
118
 
  def matches(self, other, except_re=None):
119
 
    """Return whether SELF.output matches OTHER (which may be a list
120
 
    of newline-terminated lines, or a single string).  Either value
121
 
    may be None."""
122
 
    if self.output is None:
123
 
      expected = []
124
 
    else:
125
 
      expected = self.output
126
 
    if other is None:
127
 
      actual = []
128
 
    else:
129
 
      actual = other
130
 
 
131
 
    if not isinstance(actual, list):
132
 
      actual = [actual]
 
121
    raise TypeError("ExpectedOutput does not implement direct comparison; "
 
122
                    "see the 'matches()' method")
 
123
 
 
124
  def matches(self, actual):
 
125
    """Return whether SELF matches ACTUAL (which may be a list
 
126
       of newline-terminated lines, or a single string).
 
127
    """
 
128
    assert actual is not None
 
129
    expected = self.expected
133
130
    if not isinstance(expected, list):
134
131
      expected = [expected]
135
 
 
136
 
    if except_re:
137
 
      return self.matches_except(expected, actual, except_re)
138
 
    else:
139
 
      return self.is_equivalent_list(expected, actual)
140
 
 
141
 
  def matches_except(self, expected, actual, except_re):
142
 
    "Return whether EXPECTED and ACTUAL match except for except_re."
143
 
    if not self.is_regex:
144
 
      i_expected = 0
145
 
      i_actual = 0
146
 
      while i_expected < len(expected) and i_actual < len(actual):
147
 
        if re.match(except_re, actual[i_actual]):
148
 
          i_actual += 1
149
 
        elif re.match(except_re, expected[i_expected]):
150
 
          i_expected += 1
151
 
        elif expected[i_expected] == actual[i_actual]:
152
 
          i_expected += 1
153
 
          i_actual += 1
154
 
        else:
155
 
          return False
156
 
      if i_expected == len(expected) and i_actual == len(actual):
157
 
            return True
158
 
      return False
159
 
    else:
160
 
      raise Exception("is_regex and except_re are mutually exclusive")
161
 
 
162
 
  def is_equivalent_list(self, expected, actual):
163
 
    "Return whether EXPECTED and ACTUAL are equivalent."
164
 
    if not self.is_regex:
165
 
      if self.match_all:
166
 
        # The EXPECTED lines must match the ACTUAL lines, one-to-one, in
167
 
        # the same order.
168
 
        return expected == actual
169
 
 
170
 
      # The EXPECTED lines must match a subset of the ACTUAL lines,
171
 
      # one-to-one, in the same order, with zero or more other ACTUAL
172
 
      # lines interspersed among the matching ACTUAL lines.
173
 
      i_expected = 0
174
 
      for actual_line in actual:
175
 
        if expected[i_expected] == actual_line:
176
 
          i_expected += 1
177
 
          if i_expected == len(expected):
178
 
            return True
179
 
      return False
180
 
 
181
 
    expected_re = expected[0]
182
 
    # If we want to check that every line matches the regexp
183
 
    # assume they all match and look for any that don't.  If
184
 
    # only one line matching the regexp is enough, assume none
185
 
    # match and look for even one that does.
 
132
    if not isinstance(actual, list):
 
133
      actual = [actual]
 
134
 
186
135
    if self.match_all:
187
 
      all_lines_match_re = True
188
 
    else:
189
 
      all_lines_match_re = False
190
 
 
191
 
    # If a regex was provided assume that we actually require
192
 
    # some output. Fail if we don't have any.
193
 
    if len(actual) == 0:
194
 
      return False
195
 
 
 
136
      return expected == actual
 
137
 
 
138
    i_expected = 0
196
139
    for actual_line in actual:
197
 
      if self.match_all:
198
 
        if not re.match(expected_re, actual_line):
199
 
          return False
200
 
      else:
201
 
        # As soon an actual_line matches something, then we're good.
202
 
        if re.match(expected_re, actual_line):
 
140
      if expected[i_expected] == actual_line:
 
141
        i_expected += 1
 
142
        if i_expected == len(expected):
203
143
          return True
204
 
 
205
 
    return all_lines_match_re
 
144
    return False
206
145
 
207
146
  def display_differences(self, message, label, actual):
208
 
    """Delegate to the display_lines() routine with the appropriate
209
 
    args.  MESSAGE is ignored if None."""
210
 
    display_lines(message, label, self.output, actual,
211
 
                  self.is_regex, self.is_unordered)
 
147
    """Show the differences between the expected and ACTUAL lines. Print
 
148
       MESSAGE unless it is None, the expected lines, the ACTUAL lines,
 
149
       and a diff, all labeled with LABEL.
 
150
    """
 
151
    display_lines(message, self.expected, actual, label, label)
 
152
    display_lines_diff(self.expected, actual, label, label)
212
153
 
213
154
 
214
155
class AnyOutput(ExpectedOutput):
 
156
  """Matches any non-empty output.
 
157
  """
 
158
 
215
159
  def __init__(self):
216
 
    ExpectedOutput.__init__(self, None, False)
217
 
 
218
 
  def is_equivalent_list(self, ignored, actual):
 
160
    ExpectedOutput.__init__(self, [], False)
 
161
 
 
162
  def matches(self, actual):
 
163
    assert actual is not None
 
164
 
219
165
    if len(actual) == 0:
220
166
      # No actual output. No match.
221
167
      return False
230
176
 
231
177
  def display_differences(self, message, label, actual):
232
178
    if message:
233
 
      print(message)
 
179
      logger.warn(message)
234
180
 
235
181
 
236
182
class RegexOutput(ExpectedOutput):
237
 
  is_regex = True
 
183
  """Matches a single regular expression.
 
184
 
 
185
     If MATCH_ALL is true, every actual line must match the RE.  If
 
186
     MATCH_ALL is false, at least one actual line must match the RE.  In
 
187
     any case, there must be at least one line of actual output.
 
188
  """
 
189
 
 
190
  def __init__(self, expected, match_all=True):
 
191
    "EXPECTED is a regular expression string."
 
192
    assert isinstance(expected, str)
 
193
    ExpectedOutput.__init__(self, expected, match_all)
 
194
    self.expected_re = re.compile(expected)
 
195
 
 
196
  def matches(self, actual):
 
197
    assert actual is not None
 
198
 
 
199
    if not isinstance(actual, list):
 
200
      actual = [actual]
 
201
 
 
202
    # If a regex was provided assume that we require some actual output.
 
203
    # Fail if we don't have any.
 
204
    if len(actual) == 0:
 
205
      return False
 
206
 
 
207
    if self.match_all:
 
208
      return all(self.expected_re.match(line) for line in actual)
 
209
    else:
 
210
      return any(self.expected_re.match(line) for line in actual)
 
211
 
 
212
  def display_differences(self, message, label, actual):
 
213
    display_lines(message, self.expected, actual, label + ' (regexp)', label)
 
214
 
 
215
 
 
216
class RegexListOutput(ExpectedOutput):
 
217
  """Matches an ordered list of regular expressions.
 
218
 
 
219
     If MATCH_ALL is True, the expressions must match all the actual
 
220
     lines, one-to-one, in the same order.  If MATCH_ALL is False, the
 
221
     expressions must match a subset of the actual lines, one-to-one, in
 
222
     the same order, ignoring any other actual lines among the matching
 
223
     ones.
 
224
 
 
225
     In any case, there must be at least one line of actual output.
 
226
  """
 
227
 
 
228
  def __init__(self, expected, match_all=True):
 
229
    "EXPECTED is a list of regular expression strings."
 
230
    assert isinstance(expected, list) and expected != []
 
231
    ExpectedOutput.__init__(self, expected, match_all)
 
232
    self.expected_res = [re.compile(e) for e in expected]
 
233
 
 
234
  def matches(self, actual):
 
235
    assert actual is not None
 
236
    if not isinstance(actual, list):
 
237
      actual = [actual]
 
238
 
 
239
    if self.match_all:
 
240
      return (len(self.expected_res) == len(actual) and
 
241
              all(e.match(a) for e, a in zip(self.expected_res, actual)))
 
242
 
 
243
    i_expected = 0
 
244
    for actual_line in actual:
 
245
      if self.expected_res[i_expected].match(actual_line):
 
246
        i_expected += 1
 
247
        if i_expected == len(self.expected_res):
 
248
          return True
 
249
    return False
 
250
 
 
251
  def display_differences(self, message, label, actual):
 
252
    display_lines(message, self.expected, actual, label + ' (regexp)', label)
238
253
 
239
254
 
240
255
class UnorderedOutput(ExpectedOutput):
241
 
  """Marks unordered output, and performs comparisons."""
242
 
 
243
 
  is_unordered = True
244
 
 
245
 
  def __cmp__(self, other):
246
 
    raise Exception('badness')
247
 
 
248
 
  def matches_except(self, expected, actual, except_re):
249
 
    assert type(actual) == type([]) # ### if this trips: fix it!
250
 
    return self.is_equivalent_list([l for l in expected if not except_re.match(l)],
251
 
                                   [l for l in actual if not except_re.match(l)])
252
 
 
253
 
  def is_equivalent_list(self, expected, actual):
254
 
    "Disregard the order of ACTUAL lines during comparison."
255
 
 
256
 
    e_set = set(expected)
257
 
    a_set = set(actual)
258
 
 
259
 
    if self.match_all:
260
 
      if len(e_set) != len(a_set):
 
256
  """Matches an unordered list of lines.
 
257
 
 
258
     The expected lines must match all the actual lines, one-to-one, in
 
259
     any order.
 
260
  """
 
261
 
 
262
  def __init__(self, expected):
 
263
    assert isinstance(expected, list)
 
264
    ExpectedOutput.__init__(self, expected)
 
265
 
 
266
  def matches(self, actual):
 
267
    if not isinstance(actual, list):
 
268
      actual = [actual]
 
269
 
 
270
    return sorted(self.expected) == sorted(actual)
 
271
 
 
272
  def display_differences(self, message, label, actual):
 
273
    display_lines(message, self.expected, actual, label + ' (unordered)', label)
 
274
    display_lines_diff(self.expected, actual, label + ' (unordered)', label)
 
275
 
 
276
 
 
277
class UnorderedRegexListOutput(ExpectedOutput):
 
278
  """Matches an unordered list of regular expressions.
 
279
 
 
280
     The expressions must match all the actual lines, one-to-one, in any
 
281
     order.
 
282
 
 
283
     Note: This can give a false negative result (no match) when there is
 
284
     an actual line that matches multiple expressions and a different
 
285
     actual line that matches some but not all of those same
 
286
     expressions.  The implementation matches each expression in turn to
 
287
     the first unmatched actual line that it can match, and does not try
 
288
     all the permutations when there are multiple possible matches.
 
289
  """
 
290
 
 
291
  def __init__(self, expected):
 
292
    assert isinstance(expected, list)
 
293
    ExpectedOutput.__init__(self, expected)
 
294
 
 
295
  def matches(self, actual):
 
296
    assert actual is not None
 
297
    if not isinstance(actual, list):
 
298
      actual = [actual]
 
299
 
 
300
    if len(self.expected) != len(actual):
 
301
      return False
 
302
    for e in self.expected:
 
303
      expect_re = re.compile(e)
 
304
      for actual_line in actual:
 
305
        if expect_re.match(actual_line):
 
306
          actual.remove(actual_line)
 
307
          break
 
308
      else:
 
309
        # One of the regexes was not found
261
310
        return False
262
 
      if self.is_regex:
263
 
        for expect_re in e_set:
264
 
          for actual_line in a_set:
265
 
            if re.match(expect_re, actual_line):
266
 
              a_set.remove(actual_line)
267
 
              break
268
 
          else:
269
 
            # One of the regexes was not found
270
 
            return False
 
311
    return True
 
312
 
 
313
  def display_differences(self, message, label, actual):
 
314
    display_lines(message, self.expected, actual,
 
315
                  label + ' (regexp) (unordered)', label)
 
316
 
 
317
 
 
318
class AlternateOutput(ExpectedOutput):
 
319
  """Matches any one of a list of ExpectedOutput instances.
 
320
  """
 
321
 
 
322
  def __init__(self, expected, match_all=True):
 
323
    "EXPECTED is a list of ExpectedOutput instances."
 
324
    assert isinstance(expected, list) and expected != []
 
325
    assert all(isinstance(e, ExpectedOutput) for e in expected)
 
326
    ExpectedOutput.__init__(self, expected)
 
327
 
 
328
  def matches(self, actual):
 
329
    assert actual is not None
 
330
    for e in self.expected:
 
331
      if e.matches(actual):
271
332
        return True
272
 
 
273
 
      # All expected lines must be in the output.
274
 
      return e_set == a_set
275
 
 
276
 
    if self.is_regex:
277
 
      # If any of the expected regexes are in the output, then we match.
278
 
      for expect_re in e_set:
279
 
        for actual_line in a_set:
280
 
          if re.match(expect_re, actual_line):
281
 
            return True
282
 
      return False
283
 
 
284
 
    # If any of the expected lines are in the output, then we match.
285
 
    return len(e_set.intersection(a_set)) > 0
286
 
 
287
 
 
288
 
class UnorderedRegexOutput(UnorderedOutput, RegexOutput):
289
 
  is_regex = True
290
 
  is_unordered = True
 
333
    return False
 
334
 
 
335
  def display_differences(self, message, label, actual):
 
336
    # For now, just display differences against the first alternative.
 
337
    e = self.expected[0]
 
338
    e.display_differences(message, label, actual)
291
339
 
292
340
 
293
341
######################################################################
296
344
def display_trees(message, label, expected, actual):
297
345
  'Print two trees, expected and actual.'
298
346
  if message is not None:
299
 
    print(message)
 
347
    logger.warn(message)
300
348
  if expected is not None:
301
 
    print('EXPECTED %s:' % label)
 
349
    logger.warn('EXPECTED %s:', label)
302
350
    svntest.tree.dump_tree(expected)
303
351
  if actual is not None:
304
 
    print('ACTUAL %s:' % label)
 
352
    logger.warn('ACTUAL %s:', label)
305
353
    svntest.tree.dump_tree(actual)
306
354
 
307
355
 
308
 
def display_lines(message, label, expected, actual, expected_is_regexp=None,
309
 
                  expected_is_unordered=None):
 
356
def display_lines_diff(expected, actual, expected_label, actual_label):
 
357
  """Print a unified diff between EXPECTED (labeled with EXPECTED_LABEL)
 
358
     and ACTUAL (labeled with ACTUAL_LABEL).
 
359
     Each of EXPECTED and ACTUAL is a string or a list of strings.
 
360
  """
 
361
  if not isinstance(expected, list):
 
362
    expected = [expected]
 
363
  if not isinstance(actual, list):
 
364
    actual = [actual]
 
365
  logger.warn('DIFF ' + expected_label + ':')
 
366
  for x in unified_diff(expected, actual,
 
367
                        fromfile='EXPECTED ' + expected_label,
 
368
                        tofile='ACTUAL ' + actual_label):
 
369
    logger.warn('| ' + x.rstrip())
 
370
 
 
371
def display_lines(message, expected, actual,
 
372
                  expected_label, actual_label=None):
310
373
  """Print MESSAGE, unless it is None, then print EXPECTED (labeled
311
 
  with LABEL) followed by ACTUAL (also labeled with LABEL).
312
 
  Both EXPECTED and ACTUAL may be strings or lists of strings."""
 
374
     with EXPECTED_LABEL) followed by ACTUAL (labeled with ACTUAL_LABEL).
 
375
     Each of EXPECTED and ACTUAL is a string or a list of strings.
 
376
  """
313
377
  if message is not None:
314
 
    print(message)
 
378
    logger.warn(message)
 
379
 
 
380
  if type(expected) is str:
 
381
    expected = [expected]
 
382
  if type(actual) is str:
 
383
    actual = [actual]
 
384
  if actual_label is None:
 
385
    actual_label = expected_label
315
386
  if expected is not None:
316
 
    output = 'EXPECTED %s' % label
317
 
    if expected_is_regexp:
318
 
      output += ' (regexp)'
319
 
      expected = [expected + '\n']
320
 
    if expected_is_unordered:
321
 
      output += ' (unordered)'
322
 
    output += ':'
323
 
    print(output)
 
387
    logger.warn('EXPECTED %s:', expected_label)
324
388
    for x in expected:
325
 
      sys.stdout.write(x)
 
389
      logger.warn('| ' + x.rstrip())
326
390
  if actual is not None:
327
 
    print('ACTUAL %s:' % label)
 
391
    logger.warn('ACTUAL %s:', actual_label)
328
392
    for x in actual:
329
 
      sys.stdout.write(x)
330
 
 
331
 
  # Additionally print unified diff
332
 
  if not expected_is_regexp:
333
 
    print('DIFF ' + ' '.join(output.split(' ')[1:]))
334
 
 
335
 
    if type(expected) is str:
336
 
      expected = [expected]
337
 
 
338
 
    if type(actual) is str:
339
 
      actual = [actual]
340
 
 
341
 
    for x in unified_diff(expected, actual,
342
 
                          fromfile="EXPECTED %s" % label,
343
 
                          tofile="ACTUAL %s" % label):
344
 
      sys.stdout.write(x)
 
393
      logger.warn('| ' + x.rstrip())
345
394
 
346
395
def compare_and_display_lines(message, label, expected, actual,
347
 
                              raisable=None, except_re=None):
 
396
                              raisable=None):
348
397
  """Compare two sets of output lines, and print them if they differ,
349
398
  preceded by MESSAGE iff not None.  EXPECTED may be an instance of
350
 
  ExpectedOutput (and if not, it is wrapped as such).  RAISABLE is an
 
399
  ExpectedOutput (and if not, it is wrapped as such).  ACTUAL may be a
 
400
  list of newline-terminated lines, or a single string.  RAISABLE is an
351
401
  exception class, an instance of which is thrown if ACTUAL doesn't
352
402
  match EXPECTED."""
353
403
  if raisable is None:
354
404
    raisable = svntest.main.SVNLineUnequal
355
405
  ### It'd be nicer to use createExpectedOutput() here, but its
356
406
  ### semantics don't match all current consumers of this function.
 
407
  assert expected is not None
 
408
  assert actual is not None
357
409
  if not isinstance(expected, ExpectedOutput):
358
410
    expected = ExpectedOutput(expected)
359
411
 
360
412
  if isinstance(actual, str):
361
413
    actual = [actual]
362
 
  actual = [line for line in actual if not line.startswith('DBG:')]
 
414
  actual = svntest.main.filter_dbg(actual)
363
415
 
364
 
  if not expected.matches(actual, except_re):
 
416
  if not expected.matches(actual):
365
417
    expected.display_differences(message, label, actual)
366
418
    raise raisable
367
419
 
399
451
  not None) and raise an exception."""
400
452
 
401
453
  if expected != actual:
402
 
    display_lines(message, "Exit Code",
403
 
                  str(expected) + '\n', str(actual) + '\n')
 
454
    display_lines(message, str(expected), str(actual), "Exit Code")
404
455
    raise raisable
405
456
 
406
457
# A simple dump file parser.  While sufficient for the current