spulec / freezegun

Let your Python tests travel through time
Apache License 2.0
4.15k stars 269 forks source link

FG incorrectly causes monotonic time to go back #556

Open christopherlb opened 1 month ago

christopherlb commented 1 month ago

The Python time.monotonic() clock is, by definition, a clock that cannot go back ( https://docs.python.org/3/library/time.html#time.monotonic ).

In other words, monotonic time can move forwards or stand still but never decrease. This is different to a wall clock (datetime.now()), which can go back due to manual adjustment or daylight savings etc.

Freezegun violates this concept, as causing the clock to travel back in time causes monotonic time to decrease.

This is illustrated by a failure in the following test:

    def test_time_monotonic(self) -> None:
        freezer = freeze_time("2012")
        freezer.start()
        start = time.monotonic()
        freezer.stop()

        # Simulate the system (wallclock) being set back
        freezer = freeze_time("2011")
        freezer.start()
        later = time.monotonic()
        freezer.stop()

        # Despite the system clock being moved back, monotonic time MUST never decrease.
        # It can "move forward" by 0 seconds, or more, but never back.
        assert later >= start

This test also fails if you freezetime.tick(negative_number), or seemingly any function that relies on this.

Essentially, freezegun needs to maintain two clock sources. It seems to only maintain one (time_to_freeze).

As the wall clock time (returned by e.g datetime.now()) is allowed to vary, both forwards and back, the clock source for time.monotonic() should discard negative updates. It's a design decision as to whether the test-developer needs to move both times independently with two freezegun function calls, or whether this is handled in the library.

The ability to fast forward monotonic time is an essential ability, as this is the preferred way of implementing timers (as opposed to alarms). If you can't fast forward this clock, you would need to sit and wait in test.