diff --git a/humanfriendly/__init__.py b/humanfriendly/__init__.py index 4c0a333..e2abc04 100644 --- a/humanfriendly/__init__.py +++ b/humanfriendly/__init__.py @@ -656,9 +656,9 @@ def __init__(self, start_time=None, resumable=False): if resumable: self.monotonic = True self.resumable = True - self.start_time = 0.0 + self.start_time = None self.total_time = 0.0 - elif start_time: + elif start_time is not None: self.monotonic = False self.resumable = False self.start_time = start_time @@ -687,9 +687,9 @@ def __exit__(self, exc_type=None, exc_value=None, traceback=None): """ if not self.resumable: raise ValueError("Timer is not resumable!") - if self.start_time: + if self.start_time is not None: self.total_time += monotonic() - self.start_time - self.start_time = 0.0 + self.start_time = None def sleep(self, seconds): """ @@ -725,7 +725,7 @@ def elapsed_time(self): elapsed_time = 0 if self.resumable: elapsed_time += self.total_time - if self.start_time: + if self.start_time is not None: current_time = monotonic() if self.monotonic else time.time() elapsed_time += current_time - self.start_time return elapsed_time diff --git a/humanfriendly/tests.py b/humanfriendly/tests.py index 72dad99..ea5d59a 100644 --- a/humanfriendly/tests.py +++ b/humanfriendly/tests.py @@ -744,6 +744,47 @@ def test_split(self): self.assertEqual(split('foo, bar, baz'), ['foo', 'bar', 'baz']) self.assertEqual(split('foo,bar,baz'), ['foo', 'bar', 'baz']) + def test_timer_epoch_start(self): + """An explicit epoch start uses the wall clock, including zero.""" + with PatchedAttribute(time, 'time', lambda: 12.5): + for start in (0, 0.0, -2.0, 2.0): + timer = Timer(start_time=start) + self.assertFalse(timer.monotonic) + self.assertEqual(timer.elapsed_time, 12.5 - start) + + def test_timer_zero_monotonic_start(self): + """A zero monotonic reading remains an active timer.""" + with PatchedItem(Timer.__init__.__globals__, 'monotonic', lambda: 0.0): + automatic = Timer() + resumable = Timer(resumable=True) + resumable.__enter__() + with PatchedItem(Timer.__init__.__globals__, 'monotonic', lambda: 2.5): + self.assertEqual(automatic.elapsed_time, 2.5) + self.assertEqual(resumable.elapsed_time, 2.5) + resumable.__exit__(None, None, None) + self.assertEqual(resumable.elapsed_time, 2.5) + with PatchedItem(Timer.__init__.__globals__, 'monotonic', lambda: 4.0): + self.assertEqual(resumable.elapsed_time, 2.5) + + def test_timer_inactive_and_resumed(self): + """Paused timers stay fixed and resume accumulating elapsed time.""" + with PatchedItem(Timer.__init__.__globals__, 'monotonic', lambda: 5.0): + timer = Timer(resumable=True) + self.assertEqual(timer.elapsed_time, 0) + timer.__exit__(None, None, None) + self.assertEqual(timer.elapsed_time, 0) + self.assertIs(timer.__enter__(), timer) + with PatchedItem(Timer.__init__.__globals__, 'monotonic', lambda: 7.0): + timer.__exit__(None, None, None) + self.assertEqual(timer.elapsed_time, 2.0) + timer.__exit__(None, None, None) + self.assertEqual(timer.elapsed_time, 2.0) + timer.__enter__() + with PatchedItem(Timer.__init__.__globals__, 'monotonic', lambda: 10.0): + self.assertEqual(timer.elapsed_time, 5.0) + timer.__exit__(None, None, None) + self.assertEqual(timer.elapsed_time, 5.0) + def test_timer(self): """Test :func:`humanfriendly.Timer`.""" for seconds, text in ((1, '1 second'),