Fixed #27079 -- Avoided multiple setUpClass()/tearDownClass() calls in LiveServerTestCase tests.

This commit is contained in:
Jacob Walls 2021-10-16 17:15:50 -04:00 committed by Mariusz Felisiak
parent cbd9f8531d
commit def09bf412
3 changed files with 23 additions and 54 deletions

View File

@ -1533,7 +1533,11 @@ class LiveServerTestCase(TransactionTestCase):
ALLOWED_HOSTS={'append': cls.allowed_host}, ALLOWED_HOSTS={'append': cls.allowed_host},
) )
cls._live_server_modified_settings.enable() cls._live_server_modified_settings.enable()
cls.addClassCleanup(cls._live_server_modified_settings.disable)
cls._start_server_thread()
@classmethod
def _start_server_thread(cls):
connections_override = cls._make_connections_override() connections_override = cls._make_connections_override()
for conn in connections_override.values(): for conn in connections_override.values():
# Explicitly enable thread-shareability for this connection. # Explicitly enable thread-shareability for this connection.
@ -1542,13 +1546,11 @@ class LiveServerTestCase(TransactionTestCase):
cls.server_thread = cls._create_server_thread(connections_override) cls.server_thread = cls._create_server_thread(connections_override)
cls.server_thread.daemon = True cls.server_thread.daemon = True
cls.server_thread.start() cls.server_thread.start()
cls.addClassCleanup(cls._terminate_thread)
# Wait for the live server to be ready # Wait for the live server to be ready
cls.server_thread.is_ready.wait() cls.server_thread.is_ready.wait()
if cls.server_thread.error: if cls.server_thread.error:
# Clean up behind ourselves, since tearDownClass won't get called in
# case of errors.
cls._tearDownClassInternal()
raise cls.server_thread.error raise cls.server_thread.error
@classmethod @classmethod
@ -1561,20 +1563,13 @@ class LiveServerTestCase(TransactionTestCase):
) )
@classmethod @classmethod
def _tearDownClassInternal(cls): def _terminate_thread(cls):
# Terminate the live server's thread. # Terminate the live server's thread.
cls.server_thread.terminate() cls.server_thread.terminate()
# Restore shared connections' non-shareability. # Restore shared connections' non-shareability.
for conn in cls.server_thread.connections_override.values(): for conn in cls.server_thread.connections_override.values():
conn.dec_thread_sharing() conn.dec_thread_sharing()
cls._live_server_modified_settings.disable()
super().tearDownClass()
@classmethod
def tearDownClass(cls):
cls._tearDownClassInternal()
class SerializeMixin: class SerializeMixin:
""" """

View File

@ -127,18 +127,12 @@ class LiveServerTestCaseSetupTest(LiveServerBase):
super().setUpClass() super().setUpClass()
except RuntimeError: except RuntimeError:
# LiveServerTestCase's change to ALLOWED_HOSTS should be reverted. # LiveServerTestCase's change to ALLOWED_HOSTS should be reverted.
cls.doClassCleanups()
cls.check_allowed_hosts(['testserver']) cls.check_allowed_hosts(['testserver'])
else: else:
raise RuntimeError('Server did not fail.') raise RuntimeError('Server did not fail.')
cls.set_up_called = True cls.set_up_called = True
@classmethod
def tearDownClass(cls):
# Make tearDownClass() a no-op because setUpClass() was already cleaned
# up, and because the error inside setUpClass() was handled, which will
# cause tearDownClass() to be called when it normally wouldn't.
pass
def test_set_up_class(self): def test_set_up_class(self):
self.assertIs(self.set_up_called, True) self.assertIs(self.set_up_called, True)
@ -334,7 +328,7 @@ class LiveServerPort(LiveServerBase):
""" """
TestCase = type("TestCase", (LiveServerBase,), {}) TestCase = type("TestCase", (LiveServerBase,), {})
try: try:
TestCase.setUpClass() TestCase._start_server_thread()
except OSError as e: except OSError as e:
if e.errno == errno.EADDRINUSE: if e.errno == errno.EADDRINUSE:
# We're out of ports, LiveServerTestCase correctly fails with # We're out of ports, LiveServerTestCase correctly fails with
@ -342,15 +336,12 @@ class LiveServerPort(LiveServerBase):
return return
# Unexpected error. # Unexpected error.
raise raise
try:
# We've acquired a port, ensure our server threads acquired
# different addresses.
self.assertNotEqual( self.assertNotEqual(
self.live_server_url, TestCase.live_server_url, self.live_server_url,
"Acquired duplicate server addresses for server threads: %s" % self.live_server_url TestCase.live_server_url,
f'Acquired duplicate server addresses for server threads: '
f'{self.live_server_url}',
) )
finally:
TestCase.tearDownClass()
def test_specified_port_bind(self): def test_specified_port_bind(self):
"""LiveServerTestCase.port customizes the server's port.""" """LiveServerTestCase.port customizes the server's port."""
@ -360,14 +351,13 @@ class LiveServerPort(LiveServerBase):
s.bind(('', 0)) s.bind(('', 0))
TestCase.port = s.getsockname()[1] TestCase.port = s.getsockname()[1]
s.close() s.close()
TestCase.setUpClass() TestCase._start_server_thread()
try:
self.assertEqual( self.assertEqual(
TestCase.port, TestCase.server_thread.port, TestCase.port,
'Did not use specified port for LiveServerTestCase thread: %s' % TestCase.port TestCase.server_thread.port,
f'Did not use specified port for LiveServerTestCase thread: '
f'{TestCase.port}',
) )
finally:
TestCase.tearDownClass()
class LiveServerThreadedTests(LiveServerBase): class LiveServerThreadedTests(LiveServerBase):

View File

@ -29,22 +29,8 @@ class LiveServerBase(StaticLiveServerTestCase):
# Override settings # Override settings
cls.settings_override = override_settings(**TEST_SETTINGS) cls.settings_override = override_settings(**TEST_SETTINGS)
cls.settings_override.enable() cls.settings_override.enable()
try: cls.addClassCleanup(cls.settings_override.disable)
super().setUpClass() super().setUpClass()
except Exception:
# Clean up since tearDownClass() isn't called on errors.
cls._tearDownLiveServerBase()
raise
@classmethod
def _tearDownLiveServerBase(cls):
# Restore original settings
cls.settings_override.disable()
@classmethod
def tearDownClass(cls):
super().tearDownClass()
cls._tearDownLiveServerBase()
class StaticLiveServerChecks(LiveServerBase): class StaticLiveServerChecks(LiveServerBase):
@ -74,8 +60,6 @@ class StaticLiveServerChecks(LiveServerBase):
# app without having set the required STATIC_URL setting.") # app without having set the required STATIC_URL setting.")
pass pass
else: else:
# super().setUpClass() cleans up after itself on a failure.
super().tearDownClass()
raise Exception('setUpClass() should have raised an exception.') raise Exception('setUpClass() should have raised an exception.')
def test_test_test(self): def test_test_test(self):