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