diff options
Diffstat (limited to 'testing/parser.py')
-rw-r--r-- | testing/parser.py | 166 |
1 files changed, 84 insertions, 82 deletions
diff --git a/testing/parser.py b/testing/parser.py index 035aab091..a01c4d029 100644 --- a/testing/parser.py +++ b/testing/parser.py @@ -1,48 +1,19 @@ -############################################################################# -## -## Copyright (C) 2017 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of Qt for Python. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# - -from __future__ import print_function +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only import os import re from collections import namedtuple -from .helper import StringIO +from io import StringIO + +""" +testing/parser.py + +Parse test output lines from ctest and build TestResult objects. + +TestParser.iter_blacklist adds info from the blacklist while iterating +over the test results. +""" _EXAMPLE = """ Example output: @@ -62,39 +33,47 @@ Note the field "mod_name". I had split this before, but it is necessary to use the combination as the key, because the test names are not unique. """ -# validation of our pattern: +_TEST_PAT_PRE = r""" + ^ # start + \s* # any whitespace ==: WS + ([0-9]+)/([0-9]+) # ip1 "/" n + \s+ # some WS + Test # "Test" + \s+ # some WS + \# # sharp symbol "#" + ([0-9]+) # sharp + : # colon symbol ':' + """ +_TEST_PAT = ( + _TEST_PAT_PRE + + r""" + \s+ # some WS + ([\w-]+) # mod_name + .*? # whatever (non greedy) + ( # + (Passed) # either "Passed", None + | # + \*\*\*(\w+.*?) # or None, "Something" + ) # code + \s+ # some WS + ([0-9]+\.[0-9]+) # tim + \s+ # some WS + sec # "sec" + \s* # any WS + $ # end + """ +) -_TEST_PAT = r""" - ^ # start - \s* # any whitespace ==: WS - ([0-9]+)/([0-9]+) # ip1 "/" n - \s+ # some WS - Test # "Test" - \s+ # some WS - \# # sharp symbol "#" - ([0-9]+) # sharp - : # colon symbol ':' - \s+ # some WS - ([\w-]+) # mod_name - .*? # whatever (non greedy) - ( # - (Passed) # either "Passed", None - | # - \*\*\*(\w+.*?) # or None, "Something" - ) # code - \s+ # some WS - ([0-9]+\.[0-9]+) # tim - \s+ # some WS - sec # "sec" - \s* # any WS - $ # end - """ +# validation of our pattern: assert re.match(_TEST_PAT, _EXAMPLE.splitlines()[5], re.VERBOSE) assert len(re.match(_TEST_PAT, _EXAMPLE.splitlines()[5], re.VERBOSE).groups()) == 8 assert len(re.match(_TEST_PAT, _EXAMPLE.splitlines()[7], re.VERBOSE).groups()) == 8 -TestResult = namedtuple("TestResult", ["idx", "mod_name", "passed", - "code", "time"]) +TestResult = namedtuple( + "TestResult", "idx n sharp mod_name passed " "code time fatal rich_result".split() +) + + def _parse_tests(test_log): """ Create a TestResult object for every entry. @@ -107,36 +86,56 @@ def _parse_tests(test_log): lines = f.readlines() else: lines = [] + + # PYSIDE-1229: Fix disrupted lines like "Exit code 0xc0000409\n***Exception:" + pat = _TEST_PAT_PRE + for idx, line in enumerate(lines[:-1]): + match = re.match(pat, line, re.VERBOSE) + if match and line.split()[-1] != "sec": + # don't change the number of lines + lines[idx:idx + 2] = [line.rstrip() + lines[idx + 1], ""] + pat = _TEST_PAT for line in lines: match = re.match(pat, line, re.VERBOSE) if match: - idx, n, sharp, mod_name, much_stuff, code1, code2, tim = tup = match.groups() + idx, n, sharp, mod_name, much_stuff, code1, code2, tim = match.groups() # either code1 or code2 is None code = code1 or code2 - idx, n, code, tim = int(idx), int(n), code.lower(), float(tim) - res = TestResult(idx, mod_name, code == "passed", code, tim) - result.append(res) + idx, n, sharp, code, tim = int(idx), int(n), int(sharp), code.lower(), float(tim) + item = TestResult(idx, n, sharp, mod_name, code == "passed", code, tim, False, None) + result.append(item) + + # PYSIDE-1229: Be sure that the numbering of the tests is consecutive + for idx, item in enumerate(result): + # testing fatal error: + # Use "if idx + 1 != item.idx or idx == 42:" + if idx + 1 != item.idx: + # The numbering is disrupted. Provoke an error in this line! + code = f"{code}, but lines are disrupted!" + result[idx] = item._replace( + passed=False, code=f"{item.code}, but lines are disrupted!", fatal=True + ) + break return result class TestParser(object): def __init__(self, test_log): - self._result = _parse_tests(test_log) + self._results = _parse_tests(test_log) @property - def result(self): - return self._result + def results(self): + return self._results def __len__(self): - return len(self._result) + return len(self._results) def iter_blacklist(self, blacklist): bl = blacklist - for line in self._result: - mod_name = line.mod_name - passed = line.passed - match = bl.find_matching_line(line) + for item in self.results: + passed = item.passed + match = bl.find_matching_line(item) if not passed: if match: res = "BFAIL" @@ -147,4 +146,7 @@ class TestParser(object): res = "BPASS" else: res = "PASS" - yield mod_name, res + if item.fatal: + # PYSIDE-1229: Stop the testing completely when a fatal error exists + res = "FATAL" + yield item._replace(rich_result=res) |