1import sys
2import os
3import json
4import re
5
6if sys.version_info[0] >= 3:
7    unicode = str
8
9def is_bool(x, val=None):
10    return isinstance(x, bool) and (val is None or x == val)
11
12def is_dict(x):
13    return isinstance(x, dict)
14
15def is_list(x):
16    return isinstance(x, list)
17
18def is_int(x, val=None):
19    return (isinstance(x, int) or isinstance(x, long)) and (val is None or x == val)
20
21def is_string(x, val=None):
22    return (isinstance(x, str) or isinstance(x, unicode)) and (val is None or x == val)
23
24def matches(s, pattern):
25    return is_string(s) and bool(re.search(pattern, s))
26
27def check_list_match(match, actual, expected, check=None, check_exception=None, missing_exception=None, extra_exception=None, allow_extra=False):
28    """
29    Handle the common pattern of making sure every actual item "matches" some
30    item in the expected list, and that neither list has extra items after
31    matching is completed.
32
33    @param match: Callback to check if an actual item matches an expected
34    item. Return True if the item matches, return False if the item doesn't
35    match.
36    @param actual: List of actual items to search.
37    @param expected: List of expected items to match.
38    @param check: Optional function to check that the actual item is valid by
39    comparing it to the expected item.
40    @param check_exception: Optional function that returns an argument to
41    append to any exception thrown by the check function.
42    @param missing_exception: Optional function that returns an argument to
43    append to the exception thrown when an item is not found.
44    @param extra_exception: Optional function that returns an argument to
45    append to the exception thrown when an extra item is found.
46    @param allow_extra: Optional parameter allowing there to be extra actual
47    items after all the expected items have been found.
48    """
49    assert is_list(actual)
50    _actual = actual[:]
51    for expected_item in expected:
52        found = False
53        for i, actual_item in enumerate(_actual):
54            if match(actual_item, expected_item):
55                if check:
56                    try:
57                        check(actual_item, expected_item)
58                    except BaseException as e:
59                        if check_exception:
60                            e.args += (check_exception(actual_item, expected_item),)
61                        raise
62                found = True
63                del _actual[i]
64                break
65        if missing_exception:
66            assert found, missing_exception(expected_item)
67        else:
68            assert found
69    if not allow_extra:
70        if extra_exception:
71            assert len(_actual) == 0, [extra_exception(a) for a in _actual]
72        else:
73            assert len(_actual) == 0
74
75def filter_list(f, l):
76    if l is not None:
77        l = list(filter(f, l))
78    if l == []:
79        l = None
80    return l
81
82def check_cmake(cmake):
83    assert is_dict(cmake)
84    assert sorted(cmake.keys()) == ["generator", "paths", "version"]
85    check_cmake_version(cmake["version"])
86    check_cmake_paths(cmake["paths"])
87    check_cmake_generator(cmake["generator"])
88
89def check_cmake_version(v):
90    assert is_dict(v)
91    assert sorted(v.keys()) == ["isDirty", "major", "minor", "patch", "string", "suffix"]
92    assert is_string(v["string"])
93    assert is_int(v["major"])
94    assert is_int(v["minor"])
95    assert is_int(v["patch"])
96    assert is_string(v["suffix"])
97    assert is_bool(v["isDirty"])
98
99def check_cmake_paths(v):
100    assert is_dict(v)
101    assert sorted(v.keys()) == ["cmake", "cpack", "ctest", "root"]
102    assert is_string(v["cmake"])
103    assert is_string(v["cpack"])
104    assert is_string(v["ctest"])
105    assert is_string(v["root"])
106
107def check_cmake_generator(g):
108    assert is_dict(g)
109    name = g.get("name", None)
110    assert is_string(name)
111    if name.startswith("Visual Studio"):
112        assert sorted(g.keys()) == ["multiConfig", "name", "platform"]
113        assert is_string(g["platform"])
114    else:
115        assert sorted(g.keys()) == ["multiConfig", "name"]
116    assert is_bool(g["multiConfig"], matches(name, "^(Visual Studio |Xcode$|Ninja Multi-Config$)"))
117
118def check_index_object(indexEntry, kind, major, minor, check):
119    assert is_dict(indexEntry)
120    assert sorted(indexEntry.keys()) == ["jsonFile", "kind", "version"]
121    assert is_string(indexEntry["kind"])
122    assert indexEntry["kind"] == kind
123    assert is_dict(indexEntry["version"])
124    assert sorted(indexEntry["version"].keys()) == ["major", "minor"]
125    assert indexEntry["version"]["major"] == major
126    assert indexEntry["version"]["minor"] == minor
127    assert is_string(indexEntry["jsonFile"])
128    filepath = os.path.join(reply_dir, indexEntry["jsonFile"])
129    with open(filepath) as f:
130        object = json.load(f)
131    assert is_dict(object)
132    assert "kind" in object
133    assert is_string(object["kind"])
134    assert object["kind"] == kind
135    assert "version" in object
136    assert is_dict(object["version"])
137    assert sorted(object["version"].keys()) == ["major", "minor"]
138    assert object["version"]["major"] == major
139    assert object["version"]["minor"] == minor
140    if check:
141        check(object)
142
143def check_index__test(indexEntry, major, minor):
144    def check(object):
145        assert sorted(object.keys()) == ["kind", "version"]
146    check_index_object(indexEntry, "__test", major, minor, check)
147
148def check_error(value, error):
149    assert is_dict(value)
150    assert sorted(value.keys()) == ["error"]
151    assert is_string(value["error"])
152    assert value["error"] == error
153
154def check_error_re(value, error):
155    assert is_dict(value)
156    assert sorted(value.keys()) == ["error"]
157    assert is_string(value["error"])
158    assert re.search(error, value["error"])
159
160reply_index = sys.argv[1]
161reply_dir = os.path.dirname(reply_index)
162
163with open(reply_index) as f:
164    index = json.load(f)
165