Skip to content

Commit cb1551a

Browse files
committed
Merge pull request #119 from The-Compiler/not-emitted
Add qtbot.assertNotEmitted.
2 parents 3fd9489 + 1258e28 commit cb1551a

File tree

5 files changed

+120
-2
lines changed

5 files changed

+120
-2
lines changed

CHANGELOG.rst

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
1.11.0
2+
------
3+
4+
- ``qtbot`` now has a new ``assertNotEmitted`` context manager which can be
5+
used to ensure the given signal is not emitted.
6+
17
1.10.0
28
------
39

docs/signals.rst

+15
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,18 @@ the new ``raising`` parameter::
8282
# this will be reached after all workers emit their "finished"
8383
# signal or a qtbot.SignalTimeoutError will be raised
8484
assert_application_results(app)
85+
86+
**Making sure a given signal is not emitted**
87+
88+
.. versionadded:: 1.11
89+
90+
If you want to ensure a signal is **not** emitted in a given block of code, use
91+
the :meth:`qtbot.assertNotEmitted <pytestqt.plugin.QtBot.assertNotEmitted>`
92+
context manager:
93+
94+
.. code-block:: python
95+
96+
def test_no_error(qtbot):
97+
...
98+
with qtbot.assertNotEmitted(app.worker.error):
99+
app.worker.start()

pytestqt/qtbot.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import functools
2+
import contextlib
23
import weakref
3-
from pytestqt.wait_signal import SignalBlocker, MultiSignalBlocker, SignalTimeoutError
4+
from pytestqt.wait_signal import SignalBlocker, MultiSignalBlocker, SignalTimeoutError, SignalEmittedSpy
45
from pytestqt.qt_compat import QtTest, QApplication
56

67

@@ -68,6 +69,7 @@ class QtBot(object):
6869
6970
.. automethod:: waitSignal
7071
.. automethod:: waitSignals
72+
.. automethod:: assertNotEmitted
7173
7274
**Raw QTest API**
7375
@@ -324,6 +326,23 @@ def wait(self, ms):
324326
blocker = MultiSignalBlocker(timeout=ms)
325327
blocker.wait()
326328

329+
@contextlib.contextmanager
330+
def assertNotEmitted(self, signal):
331+
"""
332+
.. versionadded:: 1.11
333+
334+
Make sure the given ``signal`` doesn't get emitted.
335+
336+
This is intended to be used as a context manager.
337+
"""
338+
spy = SignalEmittedSpy(signal)
339+
with spy:
340+
yield
341+
spy.assert_not_emitted()
342+
343+
assert_not_emitted = assertNotEmitted # pep-8 alias
344+
345+
327346

328347
# provide easy access to SignalTimeoutError to qtbot fixtures
329348
QtBot.SignalTimeoutError = SignalTimeoutError

pytestqt/wait_signal.py

+46
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,42 @@ def _cleanup(self):
185185
self._slots.clear()
186186

187187

188+
class SignalEmittedSpy(object):
189+
190+
"""
191+
.. versionadded:: 1.11
192+
193+
An object which checks if a given signal has ever been emitted.
194+
195+
Intended to be used as a context manager.
196+
"""
197+
198+
def __init__(self, signal):
199+
self.signal = signal
200+
self.emitted = False
201+
self.args = None
202+
203+
def slot(self, *args):
204+
self.emitted = True
205+
self.args = args
206+
207+
def __enter__(self):
208+
self.signal.connect(self.slot)
209+
210+
def __exit__(self, type, value, traceback):
211+
self.signal.disconnect(self.slot)
212+
213+
def assert_not_emitted(self):
214+
if self.emitted:
215+
if self.args:
216+
raise SignalEmittedError("Signal %r unexpectedly emitted with "
217+
"arguments %r" %
218+
(self.signal, list(self.args)))
219+
else:
220+
raise SignalEmittedError("Signal %r unexpectedly emitted" %
221+
(self.signal,))
222+
223+
188224
class SignalTimeoutError(Exception):
189225
"""
190226
.. versionadded:: 1.4
@@ -195,6 +231,16 @@ class SignalTimeoutError(Exception):
195231
pass
196232

197233

234+
class SignalEmittedError(Exception):
235+
"""
236+
.. versionadded:: 1.11
237+
238+
The exception thrown by :meth:`pytestqt.qtbot.QtBot.assertNotEmitted` if a
239+
signal was emitted unexpectedly.
240+
"""
241+
pass
242+
243+
198244
def _silent_disconnect(signal, slot):
199245
"""Disconnects a signal from a slot, ignoring errors. Sometimes
200246
Qt might disconnect a signal automatically for unknown reasons.

tests/test_wait_signal.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import functools
22
import time
33
import sys
4+
import fnmatch
45

56
import pytest
67

78
from pytestqt.qt_compat import QtCore, Signal, QT_API
8-
9+
from pytestqt.wait_signal import SignalEmittedError
910

1011
def test_signal_blocker_exception(qtbot):
1112
"""
@@ -312,3 +313,34 @@ def test_connected_signal(self, qtbot, signaller):
312313
blocker.connect(signaller.signal_args_2)
313314
signaller.signal_args_2.emit('foo', 2342)
314315
assert blocker.args == ['foo', 2342]
316+
317+
318+
class TestAssertNotEmitted:
319+
320+
"""Tests for qtbot.assertNotEmitted."""
321+
322+
def test_not_emitted(self, qtbot, signaller):
323+
with qtbot.assertNotEmitted(signaller.signal):
324+
pass
325+
326+
def test_emitted(self, qtbot, signaller):
327+
with pytest.raises(SignalEmittedError) as excinfo:
328+
with qtbot.assertNotEmitted(signaller.signal):
329+
signaller.signal.emit()
330+
331+
fnmatch.fnmatchcase(str(excinfo.value),
332+
"Signal * unexpectedly emitted.")
333+
334+
def test_emitted_args(self, qtbot, signaller):
335+
with pytest.raises(SignalEmittedError) as excinfo:
336+
with qtbot.assertNotEmitted(signaller.signal_args):
337+
signaller.signal_args.emit('foo', 123)
338+
339+
fnmatch.fnmatchcase(str(excinfo.value),
340+
"Signal * unexpectedly emitted with arguments "
341+
"['foo', 123]")
342+
343+
def test_disconnected(self, qtbot, signaller):
344+
with qtbot.assertNotEmitted(signaller.signal):
345+
pass
346+
signaller.signal.emit()

0 commit comments

Comments
 (0)