| Index: scripts/slave/recipe_modules/auto_bisect/test_api.py
|
| diff --git a/scripts/slave/recipe_modules/auto_bisect/test_api.py b/scripts/slave/recipe_modules/auto_bisect/test_api.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e113005976f9d8dee662ada9c1e70c1a86d7c56a
|
| --- /dev/null
|
| +++ b/scripts/slave/recipe_modules/auto_bisect/test_api.py
|
| @@ -0,0 +1,179 @@
|
| +# Copyright 2015 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +import math
|
| +
|
| +from collections import defaultdict
|
| +
|
| +from recipe_engine import recipe_test_api
|
| +
|
| +
|
| +class AutoBisectTestApi(recipe_test_api.RecipeTestApi):
|
| +
|
| + def compare_samples_data(self, data, rev_a, rev_b):
|
| + values_a = data[rev_a.commit_hash]['parsed_values'][:rev_a.test_run_count]
|
| + values_b = data[rev_b.commit_hash]['parsed_values'][:rev_b.test_run_count]
|
| + while len(values_a) < rev_a.test_run_count:
|
| + if values_a:
|
| + values_a.append(values_a[-1])
|
| + else:
|
| + values_a.append(0)
|
| +
|
| + while len(values_b) < rev_b.test_run_count:
|
| + if values_b:
|
| + values_b.append(values_b[-1])
|
| + else:
|
| + values_b.append(0)
|
| +
|
| + avg = lambda x: sum(x)/float((len(x) or 1))
|
| + mean_a = avg(values_a)
|
| + mean_b = avg(values_b)
|
| + var_a = map (lambda x: (x - mean_a) ** 2, values_a)
|
| + var_b = map (lambda x: (x - mean_b) ** 2, values_b)
|
| + std_dev_a = math.sqrt(avg(var_a))
|
| + std_dev_b = math.sqrt(avg(var_b))
|
| + result = 'needMoreData'
|
| + if len(values_a) >= 5 and len(values_b) >= 5:
|
| + if mean_a != mean_b:
|
| + result = True
|
| + if len(values_a) >= 18 and len(values_b) >= 18:
|
| + if mean_a == mean_b:
|
| + result = False
|
| + return self.m.json.output_stream(
|
| + {
|
| + 'sample_a': {
|
| + 'debug_values': values_a,
|
| + 'mean': mean_a,
|
| + 'std_dev': std_dev_a
|
| + },
|
| + 'sample_b': {
|
| + 'debug_values': values_b,
|
| + 'mean': mean_b,
|
| + 'std_dev': std_dev_b,
|
| + },
|
| + 'result': result
|
| + })
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + def hash_cp_map(self, items):
|
| + result = {}
|
| + for item in items:
|
| + if 'hash' in item and 'commit_pos' in item:
|
| + result[item['commit_pos']] = self.m.json.output_stream(
|
| + {'git_sha': item['hash']})
|
| + return result
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + @staticmethod
|
| + def revision_data(items):
|
| + result = {}
|
| + for item in items:
|
| + if 'hash' in item:
|
| + result[item['hash']] = item
|
| + return result
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + def revision_list(self, items):
|
| + result = {}
|
| + for item in items:
|
| + if 'hash' in item:
|
| + depot = item.get('depot', 'chromium')
|
| + result.setdefault(depot, [])
|
| + result[depot].append([item['hash'], item.get('commit_pos')])
|
| + # Exclude the ends of the revision range.
|
| + result['chromium'] = result['chromium'][1:-1]
|
| + for depot in result:
|
| + result[depot] = self.m.json.output_stream(result[depot])
|
| + return result
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + def deps_change(self, items):
|
| + # If the revision has the key DEPS_change, we mock the result of git show to
|
| + # appear as if DEPS was among the files changed by the CL.
|
| + result = {}
|
| + for item in items:
|
| + if 'hash' in item:
|
| + git_output = ''
|
| + if 'DEPS_change' in item:
|
| + git_output = 'DEPS'
|
| + result[item['hash']] = self.m.raw_io.stream_output(git_output)
|
| + return result
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + def diff_patch(self):
|
| + return self.m.raw_io.stream_output("""
|
| +diff --git a/DEPS b/DEPS
|
| +index 029be3b..2b3ea0a 100644
|
| +--- a/DEPS
|
| ++++ b/DEPS
|
| +@@ -13,7 +13,7 @@ deps = {
|
| + '@98fc59a5896f4ea990a4d527548204fed8f06c64',
|
| + 'build/third_party/infra_libs':
|
| + 'https://chromium.googlesource.com/infra/infra/packages/infra_libs.git'
|
| +- '@a13e6745a4edd01fee683e4157ea0195872e64eb',
|
| ++ '@15ea0920b5f83d0aff4bd042e95bc388d069d51c',
|
| + 'build/third_party/lighttpd':
|
| + 'https://chromium.googlesource.com/chromium/deps/lighttpd.git'
|
| + '@9dfa55d15937a688a92cbf2b7a8621b0927d06eb',
|
| + """)
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + def deps(self, items):
|
| + result = {}
|
| + for item in items:
|
| + if 'hash' in item:
|
| + deps_content = ''
|
| + if 'DEPS' in item:
|
| + deps_content = item['DEPS']
|
| + result[item['hash']] = self.m.raw_io.stream_output(deps_content)
|
| + return result
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + def run_results(self, items):
|
| + def single_result(v):
|
| + return self.m.raw_io.stream_output(
|
| + data = v.get('stdout', 'text from actual benchmark, (ignored)'),
|
| + retcode = v.get('retcode', 0))
|
| +
|
| + result = {'default': recipe_test_api.StepTestData()}
|
| + for item in items:
|
| + if 'hash' in item and 'test_results' in item:
|
| + result[item['hash']] = [single_result(v) for v in item['test_results']]
|
| + return result
|
| +
|
| + @recipe_test_api.mod_test_data
|
| + def cl_info(self, items):
|
| + result = {}
|
| + for item in items:
|
| + if 'hash' in item:
|
| + if 'cl_info' in item:
|
| + info = item['cl_info']
|
| + else:
|
| + info = {}
|
| + result[item['hash']] = self.m.json.output_stream(info)
|
| + return result
|
| +
|
| + def __call__(self, config_items):
|
| + return (
|
| + self.revision_data(config_items)
|
| + + self.hash_cp_map(config_items)
|
| + + self.revision_list(config_items)
|
| + + self.run_results(config_items)
|
| + + self.deps_change(config_items)
|
| + + self.deps(config_items)
|
| + + self.cl_info (config_items)
|
| + + self.diff_patch()
|
| + )
|
| +
|
| +# """Takes massive dictionary to populate test_data for all steps."""
|
| +# get commit hash
|
| +# get test results(gsutil) ?
|
| +# fetch deps
|
| +# generating patch
|
| +# reading culprit information
|
| +# expanding revision range
|
| +# hashing modified deps
|
| +# fetch builder state
|
| +# fetch build details
|
| +# (check image) gsutil
|
| +#
|
|
|