Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(512)

Side by Side Diff: third_party/WebKit/Source/platform/inspector_protocol/generate-inspector-protocol-version

Issue 2035653005: DevTools: split protocol.json into files per domain. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 os.path 50 import os.path
51 import optparse
49 import re 52 import re
50 import sys 53 import sys
51 54
55 try:
56 import json
57 except ImportError:
58 import simplejson as json
59
52 def list_to_map(items, key): 60 def list_to_map(items, key):
53 result = {} 61 result = {}
54 for item in items: 62 for item in items:
55 if not "hidden" in item: 63 if not "hidden" in item:
56 result[item[key]] = item 64 result[item[key]] = item
57 return result 65 return result
58 66
67
59 def named_list_to_map(container, name, key): 68 def named_list_to_map(container, name, key):
60 if name in container: 69 if name in container:
61 return list_to_map(container[name], key) 70 return list_to_map(container[name], key)
62 return {} 71 return {}
63 72
73
64 def removed(reverse): 74 def removed(reverse):
65 if reverse: 75 if reverse:
66 return "added" 76 return "added"
67 return "removed" 77 return "removed"
68 78
79
69 def required(reverse): 80 def required(reverse):
70 if reverse: 81 if reverse:
71 return "optional" 82 return "optional"
72 return "required" 83 return "required"
73 84
85
74 def compare_schemas(schema_1, schema_2, reverse): 86 def compare_schemas(schema_1, schema_2, reverse):
75 errors = [] 87 errors = []
76 types_1 = normalize_types_in_schema(schema_1) 88 types_1 = normalize_types_in_schema(schema_1)
77 types_2 = normalize_types_in_schema(schema_2) 89 types_2 = normalize_types_in_schema(schema_2)
78 90
79 domains_by_name_1 = list_to_map(schema_1, "domain") 91 domains_by_name_1 = list_to_map(schema_1, "domain")
80 domains_by_name_2 = list_to_map(schema_2, "domain") 92 domains_by_name_2 = list_to_map(schema_2, "domain")
81 93
82 for name in domains_by_name_1: 94 for name in domains_by_name_1:
83 domain_1 = domains_by_name_1[name] 95 domain_1 = domains_by_name_1[name]
84 if not name in domains_by_name_2: 96 if not name in domains_by_name_2:
85 errors.append("%s: domain has been %s" % (name, removed(reverse))) 97 errors.append("%s: domain has been %s" % (name, removed(reverse)))
86 continue 98 continue
87 compare_domains(domain_1, domains_by_name_2[name], types_1, types_2, err ors, reverse) 99 compare_domains(domain_1, domains_by_name_2[name], types_1, types_2, err ors, reverse)
88 return errors 100 return errors
89 101
102
90 def compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, revers e): 103 def compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, revers e):
91 domain_name = domain_1["domain"] 104 domain_name = domain_1["domain"]
92 commands_1 = named_list_to_map(domain_1, "commands", "name") 105 commands_1 = named_list_to_map(domain_1, "commands", "name")
93 commands_2 = named_list_to_map(domain_2, "commands", "name") 106 commands_2 = named_list_to_map(domain_2, "commands", "name")
94 for name in commands_1: 107 for name in commands_1:
95 command_1 = commands_1[name] 108 command_1 = commands_1[name]
96 if not name in commands_2: 109 if not name in commands_2:
97 errors.append("%s.%s: command has been %s" % (domain_1["domain"], na me, removed(reverse))) 110 errors.append("%s.%s: command has been %s" % (domain_1["domain"], na me, removed(reverse)))
98 continue 111 continue
99 compare_commands(domain_name, command_1, commands_2[name], types_map_1, types_map_2, errors, reverse) 112 compare_commands(domain_name, command_1, commands_2[name], types_map_1, types_map_2, errors, reverse)
100 113
101 events_1 = named_list_to_map(domain_1, "events", "name") 114 events_1 = named_list_to_map(domain_1, "events", "name")
102 events_2 = named_list_to_map(domain_2, "events", "name") 115 events_2 = named_list_to_map(domain_2, "events", "name")
103 for name in events_1: 116 for name in events_1:
104 event_1 = events_1[name] 117 event_1 = events_1[name]
105 if not name in events_2: 118 if not name in events_2:
106 errors.append("%s.%s: event has been %s" % (domain_1["domain"], name , removed(reverse))) 119 errors.append("%s.%s: event has been %s" % (domain_1["domain"], name , removed(reverse)))
107 continue 120 continue
108 compare_events(domain_name, event_1, events_2[name], types_map_1, types_ map_2, errors, reverse) 121 compare_events(domain_name, event_1, events_2[name], types_map_1, types_ map_2, errors, reverse)
109 122
123
110 def compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2 , errors, reverse): 124 def compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2 , errors, reverse):
111 context = domain_name + "." + command_1["name"] 125 context = domain_name + "." + command_1["name"]
112 126
113 params_1 = named_list_to_map(command_1, "parameters", "name") 127 params_1 = named_list_to_map(command_1, "parameters", "name")
114 params_2 = named_list_to_map(command_2, "parameters", "name") 128 params_2 = named_list_to_map(command_2, "parameters", "name")
115 # Note the reversed order: we allow removing but forbid adding parameters. 129 # Note the reversed order: we allow removing but forbid adding parameters.
116 compare_params_list(context, "parameter", params_2, params_1, types_map_2, t ypes_map_1, 0, errors, not reverse) 130 compare_params_list(context, "parameter", params_2, params_1, types_map_2, t ypes_map_1, 0, errors, not reverse)
117 131
118 returns_1 = named_list_to_map(command_1, "returns", "name") 132 returns_1 = named_list_to_map(command_1, "returns", "name")
119 returns_2 = named_list_to_map(command_2, "returns", "name") 133 returns_2 = named_list_to_map(command_2, "returns", "name")
120 compare_params_list(context, "response parameter", returns_1, returns_2, typ es_map_1, types_map_2, 0, errors, reverse) 134 compare_params_list(context, "response parameter", returns_1, returns_2, typ es_map_1, types_map_2, 0, errors, reverse)
121 135
136
122 def compare_events(domain_name, event_1, event_2, types_map_1, types_map_2, erro rs, reverse): 137 def compare_events(domain_name, event_1, event_2, types_map_1, types_map_2, erro rs, reverse):
123 context = domain_name + "." + event_1["name"] 138 context = domain_name + "." + event_1["name"]
124 params_1 = named_list_to_map(event_1, "parameters", "name") 139 params_1 = named_list_to_map(event_1, "parameters", "name")
125 params_2 = named_list_to_map(event_2, "parameters", "name") 140 params_2 = named_list_to_map(event_2, "parameters", "name")
126 compare_params_list(context, "parameter", params_1, params_2, types_map_1, t ypes_map_2, 0, errors, reverse) 141 compare_params_list(context, "parameter", params_1, params_2, types_map_1, t ypes_map_2, 0, errors, reverse)
127 142
143
128 def compare_params_list(context, kind, params_1, params_2, types_map_1, types_ma p_2, depth, errors, reverse): 144 def compare_params_list(context, kind, params_1, params_2, types_map_1, types_ma p_2, depth, errors, reverse):
129 for name in params_1: 145 for name in params_1:
130 param_1 = params_1[name] 146 param_1 = params_1[name]
131 if not name in params_2: 147 if not name in params_2:
132 if not "optional" in param_1: 148 if not "optional" in param_1:
133 errors.append("%s.%s: required %s has been %s" % (context, name, kind, removed(reverse))) 149 errors.append("%s.%s: required %s has been %s" % (context, name, kind, removed(reverse)))
134 continue 150 continue
135 151
136 param_2 = params_2[name] 152 param_2 = params_2[name]
137 if param_2 and "optional" in param_2 and not "optional" in param_1: 153 if param_2 and "optional" in param_2 and not "optional" in param_1:
138 errors.append("%s.%s: %s %s is now %s" % (context, name, required(re verse), kind, required(not reverse))) 154 errors.append("%s.%s: %s %s is now %s" % (context, name, required(re verse), kind, required(not reverse)))
139 continue 155 continue
140 type_1 = extract_type(param_1, types_map_1, errors) 156 type_1 = extract_type(param_1, types_map_1, errors)
141 type_2 = extract_type(param_2, types_map_2, errors) 157 type_2 = extract_type(param_2, types_map_2, errors)
142 compare_types(context + "." + name, kind, type_1, type_2, types_map_1, t ypes_map_2, depth, errors, reverse) 158 compare_types(context + "." + name, kind, type_1, type_2, types_map_1, t ypes_map_2, depth, errors, reverse)
143 159
160
144 def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth , errors, reverse): 161 def compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth , errors, reverse):
145 if depth > 10: 162 if depth > 10:
146 return 163 return
147 164
148 base_type_1 = type_1["type"] 165 base_type_1 = type_1["type"]
149 base_type_2 = type_2["type"] 166 base_type_2 = type_2["type"]
150 167
151 if base_type_1 != base_type_2: 168 if base_type_1 != base_type_2:
152 errors.append("%s: %s base type mismatch, '%s' vs '%s'" % (context, kind , base_type_1, base_type_2)) 169 errors.append("%s: %s base type mismatch, '%s' vs '%s'" % (context, kind , base_type_1, base_type_2))
153 elif base_type_1 == "object": 170 elif base_type_1 == "object":
154 params_1 = named_list_to_map(type_1, "properties", "name") 171 params_1 = named_list_to_map(type_1, "properties", "name")
155 params_2 = named_list_to_map(type_2, "properties", "name") 172 params_2 = named_list_to_map(type_2, "properties", "name")
156 # If both parameters have the same named type use it in the context. 173 # If both parameters have the same named type use it in the context.
157 if "id" in type_1 and "id" in type_2 and type_1["id"] == type_2["id"]: 174 if "id" in type_1 and "id" in type_2 and type_1["id"] == type_2["id"]:
158 type_name = type_1["id"] 175 type_name = type_1["id"]
159 else: 176 else:
160 type_name = "<object>" 177 type_name = "<object>"
161 context += " %s->%s" % (kind, type_name) 178 context += " %s->%s" % (kind, type_name)
162 compare_params_list(context, "property", params_1, params_2, types_map_1 , types_map_2, depth + 1, errors, reverse) 179 compare_params_list(context, "property", params_1, params_2, types_map_1 , types_map_2, depth + 1, errors, reverse)
163 elif base_type_1 == "array": 180 elif base_type_1 == "array":
164 item_type_1 = extract_type(type_1["items"], types_map_1, errors) 181 item_type_1 = extract_type(type_1["items"], types_map_1, errors)
165 item_type_2 = extract_type(type_2["items"], types_map_2, errors) 182 item_type_2 = extract_type(type_2["items"], types_map_2, errors)
166 compare_types(context, kind, item_type_1, item_type_2, types_map_1, type s_map_2, depth + 1, errors, reverse) 183 compare_types(context, kind, item_type_1, item_type_2, types_map_1, type s_map_2, depth + 1, errors, reverse)
167 184
185
168 def extract_type(typed_object, types_map, errors): 186 def extract_type(typed_object, types_map, errors):
169 if "type" in typed_object: 187 if "type" in typed_object:
170 result = { "id": "<transient>", "type": typed_object["type"] } 188 result = { "id": "<transient>", "type": typed_object["type"] }
171 if typed_object["type"] == "object": 189 if typed_object["type"] == "object":
172 result["properties"] = [] 190 result["properties"] = []
173 elif typed_object["type"] == "array": 191 elif typed_object["type"] == "array":
174 result["items"] = typed_object["items"] 192 result["items"] = typed_object["items"]
175 return result 193 return result
176 elif "$ref" in typed_object: 194 elif "$ref" in typed_object:
177 ref = typed_object["$ref"] 195 ref = typed_object["$ref"]
178 if not ref in types_map: 196 if not ref in types_map:
179 errors.append("Can not resolve type: %s" % ref) 197 errors.append("Can not resolve type: %s" % ref)
180 types_map[ref] = { "id": "<transient>", "type": "object" } 198 types_map[ref] = { "id": "<transient>", "type": "object" }
181 return types_map[ref] 199 return types_map[ref]
182 200
201
183 def normalize_types_in_schema(schema): 202 def normalize_types_in_schema(schema):
184 types = {} 203 types = {}
185 for domain in schema: 204 for domain in schema:
186 domain_name = domain["domain"] 205 domain_name = domain["domain"]
187 normalize_types(domain, domain_name, types) 206 normalize_types(domain, domain_name, types)
188 return types 207 return types
189 208
209
190 def normalize_types(obj, domain_name, types): 210 def normalize_types(obj, domain_name, types):
191 if isinstance(obj, list): 211 if isinstance(obj, list):
192 for item in obj: 212 for item in obj:
193 normalize_types(item, domain_name, types) 213 normalize_types(item, domain_name, types)
194 elif isinstance(obj, dict): 214 elif isinstance(obj, dict):
195 for key, value in obj.items(): 215 for key, value in obj.items():
196 if key == "$ref" and value.find(".") == -1: 216 if key == "$ref" and value.find(".") == -1:
197 obj[key] = "%s.%s" % (domain_name, value) 217 obj[key] = "%s.%s" % (domain_name, value)
198 elif key == "id": 218 elif key == "id":
199 obj[key] = "%s.%s" % (domain_name, value) 219 obj[key] = "%s.%s" % (domain_name, value)
200 types[obj[key]] = obj 220 types[obj[key]] = obj
201 else: 221 else:
202 normalize_types(value, domain_name, types) 222 normalize_types(value, domain_name, types)
203 223
204 def load_json(filename): 224
205 input_file = open(filename, "r") 225 def load_domain(file, domains):
226 if not os.path.isfile(file):
227 return
228 input_file = open(file, "r")
206 json_string = input_file.read() 229 json_string = input_file.read()
207 json_string = re.sub(":\s*true", ": True", json_string) 230 parsed_json = json.loads(json_string, object_pairs_hook=collections.OrderedD ict)
208 json_string = re.sub(":\s*false", ": False", json_string) 231 domains.append(parsed_json)
209 return eval(json_string) 232
233
234 def load_domains(folder, domains):
235 for f in os.listdir(folder):
236 filename = os.path.join(folder, f)
237 if filename.endswith(".json") and os.path.isfile(filename):
238 load_domain(filename, domains)
239
210 240
211 def self_test(): 241 def self_test():
212 def create_test_schema_1(): 242 def create_test_schema_1():
213 return [ 243 return [
214 { 244 {
215 "domain": "Network", 245 "domain": "Network",
216 "types": [ 246 "types": [
217 { 247 {
218 "id": "LoaderId", 248 "id": "LoaderId",
219 "type": "string" 249 "type": "string"
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 def errors_match(expected, actual): 415 def errors_match(expected, actual):
386 return (is_subset(actual, expected, "Unexpected") and 416 return (is_subset(actual, expected, "Unexpected") and
387 is_subset(expected, actual, "Missing")) 417 is_subset(expected, actual, "Missing"))
388 418
389 return (errors_match(expected_errors, 419 return (errors_match(expected_errors,
390 compare_schemas(create_test_schema_1(), create_test_sch ema_2(), False)) and 420 compare_schemas(create_test_schema_1(), create_test_sch ema_2(), False)) and
391 errors_match(expected_errors_reverse, 421 errors_match(expected_errors_reverse,
392 compare_schemas(create_test_schema_2(), create_test_sch ema_1(), True))) 422 compare_schemas(create_test_schema_2(), create_test_sch ema_1(), True)))
393 423
394 424
425
426 def load_domains_and_baselines(folder, domains, baseline_domains):
427 load_domains(os.path.normpath(folder + "/protocol"), domains)
428 for domain in domains:
429 version = "%s.%s" % (domain["version"]["major"], domain["version"]["mino r"])
430 domain_path = folder + "/protocol-" + version + "/" + domain["domain"] + ".json"
431 load_domain(os.path.normpath(domain_path), baseline_domains)
432
433
395 def main(): 434 def main():
396 if not self_test(): 435 if not self_test():
397 sys.stderr.write("Self-test failed") 436 sys.stderr.write("Self-test failed")
398 return 1 437 return 1
399 438
400 if len(sys.argv) < 4 or sys.argv[1] != "-o": 439 cmdline_parser = optparse.OptionParser()
401 sys.stderr.write("Usage: %s -o OUTPUT_FILE INPUT_FILE [--show-changes]\n " % sys.argv[0]) 440 cmdline_parser.add_option("--show_changes")
441 cmdline_parser.add_option("--o")
442 arg_options, arg_values = cmdline_parser.parse_args()
443
444 if len(arg_values) < 1 or not arg_options.o:
445 sys.stderr.write("Usage: %s --o OUTPUT_FILE [--show_changes] PROTOCOL_FO LDER1 ?PROTOCOL_FOLDER2 \n" % sys.argv[0])
402 return 1 446 return 1
403 447
404 output_path = sys.argv[2] 448 output_path = arg_options.o
405 output_file = open(output_path, "w") 449 output_file = open(output_path, "w")
406 450
407 input_path = sys.argv[3] 451 domains = []
408 dir_name = os.path.dirname(input_path) 452 baseline_domains = []
409 schema = load_json(input_path) 453 load_domains_and_baselines(arg_values[0], domains, baseline_domains)
454 if len(arg_values) > 1:
455 load_domains_and_baselines(arg_values[1], domains, baseline_domains)
410 456
411 major = schema["version"]["major"] 457 domains_copy = copy.deepcopy(domains)
dgozman 2016/06/03 02:24:03 deepcopy inside compare_schemas
pfeldman 2016/06/03 17:07:32 Done.
412 minor = schema["version"]["minor"] 458 baseline_domains_copy = copy.deepcopy(baseline_domains)
413 version = "%s.%s" % (major, minor) 459 if arg_options.show_changes:
414 if len(dir_name) == 0: 460 domains_copy_2 = copy.deepcopy(domains)
415 dir_name = "." 461 baseline_domains_copy_2 = copy.deepcopy(baseline_domains)
416 baseline_path = os.path.normpath(dir_name + "/Inspector-" + version + ".json ")
417 baseline_schema = load_json(baseline_path)
418 462
419 expected_errors = [ 463 expected_errors = [
420 "Debugger.globalObjectCleared: event has been removed" 464 "Debugger.globalObjectCleared: event has been removed"
421 ] 465 ]
422 466
423 errors = compare_schemas(baseline_schema["domains"], schema["domains"], Fals e) 467 errors = compare_schemas(baseline_domains, domains, False)
424 unexpected_errors = [] 468 unexpected_errors = []
425 for i in range(len(errors)): 469 for i in range(len(errors)):
426 if errors[i] not in expected_errors: 470 if errors[i] not in expected_errors:
427 unexpected_errors.append(errors[i]) 471 unexpected_errors.append(errors[i])
428 if len(unexpected_errors) > 0: 472 if len(unexpected_errors) > 0:
429 sys.stderr.write(" Compatibility with %s: FAILED\n" % version) 473 sys.stderr.write(" Compatibility checks FAILED\n")
430 for error in unexpected_errors: 474 for error in unexpected_errors:
431 sys.stderr.write( " %s\n" % error) 475 sys.stderr.write( " %s\n" % error)
432 return 1 476 return 1
433 477
434 if len(sys.argv) > 4 and sys.argv[4] == "--show-changes": 478 if arg_options.show_changes:
435 changes = compare_schemas( 479 changes = compare_schemas(domains_copy_2, baseline_domains_copy_2, True)
436 load_json(input_path)["domains"], load_json(baseline_path)["domains" ], True)
437 if len(changes) > 0: 480 if len(changes) > 0:
438 print " Public changes since %s:" % version 481 print " Public changes since %s:" % version
439 for change in changes: 482 for change in changes:
440 print " %s" % change 483 print " %s" % change
441 484
442 output_file.write(""" 485 json.dump({"domains": domains_copy}, output_file, indent=4, sort_keys=False, separators=(',', ': '))
443 #ifndef InspectorProtocolVersion_h
444 #define InspectorProtocolVersion_h
445
446 #include "platform/inspector_protocol/String16.h"
447
448 namespace blink {
449
450 String inspectorProtocolVersion() { return "%s"; }
451
452 int inspectorProtocolVersionMajor() { return %s; }
453
454 int inspectorProtocolVersionMinor() { return %s; }
455
456 }
457
458 #endif // !defined(InspectorProtocolVersion_h)
459 """ % (version, major, minor))
460
461 output_file.close() 486 output_file.close()
462 487
463 if __name__ == '__main__': 488 if __name__ == '__main__':
464 sys.exit(main()) 489 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698