Skip to content

Commit 5adb3ec

Browse files
authored
Merge pull request #3330 from zsinskri/bpd-sporadic-failure
fix "Sporadic test failures in BPD tests #3309"
2 parents f835cca + 5e5cb3c commit 5adb3ec

File tree

2 files changed

+53
-38
lines changed

2 files changed

+53
-38
lines changed

Diff for: docs/changelog.rst

+3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ For plugin developers:
8080
value.
8181
Thanks to :user:`zsinskri`.
8282
:bug:`3329`
83+
* There were sporadic failures in ``test.test_player``. Hopefully these are
84+
fixed. If they resurface, please reopen the relevant issue.
85+
:bug:`3309` :bug:`3330`
8386

8487
For packagers:
8588

Diff for: test/test_player.py

+50-38
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@
2929
import yaml
3030
import tempfile
3131
from contextlib import contextmanager
32-
import random
3332

34-
from beets.util import py3_path
33+
from beets.util import py3_path, bluelet
3534
from beetsplug import bpd
3635
import confuse
3736

@@ -231,11 +230,6 @@ def readline(self, terminator=b'\n', bufsize=1024):
231230
return line
232231

233232

234-
def start_beets(*args):
235-
import beets.ui
236-
beets.ui.main(list(args))
237-
238-
239233
def implements(commands, expectedFailure=False): # noqa: N803
240234
def _test(self):
241235
with self.run_bpd() as client:
@@ -246,6 +240,27 @@ def _test(self):
246240
return unittest.expectedFailure(_test) if expectedFailure else _test
247241

248242

243+
bluelet_listener = bluelet.Listener
244+
@mock.patch("beets.util.bluelet.Listener")
245+
def start_server(args, assigned_port, listener_patch):
246+
"""Start the bpd server, writing the port to `assigned_port`.
247+
"""
248+
def listener_wrap(host, port):
249+
"""Wrap `bluelet.Listener`, writing the port to `assigend_port`.
250+
"""
251+
# `bluelet.Listener` has previously been saved to
252+
# `bluelet_listener` as this function will replace it at its
253+
# original location.
254+
listener = bluelet_listener(host, port)
255+
# read port assigned by OS
256+
assigned_port.put_nowait(listener.sock.getsockname()[1])
257+
return listener
258+
listener_patch.side_effect = listener_wrap
259+
260+
import beets.ui
261+
beets.ui.main(args)
262+
263+
249264
class BPDTestHelper(unittest.TestCase, TestHelper):
250265
def setUp(self):
251266
self.setup_beets(disk=True)
@@ -263,22 +278,18 @@ def tearDown(self):
263278
self.unload_plugins()
264279

265280
@contextmanager
266-
def run_bpd(self, host='localhost', port=None, password=None,
267-
do_hello=True, second_client=False):
281+
def run_bpd(self, host='localhost', password=None, do_hello=True,
282+
second_client=False):
268283
""" Runs BPD in another process, configured with the same library
269284
database as we created in the setUp method. Exposes a client that is
270285
connected to the server, and kills the server at the end.
271286
"""
272-
# Choose a port (randomly) to avoid conflicts between parallel
273-
# tests.
274-
if not port:
275-
port = 9876 + random.randint(0, 10000)
276-
277287
# Create a config file:
278288
config = {
279289
'pluginpath': [py3_path(self.temp_dir)],
280290
'plugins': 'bpd',
281-
'bpd': {'host': host, 'port': port, 'control_port': port + 1},
291+
# use port 0 to let the OS choose a free port
292+
'bpd': {'host': host, 'port': 0, 'control_port': 0},
282293
}
283294
if password:
284295
config['bpd']['password'] = password
@@ -290,38 +301,39 @@ def run_bpd(self, host='localhost', port=None, password=None,
290301
config_file.close()
291302

292303
# Fork and launch BPD in the new process:
293-
args = (
304+
assigned_port = mp.Queue(2) # 2 slots, `control_port` and `port`
305+
server = mp.Process(target=start_server, args=([
294306
'--library', self.config['library'].as_filename(),
295307
'--directory', py3_path(self.libdir),
296308
'--config', py3_path(config_file.name),
297309
'bpd'
298-
)
299-
server = mp.Process(target=start_beets, args=args)
310+
], assigned_port))
300311
server.start()
301312

302-
# Wait until the socket is connected:
303-
sock, sock2 = None, None
304-
for _ in range(20):
313+
try:
314+
assigned_port.get(timeout=1) # skip control_port
315+
port = assigned_port.get(timeout=0.5) # read port
316+
305317
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
306-
if sock.connect_ex((host, port)) == 0:
307-
break
308-
else:
309-
sock.close()
310-
time.sleep(0.01)
311-
else:
312-
raise RuntimeError('Timed out waiting for the BPD server')
318+
try:
319+
sock.connect((host, port))
320+
321+
if second_client:
322+
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
323+
try:
324+
sock2.connect((host, port))
325+
yield (
326+
MPCClient(sock, do_hello),
327+
MPCClient(sock2, do_hello),
328+
)
329+
finally:
330+
sock2.close()
313331

314-
try:
315-
if second_client:
316-
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
317-
sock2.connect((host, port))
318-
yield MPCClient(sock, do_hello), MPCClient(sock2, do_hello)
319-
else:
320-
yield MPCClient(sock, do_hello)
332+
else:
333+
yield MPCClient(sock, do_hello)
334+
finally:
335+
sock.close()
321336
finally:
322-
sock.close()
323-
if sock2:
324-
sock2.close()
325337
server.terminate()
326338
server.join(timeout=0.2)
327339

0 commit comments

Comments
 (0)