Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 import math | |
| 5 | |
| 6 from collections import defaultdict | |
| 7 | |
| 8 from recipe_engine import recipe_test_api | |
| 9 | |
| 10 from auto_bisect import revision_state | |
| 11 | |
| 12 | |
| 13 class AutoBisectTestApi(recipe_test_api.RecipeTestApi): | |
| 14 def buildbot_job_status_mock(self, bb_data_list): | |
| 15 if bb_data_list: | |
| 16 return bb_data_list.pop() | |
| 17 return self.m.json.output_stream( | |
| 18 {'build': { | |
| 19 'result': 'SUCCESS', | |
| 20 'status': 'COMPLETED'}}) | |
| 21 | |
| 22 def compare_samples_data(self, data, rev_a, rev_b): | |
|
dtu
2016/09/13 23:57:28
This logic feels too complex for a test, and is in
RobertoCN
2016/09/21 22:22:48
Simplified it a lot, PTAL. Now any revision that h
| |
| 23 values_a = data[rev_a.commit_hash].get('parsed_values', [])[ | |
| 24 :rev_a.test_run_count] | |
| 25 values_b = data[rev_b.commit_hash].get('parsed_values', [])[ | |
| 26 :rev_b.test_run_count] | |
| 27 while len(values_a) < rev_a.test_run_count: | |
| 28 if values_a: | |
| 29 values_a.append(values_a[-1]) | |
| 30 else: | |
| 31 values_a.append(0) | |
| 32 | |
| 33 while len(values_b) < rev_b.test_run_count: | |
| 34 if values_b: | |
| 35 values_b.append(values_b[-1]) | |
| 36 else: | |
| 37 values_b.append(0) | |
| 38 | |
| 39 avg = lambda x: sum(x)/float((len(x) or 1)) | |
| 40 mean_a = avg(values_a) | |
| 41 mean_b = avg(values_b) | |
| 42 var_a = map (lambda x: (x - mean_a) ** 2, values_a) | |
| 43 var_b = map (lambda x: (x - mean_b) ** 2, values_b) | |
| 44 std_dev_a = math.sqrt(avg(var_a)) | |
| 45 std_dev_b = math.sqrt(avg(var_b)) | |
| 46 result = revision_state.NEED_MORE_DATA | |
| 47 if len(values_a) >= 5 and len(values_b) >= 5: | |
| 48 if mean_a != mean_b: | |
| 49 result = revision_state.SIGNIFICANTLY_DIFFERENT | |
| 50 # TODO(robertocn): Add a test that uses this clause. | |
| 51 if len(values_a) >= 18 and len(values_b) >= 18: # pragma: no cover | |
| 52 if mean_a == mean_b: | |
| 53 result = revision_state.NOT_SIGNIFICANTLY_DIFFERENT | |
| 54 return self.m.json.output_stream( | |
| 55 { | |
| 56 'sample_a': { | |
| 57 'debug_values': values_a, | |
| 58 'mean': mean_a, | |
| 59 'std_dev': std_dev_a | |
| 60 }, | |
| 61 'sample_b': { | |
| 62 'debug_values': values_b, | |
| 63 'mean': mean_b, | |
| 64 'std_dev': std_dev_b, | |
| 65 }, | |
| 66 'result': result | |
| 67 }) | |
| 68 | |
| 69 @recipe_test_api.mod_test_data | |
| 70 def hash_cp_map(self, items): | |
| 71 result = {} | |
| 72 for item in items: | |
| 73 if 'hash' in item and 'commit_pos' in item: | |
|
dtu
2016/09/13 23:57:28
Is there ever a case where 'hash' (and/or 'commit_
RobertoCN
2016/09/21 22:22:49
'hash' will always be there, 'commit_pos' may not.
| |
| 74 result[item['commit_pos']] = self.m.json.output_stream( | |
| 75 {'git_sha': item['hash']}) | |
| 76 return result | |
| 77 | |
| 78 @recipe_test_api.mod_test_data | |
| 79 @staticmethod | |
| 80 def revision_data(items): | |
|
dtu
2016/09/13 23:57:28
As far as I can tell, this is only used in one pla
RobertoCN
2016/09/21 22:22:49
compare_sample_data cannot look up data on the con
| |
| 81 result = {} | |
| 82 for item in items: | |
| 83 if 'hash' in item: | |
| 84 result[item['hash']] = item | |
| 85 return result | |
| 86 | |
| 87 @recipe_test_api.mod_test_data | |
| 88 def revision_list(self, items): | |
| 89 result = {} | |
| 90 for item in items: | |
| 91 if 'hash' in item: | |
| 92 depot = item.get('depot', 'chromium') | |
| 93 result.setdefault(depot, []) | |
| 94 result[depot].append([item['hash'], item.get('commit_pos')]) | |
| 95 # Exclude the start of the revision range. | |
| 96 result['chromium'] = result['chromium'][1:] | |
|
dtu
2016/09/13 23:57:28
Special-casing chromium is weird. Should this appl
RobertoCN
2016/09/21 22:22:48
We special case the topmost repo because the last
| |
| 97 for depot in result: | |
| 98 result[depot] = self.m.json.output_stream(result[depot]) | |
| 99 return result | |
| 100 | |
| 101 @recipe_test_api.mod_test_data | |
| 102 def deps_change(self, items): | |
| 103 # If the revision has the key DEPS_change, we mock the result of git show to | |
| 104 # appear as if DEPS was among the files changed by the CL. | |
| 105 result = {} | |
| 106 for item in items: | |
| 107 if 'hash' in item: | |
| 108 git_output = '' | |
| 109 if 'DEPS_change' in item: | |
| 110 git_output = 'DEPS' | |
| 111 result[item['hash']] = self.m.raw_io.stream_output(git_output) | |
|
dtu
2016/09/13 23:57:28
This line assumes 'hash' is in item, whereas the p
RobertoCN
2016/09/21 22:22:48
Done.
| |
| 112 return result | |
| 113 | |
| 114 @recipe_test_api.mod_test_data | |
| 115 def diff_patch(self): | |
| 116 return self.m.raw_io.stream_output(""" | |
| 117 diff --git a/DEPS b/DEPS | |
| 118 index 029be3b..2b3ea0a 100644 | |
| 119 --- a/DEPS | |
| 120 +++ b/DEPS | |
| 121 @@ -13,7 +13,7 @@ deps = { | |
| 122 '@98fc59a5896f4ea990a4d527548204fed8f06c64', | |
| 123 'build/third_party/infra_libs': | |
| 124 'https://chromium.googlesource.com/infra/infra/packages/infra_libs.git' | |
| 125 - '@a13e6745a4edd01fee683e4157ea0195872e64eb', | |
| 126 + '@15ea0920b5f83d0aff4bd042e95bc388d069d51c', | |
| 127 'build/third_party/lighttpd': | |
| 128 'https://chromium.googlesource.com/chromium/deps/lighttpd.git' | |
| 129 '@9dfa55d15937a688a92cbf2b7a8621b0927d06eb', | |
| 130 """) | |
| 131 | |
| 132 @recipe_test_api.mod_test_data | |
| 133 def deps(self, items): | |
| 134 result = {} | |
| 135 for item in items: | |
| 136 if 'hash' in item: | |
| 137 deps_content = '' | |
| 138 if 'DEPS' in item: | |
| 139 deps_content = item['DEPS'] | |
| 140 result[item['hash']] = self.m.raw_io.stream_output(deps_content) | |
| 141 return result | |
| 142 | |
| 143 @recipe_test_api.placeholder_step_data | |
| 144 @staticmethod | |
| 145 def exists_result(exists=True): | |
|
dtu
2016/09/13 23:57:28
Make protected or private.
style guide: Don't use
RobertoCN
2016/09/21 22:22:49
https://cs.chromium.org/search/?q=@staticmethod+fi
| |
| 146 if exists: | |
| 147 return 'GS location exists', 0, '' | |
| 148 return 'GS location does not exist', 1, '' | |
|
dtu
2016/09/13 23:57:28
Sorry, I can't figure out why this is the return v
RobertoCN
2016/09/21 22:22:49
the placeholder_step_data decorator converts this
| |
| 149 | |
| 150 @recipe_test_api.mod_test_data | |
| 151 def gsutil_exists(self, items): | |
| 152 result = {} | |
| 153 for item in items: | |
| 154 if 'hash' in item and 'gsutil_exists' in item: | |
| 155 result[item['hash']] = [self.exists_result(i) | |
| 156 for i in item['gsutil_exists']] | |
|
dtu
2016/09/13 23:57:28
Does it need to be a list? I don't see any case wh
RobertoCN
2016/09/21 22:22:49
We need to be able to return false for a number of
| |
| 157 return result | |
| 158 | |
| 159 @recipe_test_api.mod_test_data | |
| 160 def run_results(self, items): | |
| 161 def single_result(v): | |
| 162 return self.m.raw_io.stream_output( | |
| 163 data=v.get('stdout', 'text from actual benchmark, (ignored)'), | |
| 164 retcode=v.get('retcode', 0)) + self.m.raw_io.output( | |
| 165 data=v.get('stdout', 'text from actual benchmark, (ignored)')) | |
|
dtu
2016/09/13 23:57:28
nit: Can you assign v.get(...) to a variable and r
RobertoCN
2016/09/21 22:22:49
Done.
| |
| 166 | |
| 167 result = {'default': self.m.raw_io.stream_output('mock output', retcode=0)} | |
| 168 for item in items: | |
| 169 if 'hash' in item and 'test_results' in item: | |
| 170 result[item['hash']] = [single_result(v) for v in item['test_results']] | |
| 171 return result | |
| 172 | |
| 173 @recipe_test_api.mod_test_data | |
| 174 def cl_info(self, items): | |
| 175 result = {} | |
| 176 for item in items: | |
| 177 if 'hash' in item: | |
| 178 if 'cl_info' in item: | |
|
dtu
2016/09/13 23:57:28
info = item.get('cl_info', {})
RobertoCN
2016/09/21 22:22:48
Done.
| |
| 179 info = item['cl_info'] | |
| 180 else: | |
| 181 info = {} | |
| 182 result[item['hash']] = self.m.json.output_stream(info) | |
| 183 return result | |
| 184 | |
| 185 @recipe_test_api.mod_test_data | |
| 186 def build_status(self, items): | |
| 187 result = {} | |
| 188 for item in items: | |
| 189 if 'hash' in item and 'build_status' in item: | |
| 190 result[item['hash']] = [] | |
| 191 for entry in item['build_status']: | |
| 192 result[item['hash']].append(self.m.json.output_stream(entry)) | |
| 193 return result | |
| 194 | |
| 195 def __call__(self, config_items): | |
| 196 return ( | |
| 197 self.revision_data(config_items) | |
| 198 + self.hash_cp_map(config_items) | |
| 199 + self.revision_list(config_items) | |
| 200 + self.run_results(config_items) | |
| 201 + self.deps_change(config_items) | |
| 202 + self.deps(config_items) | |
| 203 + self.cl_info (config_items) | |
| 204 + self.diff_patch() | |
| 205 + self.gsutil_exists(config_items) | |
| 206 + self.build_status(config_items) | |
| 207 ) | |
| 208 | |
| 209 # """Takes massive dictionary to populate test_data for all steps.""" | |
| 210 # get commit hash | |
| 211 # get test results(gsutil) ? | |
| 212 # fetch deps | |
| 213 # generating patch | |
| 214 # reading culprit information | |
| 215 # expanding revision range | |
| 216 # hashing modified deps | |
| 217 # fetch builder state | |
| 218 # fetch build details | |
| 219 # (check image) gsutil | |
| 220 # | |
| OLD | NEW |