Skip to content

Commit 8db899e

Browse files
committed
tests: fix flaky TestTwistedConnection.test_connection_initialization
Patch reactor.running to False in setUp() so that maybe_start() always enters the branch that spawns the reactor thread. Without this, leaked global reactor state from prior tests can leave reactor.running as True, causing maybe_start() to skip thread creation and the reactor.run mock to never be called — making the assertion in test_connection_initialization fail intermittently. Observed in CI on PyPy 3.11 + macOS x86 (Rosetta 2), where timing differences make the reactor state leak more likely.
1 parent 153c913 commit 8db899e

1 file changed

Lines changed: 47 additions & 24 deletions

File tree

tests/unit/io/test_twistedreactor.py

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
from tests.unit.io.utils import TimerTestMixin
3232

33+
3334
class TestTwistedTimer(TimerTestMixin, unittest.TestCase):
3435
"""
3536
Simple test class that is used to validate that the TimerManager, and timer
@@ -54,7 +55,6 @@ def setUp(self):
5455

5556

5657
class TestTwistedProtocol(unittest.TestCase):
57-
5858
def setUp(self):
5959
if twistedreactor is None:
6060
raise unittest.SkipTest("Twisted libraries not available")
@@ -84,7 +84,7 @@ def test_receiving_data(self):
8484
the connection object's buffer and calls handle_read().
8585
"""
8686
self.obj_ut.makeConnection(self.tr)
87-
self.obj_ut.dataReceived('foobar')
87+
self.obj_ut.dataReceived("foobar")
8888
assert self.mock_connection.handle_read.called
8989
self.mock_connection._iobuf.write.assert_called_with("foobar")
9090

@@ -96,17 +96,27 @@ def setUp(self):
9696
if twistedreactor.TwistedConnection._loop:
9797
twistedreactor.TwistedConnection._loop._cleanup()
9898
twistedreactor.TwistedConnection.initialize_reactor()
99-
self.reactor_cft_patcher = patch(
100-
'twisted.internet.reactor.callFromThread')
101-
self.reactor_run_patcher = patch('twisted.internet.reactor.run')
99+
self.reactor_cft_patcher = patch("twisted.internet.reactor.callFromThread")
100+
self.reactor_run_patcher = patch("twisted.internet.reactor.run")
101+
# Patch reactor.running to False so maybe_start() always enters
102+
# the branch that spawns the reactor thread. Without this, leaked
103+
# reactor state from prior tests can cause reactor.running to be
104+
# True, making maybe_start() a no-op and the reactor.run mock
105+
# never called — leading to a flaky test_connection_initialization.
106+
self.reactor_running_patcher = patch(
107+
"twisted.internet.reactor.running", new=False
108+
)
102109
self.mock_reactor_cft = self.reactor_cft_patcher.start()
103110
self.mock_reactor_run = self.reactor_run_patcher.start()
104-
self.obj_ut = twistedreactor.TwistedConnection(DefaultEndPoint('1.2.3.4'),
105-
cql_version='3.0.1')
111+
self.reactor_running_patcher.start()
112+
self.obj_ut = twistedreactor.TwistedConnection(
113+
DefaultEndPoint("1.2.3.4"), cql_version="3.0.1"
114+
)
106115

107116
def tearDown(self):
108117
self.reactor_cft_patcher.stop()
109118
self.reactor_run_patcher.stop()
119+
self.reactor_running_patcher.stop()
110120

111121
def test_connection_initialization(self):
112122
"""
@@ -124,7 +134,7 @@ def test_client_connection_made(self):
124134
self.obj_ut.client_connection_made(Mock())
125135
self.obj_ut._send_options_message.assert_called_with()
126136

127-
@patch('twisted.internet.reactor.connectTCP')
137+
@patch("twisted.internet.reactor.connectTCP")
128138
def test_close(self, mock_connectTCP):
129139
"""
130140
Verify that close() disconnects the connector and errors callbacks.
@@ -144,16 +154,22 @@ def test_handle_read__incomplete(self):
144154
Verify that handle_read() processes incomplete messages properly.
145155
"""
146156
self.obj_ut.process_msg = Mock()
147-
assert self.obj_ut._iobuf.getvalue() == b'' # buf starts empty
157+
assert self.obj_ut._iobuf.getvalue() == b"" # buf starts empty
148158
# incomplete header
149-
self.obj_ut._iobuf.write(b'\x84\x00\x00\x00\x00')
159+
self.obj_ut._iobuf.write(b"\x84\x00\x00\x00\x00")
150160
self.obj_ut.handle_read()
151-
assert self.obj_ut._io_buffer.cql_frame_buffer.getvalue() == b'\x84\x00\x00\x00\x00'
161+
assert (
162+
self.obj_ut._io_buffer.cql_frame_buffer.getvalue()
163+
== b"\x84\x00\x00\x00\x00"
164+
)
152165

153166
# full header, but incomplete body
154-
self.obj_ut._iobuf.write(b'\x00\x00\x00\x15')
167+
self.obj_ut._iobuf.write(b"\x00\x00\x00\x15")
155168
self.obj_ut.handle_read()
156-
assert self.obj_ut._io_buffer.cql_frame_buffer.getvalue() == b'\x84\x00\x00\x00\x00\x00\x00\x00\x15'
169+
assert (
170+
self.obj_ut._io_buffer.cql_frame_buffer.getvalue()
171+
== b"\x84\x00\x00\x00\x00\x00\x00\x00\x15"
172+
)
157173
assert self.obj_ut._current_frame.end_pos == 30
158174

159175
# verify we never attempted to process the incomplete message
@@ -164,27 +180,34 @@ def test_handle_read__fullmessage(self):
164180
Verify that handle_read() processes complete messages properly.
165181
"""
166182
self.obj_ut.process_msg = Mock()
167-
assert self.obj_ut._iobuf.getvalue() == b'' # buf starts empty
183+
assert self.obj_ut._iobuf.getvalue() == b"" # buf starts empty
168184

169185
# write a complete message, plus 'NEXT' (to simulate next message)
170186
# assumes protocol v3+ as default Connection.protocol_version
171-
body = b'this is the drum roll'
172-
extra = b'NEXT'
173-
self.obj_ut._iobuf.write(
174-
b'\x84\x01\x00\x02\x03\x00\x00\x00\x15' + body + extra)
187+
body = b"this is the drum roll"
188+
extra = b"NEXT"
189+
self.obj_ut._iobuf.write(b"\x84\x01\x00\x02\x03\x00\x00\x00\x15" + body + extra)
175190
self.obj_ut.handle_read()
176191
assert self.obj_ut._io_buffer.cql_frame_buffer.getvalue() == extra
177192
self.obj_ut.process_msg.assert_called_with(
178-
_Frame(version=4, flags=1, stream=2, opcode=3, body_offset=9, end_pos=9 + len(body)), body)
179-
180-
@patch('twisted.internet.reactor.connectTCP')
193+
_Frame(
194+
version=4,
195+
flags=1,
196+
stream=2,
197+
opcode=3,
198+
body_offset=9,
199+
end_pos=9 + len(body),
200+
),
201+
body,
202+
)
203+
204+
@patch("twisted.internet.reactor.connectTCP")
181205
def test_push(self, mock_connectTCP):
182206
"""
183207
Verifiy that push() calls transport.write(data).
184208
"""
185209
self.obj_ut.add_connection()
186210
transport_mock = Mock()
187211
self.obj_ut.transport = transport_mock
188-
self.obj_ut.push('123 pickup')
189-
self.mock_reactor_cft.assert_called_with(
190-
transport_mock.write, '123 pickup')
212+
self.obj_ut.push("123 pickup")
213+
self.mock_reactor_cft.assert_called_with(transport_mock.write, "123 pickup")

0 commit comments

Comments
 (0)