import os, time, signal, sys from toolbox import check_label def load_config(): cfg = {} execfile('autotestconfig.py', cfg) return cfg DEFAULTS = { # title prefix, overridable in autotestconfig 'PREFIX': '', 'KEEP': 15 } class Summary(object): def __init__(self, html_dir, config): self.html_dir = html_dir self.config = config def gather_revisions(self): revisions = [] for fn in os.listdir(self.html_dir): try: if check_label(fn): revisions.append(fn) except ValueError: pass revisions.sort() return revisions def gather_outcomes(self, revisions): outcomes = {} failures = {} counts = {} for rev in revisions: dir = os.path.join(self.html_dir, str(rev), 'failed') if os.path.isdir(dir): for fn in os.listdir(dir): failures.setdefault(fn, {})[rev] = True try: f = open(os.path.join(self.html_dir, str(rev), 'outcome'), 'r') except IOError: pass else: c = 0 for line in f: line = line.strip() outcomes[line[2:], rev] = line[0] c += 1 f.close() counts[rev] = c failures = failures.items() failures.sort() skips = {} for (fn, rev), outcome in outcomes.items(): if outcome == 's': skips.setdefault(rev, []).append(fn) self.failures = failures self.outcomes = outcomes self.counts = counts self.skips = skips modmax = 0 testnames = [] for fn, revs in self.failures: if '.py.' in fn: mod, test = fn.split('.py.', 1) elif '.txt.' in fn: mod, test = fn.split('.txt.', 1) else: mod, test = fn, '' modmax = max(modmax, len(mod)) if test.endswith('.html'): test = test[:-5] testnames.append((mod, test)) self.modmax = modmax self.testnames = testnames def getoutcome(self, fn, rev, mod): while (fn, rev) not in self.outcomes: if not mod: return ' ' fn = mod + '.py..html' i = mod.rfind('.') if i < 0: mod = '' else: mod = mod[:i] else: return self.outcomes[fn, rev] def revision_complete(self, rev): try: f = open(os.path.join(self.html_dir, str(rev), 'index.html'), 'r') f.seek(-34, 2) # xxx adjust taildata = f.read() f.close() except IOError: return False return 'Done.' in taildata def revisiondate(self, rev): st = os.stat(os.path.join(self.html_dir, rev)) t = time.localtime(st.st_mtime) return time.strftime('%d %b', t) def dump(self, revisions, prefix, filename): if len(revisions) == 1: title = 'Summary of the failures in revision %s' % (revisions[0],) else: title = 'Summary of the failures in the latest %d revisions' % (len(revisions),) title = self.config['PREFIX'] + title f = open(filename, 'wb') print >> f, '' print >> f, '%s' % (title,) print >> f, '' print >> f, '' print >> f, '

%s

' % (title,) if not prefix: print >> f, 'See also the in-progress/latest run.' if "AUTOTEST_LOG" in self.config: print >> f, ('Here is the latest logged stdout ' 'of the test runner.' % (self.config['AUTOTEST_LOG'],)) if "MANUAL_RUN_CGI" in self.config: print >> f, '
' % (self.config['MANUAL_RUN_CGI'],) print >> f, 'You can start a full test run manually (unless one is' print >> f, 'already running):' print >> f, '
' print >> f, '
'

        lines = []
        for rev in revisions:
            count_failures = 0
            for fn, revs in self.failures:
                if rev in revs:
                    count_failures += 1
            count_skips = len(self.skips.get(rev, []))
            if prefix:
                link_pre = ''
                link_post = ''
            else:
                link_pre = '' % (prefix, rev)
                link_post = ''
            incomplete_note = ''
            if not self.revision_complete(rev):
                incomplete_note = ' (incomplete run)'
            lines.append('%s %s (%s)'
                         '%s%s%d failures, %d skips%s%s' % (
                ' |' * len(lines),
                prefix, rev, rev,
                self.revisiondate(rev),
                ' ' * (2 * (len(revisions) - len(lines))
                       + 10 - len(rev) - len(str(count_failures))),
                link_pre, count_failures, count_skips, link_post,
                incomplete_note))

        lines.append(' |' * len(lines))
        for line in lines:
            print >> f, line
        print >> f

        for (fn, revs), (mod, test) in zip(self.failures, self.testnames):
            line = ''
            for rev in revisions:
                if rev in revs:
                    if fn.startswith('(') and fn.endswith(')'):
                        url = '%s' % rev
                    else:
                        url = '%s/failed/%s' % (rev, fn)
                    line += (' F'
                             % (prefix, url))
                else:
                    if prefix:
                        break
                    outcome = self.getoutcome(fn, rev, mod)
                    line += ' ' + outcome
            else:
                line += '  %s  %s' % (mod.ljust(self.modmax), test)
                print >> f, line

        print >> f
        for line in lines[::-1]:
            print >> f, line

        print >> f, '
' footer = '' if not prefix: footer = '%d tests listed.' % (len(self.failures),) elif len(revisions) == 1: this_rev = revisions[0] skips = self.skips.get(this_rev) if skips: footer = '%d test%s skipped. ' % (len(skips), "s"[:len(skips)-1]) footer += '[%d outcomes] ' % self.counts[this_rev] footer += 'See run record' footer += ' or per-test outcome.' if footer: print >> f, '

%s

' % (footer,) print >> f, '' f.close() def main(targetdir, user_config=None): assert os.path.isdir(targetdir) if user_config is None: user_config = load_config() config = DEFAULTS.copy() config.update(user_config) summary = Summary(targetdir, config) revisions = summary.gather_revisions() del revisions[:-config['KEEP']] summary.gather_outcomes(revisions) summary.dump(revisions, '', os.path.join(targetdir, 'summary.html')) for rev in revisions: summary.dump([rev], '../', os.path.join(targetdir, str(rev), 'summary.html')) if __name__ == '__main__': if len(sys.argv) > 1: targetdir = sys.argv[1] else: targetdir = 'html' main(targetdir)