# Functions for manipulating .sum summary files. import re import os.path from StringIO import StringIO # Helper regex for parse_sum_line. sum_matcher = re.compile('^(.?(PASS|FAIL)): (.*)$') # You must call set_web_base at startup to set this. gdb_web_base = None def set_web_base(arg): global gdb_web_base gdb_web_base = arg if not os.path.isdir(gdb_web_base): # If the parent doesn't exist, we're confused. # So, use mkdir and not makedirs. os.mkdir(gdb_web_base, 0755) def get_web_base (): global gdb_web_base return gdb_web_base class DejaResults(object): def __init__(self): object.__init__(self) # Parse a single line from a .sum file. # Uniquify the name, and put the result into OUT_DICT. # If the line does not appear to be about a test, ignore it. def parse_sum_line(self, out_dict, line): global sum_matcher line = line.rstrip() m = re.match(sum_matcher, line) if m: result = m.group(1) test_name = m.group(3) if test_name in out_dict: i = 2 while True: nname = test_name + ' <<' + str(i) + '>>' if nname not in out_dict: break i = i + 1 test_name = nname out_dict[test_name] = result def _write_sum_file(self, sum_dict, subdir, revision, filename): global gdb_web_base if revision: bdir = os.path.join(gdb_web_base, subdir, revision) else: bdir = os.path.join (gdb_web_base, subdir) if not os.path.isdir(bdir): os.makedirs(bdir, 0755) fname = os.path.join(bdir, filename) keys = sum_dict.keys() keys.sort() f = open(fname, 'w') for k in keys: f.write(sum_dict[k] + ': ' + k + '\n') f.close() def write_sum_file(self, sum_dict, builder, revision): self._write_sum_file (sum_dict, builder, None, 'gdb.sum') # self._write_sum_file(sum_dict, builder, revision, 'gdb.sum') def write_baseline(self, sum_dict, builder, branch): self._write_sum_file(sum_dict, os.path.join(builder, branch), None, 'baseline') # Read a .sum file. # The builder name is BUILDER. # The base file name is given in FILENAME. This should be a git # revision; to read the baseline file for a branch, use `read_baseline'. # Returns a dictionary holding the .sum contents, or None if the # file did not exist. def _read_sum_file(self, subdir, revision, filename): global gdb_web_base if not revision: fname = os.path.join(gdb_web_base, subdir, filename) else: fname = os.path.join (gdb_web_base, subdir, revision, filename) if os.path.exists(fname): result = {} f = open(fname, 'r') for line in f: self.parse_sum_line (result, line) f.close() else: result = None return result def read_sum_file (self, builder, revision): return self._read_sum_file (builder, None, 'gdb.sum') # return self._read_sum_file (builder, revision, 'gdb.sum') def read_baseline(self, builder, branch): return self._read_sum_file(os.path.join(builder, branch), None, 'baseline') def read_xfail (self, builder): return self._read_sum_file (builder, None, 'xfail') # Parse some text as a .sum file and return the resulting # dictionary. def read_sum_text(self, text): cur_file = StringIO(text) cur_results = {} for line in cur_file.readlines(): self.parse_sum_line(cur_results, line) return cur_results def transform_re (self, xfails): ret = {} for r in xfails: ret[r] = re.compile (r) return ret # Compute regressions between RESULTS and BASELINE on BUILDER. # BASELINE will be modified if any new PASSes are seen. # Returns a regression report, as a string. def compute_regressions(self, builder, results, baseline): our_keys = results.keys() our_keys.sort() result = '' xfails = self.read_xfail (builder) if xfails is None: xfails = {} xfails_re = self.transform_re (xfails) for key in our_keys: # An XFAIL entry means we have an unreliable test. cont = False for x in xfails_re: if re.match (xfails_re[x], (key)): cont = True break if cont: continue # if key in xfails: # continue # A transition to PASS means we should update the baseline. if results[key] == 'PASS': if key not in baseline or baseline[key] != 'PASS': baseline[key] = 'PASS' # A regression is just a transition to FAIL. if results[key] != 'FAIL': continue if key not in baseline: result = result + 'new FAIL: ' + key + '\n' elif baseline[key] != 'FAIL': result = result + baseline[key] + ' -> FAIL: ' + key + '\n' return result