import sys
import getopt
import re
+import io
from datetime import datetime
+from operator import attrgetter
# True if unrecognised lines should cause a fatal error. Might want to turn
# this on by default later.
# they should keep the original order.
sort_logs = True
+# A version of open() that is safe against whatever binary output
+# might be added to the log.
+def safe_open (filename):
+ if sys.version_info >= (3, 0):
+ return open (filename, 'r', errors = 'surrogateescape')
+ return open (filename, 'r')
+
+# Force stdout to handle escape sequences from a safe_open file.
+if sys.version_info >= (3, 0):
+ sys.stdout = io.TextIOWrapper (sys.stdout.buffer,
+ errors = 'surrogateescape')
+
class Named:
def __init__ (self, name):
self.name = name
- def __cmp__ (self, other):
- return cmp (self.name, other.name)
-
class ToolRun (Named):
def __init__ (self, name):
Named.__init__ (self, name)
self.tool_re = re.compile (r'^\t\t=== (.*) tests ===$')
self.result_re = re.compile (r'^(PASS|XPASS|FAIL|XFAIL|UNRESOLVED'
r'|WARNING|ERROR|UNSUPPORTED|UNTESTED'
- r'|KFAIL):\s*(\S+)')
+ r'|KFAIL):\s*(.+)')
self.completed_re = re.compile (r'.* completed at (.*)')
# Pieces of text to write at the head of the output.
# start_line is a pair in which the first element is a datetime
self.end_line = None
# Known summary types.
self.count_names = [
+ '# of DejaGnu errors\t\t',
'# of expected passes\t\t',
'# of unexpected failures\t',
'# of unexpected successes\t',
harness = None
segment = None
final_using = 0
+ has_warning = 0
# If this is the first run for this variation, add any text before
# the first harness to the header.
segment = Segment (filename, file.tell())
variation.header = segment
+ # Parse the rest of the summary (the '# of ' lines).
+ if len (variation.counts) == 0:
+ variation.counts = self.zero_counts()
+
# Parse up until the first line of the summary.
if num_variations == 1:
end = '\t\t=== ' + tool.name + ' Summary ===\n'
# the harness segment, so that if a run for a particular harness
# has been split up, we can reassemble the individual segments
# in a sensible order.
+ #
+ # dejagnu sometimes issues warnings about the testing environment
+ # before running any tests. Treat them as part of the header
+ # rather than as a test result.
match = self.result_re.match (line)
- if match:
+ if match and (harness or not line.startswith ('WARNING:')):
if not harness:
self.fatal (filename, 'saw test result before harness name')
name = match.group (2)
# Ugly hack to get the right order for gfortran.
if name.startswith ('gfortran.dg/g77/'):
name = 'h' + name
- key = (name, len (harness.results))
- harness.results.append ((key, line))
- if not first_key and sort_logs:
- first_key = key
+ # If we have a time out warning, make sure it appears
+ # before the following testcase diagnostic: we insert
+ # the testname before 'program' so that sort faces a
+ # list of testnames.
+ if line.startswith ('WARNING: program timed out'):
+ has_warning = 1
+ else:
+ if has_warning == 1:
+ key = (name, len (harness.results))
+ myline = 'WARNING: %s program timed out.\n' % name
+ harness.results.append ((key, myline))
+ has_warning = 0
+ key = (name, len (harness.results))
+ harness.results.append ((key, line))
+ if not first_key and sort_logs:
+ first_key = key
+ if line.startswith ('ERROR: (DejaGnu)'):
+ for i in range (len (self.count_names)):
+ if 'DejaGnu errors' in self.count_names[i]:
+ variation.counts[i] += 1
+ break
# 'Using ...' lines are only interesting in a header. Splitting
# the test up into parallel runs leads to more 'Using ...' lines
segment.lines -= final_using
harness.add_segment (first_key, segment)
- # Parse the rest of the summary (the '# of ' lines).
- if len (variation.counts) == 0:
- variation.counts = self.zero_counts()
while True:
before = file.tell()
line = file.readline()
# Output a segment of text.
def output_segment (self, segment):
- with open (segment.filename, 'r') as file:
+ with safe_open (segment.filename) as file:
file.seek (segment.start)
for i in range (segment.lines):
sys.stdout.write (file.readline())
# with a summary at the end.
def output_variation (self, tool, variation):
self.output_segment (variation.header)
- for harness in sorted (variation.harnesses.values()):
+ for harness in sorted (variation.harnesses.values(),
+ key = attrgetter ('name')):
sys.stdout.write ('Running ' + harness.name + ' ...\n')
if self.do_sum:
- # Keep the original test result order if there was only
- # one segment for this harness. This is needed for
- # unsorted.exp, which has unusual test names. Otherwise
- # sort the tests by test filename. If there are several
- # subtests for the same test filename (such as 'compilation',
- # 'test for excess errors', etc.) then keep the subtests
- # in the original order.
- if len (harness.segments) > 1:
- harness.results.sort()
+ harness.results.sort()
for (key, line) in harness.results:
sys.stdout.write (line)
else:
try:
# Parse the input files.
for filename in self.files:
- with open (filename, 'r') as file:
+ with safe_open (filename) as file:
self.parse_file (filename, file)
# Decide what to output.