1bf215546Sopenharmony_ci#
2bf215546Sopenharmony_ci# Copyright (c) 2020 Valve Corporation
3bf215546Sopenharmony_ci#
4bf215546Sopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci# copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci# to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci# the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci# and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci# Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci#
11bf215546Sopenharmony_ci# The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci# paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci# Software.
14bf215546Sopenharmony_ci#
15bf215546Sopenharmony_ci# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21bf215546Sopenharmony_ci# IN THE SOFTWARE.
22bf215546Sopenharmony_ciimport re
23bf215546Sopenharmony_ciimport sys
24bf215546Sopenharmony_ciimport os.path
25bf215546Sopenharmony_ciimport struct
26bf215546Sopenharmony_ciimport string
27bf215546Sopenharmony_ciimport copy
28bf215546Sopenharmony_cifrom math import floor
29bf215546Sopenharmony_ci
30bf215546Sopenharmony_ciif os.isatty(sys.stdout.fileno()):
31bf215546Sopenharmony_ci    set_red = "\033[31m"
32bf215546Sopenharmony_ci    set_green = "\033[1;32m"
33bf215546Sopenharmony_ci    set_normal = "\033[0m"
34bf215546Sopenharmony_cielse:
35bf215546Sopenharmony_ci    set_red = ''
36bf215546Sopenharmony_ci    set_green = ''
37bf215546Sopenharmony_ci    set_normal = ''
38bf215546Sopenharmony_ci
39bf215546Sopenharmony_ciinitial_code = '''
40bf215546Sopenharmony_ciimport re
41bf215546Sopenharmony_ci
42bf215546Sopenharmony_cidef insert_code(code):
43bf215546Sopenharmony_ci    insert_queue.append(CodeCheck(code, current_position))
44bf215546Sopenharmony_ci
45bf215546Sopenharmony_cidef insert_pattern(pattern):
46bf215546Sopenharmony_ci    insert_queue.append(PatternCheck(pattern, False, current_position))
47bf215546Sopenharmony_ci
48bf215546Sopenharmony_cidef vector_gpr(prefix, name, size, align):
49bf215546Sopenharmony_ci    insert_code(f'{name} = {name}0')
50bf215546Sopenharmony_ci    for i in range(size):
51bf215546Sopenharmony_ci        insert_code(f'{name}{i} = {name}0 + {i}')
52bf215546Sopenharmony_ci    insert_code(f'success = {name}0 + {size - 1} == {name}{size - 1}')
53bf215546Sopenharmony_ci    insert_code(f'success = {name}0 % {align} == 0')
54bf215546Sopenharmony_ci    return f'{prefix}[#{name}0:#{name}{size - 1}]'
55bf215546Sopenharmony_ci
56bf215546Sopenharmony_cidef sgpr_vector(name, size, align):
57bf215546Sopenharmony_ci    return vector_gpr('s', name, size, align)
58bf215546Sopenharmony_ci
59bf215546Sopenharmony_cifuncs.update({
60bf215546Sopenharmony_ci    's64': lambda name: vector_gpr('s', name, 2, 2),
61bf215546Sopenharmony_ci    's96': lambda name: vector_gpr('s', name, 3, 2),
62bf215546Sopenharmony_ci    's128': lambda name: vector_gpr('s', name, 4, 4),
63bf215546Sopenharmony_ci    's256': lambda name: vector_gpr('s', name, 8, 4),
64bf215546Sopenharmony_ci    's512': lambda name: vector_gpr('s', name, 16, 4),
65bf215546Sopenharmony_ci})
66bf215546Sopenharmony_cifor i in range(2, 14):
67bf215546Sopenharmony_ci    funcs['v%d' % (i * 32)] = lambda name: vector_gpr('v', name, i, 1)
68bf215546Sopenharmony_ci
69bf215546Sopenharmony_cidef _match_func(names):
70bf215546Sopenharmony_ci    for name in names.split(' '):
71bf215546Sopenharmony_ci        insert_code(f'funcs["{name}"] = lambda _: {name}')
72bf215546Sopenharmony_ci    return ' '.join(f'${name}' for name in names.split(' '))
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_cifuncs['match_func'] = _match_func
75bf215546Sopenharmony_ci
76bf215546Sopenharmony_cidef search_re(pattern):
77bf215546Sopenharmony_ci    global success
78bf215546Sopenharmony_ci    success = re.search(pattern, output.read_line()) != None and success
79bf215546Sopenharmony_ci
80bf215546Sopenharmony_ci'''
81bf215546Sopenharmony_ci
82bf215546Sopenharmony_ciclass Check:
83bf215546Sopenharmony_ci    def __init__(self, data, position):
84bf215546Sopenharmony_ci        self.data = data.rstrip()
85bf215546Sopenharmony_ci        self.position = position
86bf215546Sopenharmony_ci
87bf215546Sopenharmony_ci    def run(self, state):
88bf215546Sopenharmony_ci        pass
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_ciclass CodeCheck(Check):
91bf215546Sopenharmony_ci    def run(self, state):
92bf215546Sopenharmony_ci        indent = 0
93bf215546Sopenharmony_ci        first_line = [l for l in self.data.split('\n') if l.strip() != ''][0]
94bf215546Sopenharmony_ci        indent_amount = len(first_line) - len(first_line.lstrip())
95bf215546Sopenharmony_ci        indent = first_line[:indent_amount]
96bf215546Sopenharmony_ci        new_lines = []
97bf215546Sopenharmony_ci        for line in self.data.split('\n'):
98bf215546Sopenharmony_ci            if line.strip() == '':
99bf215546Sopenharmony_ci                new_lines.append('')
100bf215546Sopenharmony_ci                continue
101bf215546Sopenharmony_ci            if line[:indent_amount] != indent:
102bf215546Sopenharmony_ci                state.result.log += 'unexpected indent in code check:\n'
103bf215546Sopenharmony_ci                state.result.log += self.data + '\n'
104bf215546Sopenharmony_ci                return False
105bf215546Sopenharmony_ci            new_lines.append(line[indent_amount:])
106bf215546Sopenharmony_ci        code = '\n'.join(new_lines)
107bf215546Sopenharmony_ci
108bf215546Sopenharmony_ci        try:
109bf215546Sopenharmony_ci            exec(code, state.g)
110bf215546Sopenharmony_ci            state.result.log += state.g['log']
111bf215546Sopenharmony_ci            state.g['log'] = ''
112bf215546Sopenharmony_ci        except BaseException as e:
113bf215546Sopenharmony_ci            state.result.log += 'code check at %s raised exception:\n' % self.position
114bf215546Sopenharmony_ci            state.result.log += code + '\n'
115bf215546Sopenharmony_ci            state.result.log += str(e)
116bf215546Sopenharmony_ci            return False
117bf215546Sopenharmony_ci        if not state.g['success']:
118bf215546Sopenharmony_ci            state.result.log += 'code check at %s failed:\n' % self.position
119bf215546Sopenharmony_ci            state.result.log += code + '\n'
120bf215546Sopenharmony_ci            return False
121bf215546Sopenharmony_ci        return True
122bf215546Sopenharmony_ci
123bf215546Sopenharmony_ciclass StringStream:
124bf215546Sopenharmony_ci    class Pos:
125bf215546Sopenharmony_ci        def __init__(self):
126bf215546Sopenharmony_ci            self.line = 1
127bf215546Sopenharmony_ci            self.column = 1
128bf215546Sopenharmony_ci
129bf215546Sopenharmony_ci    def __init__(self, data, name):
130bf215546Sopenharmony_ci        self.name = name
131bf215546Sopenharmony_ci        self.data = data
132bf215546Sopenharmony_ci        self.offset = 0
133bf215546Sopenharmony_ci        self.pos = StringStream.Pos()
134bf215546Sopenharmony_ci
135bf215546Sopenharmony_ci    def reset(self):
136bf215546Sopenharmony_ci        self.offset = 0
137bf215546Sopenharmony_ci        self.pos = StringStream.Pos()
138bf215546Sopenharmony_ci
139bf215546Sopenharmony_ci    def peek(self, num=1):
140bf215546Sopenharmony_ci        return self.data[self.offset:self.offset+num]
141bf215546Sopenharmony_ci
142bf215546Sopenharmony_ci    def peek_test(self, chars):
143bf215546Sopenharmony_ci        c = self.peek(1)
144bf215546Sopenharmony_ci        return c != '' and c in chars
145bf215546Sopenharmony_ci
146bf215546Sopenharmony_ci    def read(self, num=4294967296):
147bf215546Sopenharmony_ci        res = self.peek(num)
148bf215546Sopenharmony_ci        self.offset += len(res)
149bf215546Sopenharmony_ci        for c in res:
150bf215546Sopenharmony_ci            if c == '\n':
151bf215546Sopenharmony_ci                self.pos.line += 1
152bf215546Sopenharmony_ci                self.pos.column = 1
153bf215546Sopenharmony_ci            else:
154bf215546Sopenharmony_ci                self.pos.column += 1
155bf215546Sopenharmony_ci        return res
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci    def get_line(self, num):
158bf215546Sopenharmony_ci        return self.data.split('\n')[num - 1].rstrip()
159bf215546Sopenharmony_ci
160bf215546Sopenharmony_ci    def read_line(self):
161bf215546Sopenharmony_ci        line = ''
162bf215546Sopenharmony_ci        while self.peek(1) not in ['\n', '']:
163bf215546Sopenharmony_ci            line += self.read(1)
164bf215546Sopenharmony_ci        self.read(1)
165bf215546Sopenharmony_ci        return line
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci    def skip_whitespace(self, inc_line):
168bf215546Sopenharmony_ci        chars = [' ', '\t'] + (['\n'] if inc_line else [])
169bf215546Sopenharmony_ci        while self.peek(1) in chars:
170bf215546Sopenharmony_ci            self.read(1)
171bf215546Sopenharmony_ci
172bf215546Sopenharmony_ci    def get_number(self):
173bf215546Sopenharmony_ci        num = ''
174bf215546Sopenharmony_ci        while self.peek() in string.digits:
175bf215546Sopenharmony_ci            num += self.read(1)
176bf215546Sopenharmony_ci        return num
177bf215546Sopenharmony_ci
178bf215546Sopenharmony_ci    def check_identifier(self):
179bf215546Sopenharmony_ci        return self.peek_test(string.ascii_letters + '_')
180bf215546Sopenharmony_ci
181bf215546Sopenharmony_ci    def get_identifier(self):
182bf215546Sopenharmony_ci        res = ''
183bf215546Sopenharmony_ci        if self.check_identifier():
184bf215546Sopenharmony_ci            while self.peek_test(string.ascii_letters + string.digits + '_'):
185bf215546Sopenharmony_ci                res += self.read(1)
186bf215546Sopenharmony_ci        return res
187bf215546Sopenharmony_ci
188bf215546Sopenharmony_cidef format_error_lines(at, line_num, column_num, ctx, line):
189bf215546Sopenharmony_ci    pred = '%s line %d, column %d of %s: "' % (at, line_num, column_num, ctx)
190bf215546Sopenharmony_ci    return [pred + line + '"',
191bf215546Sopenharmony_ci            '-' * (column_num - 1 + len(pred)) + '^']
192bf215546Sopenharmony_ci
193bf215546Sopenharmony_ciclass MatchResult:
194bf215546Sopenharmony_ci    def __init__(self, pattern):
195bf215546Sopenharmony_ci        self.success = True
196bf215546Sopenharmony_ci        self.func_res = None
197bf215546Sopenharmony_ci        self.pattern = pattern
198bf215546Sopenharmony_ci        self.pattern_pos = StringStream.Pos()
199bf215546Sopenharmony_ci        self.output_pos = StringStream.Pos()
200bf215546Sopenharmony_ci        self.fail_message = ''
201bf215546Sopenharmony_ci
202bf215546Sopenharmony_ci    def set_pos(self, pattern, output):
203bf215546Sopenharmony_ci        self.pattern_pos.line = pattern.pos.line
204bf215546Sopenharmony_ci        self.pattern_pos.column = pattern.pos.column
205bf215546Sopenharmony_ci        self.output_pos.line = output.pos.line
206bf215546Sopenharmony_ci        self.output_pos.column = output.pos.column
207bf215546Sopenharmony_ci
208bf215546Sopenharmony_ci    def fail(self, msg):
209bf215546Sopenharmony_ci        self.success = False
210bf215546Sopenharmony_ci        self.fail_message = msg
211bf215546Sopenharmony_ci
212bf215546Sopenharmony_ci    def format_pattern_pos(self):
213bf215546Sopenharmony_ci        pat_pos = self.pattern_pos
214bf215546Sopenharmony_ci        pat_line = self.pattern.get_line(pat_pos.line)
215bf215546Sopenharmony_ci        res = format_error_lines('at', pat_pos.line, pat_pos.column, 'pattern', pat_line)
216bf215546Sopenharmony_ci        func_res = self.func_res
217bf215546Sopenharmony_ci        while func_res:
218bf215546Sopenharmony_ci            pat_pos = func_res.pattern_pos
219bf215546Sopenharmony_ci            pat_line = func_res.pattern.get_line(pat_pos.line)
220bf215546Sopenharmony_ci            res += format_error_lines('in', pat_pos.line, pat_pos.column, func_res.pattern.name, pat_line)
221bf215546Sopenharmony_ci            func_res = func_res.func_res
222bf215546Sopenharmony_ci        return '\n'.join(res)
223bf215546Sopenharmony_ci
224bf215546Sopenharmony_cidef do_match(g, pattern, output, skip_lines, in_func=False):
225bf215546Sopenharmony_ci    assert(not in_func or not skip_lines)
226bf215546Sopenharmony_ci
227bf215546Sopenharmony_ci    if not in_func:
228bf215546Sopenharmony_ci        output.skip_whitespace(False)
229bf215546Sopenharmony_ci    pattern.skip_whitespace(False)
230bf215546Sopenharmony_ci
231bf215546Sopenharmony_ci    old_g = copy.copy(g)
232bf215546Sopenharmony_ci    old_g_keys = list(g.keys())
233bf215546Sopenharmony_ci    res = MatchResult(pattern)
234bf215546Sopenharmony_ci    escape = False
235bf215546Sopenharmony_ci    while True:
236bf215546Sopenharmony_ci        res.set_pos(pattern, output)
237bf215546Sopenharmony_ci
238bf215546Sopenharmony_ci        c = pattern.read(1)
239bf215546Sopenharmony_ci        fail = False
240bf215546Sopenharmony_ci        if c == '':
241bf215546Sopenharmony_ci            break
242bf215546Sopenharmony_ci        elif output.peek() == '':
243bf215546Sopenharmony_ci            res.fail('unexpected end of output')
244bf215546Sopenharmony_ci        elif c == '\\':
245bf215546Sopenharmony_ci            escape = True
246bf215546Sopenharmony_ci            continue
247bf215546Sopenharmony_ci        elif c == '\n':
248bf215546Sopenharmony_ci            old_line = output.pos.line
249bf215546Sopenharmony_ci            output.skip_whitespace(True)
250bf215546Sopenharmony_ci            if output.pos.line == old_line:
251bf215546Sopenharmony_ci                res.fail('expected newline in output')
252bf215546Sopenharmony_ci        elif not escape and c == '#':
253bf215546Sopenharmony_ci            num = output.get_number()
254bf215546Sopenharmony_ci            if num == '':
255bf215546Sopenharmony_ci                res.fail('expected number in output')
256bf215546Sopenharmony_ci            elif pattern.check_identifier():
257bf215546Sopenharmony_ci                name = pattern.get_identifier()
258bf215546Sopenharmony_ci                if name in g and int(num) != g[name]:
259bf215546Sopenharmony_ci                    res.fail('unexpected number for \'%s\': %d (expected %d)' % (name, int(num), g[name]))
260bf215546Sopenharmony_ci                elif name != '_':
261bf215546Sopenharmony_ci                    g[name] = int(num)
262bf215546Sopenharmony_ci        elif not escape and c == '$':
263bf215546Sopenharmony_ci            name = pattern.get_identifier()
264bf215546Sopenharmony_ci
265bf215546Sopenharmony_ci            val = ''
266bf215546Sopenharmony_ci            while not output.peek_test(string.whitespace):
267bf215546Sopenharmony_ci                val += output.read(1)
268bf215546Sopenharmony_ci
269bf215546Sopenharmony_ci            if name in g and val != g[name]:
270bf215546Sopenharmony_ci                res.fail('unexpected value for \'%s\': \'%s\' (expected \'%s\')' % (name, val, g[name]))
271bf215546Sopenharmony_ci            elif name != '_':
272bf215546Sopenharmony_ci                g[name] = val
273bf215546Sopenharmony_ci        elif not escape and c == '%' and pattern.check_identifier():
274bf215546Sopenharmony_ci            if output.read(1) != '%':
275bf215546Sopenharmony_ci                res.fail('expected \'%\' in output')
276bf215546Sopenharmony_ci            else:
277bf215546Sopenharmony_ci                num = output.get_number()
278bf215546Sopenharmony_ci                if num == '':
279bf215546Sopenharmony_ci                    res.fail('expected number in output')
280bf215546Sopenharmony_ci                else:
281bf215546Sopenharmony_ci                    name = pattern.get_identifier()
282bf215546Sopenharmony_ci                    if name in g and int(num) != g[name]:
283bf215546Sopenharmony_ci                        res.fail('unexpected number for \'%s\': %d (expected %d)' % (name, int(num), g[name]))
284bf215546Sopenharmony_ci                    elif name != '_':
285bf215546Sopenharmony_ci                        g[name] = int(num)
286bf215546Sopenharmony_ci        elif not escape and c == '@' and pattern.check_identifier():
287bf215546Sopenharmony_ci            name = pattern.get_identifier()
288bf215546Sopenharmony_ci            args = ''
289bf215546Sopenharmony_ci            if pattern.peek_test('('):
290bf215546Sopenharmony_ci                pattern.read(1)
291bf215546Sopenharmony_ci                while pattern.peek() not in ['', ')']:
292bf215546Sopenharmony_ci                    args += pattern.read(1)
293bf215546Sopenharmony_ci                assert(pattern.read(1) == ')')
294bf215546Sopenharmony_ci            func_res = g['funcs'][name](args)
295bf215546Sopenharmony_ci            match_res = do_match(g, StringStream(func_res, 'expansion of "%s(%s)"' % (name, args)), output, False, True)
296bf215546Sopenharmony_ci            if not match_res.success:
297bf215546Sopenharmony_ci                res.func_res = match_res
298bf215546Sopenharmony_ci                res.output_pos = match_res.output_pos
299bf215546Sopenharmony_ci                res.fail(match_res.fail_message)
300bf215546Sopenharmony_ci        elif not escape and c == ' ':
301bf215546Sopenharmony_ci            while pattern.peek_test(' '):
302bf215546Sopenharmony_ci                pattern.read(1)
303bf215546Sopenharmony_ci
304bf215546Sopenharmony_ci            read_whitespace = False
305bf215546Sopenharmony_ci            while output.peek_test(' \t'):
306bf215546Sopenharmony_ci                output.read(1)
307bf215546Sopenharmony_ci                read_whitespace = True
308bf215546Sopenharmony_ci            if not read_whitespace:
309bf215546Sopenharmony_ci                res.fail('expected whitespace in output, got %r' % (output.peek(1)))
310bf215546Sopenharmony_ci        else:
311bf215546Sopenharmony_ci            outc = output.peek(1)
312bf215546Sopenharmony_ci            if outc != c:
313bf215546Sopenharmony_ci                res.fail('expected %r in output, got %r' % (c, outc))
314bf215546Sopenharmony_ci            else:
315bf215546Sopenharmony_ci                output.read(1)
316bf215546Sopenharmony_ci        if not res.success:
317bf215546Sopenharmony_ci            if skip_lines and output.peek() != '':
318bf215546Sopenharmony_ci                g.clear()
319bf215546Sopenharmony_ci                g.update(old_g)
320bf215546Sopenharmony_ci                res.success = True
321bf215546Sopenharmony_ci                output.read_line()
322bf215546Sopenharmony_ci                pattern.reset()
323bf215546Sopenharmony_ci                output.skip_whitespace(False)
324bf215546Sopenharmony_ci                pattern.skip_whitespace(False)
325bf215546Sopenharmony_ci            else:
326bf215546Sopenharmony_ci                return res
327bf215546Sopenharmony_ci
328bf215546Sopenharmony_ci        escape = False
329bf215546Sopenharmony_ci
330bf215546Sopenharmony_ci    if not in_func:
331bf215546Sopenharmony_ci        while output.peek() in [' ', '\t']:
332bf215546Sopenharmony_ci            output.read(1)
333bf215546Sopenharmony_ci
334bf215546Sopenharmony_ci        if output.read(1) not in ['', '\n']:
335bf215546Sopenharmony_ci            res.fail('expected end of output')
336bf215546Sopenharmony_ci            return res
337bf215546Sopenharmony_ci
338bf215546Sopenharmony_ci    return res
339bf215546Sopenharmony_ci
340bf215546Sopenharmony_ciclass PatternCheck(Check):
341bf215546Sopenharmony_ci    def __init__(self, data, search, position):
342bf215546Sopenharmony_ci        Check.__init__(self, data, position)
343bf215546Sopenharmony_ci        self.search = search
344bf215546Sopenharmony_ci
345bf215546Sopenharmony_ci    def run(self, state):
346bf215546Sopenharmony_ci        pattern_stream = StringStream(self.data.rstrip(), 'pattern')
347bf215546Sopenharmony_ci        res = do_match(state.g, pattern_stream, state.g['output'], self.search)
348bf215546Sopenharmony_ci        if not res.success:
349bf215546Sopenharmony_ci            state.result.log += 'pattern at %s failed: %s\n' % (self.position, res.fail_message)
350bf215546Sopenharmony_ci            state.result.log += res.format_pattern_pos() + '\n\n'
351bf215546Sopenharmony_ci            if not self.search:
352bf215546Sopenharmony_ci                out_line = state.g['output'].get_line(res.output_pos.line)
353bf215546Sopenharmony_ci                state.result.log += '\n'.join(format_error_lines('at', res.output_pos.line, res.output_pos.column, 'output', out_line))
354bf215546Sopenharmony_ci            else:
355bf215546Sopenharmony_ci                state.result.log += 'output was:\n'
356bf215546Sopenharmony_ci                state.result.log += state.g['output'].data.rstrip() + '\n'
357bf215546Sopenharmony_ci            return False
358bf215546Sopenharmony_ci        return True
359bf215546Sopenharmony_ci
360bf215546Sopenharmony_ciclass CheckState:
361bf215546Sopenharmony_ci    def __init__(self, result, variant, checks, output):
362bf215546Sopenharmony_ci        self.result = result
363bf215546Sopenharmony_ci        self.variant = variant
364bf215546Sopenharmony_ci        self.checks = checks
365bf215546Sopenharmony_ci
366bf215546Sopenharmony_ci        self.checks.insert(0, CodeCheck(initial_code, None))
367bf215546Sopenharmony_ci        self.insert_queue = []
368bf215546Sopenharmony_ci
369bf215546Sopenharmony_ci        self.g = {'success': True, 'funcs': {}, 'insert_queue': self.insert_queue,
370bf215546Sopenharmony_ci                  'variant': variant, 'log': '', 'output': StringStream(output, 'output'),
371bf215546Sopenharmony_ci                  'CodeCheck': CodeCheck, 'PatternCheck': PatternCheck,
372bf215546Sopenharmony_ci                  'current_position': ''}
373bf215546Sopenharmony_ci
374bf215546Sopenharmony_ciclass TestResult:
375bf215546Sopenharmony_ci    def __init__(self, expected):
376bf215546Sopenharmony_ci        self.result = ''
377bf215546Sopenharmony_ci        self.expected = expected
378bf215546Sopenharmony_ci        self.log = ''
379bf215546Sopenharmony_ci
380bf215546Sopenharmony_cidef check_output(result, variant, checks, output):
381bf215546Sopenharmony_ci    state = CheckState(result, variant, checks, output)
382bf215546Sopenharmony_ci
383bf215546Sopenharmony_ci    while len(state.checks):
384bf215546Sopenharmony_ci        check = state.checks.pop(0)
385bf215546Sopenharmony_ci        state.current_position = check.position
386bf215546Sopenharmony_ci        if not check.run(state):
387bf215546Sopenharmony_ci            result.result = 'failed'
388bf215546Sopenharmony_ci            return
389bf215546Sopenharmony_ci
390bf215546Sopenharmony_ci        for check in state.insert_queue[::-1]:
391bf215546Sopenharmony_ci            state.checks.insert(0, check)
392bf215546Sopenharmony_ci        state.insert_queue.clear()
393bf215546Sopenharmony_ci
394bf215546Sopenharmony_ci    result.result = 'passed'
395bf215546Sopenharmony_ci    return
396bf215546Sopenharmony_ci
397bf215546Sopenharmony_cidef parse_check(variant, line, checks, pos):
398bf215546Sopenharmony_ci    if line.startswith(';'):
399bf215546Sopenharmony_ci        line = line[1:]
400bf215546Sopenharmony_ci        if len(checks) and isinstance(checks[-1], CodeCheck):
401bf215546Sopenharmony_ci            checks[-1].data += '\n' + line
402bf215546Sopenharmony_ci        else:
403bf215546Sopenharmony_ci            checks.append(CodeCheck(line, pos))
404bf215546Sopenharmony_ci    elif line.startswith('!'):
405bf215546Sopenharmony_ci        checks.append(PatternCheck(line[1:], False, pos))
406bf215546Sopenharmony_ci    elif line.startswith('>>'):
407bf215546Sopenharmony_ci        checks.append(PatternCheck(line[2:], True, pos))
408bf215546Sopenharmony_ci    elif line.startswith('~'):
409bf215546Sopenharmony_ci        end = len(line)
410bf215546Sopenharmony_ci        start = len(line)
411bf215546Sopenharmony_ci        for c in [';', '!', '>>']:
412bf215546Sopenharmony_ci            if line.find(c) != -1 and line.find(c) < end:
413bf215546Sopenharmony_ci                end = line.find(c)
414bf215546Sopenharmony_ci        if end != len(line):
415bf215546Sopenharmony_ci            match = re.match(line[1:end], variant)
416bf215546Sopenharmony_ci            if match and match.end() == len(variant):
417bf215546Sopenharmony_ci                parse_check(variant, line[end:], checks, pos)
418bf215546Sopenharmony_ci
419bf215546Sopenharmony_cidef parse_test_source(test_name, variant, fname):
420bf215546Sopenharmony_ci    in_test = False
421bf215546Sopenharmony_ci    test = []
422bf215546Sopenharmony_ci    expected_result = 'passed'
423bf215546Sopenharmony_ci    line_num = 1
424bf215546Sopenharmony_ci    for line in open(fname, 'r').readlines():
425bf215546Sopenharmony_ci        if line.startswith('BEGIN_TEST(%s)' % test_name):
426bf215546Sopenharmony_ci            in_test = True
427bf215546Sopenharmony_ci        elif line.startswith('BEGIN_TEST_TODO(%s)' % test_name):
428bf215546Sopenharmony_ci            in_test = True
429bf215546Sopenharmony_ci            expected_result = 'todo'
430bf215546Sopenharmony_ci        elif line.startswith('BEGIN_TEST_FAIL(%s)' % test_name):
431bf215546Sopenharmony_ci            in_test = True
432bf215546Sopenharmony_ci            expected_result = 'failed'
433bf215546Sopenharmony_ci        elif line.startswith('END_TEST'):
434bf215546Sopenharmony_ci            in_test = False
435bf215546Sopenharmony_ci        elif in_test:
436bf215546Sopenharmony_ci            test.append((line_num, line.strip()))
437bf215546Sopenharmony_ci        line_num += 1
438bf215546Sopenharmony_ci
439bf215546Sopenharmony_ci    checks = []
440bf215546Sopenharmony_ci    for line_num, check in [(line_num, l[2:]) for line_num, l in test if l.startswith('//')]:
441bf215546Sopenharmony_ci         parse_check(variant, check, checks, 'line %d of %s' % (line_num, os.path.split(fname)[1]))
442bf215546Sopenharmony_ci
443bf215546Sopenharmony_ci    return checks, expected_result
444bf215546Sopenharmony_ci
445bf215546Sopenharmony_cidef parse_and_check_test(test_name, variant, test_file, output, current_result):
446bf215546Sopenharmony_ci    checks, expected = parse_test_source(test_name, variant, test_file)
447bf215546Sopenharmony_ci
448bf215546Sopenharmony_ci    result = TestResult(expected)
449bf215546Sopenharmony_ci    if len(checks) == 0:
450bf215546Sopenharmony_ci        result.result = 'empty'
451bf215546Sopenharmony_ci        result.log = 'no checks found'
452bf215546Sopenharmony_ci    elif current_result != None:
453bf215546Sopenharmony_ci        result.result, result.log = current_result
454bf215546Sopenharmony_ci    else:
455bf215546Sopenharmony_ci        check_output(result, variant, checks, output)
456bf215546Sopenharmony_ci        if result.result == 'failed' and expected == 'todo':
457bf215546Sopenharmony_ci            result.result = 'todo'
458bf215546Sopenharmony_ci
459bf215546Sopenharmony_ci    return result
460bf215546Sopenharmony_ci
461bf215546Sopenharmony_cidef print_results(results, output, expected):
462bf215546Sopenharmony_ci    results = {name: result for name, result in results.items() if result.result == output}
463bf215546Sopenharmony_ci    results = {name: result for name, result in results.items() if (result.result == result.expected) == expected}
464bf215546Sopenharmony_ci
465bf215546Sopenharmony_ci    if not results:
466bf215546Sopenharmony_ci        return 0
467bf215546Sopenharmony_ci
468bf215546Sopenharmony_ci    print('%s tests (%s):' % (output, 'expected' if expected else 'unexpected'))
469bf215546Sopenharmony_ci    for test, result in results.items():
470bf215546Sopenharmony_ci        color = '' if expected else set_red
471bf215546Sopenharmony_ci        print('   %s%s%s' % (color, test, set_normal))
472bf215546Sopenharmony_ci        if result.log.strip() != '':
473bf215546Sopenharmony_ci            for line in result.log.rstrip().split('\n'):
474bf215546Sopenharmony_ci                print('      ' + line.rstrip())
475bf215546Sopenharmony_ci    print('')
476bf215546Sopenharmony_ci
477bf215546Sopenharmony_ci    return len(results)
478bf215546Sopenharmony_ci
479bf215546Sopenharmony_cidef get_cstr(fp):
480bf215546Sopenharmony_ci    res = b''
481bf215546Sopenharmony_ci    while True:
482bf215546Sopenharmony_ci        c = fp.read(1)
483bf215546Sopenharmony_ci        if c == b'\x00':
484bf215546Sopenharmony_ci            return res.decode('utf-8')
485bf215546Sopenharmony_ci        else:
486bf215546Sopenharmony_ci            res += c
487bf215546Sopenharmony_ci
488bf215546Sopenharmony_ciif __name__ == "__main__":
489bf215546Sopenharmony_ci   results = {}
490bf215546Sopenharmony_ci
491bf215546Sopenharmony_ci   stdin = sys.stdin.buffer
492bf215546Sopenharmony_ci   while True:
493bf215546Sopenharmony_ci       packet_type = stdin.read(4)
494bf215546Sopenharmony_ci       if packet_type == b'':
495bf215546Sopenharmony_ci           break;
496bf215546Sopenharmony_ci
497bf215546Sopenharmony_ci       test_name = get_cstr(stdin)
498bf215546Sopenharmony_ci       test_variant = get_cstr(stdin)
499bf215546Sopenharmony_ci       if test_variant != '':
500bf215546Sopenharmony_ci           full_name = test_name + '/' + test_variant
501bf215546Sopenharmony_ci       else:
502bf215546Sopenharmony_ci           full_name = test_name
503bf215546Sopenharmony_ci
504bf215546Sopenharmony_ci       test_source_file = get_cstr(stdin)
505bf215546Sopenharmony_ci       current_result = None
506bf215546Sopenharmony_ci       if ord(stdin.read(1)):
507bf215546Sopenharmony_ci           current_result = (get_cstr(stdin), get_cstr(stdin))
508bf215546Sopenharmony_ci       code_size = struct.unpack("=L", stdin.read(4))[0]
509bf215546Sopenharmony_ci       code = stdin.read(code_size).decode('utf-8')
510bf215546Sopenharmony_ci
511bf215546Sopenharmony_ci       results[full_name] = parse_and_check_test(test_name, test_variant, test_source_file, code, current_result)
512bf215546Sopenharmony_ci
513bf215546Sopenharmony_ci   result_types = ['passed', 'failed', 'todo', 'empty']
514bf215546Sopenharmony_ci   num_expected = 0
515bf215546Sopenharmony_ci   num_unexpected = 0
516bf215546Sopenharmony_ci   for t in result_types:
517bf215546Sopenharmony_ci       num_expected += print_results(results, t, True)
518bf215546Sopenharmony_ci   for t in result_types:
519bf215546Sopenharmony_ci       num_unexpected += print_results(results, t, False)
520bf215546Sopenharmony_ci   num_expected_skipped = print_results(results, 'skipped', True)
521bf215546Sopenharmony_ci   num_unexpected_skipped = print_results(results, 'skipped', False)
522bf215546Sopenharmony_ci
523bf215546Sopenharmony_ci   num_unskipped = len(results) - num_expected_skipped - num_unexpected_skipped
524bf215546Sopenharmony_ci   color = set_red if num_unexpected else set_green
525bf215546Sopenharmony_ci   print('%s%d (%.0f%%) of %d unskipped tests had an expected result%s' % (color, num_expected, floor(num_expected / num_unskipped * 100), num_unskipped, set_normal))
526bf215546Sopenharmony_ci   if num_unexpected_skipped:
527bf215546Sopenharmony_ci       print('%s%d tests had been unexpectedly skipped%s' % (set_red, num_unexpected_skipped, set_normal))
528bf215546Sopenharmony_ci
529bf215546Sopenharmony_ci   if num_unexpected:
530bf215546Sopenharmony_ci       sys.exit(1)
531