Skip to content

Commit

Permalink
maint: add fixture to disable throttling (#5908)
Browse files Browse the repository at this point in the history
This PR adds a `disable_throttling` fixture to disable `superqt` throttles
during tests.

This is to speed up tests and do not need implement waiting on the
throttler (also tests are simplified).

---------

Co-authored-by: Lorenzo Gaifas <brisvag@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Jun 15, 2023
1 parent 517849e commit 6bfa54b
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
32 changes: 32 additions & 0 deletions napari/_tests/test_conftest_fixtures.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from unittest.mock import Mock, patch

import pytest
from qtpy.QtCore import QMutex, QThread, QTimer
from superqt.utils import qdebounced


class _TestThread(QThread):
Expand Down Expand Up @@ -44,3 +47,32 @@ def test_disable_qtimer(qtbot):
th.mutex.unlock()
qtbot.waitUntil(th.isFinished, timeout=2000)
assert not th.isRunning()


@pytest.mark.usefixtures("disable_throttling")
@patch("qtpy.QtCore.QTimer.start")
def test_disable_throttle(start_mock):
mock = Mock()

@qdebounced(timeout=50)
def f() -> str:
mock()

f()
start_mock.assert_not_called()
mock.assert_called_once()


@patch("qtpy.QtCore.QTimer.start")
@patch("qtpy.QtCore.QTimer.isActive", return_value=True)
def test_lack_disable_throttle(start_mock, _active_mock, monkeypatch):
"""This is test showing that if we do not use disable_throttling then timer is started"""
mock = Mock()

@qdebounced(timeout=50)
def f() -> str:
mock()

f()
start_mock.assert_called_once()
mock.assert_not_called()
38 changes: 37 additions & 1 deletion napari/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,8 +686,44 @@ def _single_shot(self, *args):
long_desc += "The QTimers were started in:\n"
else:
long_desc += "The QTimer was started in:\n"

def _check_throttle_info(path):
if "superqt" in path and "throttler" in path:
return (
path
+ " it's possible that there was a problem with unfinished work by a "
"qthrottler; to solve this, you can either try to wait (such as with "
"`qtbot.wait`) or disable throttling with the disable_throttling fixture"
)
return path

assert not dangling_timers, long_desc + "\n".join(
x[1] for x in dangling_timers
_check_throttle_info(x[1]) for x in dangling_timers
)


def _throttle_mock(self):
self.triggered.emit()


def _flush_mock(self):
"""There are no waiting events."""


@pytest.fixture
def disable_throttling(monkeypatch):
"""Disable qthrottler from superqt.
This is sometimes necessary to avoid flaky failures in tests
due to dangling qt timers.
"""
# if this monkeypath fails then you should update path to GenericSignalThrottler
monkeypatch.setattr(
"superqt.utils._throttler.GenericSignalThrottler.throttle",
_throttle_mock,
)
monkeypatch.setattr(
"superqt.utils._throttler.GenericSignalThrottler.flush", _flush_mock
)


Expand Down

0 comments on commit 6bfa54b

Please sign in to comment.