Beispiel #1
0
def autojump_clock(request):
    # Event dispatching through PostgreSQL LISTEN/NOTIFY is
    # invisible from trio point of view, hence waiting for
    # event with autojump_threshold=0 means we jump to timeout
    if request.config.getoption("--postgresql"):
        return MockClock(autojump_threshold=0.1)
    else:
        return MockClock(autojump_threshold=0)
Beispiel #2
0
from .head import *
from .resource import Memory, ZERO

import pytest
import trio
from trio.testing import MockClock

from functools import partial
from io import StringIO

sprint = partial(trio.run, clock=MockClock(rate=1000))
GrainExecutor = partial(GrainExecutor, config_file=False)


async def rev(n, i):
    await trio.sleep(n - i)
    return i


def test_order(monkeypatch):
    monkeypatch.setattr(trio, "run", sprint)
    N = 10
    with GrainExecutor([], Memory(8)) as exer:
        for i in range(N):
            exer.submit(Memory(2), rev, N, i)
    assert exer.results == list(range(N))


class Critical(Exception):
    pass
Beispiel #3
0
def autojump_clock():
    return MockClock(autojump_threshold=0)
Beispiel #4
0
def mock_clock():
    return MockClock()
 def _try_customizing(self):
     with pytest.raises(RuntimeError):
         self.set_clock(MockClock())
     with pytest.raises(RuntimeError):
         self.push_instrument(Instrument())
Beispiel #6
0
def frozen_clock():
    # Mocked clock is a slippy slope: we want time to go faster (or even to
    # jump to arbitrary point in time !) on some part of our application while
    # some other parts should keep using the real time.
    # For instance we want to make sure some part of a test doesn't take more than
    # x seconds in real life (typically to detect deadlock), but this test might
    # be about a ping occurring every 30s so we want to simulate this wait.
    #
    # The simple solution is to use `MockClock.rate` to make time go faster,
    # but it's bad idea given we end up with two antagonistic goals:
    # - rate should be as high as possible so that ping wait goes as fast as possible
    # - the highest rate is, the smallest real time window we have when checking for
    #   deadlock, this is especially an issue given developer machine is a behemoth
    #   while CI run on potatoes (especially on MacOS) shared with other builds...
    #
    # So the solution we choose here is to separate the two times:
    # - Parsec codebase uses the trio clock and `trio.fail_after/move_on_after`
    # - Test codebase can use `trio.fail_after/move_on_after` as long as the test
    #   doesn't use a mock clock
    # - In case of mock clock, test codebase must use `real_clock_timeout` that
    #   relies on monotonic clock and hence is totally isolated from trio's clock.
    #
    # On top of that we must be careful about the configuration of the mock clock !
    # As we said the Parsec codebase (i.e. not the tests) uses the trio clock for
    # timeout handling & sleep (e.g. in the managers), hence:
    # - Using `MockClock.rate` with a high value still lead to the issue dicussed above.
    # - `trio.to_thread.run_sync` doesn't play nice with `MockClock.autojump_threshold = 0`
    #   given trio considers the coroutine waiting for the thread is idle and hence
    #   trigger the clock jump. So a perfectly fine async code may break tests in
    #   an unexpected way if it starts using `trio.to_thread.run_sync`...
    #
    # So the idea of the `frozen_clock` is to only advance when expecially
    # specified in the test (i.e. rate 0 and no autojump_threshold).
    # This way only the test code has control over the application timeout
    # handling, and we have a clean separation with the test timeout (i.e. using
    # `real_clock_timeout` to detect the test endup in a deadlock)
    #
    # The drawback of this approach is manually handling time jump can be cumbersome.
    # For instance the backend connection retry logic:
    # - sleeps for some time
    # - connects to the backend
    # - starts sync&message monitors
    # - message monitor may trigger modifications in the sync monitor
    # - in case of modification, sync monitor is going to sleep for a short time
    #   before doing the sync of the modification
    #
    # So to avoid having to mix `MockClock.jump` and `trio.testing.wait_all_tasks_blocked`
    # in a very complex and fragile way, we introduce the `sleep_with_autojump()`
    # method that is the only place where clock is going to move behind our back, but
    # for only the amount of time we choose, and only in a very explicit manner.
    #
    # Finally, an additional bonus to this approach is we can use breakpoint in the
    # code without worrying about triggering a timeout ;-)

    clock = MockClock(rate=0, autojump_threshold=math.inf)

    clock.real_clock_timeout = real_clock_timeout  # Quick access helper

    async def _sleep_with_autojump(seconds):
        old_rate = clock.rate
        old_autojump_threshold = clock.autojump_threshold
        clock.rate = 0
        clock.autojump_threshold = 0.01
        try:
            await trio.sleep(seconds)
        finally:
            clock.rate = old_rate
            clock.autojump_threshold = old_autojump_threshold

    clock.sleep_with_autojump = _sleep_with_autojump
    yield clock