Chromium Code Reviews| Index: tools/isolate/run_test_from_archive.py |
| diff --git a/tools/isolate/run_test_from_archive.py b/tools/isolate/run_test_from_archive.py |
| index 7710a6ed7059e7391b29b50e260c338a79ac9031..023a0df779f889fef65138f320a92eedeabda4e1 100755 |
| --- a/tools/isolate/run_test_from_archive.py |
| +++ b/tools/isolate/run_test_from_archive.py |
| @@ -26,6 +26,13 @@ import urllib |
| # Types of action accepted by recreate_tree(). |
| HARDLINK, SYMLINK, COPY = range(1, 4) |
| +RE_IS_SHA1 = re.compile(r'^[a-fA-F0-9]{40}$') |
| + |
| + |
| +class ConfigError(ValueError): |
| + """Generic failure to load a manifest.""" |
| + pass |
| + |
| class MappingError(OSError): |
| """Failed to recreate the tree.""" |
| @@ -172,6 +179,101 @@ def make_temp_dir(prefix, root_dir): |
| return tempfile.mkdtemp(prefix=prefix, dir=base_temp_dir) |
| +def assert_is_str(value): |
| + if not isinstance(value, basestring): |
| + raise ConfigError('Expected string, got %r' % value) |
| + |
| + |
| +def assert_is_int(value): |
| + if not isinstance(value, int): |
| + raise ConfigError('Expected int, got %r' % value) |
| + |
| + |
| +def assert_is_bool(value): |
| + if value not in (None, True, False): |
| + raise ConfigError('Expected bool, got %r' % value) |
| + |
| + |
| +def assert_is_sha1(value): |
| + assert_is_str(value) |
| + if not RE_IS_SHA1.match(value): |
| + raise ConfigError('Expected sha-1, got %r' % value) |
| + |
| + |
| +def get_assert_is_list(check): |
| + """Returns a function that asserts the value is a list.""" |
| + def is_list_item(value): |
| + if not isinstance(value, list): |
| + raise ConfigError('Expected list, got %r' % value) |
| + any(check(item) for item in value) |
| + return is_list_item |
| + |
| + |
| +def get_assert_is_dict(key_check, values_check): |
| + """Returns a function that ensure the value is a dict. |
| + |
| + It checks all the keys passes key_check and values_check is a dict of each |
| + checks for each key. |
| + |
| + If values_check contains a None entry, keys not in values_check will be |
| + checked with this key. If None is not present in values_check, unknown keys |
| + will be refused. |
| + """ |
| + def is_dict_item(value): |
| + if not isinstance(value, dict): |
| + raise ConfigError('Expected dict, got %r' % value) |
| + |
| + for key in value: |
| + key_check(key) |
| + |
| + # values_check is the set of valid keys. |
| + unknown = set(value) - set(values_check) |
| + if unknown and not None in values_check: |
| + raise ConfigError( |
| + 'Got unknown key(s) %s in %r; expected only %s' % |
| + (', '.join(unknown), value, ', '.join(values_check))) |
| + for k, v in value.iteritems(): |
| + if k in values_check: |
| + values_check[k](v) |
| + else: |
| + values_check[None](v) |
| + return is_dict_item |
| + |
| + |
| +def load_manifest(content): |
| + """Verifies the manifest is valid and loads this object with the json data. |
| + """ |
| + data = json.loads(content) |
| + |
| + file_keys = { |
| + 'link': assert_is_str, |
| + 'mode': assert_is_int, |
| + 'sha-1': assert_is_sha1, |
| + 'timestamp': assert_is_int, |
| + } |
| + |
| + files_key = { |
| + # Any file has to fit files_keys specification. |
| + None: get_assert_is_dict(assert_is_str, file_keys), |
| + } |
| + |
| + keys = { |
| + 'command': get_assert_is_list(assert_is_str), |
| + # Could use assert_is_valid_filename instead of assert_is_str. |
| + 'files': get_assert_is_dict(assert_is_str, files_key), |
| + 'read_only': assert_is_bool, |
| + 'relative_cwd': assert_is_str, |
| + } |
| + |
| + get_assert_is_dict(assert_is_str, keys)(data) |
|
cmp
2012/08/28 17:38:29
this code requires a lot of state to be in-mind to
|
| + # Add a special verification. |
| + for value in data.get('files', {}).itervalues(): |
| + if bool('sha-1' in value) and bool('link' in value): |
| + raise ConfigError( |
| + 'Did not expect both \'sha-1\' and \'link\', got: %r' % value) |
| + return data |
| + |
| + |
| def fix_python_path(cmd): |
| """Returns the fixed command line to call the right python executable.""" |
| out = cmd[:] |
| @@ -393,7 +495,7 @@ def run_tha_test(manifest, cache_dir, remote, policies): |
| # A symlink. |
| os.symlink(properties['link'], outfile) |
| else: |
| - raise ValueError('Unexpected entry: %s' % properties) |
| + raise ConfigError('Unexpected entry: %s' % properties) |
| if 'mode' in properties: |
| # It's not set on Windows. |
| os.chmod(outfile, properties['mode']) |
| @@ -482,7 +584,7 @@ def main(): |
| # First calculate the reference to it. |
| options.manifest = '%s/%s' % (options.remote.rstrip('/'), options.hash) |
| try: |
| - manifest = json.load(open_remote(options.manifest)) |
| + manifest = load_manifest(open_remote(options.manifest).read()) |
| except IOError as e: |
| parser.error( |
| 'Failed to read manifest %s; remote:%s; hash:%s; %s' % |
| @@ -496,7 +598,7 @@ def main(): |
| os.path.abspath(options.cache), |
| options.remote, |
| policies) |
| - except MappingError, e: |
| + except (ConfigError, MappingError), e: |
| print >> sys.stderr, str(e) |
| return 1 |