aboutsummaryrefslogtreecommitdiffstats
path: root/testing/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/parser.py')
-rw-r--r--testing/parser.py166
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)