dg-extract-results.py revision 1.1.1.3 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.1.2 mrg '# of DejaGnu errors\t\t',
138 1.1 mrg '# of expected passes\t\t',
139 1.1 mrg '# of unexpected failures\t',
140 1.1 mrg '# of unexpected successes\t',
141 1.1 mrg '# of expected failures\t\t',
142 1.1 mrg '# of unknown successes\t\t',
143 1.1 mrg '# of known failures\t\t',
144 1.1 mrg '# of untested testcases\t\t',
145 1.1 mrg '# of unresolved testcases\t',
146 1.1 mrg '# of unsupported tests\t\t'
147 1.1 mrg ]
148 1.1 mrg self.runs = dict()
149 1.1 mrg
150 1.1 mrg def usage (self):
151 1.1 mrg name = sys.argv[0]
152 1.1 mrg sys.stderr.write ('Usage: ' + name
153 1.1 mrg + ''' [-t tool] [-l variant-list] [-L] log-or-sum-file ...
154 1.1 mrg
155 1.1 mrg tool The tool (e.g. g++, libffi) for which to create a
156 1.1 mrg new test summary file. If not specified then output
157 1.1 mrg is created for all tools.
158 1.1 mrg variant-list One or more test variant names. If the list is
159 1.1 mrg not specified then one is constructed from all
160 1.1 mrg variants in the files for <tool>.
161 1.1 mrg sum-file A test summary file with the format of those
162 1.1 mrg created by runtest from DejaGnu.
163 1.1 mrg If -L is used, merge *.log files instead of *.sum. In this
164 1.1 mrg mode the exact order of lines may not be preserved, just different
165 1.1 mrg Running *.exp chunks should be in correct order.
166 1.1 mrg ''')
167 1.1 mrg sys.exit (1)
168 1.1 mrg
169 1.1 mrg def fatal (self, what, string):
170 1.1 mrg if not what:
171 1.1 mrg what = sys.argv[0]
172 1.1 mrg sys.stderr.write (what + ': ' + string + '\n')
173 1.1 mrg sys.exit (1)
174 1.1 mrg
175 1.1 mrg # Parse the command-line arguments.
176 1.1 mrg def parse_cmdline (self):
177 1.1 mrg try:
178 1.1 mrg (options, self.files) = getopt.getopt (sys.argv[1:], 'l:t:L')
179 1.1 mrg if len (self.files) == 0:
180 1.1 mrg self.usage()
181 1.1 mrg for (option, value) in options:
182 1.1 mrg if option == '-l':
183 1.1 mrg self.variations.append (value)
184 1.1 mrg elif option == '-t':
185 1.1 mrg self.tools.append (value)
186 1.1 mrg else:
187 1.1 mrg self.do_sum = False
188 1.1 mrg except getopt.GetoptError as e:
189 1.1 mrg self.fatal (None, e.msg)
190 1.1 mrg
191 1.1 mrg # Try to parse time string TIME, returning an arbitrary time on failure.
192 1.1 mrg # Getting this right is just a nice-to-have so failures should be silent.
193 1.1 mrg def parse_time (self, time):
194 1.1 mrg try:
195 1.1 mrg return datetime.strptime (time, '%c')
196 1.1 mrg except ValueError:
197 1.1 mrg return datetime.now()
198 1.1 mrg
199 1.1 mrg # Parse an integer and abort on failure.
200 1.1 mrg def parse_int (self, filename, value):
201 1.1 mrg try:
202 1.1 mrg return int (value)
203 1.1 mrg except ValueError:
204 1.1 mrg self.fatal (filename, 'expected an integer, got: ' + value)
205 1.1 mrg
206 1.1 mrg # Return a list that represents no test results.
207 1.1 mrg def zero_counts (self):
208 1.1 mrg return [0 for x in self.count_names]
209 1.1 mrg
210 1.1 mrg # Return the ToolRun for tool NAME.
211 1.1 mrg def get_tool (self, name):
212 1.1 mrg if name not in self.runs:
213 1.1 mrg self.runs[name] = ToolRun (name)
214 1.1 mrg return self.runs[name]
215 1.1 mrg
216 1.1 mrg # Add the result counts in list FROMC to TOC.
217 1.1 mrg def accumulate_counts (self, toc, fromc):
218 1.1 mrg for i in range (len (self.count_names)):
219 1.1 mrg toc[i] += fromc[i]
220 1.1 mrg
221 1.1 mrg # Parse the list of variations after 'Schedule of variations:'.
222 1.1 mrg # Return the number seen.
223 1.1 mrg def parse_variations (self, filename, file):
224 1.1 mrg num_variations = 0
225 1.1 mrg while True:
226 1.1 mrg line = file.readline()
227 1.1 mrg if line == '':
228 1.1 mrg self.fatal (filename, 'could not parse variation list')
229 1.1 mrg if line == '\n':
230 1.1 mrg break
231 1.1 mrg self.known_variations.add (line.strip())
232 1.1 mrg num_variations += 1
233 1.1 mrg return num_variations
234 1.1 mrg
235 1.1 mrg # Parse from the first line after 'Running target ...' to the end
236 1.1 mrg # of the run's summary.
237 1.1 mrg def parse_run (self, filename, file, tool, variation, num_variations):
238 1.1 mrg header = None
239 1.1 mrg harness = None
240 1.1 mrg segment = None
241 1.1 mrg final_using = 0
242 1.1.1.3 mrg has_warning = 0
243 1.1 mrg
244 1.1 mrg # If this is the first run for this variation, add any text before
245 1.1 mrg # the first harness to the header.
246 1.1 mrg if not variation.header:
247 1.1 mrg segment = Segment (filename, file.tell())
248 1.1 mrg variation.header = segment
249 1.1 mrg
250 1.1.1.2 mrg # Parse the rest of the summary (the '# of ' lines).
251 1.1.1.2 mrg if len (variation.counts) == 0:
252 1.1.1.2 mrg variation.counts = self.zero_counts()
253 1.1.1.2 mrg
254 1.1 mrg # Parse up until the first line of the summary.
255 1.1 mrg if num_variations == 1:
256 1.1 mrg end = '\t\t=== ' + tool.name + ' Summary ===\n'
257 1.1 mrg else:
258 1.1 mrg end = ('\t\t=== ' + tool.name + ' Summary for '
259 1.1 mrg + variation.name + ' ===\n')
260 1.1 mrg while True:
261 1.1 mrg line = file.readline()
262 1.1 mrg if line == '':
263 1.1 mrg self.fatal (filename, 'no recognised summary line')
264 1.1 mrg if line == end:
265 1.1 mrg break
266 1.1 mrg
267 1.1 mrg # Look for the start of a new harness.
268 1.1 mrg if line.startswith ('Running ') and line.endswith (' ...\n'):
269 1.1 mrg # Close off the current harness segment, if any.
270 1.1 mrg if harness:
271 1.1 mrg segment.lines -= final_using
272 1.1 mrg harness.add_segment (first_key, segment)
273 1.1 mrg name = line[len ('Running '):-len(' ...\n')]
274 1.1 mrg harness = variation.get_harness (name)
275 1.1 mrg segment = Segment (filename, file.tell())
276 1.1 mrg first_key = None
277 1.1 mrg final_using = 0
278 1.1 mrg continue
279 1.1 mrg
280 1.1 mrg # Record test results. Associate the first test result with
281 1.1 mrg # the harness segment, so that if a run for a particular harness
282 1.1 mrg # has been split up, we can reassemble the individual segments
283 1.1 mrg # in a sensible order.
284 1.1 mrg #
285 1.1 mrg # dejagnu sometimes issues warnings about the testing environment
286 1.1 mrg # before running any tests. Treat them as part of the header
287 1.1 mrg # rather than as a test result.
288 1.1 mrg match = self.result_re.match (line)
289 1.1 mrg if match and (harness or not line.startswith ('WARNING:')):
290 1.1 mrg if not harness:
291 1.1 mrg self.fatal (filename, 'saw test result before harness name')
292 1.1 mrg name = match.group (2)
293 1.1 mrg # Ugly hack to get the right order for gfortran.
294 1.1 mrg if name.startswith ('gfortran.dg/g77/'):
295 1.1 mrg name = 'h' + name
296 1.1.1.3 mrg # If we have a time out warning, make sure it appears
297 1.1.1.3 mrg # before the following testcase diagnostic: we insert
298 1.1.1.3 mrg # the testname before 'program' so that sort faces a
299 1.1.1.3 mrg # list of testnames.
300 1.1.1.3 mrg if line.startswith ('WARNING: program timed out'):
301 1.1.1.3 mrg has_warning = 1
302 1.1.1.3 mrg else:
303 1.1.1.3 mrg if has_warning == 1:
304 1.1.1.3 mrg key = (name, len (harness.results))
305 1.1.1.3 mrg myline = 'WARNING: %s program timed out.\n' % name
306 1.1.1.3 mrg harness.results.append ((key, myline))
307 1.1.1.3 mrg has_warning = 0
308 1.1.1.3 mrg key = (name, len (harness.results))
309 1.1.1.3 mrg harness.results.append ((key, line))
310 1.1.1.3 mrg if not first_key and sort_logs:
311 1.1.1.3 mrg first_key = key
312 1.1.1.2 mrg if line.startswith ('ERROR: (DejaGnu)'):
313 1.1.1.2 mrg for i in range (len (self.count_names)):
314 1.1.1.2 mrg if 'DejaGnu errors' in self.count_names[i]:
315 1.1.1.2 mrg variation.counts[i] += 1
316 1.1.1.2 mrg break
317 1.1 mrg
318 1.1 mrg # 'Using ...' lines are only interesting in a header. Splitting
319 1.1 mrg # the test up into parallel runs leads to more 'Using ...' lines
320 1.1 mrg # than there would be in a single log.
321 1.1 mrg if line.startswith ('Using '):
322 1.1 mrg final_using += 1
323 1.1 mrg else:
324 1.1 mrg final_using = 0
325 1.1 mrg
326 1.1 mrg # Add other text to the current segment, if any.
327 1.1 mrg if segment:
328 1.1 mrg segment.lines += 1
329 1.1 mrg
330 1.1 mrg # Close off the final harness segment, if any.
331 1.1 mrg if harness:
332 1.1 mrg segment.lines -= final_using
333 1.1 mrg harness.add_segment (first_key, segment)
334 1.1 mrg
335 1.1 mrg while True:
336 1.1 mrg before = file.tell()
337 1.1 mrg line = file.readline()
338 1.1 mrg if line == '':
339 1.1 mrg break
340 1.1 mrg if line == '\n':
341 1.1 mrg continue
342 1.1 mrg if not line.startswith ('# '):
343 1.1 mrg file.seek (before)
344 1.1 mrg break
345 1.1 mrg found = False
346 1.1 mrg for i in range (len (self.count_names)):
347 1.1 mrg if line.startswith (self.count_names[i]):
348 1.1 mrg count = line[len (self.count_names[i]):-1].strip()
349 1.1 mrg variation.counts[i] += self.parse_int (filename, count)
350 1.1 mrg found = True
351 1.1 mrg break
352 1.1 mrg if not found:
353 1.1 mrg self.fatal (filename, 'unknown test result: ' + line[:-1])
354 1.1 mrg
355 1.1 mrg # Parse an acats run, which uses a different format from dejagnu.
356 1.1 mrg # We have just skipped over '=== acats configuration ==='.
357 1.1 mrg def parse_acats_run (self, filename, file):
358 1.1 mrg # Parse the preamble, which describes the configuration and logs
359 1.1 mrg # the creation of support files.
360 1.1 mrg record = (self.acats_premable == '')
361 1.1 mrg if record:
362 1.1 mrg self.acats_premable = '\t\t=== acats configuration ===\n'
363 1.1 mrg while True:
364 1.1 mrg line = file.readline()
365 1.1 mrg if line == '':
366 1.1 mrg self.fatal (filename, 'could not parse acats preamble')
367 1.1 mrg if line == '\t\t=== acats tests ===\n':
368 1.1 mrg break
369 1.1 mrg if record:
370 1.1 mrg self.acats_premable += line
371 1.1 mrg
372 1.1 mrg # Parse the test results themselves, using a dummy variation name.
373 1.1 mrg tool = self.get_tool ('acats')
374 1.1 mrg variation = tool.get_variation ('none')
375 1.1 mrg self.parse_run (filename, file, tool, variation, 1)
376 1.1 mrg
377 1.1 mrg # Parse the failure list.
378 1.1 mrg while True:
379 1.1 mrg before = file.tell()
380 1.1 mrg line = file.readline()
381 1.1 mrg if line.startswith ('*** FAILURES: '):
382 1.1 mrg self.acats_failures.append (line[len ('*** FAILURES: '):-1])
383 1.1 mrg continue
384 1.1 mrg file.seek (before)
385 1.1 mrg break
386 1.1 mrg
387 1.1 mrg # Parse the final summary at the end of a log in order to capture
388 1.1 mrg # the version output that follows it.
389 1.1 mrg def parse_final_summary (self, filename, file):
390 1.1 mrg record = (self.version_output == '')
391 1.1 mrg while True:
392 1.1 mrg line = file.readline()
393 1.1 mrg if line == '':
394 1.1 mrg break
395 1.1 mrg if line.startswith ('# of '):
396 1.1 mrg continue
397 1.1 mrg if record:
398 1.1 mrg self.version_output += line
399 1.1 mrg if line == '\n':
400 1.1 mrg break
401 1.1 mrg
402 1.1 mrg # Parse a .log or .sum file.
403 1.1 mrg def parse_file (self, filename, file):
404 1.1 mrg tool = None
405 1.1 mrg target = None
406 1.1 mrg num_variations = 1
407 1.1 mrg while True:
408 1.1 mrg line = file.readline()
409 1.1 mrg if line == '':
410 1.1 mrg return
411 1.1 mrg
412 1.1 mrg # Parse the list of variations, which comes before the test
413 1.1 mrg # runs themselves.
414 1.1 mrg if line.startswith ('Schedule of variations:'):
415 1.1 mrg num_variations = self.parse_variations (filename, file)
416 1.1 mrg continue
417 1.1 mrg
418 1.1 mrg # Parse a testsuite run for one tool/variation combination.
419 1.1 mrg if line.startswith ('Running target '):
420 1.1 mrg name = line[len ('Running target '):-1]
421 1.1 mrg if not tool:
422 1.1 mrg self.fatal (filename, 'could not parse tool name')
423 1.1 mrg if name not in self.known_variations:
424 1.1 mrg self.fatal (filename, 'unknown target: ' + name)
425 1.1 mrg self.parse_run (filename, file, tool,
426 1.1 mrg tool.get_variation (name),
427 1.1 mrg num_variations)
428 1.1 mrg # If there is only one variation then there is no separate
429 1.1 mrg # summary for it. Record any following version output.
430 1.1 mrg if num_variations == 1:
431 1.1 mrg self.parse_final_summary (filename, file)
432 1.1 mrg continue
433 1.1 mrg
434 1.1 mrg # Parse the start line. In the case where several files are being
435 1.1 mrg # parsed, pick the one with the earliest time.
436 1.1 mrg match = self.test_run_re.match (line)
437 1.1 mrg if match:
438 1.1 mrg time = self.parse_time (match.group (2))
439 1.1 mrg if not self.start_line or self.start_line[0] > time:
440 1.1 mrg self.start_line = (time, line)
441 1.1 mrg continue
442 1.1 mrg
443 1.1 mrg # Parse the form used for native testing.
444 1.1 mrg if line.startswith ('Native configuration is '):
445 1.1 mrg self.native_line = line
446 1.1 mrg continue
447 1.1 mrg
448 1.1 mrg # Parse the target triplet.
449 1.1 mrg if line.startswith ('Target is '):
450 1.1 mrg self.target_line = line
451 1.1 mrg continue
452 1.1 mrg
453 1.1 mrg # Parse the host triplet.
454 1.1 mrg if line.startswith ('Host is '):
455 1.1 mrg self.host_line = line
456 1.1 mrg continue
457 1.1 mrg
458 1.1 mrg # Parse the acats premable.
459 1.1 mrg if line == '\t\t=== acats configuration ===\n':
460 1.1 mrg self.parse_acats_run (filename, file)
461 1.1 mrg continue
462 1.1 mrg
463 1.1 mrg # Parse the tool name.
464 1.1 mrg match = self.tool_re.match (line)
465 1.1 mrg if match:
466 1.1 mrg tool = self.get_tool (match.group (1))
467 1.1 mrg continue
468 1.1 mrg
469 1.1 mrg # Skip over the final summary (which we instead create from
470 1.1 mrg # individual runs) and parse the version output.
471 1.1 mrg if tool and line == '\t\t=== ' + tool.name + ' Summary ===\n':
472 1.1 mrg if file.readline() != '\n':
473 1.1 mrg self.fatal (filename, 'expected blank line after summary')
474 1.1 mrg self.parse_final_summary (filename, file)
475 1.1 mrg continue
476 1.1 mrg
477 1.1 mrg # Parse the completion line. In the case where several files
478 1.1 mrg # are being parsed, pick the one with the latest time.
479 1.1 mrg match = self.completed_re.match (line)
480 1.1 mrg if match:
481 1.1 mrg time = self.parse_time (match.group (1))
482 1.1 mrg if not self.end_line or self.end_line[0] < time:
483 1.1 mrg self.end_line = (time, line)
484 1.1 mrg continue
485 1.1 mrg
486 1.1 mrg # Sanity check to make sure that important text doesn't get
487 1.1 mrg # dropped accidentally.
488 1.1 mrg if strict and line.strip() != '':
489 1.1 mrg self.fatal (filename, 'unrecognised line: ' + line[:-1])
490 1.1 mrg
491 1.1 mrg # Output a segment of text.
492 1.1 mrg def output_segment (self, segment):
493 1.1 mrg with safe_open (segment.filename) as file:
494 1.1 mrg file.seek (segment.start)
495 1.1 mrg for i in range (segment.lines):
496 1.1 mrg sys.stdout.write (file.readline())
497 1.1 mrg
498 1.1 mrg # Output a summary giving the number of times each type of result has
499 1.1 mrg # been seen.
500 1.1 mrg def output_summary (self, tool, counts):
501 1.1 mrg for i in range (len (self.count_names)):
502 1.1 mrg name = self.count_names[i]
503 1.1 mrg # dejagnu only prints result types that were seen at least once,
504 1.1 mrg # but acats always prints a number of unexpected failures.
505 1.1 mrg if (counts[i] > 0
506 1.1 mrg or (tool.name == 'acats'
507 1.1 mrg and name.startswith ('# of unexpected failures'))):
508 1.1 mrg sys.stdout.write ('%s%d\n' % (name, counts[i]))
509 1.1 mrg
510 1.1 mrg # Output unified .log or .sum information for a particular variation,
511 1.1 mrg # with a summary at the end.
512 1.1 mrg def output_variation (self, tool, variation):
513 1.1 mrg self.output_segment (variation.header)
514 1.1 mrg for harness in sorted (variation.harnesses.values(),
515 1.1 mrg key = attrgetter ('name')):
516 1.1 mrg sys.stdout.write ('Running ' + harness.name + ' ...\n')
517 1.1 mrg if self.do_sum:
518 1.1 mrg harness.results.sort()
519 1.1 mrg for (key, line) in harness.results:
520 1.1 mrg sys.stdout.write (line)
521 1.1 mrg else:
522 1.1 mrg # Rearrange the log segments into test order (but without
523 1.1 mrg # rearranging text within those segments).
524 1.1 mrg for key in sorted (harness.segments.keys()):
525 1.1 mrg self.output_segment (harness.segments[key])
526 1.1 mrg for segment in harness.empty:
527 1.1 mrg self.output_segment (segment)
528 1.1 mrg if len (self.variations) > 1:
529 1.1 mrg sys.stdout.write ('\t\t=== ' + tool.name + ' Summary for '
530 1.1 mrg + variation.name + ' ===\n\n')
531 1.1 mrg self.output_summary (tool, variation.counts)
532 1.1 mrg
533 1.1 mrg # Output unified .log or .sum information for a particular tool,
534 1.1 mrg # with a summary at the end.
535 1.1 mrg def output_tool (self, tool):
536 1.1 mrg counts = self.zero_counts()
537 1.1 mrg if tool.name == 'acats':
538 1.1 mrg # acats doesn't use variations, so just output everything.
539 1.1 mrg # It also has a different approach to whitespace.
540 1.1 mrg sys.stdout.write ('\t\t=== ' + tool.name + ' tests ===\n')
541 1.1 mrg for variation in tool.variations.values():
542 1.1 mrg self.output_variation (tool, variation)
543 1.1 mrg self.accumulate_counts (counts, variation.counts)
544 1.1 mrg sys.stdout.write ('\t\t=== ' + tool.name + ' Summary ===\n')
545 1.1 mrg else:
546 1.1 mrg # Output the results in the usual dejagnu runtest format.
547 1.1 mrg sys.stdout.write ('\n\t\t=== ' + tool.name + ' tests ===\n\n'
548 1.1 mrg 'Schedule of variations:\n')
549 1.1 mrg for name in self.variations:
550 1.1 mrg if name in tool.variations:
551 1.1 mrg sys.stdout.write (' ' + name + '\n')
552 1.1 mrg sys.stdout.write ('\n')
553 1.1 mrg for name in self.variations:
554 1.1 mrg if name in tool.variations:
555 1.1 mrg variation = tool.variations[name]
556 1.1 mrg sys.stdout.write ('Running target '
557 1.1 mrg + variation.name + '\n')
558 1.1 mrg self.output_variation (tool, variation)
559 1.1 mrg self.accumulate_counts (counts, variation.counts)
560 1.1 mrg sys.stdout.write ('\n\t\t=== ' + tool.name + ' Summary ===\n\n')
561 1.1 mrg self.output_summary (tool, counts)
562 1.1 mrg
563 1.1 mrg def main (self):
564 1.1 mrg self.parse_cmdline()
565 1.1 mrg try:
566 1.1 mrg # Parse the input files.
567 1.1 mrg for filename in self.files:
568 1.1 mrg with safe_open (filename) as file:
569 1.1 mrg self.parse_file (filename, file)
570 1.1 mrg
571 1.1 mrg # Decide what to output.
572 1.1 mrg if len (self.variations) == 0:
573 1.1 mrg self.variations = sorted (self.known_variations)
574 1.1 mrg else:
575 1.1 mrg for name in self.variations:
576 1.1 mrg if name not in self.known_variations:
577 1.1 mrg self.fatal (None, 'no results for ' + name)
578 1.1 mrg if len (self.tools) == 0:
579 1.1 mrg self.tools = sorted (self.runs.keys())
580 1.1 mrg
581 1.1 mrg # Output the header.
582 1.1 mrg if self.start_line:
583 1.1 mrg sys.stdout.write (self.start_line[1])
584 1.1 mrg sys.stdout.write (self.native_line)
585 1.1 mrg sys.stdout.write (self.target_line)
586 1.1 mrg sys.stdout.write (self.host_line)
587 1.1 mrg sys.stdout.write (self.acats_premable)
588 1.1 mrg
589 1.1 mrg # Output the main body.
590 1.1 mrg for name in self.tools:
591 1.1 mrg if name not in self.runs:
592 1.1 mrg self.fatal (None, 'no results for ' + name)
593 1.1 mrg self.output_tool (self.runs[name])
594 1.1 mrg
595 1.1 mrg # Output the footer.
596 1.1 mrg if len (self.acats_failures) > 0:
597 1.1 mrg sys.stdout.write ('*** FAILURES: '
598 1.1 mrg + ' '.join (self.acats_failures) + '\n')
599 1.1 mrg sys.stdout.write (self.version_output)
600 1.1 mrg if self.end_line:
601 1.1 mrg sys.stdout.write (self.end_line[1])
602 1.1 mrg except IOError as e:
603 1.1 mrg self.fatal (e.filename, e.strerror)
604 1.1 mrg
605 1.1 mrg Prog().main()
606