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