File: test-p1689.py

package info (click to toggle)
gcc-arm-none-eabi 15%3A14.2.rel1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,099,328 kB
  • sloc: cpp: 3,627,108; ansic: 2,571,498; ada: 834,230; f90: 235,082; makefile: 79,231; asm: 74,984; xml: 51,692; exp: 39,736; sh: 33,298; objc: 15,629; python: 15,069; fortran: 14,429; pascal: 7,003; awk: 5,070; perl: 3,106; ml: 285; lisp: 253; lex: 204; haskell: 135
file content (222 lines) | stat: -rw-r--r-- 6,020 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import json


# Parameters.
ALL_ERRORS = False
REPLACEMENTS = {}


def _print_path(path):
    '''Format a JSON path for output.'''
    return '/'.join(path)


def _report_error(msg):
    '''Report an error.'''
    full_msg = 'ERROR: ' + msg
    if ALL_ERRORS:
        print(full_msg)
    else:
        raise RuntimeError(full_msg)


def _error_type_mismatch(path, actual, expect):
    '''Report that there is a type mismatch.'''
    _report_error('type mismatch at %s: actual: "%s" expect: "%s"' % (_print_path(path), actual, expect))


def _error_unknown_type(path, typ):
    '''Report that there is an unknown type in the JSON object.'''
    _report_error('unknown type at %s: "%s"' % (_print_path(path), typ))


def _error_length_mismatch(path, actual, expect):
    '''Report a length mismatch in an object or array.'''
    _report_error('length mismatch at %s: actual: "%s" expect: "%s"' % (_print_path(path), actual, expect))


def _error_unexpect_value(path, actual, expect):
    '''Report a value mismatch.'''
    _report_error('value mismatch at %s: actual: "%s" expect: "%s"' % (_print_path(path), actual, expect))


def _error_extra_key(path, key):
    '''Report on a key that is unexpected.'''
    _report_error('extra key at %s: "%s"' % (_print_path(path), key))


def _error_missing_key(path, key):
    '''Report on a key that is missing.'''
    _report_error('extra key at %s: %s' % (_print_path(path), key))


def _compare_object(path, actual, expect):
    '''Compare a JSON object.'''
    is_ok = True

    if not len(actual) == len(expect):
        _error_length_mismatch(path, len(actual), len(expect))
        is_ok = False

    for key in actual:
        if key not in expect:
            _error_extra_key(path, key)
            is_ok = False
        else:
            sub_error = compare_json(path + [key], actual[key], expect[key])
            if sub_error:
                is_ok = False

    for key in expect:
        if key not in actual:
            _error_missing_key(path, key)
            is_ok = False

    return is_ok


def _compare_array(path, actual, expect):
    '''Compare a JSON array.'''
    is_ok = True

    if not len(actual) == len(expect):
        _error_length_mismatch(path, len(actual), len(expect))
        is_ok = False

    for (idx, (a, e)) in enumerate(zip(actual, expect)):
        sub_error = compare_json(path + [str(idx)], a, e)
        if sub_error:
            is_ok = False

    return is_ok


def _make_replacements(value):
    for (old, new) in REPLACEMENTS.values():
        value = value.replace(old, new)
    return value


def _compare_string(path, actual, expect):
    '''Compare a JSON string supporting replacements in the expected output.'''
    expect = _make_replacements(expect)

    if not actual == expect:
        _error_unexpect_value(path, actual, expect)
        return False
    else:
        print('%s is ok: %s' % (_print_path(path), actual))
    return True


def _compare_number(path, actual, expect):
    '''Compare a JSON integer.'''
    if not actual == expect:
        _error_unexpect_value(path, actual, expect)
        return False
    else:
        print('%s is ok: %s' % (_print_path(path), actual))
    return True


def _inspect_ordering(arr):
    req_ordering = True

    if not arr:
        return arr, req_ordering

    if arr[0] == '__P1689_unordered__':
        arr.pop(0)
        req_ordering = False

    return arr, req_ordering


def compare_json(path, actual, expect):
    actual_type = type(actual)
    expect_type = type(expect)

    is_ok = True

    if not actual_type == expect_type:
        _error_type_mismatch(path, actual_type, expect_type)
        is_ok = False
    elif actual_type == dict:
        is_ok = _compare_object(path, actual, expect)
    elif actual_type == list:
        expect, req_ordering = _inspect_ordering(expect)
        if not req_ordering:
            actual = set(actual)
            expect = set(expect)
        is_ok = _compare_array(path, actual, expect)
    elif actual_type == str:
        is_ok = _compare_string(path, actual, expect)
    elif actual_type == float:
        is_ok = _compare_number(path, actual, expect)
    elif actual_type == int:
        is_ok = _compare_number(path, actual, expect)
    elif actual_type == bool:
        is_ok = _compare_number(path, actual, expect)
    elif actual_type == type(None):
        pass
    else:
        _error_unknown_type(path, actual_type)
        is_ok = False

    return is_ok


def validate_p1689(actual, expect):
    '''Validate a P1689 file against an expected output file.

    Returns `False` if it fails, `True` if they are the same.
    '''
    with open(actual, 'r') as fin:
        actual_content = fin.read()
    with open(expect, 'r') as fin:
        expect_content = fin.read()

    actual_json = json.loads(actual_content)
    expect_json = json.loads(expect_content)

    return compare_json([], actual_json, expect_json)


if __name__ == '__main__':
    import sys

    actual = None
    expect = None

    # Parse arguments.
    args = sys.argv[1:]
    while args:
        # Take an argument.
        arg = args.pop(0)

        # Parse out replacement expressions.
        if arg == '-r' or arg == '--replace':
            replacement = args.pop(0)
            (key, value) = replacement.split('=', maxsplit=1)
            REPLACEMENTS[key] = value
        # Flag to change how errors are reported.
        elif arg == '-A' or arg == '--all':
            ALL_ERRORS = True
        # Required arguments.
        elif arg == '-a' or arg == '--actual':
            actual = args.pop(0)
        elif arg == '-e' or arg == '--expect':
            expect = args.pop(0)

    # Validate that we have the required arguments.
    if actual is None:
        raise RuntimeError('missing "actual" file')
    if expect is None:
        raise RuntimeError('missing "expect" file')

    # Do the actual work.
    is_ok = validate_p1689(actual, expect)

    # Fail if errors are found.
    if not is_ok:
        sys.exit(1)