def calculate_group_totals(self): """Calculate the number of passes, fails, etc at each level.""" for name, result in six.iteritems(self.tests): # If there are subtests treat the test as if it is a group instead # of a test. if result.subtests: for res in six.itervalues(result.subtests): res = str(res) temp = name self.totals[temp][res] += 1 while temp: temp = grouptools.groupname(temp) self.totals[temp][res] += 1 self.totals['root'][res] += 1 else: res = str(result.result) while name: name = grouptools.groupname(name) self.totals[name][res] += 1 self.totals['root'][res] += 1
def calculate_group_totals(self): """Calculate the number of pases, fails, etc at each level.""" for name, result in six.iteritems(self.tests): # If there are subtests treat the test as if it is a group instead # of a test. if result.subtests: for res in six.itervalues(result.subtests): res = str(res) temp = name self.totals[temp][res] += 1 while temp: temp = grouptools.groupname(temp) self.totals[temp][res] += 1 self.totals['root'][res] += 1 else: res = str(result.result) while name: name = grouptools.groupname(name) self.totals[name][res] += 1 self.totals['root'][res] += 1
def __init__(self, summary, page): """ Steps through the list of groups and tests from all of the results and generates a list of dicts that are passed to mako and turned into HTML """ def returnList(open, close): """ As HTMLIndex iterates through the groups and tests it uses this function to determine which groups to close (and thus reduce the depth of the next write) and which ones to open (thus increasing the depth) To that end one of two things happens, the path to the previous group (close) and the next group (open) are equal, in that event we don't want to open and close, becasue that will result in a sawtooth pattern of a group with one test followed by the same group with one test, over and over. Instead we simply return two empty lists, which will result in writing a long list of test results. The second option is that the paths are different, and the function determines any commonality between the paths, and returns the differences as close (the groups which are completly written) and open (the new groups to write). """ common = [] # Open and close are lists, representing the group hierarchy, open # being the groups that need are soon to be written, and close # representing the groups that have finished writing. if open == close: return [], [] else: for i, j in itertools.izip_longest(open, close): if i != j: for k in common: open.remove(k) close.remove(k) return open, close else: common.append(i) # set a starting depth of 1, 0 is used for 'all' so 1 is the # next available group depth = 1 # Current dir is a list representing the groups currently being # written. currentDir = [] # Add a new 'tab' for each result self._newRow() self.append({'type': 'other', 'text': '<td />'}) for each in summary.results: href = normalize_href(os.path.join( escape_pathname(each.name), "index.html")) self.append({'type': 'other', 'text': '<td class="head"><b>%(name)s</b><br />' '(<a href="%(href)s">info</a>)' '</td>' % {'name': each.name, 'href': href}}) self._endRow() # Add the toplevel 'all' group self._newRow() self._groupRow("head", 0, 'all') for each in summary.results: self._groupResult(summary.fractions[each.name]['all'], summary.status[each.name]['all']) self._endRow() # Add the groups and tests to the out list for key in sorted(page): # Split the group names and test names, then determine # which groups to close and which to open openList = key.split(grouptools.SEPARATOR) test = openList.pop() openList, closeList = returnList(openList, list(currentDir)) # Close any groups in the close list # for each group closed, reduce the depth by one for i in reversed(closeList): currentDir.remove(i) depth -= 1 # Open new groups for localGroup in openList: self._newRow() # Add the left-most column: the name of the group self._groupRow("head", depth, localGroup) # Add the group that we just opened to the currentDir, which # will then be used to add that group to the HTML list. If # there is a KeyError (the group doesn't exist), use (0, 0) # which will get skip. This sets the group coloring correctly currentDir.append(localGroup) for each in summary.results: # Decide which fields need to be updated self._groupResult( summary.fractions[each.name][grouptools.join(*currentDir)], summary.status[each.name][grouptools.join(*currentDir)]) # After each group increase the depth by one depth += 1 self._endRow() # Add the tests for the current group self._newRow() # Add the left-most column: the name of the test self._testRow("group", depth, test) # Add the result from each test result to the HTML summary If there # is a KeyError (a result doesn't contain a particular test), # return Not Run, with clas skip for highlighting for each in summary.results: # If the "group" at the top of the key heirachy contains # 'subtest' then it is really not a group, link to that page try: if each.tests[grouptools.groupname(key)]['subtest']: href = grouptools.groupname(key) except KeyError: href = key href = escape_filename(href) try: self._testResult(escape_pathname(each.name), href, summary.status[each.name][key]) except KeyError: self.append({'type': 'other', 'text': '<td class="skip">Not Run</td>'}) self._endRow()
def test_basic(self): assert grouptools.groupname(grouptools.join('g1', 'g2', 't1')) == \ grouptools.join('g1', 'g2')
def __init__(self, resultfiles): """ Create an initial object with all of the result information rolled up in an easy to process form. The constructor of the summary class has an attribute for each HTML summary page, which are fed into the index.mako file to produce HTML files. resultfiles is a list of paths to JSON results generated by piglit-run. """ # Create a Result object for each piglit result and append it to the # results list self.results = [backends.load(i) for i in resultfiles] self.status = {} self.fractions = {} self.totals = {} self.tests = {'all': set(), 'changes': set(), 'problems': set(), 'skipped': set(), 'regressions': set(), 'fixes': set(), 'enabled': set(), 'disabled': set(), 'incomplete': set()} def fgh(test, result): """ Helper for updating the fractions and status lists """ fraction[test] = tuple( [sum(i) for i in zip(fraction[test], result.fraction)]) # If the new status is worse update it, or if the new status is # SKIP (which is equivalent to notrun) and the current is NOTRUN # update it if (status[test] < result or (result == so.SKIP and status[test] == so.NOTRUN)): status[test] = result for results in self.results: # Create a set of all of the tset names across all of the runs self.tests['all'] = set(self.tests['all'] | set(results.tests)) # Create two dictionaries that have a default factory: they return # a default value instead of a key error. # This default key must be callable self.fractions[results.name] = \ collections.defaultdict(lambda: (0, 0)) self.status[results.name] = \ collections.defaultdict(lambda: so.NOTRUN) # short names fraction = self.fractions[results.name] status = self.status[results.name] # store the results to be appeneded to results. Adding them in the # loop will cause a RuntimeError temp_results = {} for key, value in results.tests.iteritems(): # if the first character of key is a / then our while loop will # become an infinite loop. Beyond that / should never be the # leading character, if it is then there is a bug in one of the # test profiles. assert key[0] != '/' # Treat a test with subtests as if it is a group, assign the # subtests' statuses and fractions down to the test, and then # proceed like normal. if 'subtest' in value: for (subt, subv) in value['subtest'].iteritems(): subt = grouptools.join(key, subt) subv = so.status_lookup(subv) # Add the subtest to the fractions and status lists fraction[subt] = subv.fraction status[subt] = subv temp_results.update({subt: {'result': subv}}) self.tests['all'].add(subt) while subt != '': fgh(subt, subv) subt = grouptools.groupname(subt) fgh('all', subv) # remove the test from the 'all' list, this will cause to # be treated as a group self.tests['all'].discard(key) else: # Walk the test name as if it was a path, at each level # update the tests passed over the total number of tests # (fractions), and update the status of the current level # if the status of the previous level was worse, but is not # skip while key != '': fgh(key, value['result']) key = grouptools.groupname(key) # when we hit the root update the 'all' group and stop fgh('all', value['result']) # Update the the results.tests dictionary with the subtests so that # they are entered into the appropriate pages other than all. # Updating it in the loop will raise a RuntimeError for key, value in temp_results.iteritems(): results.tests[key] = value # Create the lists of statuses like problems, regressions, fixes, # changes and skips for test in self.tests['all']: status = [] for each in self.results: try: status.append(each.tests[test]['result']) except KeyError: status.append(so.NOTRUN) # Problems include: warn, dmesg-warn, fail, dmesg-fail, and crash. # Skip does not go on this page, it has the 'skipped' page if max(status) > so.PASS: self.tests['problems'].add(test) # Find all tests with a status of skip if so.SKIP in status: self.tests['skipped'].add(test) if so.INCOMPLETE in status: self.tests['incomplete'].add(test) # find fixes, regressions, and changes for i in xrange(len(status) - 1): first = status[i] last = status[i + 1] if first in [so.SKIP, so.NOTRUN] and \ last not in [so.SKIP, so.NOTRUN]: self.tests['enabled'].add(test) self.tests['changes'].add(test) elif last in [so.SKIP, so.NOTRUN] and \ first not in [so.SKIP, so.NOTRUN]: self.tests['disabled'].add(test) self.tests['changes'].add(test) elif first < last: self.tests['regressions'].add(test) self.tests['changes'].add(test) elif first > last: self.tests['fixes'].add(test) self.tests['changes'].add(test)