11cb0ef41Sopenharmony_ci#!/usr/bin/env python
21cb0ef41Sopenharmony_ci# Copyright (c) 2011 Google Inc. All rights reserved.
31cb0ef41Sopenharmony_ci#
41cb0ef41Sopenharmony_ci# Redistribution and use in source and binary forms, with or without
51cb0ef41Sopenharmony_ci# modification, are permitted provided that the following conditions are
61cb0ef41Sopenharmony_ci# met:
71cb0ef41Sopenharmony_ci#
81cb0ef41Sopenharmony_ci#     * Redistributions of source code must retain the above copyright
91cb0ef41Sopenharmony_ci# notice, this list of conditions and the following disclaimer.
101cb0ef41Sopenharmony_ci#     * Redistributions in binary form must reproduce the above
111cb0ef41Sopenharmony_ci# copyright notice, this list of conditions and the following disclaimer
121cb0ef41Sopenharmony_ci# in the documentation and/or other materials provided with the
131cb0ef41Sopenharmony_ci# distribution.
141cb0ef41Sopenharmony_ci#     * Neither the name of Google Inc. nor the names of its
151cb0ef41Sopenharmony_ci# contributors may be used to endorse or promote products derived from
161cb0ef41Sopenharmony_ci# this software without specific prior written permission.
171cb0ef41Sopenharmony_ci#
181cb0ef41Sopenharmony_ci# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
191cb0ef41Sopenharmony_ci# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
201cb0ef41Sopenharmony_ci# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
211cb0ef41Sopenharmony_ci# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
221cb0ef41Sopenharmony_ci# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
231cb0ef41Sopenharmony_ci# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
241cb0ef41Sopenharmony_ci# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
251cb0ef41Sopenharmony_ci# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
261cb0ef41Sopenharmony_ci# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
271cb0ef41Sopenharmony_ci# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
281cb0ef41Sopenharmony_ci# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
291cb0ef41Sopenharmony_ci#
301cb0ef41Sopenharmony_ci# Inspector protocol validator.
311cb0ef41Sopenharmony_ci#
321cb0ef41Sopenharmony_ci# Tests that subsequent protocol changes are not breaking backwards compatibility.
331cb0ef41Sopenharmony_ci# Following violations are reported:
341cb0ef41Sopenharmony_ci#
351cb0ef41Sopenharmony_ci#   - Domain has been removed
361cb0ef41Sopenharmony_ci#   - Command has been removed
371cb0ef41Sopenharmony_ci#   - Required command parameter was added or changed from optional
381cb0ef41Sopenharmony_ci#   - Required response parameter was removed or changed to optional
391cb0ef41Sopenharmony_ci#   - Event has been removed
401cb0ef41Sopenharmony_ci#   - Required event parameter was removed or changed to optional
411cb0ef41Sopenharmony_ci#   - Parameter type has changed.
421cb0ef41Sopenharmony_ci#
431cb0ef41Sopenharmony_ci# For the parameters with composite types the above checks are also applied
441cb0ef41Sopenharmony_ci# recursively to every property of the type.
451cb0ef41Sopenharmony_ci#
461cb0ef41Sopenharmony_ci# Adding --show_changes to the command line prints out a list of valid public API changes.
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_cifrom __future__ import print_function
491cb0ef41Sopenharmony_ciimport copy
501cb0ef41Sopenharmony_ciimport os.path
511cb0ef41Sopenharmony_ciimport optparse
521cb0ef41Sopenharmony_ciimport sys
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ciimport pdl
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_citry:
571cb0ef41Sopenharmony_ci    import json
581cb0ef41Sopenharmony_ciexcept ImportError:
591cb0ef41Sopenharmony_ci    import simplejson as json
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_cidef list_to_map(items, key):
631cb0ef41Sopenharmony_ci    result = {}
641cb0ef41Sopenharmony_ci    for item in items:
651cb0ef41Sopenharmony_ci        if "experimental" not in item and "hidden" not in item:
661cb0ef41Sopenharmony_ci            result[item[key]] = item
671cb0ef41Sopenharmony_ci    return result
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_cidef named_list_to_map(container, name, key):
711cb0ef41Sopenharmony_ci    if name in container:
721cb0ef41Sopenharmony_ci        return list_to_map(container[name], key)
731cb0ef41Sopenharmony_ci    return {}
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_cidef removed(reverse):
771cb0ef41Sopenharmony_ci    if reverse:
781cb0ef41Sopenharmony_ci        return "added"
791cb0ef41Sopenharmony_ci    return "removed"
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_cidef required(reverse):
831cb0ef41Sopenharmony_ci    if reverse:
841cb0ef41Sopenharmony_ci        return "optional"
851cb0ef41Sopenharmony_ci    return "required"
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_cidef compare_schemas(d_1, d_2, reverse):
891cb0ef41Sopenharmony_ci    errors = []
901cb0ef41Sopenharmony_ci    domains_1 = copy.deepcopy(d_1)
911cb0ef41Sopenharmony_ci    domains_2 = copy.deepcopy(d_2)
921cb0ef41Sopenharmony_ci    types_1 = normalize_types_in_schema(domains_1)
931cb0ef41Sopenharmony_ci    types_2 = normalize_types_in_schema(domains_2)
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci    domains_by_name_1 = list_to_map(domains_1, "domain")
961cb0ef41Sopenharmony_ci    domains_by_name_2 = list_to_map(domains_2, "domain")
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci    for name in domains_by_name_1:
991cb0ef41Sopenharmony_ci        domain_1 = domains_by_name_1[name]
1001cb0ef41Sopenharmony_ci        if name not in domains_by_name_2:
1011cb0ef41Sopenharmony_ci            errors.append("%s: domain has been %s" % (name, removed(reverse)))
1021cb0ef41Sopenharmony_ci            continue
1031cb0ef41Sopenharmony_ci        compare_domains(domain_1, domains_by_name_2[name], types_1, types_2, errors, reverse)
1041cb0ef41Sopenharmony_ci    return errors
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_cidef compare_domains(domain_1, domain_2, types_map_1, types_map_2, errors, reverse):
1081cb0ef41Sopenharmony_ci    domain_name = domain_1["domain"]
1091cb0ef41Sopenharmony_ci    commands_1 = named_list_to_map(domain_1, "commands", "name")
1101cb0ef41Sopenharmony_ci    commands_2 = named_list_to_map(domain_2, "commands", "name")
1111cb0ef41Sopenharmony_ci    for name in commands_1:
1121cb0ef41Sopenharmony_ci        command_1 = commands_1[name]
1131cb0ef41Sopenharmony_ci        if name not in commands_2:
1141cb0ef41Sopenharmony_ci            errors.append("%s.%s: command has been %s" % (domain_1["domain"], name, removed(reverse)))
1151cb0ef41Sopenharmony_ci            continue
1161cb0ef41Sopenharmony_ci        compare_commands(domain_name, command_1, commands_2[name], types_map_1, types_map_2, errors, reverse)
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ci    events_1 = named_list_to_map(domain_1, "events", "name")
1191cb0ef41Sopenharmony_ci    events_2 = named_list_to_map(domain_2, "events", "name")
1201cb0ef41Sopenharmony_ci    for name in events_1:
1211cb0ef41Sopenharmony_ci        event_1 = events_1[name]
1221cb0ef41Sopenharmony_ci        if name not in events_2:
1231cb0ef41Sopenharmony_ci            errors.append("%s.%s: event has been %s" % (domain_1["domain"], name, removed(reverse)))
1241cb0ef41Sopenharmony_ci            continue
1251cb0ef41Sopenharmony_ci        compare_events(domain_name, event_1, events_2[name], types_map_1, types_map_2, errors, reverse)
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_cidef compare_commands(domain_name, command_1, command_2, types_map_1, types_map_2, errors, reverse):
1291cb0ef41Sopenharmony_ci    context = domain_name + "." + command_1["name"]
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_ci    params_1 = named_list_to_map(command_1, "parameters", "name")
1321cb0ef41Sopenharmony_ci    params_2 = named_list_to_map(command_2, "parameters", "name")
1331cb0ef41Sopenharmony_ci    # Note the reversed order: we allow removing but forbid adding parameters.
1341cb0ef41Sopenharmony_ci    compare_params_list(context, "parameter", params_2, params_1, types_map_2, types_map_1, 0, errors, not reverse)
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_ci    returns_1 = named_list_to_map(command_1, "returns", "name")
1371cb0ef41Sopenharmony_ci    returns_2 = named_list_to_map(command_2, "returns", "name")
1381cb0ef41Sopenharmony_ci    compare_params_list(context, "response parameter", returns_1, returns_2, types_map_1, types_map_2, 0, errors, reverse)
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_cidef compare_events(domain_name, event_1, event_2, types_map_1, types_map_2, errors, reverse):
1421cb0ef41Sopenharmony_ci    context = domain_name + "." + event_1["name"]
1431cb0ef41Sopenharmony_ci    params_1 = named_list_to_map(event_1, "parameters", "name")
1441cb0ef41Sopenharmony_ci    params_2 = named_list_to_map(event_2, "parameters", "name")
1451cb0ef41Sopenharmony_ci    compare_params_list(context, "parameter", params_1, params_2, types_map_1, types_map_2, 0, errors, reverse)
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_cidef compare_params_list(context, kind, params_1, params_2, types_map_1, types_map_2, depth, errors, reverse):
1491cb0ef41Sopenharmony_ci    for name in params_1:
1501cb0ef41Sopenharmony_ci        param_1 = params_1[name]
1511cb0ef41Sopenharmony_ci        if name not in params_2:
1521cb0ef41Sopenharmony_ci            if "optional" not in param_1:
1531cb0ef41Sopenharmony_ci                errors.append("%s.%s: required %s has been %s" % (context, name, kind, removed(reverse)))
1541cb0ef41Sopenharmony_ci            continue
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci        param_2 = params_2[name]
1571cb0ef41Sopenharmony_ci        if param_2 and "optional" in param_2 and "optional" not in param_1:
1581cb0ef41Sopenharmony_ci            errors.append("%s.%s: %s %s is now %s" % (context, name, required(reverse), kind, required(not reverse)))
1591cb0ef41Sopenharmony_ci            continue
1601cb0ef41Sopenharmony_ci        type_1 = extract_type(param_1, types_map_1, errors)
1611cb0ef41Sopenharmony_ci        type_2 = extract_type(param_2, types_map_2, errors)
1621cb0ef41Sopenharmony_ci        compare_types(context + "." + name, kind, type_1, type_2, types_map_1, types_map_2, depth, errors, reverse)
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_cidef compare_types(context, kind, type_1, type_2, types_map_1, types_map_2, depth, errors, reverse):
1661cb0ef41Sopenharmony_ci    if depth > 5:
1671cb0ef41Sopenharmony_ci        return
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci    base_type_1 = type_1["type"]
1701cb0ef41Sopenharmony_ci    base_type_2 = type_2["type"]
1711cb0ef41Sopenharmony_ci
1721cb0ef41Sopenharmony_ci    # Binary and string have the same wire representation in JSON.
1731cb0ef41Sopenharmony_ci    if ((base_type_1 == "string" and base_type_2 == "binary") or
1741cb0ef41Sopenharmony_ci        (base_type_2 == "string" and base_type_1 == "binary")):
1751cb0ef41Sopenharmony_ci      return
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci    if base_type_1 != base_type_2:
1781cb0ef41Sopenharmony_ci        errors.append("%s: %s base type mismatch, '%s' vs '%s'" % (context, kind, base_type_1, base_type_2))
1791cb0ef41Sopenharmony_ci    elif base_type_1 == "object":
1801cb0ef41Sopenharmony_ci        params_1 = named_list_to_map(type_1, "properties", "name")
1811cb0ef41Sopenharmony_ci        params_2 = named_list_to_map(type_2, "properties", "name")
1821cb0ef41Sopenharmony_ci        # If both parameters have the same named type use it in the context.
1831cb0ef41Sopenharmony_ci        if "id" in type_1 and "id" in type_2 and type_1["id"] == type_2["id"]:
1841cb0ef41Sopenharmony_ci            type_name = type_1["id"]
1851cb0ef41Sopenharmony_ci        else:
1861cb0ef41Sopenharmony_ci            type_name = "<object>"
1871cb0ef41Sopenharmony_ci        context += " %s->%s" % (kind, type_name)
1881cb0ef41Sopenharmony_ci        compare_params_list(context, "property", params_1, params_2, types_map_1, types_map_2, depth + 1, errors, reverse)
1891cb0ef41Sopenharmony_ci    elif base_type_1 == "array":
1901cb0ef41Sopenharmony_ci        item_type_1 = extract_type(type_1["items"], types_map_1, errors)
1911cb0ef41Sopenharmony_ci        item_type_2 = extract_type(type_2["items"], types_map_2, errors)
1921cb0ef41Sopenharmony_ci        compare_types(context, kind, item_type_1, item_type_2, types_map_1, types_map_2, depth + 1, errors, reverse)
1931cb0ef41Sopenharmony_ci
1941cb0ef41Sopenharmony_ci
1951cb0ef41Sopenharmony_cidef extract_type(typed_object, types_map, errors):
1961cb0ef41Sopenharmony_ci    if "type" in typed_object:
1971cb0ef41Sopenharmony_ci        result = {"id": "<transient>", "type": typed_object["type"]}
1981cb0ef41Sopenharmony_ci        if typed_object["type"] == "object":
1991cb0ef41Sopenharmony_ci            result["properties"] = []
2001cb0ef41Sopenharmony_ci        elif typed_object["type"] == "array":
2011cb0ef41Sopenharmony_ci            result["items"] = typed_object["items"]
2021cb0ef41Sopenharmony_ci        return result
2031cb0ef41Sopenharmony_ci    elif "$ref" in typed_object:
2041cb0ef41Sopenharmony_ci        ref = typed_object["$ref"]
2051cb0ef41Sopenharmony_ci        if ref not in types_map:
2061cb0ef41Sopenharmony_ci            errors.append("Can not resolve type: %s" % ref)
2071cb0ef41Sopenharmony_ci            types_map[ref] = {"id": "<transient>", "type": "object"}
2081cb0ef41Sopenharmony_ci        return types_map[ref]
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_cidef normalize_types_in_schema(domains):
2121cb0ef41Sopenharmony_ci    types = {}
2131cb0ef41Sopenharmony_ci    for domain in domains:
2141cb0ef41Sopenharmony_ci        domain_name = domain["domain"]
2151cb0ef41Sopenharmony_ci        normalize_types(domain, domain_name, types)
2161cb0ef41Sopenharmony_ci    return types
2171cb0ef41Sopenharmony_ci
2181cb0ef41Sopenharmony_ci
2191cb0ef41Sopenharmony_cidef normalize_types(obj, domain_name, types):
2201cb0ef41Sopenharmony_ci    if isinstance(obj, list):
2211cb0ef41Sopenharmony_ci        for item in obj:
2221cb0ef41Sopenharmony_ci            normalize_types(item, domain_name, types)
2231cb0ef41Sopenharmony_ci    elif isinstance(obj, dict):
2241cb0ef41Sopenharmony_ci        for key, value in obj.items():
2251cb0ef41Sopenharmony_ci            if key == "$ref" and value.find(".") == -1:
2261cb0ef41Sopenharmony_ci                obj[key] = "%s.%s" % (domain_name, value)
2271cb0ef41Sopenharmony_ci            elif key == "id":
2281cb0ef41Sopenharmony_ci                obj[key] = "%s.%s" % (domain_name, value)
2291cb0ef41Sopenharmony_ci                types[obj[key]] = obj
2301cb0ef41Sopenharmony_ci            else:
2311cb0ef41Sopenharmony_ci                normalize_types(value, domain_name, types)
2321cb0ef41Sopenharmony_ci
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_cidef load_schema(file_name, domains):
2351cb0ef41Sopenharmony_ci    # pylint: disable=W0613
2361cb0ef41Sopenharmony_ci    if not os.path.isfile(file_name):
2371cb0ef41Sopenharmony_ci        return
2381cb0ef41Sopenharmony_ci    input_file = open(file_name, "r")
2391cb0ef41Sopenharmony_ci    parsed_json = pdl.loads(input_file.read(), file_name)
2401cb0ef41Sopenharmony_ci    input_file.close()
2411cb0ef41Sopenharmony_ci    domains += parsed_json["domains"]
2421cb0ef41Sopenharmony_ci    return parsed_json["version"]
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci
2451cb0ef41Sopenharmony_cidef self_test():
2461cb0ef41Sopenharmony_ci    def create_test_schema_1():
2471cb0ef41Sopenharmony_ci        return [
2481cb0ef41Sopenharmony_ci            {
2491cb0ef41Sopenharmony_ci                "domain": "Network",
2501cb0ef41Sopenharmony_ci                "types": [
2511cb0ef41Sopenharmony_ci                    {
2521cb0ef41Sopenharmony_ci                        "id": "LoaderId",
2531cb0ef41Sopenharmony_ci                        "type": "string"
2541cb0ef41Sopenharmony_ci                    },
2551cb0ef41Sopenharmony_ci                    {
2561cb0ef41Sopenharmony_ci                        "id": "Headers",
2571cb0ef41Sopenharmony_ci                        "type": "object"
2581cb0ef41Sopenharmony_ci                    },
2591cb0ef41Sopenharmony_ci                    {
2601cb0ef41Sopenharmony_ci                        "id": "Request",
2611cb0ef41Sopenharmony_ci                        "type": "object",
2621cb0ef41Sopenharmony_ci                        "properties": [
2631cb0ef41Sopenharmony_ci                            {"name": "url", "type": "string"},
2641cb0ef41Sopenharmony_ci                            {"name": "method", "type": "string"},
2651cb0ef41Sopenharmony_ci                            {"name": "headers", "$ref": "Headers"},
2661cb0ef41Sopenharmony_ci                            {"name": "becameOptionalField", "type": "string"},
2671cb0ef41Sopenharmony_ci                            {"name": "removedField", "type": "string"},
2681cb0ef41Sopenharmony_ci                        ]
2691cb0ef41Sopenharmony_ci                    }
2701cb0ef41Sopenharmony_ci                ],
2711cb0ef41Sopenharmony_ci                "commands": [
2721cb0ef41Sopenharmony_ci                    {
2731cb0ef41Sopenharmony_ci                        "name": "removedCommand",
2741cb0ef41Sopenharmony_ci                    },
2751cb0ef41Sopenharmony_ci                    {
2761cb0ef41Sopenharmony_ci                        "name": "setExtraHTTPHeaders",
2771cb0ef41Sopenharmony_ci                        "parameters": [
2781cb0ef41Sopenharmony_ci                            {"name": "headers", "$ref": "Headers"},
2791cb0ef41Sopenharmony_ci                            {"name": "mismatched", "type": "string"},
2801cb0ef41Sopenharmony_ci                            {"name": "becameOptional", "$ref": "Headers"},
2811cb0ef41Sopenharmony_ci                            {"name": "removedRequired", "$ref": "Headers"},
2821cb0ef41Sopenharmony_ci                            {"name": "becameRequired", "$ref": "Headers", "optional": True},
2831cb0ef41Sopenharmony_ci                            {"name": "removedOptional", "$ref": "Headers", "optional": True},
2841cb0ef41Sopenharmony_ci                        ],
2851cb0ef41Sopenharmony_ci                        "returns": [
2861cb0ef41Sopenharmony_ci                            {"name": "mimeType", "type": "string"},
2871cb0ef41Sopenharmony_ci                            {"name": "becameOptional", "type": "string"},
2881cb0ef41Sopenharmony_ci                            {"name": "removedRequired", "type": "string"},
2891cb0ef41Sopenharmony_ci                            {"name": "becameRequired", "type": "string", "optional": True},
2901cb0ef41Sopenharmony_ci                            {"name": "removedOptional", "type": "string", "optional": True},
2911cb0ef41Sopenharmony_ci                        ]
2921cb0ef41Sopenharmony_ci                    }
2931cb0ef41Sopenharmony_ci                ],
2941cb0ef41Sopenharmony_ci                "events": [
2951cb0ef41Sopenharmony_ci                    {
2961cb0ef41Sopenharmony_ci                        "name": "requestWillBeSent",
2971cb0ef41Sopenharmony_ci                        "parameters": [
2981cb0ef41Sopenharmony_ci                            {"name": "frameId", "type": "string", "experimental": True},
2991cb0ef41Sopenharmony_ci                            {"name": "request", "$ref": "Request"},
3001cb0ef41Sopenharmony_ci                            {"name": "becameOptional", "type": "string"},
3011cb0ef41Sopenharmony_ci                            {"name": "removedRequired", "type": "string"},
3021cb0ef41Sopenharmony_ci                            {"name": "becameRequired", "type": "string", "optional": True},
3031cb0ef41Sopenharmony_ci                            {"name": "removedOptional", "type": "string", "optional": True},
3041cb0ef41Sopenharmony_ci                        ]
3051cb0ef41Sopenharmony_ci                    },
3061cb0ef41Sopenharmony_ci                    {
3071cb0ef41Sopenharmony_ci                        "name": "removedEvent",
3081cb0ef41Sopenharmony_ci                        "parameters": [
3091cb0ef41Sopenharmony_ci                            {"name": "errorText", "type": "string"},
3101cb0ef41Sopenharmony_ci                            {"name": "canceled", "type": "boolean", "optional": True}
3111cb0ef41Sopenharmony_ci                        ]
3121cb0ef41Sopenharmony_ci                    }
3131cb0ef41Sopenharmony_ci                ]
3141cb0ef41Sopenharmony_ci            },
3151cb0ef41Sopenharmony_ci            {
3161cb0ef41Sopenharmony_ci                "domain":  "removedDomain"
3171cb0ef41Sopenharmony_ci            }
3181cb0ef41Sopenharmony_ci        ]
3191cb0ef41Sopenharmony_ci
3201cb0ef41Sopenharmony_ci    def create_test_schema_2():
3211cb0ef41Sopenharmony_ci        return [
3221cb0ef41Sopenharmony_ci            {
3231cb0ef41Sopenharmony_ci                "domain": "Network",
3241cb0ef41Sopenharmony_ci                "types": [
3251cb0ef41Sopenharmony_ci                    {
3261cb0ef41Sopenharmony_ci                        "id": "LoaderId",
3271cb0ef41Sopenharmony_ci                        "type": "string"
3281cb0ef41Sopenharmony_ci                    },
3291cb0ef41Sopenharmony_ci                    {
3301cb0ef41Sopenharmony_ci                        "id": "Request",
3311cb0ef41Sopenharmony_ci                        "type": "object",
3321cb0ef41Sopenharmony_ci                        "properties": [
3331cb0ef41Sopenharmony_ci                            {"name": "url", "type": "string"},
3341cb0ef41Sopenharmony_ci                            {"name": "method", "type": "string"},
3351cb0ef41Sopenharmony_ci                            {"name": "headers", "type": "object"},
3361cb0ef41Sopenharmony_ci                            {"name": "becameOptionalField", "type": "string", "optional": True},
3371cb0ef41Sopenharmony_ci                        ]
3381cb0ef41Sopenharmony_ci                    }
3391cb0ef41Sopenharmony_ci                ],
3401cb0ef41Sopenharmony_ci                "commands": [
3411cb0ef41Sopenharmony_ci                    {
3421cb0ef41Sopenharmony_ci                        "name": "addedCommand",
3431cb0ef41Sopenharmony_ci                    },
3441cb0ef41Sopenharmony_ci                    {
3451cb0ef41Sopenharmony_ci                        "name": "setExtraHTTPHeaders",
3461cb0ef41Sopenharmony_ci                        "parameters": [
3471cb0ef41Sopenharmony_ci                            {"name": "headers", "type": "object"},
3481cb0ef41Sopenharmony_ci                            {"name": "mismatched", "type": "object"},
3491cb0ef41Sopenharmony_ci                            {"name": "becameOptional", "type": "object", "optional": True},
3501cb0ef41Sopenharmony_ci                            {"name": "addedRequired", "type": "object"},
3511cb0ef41Sopenharmony_ci                            {"name": "becameRequired", "type": "object"},
3521cb0ef41Sopenharmony_ci                            {"name": "addedOptional", "type": "object", "optional": True},
3531cb0ef41Sopenharmony_ci                        ],
3541cb0ef41Sopenharmony_ci                        "returns": [
3551cb0ef41Sopenharmony_ci                            {"name": "mimeType", "type": "string"},
3561cb0ef41Sopenharmony_ci                            {"name": "becameOptional", "type": "string", "optional": True},
3571cb0ef41Sopenharmony_ci                            {"name": "addedRequired", "type": "string"},
3581cb0ef41Sopenharmony_ci                            {"name": "becameRequired", "type": "string"},
3591cb0ef41Sopenharmony_ci                            {"name": "addedOptional", "type": "string", "optional": True},
3601cb0ef41Sopenharmony_ci                        ]
3611cb0ef41Sopenharmony_ci                    }
3621cb0ef41Sopenharmony_ci                ],
3631cb0ef41Sopenharmony_ci                "events": [
3641cb0ef41Sopenharmony_ci                    {
3651cb0ef41Sopenharmony_ci                        "name": "requestWillBeSent",
3661cb0ef41Sopenharmony_ci                        "parameters": [
3671cb0ef41Sopenharmony_ci                            {"name": "request", "$ref": "Request"},
3681cb0ef41Sopenharmony_ci                            {"name": "becameOptional", "type": "string", "optional": True},
3691cb0ef41Sopenharmony_ci                            {"name": "addedRequired", "type": "string"},
3701cb0ef41Sopenharmony_ci                            {"name": "becameRequired", "type": "string"},
3711cb0ef41Sopenharmony_ci                            {"name": "addedOptional", "type": "string", "optional": True},
3721cb0ef41Sopenharmony_ci                        ]
3731cb0ef41Sopenharmony_ci                    },
3741cb0ef41Sopenharmony_ci                    {
3751cb0ef41Sopenharmony_ci                        "name": "addedEvent"
3761cb0ef41Sopenharmony_ci                    }
3771cb0ef41Sopenharmony_ci                ]
3781cb0ef41Sopenharmony_ci            },
3791cb0ef41Sopenharmony_ci            {
3801cb0ef41Sopenharmony_ci                "domain": "addedDomain"
3811cb0ef41Sopenharmony_ci            }
3821cb0ef41Sopenharmony_ci        ]
3831cb0ef41Sopenharmony_ci
3841cb0ef41Sopenharmony_ci    expected_errors = [
3851cb0ef41Sopenharmony_ci        "removedDomain: domain has been removed",
3861cb0ef41Sopenharmony_ci        "Network.removedCommand: command has been removed",
3871cb0ef41Sopenharmony_ci        "Network.removedEvent: event has been removed",
3881cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, 'object' vs 'string'",
3891cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.addedRequired: required parameter has been added",
3901cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.becameRequired: optional parameter is now required",
3911cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.removedRequired: required response parameter has been removed",
3921cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.becameOptional: required response parameter is now optional",
3931cb0ef41Sopenharmony_ci        "Network.requestWillBeSent.removedRequired: required parameter has been removed",
3941cb0ef41Sopenharmony_ci        "Network.requestWillBeSent.becameOptional: required parameter is now optional",
3951cb0ef41Sopenharmony_ci        "Network.requestWillBeSent.request parameter->Network.Request.removedField: required property has been removed",
3961cb0ef41Sopenharmony_ci        "Network.requestWillBeSent.request parameter->Network.Request.becameOptionalField: required property is now optional",
3971cb0ef41Sopenharmony_ci    ]
3981cb0ef41Sopenharmony_ci
3991cb0ef41Sopenharmony_ci    expected_errors_reverse = [
4001cb0ef41Sopenharmony_ci        "addedDomain: domain has been added",
4011cb0ef41Sopenharmony_ci        "Network.addedEvent: event has been added",
4021cb0ef41Sopenharmony_ci        "Network.addedCommand: command has been added",
4031cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.mismatched: parameter base type mismatch, 'string' vs 'object'",
4041cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.removedRequired: required parameter has been removed",
4051cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.becameOptional: required parameter is now optional",
4061cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.addedRequired: required response parameter has been added",
4071cb0ef41Sopenharmony_ci        "Network.setExtraHTTPHeaders.becameRequired: optional response parameter is now required",
4081cb0ef41Sopenharmony_ci        "Network.requestWillBeSent.becameRequired: optional parameter is now required",
4091cb0ef41Sopenharmony_ci        "Network.requestWillBeSent.addedRequired: required parameter has been added",
4101cb0ef41Sopenharmony_ci    ]
4111cb0ef41Sopenharmony_ci
4121cb0ef41Sopenharmony_ci    def is_subset(subset, superset, message):
4131cb0ef41Sopenharmony_ci        for i in range(len(subset)):
4141cb0ef41Sopenharmony_ci            if subset[i] not in superset:
4151cb0ef41Sopenharmony_ci                sys.stderr.write("%s error: %s\n" % (message, subset[i]))
4161cb0ef41Sopenharmony_ci                return False
4171cb0ef41Sopenharmony_ci        return True
4181cb0ef41Sopenharmony_ci
4191cb0ef41Sopenharmony_ci    def errors_match(expected, actual):
4201cb0ef41Sopenharmony_ci        return (is_subset(actual, expected, "Unexpected") and
4211cb0ef41Sopenharmony_ci                is_subset(expected, actual, "Missing"))
4221cb0ef41Sopenharmony_ci
4231cb0ef41Sopenharmony_ci    return (errors_match(expected_errors,
4241cb0ef41Sopenharmony_ci                         compare_schemas(create_test_schema_1(), create_test_schema_2(), False)) and
4251cb0ef41Sopenharmony_ci            errors_match(expected_errors_reverse,
4261cb0ef41Sopenharmony_ci                         compare_schemas(create_test_schema_2(), create_test_schema_1(), True)))
4271cb0ef41Sopenharmony_ci
4281cb0ef41Sopenharmony_ci
4291cb0ef41Sopenharmony_cidef load_domains_and_baselines(file_name, domains, baseline_domains):
4301cb0ef41Sopenharmony_ci    version = load_schema(os.path.normpath(file_name), domains)
4311cb0ef41Sopenharmony_ci    suffix = "-%s.%s.json" % (version["major"], version["minor"])
4321cb0ef41Sopenharmony_ci    baseline_file = file_name.replace(".json", suffix)
4331cb0ef41Sopenharmony_ci    baseline_file = file_name.replace(".pdl", suffix)
4341cb0ef41Sopenharmony_ci    load_schema(os.path.normpath(baseline_file), baseline_domains)
4351cb0ef41Sopenharmony_ci    return version
4361cb0ef41Sopenharmony_ci
4371cb0ef41Sopenharmony_ci
4381cb0ef41Sopenharmony_cidef main():
4391cb0ef41Sopenharmony_ci    if not self_test():
4401cb0ef41Sopenharmony_ci        sys.stderr.write("Self-test failed")
4411cb0ef41Sopenharmony_ci        return 1
4421cb0ef41Sopenharmony_ci
4431cb0ef41Sopenharmony_ci    cmdline_parser = optparse.OptionParser()
4441cb0ef41Sopenharmony_ci    cmdline_parser.add_option("--show_changes")
4451cb0ef41Sopenharmony_ci    cmdline_parser.add_option("--expected_errors")
4461cb0ef41Sopenharmony_ci    cmdline_parser.add_option("--stamp")
4471cb0ef41Sopenharmony_ci    arg_options, arg_values = cmdline_parser.parse_args()
4481cb0ef41Sopenharmony_ci
4491cb0ef41Sopenharmony_ci    if len(arg_values) < 1:
4501cb0ef41Sopenharmony_ci        sys.stderr.write("Usage: %s [--show_changes] <protocol-1> [, <protocol-2>...]\n" % sys.argv[0])
4511cb0ef41Sopenharmony_ci        return 1
4521cb0ef41Sopenharmony_ci
4531cb0ef41Sopenharmony_ci    domains = []
4541cb0ef41Sopenharmony_ci    baseline_domains = []
4551cb0ef41Sopenharmony_ci    version = load_domains_and_baselines(arg_values[0], domains, baseline_domains)
4561cb0ef41Sopenharmony_ci    for dependency in arg_values[1:]:
4571cb0ef41Sopenharmony_ci        load_domains_and_baselines(dependency, domains, baseline_domains)
4581cb0ef41Sopenharmony_ci
4591cb0ef41Sopenharmony_ci    expected_errors = []
4601cb0ef41Sopenharmony_ci    if arg_options.expected_errors:
4611cb0ef41Sopenharmony_ci        expected_errors_file = open(arg_options.expected_errors, "r")
4621cb0ef41Sopenharmony_ci        expected_errors = json.loads(expected_errors_file.read())["errors"]
4631cb0ef41Sopenharmony_ci        expected_errors_file.close()
4641cb0ef41Sopenharmony_ci
4651cb0ef41Sopenharmony_ci    errors = compare_schemas(baseline_domains, domains, False)
4661cb0ef41Sopenharmony_ci    unexpected_errors = []
4671cb0ef41Sopenharmony_ci    for i in range(len(errors)):
4681cb0ef41Sopenharmony_ci        if errors[i] not in expected_errors:
4691cb0ef41Sopenharmony_ci            unexpected_errors.append(errors[i])
4701cb0ef41Sopenharmony_ci    if len(unexpected_errors) > 0:
4711cb0ef41Sopenharmony_ci        sys.stderr.write("  Compatibility checks FAILED\n")
4721cb0ef41Sopenharmony_ci        for error in unexpected_errors:
4731cb0ef41Sopenharmony_ci            sys.stderr.write("    %s\n" % error)
4741cb0ef41Sopenharmony_ci        return 1
4751cb0ef41Sopenharmony_ci
4761cb0ef41Sopenharmony_ci    if arg_options.show_changes:
4771cb0ef41Sopenharmony_ci        changes = compare_schemas(domains, baseline_domains, True)
4781cb0ef41Sopenharmony_ci        if len(changes) > 0:
4791cb0ef41Sopenharmony_ci            print("  Public changes since %s:" % version)
4801cb0ef41Sopenharmony_ci            for change in changes:
4811cb0ef41Sopenharmony_ci                print("    %s" % change)
4821cb0ef41Sopenharmony_ci
4831cb0ef41Sopenharmony_ci    if arg_options.stamp:
4841cb0ef41Sopenharmony_ci        with open(arg_options.stamp, 'a') as _:
4851cb0ef41Sopenharmony_ci            pass
4861cb0ef41Sopenharmony_ci
4871cb0ef41Sopenharmony_ciif __name__ == '__main__':
4881cb0ef41Sopenharmony_ci    sys.exit(main())
489