# vim:fileencoding=utf-8 # (c) 2011 Michał Górny # Released under the terms of the 2-clause BSD license. from collections import defaultdict from gentoopm.util import ABCObject from abc import abstractmethod from . import OutputModule class HTMLOutput(OutputModule): """ HTML output module. """ name = 'html' def __init__(self, path = None): if path is None: path = 'pms-test-suite-output.html' self._path = path _htmlheader = ''' PMS Test Suite test results ''' _htmlfooter = '''
''' def __call__(self, allresults, verbose = False): mypms = [] def _results_by_test(allresults): ret = defaultdict(lambda: defaultdict(lambda: None)) for pm, results in allresults.items(): mypms.append(pm) for t, r in results.items(): ret[t][pm] = r return ret def _results_by_class(reorderedresults): ret = defaultdict(dict) for t, r in reorderedresults.items(): ret[t.short_name][t] = r for cl in sorted(ret): yield cl, ret[cl] def _sorted_pms(pms): for pm in sorted(pms, key = lambda pm: mypms.index(pm)): yield (pm, pms[pm]) class HTMLElem(ABCObject): @abstractmethod def set_rowspan(self, rowspan): pass @abstractmethod def __str__(self): pass class TD(HTMLElem): _elem = 'td' def __init__(self, text, colspan = 1): self._attrs = [] self._text = text self._colspan = colspan if colspan != 1: self._attrs.append('colspan="%d"' % colspan) def set_rowspan(self, rowspan): if rowspan != 1: self._attrs.append('rowspan="%d"' % rowspan) def __str__(self): return '<%s>%s' % (' '.join([self._elem] + self._attrs), self._text, self._elem) class TH(TD): _elem = 'th' class ValCell(TD): _color_class = '' def __init__(self, text): TD.__init__(self, text) self._attrs.append('class="value %s"' % self._color_class) class ColorValCell(ValCell): def __init__(self, text, a): if a.undefined: self._color_class = 'unk-good' if a else 'unk-bad' else: self._color_class = 'good' if a else 'bad' ValCell.__init__(self, text) class BoolCell(ValCell): def __init__(self, r): if not r.undefined: self._color_class = 'good' if r else 'bad' ValCell.__init__(self, 'OK' if r else 'FAIL') else: # undefined result self._color_class = 'unk-good' if r else 'unk-bad' ValCell.__init__(self, 'n/a (but OK)' if r \ else 'n/a (but FAIL)') class UnknownValCell(ValCell): _color_class = 'unknown' def __init__(self): ValCell.__init__(self, '?') class NoCell(HTMLElem): def __init__(self): pass def set_rowspan(self, rowspan): pass def __str__(self): return '' ret = True results = _results_by_test(allresults) table = defaultdict(lambda: defaultdict(lambda: None)) table[0][0] = TH('Test name') table[0][1] = TH('EAPI') table[0][2] = TH('Assertion', colspan = 2) table[0][4] = TH('Expected') for i, pm in enumerate(mypms): table[0][5 + i*2] = TH('%s %s' % (pm.name, pm.version), colspan = 2) table[0][6 + i*2] = NoCell() table[1][5 + i*2] = TH('Actual') table[1][6 + i*2] = TH('Result') maxcol = 7 + i*2 row = 2 for cl, tests in _results_by_class(results): table[row][0] = cl for t in sorted(tests, key = lambda t: t.eapi): table[row][1] = t.eapi test_asserts = [] col = 5 for pm, r in _sorted_pms(tests[t]): table[row][col+1] = BoolCell(r) for ai, a in enumerate(r.assertions): if a.name not in test_asserts: test_asserts.append(a.name) crow = row + test_asserts.index(a.name) if a.prefix is not None: if ai == 0 or table[crow-1][2] != a.prefix: table[crow][2] = a.prefix table[crow][3] = a.unprefixed_name else: table[crow][2] = TD(a.name, colspan = 2) table[crow][3] = NoCell() table[crow][4] = ValCell(a.expected) for c in range(5, maxcol, 2): table[crow][c] = UnknownValCell() else: crow = row + test_asserts.index(a.name) table[crow][col] = ColorValCell(a.actual, a) col += 2 row += len(test_asserts) f = open(self._path, 'w') f.write(self._htmlheader) for y in range(0, row): f.write('') for x in range(0, maxcol): cell = table[y][x] if cell is not None: rowspan = 1 for y2 in range(y + 1, row): if table[y2][x] is None: rowspan += 1 else: break if not isinstance(cell, HTMLElem): cell = TD(cell) cell.set_rowspan(rowspan) f.write(str(cell)) f.write('') f.write(self._htmlfooter) f.close() return ret