diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index 631551d337..dbbd7cbf5c 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -22,6 +22,7 @@ import math import os import pickle import sys +import traceback import unittest from contextlib import closing, contextmanager from gzip import GzipFile @@ -65,7 +66,7 @@ from swift.obj import server as object_server from swift.common.middleware import proxy_logging, versioned_writes from swift.common.middleware.acl import parse_acl, format_acl from swift.common.exceptions import ChunkReadTimeout, DiskFileNotExist, \ - APIVersionError + APIVersionError, ChunkWriteTimeout from swift.common import utils, constraints from swift.common.ring import RingData from swift.common.utils import mkdirs, normalize_timestamp, NullLogger @@ -5827,30 +5828,52 @@ class TestObjectController(unittest.TestCase): exp = 'HTTP/1.1 201' self.assertEqual(headers[:len(exp)], exp) - with mock.patch.object(_test_servers[0], 'client_timeout', new=5): - # get object - fd.write('GET /v1/a/ec-discon/test HTTP/1.1\r\n' - 'Host: localhost\r\n' - 'Connection: close\r\n' - 'X-Storage-Token: t\r\n' - '\r\n') - fd.flush() - headers = readuntil2crlfs(fd) - exp = 'HTTP/1.1 200' - self.assertEqual(headers[:len(exp)], exp) + class WrappedTimeout(ChunkWriteTimeout): + def __enter__(self): + timeouts[self] = traceback.extract_stack() + return super(WrappedTimeout, self).__enter__() - # read most of the object, and disconnect - fd.read(10) - sock.fd._sock.close() - condition = \ - lambda: _test_servers[0].logger.get_lines_for_level('warning') - self._sleep_enough(condition) + def __exit__(self, typ, value, tb): + timeouts[self] = None + return super(WrappedTimeout, self).__exit__(typ, value, tb) + + timeouts = {} + with mock.patch('swift.proxy.controllers.base.ChunkWriteTimeout', + WrappedTimeout): + with mock.patch.object(_test_servers[0], 'client_timeout', new=5): + # get object + fd.write('GET /v1/a/ec-discon/test HTTP/1.1\r\n' + 'Host: localhost\r\n' + 'Connection: close\r\n' + 'X-Storage-Token: t\r\n' + '\r\n') + fd.flush() + headers = readuntil2crlfs(fd) + exp = 'HTTP/1.1 200' + self.assertEqual(headers[:len(exp)], exp) + + # read most of the object, and disconnect + fd.read(10) + sock.fd._sock.close() + self._sleep_enough( + lambda: + _test_servers[0].logger.get_lines_for_level('warning')) # check for disconnect message! expected = ['Client disconnected on read'] * 2 self.assertEqual( _test_servers[0].logger.get_lines_for_level('warning'), expected) + # check that no coro was left waiting to write + self.assertTrue(timeouts) # sanity - WrappedTimeout did get called + missing_exits = filter(lambda tb: tb is not None, timeouts.values()) + self.assertFalse( + missing_exits, 'Failed to exit all ChunkWriteTimeouts.\n' + + ''.join(['No exit from ChunkWriteTimeout entered at:\n' + + ''.join(traceback.format_list(tb)[:-1]) + for tb in missing_exits])) + # and check that the ChunkWriteTimeouts did not raise Exceptions + self.assertFalse(_test_servers[0].logger.get_lines_for_level('error')) @unpatch_policies def test_ec_client_put_disconnect(self):