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