| Index: scripts/slave/recipe_modules/auto_bisect/config_validation.py
|
| diff --git a/scripts/slave/recipe_modules/auto_bisect/config_validation.py b/scripts/slave/recipe_modules/auto_bisect/config_validation.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1e4ecfb77318aef22303e0b75c6800457c2e36ee
|
| --- /dev/null
|
| +++ b/scripts/slave/recipe_modules/auto_bisect/config_validation.py
|
| @@ -0,0 +1,110 @@
|
| +# Copyright 2016 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 re
|
| +
|
| +# Note: this module is tested by a unit test config_validation_test.py,
|
| +# rather than recipe simulation tests.
|
| +
|
| +_BISECT_CONFIG_SCHEMA = {
|
| + 'command': {'type': 'string', 'required': True},
|
| + 'good_revision': {'type': 'revision', 'required': True},
|
| + 'bad_revision': {'type': 'revision', 'required': True},
|
| + 'bisect_bot': {'type': 'string'},
|
| + 'metric': {'type': 'string'},
|
| + 'bug_id': {'type': 'integer'},
|
| + 'repeat_count': {'type': 'integer'},
|
| + 'max_time_minutes': {'type': 'integer'},
|
| + 'bisect_mode': {'type': 'string',
|
| + 'choices': ['mean', 'return_code', 'std_dev']},
|
| + 'gs_bucket': {'type': 'string'},
|
| + 'builder_host': {'type': 'string'},
|
| + 'builder_port': {'type': 'integer'},
|
| + 'test_type': {'type': 'string'},
|
| + 'improvement_direction': {'type': 'integer'},
|
| + 'recipe_tester_name': {'type': 'string'},
|
| + 'try_job_id': {'type': 'integer'},
|
| +}
|
| +
|
| +
|
| +class ValidationFail(Exception):
|
| + """An exception class that represents a failure to validate."""
|
| +
|
| +
|
| +def validate_bisect_config(config, schema=None):
|
| + """Checks the correctness of the given bisect job config."""
|
| + schema = _BISECT_CONFIG_SCHEMA if schema is None else schema
|
| + for key in set(schema):
|
| + validate_key(config, schema, key)
|
| +
|
| + if 'good_revision' in schema and 'bad_revision' in schema:
|
| + _validate_revisions(config.get('good_revision'), config.get('bad_revision'))
|
| +
|
| + if 'bisect_mode' in schema and 'metric' in schema:
|
| + _validate_metric(config.get('bisect_mode'), config.get('metric'))
|
| +
|
| +
|
| +def validate_key(config, schema, key): # pragma: no cover
|
| + """Checks the correctness of the given field in a config."""
|
| + if schema[key].get('required') and key not in config:
|
| + raise ValidationFail('Required key "%s" missing.' % key)
|
| + if key not in config:
|
| + return # Optional field.
|
| + value = config[key]
|
| + field_type = schema[key].get('type')
|
| + if field_type == 'string':
|
| + _validate_string(value, key)
|
| + elif field_type == 'integer':
|
| + _validate_integer(value, key)
|
| + elif field_type == 'revision':
|
| + _validate_revision(value, key)
|
| + elif field_type == 'boolean':
|
| + _validate_boolean(value, key)
|
| + if 'choices' in schema[key] and value not in schema[key]['choices']:
|
| + _fail(value, key)
|
| +
|
| +
|
| +def _fail(value, key):
|
| + raise ValidationFail('Invalid value %r for "%s".' % (value, key))
|
| +
|
| +
|
| +def _validate_string(value, key): # pragma: no cover
|
| + if not isinstance(value, basestring):
|
| + _fail(value, key)
|
| +
|
| +
|
| +def _validate_revision(value, key): # pragma: no cover
|
| + s = str(value)
|
| + if not (s.isdigit() or re.match('^[0-9A-Fa-f]{40}$', s)):
|
| + _fail(value, key)
|
| +
|
| +
|
| +def _validate_integer(value, key): # pragma: no cover
|
| + try:
|
| + int(value)
|
| + except ValueError:
|
| + _fail(value, key)
|
| +
|
| +
|
| +def _validate_boolean(value, key): # pragma: no cover
|
| + if value not in (True, False):
|
| + _fail(value, key)
|
| +
|
| +
|
| +def _validate_revisions(good_revision, bad_revision): # pragma: no cover
|
| + try:
|
| + earlier = int(good_revision)
|
| + later = int(bad_revision)
|
| + except ValueError:
|
| + return # The revisions could be sha1 hashes.
|
| + if earlier >= later:
|
| + raise ValidationFail('Order of good_revision (%d) and bad_revision(%d) '
|
| + 'is reversed.' % (earlier, later))
|
| +
|
| +
|
| +def _validate_metric(bisect_mode, metric): # pragma: no cover
|
| + if bisect_mode not in ('mean', 'std_dev'):
|
| + return
|
| + if not (isinstance(metric, basestring) and metric.count('/') == 1):
|
| + raise ValidationFail('Invalid value for "metric": %s' % metric)
|
|
|