1# Copyright 2016 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
5"""A handler and functions to check whether bisect is supported."""
6
7import re
8
9from dashboard import request_handler
10from dashboard import namespaced_stored_object
11
12# A set of suites for which we can't do performance bisects.
13# This list currently also exists in the front-end code.
14_UNBISECTABLE_SUITES = [
15    'arc-perf-test',
16    'browser_tests',
17    'content_browsertests',
18    'resource_sizes',
19    'sizes',
20    'v8',
21]
22
23# The bisect bot map stored in datastore is expected to be
24# a dict mapping master names to [perf bot, bisect bot] pairs.
25# If a master name is not in the dict, bisect isn't supported.
26BISECT_BOT_MAP_KEY = 'bisect_bot_map'
27
28
29class CanBisectHandler(request_handler.RequestHandler):
30
31  def post(self):
32    """Checks whether bisect is supported for a test.
33
34    Request parameters:
35      test_path: A full test path (Master/bot/benchmark/...)
36      start_revision: The start of the bisect revision range.
37      end_revision: The end of the bisect revision range.
38
39    Outputs: The string "true" or the string "false".
40    """
41    can_bisect = (
42        IsValidTestForBisect(self.request.get('test_path')) and
43        IsValidRevisionForBisect(self.request.get('start_revision')) and
44        IsValidRevisionForBisect(self.request.get('end_revision')))
45    self.response.write('true' if can_bisect else 'false')
46
47
48def IsValidTestForBisect(test_path):
49  """Checks whether a test is valid for bisect."""
50  if not test_path:
51    return False
52  path_parts = test_path.split('/')
53  if len(path_parts) < 3:
54    return False
55  if not _MasterNameIsWhitelisted(path_parts[0]):
56    return False
57  if path_parts[2] in _UNBISECTABLE_SUITES:
58    return False
59  if test_path.endswith('/ref') or test_path.endswith('_ref'):
60    return False
61  return True
62
63
64def _MasterNameIsWhitelisted(master_name):
65  """Checks whether a master name is acceptable by checking a whitelist."""
66  bisect_bot_map = namespaced_stored_object.Get(BISECT_BOT_MAP_KEY)
67  if not bisect_bot_map:
68    return True  # If there's no list available, all names are OK.
69  whitelisted_masters = list(bisect_bot_map)
70  return master_name in whitelisted_masters
71
72
73def IsValidRevisionForBisect(revision):
74  """Checks whether a revision looks like a valid revision for bisect."""
75  return _IsGitHash(revision) or re.match(r'^[0-9]{5,7}$', str(revision))
76
77
78def _IsGitHash(revision):
79  """Checks whether the input looks like a SHA1 hash."""
80  return re.match(r'[a-fA-F0-9]{40}$', str(revision))
81