You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
5.8 KiB
Python
181 lines
5.8 KiB
Python
from __future__ import print_function, absolute_import, division
|
|
|
|
import sys
|
|
from os import pipe
|
|
|
|
|
|
import gevent
|
|
from gevent import os
|
|
from gevent import Greenlet, joinall
|
|
|
|
from gevent import testing as greentest
|
|
from gevent.testing import mock
|
|
from gevent.testing import six
|
|
from gevent.testing.skipping import skipOnLibuvOnPyPyOnWin
|
|
|
|
|
|
class TestOS_tp(greentest.TestCase):
|
|
|
|
__timeout__ = greentest.LARGE_TIMEOUT
|
|
|
|
def pipe(self):
|
|
return pipe()
|
|
|
|
read = staticmethod(os.tp_read)
|
|
write = staticmethod(os.tp_write)
|
|
|
|
@skipOnLibuvOnPyPyOnWin("Sometimes times out")
|
|
def _test_if_pipe_blocks(self, buffer_class):
|
|
r, w = self.pipe()
|
|
# set nbytes such that for sure it is > maximum pipe buffer
|
|
nbytes = 1000000
|
|
block = b'x' * 4096
|
|
buf = buffer_class(block)
|
|
# Lack of "nonlocal" keyword in Python 2.x:
|
|
bytesread = [0]
|
|
byteswritten = [0]
|
|
|
|
def produce():
|
|
while byteswritten[0] != nbytes:
|
|
bytesleft = nbytes - byteswritten[0]
|
|
byteswritten[0] += self.write(w, buf[:min(bytesleft, 4096)])
|
|
|
|
def consume():
|
|
while bytesread[0] != nbytes:
|
|
bytesleft = nbytes - bytesread[0]
|
|
bytesread[0] += len(self.read(r, min(bytesleft, 4096)))
|
|
|
|
producer = Greenlet(produce)
|
|
producer.start()
|
|
consumer = Greenlet(consume)
|
|
consumer.start_later(1)
|
|
# If patching was not succesful, the producer will have filled
|
|
# the pipe before the consumer starts, and would block the entire
|
|
# process. Therefore the next line would never finish.
|
|
joinall([producer, consumer])
|
|
self.assertEqual(bytesread[0], nbytes)
|
|
self.assertEqual(bytesread[0], byteswritten[0])
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
def test_if_pipe_blocks_buffer(self):
|
|
self._test_if_pipe_blocks(six.builtins.buffer)
|
|
|
|
if sys.version_info[:2] >= (2, 7):
|
|
|
|
def test_if_pipe_blocks_memoryview(self):
|
|
self._test_if_pipe_blocks(six.builtins.memoryview)
|
|
|
|
|
|
@greentest.skipUnless(hasattr(os, 'make_nonblocking'),
|
|
"Only on POSIX")
|
|
class TestOS_nb(TestOS_tp):
|
|
|
|
def read(self, fd, count):
|
|
return os.nb_read(fd, count)
|
|
|
|
def write(self, fd, count):
|
|
return os.nb_write(fd, count)
|
|
|
|
def pipe(self):
|
|
r, w = super(TestOS_nb, self).pipe()
|
|
os.make_nonblocking(r)
|
|
os.make_nonblocking(w)
|
|
return r, w
|
|
|
|
def _make_ignored_oserror(self):
|
|
import errno
|
|
ignored_oserror = OSError()
|
|
ignored_oserror.errno = errno.EINTR
|
|
return ignored_oserror
|
|
|
|
|
|
def _check_hub_event_closed(self, mock_get_hub, fd, event):
|
|
mock_get_hub.assert_called_once_with()
|
|
hub = mock_get_hub.return_value
|
|
io = hub.loop.io
|
|
io.assert_called_once_with(fd, event)
|
|
|
|
event = io.return_value
|
|
event.close.assert_called_once_with()
|
|
|
|
def _test_event_closed_on_normal_io(self, nb_func, nb_arg,
|
|
mock_io, mock_get_hub, event):
|
|
mock_io.side_effect = [self._make_ignored_oserror(), 42]
|
|
|
|
fd = 100
|
|
result = nb_func(fd, nb_arg)
|
|
self.assertEqual(result, 42)
|
|
|
|
self._check_hub_event_closed(mock_get_hub, fd, event)
|
|
|
|
def _test_event_closed_on_io_error(self, nb_func, nb_arg,
|
|
mock_io, mock_get_hub, event):
|
|
mock_io.side_effect = [self._make_ignored_oserror(), ValueError()]
|
|
|
|
fd = 100
|
|
|
|
with self.assertRaises(ValueError):
|
|
nb_func(fd, nb_arg)
|
|
|
|
self._check_hub_event_closed(mock_get_hub, fd, event)
|
|
|
|
@mock.patch('gevent.os.get_hub')
|
|
@mock.patch('gevent.os._write')
|
|
def test_event_closed_on_write(self, mock_write, mock_get_hub):
|
|
self._test_event_closed_on_normal_io(os.nb_write, b'buf',
|
|
mock_write, mock_get_hub,
|
|
2)
|
|
|
|
@mock.patch('gevent.os.get_hub')
|
|
@mock.patch('gevent.os._write')
|
|
def test_event_closed_on_write_error(self, mock_write, mock_get_hub):
|
|
self._test_event_closed_on_io_error(os.nb_write, b'buf',
|
|
mock_write, mock_get_hub,
|
|
2)
|
|
|
|
@mock.patch('gevent.os.get_hub')
|
|
@mock.patch('gevent.os._read')
|
|
def test_event_closed_on_read(self, mock_read, mock_get_hub):
|
|
self._test_event_closed_on_normal_io(os.nb_read, b'buf',
|
|
mock_read, mock_get_hub,
|
|
1)
|
|
|
|
@mock.patch('gevent.os.get_hub')
|
|
@mock.patch('gevent.os._read')
|
|
def test_event_closed_on_read_error(self, mock_read, mock_get_hub):
|
|
self._test_event_closed_on_io_error(os.nb_read, b'buf',
|
|
mock_read, mock_get_hub,
|
|
1)
|
|
|
|
|
|
@greentest.skipUnless(hasattr(os, 'fork_and_watch'),
|
|
"Only on POSIX")
|
|
class TestForkAndWatch(greentest.TestCase):
|
|
|
|
__timeout__ = greentest.LARGE_TIMEOUT
|
|
|
|
def test_waitpid_all(self):
|
|
# Cover this specific case.
|
|
pid = os.fork_and_watch()
|
|
if pid:
|
|
os.waitpid(-1, 0)
|
|
# Can't assert on what the found pid actually was,
|
|
# our testrunner may have spawned multiple children.
|
|
os._reap_children(0) # make the leakchecker happy
|
|
else: # pragma: no cover
|
|
gevent.sleep(2)
|
|
# The test framework will catch a regular SystemExit
|
|
# from sys.exit(), we need to just kill the process.
|
|
os._exit(0)
|
|
|
|
def test_waitpid_wrong_neg(self):
|
|
self.assertRaises(OSError, os.waitpid, -2, 0)
|
|
|
|
def test_waitpid_wrong_pos(self):
|
|
self.assertRaises(OSError, os.waitpid, 1, 0)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
greentest.main()
|