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 |
| 11 class AutoBisectTestApi(recipe_test_api.RecipeTestApi): |
| 12 def buildbot_job_status_mock(self, bb_data_list): |
| 13 if bb_data_list: |
| 14 return bb_data_list.pop() |
| 15 return self.m.json.output_stream( |
| 16 {'build': { |
| 17 'result': 'SUCCESS', |
| 18 'status': 'COMPLETED'}}) |
| 19 |
| 20 def compare_samples_data(self, data, rev_a, rev_b): |
| 21 values_a = data[rev_a.commit_hash].get('parsed_values', [])[ |
| 22 :rev_a.test_run_count] |
| 23 values_b = data[rev_b.commit_hash].get('parsed_values', [])[ |
| 24 :rev_b.test_run_count] |
| 25 while len(values_a) < rev_a.test_run_count: |
| 26 if values_a: |
| 27 values_a.append(values_a[-1]) |
| 28 else: |
| 29 values_a.append(0) |
| 30 |
| 31 while len(values_b) < rev_b.test_run_count: |
| 32 if values_b: |
| 33 values_b.append(values_b[-1]) |
| 34 else: |
| 35 values_b.append(0) |
| 36 |
| 37 avg = lambda x: sum(x)/float((len(x) or 1)) |
| 38 mean_a = avg(values_a) |
| 39 mean_b = avg(values_b) |
| 40 var_a = map (lambda x: (x - mean_a) ** 2, values_a) |
| 41 var_b = map (lambda x: (x - mean_b) ** 2, values_b) |
| 42 std_dev_a = math.sqrt(avg(var_a)) |
| 43 std_dev_b = math.sqrt(avg(var_b)) |
| 44 result = 'needMoreData' |
| 45 if len(values_a) >= 5 and len(values_b) >= 5: |
| 46 if mean_a != mean_b: |
| 47 result = True |
| 48 # TODO(robertocn): Add a test that uses this clause. |
| 49 if len(values_a) >= 18 and len(values_b) >= 18: # pragma: no cover |
| 50 if mean_a == mean_b: |
| 51 result = False |
| 52 return self.m.json.output_stream( |
| 53 { |
| 54 'sample_a': { |
| 55 'debug_values': values_a, |
| 56 'mean': mean_a, |
| 57 'std_dev': std_dev_a |
| 58 }, |
| 59 'sample_b': { |
| 60 'debug_values': values_b, |
| 61 'mean': mean_b, |
| 62 'std_dev': std_dev_b, |
| 63 }, |
| 64 'result': result |
| 65 }) |
| 66 |
| 67 @recipe_test_api.mod_test_data |
| 68 def hash_cp_map(self, items): |
| 69 result = {} |
| 70 for item in items: |
| 71 if 'hash' in item and 'commit_pos' in item: |
| 72 result[item['commit_pos']] = self.m.json.output_stream( |
| 73 {'git_sha': item['hash']}) |
| 74 return result |
| 75 |
| 76 @recipe_test_api.mod_test_data |
| 77 @staticmethod |
| 78 def revision_data(items): |
| 79 result = {} |
| 80 for item in items: |
| 81 if 'hash' in item: |
| 82 result[item['hash']] = item |
| 83 return result |
| 84 |
| 85 @recipe_test_api.mod_test_data |
| 86 def revision_list(self, items): |
| 87 result = {} |
| 88 for item in items: |
| 89 if 'hash' in item: |
| 90 depot = item.get('depot', 'chromium') |
| 91 result.setdefault(depot, []) |
| 92 result[depot].append([item['hash'], item.get('commit_pos')]) |
| 93 # Exclude the start of the revision range. |
| 94 result['chromium'] = result['chromium'][1:] |
| 95 for depot in result: |
| 96 result[depot] = self.m.json.output_stream(result[depot]) |
| 97 return result |
| 98 |
| 99 @recipe_test_api.mod_test_data |
| 100 def deps_change(self, items): |
| 101 # If the revision has the key DEPS_change, we mock the result of git show to |
| 102 # appear as if DEPS was among the files changed by the CL. |
| 103 result = {} |
| 104 for item in items: |
| 105 if 'hash' in item: |
| 106 git_output = '' |
| 107 if 'DEPS_change' in item: |
| 108 git_output = 'DEPS' |
| 109 result[item['hash']] = self.m.raw_io.stream_output(git_output) |
| 110 return result |
| 111 |
| 112 @recipe_test_api.mod_test_data |
| 113 def diff_patch(self): |
| 114 return self.m.raw_io.stream_output(""" |
| 115 diff --git a/DEPS b/DEPS |
| 116 index 029be3b..2b3ea0a 100644 |
| 117 --- a/DEPS |
| 118 +++ b/DEPS |
| 119 @@ -13,7 +13,7 @@ deps = { |
| 120 '@98fc59a5896f4ea990a4d527548204fed8f06c64', |
| 121 'build/third_party/infra_libs': |
| 122 'https://chromium.googlesource.com/infra/infra/packages/infra_libs.git' |
| 123 - '@a13e6745a4edd01fee683e4157ea0195872e64eb', |
| 124 + '@15ea0920b5f83d0aff4bd042e95bc388d069d51c', |
| 125 'build/third_party/lighttpd': |
| 126 'https://chromium.googlesource.com/chromium/deps/lighttpd.git' |
| 127 '@9dfa55d15937a688a92cbf2b7a8621b0927d06eb', |
| 128 """) |
| 129 |
| 130 @recipe_test_api.mod_test_data |
| 131 def deps(self, items): |
| 132 result = {} |
| 133 for item in items: |
| 134 if 'hash' in item: |
| 135 deps_content = '' |
| 136 if 'DEPS' in item: |
| 137 deps_content = item['DEPS'] |
| 138 result[item['hash']] = self.m.raw_io.stream_output(deps_content) |
| 139 return result |
| 140 |
| 141 @recipe_test_api.placeholder_step_data |
| 142 @staticmethod |
| 143 def exists_result(exists=True): |
| 144 if exists: |
| 145 return 'GS location exists', 0, '' |
| 146 return 'GS location does not exist', 1, '' |
| 147 |
| 148 @recipe_test_api.mod_test_data |
| 149 def gsutil_exists(self, items): |
| 150 result = {} |
| 151 for item in items: |
| 152 if 'hash' in item and 'gsutil_exists' in item: |
| 153 result[item['hash']] = [self.exists_result(i) |
| 154 for i in item['gsutil_exists']] |
| 155 return result |
| 156 |
| 157 @recipe_test_api.mod_test_data |
| 158 def run_results(self, items): |
| 159 def single_result(v): |
| 160 return self.m.raw_io.stream_output( |
| 161 data=v.get('stdout', 'text from actual benchmark, (ignored)'), |
| 162 retcode=v.get('retcode', 0)) + self.m.raw_io.output( |
| 163 data=v.get('stdout', 'text from actual benchmark, (ignored)')) |
| 164 |
| 165 result = {'default': self.m.raw_io.stream_output('mock output', retcode=0)} |
| 166 for item in items: |
| 167 if 'hash' in item and 'test_results' in item: |
| 168 result[item['hash']] = [single_result(v) for v in item['test_results']] |
| 169 return result |
| 170 |
| 171 @recipe_test_api.mod_test_data |
| 172 def cl_info(self, items): |
| 173 result = {} |
| 174 for item in items: |
| 175 if 'hash' in item: |
| 176 if 'cl_info' in item: |
| 177 info = item['cl_info'] |
| 178 else: |
| 179 info = {} |
| 180 result[item['hash']] = self.m.json.output_stream(info) |
| 181 return result |
| 182 |
| 183 @recipe_test_api.mod_test_data |
| 184 def build_status(self, items): |
| 185 result = {} |
| 186 for item in items: |
| 187 if 'hash' in item and 'build_status' in item: |
| 188 result[item['hash']] = [] |
| 189 for entry in item['build_status']: |
| 190 result[item['hash']].append(self.m.json.output_stream(entry)) |
| 191 return result |
| 192 |
| 193 def __call__(self, config_items): |
| 194 return ( |
| 195 self.revision_data(config_items) |
| 196 + self.hash_cp_map(config_items) |
| 197 + self.revision_list(config_items) |
| 198 + self.run_results(config_items) |
| 199 + self.deps_change(config_items) |
| 200 + self.deps(config_items) |
| 201 + self.cl_info (config_items) |
| 202 + self.diff_patch() |
| 203 + self.gsutil_exists(config_items) |
| 204 + self.build_status(config_items) |
| 205 ) |
| 206 |
| 207 # """Takes massive dictionary to populate test_data for all steps.""" |
| 208 # get commit hash |
| 209 # get test results(gsutil) ? |
| 210 # fetch deps |
| 211 # generating patch |
| 212 # reading culprit information |
| 213 # expanding revision range |
| 214 # hashing modified deps |
| 215 # fetch builder state |
| 216 # fetch build details |
| 217 # (check image) gsutil |
| 218 # |
OLD | NEW |