| OLD | NEW |
| (Empty) | |
| 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 import re |
| 6 |
| 7 # Note: this module is tested by a unit test config_validation_test.py, |
| 8 # rather than recipe simulation tests. |
| 9 |
| 10 _BISECT_CONFIG_SCHEMA = { |
| 11 'command': {'type': 'string', 'required': True}, |
| 12 'good_revision': {'type': 'revision', 'required': True}, |
| 13 'bad_revision': {'type': 'revision', 'required': True}, |
| 14 'bisect_bot': {'type': 'string'}, |
| 15 'metric': {'type': 'string'}, |
| 16 'bug_id': {'type': 'integer'}, |
| 17 'repeat_count': {'type': 'integer'}, |
| 18 'max_time_minutes': {'type': 'integer'}, |
| 19 'bisect_mode': {'type': 'string', |
| 20 'choices': ['mean', 'return_code', 'std_dev']}, |
| 21 'gs_bucket': {'type': 'string'}, |
| 22 'builder_host': {'type': 'string'}, |
| 23 'builder_port': {'type': 'integer'}, |
| 24 'test_type': {'type': 'string'}, |
| 25 'improvement_direction': {'type': 'integer'}, |
| 26 'recipe_tester_name': {'type': 'string'}, |
| 27 'try_job_id': {'type': 'integer'}, |
| 28 } |
| 29 |
| 30 |
| 31 class ValidationFail(Exception): |
| 32 """An exception class that represents a failure to validate.""" |
| 33 |
| 34 |
| 35 def validate_bisect_config(config, schema=None): |
| 36 """Checks the correctness of the given bisect job config.""" |
| 37 schema = _BISECT_CONFIG_SCHEMA if schema is None else schema |
| 38 for key in set(schema): |
| 39 validate_key(config, schema, key) |
| 40 |
| 41 if 'good_revision' in schema and 'bad_revision' in schema: |
| 42 _validate_revisions(config.get('good_revision'), config.get('bad_revision')) |
| 43 |
| 44 if 'bisect_mode' in schema and 'metric' in schema: |
| 45 _validate_metric(config.get('bisect_mode'), config.get('metric')) |
| 46 |
| 47 |
| 48 def validate_key(config, schema, key): # pragma: no cover |
| 49 """Checks the correctness of the given field in a config.""" |
| 50 if schema[key].get('required') and key not in config: |
| 51 raise ValidationFail('Required key "%s" missing.' % key) |
| 52 if key not in config: |
| 53 return # Optional field. |
| 54 value = config[key] |
| 55 field_type = schema[key].get('type') |
| 56 if field_type == 'string': |
| 57 _validate_string(value, key) |
| 58 elif field_type == 'integer': |
| 59 _validate_integer(value, key) |
| 60 elif field_type == 'revision': |
| 61 _validate_revision(value, key) |
| 62 elif field_type == 'boolean': |
| 63 _validate_boolean(value, key) |
| 64 if 'choices' in schema[key] and value not in schema[key]['choices']: |
| 65 _fail(value, key) |
| 66 |
| 67 |
| 68 def _fail(value, key): |
| 69 raise ValidationFail('Invalid value %r for "%s".' % (value, key)) |
| 70 |
| 71 |
| 72 def _validate_string(value, key): # pragma: no cover |
| 73 if not isinstance(value, basestring): |
| 74 _fail(value, key) |
| 75 |
| 76 |
| 77 def _validate_revision(value, key): # pragma: no cover |
| 78 s = str(value) |
| 79 if not (s.isdigit() or re.match('^[0-9A-Fa-f]{40}$', s)): |
| 80 _fail(value, key) |
| 81 |
| 82 |
| 83 def _validate_integer(value, key): # pragma: no cover |
| 84 try: |
| 85 int(value) |
| 86 except ValueError: |
| 87 _fail(value, key) |
| 88 |
| 89 |
| 90 def _validate_boolean(value, key): # pragma: no cover |
| 91 if value not in (True, False): |
| 92 _fail(value, key) |
| 93 |
| 94 |
| 95 def _validate_revisions(good_revision, bad_revision): # pragma: no cover |
| 96 try: |
| 97 earlier = int(good_revision) |
| 98 later = int(bad_revision) |
| 99 except ValueError: |
| 100 return # The revisions could be sha1 hashes. |
| 101 if earlier >= later: |
| 102 raise ValidationFail('Order of good_revision (%d) and bad_revision(%d) ' |
| 103 'is reversed.' % (earlier, later)) |
| 104 |
| 105 |
| 106 def _validate_metric(bisect_mode, metric): # pragma: no cover |
| 107 if bisect_mode not in ('mean', 'std_dev'): |
| 108 return |
| 109 if not (isinstance(metric, basestring) and metric.count('/') == 1): |
| 110 raise ValidationFail('Invalid value for "metric": %s' % metric) |
| OLD | NEW |