|
11 | 11 | import threading |
12 | 12 | import unittest |
13 | 13 | import socketserver |
| 14 | +from unittest import mock |
14 | 15 |
|
15 | 16 | import test.support |
16 | 17 | from test.support import reap_children, verbose |
@@ -467,6 +468,59 @@ def run_client(): |
467 | 468 |
|
468 | 469 | class MiscTestCase(unittest.TestCase): |
469 | 470 |
|
| 471 | + def test_handle_request_timeout_at_deadline(self): |
| 472 | + # Let a second poll advance past the deadline so a regression fails |
| 473 | + # instead of looping forever on a frozen clock. |
| 474 | + cases = ( |
| 475 | + (0, [10, 10, 11], [0]), |
| 476 | + (1, [10, 11, 12], [1]), |
| 477 | + (1, [10, 10.25, 11, 12], [1, 0.75]), |
| 478 | + (1, [10, 11.25], [1]), |
| 479 | + ) |
| 480 | + for timeout, clock, expected_timeouts in cases: |
| 481 | + with self.subTest(timeout=timeout, clock=clock): |
| 482 | + with socketserver.TCPServer( |
| 483 | + (HOST, 0), socketserver.BaseRequestHandler |
| 484 | + ) as server: |
| 485 | + server.timeout = timeout |
| 486 | + with ( |
| 487 | + mock.patch('socketserver.time', side_effect=clock), |
| 488 | + mock.patch('socketserver._ServerSelector') as selector_cls, |
| 489 | + mock.patch.object( |
| 490 | + server, 'handle_timeout', |
| 491 | + return_value=mock.sentinel.timeout, |
| 492 | + ) as handle_timeout, |
| 493 | + ): |
| 494 | + selector = selector_cls.return_value.__enter__.return_value |
| 495 | + selector.select.return_value = [] |
| 496 | + result = server.handle_request() |
| 497 | + self.assertIs(result, mock.sentinel.timeout) |
| 498 | + self.assertEqual(selector.select.call_args_list, |
| 499 | + [mock.call(t) for t in expected_timeouts]) |
| 500 | + handle_timeout.assert_called_once_with() |
| 501 | + |
| 502 | + def test_handle_request_ready_with_zero_timeout(self): |
| 503 | + with socketserver.TCPServer( |
| 504 | + (HOST, 0), socketserver.BaseRequestHandler |
| 505 | + ) as server: |
| 506 | + server.timeout = 0 |
| 507 | + with ( |
| 508 | + mock.patch('socketserver.time', return_value=10), |
| 509 | + mock.patch('socketserver._ServerSelector') as selector_cls, |
| 510 | + mock.patch.object( |
| 511 | + server, '_handle_request_noblock', |
| 512 | + return_value=mock.sentinel.request, |
| 513 | + ) as handle_request, |
| 514 | + mock.patch.object(server, 'handle_timeout') as handle_timeout, |
| 515 | + ): |
| 516 | + selector = selector_cls.return_value.__enter__.return_value |
| 517 | + selector.select.return_value = [mock.sentinel.ready] |
| 518 | + result = server.handle_request() |
| 519 | + self.assertIs(result, mock.sentinel.request) |
| 520 | + selector.select.assert_called_once_with(0) |
| 521 | + handle_request.assert_called_once_with() |
| 522 | + handle_timeout.assert_not_called() |
| 523 | + |
470 | 524 | def test_all(self): |
471 | 525 | # objects defined in the module should be in __all__ |
472 | 526 | expected = [] |
|
0 commit comments