import pytest
import time

from pytestqt.qt_compat import QtCore, Signal


class Signaller(QtCore.QObject):

    signal = Signal()


def test_signal_blocker_exception(qtbot):
    """
    Make sure waitSignal without signals and timeout doesn't hang, but raises
    ValueError instead.
    """
    with pytest.raises(ValueError):
        qtbot.waitSignal(None, None).wait()


def explicit_wait(qtbot, signal, timeout):
    """
    Explicit wait for the signal using blocker API.
    """
    blocker = qtbot.waitSignal(signal, timeout)
    assert not blocker.signal_triggered
    blocker.wait()
    return blocker


def context_manager_wait(qtbot, signal, timeout):
    """
    Waiting for signal using context manager API.
    """
    with qtbot.waitSignal(signal, timeout) as blocker:
        pass
    return blocker


@pytest.mark.parametrize(
    ('wait_function', 'emit_delay', 'timeout', 'expected_signal_triggered'),
    [
        (explicit_wait, 500, 2000, True),
        (explicit_wait, 500, None, True),
        (context_manager_wait, 500, 2000, True),
        (context_manager_wait, 500, None, True),
        (explicit_wait, 2000, 500, False),
        (context_manager_wait, 2000, 500, False),
    ]
)
def test_signal_triggered(qtbot, wait_function, emit_delay, timeout,
                          expected_signal_triggered):
    """
    Testing for a signal in different conditions, ensuring we are obtaining
    the expected results.
    """
    signaller = Signaller()
    QtCore.QTimer.singleShot(emit_delay, signaller.signal.emit)

    # block signal until either signal is emitted or timeout is reached
    start_time = time.time()
    blocker = wait_function(qtbot, signaller.signal, timeout)

    # Check that event loop exited.
    assert not blocker._loop.isRunning()

    # ensure that either signal was triggered or timeout occurred
    assert blocker.signal_triggered == expected_signal_triggered

    # Check that we exited by the earliest parameter; timeout = None means
    # wait forever, so ensure we waited at most 4 times emit-delay
    if timeout is None:
        timeout = emit_delay * 4
    max_wait_ms = max(emit_delay, timeout)
    assert time.time() - start_time < (max_wait_ms / 1000.0)


def test_explicit_emit(qtbot):
    """
    Make sure an explicit emit() inside a waitSignal block works.
    """
    signaller = Signaller()
    with qtbot.waitSignal(signaller.signal, timeout=5000) as waiting:
        signaller.signal.emit()

    assert waiting.signal_triggered
@@ -1201,16 +1201,88 @@
 ),%0A    %5D
+ * 2  # Running all tests twice to catch a QTimer segfault, see #42/#43.
 %0A)%0Adef t
@@ -2552,24 +2552,25 @@
 waiting.signal_triggered
+%0A

Add test for QTimer segfault.