Commit 30fe4b99 authored by Jason Madden's avatar Jason Madden

Refactor test__core_stat into a proper TestCase.

This lets us start using our infrastructure for flaky timeouts.
parent 1a418d12
...@@ -641,7 +641,7 @@ class Hub(RawGreenlet): ...@@ -641,7 +641,7 @@ class Hub(RawGreenlet):
def wait(self, watcher): def wait(self, watcher):
""" """
Wait until the *watcher* (which should not be started) is ready. Wait until the *watcher* (which must not be started) is ready.
The current greenlet will be unscheduled during this time. The current greenlet will be unscheduled during this time.
...@@ -829,10 +829,7 @@ class Waiter(object): ...@@ -829,10 +829,7 @@ class Waiter(object):
__slots__ = ['hub', 'greenlet', 'value', '_exception'] __slots__ = ['hub', 'greenlet', 'value', '_exception']
def __init__(self, hub=None): def __init__(self, hub=None):
if hub is None: self.hub = get_hub() if hub is None else hub
self.hub = get_hub()
else:
self.hub = hub
self.greenlet = None self.greenlet = None
self.value = None self.value = None
self._exception = _NONE self._exception = _NONE
......
...@@ -115,7 +115,7 @@ class Timeout(BaseException): ...@@ -115,7 +115,7 @@ class Timeout(BaseException):
.. caution:: .. caution::
A *seconds* value less than 0.0 (e.g., -1) is poorly defined. In the future, A *seconds* value less than 0.0 (e.g., -1) is poorly defined. In the future,
support for negative values is likely to do the same thing as a value support for negative values is likely to do the same thing as a value
if ``None``. of ``None``.
.. versionchanged:: 1.1b2 .. versionchanged:: 1.1b2
If *seconds* is not given or is ``None``, no longer allocate a libev If *seconds* is not given or is ``None``, no longer allocate a libev
...@@ -137,6 +137,7 @@ class Timeout(BaseException): ...@@ -137,6 +137,7 @@ class Timeout(BaseException):
# Plus, in general, it should be more efficient # Plus, in general, it should be more efficient
self.timer = _FakeTimer self.timer = _FakeTimer
else: else:
# XXX: A zero second timer could cause libuv to block the loop.
self.timer = get_hub().loop.timer(seconds or 0.0, ref=ref, priority=priority) self.timer = get_hub().loop.timer(seconds or 0.0, ref=ref, priority=priority)
def start(self): def start(self):
......
from __future__ import print_function from __future__ import print_function
import gevent
import gevent.core
import os import os
import sys import tempfile
import time import time
#pylint: disable=protected-access import gevent
import gevent.core
filename = 'tmp.test__core_stat.%s' % os.getpid() import greentest
hub = gevent.get_hub() #pylint: disable=protected-access
DELAY = 0.5
EV_USE_INOTIFY = getattr(gevent.core, 'EV_USE_INOTIFY', None) DELAY = 0.5
WIN = sys.platform.startswith('win') WIN = greentest.WIN
LIBUV = getattr(gevent.core, 'libuv', None) LIBUV = greentest.LIBUV
def test(): class TestCoreStat(greentest.TestCase):
try:
open(filename, 'wb', buffering=0).close()
assert os.path.exists(filename), filename
def write(): __timeout__ = greentest.LARGE_TIMEOUT
with open(filename, 'wb', buffering=0) as f:
f.write(b'x')
start = time.time() def setUp(self):
greenlet = gevent.spawn_later(DELAY, write) super(TestCoreStat, self).setUp()
fd, path = tempfile.mkstemp(suffix='.gevent_test_core_stat')
os.close(fd)
self.temp_path = path
self.hub = gevent.get_hub()
# If we don't specify an interval, we default to zero. # If we don't specify an interval, we default to zero.
# libev interprets that as meaning to use its default interval, # libev interprets that as meaning to use its default interval,
# which is about 5 seconds. If we go below it's minimum check # which is about 5 seconds. If we go below it's minimum check
# threshold, it bumps it up to the minimum. # threshold, it bumps it up to the minimum.
watcher = hub.loop.stat(filename, interval=-1) self.watcher = self.hub.loop.stat(self.temp_path, interval=-1)
assert watcher.path == filename, (watcher.path, filename)
filenames = filename if isinstance(filename, bytes) else filename.encode('ascii') def tearDown(self):
assert watcher._paths == filenames, (watcher._paths, filenames) if os.path.exists(self.temp_path):
assert watcher.interval == -1 os.unlink(self.temp_path)
super(TestCoreStat, self).tearDown()
def check_attr(name, none):
# Deals with the complex behaviour of the 'attr' and 'prev' def _write(self):
# attributes on Windows. This codifies it, rather than simply letting with open(self.temp_path, 'wb', buffering=0) as f:
# the test fail, so we know exactly when and what changes it. f.write(b'x')
try:
x = getattr(watcher, name) def _check_attr(self, name, none):
except ImportError: # Deals with the complex behaviour of the 'attr' and 'prev'
if WIN: # attributes on Windows. This codifies it, rather than simply letting
# the 'posix' module is not available # the test fail, so we know exactly when and what changes it.
pass try:
else: x = getattr(self.watcher, name)
raise except ImportError:
if WIN:
# the 'posix' module is not available
pass
else:
raise
else:
if WIN and not LIBUV:
# The ImportError is only raised for the first time;
# after that, the attribute starts returning None
self.assertIsNone(x, "Only None is supported on Windows")
if none:
self.assertIsNone(x, name)
else: else:
if WIN and not LIBUV: self.assertIsNotNone(x, name)
# The ImportError is only raised for the first time;
# after that, the attribute starts returning None
assert x is None, ("Only None is supported on Windows", x)
if none:
assert x is None, x
else:
assert x is not None, x
with gevent.Timeout(5 + DELAY + 0.5): def _wait_on_greenlet(self, func, *greenlet_args):
hub.wait(watcher) start = time.time()
self.hub.loop.update()
greenlet = gevent.spawn_later(DELAY, func, *greenlet_args)
with gevent.Timeout(5 + DELAY + 0.5):
self.hub.wait(self.watcher)
now = time.time() now = time.time()
if now - start - DELAY <= 0.0:
# Sigh. This is especially true on PyPy. self.assertGreaterEqual(now, start, "Time must move forward")
assert WIN, ("Bad timer resolution expected on Windows, test is useless", start, now)
print("On windows, bad timer resolution prevents this test from running") wait_duration = now - start
return reaction = wait_duration - DELAY
reaction = now - start - DELAY
print('Watcher %s reacted after %.4f seconds (write)' % (watcher, reaction)) if reaction <= 0.0:
if reaction >= DELAY and EV_USE_INOTIFY: # Sigh. This is especially true on PyPy on Windows
print('WARNING: inotify failed (write)') raise greentest.FlakyTestRaceCondition(
assert reaction >= 0.0, 'Watcher %s reacted too early (write): %.3fs' % (watcher, reaction) "Bad timer resolution (on Windows?), test is useless. Start %s, now %s" % (start, now))
check_attr('attr', False)
check_attr('prev', False) self.assertGreaterEqual(
# The watcher interval changed after it started; -1 is illegal reaction, 0.0,
assert watcher.interval != -1, watcher.interval 'Watcher %s reacted too early: %.3fs' % (self.watcher, reaction))
greenlet.join() greenlet.join()
gevent.spawn_later(DELAY, os.unlink, filename)
start = time.time() def test_watcher_basics(self):
watcher = self.watcher
filename = self.temp_path
self.assertEqual(watcher.path, filename)
filenames = filename if isinstance(filename, bytes) else filename.encode('ascii')
self.assertEqual(watcher._paths, filenames)
self.assertEqual(watcher.interval, -1)
with gevent.Timeout(5 + DELAY + 0.5): def test_write(self):
hub.wait(watcher) self._wait_on_greenlet(self._write)
self._check_attr('attr', False)
self._check_attr('prev', False)
# The watcher interval changed after it started; -1 is illegal
self.assertNotEqual(self.watcher.interval, -1)
reaction = time.time() - start - DELAY def test_unlink(self):
print('Watcher %s reacted after %.4f seconds (unlink)' % (watcher, reaction)) self._wait_on_greenlet(os.unlink, self.temp_path)
if reaction >= DELAY and EV_USE_INOTIFY:
print('WARNING: inotify failed (unlink)')
assert reaction >= 0.0, 'Watcher %s reacted too early (unlink): %.3fs' % (watcher, reaction)
check_attr('attr', True)
check_attr('prev', False)
finally: self._check_attr('attr', True)
if os.path.exists(filename): self._check_attr('prev', False)
os.unlink(filename)
if __name__ == '__main__': if __name__ == '__main__':
test() greentest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment