OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 Google Inc. All rights reserved. | 2 # Copyright (c) 2011 Google Inc. All rights reserved. |
3 # | 3 # |
4 # Redistribution and use in source and binary forms, with or without | 4 # Redistribution and use in source and binary forms, with or without |
5 # modification, are permitted provided that the following conditions are | 5 # modification, are permitted provided that the following conditions are |
6 # met: | 6 # met: |
7 # | 7 # |
8 # * Redistributions of source code must retain the above copyright | 8 # * Redistributions of source code must retain the above copyright |
9 # notice, this list of conditions and the following disclaimer. | 9 # notice, this list of conditions and the following disclaimer. |
10 # * Redistributions in binary form must reproduce the above | 10 # * Redistributions in binary form must reproduce the above |
(...skipping 27 matching lines...) Expand all Loading... |
38 # - Required response parameter was removed or changed to optional | 38 # - Required response parameter was removed or changed to optional |
39 # - Event has been removed | 39 # - Event has been removed |
40 # - Required event parameter was removed or changed to optional | 40 # - Required event parameter was removed or changed to optional |
41 # - Parameter type has changed. | 41 # - Parameter type has changed. |
42 # | 42 # |
43 # For the parameters with composite types the above checks are also applied | 43 # For the parameters with composite types the above checks are also applied |
44 # recursively to every property of the type. | 44 # recursively to every property of the type. |
45 # | 45 # |
46 # Adding --show_changes to the command line prints out a list of valid public AP
I changes. | 46 # Adding --show_changes to the command line prints out a list of valid public AP
I changes. |
47 | 47 |
48 import collections | |
49 import copy | 48 import copy |
50 import os.path | 49 import os.path |
51 import optparse | 50 import optparse |
52 import re | |
53 import sys | 51 import sys |
54 | 52 |
55 try: | 53 try: |
56 import json | 54 import json |
57 except ImportError: | 55 except ImportError: |
58 import simplejson as json | 56 import simplejson as json |
59 | 57 |
| 58 |
60 def list_to_map(items, key): | 59 def list_to_map(items, key): |
61 result = {} | 60 result = {} |
62 for item in items: | 61 for item in items: |
63 if not "experimental" in item and not "hidden" in item: | 62 if "experimental" not in item and "hidden" not in item: |
64 result[item[key]] = item | 63 result[item[key]] = item |
65 return result | 64 return result |
66 | 65 |
67 | 66 |
68 def named_list_to_map(container, name, key): | 67 def named_list_to_map(container, name, key): |
69 if name in container: | 68 if name in container: |
70 return list_to_map(container[name], key) | 69 return list_to_map(container[name], key) |
71 return {} | 70 return {} |
72 | 71 |
73 | 72 |
(...skipping 14 matching lines...) Expand all Loading... |
88 domains_1 = copy.deepcopy(d_1) | 87 domains_1 = copy.deepcopy(d_1) |
89 domains_2 = copy.deepcopy(d_2) | 88 domains_2 = copy.deepcopy(d_2) |
90 types_1 = normalize_types_in_schema(domains_1) | 89 types_1 = normalize_types_in_schema(domains_1) |
91 types_2 = normalize_types_in_schema(domains_2) | 90 types_2 = normalize_types_in_schema(domains_2) |
92 | 91 |
93 domains_by_name_1 = list_to_map(domains_1, "domain") | 92 domains_by_name_1 = list_to_map(domains_1, "domain") |
94 domains_by_name_2 = list_to_map(domains_2, "domain") | 93 domains_by_name_2 = list_to_map(domains_2, "domain") |
95 | 94 |
96 for name in domains_by_name_1: | 95 for name in domains_by_name_1: |
97 domain_1 = domains_by_name_1[name] | 96 domain_1 = domains_by_name_1[name] |
98 if not name in domains_by_name_2: | 97 if name not in domains_by_name_2: |
99 errors.append("%s: domain has been %s" % (name, removed(reverse))) | 98 errors.append("%s: domain has been %s" % (name, removed(reverse))) |
100 continue | 99 continue |
101 compare_domains(domain_1, domains_by_name_2[name], types_1, types_2, err
ors, reverse) | 100 compare_domains(domain_1, domains_by_name_2[name], types_1, types_2, err
ors, reverse) |
102 return errors | 101 return errors |
103 | 102 |
104 | 103 |
105 def compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, revers
e): | 104 def compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, revers
e): |
106 domain_name = domain_1["domain"] | 105 domain_name = domain_1["domain"] |
107 commands_1 = named_list_to_map(domain_1, "commands", "name") | 106 commands_1 = named_list_to_map(domain_1, "commands", "name") |
108 commands_2 = named_list_to_map(domain_2, "commands", "name") | 107 commands_2 = named_list_to_map(domain_2, "commands", "name") |
109 for name in commands_1: | 108 for name in commands_1: |
110 command_1 = commands_1[name] | 109 command_1 = commands_1[name] |
111 if not name in commands_2: | 110 if name not in commands_2: |
112 errors.append("%s.%s: command has been %s" % (domain_1["domain"], na
me, removed(reverse))) | 111 errors.append("%s.%s: command has been %s" % (domain_1["domain"], na
me, removed(reverse))) |
113 continue | 112 continue |
114 compare_commands(domain_name, command_1, commands_2[name], types_map_1,
types_map_2, errors, reverse) | 113 compare_commands(domain_name, command_1, commands_2[name], types_map_1,
types_map_2, errors, reverse) |
115 | 114 |
116 events_1 = named_list_to_map(domain_1, "events", "name") | 115 events_1 = named_list_to_map(domain_1, "events", "name") |
117 events_2 = named_list_to_map(domain_2, "events", "name") | 116 events_2 = named_list_to_map(domain_2, "events", "name") |
118 for name in events_1: | 117 for name in events_1: |
119 event_1 = events_1[name] | 118 event_1 = events_1[name] |
120 if not name in events_2: | 119 if name not in events_2: |
121 errors.append("%s.%s: event has been %s" % (domain_1["domain"], name
, removed(reverse))) | 120 errors.append("%s.%s: event has been %s" % (domain_1["domain"], name
, removed(reverse))) |
122 continue | 121 continue |
123 compare_events(domain_name, event_1, events_2[name], types_map_1, types_
map_2, errors, reverse) | 122 compare_events(domain_name, event_1, events_2[name], types_map_1, types_
map_2, errors, reverse) |
124 | 123 |
125 | 124 |
126 def compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2
, errors, reverse): | 125 def compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2
, errors, reverse): |
127 context = domain_name + "." + command_1["name"] | 126 context = domain_name + "." + command_1["name"] |
128 | 127 |
129 params_1 = named_list_to_map(command_1, "parameters", "name") | 128 params_1 = named_list_to_map(command_1, "parameters", "name") |
130 params_2 = named_list_to_map(command_2, "parameters", "name") | 129 params_2 = named_list_to_map(command_2, "parameters", "name") |
131 # Note the reversed order: we allow removing but forbid adding parameters. | 130 # Note the reversed order: we allow removing but forbid adding parameters. |
132 compare_params_list(context, "parameter", params_2, params_1, types_map_2, t
ypes_map_1, 0, errors, not reverse) | 131 compare_params_list(context, "parameter", params_2, params_1, types_map_2, t
ypes_map_1, 0, errors, not reverse) |
133 | 132 |
134 returns_1 = named_list_to_map(command_1, "returns", "name") | 133 returns_1 = named_list_to_map(command_1, "returns", "name") |
135 returns_2 = named_list_to_map(command_2, "returns", "name") | 134 returns_2 = named_list_to_map(command_2, "returns", "name") |
136 compare_params_list(context, "response parameter", returns_1, returns_2, typ
es_map_1, types_map_2, 0, errors, reverse) | 135 compare_params_list(context, "response parameter", returns_1, returns_2, typ
es_map_1, types_map_2, 0, errors, reverse) |
137 | 136 |
138 | 137 |
139 def compare_events(domain_name, event_1, event_2, types_map_1, types_map_2, erro
rs, reverse): | 138 def compare_events(domain_name, event_1, event_2, types_map_1, types_map_2, erro
rs, reverse): |
140 context = domain_name + "." + event_1["name"] | 139 context = domain_name + "." + event_1["name"] |
141 params_1 = named_list_to_map(event_1, "parameters", "name") | 140 params_1 = named_list_to_map(event_1, "parameters", "name") |
142 params_2 = named_list_to_map(event_2, "parameters", "name") | 141 params_2 = named_list_to_map(event_2, "parameters", "name") |
143 compare_params_list(context, "parameter", params_1, params_2, types_map_1, t
ypes_map_2, 0, errors, reverse) | 142 compare_params_list(context, "parameter", params_1, params_2, types_map_1, t
ypes_map_2, 0, errors, reverse) |
144 | 143 |
145 | 144 |
146 def compare_params_list(context, kind, params_1, params_2, types_map_1, types_ma
p_2, depth, errors, reverse): | 145 def compare_params_list(context, kind, params_1, params_2, types_map_1, types_ma
p_2, depth, errors, reverse): |
147 for name in params_1: | 146 for name in params_1: |
148 param_1 = params_1[name] | 147 param_1 = params_1[name] |
149 if not name in params_2: | 148 if name not in params_2: |
150 if not "optional" in param_1: | 149 if "optional" not in param_1: |
151 errors.append("%s.%s: required %s has been %s" % (context, name,
kind, removed(reverse))) | 150 errors.append("%s.%s: required %s has been %s" % (context, name,
kind, removed(reverse))) |
152 continue | 151 continue |
153 | 152 |
154 param_2 = params_2[name] | 153 param_2 = params_2[name] |
155 if param_2 and "optional" in param_2 and not "optional" in param_1: | 154 if param_2 and "optional" in param_2 and "optional" not in param_1: |
156 errors.append("%s.%s: %s %s is now %s" % (context, name, required(re
verse), kind, required(not reverse))) | 155 errors.append("%s.%s: %s %s is now %s" % (context, name, required(re
verse), kind, required(not reverse))) |
157 continue | 156 continue |
158 type_1 = extract_type(param_1, types_map_1, errors) | 157 type_1 = extract_type(param_1, types_map_1, errors) |
159 type_2 = extract_type(param_2, types_map_2, errors) | 158 type_2 = extract_type(param_2, types_map_2, errors) |
160 compare_types(context + "." + name, kind, type_1, type_2, types_map_1, t
ypes_map_2, depth, errors, reverse) | 159 compare_types(context + "." + name, kind, type_1, type_2, types_map_1, t
ypes_map_2, depth, errors, reverse) |
161 | 160 |
162 | 161 |
163 def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth
, errors, reverse): | 162 def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth
, errors, reverse): |
164 if depth > 10: | 163 if depth > 10: |
165 return | 164 return |
(...skipping 14 matching lines...) Expand all Loading... |
180 context += " %s->%s" % (kind, type_name) | 179 context += " %s->%s" % (kind, type_name) |
181 compare_params_list(context, "property", params_1, params_2, types_map_1
, types_map_2, depth + 1, errors, reverse) | 180 compare_params_list(context, "property", params_1, params_2, types_map_1
, types_map_2, depth + 1, errors, reverse) |
182 elif base_type_1 == "array": | 181 elif base_type_1 == "array": |
183 item_type_1 = extract_type(type_1["items"], types_map_1, errors) | 182 item_type_1 = extract_type(type_1["items"], types_map_1, errors) |
184 item_type_2 = extract_type(type_2["items"], types_map_2, errors) | 183 item_type_2 = extract_type(type_2["items"], types_map_2, errors) |
185 compare_types(context, kind, item_type_1, item_type_2, types_map_1, type
s_map_2, depth + 1, errors, reverse) | 184 compare_types(context, kind, item_type_1, item_type_2, types_map_1, type
s_map_2, depth + 1, errors, reverse) |
186 | 185 |
187 | 186 |
188 def extract_type(typed_object, types_map, errors): | 187 def extract_type(typed_object, types_map, errors): |
189 if "type" in typed_object: | 188 if "type" in typed_object: |
190 result = { "id": "<transient>", "type": typed_object["type"] } | 189 result = {"id": "<transient>", "type": typed_object["type"]} |
191 if typed_object["type"] == "object": | 190 if typed_object["type"] == "object": |
192 result["properties"] = [] | 191 result["properties"] = [] |
193 elif typed_object["type"] == "array": | 192 elif typed_object["type"] == "array": |
194 result["items"] = typed_object["items"] | 193 result["items"] = typed_object["items"] |
195 return result | 194 return result |
196 elif "$ref" in typed_object: | 195 elif "$ref" in typed_object: |
197 ref = typed_object["$ref"] | 196 ref = typed_object["$ref"] |
198 if not ref in types_map: | 197 if ref not in types_map: |
199 errors.append("Can not resolve type: %s" % ref) | 198 errors.append("Can not resolve type: %s" % ref) |
200 types_map[ref] = { "id": "<transient>", "type": "object" } | 199 types_map[ref] = {"id": "<transient>", "type": "object"} |
201 return types_map[ref] | 200 return types_map[ref] |
202 | 201 |
203 | 202 |
204 def normalize_types_in_schema(domains): | 203 def normalize_types_in_schema(domains): |
205 types = {} | 204 types = {} |
206 for domain in domains: | 205 for domain in domains: |
207 domain_name = domain["domain"] | 206 domain_name = domain["domain"] |
208 normalize_types(domain, domain_name, types) | 207 normalize_types(domain, domain_name, types) |
209 return types | 208 return types |
210 | 209 |
211 | 210 |
212 def normalize_types(obj, domain_name, types): | 211 def normalize_types(obj, domain_name, types): |
213 if isinstance(obj, list): | 212 if isinstance(obj, list): |
214 for item in obj: | 213 for item in obj: |
215 normalize_types(item, domain_name, types) | 214 normalize_types(item, domain_name, types) |
216 elif isinstance(obj, dict): | 215 elif isinstance(obj, dict): |
217 for key, value in obj.items(): | 216 for key, value in obj.items(): |
218 if key == "$ref" and value.find(".") == -1: | 217 if key == "$ref" and value.find(".") == -1: |
219 obj[key] = "%s.%s" % (domain_name, value) | 218 obj[key] = "%s.%s" % (domain_name, value) |
220 elif key == "id": | 219 elif key == "id": |
221 obj[key] = "%s.%s" % (domain_name, value) | 220 obj[key] = "%s.%s" % (domain_name, value) |
222 types[obj[key]] = obj | 221 types[obj[key]] = obj |
223 else: | 222 else: |
224 normalize_types(value, domain_name, types) | 223 normalize_types(value, domain_name, types) |
225 | 224 |
226 | 225 |
227 def load_schema(file, domains): | 226 def load_schema(file_name, domains): |
228 if not os.path.isfile(file): | 227 # pylint: disable=W0613 |
| 228 if not os.path.isfile(file_name): |
229 return | 229 return |
230 input_file = open(file, "r") | 230 input_file = open(file_name, "r") |
231 json_string = input_file.read() | 231 json_string = input_file.read() |
232 parsed_json = json.loads(json_string) | 232 parsed_json = json.loads(json_string) |
233 domains += parsed_json["domains"] | 233 domains += parsed_json["domains"] |
234 return parsed_json["version"] | 234 return parsed_json["version"] |
235 | 235 |
236 | 236 |
237 def self_test(): | 237 def self_test(): |
238 def create_test_schema_1(): | 238 def create_test_schema_1(): |
239 return [ | 239 return [ |
240 { | 240 { |
241 "domain": "Network", | 241 "domain": "Network", |
242 "types": [ | 242 "types": [ |
243 { | 243 { |
244 "id": "LoaderId", | 244 "id": "LoaderId", |
245 "type": "string" | 245 "type": "string" |
246 }, | 246 }, |
247 { | 247 { |
248 "id": "Headers", | 248 "id": "Headers", |
249 "type": "object" | 249 "type": "object" |
250 }, | 250 }, |
251 { | 251 { |
252 "id": "Request", | 252 "id": "Request", |
253 "type": "object", | 253 "type": "object", |
254 "properties": [ | 254 "properties": [ |
255 { "name": "url", "type": "string" }, | 255 {"name": "url", "type": "string"}, |
256 { "name": "method", "type": "string" }, | 256 {"name": "method", "type": "string"}, |
257 { "name": "headers", "$ref": "Headers" }, | 257 {"name": "headers", "$ref": "Headers"}, |
258 { "name": "becameOptionalField", "type": "string" }, | 258 {"name": "becameOptionalField", "type": "string"}, |
259 { "name": "removedField", "type": "string" }, | 259 {"name": "removedField", "type": "string"}, |
260 ] | |
261 } | |
262 ], | |
263 "commands": [ | |
264 { | |
265 "name": "removedCommand", | |
266 }, | |
267 { | |
268 "name": "setExtraHTTPHeaders", | |
269 "parameters": [ | |
270 { "name": "headers", "$ref": "Headers" }, | |
271 { "name": "mismatched", "type": "string" }, | |
272 { "name": "becameOptional", "$ref": "Headers" }, | |
273 { "name": "removedRequired", "$ref": "Headers" }, | |
274 { "name": "becameRequired", "$ref": "Headers", "optional
": True }, | |
275 { "name": "removedOptional", "$ref": "Headers", "optiona
l": True }, | |
276 ], | |
277 "returns": [ | |
278 { "name": "mimeType", "type": "string" }, | |
279 { "name": "becameOptional", "type": "string" }, | |
280 { "name": "removedRequired", "type": "string" }, | |
281 { "name": "becameRequired", "type": "string", "optional"
: True }, | |
282 { "name": "removedOptional", "type": "string", "optional
": True }, | |
283 ] | |
284 } | |
285 ], | |
286 "events": [ | |
287 { | |
288 "name": "requestWillBeSent", | |
289 "parameters": [ | |
290 { "name": "frameId", "type": "string", "experimental": T
rue }, | |
291 { "name": "request", "$ref": "Request" }, | |
292 { "name": "becameOptional", "type": "string" }, | |
293 { "name": "removedRequired", "type": "string" }, | |
294 { "name": "becameRequired", "type": "string", "optional"
: True }, | |
295 { "name": "removedOptional", "type": "string", "optional
": True }, | |
296 ] | 260 ] |
297 }, | 261 } |
298 { | 262 ], |
299 "name": "removedEvent", | 263 "commands": [ |
300 "parameters": [ | 264 { |
301 { "name": "errorText", "type": "string" }, | 265 "name": "removedCommand", |
302 { "name": "canceled", "type": "boolean", "optional": Tru
e } | 266 }, |
303 ] | 267 { |
304 } | 268 "name": "setExtraHTTPHeaders", |
305 ] | 269 "parameters": [ |
306 }, | 270 {"name": "headers", "$ref": "Headers"}, |
307 { | 271 {"name": "mismatched", "type": "string"}, |
308 "domain": "removedDomain" | 272 {"name": "becameOptional", "$ref": "Headers"}, |
309 } | 273 {"name": "removedRequired", "$ref": "Headers"}, |
310 ] | 274 {"name": "becameRequired", "$ref": "Headers", "optio
nal": True}, |
| 275 {"name": "removedOptional", "$ref": "Headers", "opti
onal": True}, |
| 276 ], |
| 277 "returns": [ |
| 278 {"name": "mimeType", "type": "string"}, |
| 279 {"name": "becameOptional", "type": "string"}, |
| 280 {"name": "removedRequired", "type": "string"}, |
| 281 {"name": "becameRequired", "type": "string", "option
al": True}, |
| 282 {"name": "removedOptional", "type": "string", "optio
nal": True}, |
| 283 ] |
| 284 } |
| 285 ], |
| 286 "events": [ |
| 287 { |
| 288 "name": "requestWillBeSent", |
| 289 "parameters": [ |
| 290 {"name": "frameId", "type": "string", "experimental"
: True}, |
| 291 {"name": "request", "$ref": "Request"}, |
| 292 {"name": "becameOptional", "type": "string"}, |
| 293 {"name": "removedRequired", "type": "string"}, |
| 294 {"name": "becameRequired", "type": "string", "option
al": True}, |
| 295 {"name": "removedOptional", "type": "string", "optio
nal": True}, |
| 296 ] |
| 297 }, |
| 298 { |
| 299 "name": "removedEvent", |
| 300 "parameters": [ |
| 301 {"name": "errorText", "type": "string"}, |
| 302 {"name": "canceled", "type": "boolean", "optional":
True} |
| 303 ] |
| 304 } |
| 305 ] |
| 306 }, |
| 307 { |
| 308 "domain": "removedDomain" |
| 309 } |
| 310 ] |
311 | 311 |
312 def create_test_schema_2(): | 312 def create_test_schema_2(): |
313 return [ | 313 return [ |
314 { | 314 { |
315 "domain": "Network", | 315 "domain": "Network", |
316 "types": [ | 316 "types": [ |
317 { | 317 { |
318 "id": "LoaderId", | 318 "id": "LoaderId", |
319 "type": "string" | 319 "type": "string" |
320 }, | 320 }, |
321 { | 321 { |
322 "id": "Request", | 322 "id": "Request", |
323 "type": "object", | 323 "type": "object", |
324 "properties": [ | 324 "properties": [ |
325 { "name": "url", "type": "string" }, | 325 {"name": "url", "type": "string"}, |
326 { "name": "method", "type": "string" }, | 326 {"name": "method", "type": "string"}, |
327 { "name": "headers", "type": "object" }, | 327 {"name": "headers", "type": "object"}, |
328 { "name": "becameOptionalField", "type": "string", "opti
onal": True }, | 328 {"name": "becameOptionalField", "type": "string", "o
ptional": True}, |
329 ] | 329 ] |
330 } | 330 } |
331 ], | 331 ], |
332 "commands": [ | 332 "commands": [ |
333 { | 333 { |
334 "name": "addedCommand", | 334 "name": "addedCommand", |
335 }, | 335 }, |
336 { | 336 { |
337 "name": "setExtraHTTPHeaders", | 337 "name": "setExtraHTTPHeaders", |
338 "parameters": [ | 338 "parameters": [ |
339 { "name": "headers", "type": "object" }, | 339 {"name": "headers", "type": "object"}, |
340 { "name": "mismatched", "type": "object" }, | 340 {"name": "mismatched", "type": "object"}, |
341 { "name": "becameOptional", "type": "object" , "optional
": True }, | 341 {"name": "becameOptional", "type": "object", "option
al": True}, |
342 { "name": "addedRequired", "type": "object" }, | 342 {"name": "addedRequired", "type": "object"}, |
343 { "name": "becameRequired", "type": "object" }, | 343 {"name": "becameRequired", "type": "object"}, |
344 { "name": "addedOptional", "type": "object", "optional":
True }, | 344 {"name": "addedOptional", "type": "object", "optiona
l": True}, |
345 ], | 345 ], |
346 "returns": [ | 346 "returns": [ |
347 { "name": "mimeType", "type": "string" }, | 347 {"name": "mimeType", "type": "string"}, |
348 { "name": "becameOptional", "type": "string", "optional"
: True }, | 348 {"name": "becameOptional", "type": "string", "option
al": True}, |
349 { "name": "addedRequired", "type": "string"}, | 349 {"name": "addedRequired", "type": "string"}, |
350 { "name": "becameRequired", "type": "string" }, | 350 {"name": "becameRequired", "type": "string"}, |
351 { "name": "addedOptional", "type": "string", "optional":
True }, | 351 {"name": "addedOptional", "type": "string", "optiona
l": True}, |
352 ] | 352 ] |
353 } | 353 } |
354 ], | 354 ], |
355 "events": [ | 355 "events": [ |
356 { | 356 { |
357 "name": "requestWillBeSent", | 357 "name": "requestWillBeSent", |
358 "parameters": [ | 358 "parameters": [ |
359 { "name": "request", "$ref": "Request" }, | 359 {"name": "request", "$ref": "Request"}, |
360 { "name": "becameOptional", "type": "string", "optional"
: True }, | 360 {"name": "becameOptional", "type": "string", "option
al": True}, |
361 { "name": "addedRequired", "type": "string"}, | 361 {"name": "addedRequired", "type": "string"}, |
362 { "name": "becameRequired", "type": "string" }, | 362 {"name": "becameRequired", "type": "string"}, |
363 { "name": "addedOptional", "type": "string", "optional":
True }, | 363 {"name": "addedOptional", "type": "string", "optiona
l": True}, |
364 ] | 364 ] |
365 }, | 365 }, |
366 { | 366 { |
367 "name": "addedEvent" | 367 "name": "addedEvent" |
368 } | 368 } |
369 ] | 369 ] |
370 }, | 370 }, |
371 { | 371 { |
372 "domain": "addedDomain" | 372 "domain": "addedDomain" |
373 } | 373 } |
374 ] | 374 ] |
375 | 375 |
376 expected_errors = [ | 376 expected_errors = [ |
377 "removedDomain: domain has been removed", | 377 "removedDomain: domain has been removed", |
378 "Network.removedCommand: command has been removed", | 378 "Network.removedCommand: command has been removed", |
379 "Network.removedEvent: event has been removed", | 379 "Network.removedEvent: event has been removed", |
380 "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, '
object' vs 'string'", | 380 "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, '
object' vs 'string'", |
381 "Network.setExtraHTTPHeaders.addedRequired: required parameter has been
added", | 381 "Network.setExtraHTTPHeaders.addedRequired: required parameter has been
added", |
382 "Network.setExtraHTTPHeaders.becameRequired: optional parameter is now r
equired", | 382 "Network.setExtraHTTPHeaders.becameRequired: optional parameter is now r
equired", |
383 "Network.setExtraHTTPHeaders.removedRequired: required response paramete
r has been removed", | 383 "Network.setExtraHTTPHeaders.removedRequired: required response paramete
r has been removed", |
384 "Network.setExtraHTTPHeaders.becameOptional: required response parameter
is now optional", | 384 "Network.setExtraHTTPHeaders.becameOptional: required response parameter
is now optional", |
385 "Network.requestWillBeSent.removedRequired: required parameter has been
removed", | 385 "Network.requestWillBeSent.removedRequired: required parameter has been
removed", |
386 "Network.requestWillBeSent.becameOptional: required parameter is now opt
ional", | 386 "Network.requestWillBeSent.becameOptional: required parameter is now opt
ional", |
387 "Network.requestWillBeSent.request parameter->Network.Request.removedFie
ld: required property has been removed", | 387 "Network.requestWillBeSent.request parameter->Network.Request.removedFie
ld: required property has been removed", |
388 "Network.requestWillBeSent.request parameter->Network.Request.becameOpti
onalField: required property is now optional", | 388 "Network.requestWillBeSent.request parameter->Network.Request.becameOpti
onalField: required property is now optional", |
389 ] | 389 ] |
390 | 390 |
391 expected_errors_reverse = [ | 391 expected_errors_reverse = [ |
392 "addedDomain: domain has been added", | 392 "addedDomain: domain has been added", |
393 "Network.addedEvent: event has been added", | 393 "Network.addedEvent: event has been added", |
394 "Network.addedCommand: command has been added", | 394 "Network.addedCommand: command has been added", |
395 "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, 's
tring' vs 'object'", | 395 "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, '
string' vs 'object'", |
396 "Network.setExtraHTTPHeaders.removedRequired: required parameter has been
removed", | 396 "Network.setExtraHTTPHeaders.removedRequired: required parameter has bee
n removed", |
397 "Network.setExtraHTTPHeaders.becameOptional: required parameter is now op
tional", | 397 "Network.setExtraHTTPHeaders.becameOptional: required parameter is now o
ptional", |
398 "Network.setExtraHTTPHeaders.addedRequired: required response parameter h
as been added", | 398 "Network.setExtraHTTPHeaders.addedRequired: required response parameter
has been added", |
399 "Network.setExtraHTTPHeaders.becameRequired: optional response parameter
is now required", | 399 "Network.setExtraHTTPHeaders.becameRequired: optional response parameter
is now required", |
400 "Network.requestWillBeSent.becameRequired: optional parameter is now requ
ired", | 400 "Network.requestWillBeSent.becameRequired: optional parameter is now req
uired", |
401 "Network.requestWillBeSent.addedRequired: required parameter has been add
ed", | 401 "Network.requestWillBeSent.addedRequired: required parameter has been ad
ded", |
402 ] | 402 ] |
403 | 403 |
404 def is_subset(subset, superset, message): | 404 def is_subset(subset, superset, message): |
405 for i in range(len(subset)): | 405 for i in range(len(subset)): |
406 if subset[i] not in superset: | 406 if subset[i] not in superset: |
407 sys.stderr.write("%s error: %s\n" % (message, subset[i])) | 407 sys.stderr.write("%s error: %s\n" % (message, subset[i])) |
408 return False | 408 return False |
409 return True | 409 return True |
410 | 410 |
411 def errors_match(expected, actual): | 411 def errors_match(expected, actual): |
412 return (is_subset(actual, expected, "Unexpected") and | 412 return (is_subset(actual, expected, "Unexpected") and |
413 is_subset(expected, actual, "Missing")) | 413 is_subset(expected, actual, "Missing")) |
414 | 414 |
415 return (errors_match(expected_errors, | 415 return (errors_match(expected_errors, |
416 compare_schemas(create_test_schema_1(), create_test_sch
ema_2(), False)) and | 416 compare_schemas(create_test_schema_1(), create_test_sch
ema_2(), False)) and |
417 errors_match(expected_errors_reverse, | 417 errors_match(expected_errors_reverse, |
418 compare_schemas(create_test_schema_2(), create_test_sch
ema_1(), True))) | 418 compare_schemas(create_test_schema_2(), create_test_sch
ema_1(), True))) |
419 | 419 |
420 | 420 |
421 | 421 def load_domains_and_baselines(file_name, domains, baseline_domains): |
422 def load_domains_and_baselines(file, domains, baseline_domains): | 422 version = load_schema(os.path.normpath(file_name), domains) |
423 version = load_schema(os.path.normpath(file), domains) | |
424 suffix = "-%s.%s.json" % (version["major"], version["minor"]) | 423 suffix = "-%s.%s.json" % (version["major"], version["minor"]) |
425 baseline_file = file.replace(".json", suffix) | 424 baseline_file = file_name.replace(".json", suffix) |
426 load_schema(os.path.normpath(baseline_file), baseline_domains) | 425 load_schema(os.path.normpath(baseline_file), baseline_domains) |
427 return version | 426 return version |
428 | 427 |
429 | 428 |
430 def main(): | 429 def main(): |
431 if not self_test(): | 430 if not self_test(): |
432 sys.stderr.write("Self-test failed") | 431 sys.stderr.write("Self-test failed") |
433 return 1 | 432 return 1 |
434 | 433 |
435 cmdline_parser = optparse.OptionParser() | 434 cmdline_parser = optparse.OptionParser() |
436 cmdline_parser.add_option("--show_changes") | 435 cmdline_parser.add_option("--show_changes") |
437 cmdline_parser.add_option("--o") | 436 cmdline_parser.add_option("--expected_errors") |
| 437 cmdline_parser.add_option("--stamp") |
438 arg_options, arg_values = cmdline_parser.parse_args() | 438 arg_options, arg_values = cmdline_parser.parse_args() |
439 | 439 |
440 if len(arg_values) < 1 or not arg_options.o: | 440 if len(arg_values) < 1: |
441 sys.stderr.write("Usage: %s --o OUTPUT_FILE [--show_changes] PROTOCOL_FO
LDER1 ?PROTOCOL_FOLDER2 \n" % sys.argv[0]) | 441 sys.stderr.write("Usage: %s [--show_changes] <protocol-1> [, <protocol-2
>...]\n" % sys.argv[0]) |
442 return 1 | 442 return 1 |
443 | 443 |
444 output_path = arg_options.o | |
445 output_file = open(output_path, "w") | |
446 | |
447 domains = [] | 444 domains = [] |
448 baseline_domains = [] | 445 baseline_domains = [] |
449 version = load_domains_and_baselines(arg_values[0], domains, baseline_domain
s) | 446 version = load_domains_and_baselines(arg_values[0], domains, baseline_domain
s) |
450 if len(arg_values) > 1: | 447 for dependency in arg_values[1:]: |
451 load_domains_and_baselines(arg_values[1], domains, baseline_domains) | 448 load_domains_and_baselines(dependency, domains, baseline_domains) |
452 | 449 |
453 expected_errors = [ | 450 expected_errors = [] |
454 "Debugger.globalObjectCleared: event has been removed", | 451 if arg_options.expected_errors: |
455 "Runtime.executionContextCreated.context parameter->Runtime.ExecutionCon
textDescription.frameId: required property has been removed", | 452 expected_errors_file = open(arg_options.expected_errors, "r") |
456 "Debugger.canSetScriptSource: command has been removed", | 453 expected_errors = json.loads(expected_errors_file.read())["errors"] |
457 "Console.messageRepeatCountUpdated: event has been removed", | 454 expected_errors_file.close() |
458 "Console.messagesCleared: event has been removed" | |
459 ] | |
460 | 455 |
461 errors = compare_schemas(baseline_domains, domains, False) | 456 errors = compare_schemas(baseline_domains, domains, False) |
462 unexpected_errors = [] | 457 unexpected_errors = [] |
463 for i in range(len(errors)): | 458 for i in range(len(errors)): |
464 if errors[i] not in expected_errors: | 459 if errors[i] not in expected_errors: |
465 unexpected_errors.append(errors[i]) | 460 unexpected_errors.append(errors[i]) |
466 if len(unexpected_errors) > 0: | 461 if len(unexpected_errors) > 0: |
467 sys.stderr.write(" Compatibility checks FAILED\n") | 462 sys.stderr.write(" Compatibility checks FAILED\n") |
468 for error in unexpected_errors: | 463 for error in unexpected_errors: |
469 sys.stderr.write( " %s\n" % error) | 464 sys.stderr.write(" %s\n" % error) |
470 return 1 | 465 return 1 |
471 | 466 |
472 if arg_options.show_changes: | 467 if arg_options.show_changes: |
473 changes = compare_schemas(domains, baseline_domains, True) | 468 changes = compare_schemas(domains, baseline_domains, True) |
474 if len(changes) > 0: | 469 if len(changes) > 0: |
475 print " Public changes since %s:" % version | 470 print " Public changes since %s:" % version |
476 for change in changes: | 471 for change in changes: |
477 print " %s" % change | 472 print " %s" % change |
478 | 473 |
479 json.dump({"version": version, "domains": domains}, output_file, indent=4, s
ort_keys=False, separators=(',', ': ')) | 474 if arg_options.stamp: |
480 output_file.close() | 475 with open(arg_options.stamp, 'a') as _: |
| 476 pass |
481 | 477 |
482 if __name__ == '__main__': | 478 if __name__ == '__main__': |
483 sys.exit(main()) | 479 sys.exit(main()) |
OLD | NEW |