diff options
Diffstat (limited to 'sources/pyside6/tests/util/helper')
-rw-r--r-- | sources/pyside6/tests/util/helper/__init__.py | 1 | ||||
-rw-r--r-- | sources/pyside6/tests/util/helper/basicpyslotcase.py | 33 | ||||
-rw-r--r-- | sources/pyside6/tests/util/helper/docmodifier.py | 91 | ||||
-rw-r--r-- | sources/pyside6/tests/util/helper/helper.py | 37 | ||||
-rw-r--r-- | sources/pyside6/tests/util/helper/helper.pyproject | 5 | ||||
-rw-r--r-- | sources/pyside6/tests/util/helper/timedqapplication.py | 27 | ||||
-rw-r--r-- | sources/pyside6/tests/util/helper/timedqguiapplication.py | 27 | ||||
-rw-r--r-- | sources/pyside6/tests/util/helper/usesqapplication.py | 43 |
8 files changed, 264 insertions, 0 deletions
diff --git a/sources/pyside6/tests/util/helper/__init__.py b/sources/pyside6/tests/util/helper/__init__.py new file mode 100644 index 000000000..218d8921e --- /dev/null +++ b/sources/pyside6/tests/util/helper/__init__.py @@ -0,0 +1 @@ +# This file intentionally left blank. diff --git a/sources/pyside6/tests/util/helper/basicpyslotcase.py b/sources/pyside6/tests/util/helper/basicpyslotcase.py new file mode 100644 index 000000000..500735c3a --- /dev/null +++ b/sources/pyside6/tests/util/helper/basicpyslotcase.py @@ -0,0 +1,33 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import gc + + +class BasicPySlotCase(object): + '''Base class that tests python slots and signal emissions. + + Python slots are defined as any callable passed to QObject.connect(). + ''' + def setUp(self): + self.called = False + + def tearDown(self): + try: + del self.args + except: + pass + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + + def cb(self, *args): + '''Simple callback with arbitrary arguments. + + The test function must setup the 'args' attribute with a sequence + containing the arguments expected to be received by this slot. + Currently only a single connection is supported. + ''' + if tuple(self.args) == args: + self.called = True + else: + raise ValueError('Invalid arguments for callback') diff --git a/sources/pyside6/tests/util/helper/docmodifier.py b/sources/pyside6/tests/util/helper/docmodifier.py new file mode 100644 index 000000000..cfb665640 --- /dev/null +++ b/sources/pyside6/tests/util/helper/docmodifier.py @@ -0,0 +1,91 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Helper metaclass do 'decorate' docstrings from base test case classes''' + +import types + + +def copy_func(func): + '''Helper function to copy a function object (except docstring)''' + return types.FunctionType(func.func_code, func.func_globals, func.func_name, + func.func_defaults, func.func_closure) + + +class DocModifier(type): + '''Metaclass for modifiying method documentation. + + It allows the managed class to modify the method documentation, adding + prefixes and suffixes to a given set of methods. + + For example, you have some unittest.TestCases that run on different set + of data. These methods could be written once in a base class and the + inheriting class just setup different setUp/tearDown methods with the + proper data. To differentiate the methods when using docstrings, you + can add a suffix or prefix to the docstring. + + Variables in the implementing class: + + doc_prefix - The prefix to the docstring + doc_suffix - The suffix to the docstring + doc_filter - The function to filter the methods. If not provided, this + no docstrings are changed.''' + + def __new__(mcs, name, bases, dct): + + # FIXME currently we have to define doc_filter on each subclass + filter_function = dct.get('doc_filter') + if not filter_function: + filter_function = lambda x: False + + for base in bases: + for attr in [x for x in base.__dict__ if filter_function(x)]: + original = getattr(base, attr) + + if original.__doc__: + copy = copy_func(original) + copy.__doc__ = (dct.get('doc_prefix', '') + + original.__doc__ + + dct.get('doc_suffix', '')) + dct[attr] = copy + + return type.__new__(mcs, name, bases, dct) + + def __init__(mcs, name, bases, dct): + super(DocModifier, mcs).__init__(name, bases, dct) + + +if __name__ == '__main__': + + # tests + class BaseTest(object): + __metaclass__ = DocModifier + + def testBase(self): + '''base''' + + def testWithoutDoc(self): + pass + + class Implementing(BaseTest): + + doc_filter = lambda x: x.startswith('test') + doc_prefix = 'prefix' + doc_suffix = 'suffix' + + class OnlyPrefix(BaseTest): + + doc_filter = lambda x: x.startswith('test') + doc_prefix = 'prefix' + + class OnlySuffix(BaseTest): + + doc_filter = lambda x: x.startswith('test') + doc_suffix = 'suffix' + + assert (Implementing.testBase.__doc__ == 'prefixbasesuffix') + assert (Implementing.testWithoutDoc.__doc__ == None) + assert (OnlySuffix.testBase.__doc__ == 'basesuffix') + assert (OnlySuffix.testWithoutDoc.__doc__ == None) + assert (OnlyPrefix.testBase.__doc__ == 'prefixbase') + assert (OnlyPrefix.testWithoutDoc.__doc__ == None) diff --git a/sources/pyside6/tests/util/helper/helper.py b/sources/pyside6/tests/util/helper/helper.py new file mode 100644 index 000000000..f80753f1f --- /dev/null +++ b/sources/pyside6/tests/util/helper/helper.py @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Helper classes and functions''' + +import os +from random import randint + + +def adjust_filename(filename, orig_mod_filename): + dirpath = os.path.dirname(os.path.abspath(orig_mod_filename)) + return os.path.join(dirpath, filename) + + +def _join_qml_errors(errors): + '''Return an error string from a list of QQmlError''' + result = '' + for i, error in enumerate(errors): + if i: + result += ', ' + result += error.toString() + return result + + +def quickview_errorstring(quickview): + '''Return an error string from a QQuickView''' + return _join_qml_errors(quickview.errors()) + + +def qmlcomponent_errorstring(component): + '''Return an error string from a QQmlComponent''' + return _join_qml_errors(component.errors()) + + +def random_string(size=5): + '''Generate random string with the given size''' + return ''.join(map(chr, [randint(33, 126) for x in range(size)])) diff --git a/sources/pyside6/tests/util/helper/helper.pyproject b/sources/pyside6/tests/util/helper/helper.pyproject new file mode 100644 index 000000000..dc23d87a5 --- /dev/null +++ b/sources/pyside6/tests/util/helper/helper.pyproject @@ -0,0 +1,5 @@ +{ + "files": ["basicpyslotcase.py", "docmodifier.py", "helper.py", + "timedqapplication.py", "usesqapplication.py", "usesqcoreapplication.py", + "usesqguiapplication.py" ] +} diff --git a/sources/pyside6/tests/util/helper/timedqapplication.py b/sources/pyside6/tests/util/helper/timedqapplication.py new file mode 100644 index 000000000..d9250a9e0 --- /dev/null +++ b/sources/pyside6/tests/util/helper/timedqapplication.py @@ -0,0 +1,27 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Helper classes and functions''' + +import gc +import unittest + +from PySide6.QtCore import QTimer +from PySide6.QtWidgets import QApplication + + +class TimedQApplication(unittest.TestCase): + '''Helper class with timed QApplication exec loop''' + + def setUp(self, timeout=100): + '''Setups this Application. + + timeout - timeout in milisseconds''' + self.app = QApplication.instance() or QApplication([]) + QTimer.singleShot(timeout, self.app.quit) + + def tearDown(self): + '''Delete resources''' + del self.app + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() diff --git a/sources/pyside6/tests/util/helper/timedqguiapplication.py b/sources/pyside6/tests/util/helper/timedqguiapplication.py new file mode 100644 index 000000000..3d9a4217c --- /dev/null +++ b/sources/pyside6/tests/util/helper/timedqguiapplication.py @@ -0,0 +1,27 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Helper classes and functions''' + +import gc +import unittest + +from PySide6.QtCore import QTimer +from PySide6.QtGui import QGuiApplication + + +class TimedQGuiApplication(unittest.TestCase): + '''Helper class with timed QGuiApplication exec loop''' + + def setUp(self, timeout=100): + '''Sets up this Application. + + timeout - timeout in millisseconds''' + self.app = QGuiApplication.instance() or QGuiApplication([]) + QTimer.singleShot(timeout, self.app.quit) + + def tearDown(self): + '''Delete resources''' + del self.app + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() diff --git a/sources/pyside6/tests/util/helper/usesqapplication.py b/sources/pyside6/tests/util/helper/usesqapplication.py new file mode 100644 index 000000000..f62e320f4 --- /dev/null +++ b/sources/pyside6/tests/util/helper/usesqapplication.py @@ -0,0 +1,43 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +'''Helper classes and functions''' + +import gc +import sys +import unittest + +# This version avoids explicit import in order to adapt to the +# import decision of the main module. +# This should work with every compatible library. +# Replaces the QtGui and QtCore versions as well. + + +class UsesQApplication(unittest.TestCase): + '''Helper class to provide Q(Core|Gui|)Application instances + Just connect or call self.exit_app_cb. When called, will ask + self.app to exit. + ''' + + def setUp(self): + '''Creates the QApplication instance''' + module = sys.modules[sorted(_ for _ in sys.modules + if _.endswith((".QtCore", ".QtGui", ".QtWidgets")))[-1]] + found = module.__name__.rsplit(".")[-1] + cls = getattr(module, {"QtWidgets": "QApplication", + "QtGui": "QGuiApplication", + "QtCore": "QCoreApplication"}[found]) + # Simple way of making instance a singleton + super().setUp() + self.app = cls.instance() or cls([]) + + def tearDown(self): + '''Deletes the reference owned by self''' + del self.app + # PYSIDE-535: Need to collect garbage in PyPy to trigger deletion + gc.collect() + super().tearDown() + + def exit_app_cb(self): + '''Quits the application''' + self.app.exit(0) |