Merge "Add test coverage for splice
and tee
failure scenarios"
This commit is contained in:
commit
682f660d98
@ -32,7 +32,7 @@ from hashlib import md5
|
||||
from contextlib import closing, nested
|
||||
from gzip import GzipFile
|
||||
|
||||
from eventlet import tpool
|
||||
from eventlet import hubs, timeout, tpool
|
||||
from test.unit import (FakeLogger, mock as unit_mock, temptree,
|
||||
patch_policies, debug_logger)
|
||||
|
||||
@ -2285,6 +2285,90 @@ class TestDiskFile(unittest.TestCase):
|
||||
log_lines = self.df_mgr.logger.get_lines_for_level('warning')
|
||||
self.assert_('MD5 sockets' in log_lines[-1])
|
||||
|
||||
def test_tee_to_md5_pipe_length_mismatch(self):
|
||||
if not self._system_can_zero_copy():
|
||||
raise SkipTest("zero-copy support is missing")
|
||||
|
||||
self.conf['splice'] = 'on'
|
||||
|
||||
df = self._get_open_disk_file(fsize=16385)
|
||||
reader = df.reader()
|
||||
self.assertTrue(reader.can_zero_copy_send())
|
||||
|
||||
with mock.patch('swift.obj.diskfile.tee') as mock_tee:
|
||||
mock_tee.side_effect = lambda _1, _2, _3, cnt: cnt - 1
|
||||
|
||||
with open('/dev/null', 'w') as devnull:
|
||||
exc_re = (r'tee\(\) failed: tried to move \d+ bytes, but only '
|
||||
'moved -?\d+')
|
||||
self.assertRaisesRegexp(Exception, exc_re,
|
||||
reader.zero_copy_send,
|
||||
devnull.fileno())
|
||||
|
||||
def test_splice_to_wsockfd_blocks(self):
|
||||
if not self._system_can_zero_copy():
|
||||
raise SkipTest("zero-copy support is missing")
|
||||
|
||||
self.conf['splice'] = 'on'
|
||||
|
||||
df = self._get_open_disk_file(fsize=16385)
|
||||
reader = df.reader()
|
||||
self.assertTrue(reader.can_zero_copy_send())
|
||||
|
||||
with mock.patch('swift.obj.diskfile.splice') as mock_splice, \
|
||||
mock.patch.object(reader, 'close', side_effect=reader.close) \
|
||||
as mock_close, \
|
||||
open('/dev/null', 'w') as devnull, \
|
||||
mock.patch('swift.obj.diskfile.trampoline') as mock_trampoline:
|
||||
|
||||
# Set up mock of `splice`
|
||||
splice_called = [False] # State hack
|
||||
|
||||
def fake_splice(fd_in, off_in, fd_out, off_out, len_, flags):
|
||||
if fd_out == devnull.fileno() and not splice_called[0]:
|
||||
splice_called[0] = True
|
||||
err = errno.EWOULDBLOCK
|
||||
raise IOError(err, os.strerror(err))
|
||||
|
||||
return splice(fd_in, off_in, fd_out, off_out,
|
||||
len_, flags)
|
||||
|
||||
mock_splice.side_effect = fake_splice
|
||||
|
||||
# Set up mock of `trampoline`
|
||||
# There are 2 reasons to mock this:
|
||||
#
|
||||
# - We want to ensure it's called with the expected arguments at
|
||||
# least once
|
||||
# - When called with our write FD (which points to `/dev/null`), we
|
||||
# can't actually call `trampoline`, because adding such FD to an
|
||||
# `epoll` handle results in `EPERM`
|
||||
def fake_trampoline(fd, read=None, write=None, timeout=None,
|
||||
timeout_exc=timeout.Timeout,
|
||||
mark_as_closed=None):
|
||||
if write and fd == devnull.fileno():
|
||||
return
|
||||
else:
|
||||
hubs.trampoline(fd, read=read, write=write,
|
||||
timeout=timeout, timeout_exc=timeout_exc,
|
||||
mark_as_closed=mark_as_closed)
|
||||
|
||||
mock_trampoline.side_effect = fake_trampoline
|
||||
|
||||
reader.zero_copy_send(devnull.fileno())
|
||||
|
||||
# Assert the end of `zero_copy_send` was reached
|
||||
mock_close.assert_called()
|
||||
# Assert there was at least one call to `trampoline` waiting for
|
||||
# `write` access to the output FD
|
||||
mock_trampoline.assert_any_call(devnull.fileno(), write=True)
|
||||
# Assert at least one call to `splice` with the output FD we expect
|
||||
for call in mock_splice.call_args_list:
|
||||
args = call[0]
|
||||
if args[2] == devnull.fileno():
|
||||
break
|
||||
else:
|
||||
self.fail('`splice` not called with expected arguments')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
Reference in New Issue
Block a user