xref: /netbsd-src/external/gpl3/gcc.old/dist/contrib/testsuite-management/validate_failures.py (revision 36ac495d2b3ea2b9d96377b2143ebfedac224b92)
1*36ac495dSmrg#!/usr/bin/python
2*36ac495dSmrg
3*36ac495dSmrg# Script to compare testsuite failures against a list of known-to-fail
4*36ac495dSmrg# tests.
5*36ac495dSmrg#
6*36ac495dSmrg# NOTE: This script is used in installations that are running Python 2.4.
7*36ac495dSmrg#       Please stick to syntax features available in 2.4 and earlier
8*36ac495dSmrg#       versions.
9*36ac495dSmrg
10*36ac495dSmrg# Contributed by Diego Novillo <dnovillo@google.com>
11*36ac495dSmrg#
12*36ac495dSmrg# Copyright (C) 2011-2013 Free Software Foundation, Inc.
13*36ac495dSmrg#
14*36ac495dSmrg# This file is part of GCC.
15*36ac495dSmrg#
16*36ac495dSmrg# GCC is free software; you can redistribute it and/or modify
17*36ac495dSmrg# it under the terms of the GNU General Public License as published by
18*36ac495dSmrg# the Free Software Foundation; either version 3, or (at your option)
19*36ac495dSmrg# any later version.
20*36ac495dSmrg#
21*36ac495dSmrg# GCC is distributed in the hope that it will be useful,
22*36ac495dSmrg# but WITHOUT ANY WARRANTY; without even the implied warranty of
23*36ac495dSmrg# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24*36ac495dSmrg# GNU General Public License for more details.
25*36ac495dSmrg#
26*36ac495dSmrg# You should have received a copy of the GNU General Public License
27*36ac495dSmrg# along with GCC; see the file COPYING.  If not, write to
28*36ac495dSmrg# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
29*36ac495dSmrg# Boston, MA 02110-1301, USA.
30*36ac495dSmrg
31*36ac495dSmrg"""This script provides a coarser XFAILing mechanism that requires no
32*36ac495dSmrgdetailed DejaGNU markings.  This is useful in a variety of scenarios:
33*36ac495dSmrg
34*36ac495dSmrg- Development branches with many known failures waiting to be fixed.
35*36ac495dSmrg- Release branches with known failures that are not considered
36*36ac495dSmrg  important for the particular release criteria used in that branch.
37*36ac495dSmrg
38*36ac495dSmrgThe script must be executed from the toplevel build directory.  When
39*36ac495dSmrgexecuted it will:
40*36ac495dSmrg
41*36ac495dSmrg1- Determine the target built: TARGET
42*36ac495dSmrg2- Determine the source directory: SRCDIR
43*36ac495dSmrg3- Look for a failure manifest file in
44*36ac495dSmrg   <SRCDIR>/<MANIFEST_SUBDIR>/<MANIFEST_NAME>.xfail
45*36ac495dSmrg4- Collect all the <tool>.sum files from the build tree.
46*36ac495dSmrg5- Produce a report stating:
47*36ac495dSmrg   a- Failures expected in the manifest but not present in the build.
48*36ac495dSmrg   b- Failures in the build not expected in the manifest.
49*36ac495dSmrg6- If all the build failures are expected in the manifest, it exits
50*36ac495dSmrg   with exit code 0.  Otherwise, it exits with error code 1.
51*36ac495dSmrg
52*36ac495dSmrgManifest files contain expected DejaGNU results that are otherwise
53*36ac495dSmrgtreated as failures.
54*36ac495dSmrgThey may also contain additional text:
55*36ac495dSmrg
56*36ac495dSmrg# This is a comment.  - self explanatory
57*36ac495dSmrg@include file         - the file is a path relative to the includer
58*36ac495dSmrg@remove result text   - result text is removed from the expected set
59*36ac495dSmrg"""
60*36ac495dSmrg
61*36ac495dSmrgimport datetime
62*36ac495dSmrgimport optparse
63*36ac495dSmrgimport os
64*36ac495dSmrgimport re
65*36ac495dSmrgimport sys
66*36ac495dSmrg
67*36ac495dSmrg# Handled test results.
68*36ac495dSmrg_VALID_TEST_RESULTS = [ 'FAIL', 'UNRESOLVED', 'XPASS', 'ERROR' ]
69*36ac495dSmrg_VALID_TEST_RESULTS_REX = re.compile("%s" % "|".join(_VALID_TEST_RESULTS))
70*36ac495dSmrg
71*36ac495dSmrg# Subdirectory of srcdir in which to find the manifest file.
72*36ac495dSmrg_MANIFEST_SUBDIR = 'contrib/testsuite-management'
73*36ac495dSmrg
74*36ac495dSmrg# Pattern for naming manifest files.
75*36ac495dSmrg# The first argument should be the toplevel GCC(/GNU tool) source directory.
76*36ac495dSmrg# The second argument is the manifest subdir.
77*36ac495dSmrg# The third argument is the manifest target, which defaults to the target
78*36ac495dSmrg# triplet used during the build.
79*36ac495dSmrg_MANIFEST_PATH_PATTERN = '%s/%s/%s.xfail'
80*36ac495dSmrg
81*36ac495dSmrg# The options passed to the program.
82*36ac495dSmrg_OPTIONS = None
83*36ac495dSmrg
84*36ac495dSmrgdef Error(msg):
85*36ac495dSmrg  print >>sys.stderr, 'error: %s' % msg
86*36ac495dSmrg  sys.exit(1)
87*36ac495dSmrg
88*36ac495dSmrg
89*36ac495dSmrgclass TestResult(object):
90*36ac495dSmrg  """Describes a single DejaGNU test result as emitted in .sum files.
91*36ac495dSmrg
92*36ac495dSmrg  We are only interested in representing unsuccessful tests.  So, only
93*36ac495dSmrg  a subset of all the tests are loaded.
94*36ac495dSmrg
95*36ac495dSmrg  The summary line used to build the test result should have this format:
96*36ac495dSmrg
97*36ac495dSmrg  attrlist | XPASS: gcc.dg/unroll_1.c (test for excess errors)
98*36ac495dSmrg  ^^^^^^^^   ^^^^^  ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
99*36ac495dSmrg  optional   state  name              description
100*36ac495dSmrg  attributes
101*36ac495dSmrg
102*36ac495dSmrg  Attributes:
103*36ac495dSmrg    attrlist: A comma separated list of attributes.
104*36ac495dSmrg      Valid values:
105*36ac495dSmrg        flaky            Indicates that this test may not always fail.  These
106*36ac495dSmrg                         tests are reported, but their presence does not affect
107*36ac495dSmrg                         the results.
108*36ac495dSmrg
109*36ac495dSmrg        expire=YYYYMMDD  After this date, this test will produce an error
110*36ac495dSmrg                         whether it is in the manifest or not.
111*36ac495dSmrg
112*36ac495dSmrg    state: One of UNRESOLVED, XPASS or FAIL.
113*36ac495dSmrg    name: File name for the test.
114*36ac495dSmrg    description: String describing the test (flags used, dejagnu message, etc)
115*36ac495dSmrg    ordinal: Monotonically increasing integer.
116*36ac495dSmrg             It is used to keep results for one .exp file sorted
117*36ac495dSmrg             by the order the tests were run.
118*36ac495dSmrg  """
119*36ac495dSmrg
120*36ac495dSmrg  def __init__(self, summary_line, ordinal=-1):
121*36ac495dSmrg    try:
122*36ac495dSmrg      (self.attrs, summary_line) = SplitAttributesFromSummaryLine(summary_line)
123*36ac495dSmrg      try:
124*36ac495dSmrg        (self.state,
125*36ac495dSmrg         self.name,
126*36ac495dSmrg         self.description) = re.match(r'([A-Z]+):\s*(\S+)\s*(.*)',
127*36ac495dSmrg                                      summary_line).groups()
128*36ac495dSmrg      except:
129*36ac495dSmrg        print 'Failed to parse summary line: "%s"' % summary_line
130*36ac495dSmrg        raise
131*36ac495dSmrg      self.ordinal = ordinal
132*36ac495dSmrg    except ValueError:
133*36ac495dSmrg      Error('Cannot parse summary line "%s"' % summary_line)
134*36ac495dSmrg
135*36ac495dSmrg    if self.state not in _VALID_TEST_RESULTS:
136*36ac495dSmrg      Error('Invalid test result %s in "%s" (parsed as "%s")' % (
137*36ac495dSmrg            self.state, summary_line, self))
138*36ac495dSmrg
139*36ac495dSmrg  def __lt__(self, other):
140*36ac495dSmrg    return (self.name < other.name or
141*36ac495dSmrg            (self.name == other.name and self.ordinal < other.ordinal))
142*36ac495dSmrg
143*36ac495dSmrg  def __hash__(self):
144*36ac495dSmrg    return hash(self.state) ^ hash(self.name) ^ hash(self.description)
145*36ac495dSmrg
146*36ac495dSmrg  def __eq__(self, other):
147*36ac495dSmrg    return (self.state == other.state and
148*36ac495dSmrg            self.name == other.name and
149*36ac495dSmrg            self.description == other.description)
150*36ac495dSmrg
151*36ac495dSmrg  def __ne__(self, other):
152*36ac495dSmrg    return not (self == other)
153*36ac495dSmrg
154*36ac495dSmrg  def __str__(self):
155*36ac495dSmrg    attrs = ''
156*36ac495dSmrg    if self.attrs:
157*36ac495dSmrg      attrs = '%s | ' % self.attrs
158*36ac495dSmrg    return '%s%s: %s %s' % (attrs, self.state, self.name, self.description)
159*36ac495dSmrg
160*36ac495dSmrg  def ExpirationDate(self):
161*36ac495dSmrg    # Return a datetime.date object with the expiration date for this
162*36ac495dSmrg    # test result.  Return None, if no expiration has been set.
163*36ac495dSmrg    if re.search(r'expire=', self.attrs):
164*36ac495dSmrg      expiration = re.search(r'expire=(\d\d\d\d)(\d\d)(\d\d)', self.attrs)
165*36ac495dSmrg      if not expiration:
166*36ac495dSmrg        Error('Invalid expire= format in "%s".  Must be of the form '
167*36ac495dSmrg              '"expire=YYYYMMDD"' % self)
168*36ac495dSmrg      return datetime.date(int(expiration.group(1)),
169*36ac495dSmrg                           int(expiration.group(2)),
170*36ac495dSmrg                           int(expiration.group(3)))
171*36ac495dSmrg    return None
172*36ac495dSmrg
173*36ac495dSmrg  def HasExpired(self):
174*36ac495dSmrg    # Return True if the expiration date of this result has passed.
175*36ac495dSmrg    expiration_date = self.ExpirationDate()
176*36ac495dSmrg    if expiration_date:
177*36ac495dSmrg      now = datetime.date.today()
178*36ac495dSmrg      return now > expiration_date
179*36ac495dSmrg
180*36ac495dSmrg
181*36ac495dSmrgdef GetMakefileValue(makefile_name, value_name):
182*36ac495dSmrg  if os.path.exists(makefile_name):
183*36ac495dSmrg    makefile = open(makefile_name)
184*36ac495dSmrg    for line in makefile:
185*36ac495dSmrg      if line.startswith(value_name):
186*36ac495dSmrg        (_, value) = line.split('=', 1)
187*36ac495dSmrg        value = value.strip()
188*36ac495dSmrg        makefile.close()
189*36ac495dSmrg        return value
190*36ac495dSmrg    makefile.close()
191*36ac495dSmrg  return None
192*36ac495dSmrg
193*36ac495dSmrg
194*36ac495dSmrgdef ValidBuildDirectory(builddir):
195*36ac495dSmrg  if (not os.path.exists(builddir) or
196*36ac495dSmrg      not os.path.exists('%s/Makefile' % builddir)):
197*36ac495dSmrg    return False
198*36ac495dSmrg  return True
199*36ac495dSmrg
200*36ac495dSmrg
201*36ac495dSmrgdef IsComment(line):
202*36ac495dSmrg  """Return True if line is a comment."""
203*36ac495dSmrg  return line.startswith('#')
204*36ac495dSmrg
205*36ac495dSmrg
206*36ac495dSmrgdef SplitAttributesFromSummaryLine(line):
207*36ac495dSmrg  """Splits off attributes from a summary line, if present."""
208*36ac495dSmrg  if '|' in line and not _VALID_TEST_RESULTS_REX.match(line):
209*36ac495dSmrg    (attrs, line) = line.split('|', 1)
210*36ac495dSmrg    attrs = attrs.strip()
211*36ac495dSmrg  else:
212*36ac495dSmrg    attrs = ''
213*36ac495dSmrg  line = line.strip()
214*36ac495dSmrg  return (attrs, line)
215*36ac495dSmrg
216*36ac495dSmrg
217*36ac495dSmrgdef IsInterestingResult(line):
218*36ac495dSmrg  """Return True if line is one of the summary lines we care about."""
219*36ac495dSmrg  (_, line) = SplitAttributesFromSummaryLine(line)
220*36ac495dSmrg  return bool(_VALID_TEST_RESULTS_REX.match(line))
221*36ac495dSmrg
222*36ac495dSmrg
223*36ac495dSmrgdef IsInclude(line):
224*36ac495dSmrg  """Return True if line is an include of another file."""
225*36ac495dSmrg  return line.startswith("@include ")
226*36ac495dSmrg
227*36ac495dSmrg
228*36ac495dSmrgdef GetIncludeFile(line, includer):
229*36ac495dSmrg  """Extract the name of the include file from line."""
230*36ac495dSmrg  includer_dir = os.path.dirname(includer)
231*36ac495dSmrg  include_file = line[len("@include "):]
232*36ac495dSmrg  return os.path.join(includer_dir, include_file.strip())
233*36ac495dSmrg
234*36ac495dSmrg
235*36ac495dSmrgdef IsNegativeResult(line):
236*36ac495dSmrg  """Return True if line should be removed from the expected results."""
237*36ac495dSmrg  return line.startswith("@remove ")
238*36ac495dSmrg
239*36ac495dSmrg
240*36ac495dSmrgdef GetNegativeResult(line):
241*36ac495dSmrg  """Extract the name of the negative result from line."""
242*36ac495dSmrg  line = line[len("@remove "):]
243*36ac495dSmrg  return line.strip()
244*36ac495dSmrg
245*36ac495dSmrg
246*36ac495dSmrgdef ParseManifestWorker(result_set, manifest_path):
247*36ac495dSmrg  """Read manifest_path, adding the contents to result_set."""
248*36ac495dSmrg  if _OPTIONS.verbosity >= 1:
249*36ac495dSmrg    print 'Parsing manifest file %s.' % manifest_path
250*36ac495dSmrg  manifest_file = open(manifest_path)
251*36ac495dSmrg  for line in manifest_file:
252*36ac495dSmrg    line = line.strip()
253*36ac495dSmrg    if line == "":
254*36ac495dSmrg      pass
255*36ac495dSmrg    elif IsComment(line):
256*36ac495dSmrg      pass
257*36ac495dSmrg    elif IsNegativeResult(line):
258*36ac495dSmrg      result_set.remove(TestResult(GetNegativeResult(line)))
259*36ac495dSmrg    elif IsInclude(line):
260*36ac495dSmrg      ParseManifestWorker(result_set, GetIncludeFile(line, manifest_path))
261*36ac495dSmrg    elif IsInterestingResult(line):
262*36ac495dSmrg      result_set.add(TestResult(line))
263*36ac495dSmrg    else:
264*36ac495dSmrg      Error('Unrecognized line in manifest file: %s' % line)
265*36ac495dSmrg  manifest_file.close()
266*36ac495dSmrg
267*36ac495dSmrg
268*36ac495dSmrgdef ParseManifest(manifest_path):
269*36ac495dSmrg  """Create a set of TestResult instances from the given manifest file."""
270*36ac495dSmrg  result_set = set()
271*36ac495dSmrg  ParseManifestWorker(result_set, manifest_path)
272*36ac495dSmrg  return result_set
273*36ac495dSmrg
274*36ac495dSmrg
275*36ac495dSmrgdef ParseSummary(sum_fname):
276*36ac495dSmrg  """Create a set of TestResult instances from the given summary file."""
277*36ac495dSmrg  result_set = set()
278*36ac495dSmrg  # ordinal is used when sorting the results so that tests within each
279*36ac495dSmrg  # .exp file are kept sorted.
280*36ac495dSmrg  ordinal=0
281*36ac495dSmrg  sum_file = open(sum_fname)
282*36ac495dSmrg  for line in sum_file:
283*36ac495dSmrg    if IsInterestingResult(line):
284*36ac495dSmrg      result = TestResult(line, ordinal)
285*36ac495dSmrg      ordinal += 1
286*36ac495dSmrg      if result.HasExpired():
287*36ac495dSmrg        # Tests that have expired are not added to the set of expected
288*36ac495dSmrg        # results. If they are still present in the set of actual results,
289*36ac495dSmrg        # they will cause an error to be reported.
290*36ac495dSmrg        print 'WARNING: Expected failure "%s" has expired.' % line.strip()
291*36ac495dSmrg        continue
292*36ac495dSmrg      result_set.add(result)
293*36ac495dSmrg  sum_file.close()
294*36ac495dSmrg  return result_set
295*36ac495dSmrg
296*36ac495dSmrg
297*36ac495dSmrgdef GetManifest(manifest_path):
298*36ac495dSmrg  """Build a set of expected failures from the manifest file.
299*36ac495dSmrg
300*36ac495dSmrg  Each entry in the manifest file should have the format understood
301*36ac495dSmrg  by the TestResult constructor.
302*36ac495dSmrg
303*36ac495dSmrg  If no manifest file exists for this target, it returns an empty set.
304*36ac495dSmrg  """
305*36ac495dSmrg  if os.path.exists(manifest_path):
306*36ac495dSmrg    return ParseManifest(manifest_path)
307*36ac495dSmrg  else:
308*36ac495dSmrg    return set()
309*36ac495dSmrg
310*36ac495dSmrg
311*36ac495dSmrgdef CollectSumFiles(builddir):
312*36ac495dSmrg  sum_files = []
313*36ac495dSmrg  for root, dirs, files in os.walk(builddir):
314*36ac495dSmrg    for ignored in ('.svn', '.git'):
315*36ac495dSmrg      if ignored in dirs:
316*36ac495dSmrg        dirs.remove(ignored)
317*36ac495dSmrg    for fname in files:
318*36ac495dSmrg      if fname.endswith('.sum'):
319*36ac495dSmrg        sum_files.append(os.path.join(root, fname))
320*36ac495dSmrg  return sum_files
321*36ac495dSmrg
322*36ac495dSmrg
323*36ac495dSmrgdef GetResults(sum_files):
324*36ac495dSmrg  """Collect all the test results from the given .sum files."""
325*36ac495dSmrg  build_results = set()
326*36ac495dSmrg  for sum_fname in sum_files:
327*36ac495dSmrg    print '\t%s' % sum_fname
328*36ac495dSmrg    build_results |= ParseSummary(sum_fname)
329*36ac495dSmrg  return build_results
330*36ac495dSmrg
331*36ac495dSmrg
332*36ac495dSmrgdef CompareResults(manifest, actual):
333*36ac495dSmrg  """Compare sets of results and return two lists:
334*36ac495dSmrg     - List of results present in ACTUAL but missing from MANIFEST.
335*36ac495dSmrg     - List of results present in MANIFEST but missing from ACTUAL.
336*36ac495dSmrg  """
337*36ac495dSmrg  # Collect all the actual results not present in the manifest.
338*36ac495dSmrg  # Results in this set will be reported as errors.
339*36ac495dSmrg  actual_vs_manifest = set()
340*36ac495dSmrg  for actual_result in actual:
341*36ac495dSmrg    if actual_result not in manifest:
342*36ac495dSmrg      actual_vs_manifest.add(actual_result)
343*36ac495dSmrg
344*36ac495dSmrg  # Collect all the tests in the manifest that were not found
345*36ac495dSmrg  # in the actual results.
346*36ac495dSmrg  # Results in this set will be reported as warnings (since
347*36ac495dSmrg  # they are expected failures that are not failing anymore).
348*36ac495dSmrg  manifest_vs_actual = set()
349*36ac495dSmrg  for expected_result in manifest:
350*36ac495dSmrg    # Ignore tests marked flaky.
351*36ac495dSmrg    if 'flaky' in expected_result.attrs:
352*36ac495dSmrg      continue
353*36ac495dSmrg    if expected_result not in actual:
354*36ac495dSmrg      manifest_vs_actual.add(expected_result)
355*36ac495dSmrg
356*36ac495dSmrg  return actual_vs_manifest, manifest_vs_actual
357*36ac495dSmrg
358*36ac495dSmrg
359*36ac495dSmrgdef GetManifestPath(srcdir, target, user_provided_must_exist):
360*36ac495dSmrg  """Return the full path to the manifest file."""
361*36ac495dSmrg  manifest_path = _OPTIONS.manifest
362*36ac495dSmrg  if manifest_path:
363*36ac495dSmrg    if user_provided_must_exist and not os.path.exists(manifest_path):
364*36ac495dSmrg      Error('Manifest does not exist: %s' % manifest_path)
365*36ac495dSmrg    return manifest_path
366*36ac495dSmrg  else:
367*36ac495dSmrg    if not srcdir:
368*36ac495dSmrg      Error('Could not determine the location of GCC\'s source tree. '
369*36ac495dSmrg            'The Makefile does not contain a definition for "srcdir".')
370*36ac495dSmrg    if not target:
371*36ac495dSmrg      Error('Could not determine the target triplet for this build. '
372*36ac495dSmrg            'The Makefile does not contain a definition for "target_alias".')
373*36ac495dSmrg    return _MANIFEST_PATH_PATTERN % (srcdir, _MANIFEST_SUBDIR, target)
374*36ac495dSmrg
375*36ac495dSmrg
376*36ac495dSmrgdef GetBuildData():
377*36ac495dSmrg  if not ValidBuildDirectory(_OPTIONS.build_dir):
378*36ac495dSmrg    # If we have been given a set of results to use, we may
379*36ac495dSmrg    # not be inside a valid GCC build directory.  In that case,
380*36ac495dSmrg    # the user must provide both a manifest file and a set
381*36ac495dSmrg    # of results to check against it.
382*36ac495dSmrg    if not _OPTIONS.results or not _OPTIONS.manifest:
383*36ac495dSmrg      Error('%s is not a valid GCC top level build directory. '
384*36ac495dSmrg            'You must use --manifest and --results to do the validation.' %
385*36ac495dSmrg            _OPTIONS.build_dir)
386*36ac495dSmrg    else:
387*36ac495dSmrg      return None, None
388*36ac495dSmrg  srcdir = GetMakefileValue('%s/Makefile' % _OPTIONS.build_dir, 'srcdir =')
389*36ac495dSmrg  target = GetMakefileValue('%s/Makefile' % _OPTIONS.build_dir, 'target_alias=')
390*36ac495dSmrg  print 'Source directory: %s' % srcdir
391*36ac495dSmrg  print 'Build target:     %s' % target
392*36ac495dSmrg  return srcdir, target
393*36ac495dSmrg
394*36ac495dSmrg
395*36ac495dSmrgdef PrintSummary(msg, summary):
396*36ac495dSmrg  print '\n\n%s' % msg
397*36ac495dSmrg  for result in sorted(summary):
398*36ac495dSmrg    print result
399*36ac495dSmrg
400*36ac495dSmrg
401*36ac495dSmrgdef GetSumFiles(results, build_dir):
402*36ac495dSmrg  if not results:
403*36ac495dSmrg    print 'Getting actual results from build directory %s' % build_dir
404*36ac495dSmrg    sum_files = CollectSumFiles(build_dir)
405*36ac495dSmrg  else:
406*36ac495dSmrg    print 'Getting actual results from user-provided results'
407*36ac495dSmrg    sum_files = results.split()
408*36ac495dSmrg  return sum_files
409*36ac495dSmrg
410*36ac495dSmrg
411*36ac495dSmrgdef PerformComparison(expected, actual, ignore_missing_failures):
412*36ac495dSmrg  actual_vs_expected, expected_vs_actual = CompareResults(expected, actual)
413*36ac495dSmrg
414*36ac495dSmrg  tests_ok = True
415*36ac495dSmrg  if len(actual_vs_expected) > 0:
416*36ac495dSmrg    PrintSummary('Unexpected results in this build (new failures)',
417*36ac495dSmrg                 actual_vs_expected)
418*36ac495dSmrg    tests_ok = False
419*36ac495dSmrg
420*36ac495dSmrg  if not ignore_missing_failures and len(expected_vs_actual) > 0:
421*36ac495dSmrg    PrintSummary('Expected results not present in this build (fixed tests)'
422*36ac495dSmrg                 '\n\nNOTE: This is not a failure.  It just means that these '
423*36ac495dSmrg                 'tests were expected\nto fail, but either they worked in '
424*36ac495dSmrg                 'this configuration or they were not\npresent at all.\n',
425*36ac495dSmrg                 expected_vs_actual)
426*36ac495dSmrg
427*36ac495dSmrg  if tests_ok:
428*36ac495dSmrg    print '\nSUCCESS: No unexpected failures.'
429*36ac495dSmrg
430*36ac495dSmrg  return tests_ok
431*36ac495dSmrg
432*36ac495dSmrg
433*36ac495dSmrgdef CheckExpectedResults():
434*36ac495dSmrg  srcdir, target = GetBuildData()
435*36ac495dSmrg  manifest_path = GetManifestPath(srcdir, target, True)
436*36ac495dSmrg  print 'Manifest:         %s' % manifest_path
437*36ac495dSmrg  manifest = GetManifest(manifest_path)
438*36ac495dSmrg  sum_files = GetSumFiles(_OPTIONS.results, _OPTIONS.build_dir)
439*36ac495dSmrg  actual = GetResults(sum_files)
440*36ac495dSmrg
441*36ac495dSmrg  if _OPTIONS.verbosity >= 1:
442*36ac495dSmrg    PrintSummary('Tests expected to fail', manifest)
443*36ac495dSmrg    PrintSummary('\nActual test results', actual)
444*36ac495dSmrg
445*36ac495dSmrg  return PerformComparison(manifest, actual, _OPTIONS.ignore_missing_failures)
446*36ac495dSmrg
447*36ac495dSmrg
448*36ac495dSmrgdef ProduceManifest():
449*36ac495dSmrg  (srcdir, target) = GetBuildData()
450*36ac495dSmrg  manifest_path = GetManifestPath(srcdir, target, False)
451*36ac495dSmrg  print 'Manifest:         %s' % manifest_path
452*36ac495dSmrg  if os.path.exists(manifest_path) and not _OPTIONS.force:
453*36ac495dSmrg    Error('Manifest file %s already exists.\nUse --force to overwrite.' %
454*36ac495dSmrg          manifest_path)
455*36ac495dSmrg
456*36ac495dSmrg  sum_files = GetSumFiles(_OPTIONS.results, _OPTIONS.build_dir)
457*36ac495dSmrg  actual = GetResults(sum_files)
458*36ac495dSmrg  manifest_file = open(manifest_path, 'w')
459*36ac495dSmrg  for result in sorted(actual):
460*36ac495dSmrg    print result
461*36ac495dSmrg    manifest_file.write('%s\n' % result)
462*36ac495dSmrg  manifest_file.close()
463*36ac495dSmrg
464*36ac495dSmrg  return True
465*36ac495dSmrg
466*36ac495dSmrg
467*36ac495dSmrgdef CompareBuilds():
468*36ac495dSmrg  (srcdir, target) = GetBuildData()
469*36ac495dSmrg
470*36ac495dSmrg  sum_files = GetSumFiles(_OPTIONS.results, _OPTIONS.build_dir)
471*36ac495dSmrg  actual = GetResults(sum_files)
472*36ac495dSmrg
473*36ac495dSmrg  clean_sum_files = GetSumFiles(_OPTIONS.results, _OPTIONS.clean_build)
474*36ac495dSmrg  clean = GetResults(clean_sum_files)
475*36ac495dSmrg
476*36ac495dSmrg  return PerformComparison(clean, actual, _OPTIONS.ignore_missing_failures)
477*36ac495dSmrg
478*36ac495dSmrg
479*36ac495dSmrgdef Main(argv):
480*36ac495dSmrg  parser = optparse.OptionParser(usage=__doc__)
481*36ac495dSmrg
482*36ac495dSmrg  # Keep the following list sorted by option name.
483*36ac495dSmrg  parser.add_option('--build_dir', action='store', type='string',
484*36ac495dSmrg                    dest='build_dir', default='.',
485*36ac495dSmrg                    help='Build directory to check (default = .)')
486*36ac495dSmrg  parser.add_option('--clean_build', action='store', type='string',
487*36ac495dSmrg                    dest='clean_build', default=None,
488*36ac495dSmrg                    help='Compare test results from this build against '
489*36ac495dSmrg                    'those of another (clean) build.  Use this option '
490*36ac495dSmrg                    'when comparing the test results of your patch versus '
491*36ac495dSmrg                    'the test results of a clean build without your patch. '
492*36ac495dSmrg                    'You must provide the path to the top directory of your '
493*36ac495dSmrg                    'clean build.')
494*36ac495dSmrg  parser.add_option('--force', action='store_true', dest='force',
495*36ac495dSmrg                    default=False, help='When used with --produce_manifest, '
496*36ac495dSmrg                    'it will overwrite an existing manifest file '
497*36ac495dSmrg                    '(default = False)')
498*36ac495dSmrg  parser.add_option('--ignore_missing_failures', action='store_true',
499*36ac495dSmrg                    dest='ignore_missing_failures', default=False,
500*36ac495dSmrg                    help='When a failure is expected in the manifest but '
501*36ac495dSmrg                    'it is not found in the actual results, the script '
502*36ac495dSmrg                    'produces a note alerting to this fact. This means '
503*36ac495dSmrg                    'that the expected failure has been fixed, or '
504*36ac495dSmrg                    'it did not run, or it may simply be flaky '
505*36ac495dSmrg                    '(default = False)')
506*36ac495dSmrg  parser.add_option('--manifest', action='store', type='string',
507*36ac495dSmrg                    dest='manifest', default=None,
508*36ac495dSmrg                    help='Name of the manifest file to use (default = '
509*36ac495dSmrg                    'taken from '
510*36ac495dSmrg                    'contrib/testsuite-managment/<target_alias>.xfail)')
511*36ac495dSmrg  parser.add_option('--produce_manifest', action='store_true',
512*36ac495dSmrg                    dest='produce_manifest', default=False,
513*36ac495dSmrg                    help='Produce the manifest for the current '
514*36ac495dSmrg                    'build (default = False)')
515*36ac495dSmrg  parser.add_option('--results', action='store', type='string',
516*36ac495dSmrg                    dest='results', default=None, help='Space-separated list '
517*36ac495dSmrg                    'of .sum files with the testing results to check. The '
518*36ac495dSmrg                    'only content needed from these files are the lines '
519*36ac495dSmrg                    'starting with FAIL, XPASS or UNRESOLVED (default = '
520*36ac495dSmrg                    '.sum files collected from the build directory).')
521*36ac495dSmrg  parser.add_option('--verbosity', action='store', dest='verbosity',
522*36ac495dSmrg                    type='int', default=0, help='Verbosity level (default = 0)')
523*36ac495dSmrg  global _OPTIONS
524*36ac495dSmrg  (_OPTIONS, _) = parser.parse_args(argv[1:])
525*36ac495dSmrg
526*36ac495dSmrg  if _OPTIONS.produce_manifest:
527*36ac495dSmrg    retval = ProduceManifest()
528*36ac495dSmrg  elif _OPTIONS.clean_build:
529*36ac495dSmrg    retval = CompareBuilds()
530*36ac495dSmrg  else:
531*36ac495dSmrg    retval = CheckExpectedResults()
532*36ac495dSmrg
533*36ac495dSmrg  if retval:
534*36ac495dSmrg    return 0
535*36ac495dSmrg  else:
536*36ac495dSmrg    return 1
537*36ac495dSmrg
538*36ac495dSmrg
539*36ac495dSmrgif __name__ == '__main__':
540*36ac495dSmrg  retval = Main(sys.argv)
541*36ac495dSmrg  sys.exit(retval)
542