Commit 9b87786b authored by Jason Madden's avatar Jason Madden

More refactoring to separate concurrent from synchronous FileObject tests.

This cleans up the skips needed.
parent ab6536fd
...@@ -465,6 +465,7 @@ if LIBUV: ...@@ -465,6 +465,7 @@ if LIBUV:
# mostly but not exclusively on Python 2. # mostly but not exclusively on Python 2.
'test_socket.BufferIOTest.testRecvFromIntoBytearray', 'test_socket.BufferIOTest.testRecvFromIntoBytearray',
'test_socket.BufferIOTest.testRecvFromIntoArray', 'test_socket.BufferIOTest.testRecvFromIntoArray',
'test_socket.BufferIOTest.testRecvIntoArray',
'test_socket.BufferIOTest.testRecvFromIntoEmptyBuffer', 'test_socket.BufferIOTest.testRecvFromIntoEmptyBuffer',
'test_socket.BufferIOTest.testRecvFromIntoMemoryview', 'test_socket.BufferIOTest.testRecvFromIntoMemoryview',
'test_socket.BufferIOTest.testRecvFromIntoSmallBuffer', 'test_socket.BufferIOTest.testRecvFromIntoSmallBuffer',
......
...@@ -12,7 +12,7 @@ import gevent.testing as greentest ...@@ -12,7 +12,7 @@ import gevent.testing as greentest
from gevent.testing.sysinfo import PY3 from gevent.testing.sysinfo import PY3
from gevent.testing.flaky import reraiseFlakyTestRaceConditionLibuv from gevent.testing.flaky import reraiseFlakyTestRaceConditionLibuv
from gevent.testing.skipping import skipOnLibuvOnCIOnPyPy from gevent.testing.skipping import skipOnLibuvOnCIOnPyPy
from gevent.testing.skipping import skipOnWindows
try: try:
ResourceWarning ResourceWarning
...@@ -28,24 +28,26 @@ def writer(fobj, line): ...@@ -28,24 +28,26 @@ def writer(fobj, line):
fobj.close() fobj.close()
class TestFileObjectThread(greentest.TestCase): def close_fd_quietly(fd):
try:
os.close(fd)
except (IOError, OSError):
pass
class TestFileObjectBlock(greentest.TestCase):
def _getTargetClass(self): def _getTargetClass(self):
return fileobject.FileObjectThread return fileobject.FileObjectBlock
def _makeOne(self, *args, **kwargs): def _makeOne(self, *args, **kwargs):
return self._getTargetClass()(*args, **kwargs) return self._getTargetClass()(*args, **kwargs)
def _test_del(self, **kwargs): def _test_del(self, **kwargs):
pipe = os.pipe() r, w = os.pipe()
try: self.addCleanup(close_fd_quietly, r)
self._do_test_del(pipe, **kwargs) self.addCleanup(close_fd_quietly, w)
finally:
for f in pipe: self._do_test_del((r, w), **kwargs)
try:
os.close(f)
except (IOError, OSError):
pass
def _do_test_del(self, pipe, **kwargs): def _do_test_del(self, pipe, **kwargs):
r, w = pipe r, w = pipe
...@@ -76,48 +78,13 @@ class TestFileObjectThread(greentest.TestCase): ...@@ -76,48 +78,13 @@ class TestFileObjectThread(greentest.TestCase):
with self._makeOne(r, 'rb') as fobj: with self._makeOne(r, 'rb') as fobj:
self.assertEqual(fobj.read(), b'x') self.assertEqual(fobj.read(), b'x')
# We only use FileObjectThread on Win32. Sometimes the
# visibility of the 'close' operation, which happens in a
# background thread, doesn't make it to the foreground
# thread in a timely fashion, leading to 'os.close(4) must
# not succeed' in test_del_close. We have the same thing
# with flushing and closing in test_newlines. Both of
# these are most commonly (only?) observed on Py27/64-bit.
# They also appear on 64-bit 3.6 with libuv
@skipOnWindows("Thread race conditions")
def test_del(self): def test_del(self):
# Close should be true by default # Close should be true by default
self._test_del() self._test_del()
@skipOnWindows("Thread race conditions")
def test_del_close(self): def test_del_close(self):
self._test_del(close=True) self._test_del(close=True)
# FileObjectThread uses os.fdopen() when passed a file-descriptor, which returns
# an object with a destructor that can't be bypassed, so we can't even
# create one that way
def test_del_noclose(self):
with self.assertRaisesRegex(TypeError,
'FileObjectThread does not support close=False on an fd.'):
self._test_del(close=False)
def test_newlines(self):
import warnings
r, w = os.pipe()
lines = [b'line1\n', b'line2\r', b'line3\r\n', b'line4\r\nline5', b'\nline6']
g = gevent.spawn(writer, self._makeOne(w, 'wb'), lines)
try:
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
# U is deprecated in Python 3, shows up on FileObjectThread
fobj = self._makeOne(r, 'rU')
result = fobj.read()
fobj.close()
self.assertEqual('line1\nline2\nline3\nline4\nline5\nline6', result)
finally:
g.kill()
@skipOnLibuvOnCIOnPyPy("This appears to crash on libuv/pypy/travis.") @skipOnLibuvOnCIOnPyPy("This appears to crash on libuv/pypy/travis.")
# No idea why, can't duplicate locally. # No idea why, can't duplicate locally.
def test_seek(self): def test_seek(self):
...@@ -144,7 +111,9 @@ class TestFileObjectThread(greentest.TestCase): ...@@ -144,7 +111,9 @@ class TestFileObjectThread(greentest.TestCase):
# That shouldn't have any effect on io watchers, though, which were # That shouldn't have any effect on io watchers, though, which were
# already being explicitly closed. # already being explicitly closed.
reraiseFlakyTestRaceConditionLibuv() reraiseFlakyTestRaceConditionLibuv()
if PY3 or not isinstance(f, fileobject.FileObjectThread): if PY3 or hasattr(f, 'seekable'):
# On Python 3, all objects should have seekable.
# On Python 2, only our custom objects do.
self.assertTrue(f.seekable()) self.assertTrue(f.seekable())
f.seek(15) f.seek(15)
self.assertEqual(15, f.tell()) self.assertEqual(15, f.tell())
...@@ -161,6 +130,11 @@ class TestFileObjectThread(greentest.TestCase): ...@@ -161,6 +130,11 @@ class TestFileObjectThread(greentest.TestCase):
x.close() x.close()
y.close() y.close()
class ConcurrentFileObjectMixin(object):
# Additional tests for fileobjects that cooperate
# and we have full control of the implementation
def test_read1(self): def test_read1(self):
# Issue #840 # Issue #840
r, w = os.pipe() r, w = os.pipe()
...@@ -170,7 +144,6 @@ class TestFileObjectThread(greentest.TestCase): ...@@ -170,7 +144,6 @@ class TestFileObjectThread(greentest.TestCase):
self._close_on_teardown(y) self._close_on_teardown(y)
self.assertTrue(hasattr(x, 'read1')) self.assertTrue(hasattr(x, 'read1'))
#if FileObject is not FileObjectThread:
def test_bufsize_0(self): def test_bufsize_0(self):
# Issue #840 # Issue #840
r, w = os.pipe() r, w = os.pipe()
...@@ -186,27 +159,72 @@ class TestFileObjectThread(greentest.TestCase): ...@@ -186,27 +159,72 @@ class TestFileObjectThread(greentest.TestCase):
b = x.read(1) b = x.read(1)
self.assertEqual(b, b'2') self.assertEqual(b, b'2')
def test_newlines(self):
import warnings
r, w = os.pipe()
lines = [b'line1\n', b'line2\r', b'line3\r\n', b'line4\r\nline5', b'\nline6']
g = gevent.spawn(writer, self._makeOne(w, 'wb'), lines)
try:
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
# U is deprecated in Python 3, shows up on FileObjectThread
fobj = self._makeOne(r, 'rU')
result = fobj.read()
fobj.close()
self.assertEqual('line1\nline2\nline3\nline4\nline5\nline6', result)
finally:
g.kill()
class TestFileObjectThread(ConcurrentFileObjectMixin,
TestFileObjectBlock):
def _getTargetClass(self):
return fileobject.FileObjectThread
# FileObjectThread uses os.fdopen() when passed a file-descriptor,
# which returns an object with a destructor that can't be
# bypassed, so we can't even create one that way
def test_del_noclose(self):
with self.assertRaisesRegex(TypeError,
'FileObjectThread does not support close=False on an fd.'):
self._test_del(close=False)
# We don't test this with FileObjectThread. Sometimes the
# visibility of the 'close' operation, which happens in a
# background thread, doesn't make it to the foreground
# thread in a timely fashion, leading to 'os.close(4) must
# not succeed' in test_del_close. We have the same thing
# with flushing and closing in test_newlines. Both of
# these are most commonly (only?) observed on Py27/64-bit.
# They also appear on 64-bit 3.6 with libuv
def test_del(self):
raise unittest.SkipTest("Race conditions")
def test_del_close(self):
raise unittest.SkipTest("Race conditions")
@unittest.skipUnless( @unittest.skipUnless(
hasattr(fileobject, 'FileObjectPosix'), hasattr(fileobject, 'FileObjectPosix'),
"Needs FileObjectPosix" "Needs FileObjectPosix"
) )
class TestFileObjectPosix(TestFileObjectThread): class TestFileObjectPosix(ConcurrentFileObjectMixin,
TestFileObjectBlock):
def _getTargetClass(self): def _getTargetClass(self):
return fileobject.FileObjectPosix return fileobject.FileObjectPosix
def test_del_noclose(self):
self._test_del(close=False)
def test_seek_raises_ioerror(self): def test_seek_raises_ioerror(self):
# https://github.com/gevent/gevent/issues/1323 # https://github.com/gevent/gevent/issues/1323
# Get a non-seekable file descriptor # Get a non-seekable file descriptor
r, w = os.pipe() r, w = os.pipe()
self.addCleanup(os.close, r) self.addCleanup(close_fd_quietly, r)
self.addCleanup(os.close, w) self.addCleanup(close_fd_quietly, w)
with self.assertRaises(OSError) as ctx: with self.assertRaises(OSError) as ctx:
os.lseek(r, 0, os.SEEK_SET) os.lseek(r, 0, os.SEEK_SET)
......
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