core: Update pending track handling and fix consume / gapless issue

- Pending track should only be triggered by stream_changed if there is one.
- Tracklist changed was incorrectly calling stop breaking tests and gapless
- Tests have been updated to capture and replay audio events. This should avoid
  test deadlocks while still using the audio fakes.
This commit is contained in:
Thomas Adamcik 2015-03-16 23:50:08 +01:00
parent 9488973592
commit 65f87e89f1
3 changed files with 62 additions and 40 deletions

View File

@ -138,10 +138,10 @@ class PlaybackController(object):
# TODO: self._trigger_track_playback_ended?
def on_stream_changed(self, uri):
self.current_tl_track = self._pending_tl_track
self._pending_tl_track = None
self._trigger_track_playback_started()
# TODO: self._trigger_track_playback_ended?
if self._pending_tl_track:
self.current_tl_track = self._pending_tl_track
self._pending_tl_track = None
self._trigger_track_playback_started()
def on_about_to_finish(self):
# TODO: check that we always have a current track
@ -163,9 +163,12 @@ class PlaybackController(object):
Used by :class:`mopidy.core.TracklistController`.
"""
if self.current_tl_track not in self.core.tracklist.tl_tracks:
if not self.core.tracklist.tl_tracks:
self.stop()
self.current_tl_track = None
elif self.current_tl_track not in self.core.tracklist.tl_tracks:
self.current_tl_track = None
def next(self):
"""

View File

@ -30,21 +30,28 @@ class TestCurrentAndPendingTlTrack(unittest.TestCase):
self.core.tracklist.add(self.tracks)
self.events = []
self.patcher = mock.patch('mopidy.audio.listener.AudioListener.send')
self.send_mock = self.patcher.start()
def send(event, **kwargs):
self.events.append((event, kwargs))
self.send_mock.side_effect = send
def tearDown(self): # noqa: N802
pykka.ActorRegistry.stop_all()
self.patcher.stop()
def trigger_about_to_finish(self):
self.audio.prepare_change()
# TODO: trigger via dummy audio?
self.playback.on_about_to_finish()
def trigger_about_to_finish(self, block_stream_changed=False):
callback = self.audio.get_about_to_finish_callback().get()
callback()
def trigger_stream_changed(self):
# TODO: trigger via dummy audio?
self.playback.on_stream_changed(None)
def trigger_end_of_stream(self):
# TODO: trigger via dummy audio?
self.playback.on_end_of_stream()
while self.events:
event, kwargs = self.events.pop(0)
if event == 'stream_changed' and block_stream_changed:
continue
self.core.on_event(event, **kwargs)
def test_pending_tl_track_is_none(self):
self.core.playback.play()
@ -52,31 +59,28 @@ class TestCurrentAndPendingTlTrack(unittest.TestCase):
def test_pending_tl_track_after_about_to_finish(self):
self.core.playback.play()
self.trigger_about_to_finish()
self.trigger_about_to_finish(block_stream_changed=True)
self.assertEqual(self.playback._pending_tl_track.track.uri, 'dummy:b')
def test_pending_tl_track_after_stream_changed(self):
self.trigger_about_to_finish()
self.trigger_stream_changed()
self.assertEqual(self.playback._pending_tl_track, None)
def test_current_tl_track_after_about_to_finish(self):
self.core.playback.play()
self.trigger_about_to_finish()
self.trigger_about_to_finish(block_stream_changed=True)
self.assertEqual(self.playback.current_tl_track.track.uri, 'dummy:a')
def test_current_tl_track_after_stream_changed(self):
self.core.playback.play()
self.trigger_about_to_finish()
self.trigger_stream_changed()
self.assertEqual(self.playback.current_tl_track.track.uri, 'dummy:b')
def test_current_tl_track_after_end_of_stream(self):
self.core.playback.play()
self.trigger_about_to_finish()
self.trigger_stream_changed()
self.trigger_about_to_finish()
self.trigger_end_of_stream()
self.trigger_about_to_finish() # EOS
self.assertEqual(self.playback.current_tl_track, None)

View File

@ -1,5 +1,6 @@
from __future__ import absolute_import, unicode_literals
import logging
import time
import unittest
@ -15,6 +16,7 @@ from mopidy.models import Track
from tests import path_to_data_dir
from tests.local import generate_song, populate_tracklist
logger = logging.getLogger(__name__)
# TODO Test 'playlist repeat', e.g. repeat=1,single=0
@ -40,18 +42,19 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.tracklist.add([track])
def trigger_about_to_finish(self):
self.audio.prepare_change().get()
self.playback.on_about_to_finish()
self.playback.on_stream_changed(None)
callback = self.audio.get_about_to_finish_callback().get()
callback()
def trigger_end_of_stream(self):
self.playback.on_end_of_stream()
while self.events:
event, kwargs = self.events.pop(0)
logger.debug('Replaying: %s %s', event, kwargs)
self.core.on_event(event, **kwargs)
def setUp(self): # noqa: N802
self.audio = audio.DummyAudio.start().proxy()
self.backend = actor.LocalBackend.start(
config=self.config, audio=self.audio).proxy()
self.core = core.Core(backends=[self.backend])
self.core = core.Core(backends=[self.backend], audio=self.audio)
self.playback = self.core.playback
self.tracklist = self.core.tracklist
@ -60,8 +63,18 @@ class LocalPlaybackProviderTest(unittest.TestCase):
assert self.tracks[0].length >= 2000, \
'First song needs to be at least 2000 miliseconds'
self.events = []
self.patcher = mock.patch('mopidy.audio.listener.AudioListener.send')
self.send_mock = self.patcher.start()
def send(event, **kwargs):
self.events.append((event, kwargs))
self.send_mock.side_effect = send
def tearDown(self): # noqa: N802
pykka.ActorRegistry.stop_all()
self.patcher.stop()
def test_uri_scheme(self):
self.assertNotIn('file', self.core.uri_schemes)
@ -172,7 +185,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
def test_current_track_after_completed_playlist(self):
self.playback.play(self.tracklist.tl_tracks[-1])
self.trigger_about_to_finish()
self.trigger_end_of_stream()
# EOS should have triggered
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
self.assertEqual(self.playback.current_track, None)
@ -435,7 +448,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.assertEqual(self.tracklist.index(tl_track), i)
self.trigger_about_to_finish()
self.trigger_end_of_stream()
# EOS should have triggered
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@ -444,7 +457,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.playback.play()
for _ in self.tracks:
self.trigger_about_to_finish()
self.trigger_end_of_stream()
# EOS should have triggered
self.assertEqual(self.playback.current_track, None)
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@ -517,7 +530,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.tracklist.next_track(tl_track), self.tl_tracks[0])
@populate_tracklist
def test_on_about_to_finis_with_consume(self):
def test_on_about_to_finish_with_consume(self):
self.tracklist.consume = True
self.playback.play()
self.trigger_about_to_finish()
@ -651,7 +664,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
def test_tracklist_position_at_end_of_playlist(self):
self.playback.play(self.tracklist.tl_tracks[-1])
self.trigger_about_to_finish()
self.trigger_end_of_stream()
# EOS should have triggered
tl_track = self.playback.current_tl_track
self.assertEqual(self.tracklist.index(tl_track), None)
@ -919,9 +932,10 @@ class LocalPlaybackProviderTest(unittest.TestCase):
def test_playlist_is_empty_after_all_tracks_are_played_with_consume(self):
self.tracklist.consume = True
self.playback.play()
for _ in self.tracks:
for t in self.tracks:
self.trigger_about_to_finish()
self.trigger_end_of_stream()
# EOS should have trigger
self.assertEqual(len(self.tracklist.tracks), 0)
@ -978,7 +992,7 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.assertEqual(self.playback.current_track, self.tracks[0])
self.trigger_about_to_finish()
self.assertEqual(self.playback.current_track, None)
self.trigger_end_of_stream()
# EOS should have triggered
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_tracklist
@ -988,14 +1002,14 @@ class LocalPlaybackProviderTest(unittest.TestCase):
self.playback.play()
self.trigger_about_to_finish()
self.assertEqual(self.playback.current_track, None)
self.trigger_end_of_stream()
# EOS should have triggered
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
@populate_tracklist
def test_end_of_playlist_stops(self):
self.playback.play(self.tracklist.tl_tracks[-1])
self.trigger_about_to_finish()
self.trigger_end_of_stream()
# EOS should have triggered
self.assertEqual(self.playback.state, PlaybackState.STOPPED)
def test_repeat_off_by_default(self):
@ -1043,9 +1057,10 @@ class LocalPlaybackProviderTest(unittest.TestCase):
def test_random_with_eot_until_end_of_playlist_and_play_from_start(self):
self.tracklist.random = True
self.playback.play()
for _ in self.tracks:
self.trigger_about_to_finish()
self.trigger_end_of_stream()
# EOS should have triggered
tl_track = self.playback.current_tl_track
self.assertNotEqual(self.tracklist.eot_track(tl_track), None)