OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 ''' | 6 ''' |
7 Checks a policy_templates.json file for conformity to its syntax specification. | 7 Checks a policy_templates.json file for conformity to its syntax specification. |
8 ''' | 8 ''' |
9 | 9 |
10 import json | 10 import json |
11 import optparse | 11 import optparse |
12 import os | 12 import os |
13 import re | 13 import re |
14 import sys | 14 import sys |
15 | 15 |
16 | 16 |
17 LEADING_WHITESPACE = re.compile('^([ \t]*)') | 17 LEADING_WHITESPACE = re.compile('^([ \t]*)') |
18 TRAILING_WHITESPACE = re.compile('.*?([ \t]+)$') | 18 TRAILING_WHITESPACE = re.compile('.*?([ \t]+)$') |
19 # Matches all non-empty strings that contain no whitespaces. | 19 # Matches all non-empty strings that contain no whitespaces. |
20 NO_WHITESPACE = re.compile('[^\s]+$') | 20 NO_WHITESPACE = re.compile('[^\s]+$') |
21 | 21 |
| 22 # Convert a 'type' to its corresponding schema type. |
| 23 TYPE_TO_SCHEMA = { |
| 24 'int': 'integer', |
| 25 'list': 'array', |
| 26 'dict': 'object', |
| 27 'main': 'boolean', |
| 28 'string': 'string', |
| 29 'int-enum': 'integer', |
| 30 'string-enum': 'string', |
| 31 } |
22 | 32 |
23 class PolicyTemplateChecker(object): | 33 class PolicyTemplateChecker(object): |
24 | 34 |
25 def __init__(self): | 35 def __init__(self): |
26 self.error_count = 0 | 36 self.error_count = 0 |
27 self.warning_count = 0 | 37 self.warning_count = 0 |
28 self.num_policies = 0 | 38 self.num_policies = 0 |
29 self.num_groups = 0 | 39 self.num_groups = 0 |
30 self.num_policies_in_groups = 0 | 40 self.num_policies_in_groups = 0 |
31 self.options = None | 41 self.options = None |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 ''' | 115 ''' |
106 Checks a set of policy_ids to make sure it contains a continuous range | 116 Checks a set of policy_ids to make sure it contains a continuous range |
107 of entries (i.e. no holes). | 117 of entries (i.e. no holes). |
108 Holes would not be a technical problem, but we want to ensure that nobody | 118 Holes would not be a technical problem, but we want to ensure that nobody |
109 accidentally omits IDs. | 119 accidentally omits IDs. |
110 ''' | 120 ''' |
111 for i in range(len(policy_ids)): | 121 for i in range(len(policy_ids)): |
112 if (i + 1) not in policy_ids: | 122 if (i + 1) not in policy_ids: |
113 self._Error('No policy with id: %s' % (i + 1)) | 123 self._Error('No policy with id: %s' % (i + 1)) |
114 | 124 |
| 125 def _CheckPolicySchema(self, policy, policy_type): |
| 126 '''Checks that the 'schema' field matches the 'type' field.''' |
| 127 self._CheckContains(policy, 'schema', dict) |
| 128 if isinstance(policy.get('schema'), dict): |
| 129 self._CheckContains(policy['schema'], 'type', str) |
| 130 if policy['schema'].get('type') != TYPE_TO_SCHEMA[policy_type]: |
| 131 self._Error('Schema type must match the existing type for policy %s' % |
| 132 policy.get('name')) |
| 133 |
115 def _CheckPolicy(self, policy, is_in_group, policy_ids): | 134 def _CheckPolicy(self, policy, is_in_group, policy_ids): |
116 if not isinstance(policy, dict): | 135 if not isinstance(policy, dict): |
117 self._Error('Each policy must be a dictionary.', 'policy', None, policy) | 136 self._Error('Each policy must be a dictionary.', 'policy', None, policy) |
118 return | 137 return |
119 | 138 |
120 # There should not be any unknown keys in |policy|. | 139 # There should not be any unknown keys in |policy|. |
121 for key in policy: | 140 for key in policy: |
122 if key not in ('name', 'type', 'caption', 'desc', 'device_only', | 141 if key not in ('name', 'type', 'caption', 'desc', 'device_only', |
123 'supported_on', 'label', 'policies', 'items', | 142 'supported_on', 'label', 'policies', 'items', |
124 'example_value', 'features', 'deprecated', 'future', | 143 'example_value', 'features', 'deprecated', 'future', |
125 'id'): | 144 'id', 'schema'): |
126 self.warning_count += 1 | 145 self.warning_count += 1 |
127 print ('In policy %s: Warning: Unknown key: %s' % | 146 print ('In policy %s: Warning: Unknown key: %s' % |
128 (policy.get('name'), key)) | 147 (policy.get('name'), key)) |
129 | 148 |
130 # Each policy must have a name. | 149 # Each policy must have a name. |
131 self._CheckContains(policy, 'name', str, regexp_check=NO_WHITESPACE) | 150 self._CheckContains(policy, 'name', str, regexp_check=NO_WHITESPACE) |
132 | 151 |
133 # Each policy must have a type. | 152 # Each policy must have a type. |
134 policy_type = self._CheckContains(policy, 'type', str) | 153 policy_type = self._CheckContains(policy, 'type', str) |
135 if policy_type not in ('group', 'main', 'string', 'int', 'list', 'int-enum', | 154 if policy_type not in ('group', 'main', 'string', 'int', 'list', 'int-enum', |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 'policy', policy) | 193 'policy', policy) |
175 | 194 |
176 # Statistics. | 195 # Statistics. |
177 self.num_groups += 1 | 196 self.num_groups += 1 |
178 else: # policy_type != group | 197 else: # policy_type != group |
179 | 198 |
180 # Each policy must have a protobuf ID. | 199 # Each policy must have a protobuf ID. |
181 id = self._CheckContains(policy, 'id', int) | 200 id = self._CheckContains(policy, 'id', int) |
182 self._AddPolicyID(id, policy_ids, policy) | 201 self._AddPolicyID(id, policy_ids, policy) |
183 | 202 |
| 203 # 'schema' is the new 'type'. |
| 204 # TODO(joaodasilva): remove the 'type' checks once 'schema' is used |
| 205 # everywhere. |
| 206 self._CheckPolicySchema(policy, policy_type) |
| 207 |
184 # Each policy must have a supported_on list. | 208 # Each policy must have a supported_on list. |
185 supported_on = self._CheckContains(policy, 'supported_on', list) | 209 supported_on = self._CheckContains(policy, 'supported_on', list) |
186 if supported_on is not None: | 210 if supported_on is not None: |
187 for s in supported_on: | 211 for s in supported_on: |
188 if not isinstance(s, str): | 212 if not isinstance(s, str): |
189 self._Error('Entries in "supported_on" must be strings.', 'policy', | 213 self._Error('Entries in "supported_on" must be strings.', 'policy', |
190 policy, supported_on) | 214 policy, supported_on) |
191 | 215 |
192 # Each policy must have a 'features' dict. | 216 # Each policy must have a 'features' dict. |
193 self._CheckContains(policy, 'features', dict) | 217 self._CheckContains(policy, 'features', dict) |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 if filename is None: | 434 if filename is None: |
411 if len(args) != 2: | 435 if len(args) != 2: |
412 parser.print_help() | 436 parser.print_help() |
413 sys.exit(1) | 437 sys.exit(1) |
414 filename = args[1] | 438 filename = args[1] |
415 return self.Main(filename, options) | 439 return self.Main(filename, options) |
416 | 440 |
417 | 441 |
418 if __name__ == '__main__': | 442 if __name__ == '__main__': |
419 sys.exit(PolicyTemplateChecker().Run(sys.argv)) | 443 sys.exit(PolicyTemplateChecker().Run(sys.argv)) |
OLD | NEW |