1 1.1 christos #!/usr/bin/python 2 1.1 christos # 3 1.1.1.4 christos # Copyright (C) 2014-2025 Free Software Foundation, Inc. 4 1.1 christos # 5 1.1 christos # This script is free software; you can redistribute it and/or modify 6 1.1 christos # it under the terms of the GNU General Public License as published by 7 1.1 christos # the Free Software Foundation; either version 3, or (at your option) 8 1.1 christos # any later version. 9 1.1 christos 10 1.1 christos import sys 11 1.1 christos import getopt 12 1.1 christos import re 13 1.1 christos import io 14 1.1 christos from datetime import datetime 15 1.1 christos from operator import attrgetter 16 1.1 christos 17 1.1 christos # True if unrecognised lines should cause a fatal error. Might want to turn 18 1.1 christos # this on by default later. 19 1.1 christos strict = False 20 1.1 christos 21 1.1 christos # True if the order of .log segments should match the .sum file, false if 22 1.1 christos # they should keep the original order. 23 1.1 christos sort_logs = True 24 1.1 christos 25 1.1 christos # A version of open() that is safe against whatever binary output 26 1.1 christos # might be added to the log. 27 1.1 christos def safe_open (filename): 28 1.1 christos if sys.version_info >= (3, 0): 29 1.1 christos return open (filename, 'r', errors = 'surrogateescape') 30 1.1 christos return open (filename, 'r') 31 1.1 christos 32 1.1 christos # Force stdout to handle escape sequences from a safe_open file. 33 1.1 christos if sys.version_info >= (3, 0): 34 1.1 christos sys.stdout = io.TextIOWrapper (sys.stdout.buffer, 35 1.1 christos errors = 'surrogateescape') 36 1.1 christos 37 1.1 christos class Named: 38 1.1 christos def __init__ (self, name): 39 1.1 christos self.name = name 40 1.1 christos 41 1.1 christos class ToolRun (Named): 42 1.1 christos def __init__ (self, name): 43 1.1 christos Named.__init__ (self, name) 44 1.1 christos # The variations run for this tool, mapped by --target_board name. 45 1.1 christos self.variations = dict() 46 1.1 christos 47 1.1 christos # Return the VariationRun for variation NAME. 48 1.1 christos def get_variation (self, name): 49 1.1 christos if name not in self.variations: 50 1.1 christos self.variations[name] = VariationRun (name) 51 1.1 christos return self.variations[name] 52 1.1 christos 53 1.1 christos class VariationRun (Named): 54 1.1 christos def __init__ (self, name): 55 1.1 christos Named.__init__ (self, name) 56 1.1 christos # A segment of text before the harness runs start, describing which 57 1.1 christos # baseboard files were loaded for the target. 58 1.1 christos self.header = None 59 1.1 christos # The harnesses run for this variation, mapped by filename. 60 1.1 christos self.harnesses = dict() 61 1.1 christos # A list giving the number of times each type of result has 62 1.1 christos # been seen. 63 1.1 christos self.counts = [] 64 1.1 christos 65 1.1 christos # Return the HarnessRun for harness NAME. 66 1.1 christos def get_harness (self, name): 67 1.1 christos if name not in self.harnesses: 68 1.1 christos self.harnesses[name] = HarnessRun (name) 69 1.1 christos return self.harnesses[name] 70 1.1 christos 71 1.1 christos class HarnessRun (Named): 72 1.1 christos def __init__ (self, name): 73 1.1 christos Named.__init__ (self, name) 74 1.1 christos # Segments of text that make up the harness run, mapped by a test-based 75 1.1 christos # key that can be used to order them. 76 1.1 christos self.segments = dict() 77 1.1 christos # Segments of text that make up the harness run but which have 78 1.1 christos # no recognized test results. These are typically harnesses that 79 1.1 christos # are completely skipped for the target. 80 1.1 christos self.empty = [] 81 1.1 christos # A list of results. Each entry is a pair in which the first element 82 1.1 christos # is a unique sorting key and in which the second is the full 83 1.1 christos # PASS/FAIL line. 84 1.1 christos self.results = [] 85 1.1 christos 86 1.1 christos # Add a segment of text to the harness run. If the segment includes 87 1.1 christos # test results, KEY is an example of one of them, and can be used to 88 1.1 christos # combine the individual segments in order. If the segment has no 89 1.1 christos # test results (e.g. because the harness doesn't do anything for the 90 1.1 christos # current configuration) then KEY is None instead. In that case 91 1.1 christos # just collect the segments in the order that we see them. 92 1.1 christos def add_segment (self, key, segment): 93 1.1 christos if key: 94 1.1 christos assert key not in self.segments 95 1.1 christos self.segments[key] = segment 96 1.1 christos else: 97 1.1 christos self.empty.append (segment) 98 1.1 christos 99 1.1 christos class Segment: 100 1.1 christos def __init__ (self, filename, start): 101 1.1 christos self.filename = filename 102 1.1 christos self.start = start 103 1.1 christos self.lines = 0 104 1.1 christos 105 1.1 christos class Prog: 106 1.1 christos def __init__ (self): 107 1.1 christos # The variations specified on the command line. 108 1.1 christos self.variations = [] 109 1.1 christos # The variations seen in the input files. 110 1.1 christos self.known_variations = set() 111 1.1 christos # The tools specified on the command line. 112 1.1 christos self.tools = [] 113 1.1 christos # Whether to create .sum rather than .log output. 114 1.1 christos self.do_sum = True 115 1.1 christos # Regexps used while parsing. 116 1.1.1.3 christos self.test_run_re = re.compile (r'^Test run by (\S+) on (.*)$', 117 1.1.1.3 christos re.IGNORECASE) 118 1.1 christos self.tool_re = re.compile (r'^\t\t=== (.*) tests ===$') 119 1.1 christos self.result_re = re.compile (r'^(PASS|XPASS|FAIL|XFAIL|UNRESOLVED' 120 1.1 christos r'|WARNING|ERROR|UNSUPPORTED|UNTESTED' 121 1.1.1.2 christos r'|KFAIL|KPASS|PATH|DUPLICATE):\s*(.+)') 122 1.1 christos self.completed_re = re.compile (r'.* completed at (.*)') 123 1.1 christos # Pieces of text to write at the head of the output. 124 1.1 christos # start_line is a pair in which the first element is a datetime 125 1.1 christos # and in which the second is the associated 'Test Run By' line. 126 1.1 christos self.start_line = None 127 1.1 christos self.native_line = '' 128 1.1 christos self.target_line = '' 129 1.1 christos self.host_line = '' 130 1.1 christos self.acats_premable = '' 131 1.1 christos # Pieces of text to write at the end of the output. 132 1.1 christos # end_line is like start_line but for the 'runtest completed' line. 133 1.1 christos self.acats_failures = [] 134 1.1 christos self.version_output = '' 135 1.1 christos self.end_line = None 136 1.1 christos # Known summary types. 137 1.1 christos self.count_names = [ 138 1.1 christos '# of DejaGnu errors\t\t', 139 1.1 christos '# of expected passes\t\t', 140 1.1 christos '# of unexpected failures\t', 141 1.1 christos '# of unexpected successes\t', 142 1.1 christos '# of expected failures\t\t', 143 1.1 christos '# of unknown successes\t\t', 144 1.1 christos '# of known failures\t\t', 145 1.1 christos '# of untested testcases\t\t', 146 1.1 christos '# of unresolved testcases\t', 147 1.1.1.2 christos '# of unsupported tests\t\t', 148 1.1.1.2 christos '# of paths in test names\t', 149 1.1.1.4 christos '# of duplicate test names\t', 150 1.1.1.4 christos '# of unexpected core files\t' 151 1.1 christos ] 152 1.1 christos self.runs = dict() 153 1.1 christos 154 1.1 christos def usage (self): 155 1.1 christos name = sys.argv[0] 156 1.1 christos sys.stderr.write ('Usage: ' + name 157 1.1 christos + ''' [-t tool] [-l variant-list] [-L] log-or-sum-file ... 158 1.1 christos 159 1.1 christos tool The tool (e.g. g++, libffi) for which to create a 160 1.1 christos new test summary file. If not specified then output 161 1.1 christos is created for all tools. 162 1.1 christos variant-list One or more test variant names. If the list is 163 1.1 christos not specified then one is constructed from all 164 1.1 christos variants in the files for <tool>. 165 1.1 christos sum-file A test summary file with the format of those 166 1.1 christos created by runtest from DejaGnu. 167 1.1 christos If -L is used, merge *.log files instead of *.sum. In this 168 1.1 christos mode the exact order of lines may not be preserved, just different 169 1.1 christos Running *.exp chunks should be in correct order. 170 1.1 christos ''') 171 1.1 christos sys.exit (1) 172 1.1 christos 173 1.1 christos def fatal (self, what, string): 174 1.1 christos if not what: 175 1.1 christos what = sys.argv[0] 176 1.1 christos sys.stderr.write (what + ': ' + string + '\n') 177 1.1 christos sys.exit (1) 178 1.1 christos 179 1.1 christos # Parse the command-line arguments. 180 1.1 christos def parse_cmdline (self): 181 1.1 christos try: 182 1.1 christos (options, self.files) = getopt.getopt (sys.argv[1:], 'l:t:L') 183 1.1 christos if len (self.files) == 0: 184 1.1 christos self.usage() 185 1.1 christos for (option, value) in options: 186 1.1 christos if option == '-l': 187 1.1 christos self.variations.append (value) 188 1.1 christos elif option == '-t': 189 1.1 christos self.tools.append (value) 190 1.1 christos else: 191 1.1 christos self.do_sum = False 192 1.1 christos except getopt.GetoptError as e: 193 1.1 christos self.fatal (None, e.msg) 194 1.1 christos 195 1.1 christos # Try to parse time string TIME, returning an arbitrary time on failure. 196 1.1 christos # Getting this right is just a nice-to-have so failures should be silent. 197 1.1 christos def parse_time (self, time): 198 1.1 christos try: 199 1.1 christos return datetime.strptime (time, '%c') 200 1.1 christos except ValueError: 201 1.1 christos return datetime.now() 202 1.1 christos 203 1.1 christos # Parse an integer and abort on failure. 204 1.1 christos def parse_int (self, filename, value): 205 1.1 christos try: 206 1.1 christos return int (value) 207 1.1 christos except ValueError: 208 1.1 christos self.fatal (filename, 'expected an integer, got: ' + value) 209 1.1 christos 210 1.1 christos # Return a list that represents no test results. 211 1.1 christos def zero_counts (self): 212 1.1 christos return [0 for x in self.count_names] 213 1.1 christos 214 1.1 christos # Return the ToolRun for tool NAME. 215 1.1 christos def get_tool (self, name): 216 1.1 christos if name not in self.runs: 217 1.1 christos self.runs[name] = ToolRun (name) 218 1.1 christos return self.runs[name] 219 1.1 christos 220 1.1 christos # Add the result counts in list FROMC to TOC. 221 1.1 christos def accumulate_counts (self, toc, fromc): 222 1.1 christos for i in range (len (self.count_names)): 223 1.1 christos toc[i] += fromc[i] 224 1.1 christos 225 1.1 christos # Parse the list of variations after 'Schedule of variations:'. 226 1.1 christos # Return the number seen. 227 1.1 christos def parse_variations (self, filename, file): 228 1.1 christos num_variations = 0 229 1.1 christos while True: 230 1.1 christos line = file.readline() 231 1.1 christos if line == '': 232 1.1 christos self.fatal (filename, 'could not parse variation list') 233 1.1 christos if line == '\n': 234 1.1 christos break 235 1.1 christos self.known_variations.add (line.strip()) 236 1.1 christos num_variations += 1 237 1.1 christos return num_variations 238 1.1 christos 239 1.1 christos # Parse from the first line after 'Running target ...' to the end 240 1.1 christos # of the run's summary. 241 1.1 christos def parse_run (self, filename, file, tool, variation, num_variations): 242 1.1 christos header = None 243 1.1 christos harness = None 244 1.1 christos segment = None 245 1.1 christos final_using = 0 246 1.1.1.2 christos has_warning = 0 247 1.1 christos 248 1.1 christos # If this is the first run for this variation, add any text before 249 1.1 christos # the first harness to the header. 250 1.1 christos if not variation.header: 251 1.1 christos segment = Segment (filename, file.tell()) 252 1.1 christos variation.header = segment 253 1.1 christos 254 1.1 christos # Parse the rest of the summary (the '# of ' lines). 255 1.1 christos if len (variation.counts) == 0: 256 1.1 christos variation.counts = self.zero_counts() 257 1.1 christos 258 1.1 christos # Parse up until the first line of the summary. 259 1.1 christos if num_variations == 1: 260 1.1 christos end = '\t\t=== ' + tool.name + ' Summary ===\n' 261 1.1 christos else: 262 1.1 christos end = ('\t\t=== ' + tool.name + ' Summary for ' 263 1.1 christos + variation.name + ' ===\n') 264 1.1 christos while True: 265 1.1 christos line = file.readline() 266 1.1 christos if line == '': 267 1.1 christos self.fatal (filename, 'no recognised summary line') 268 1.1 christos if line == end: 269 1.1 christos break 270 1.1 christos 271 1.1 christos # Look for the start of a new harness. 272 1.1 christos if line.startswith ('Running ') and line.endswith (' ...\n'): 273 1.1 christos # Close off the current harness segment, if any. 274 1.1 christos if harness: 275 1.1 christos segment.lines -= final_using 276 1.1 christos harness.add_segment (first_key, segment) 277 1.1 christos name = line[len ('Running '):-len(' ...\n')] 278 1.1 christos harness = variation.get_harness (name) 279 1.1 christos segment = Segment (filename, file.tell()) 280 1.1 christos first_key = None 281 1.1 christos final_using = 0 282 1.1 christos continue 283 1.1 christos 284 1.1 christos # Record test results. Associate the first test result with 285 1.1 christos # the harness segment, so that if a run for a particular harness 286 1.1 christos # has been split up, we can reassemble the individual segments 287 1.1 christos # in a sensible order. 288 1.1 christos # 289 1.1 christos # dejagnu sometimes issues warnings about the testing environment 290 1.1 christos # before running any tests. Treat them as part of the header 291 1.1 christos # rather than as a test result. 292 1.1 christos match = self.result_re.match (line) 293 1.1 christos if match and (harness or not line.startswith ('WARNING:')): 294 1.1 christos if not harness: 295 1.1 christos self.fatal (filename, 'saw test result before harness name') 296 1.1 christos name = match.group (2) 297 1.1 christos # Ugly hack to get the right order for gfortran. 298 1.1 christos if name.startswith ('gfortran.dg/g77/'): 299 1.1 christos name = 'h' + name 300 1.1.1.2 christos # If we have a time out warning, make sure it appears 301 1.1.1.2 christos # before the following testcase diagnostic: we insert 302 1.1.1.2 christos # the testname before 'program' so that sort faces a 303 1.1.1.2 christos # list of testnames. 304 1.1.1.2 christos if line.startswith ('WARNING: program timed out'): 305 1.1.1.2 christos has_warning = 1 306 1.1.1.2 christos else: 307 1.1.1.2 christos if has_warning == 1: 308 1.1.1.2 christos key = (name, len (harness.results)) 309 1.1.1.2 christos myline = 'WARNING: %s program timed out.\n' % name 310 1.1.1.2 christos harness.results.append ((key, myline)) 311 1.1.1.2 christos has_warning = 0 312 1.1.1.2 christos key = (name, len (harness.results)) 313 1.1.1.2 christos harness.results.append ((key, line)) 314 1.1.1.2 christos if not first_key and sort_logs: 315 1.1.1.2 christos first_key = key 316 1.1 christos if line.startswith ('ERROR: (DejaGnu)'): 317 1.1 christos for i in range (len (self.count_names)): 318 1.1 christos if 'DejaGnu errors' in self.count_names[i]: 319 1.1 christos variation.counts[i] += 1 320 1.1 christos break 321 1.1 christos 322 1.1 christos # 'Using ...' lines are only interesting in a header. Splitting 323 1.1 christos # the test up into parallel runs leads to more 'Using ...' lines 324 1.1 christos # than there would be in a single log. 325 1.1 christos if line.startswith ('Using '): 326 1.1 christos final_using += 1 327 1.1 christos else: 328 1.1 christos final_using = 0 329 1.1 christos 330 1.1 christos # Add other text to the current segment, if any. 331 1.1 christos if segment: 332 1.1 christos segment.lines += 1 333 1.1 christos 334 1.1 christos # Close off the final harness segment, if any. 335 1.1 christos if harness: 336 1.1 christos segment.lines -= final_using 337 1.1 christos harness.add_segment (first_key, segment) 338 1.1 christos 339 1.1 christos while True: 340 1.1 christos before = file.tell() 341 1.1 christos line = file.readline() 342 1.1 christos if line == '': 343 1.1 christos break 344 1.1 christos if line == '\n': 345 1.1 christos continue 346 1.1 christos if not line.startswith ('# '): 347 1.1 christos file.seek (before) 348 1.1 christos break 349 1.1 christos found = False 350 1.1 christos for i in range (len (self.count_names)): 351 1.1 christos if line.startswith (self.count_names[i]): 352 1.1 christos count = line[len (self.count_names[i]):-1].strip() 353 1.1 christos variation.counts[i] += self.parse_int (filename, count) 354 1.1 christos found = True 355 1.1 christos break 356 1.1 christos if not found: 357 1.1 christos self.fatal (filename, 'unknown test result: ' + line[:-1]) 358 1.1 christos 359 1.1 christos # Parse an acats run, which uses a different format from dejagnu. 360 1.1 christos # We have just skipped over '=== acats configuration ==='. 361 1.1 christos def parse_acats_run (self, filename, file): 362 1.1 christos # Parse the preamble, which describes the configuration and logs 363 1.1 christos # the creation of support files. 364 1.1 christos record = (self.acats_premable == '') 365 1.1 christos if record: 366 1.1 christos self.acats_premable = '\t\t=== acats configuration ===\n' 367 1.1 christos while True: 368 1.1 christos line = file.readline() 369 1.1 christos if line == '': 370 1.1 christos self.fatal (filename, 'could not parse acats preamble') 371 1.1 christos if line == '\t\t=== acats tests ===\n': 372 1.1 christos break 373 1.1 christos if record: 374 1.1 christos self.acats_premable += line 375 1.1 christos 376 1.1 christos # Parse the test results themselves, using a dummy variation name. 377 1.1 christos tool = self.get_tool ('acats') 378 1.1 christos variation = tool.get_variation ('none') 379 1.1 christos self.parse_run (filename, file, tool, variation, 1) 380 1.1 christos 381 1.1 christos # Parse the failure list. 382 1.1 christos while True: 383 1.1 christos before = file.tell() 384 1.1 christos line = file.readline() 385 1.1 christos if line.startswith ('*** FAILURES: '): 386 1.1 christos self.acats_failures.append (line[len ('*** FAILURES: '):-1]) 387 1.1 christos continue 388 1.1 christos file.seek (before) 389 1.1 christos break 390 1.1 christos 391 1.1 christos # Parse the final summary at the end of a log in order to capture 392 1.1 christos # the version output that follows it. 393 1.1 christos def parse_final_summary (self, filename, file): 394 1.1 christos record = (self.version_output == '') 395 1.1 christos while True: 396 1.1 christos line = file.readline() 397 1.1 christos if line == '': 398 1.1 christos break 399 1.1 christos if line.startswith ('# of '): 400 1.1 christos continue 401 1.1 christos if record: 402 1.1 christos self.version_output += line 403 1.1 christos if line == '\n': 404 1.1 christos break 405 1.1 christos 406 1.1 christos # Parse a .log or .sum file. 407 1.1 christos def parse_file (self, filename, file): 408 1.1 christos tool = None 409 1.1 christos target = None 410 1.1 christos num_variations = 1 411 1.1 christos while True: 412 1.1 christos line = file.readline() 413 1.1 christos if line == '': 414 1.1 christos return 415 1.1 christos 416 1.1 christos # Parse the list of variations, which comes before the test 417 1.1 christos # runs themselves. 418 1.1 christos if line.startswith ('Schedule of variations:'): 419 1.1 christos num_variations = self.parse_variations (filename, file) 420 1.1 christos continue 421 1.1 christos 422 1.1 christos # Parse a testsuite run for one tool/variation combination. 423 1.1 christos if line.startswith ('Running target '): 424 1.1 christos name = line[len ('Running target '):-1] 425 1.1 christos if not tool: 426 1.1 christos self.fatal (filename, 'could not parse tool name') 427 1.1 christos if name not in self.known_variations: 428 1.1 christos self.fatal (filename, 'unknown target: ' + name) 429 1.1 christos self.parse_run (filename, file, tool, 430 1.1 christos tool.get_variation (name), 431 1.1 christos num_variations) 432 1.1 christos # If there is only one variation then there is no separate 433 1.1 christos # summary for it. Record any following version output. 434 1.1 christos if num_variations == 1: 435 1.1 christos self.parse_final_summary (filename, file) 436 1.1 christos continue 437 1.1 christos 438 1.1 christos # Parse the start line. In the case where several files are being 439 1.1 christos # parsed, pick the one with the earliest time. 440 1.1 christos match = self.test_run_re.match (line) 441 1.1 christos if match: 442 1.1 christos time = self.parse_time (match.group (2)) 443 1.1 christos if not self.start_line or self.start_line[0] > time: 444 1.1 christos self.start_line = (time, line) 445 1.1 christos continue 446 1.1 christos 447 1.1 christos # Parse the form used for native testing. 448 1.1 christos if line.startswith ('Native configuration is '): 449 1.1 christos self.native_line = line 450 1.1 christos continue 451 1.1 christos 452 1.1 christos # Parse the target triplet. 453 1.1 christos if line.startswith ('Target is '): 454 1.1 christos self.target_line = line 455 1.1 christos continue 456 1.1 christos 457 1.1 christos # Parse the host triplet. 458 1.1 christos if line.startswith ('Host is '): 459 1.1 christos self.host_line = line 460 1.1 christos continue 461 1.1 christos 462 1.1 christos # Parse the acats premable. 463 1.1 christos if line == '\t\t=== acats configuration ===\n': 464 1.1 christos self.parse_acats_run (filename, file) 465 1.1 christos continue 466 1.1 christos 467 1.1 christos # Parse the tool name. 468 1.1 christos match = self.tool_re.match (line) 469 1.1 christos if match: 470 1.1 christos tool = self.get_tool (match.group (1)) 471 1.1 christos continue 472 1.1 christos 473 1.1 christos # Skip over the final summary (which we instead create from 474 1.1 christos # individual runs) and parse the version output. 475 1.1 christos if tool and line == '\t\t=== ' + tool.name + ' Summary ===\n': 476 1.1 christos if file.readline() != '\n': 477 1.1 christos self.fatal (filename, 'expected blank line after summary') 478 1.1 christos self.parse_final_summary (filename, file) 479 1.1 christos continue 480 1.1 christos 481 1.1 christos # Parse the completion line. In the case where several files 482 1.1 christos # are being parsed, pick the one with the latest time. 483 1.1 christos match = self.completed_re.match (line) 484 1.1 christos if match: 485 1.1 christos time = self.parse_time (match.group (1)) 486 1.1 christos if not self.end_line or self.end_line[0] < time: 487 1.1 christos self.end_line = (time, line) 488 1.1 christos continue 489 1.1 christos 490 1.1 christos # Sanity check to make sure that important text doesn't get 491 1.1 christos # dropped accidentally. 492 1.1 christos if strict and line.strip() != '': 493 1.1 christos self.fatal (filename, 'unrecognised line: ' + line[:-1]) 494 1.1 christos 495 1.1 christos # Output a segment of text. 496 1.1 christos def output_segment (self, segment): 497 1.1 christos with safe_open (segment.filename) as file: 498 1.1 christos file.seek (segment.start) 499 1.1 christos for i in range (segment.lines): 500 1.1 christos sys.stdout.write (file.readline()) 501 1.1 christos 502 1.1 christos # Output a summary giving the number of times each type of result has 503 1.1 christos # been seen. 504 1.1 christos def output_summary (self, tool, counts): 505 1.1 christos for i in range (len (self.count_names)): 506 1.1 christos name = self.count_names[i] 507 1.1 christos # dejagnu only prints result types that were seen at least once, 508 1.1 christos # but acats always prints a number of unexpected failures. 509 1.1 christos if (counts[i] > 0 510 1.1 christos or (tool.name == 'acats' 511 1.1 christos and name.startswith ('# of unexpected failures'))): 512 1.1 christos sys.stdout.write ('%s%d\n' % (name, counts[i])) 513 1.1 christos 514 1.1 christos # Output unified .log or .sum information for a particular variation, 515 1.1 christos # with a summary at the end. 516 1.1 christos def output_variation (self, tool, variation): 517 1.1 christos self.output_segment (variation.header) 518 1.1 christos for harness in sorted (variation.harnesses.values(), 519 1.1 christos key = attrgetter ('name')): 520 1.1 christos sys.stdout.write ('Running ' + harness.name + ' ...\n') 521 1.1 christos if self.do_sum: 522 1.1 christos harness.results.sort() 523 1.1 christos for (key, line) in harness.results: 524 1.1 christos sys.stdout.write (line) 525 1.1 christos else: 526 1.1 christos # Rearrange the log segments into test order (but without 527 1.1 christos # rearranging text within those segments). 528 1.1 christos for key in sorted (harness.segments.keys()): 529 1.1 christos self.output_segment (harness.segments[key]) 530 1.1 christos for segment in harness.empty: 531 1.1 christos self.output_segment (segment) 532 1.1 christos if len (self.variations) > 1: 533 1.1 christos sys.stdout.write ('\t\t=== ' + tool.name + ' Summary for ' 534 1.1 christos + variation.name + ' ===\n\n') 535 1.1 christos self.output_summary (tool, variation.counts) 536 1.1 christos 537 1.1 christos # Output unified .log or .sum information for a particular tool, 538 1.1 christos # with a summary at the end. 539 1.1 christos def output_tool (self, tool): 540 1.1 christos counts = self.zero_counts() 541 1.1 christos if tool.name == 'acats': 542 1.1 christos # acats doesn't use variations, so just output everything. 543 1.1 christos # It also has a different approach to whitespace. 544 1.1 christos sys.stdout.write ('\t\t=== ' + tool.name + ' tests ===\n') 545 1.1 christos for variation in tool.variations.values(): 546 1.1 christos self.output_variation (tool, variation) 547 1.1 christos self.accumulate_counts (counts, variation.counts) 548 1.1 christos sys.stdout.write ('\t\t=== ' + tool.name + ' Summary ===\n') 549 1.1 christos else: 550 1.1 christos # Output the results in the usual dejagnu runtest format. 551 1.1 christos sys.stdout.write ('\n\t\t=== ' + tool.name + ' tests ===\n\n' 552 1.1 christos 'Schedule of variations:\n') 553 1.1 christos for name in self.variations: 554 1.1 christos if name in tool.variations: 555 1.1 christos sys.stdout.write (' ' + name + '\n') 556 1.1 christos sys.stdout.write ('\n') 557 1.1 christos for name in self.variations: 558 1.1 christos if name in tool.variations: 559 1.1 christos variation = tool.variations[name] 560 1.1 christos sys.stdout.write ('Running target ' 561 1.1 christos + variation.name + '\n') 562 1.1 christos self.output_variation (tool, variation) 563 1.1 christos self.accumulate_counts (counts, variation.counts) 564 1.1 christos sys.stdout.write ('\n\t\t=== ' + tool.name + ' Summary ===\n\n') 565 1.1 christos self.output_summary (tool, counts) 566 1.1 christos 567 1.1 christos def main (self): 568 1.1 christos self.parse_cmdline() 569 1.1 christos try: 570 1.1 christos # Parse the input files. 571 1.1 christos for filename in self.files: 572 1.1 christos with safe_open (filename) as file: 573 1.1 christos self.parse_file (filename, file) 574 1.1 christos 575 1.1 christos # Decide what to output. 576 1.1 christos if len (self.variations) == 0: 577 1.1 christos self.variations = sorted (self.known_variations) 578 1.1 christos else: 579 1.1 christos for name in self.variations: 580 1.1 christos if name not in self.known_variations: 581 1.1 christos self.fatal (None, 'no results for ' + name) 582 1.1 christos if len (self.tools) == 0: 583 1.1 christos self.tools = sorted (self.runs.keys()) 584 1.1 christos 585 1.1 christos # Output the header. 586 1.1 christos if self.start_line: 587 1.1 christos sys.stdout.write (self.start_line[1]) 588 1.1 christos sys.stdout.write (self.native_line) 589 1.1 christos sys.stdout.write (self.target_line) 590 1.1 christos sys.stdout.write (self.host_line) 591 1.1 christos sys.stdout.write (self.acats_premable) 592 1.1 christos 593 1.1 christos # Output the main body. 594 1.1 christos for name in self.tools: 595 1.1 christos if name not in self.runs: 596 1.1 christos self.fatal (None, 'no results for ' + name) 597 1.1 christos self.output_tool (self.runs[name]) 598 1.1 christos 599 1.1 christos # Output the footer. 600 1.1 christos if len (self.acats_failures) > 0: 601 1.1 christos sys.stdout.write ('*** FAILURES: ' 602 1.1 christos + ' '.join (self.acats_failures) + '\n') 603 1.1 christos sys.stdout.write (self.version_output) 604 1.1 christos if self.end_line: 605 1.1 christos sys.stdout.write (self.end_line[1]) 606 1.1 christos except IOError as e: 607 1.1 christos self.fatal (e.filename, e.strerror) 608 1.1 christos 609 1.1 christos Prog().main() 610