summaryrefslogtreecommitdiff
path: root/silx/gui/test/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/test/utils.py')
-rw-r--r--silx/gui/test/utils.py88
1 files changed, 76 insertions, 12 deletions
diff --git a/silx/gui/test/utils.py b/silx/gui/test/utils.py
index 19c448a..3eee474 100644
--- a/silx/gui/test/utils.py
+++ b/silx/gui/test/utils.py
@@ -26,7 +26,7 @@
__authors__ = ["T. Vincent"]
__license__ = "MIT"
-__date__ = "01/09/2017"
+__date__ = "27/02/2018"
import gc
@@ -43,6 +43,8 @@ from silx.gui import qt
if qt.BINDING == 'PySide':
from PySide.QtTest import QTest
+elif qt.BINDING == 'PySide2':
+ from PySide2.QtTest import QTest
elif qt.BINDING == 'PyQt5':
from PyQt5.QtTest import QTest
elif qt.BINDING == 'PyQt4':
@@ -137,11 +139,9 @@ class TestCaseQt(unittest.TestCase):
# Makes sure a QApplication exists and do it once for all
_qapp = qt.QApplication.instance() or qt.QApplication([])
- # Create/delate a QWidget to make sure init of QDesktopWidget
- _dummyWidget = qt.QWidget()
- _dummyWidget.setAttribute(qt.Qt.WA_DeleteOnClose)
- _dummyWidget.show()
- _dummyWidget.close()
+ # Makes sure QDesktopWidget is init
+ # Otherwise it happens randomly during the tests
+ cls._desktopWidget = _qapp.desktop()
_qapp.processEvents()
@classmethod
@@ -163,9 +163,10 @@ class TestCaseQt(unittest.TestCase):
# For Python < 3.4
result = getattr(self, '_outcomeForDoCleanups', self._resultForDoCleanups)
+ skipped = self.id() in [case.id() for case, _ in result.skipped]
error = self.id() in [case.id() for case, _ in result.errors]
failure = self.id() in [case.id() for case, _ in result.failures]
- return not error and not failure
+ return not error and not failure and not skipped
def _checkForUnreleasedWidgets(self):
"""Test fixture checking that no more widgets exists."""
@@ -175,7 +176,7 @@ class TestCaseQt(unittest.TestCase):
if widget not in self.__previousWidgets]
del self.__previousWidgets
- if qt.BINDING == 'PySide':
+ if qt.BINDING in ('PySide', 'PySide2'):
return # Do not test for leaking widgets with PySide
allowedLeakingWidgets = self.allowedLeakingWidgets
@@ -318,24 +319,25 @@ class TestCaseQt(unittest.TestCase):
"""
QTest.qSleep(ms + self.TIMEOUT_WAIT)
- def qWait(self, ms=None):
+ @classmethod
+ def qWait(cls, ms=None):
"""Waits for ms milliseconds, events will be processed.
See QTest.qWait for details.
"""
if ms is None:
- ms = self.DEFAULT_TIMEOUT_WAIT
+ ms = cls.DEFAULT_TIMEOUT_WAIT
if qt.BINDING == 'PySide':
# PySide has no qWait, provide a replacement
timeout = int(ms)
endTimeMS = int(time.time() * 1000) + timeout
while timeout > 0:
- self.qapp.processEvents(qt.QEventLoop.AllEvents,
+ _qapp.processEvents(qt.QEventLoop.AllEvents,
maxtime=timeout)
timeout = endTimeMS - int(time.time() * 1000)
else:
- QTest.qWait(ms + self.TIMEOUT_WAIT)
+ QTest.qWait(ms + cls.TIMEOUT_WAIT)
def qWaitForWindowExposed(self, window, timeout=None):
"""Waits until the window is shown in the screen.
@@ -349,6 +351,57 @@ class TestCaseQt(unittest.TestCase):
return result
+ _qobject_destroyed = False
+
+ @classmethod
+ def _aboutToDestroy(cls):
+ cls._qobject_destroyed = True
+
+ @classmethod
+ def qWaitForDestroy(cls, ref):
+ """
+ Wait for Qt object destruction.
+
+ Use a weakref as parameter to avoid any strong references to the
+ object.
+
+ It have to be used as following. Removing the reference to the object
+ before calling the function looks to be expected, else
+ :meth:`deleteLater` will not work.
+
+ .. code-block:: python
+
+ ref = weakref.ref(self.obj)
+ self.obj = None
+ self.qWaitForDestroy(ref)
+
+ :param weakref ref: A weakref to an object to avoid any reference
+ :return: True if the object was destroyed
+ :rtype: bool
+ """
+ cls._qobject_destroyed = False
+ if qt.BINDING == 'PyQt4':
+ # Without this, QWidget will be still alive on PyQt4
+ # (at least on Windows Python 2.7)
+ # If it is not skipped on PySide, silx.gui.dialog tests will
+ # segfault (at least on Windows Python 2.7)
+ import gc
+ gc.collect()
+ qobject = ref()
+ if qobject is None:
+ return True
+ qobject.destroyed.connect(cls._aboutToDestroy)
+ qobject.deleteLater()
+ qobject = None
+ for _ in range(10):
+ if cls._qobject_destroyed:
+ break
+ cls.qWait(10)
+ else:
+ _logger.debug("Object was not destroyed")
+
+ return ref() is None
+
def logScreenShot(self, level=logging.ERROR):
"""Take a screenshot and log it into the logging system if the
logger is enabled for the expected level.
@@ -454,3 +507,14 @@ def getQToolButtonFromAction(action):
if isinstance(widget, qt.QToolButton):
return widget
return None
+
+
+def findChildren(parent, kind, name=None):
+ if qt.BINDING == "PySide" and name is not None:
+ result = []
+ for obj in parent.findChildren(kind):
+ if obj.objectName() == name:
+ result.append(obj)
+ return result
+ else:
+ return parent.findChildren(kind, name=name)