OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 |
| 3 ''' |
| 4 Copyright 2013 Google Inc. |
| 5 |
| 6 Use of this source code is governed by a BSD-style license that can be |
| 7 found in the LICENSE file. |
| 8 ''' |
| 9 |
| 10 ''' |
| 11 Repackage expected/actual GM results as needed by our HTML rebaseline viewer. |
| 12 ''' |
| 13 |
| 14 # System-level imports |
| 15 import fnmatch |
| 16 import json |
| 17 import os |
| 18 import re |
| 19 import sys |
| 20 |
| 21 # Imports from within Skia |
| 22 # |
| 23 # We need to add the 'gm' directory, so that we can import gm_json.py within |
| 24 # that directory. That script allows us to parse the actual-results.json file |
| 25 # written out by the GM tool. |
| 26 # Make sure that the 'gm' dir is in the PYTHONPATH, but add it at the *end* |
| 27 # so any dirs that are already in the PYTHONPATH will be preferred. |
| 28 GM_DIRECTORY = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) |
| 29 if GM_DIRECTORY not in sys.path: |
| 30 sys.path.append(GM_DIRECTORY) |
| 31 import gm_json |
| 32 |
| 33 IMAGE_FILENAME_RE = re.compile(gm_json.IMAGE_FILENAME_PATTERN) |
| 34 |
| 35 class Results(object): |
| 36 """ Loads actual and expected results from all builders, supplying combined |
| 37 reports as requested. """ |
| 38 |
| 39 def __init__(self, actuals_root, expected_root): |
| 40 """ |
| 41 params: |
| 42 actuals_root: root directory containing all actual-results.json files |
| 43 expected_root: root directory containing all expected-results.json files |
| 44 """ |
| 45 self._actual_builder_dicts = Results._GetDictsFromRoot(actuals_root) |
| 46 self._expected_builder_dicts = Results._GetDictsFromRoot(expected_root) |
| 47 |
| 48 @staticmethod |
| 49 def _GetDictsFromRoot(root, pattern='*.json'): |
| 50 """Read all JSON dictionaries within a directory tree, returning them within |
| 51 a meta-dictionary (keyed by the builder type for each dictionary). |
| 52 |
| 53 params: |
| 54 root: path to root of directory tree |
| 55 pattern: which files to read within root (fnmatch-style pattern) |
| 56 """ |
| 57 meta_dict = {} |
| 58 for dirpath, dirnames, filenames in os.walk(root): |
| 59 for matching_filename in fnmatch.filter(filenames, pattern): |
| 60 builder = os.path.basename(dirpath) |
| 61 # EPOGER: make this check configurable? |
| 62 if builder.endswith('-Trybot'): |
| 63 continue |
| 64 fullpath = os.path.join(dirpath, matching_filename) |
| 65 meta_dict[builder] = gm_json.LoadFromFile(fullpath) |
| 66 return meta_dict |
| 67 |
| 68 def OfType(self, result_type): |
| 69 """Returns an array of all tests, across all builders, of a certain |
| 70 result_type. Returns the array in this form: |
| 71 |
| 72 [ |
| 73 { |
| 74 "builder": "Test-Mac10.6-MacMini4.1-GeForce320M-x86-Debug", |
| 75 "test": "bigmatrix", |
| 76 "config": "8888", |
| 77 "expectedHashType": "bitmap-64bitMD5", |
| 78 "expectedHashDigest": "10894408024079689926", |
| 79 "actualHashType": "bitmap-64bitMD5", |
| 80 "actualHashDigest": "2409857384569", |
| 81 }, |
| 82 ... |
| 83 ] |
| 84 |
| 85 params: |
| 86 result_type: string describing result type, such as 'failed'; should be |
| 87 one of the gm_json.JSONKEY_ACTUALRESULTS_* strings. |
| 88 """ |
| 89 # EPOGER: validate that result_type is one of the gm_json.JSONKEY_ACTUALRESU
LTS_* strings? |
| 90 array = [] |
| 91 for builder in sorted(self._actual_builder_dicts.keys()): |
| 92 results_of_this_type = self._actual_builder_dicts[builder][gm_json.JSONKEY
_ACTUALRESULTS][result_type] |
| 93 if not results_of_this_type: |
| 94 continue |
| 95 for image_name in sorted(results_of_this_type.keys()): |
| 96 actual_image = results_of_this_type[image_name] |
| 97 try: |
| 98 # EPOGER: assumes a single allowed digest per test |
| 99 expected_image = self._expected_builder_dicts[builder][gm_json.JSONKEY
_EXPECTEDRESULTS][image_name][gm_json.JSONKEY_EXPECTEDRESULTS_ALLOWEDDIGESTS][0] |
| 100 except (KeyError, TypeError): |
| 101 # It's OK to find no expectations, if this is a NOCOMPARISON test! |
| 102 # But we still want to look for expectations, in case someone has |
| 103 # committed expectations and the bots just haven't tested them yet. |
| 104 if result_type != gm_json.JSONKEY_ACTUALRESULTS_NOCOMPARISON: |
| 105 raise |
| 106 expected_image = [None, None] |
| 107 |
| 108 # If this test was recently rebaselined, it will remain in the "failed" |
| 109 # set of actuals until all the bots have cycled (although the |
| 110 # expectations have indeed been set from the most recent actuals). |
| 111 # Weed out such cases. |
| 112 # EPOGER: make this optional? |
| 113 if expected_image == actual_image: |
| 114 continue |
| 115 |
| 116 # EPOGER: call re.match just once for both test and config? |
| 117 array.append({ |
| 118 "builder": builder, |
| 119 "test": IMAGE_FILENAME_RE.match(image_name).group(1), |
| 120 "config": IMAGE_FILENAME_RE.match(image_name).group(2), |
| 121 "actualHashType": actual_image[0], |
| 122 "actualHashDigest": str(actual_image[1]), |
| 123 "expectedHashType": expected_image[0], |
| 124 "expectedHashDigest": str(expected_image[1]), |
| 125 }) |
| 126 return array |
| 127 |
| 128 |
| 129 if __name__ == '__main__': |
| 130 # EPOGER: actual_root should point at a separate checkout of gm-actual |
| 131 # EPOGER: expected_root should point at a separately checked-out expectations/
gm workspace |
| 132 results = Results( |
| 133 actuals_root='/usr/local/google/home/epoger/src/rebaseline-mockup/gm-actua
l', |
| 134 expected_root=os.path.join(GM_DIRECTORY, os.pardir, 'expectations', 'gm')) |
| 135 for result_type in ['failed', 'failure-ignored', 'no-comparison']: |
| 136 gm_json.WriteToFile(results.OfType(result_type), os.path.join('EPOGER-result
s', result_type + '.json')) |
OLD | NEW |