| Index: gm/rebaseline_server/results.py
|
| ===================================================================
|
| --- gm/rebaseline_server/results.py (revision 11711)
|
| +++ gm/rebaseline_server/results.py (working copy)
|
| @@ -12,6 +12,7 @@
|
| # System-level imports
|
| import fnmatch
|
| import json
|
| +import logging
|
| import os
|
| import re
|
| import sys
|
| @@ -32,26 +33,36 @@
|
| CATEGORIES_TO_SUMMARIZE = [
|
| 'builder', 'test', 'config', 'resultType',
|
| ]
|
| +RESULTS_ALL = 'all'
|
| +RESULTS_FAILURES = 'failures'
|
|
|
| class Results(object):
|
| """ Loads actual and expected results from all builders, supplying combined
|
| - reports as requested. """
|
| + reports as requested.
|
|
|
| + Once this object has been constructed, the results are immutable. If you
|
| + want to update the results based on updated JSON file contents, you will
|
| + need to create a new Results object."""
|
| +
|
| def __init__(self, actuals_root, expected_root):
|
| """
|
| Args:
|
| actuals_root: root directory containing all actual-results.json files
|
| expected_root: root directory containing all expected-results.json files
|
| """
|
| - self._actual_builder_dicts = Results._GetDictsFromRoot(actuals_root)
|
| - self._expected_builder_dicts = Results._GetDictsFromRoot(expected_root)
|
| - self._all_results = Results._Combine(
|
| - actual_builder_dicts=self._actual_builder_dicts,
|
| - expected_builder_dicts=self._expected_builder_dicts)
|
| + self._actual_builder_dicts = Results._get_dicts_from_root(actuals_root)
|
| + self._expected_builder_dicts = Results._get_dicts_from_root(expected_root)
|
| + self._combine_actual_and_expected()
|
|
|
| - def GetAll(self):
|
| - """Return results of all tests, as a dictionary in this form:
|
| + def get_results_of_type(self, type):
|
| + """Return results of some/all tests (depending on 'type' parameter).
|
|
|
| + Args:
|
| + type: string describing which types of results to include; must be one
|
| + of the RESULTS_* constants
|
| +
|
| + Results are returned as a dictionary in this form:
|
| +
|
| {
|
| 'categories': # dictionary of categories listed in
|
| # CATEGORIES_TO_SUMMARIZE, with the number of times
|
| @@ -76,7 +87,6 @@
|
| 'testData': # list of test results, with a dictionary for each
|
| [
|
| {
|
| - 'index': 0, # index of this result within testData list
|
| 'builder': 'Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug',
|
| 'test': 'bigmatrix',
|
| 'config': '8888',
|
| @@ -90,10 +100,10 @@
|
| ], # end of 'testData' list
|
| }
|
| """
|
| - return self._all_results
|
| + return self._results[type]
|
|
|
| @staticmethod
|
| - def _GetDictsFromRoot(root, pattern='*.json'):
|
| + def _get_dicts_from_root(root, pattern='*.json'):
|
| """Read all JSON dictionaries within a directory tree.
|
|
|
| Args:
|
| @@ -114,37 +124,32 @@
|
| meta_dict[builder] = gm_json.LoadFromFile(fullpath)
|
| return meta_dict
|
|
|
| - @staticmethod
|
| - def _Combine(actual_builder_dicts, expected_builder_dicts):
|
| + def _combine_actual_and_expected(self):
|
| """Gathers the results of all tests, across all builders (based on the
|
| - contents of actual_builder_dicts and expected_builder_dicts).
|
| -
|
| - This is a static method, because once we start refreshing results
|
| - asynchronously, we need to make sure we are not corrupting the object's
|
| - member variables.
|
| -
|
| - Args:
|
| - actual_builder_dicts: a meta-dictionary of all actual JSON results,
|
| - as returned by _GetDictsFromRoot().
|
| - actual_builder_dicts: a meta-dictionary of all expected JSON results,
|
| - as returned by _GetDictsFromRoot().
|
| -
|
| - Returns:
|
| - A list of all the results of all tests, in the same form returned by
|
| - self.GetAll().
|
| + contents of self._actual_builder_dicts and self._expected_builder_dicts),
|
| + and stores them in self._results.
|
| """
|
| - test_data = []
|
| - category_dict = {}
|
| - Results._EnsureIncludedInCategoryDict(category_dict, 'resultType', [
|
| + categories_all = {}
|
| + categories_failures = {}
|
| + Results._ensure_included_in_category_dict(categories_all,
|
| + 'resultType', [
|
| gm_json.JSONKEY_ACTUALRESULTS_FAILED,
|
| gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED,
|
| gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
|
| gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED,
|
| ])
|
| + Results._ensure_included_in_category_dict(categories_failures,
|
| + 'resultType', [
|
| + gm_json.JSONKEY_ACTUALRESULTS_FAILED,
|
| + gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED,
|
| + gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
|
| + ])
|
|
|
| - for builder in sorted(actual_builder_dicts.keys()):
|
| + data_all = []
|
| + data_failures = []
|
| + for builder in sorted(self._actual_builder_dicts.keys()):
|
| actual_results_for_this_builder = (
|
| - actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
|
| + self._actual_builder_dicts[builder][gm_json.JSONKEY_ACTUALRESULTS])
|
| for result_type in sorted(actual_results_for_this_builder.keys()):
|
| results_of_this_type = actual_results_for_this_builder[result_type]
|
| if not results_of_this_type:
|
| @@ -154,7 +159,7 @@
|
| try:
|
| # TODO(epoger): assumes a single allowed digest per test
|
| expected_image = (
|
| - expected_builder_dicts
|
| + self._expected_builder_dicts
|
| [builder][gm_json.JSONKEY_EXPECTEDRESULTS]
|
| [image_name][gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS]
|
| [0])
|
| @@ -186,11 +191,11 @@
|
| if result_type not in [
|
| gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON,
|
| gm_json.JSONKEY_ACTUALRESULTS_FAILUREIGNORED] :
|
| - print 'WARNING: No expectations found for test: %s' % {
|
| + logging.warning('No expectations found for test: %s' % {
|
| 'builder': builder,
|
| 'image_name': image_name,
|
| 'result_type': result_type,
|
| - }
|
| + })
|
| expected_image = [None, None]
|
|
|
| # If this test was recently rebaselined, it will remain in
|
| @@ -213,7 +218,6 @@
|
|
|
| (test, config) = IMAGE_FILENAME_RE.match(image_name).groups()
|
| results_for_this_test = {
|
| - 'index': len(test_data),
|
| 'builder': builder,
|
| 'test': test,
|
| 'config': config,
|
| @@ -223,14 +227,25 @@
|
| 'expectedHashType': expected_image[0],
|
| 'expectedHashDigest': str(expected_image[1]),
|
| }
|
| - Results._AddToCategoryDict(category_dict, results_for_this_test)
|
| - test_data.append(results_for_this_test)
|
| - return {'categories': category_dict, 'testData': test_data}
|
| + Results._add_to_category_dict(categories_all, results_for_this_test)
|
| + data_all.append(results_for_this_test)
|
| + if updated_result_type != gm_json.JSONKEY_ACTUALRESULTS_SUCCEEDED:
|
| + Results._add_to_category_dict(categories_failures,
|
| + results_for_this_test)
|
| + data_failures.append(results_for_this_test)
|
|
|
| + self._results = {
|
| + RESULTS_ALL:
|
| + {'categories': categories_all, 'testData': data_all},
|
| + RESULTS_FAILURES:
|
| + {'categories': categories_failures, 'testData': data_failures},
|
| + }
|
| +
|
| @staticmethod
|
| - def _AddToCategoryDict(category_dict, test_results):
|
| + def _add_to_category_dict(category_dict, test_results):
|
| """Add test_results to the category dictionary we are building.
|
| - (See documentation of self.GetAll() for the format of this dictionary.)
|
| + (See documentation of self.get_results_of_type() for the format of this
|
| + dictionary.)
|
|
|
| Args:
|
| category_dict: category dict-of-dicts to add to; modify this in-place
|
| @@ -252,11 +267,12 @@
|
| category_dict[category][category_value] += 1
|
|
|
| @staticmethod
|
| - def _EnsureIncludedInCategoryDict(category_dict,
|
| - category_name, category_values):
|
| + def _ensure_included_in_category_dict(category_dict,
|
| + category_name, category_values):
|
| """Ensure that the category name/value pairs are included in category_dict,
|
| even if there aren't any results with that name/value pair.
|
| - (See documentation of self.GetAll() for the format of this dictionary.)
|
| + (See documentation of self.get_results_of_type() for the format of this
|
| + dictionary.)
|
|
|
| Args:
|
| category_dict: category dict-of-dicts to modify
|
|
|