New CoreState to hold all core states
- Introduce a CoreState class that holds all core states - Move xState classes to internal - Use validation.check_instance for consistent error messages - Store tlid instead of TlTrack to restore last played track
This commit is contained in:
parent
74344f2b19
commit
4869619bb9
@ -18,9 +18,9 @@ from mopidy.core.mixer import MixerController
|
||||
from mopidy.core.playback import PlaybackController
|
||||
from mopidy.core.playlists import PlaylistsController
|
||||
from mopidy.core.tracklist import TracklistController
|
||||
from mopidy.internal import versioning
|
||||
from mopidy.internal import storage, validation, versioning
|
||||
from mopidy.internal.deprecation import deprecated_property
|
||||
from mopidy.models import storage
|
||||
from mopidy.internal.models import CoreState
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -163,7 +163,7 @@ class Core(
|
||||
if len(coverage):
|
||||
self.load_state('persistent', coverage)
|
||||
except Exception as e:
|
||||
logger.warn('setup: Unexpected error: %s', str(e))
|
||||
logger.warn('Restore state: Unexpected error: %s', str(e))
|
||||
|
||||
def teardown(self):
|
||||
try:
|
||||
@ -172,7 +172,7 @@ class Core(
|
||||
if amount and 'off' != amount:
|
||||
self.save_state('persistent')
|
||||
except Exception as e:
|
||||
logger.warn('teardown: Unexpected error: %s', str(e))
|
||||
logger.warn('Export state: Unexpected error: %s', str(e))
|
||||
|
||||
def save_state(self, name):
|
||||
"""
|
||||
@ -191,12 +191,13 @@ class Core(
|
||||
|
||||
data = {}
|
||||
data['version'] = mopidy.__version__
|
||||
data['tracklist'] = self.tracklist._export_state()
|
||||
data['history'] = self.history._export_state()
|
||||
data['playback'] = self.playback._export_state()
|
||||
data['mixer'] = self.mixer._export_state()
|
||||
data['state'] = CoreState(
|
||||
tracklist=self.tracklist._export_state(),
|
||||
history=self.history._export_state(),
|
||||
playback=self.playback._export_state(),
|
||||
mixer=self.mixer._export_state())
|
||||
storage.save(file_name, data)
|
||||
logger.debug('Save state done')
|
||||
logger.debug('Save state done.')
|
||||
|
||||
def load_state(self, name, coverage):
|
||||
"""
|
||||
@ -226,15 +227,14 @@ class Core(
|
||||
logger.info('Load state from %s', file_name)
|
||||
|
||||
data = storage.load(file_name)
|
||||
if 'history' in data:
|
||||
self.history._restore_state(data['history'], coverage)
|
||||
if 'tracklist' in data:
|
||||
self.tracklist._restore_state(data['tracklist'], coverage)
|
||||
if 'playback' in data:
|
||||
if 'state' in data:
|
||||
core_state = data['state']
|
||||
validation.check_instance(core_state, CoreState)
|
||||
self.history._restore_state(core_state.history, coverage)
|
||||
self.tracklist._restore_state(core_state.tracklist, coverage)
|
||||
# playback after tracklist
|
||||
self.playback._restore_state(data['playback'], coverage)
|
||||
if 'mixer' in data:
|
||||
self.mixer._restore_state(data['mixer'], coverage)
|
||||
self.playback._restore_state(core_state.playback, coverage)
|
||||
self.mixer._restore_state(core_state.mixer, coverage)
|
||||
logger.debug('Load state done.')
|
||||
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import logging
|
||||
import time
|
||||
|
||||
from mopidy import models
|
||||
|
||||
from mopidy.internal.models import HistoryState, HistoryTrack
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -62,15 +62,13 @@ class HistoryController(object):
|
||||
"""Internal method for :class:`mopidy.Core`."""
|
||||
history_list = []
|
||||
for timestamp, track in self._history:
|
||||
history_list.append(models.HistoryTrack(
|
||||
history_list.append(HistoryTrack(
|
||||
timestamp=timestamp, track=track))
|
||||
return models.HistoryState(history=history_list)
|
||||
return HistoryState(history=history_list)
|
||||
|
||||
def _restore_state(self, state, coverage):
|
||||
"""Internal method for :class:`mopidy.Core`."""
|
||||
if state:
|
||||
if not isinstance(state, models.HistoryState):
|
||||
raise TypeError('Expect an argument of type "HistoryState"')
|
||||
if 'history' in coverage:
|
||||
self._history = []
|
||||
for htrack in state.history:
|
||||
|
||||
@ -5,7 +5,7 @@ import logging
|
||||
|
||||
from mopidy import exceptions
|
||||
from mopidy.internal import validation
|
||||
from mopidy.models import MixerState
|
||||
from mopidy.internal.models import MixerState
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -108,8 +108,6 @@ class MixerController(object):
|
||||
def _restore_state(self, state, coverage):
|
||||
"""Internal method for :class:`mopidy.Core`."""
|
||||
if state:
|
||||
if not isinstance(state, MixerState):
|
||||
raise TypeError('Expect an argument of type "MixerState"')
|
||||
if 'volume' in coverage:
|
||||
if state.volume:
|
||||
self.set_volume(state.volume)
|
||||
|
||||
@ -2,11 +2,10 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from mopidy import models
|
||||
from mopidy.audio import PlaybackState
|
||||
from mopidy.compat import urllib
|
||||
from mopidy.core import listener
|
||||
from mopidy.internal import deprecation, validation
|
||||
from mopidy.internal import deprecation, models, validation
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -552,14 +551,13 @@ class PlaybackController(object):
|
||||
def _restore_state(self, state, coverage):
|
||||
"""Internal method for :class:`mopidy.Core`."""
|
||||
if state:
|
||||
if not isinstance(state, models.PlaybackState):
|
||||
raise TypeError('Expect an argument of type "PlaybackState"')
|
||||
new_state = None
|
||||
if 'play-always' in coverage:
|
||||
new_state = PlaybackState.PLAYING
|
||||
if 'play-last' in coverage:
|
||||
new_state = state.state
|
||||
if state.tlid is not None:
|
||||
# TODO: restore to 'paused' state
|
||||
if PlaybackState.PLAYING == new_state:
|
||||
self.play(tlid=state.tlid)
|
||||
# TODO: seek to state.position?
|
||||
|
||||
@ -6,7 +6,8 @@ import random
|
||||
from mopidy import exceptions
|
||||
from mopidy.core import listener
|
||||
from mopidy.internal import deprecation, validation
|
||||
from mopidy.models import TlTrack, Track, TracklistState
|
||||
from mopidy.internal.models import TracklistState
|
||||
from mopidy.models import TlTrack, Track
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -658,8 +659,6 @@ class TracklistController(object):
|
||||
def _restore_state(self, state, coverage):
|
||||
"""Internal method for :class:`mopidy.Core`."""
|
||||
if state:
|
||||
if not isinstance(state, TracklistState):
|
||||
raise TypeError('Expect an argument of type "TracklistState"')
|
||||
if 'mode' in coverage:
|
||||
self.set_consume(state.consume)
|
||||
self.set_random(state.random)
|
||||
@ -670,5 +669,9 @@ class TracklistController(object):
|
||||
self._next_tlid = state.next_tlid
|
||||
self._tl_tracks = []
|
||||
for track in state.tl_tracks:
|
||||
# TODO: check if any backend will play the track.
|
||||
# Could be an issue with music streaming services
|
||||
# (login), disabled extensions and automatically
|
||||
# generated playlists (pandora).
|
||||
self._tl_tracks.append(track)
|
||||
self._trigger_tracklist_changed()
|
||||
|
||||
142
mopidy/internal/models.py
Normal file
142
mopidy/internal/models.py
Normal file
@ -0,0 +1,142 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from mopidy.internal import validation
|
||||
from mopidy.models import Ref, TlTrack, fields
|
||||
from mopidy.models.immutable import ValidatedImmutableObject
|
||||
|
||||
_MODELS = ['HistoryTrack', 'HistoryState', 'MixerState', 'PlaybackState',
|
||||
'TracklistState', 'CoreState']
|
||||
|
||||
|
||||
class HistoryTrack(ValidatedImmutableObject):
|
||||
"""
|
||||
A history track. Wraps a :class:`Ref` and it's timestamp.
|
||||
|
||||
:param timestamp: the timestamp
|
||||
:type timestamp: int
|
||||
:param track: the track reference
|
||||
:type track: :class:`Ref`
|
||||
"""
|
||||
|
||||
# The timestamp. Read-only.
|
||||
timestamp = fields.Integer()
|
||||
|
||||
# The track reference. Read-only.
|
||||
track = fields.Field(type=Ref)
|
||||
|
||||
|
||||
class HistoryState(ValidatedImmutableObject):
|
||||
"""
|
||||
State of the history controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param history: the track history
|
||||
:type history: list of :class:`HistoryTrack`
|
||||
"""
|
||||
|
||||
# The tracks. Read-only.
|
||||
history = fields.Collection(type=HistoryTrack, container=tuple)
|
||||
|
||||
|
||||
class MixerState(ValidatedImmutableObject):
|
||||
"""
|
||||
State of the mixer controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param volume: the volume
|
||||
:type volume: int
|
||||
"""
|
||||
|
||||
# The volume. Read-only.
|
||||
volume = fields.Integer(min=0, max=100)
|
||||
|
||||
|
||||
class PlaybackState(ValidatedImmutableObject):
|
||||
"""
|
||||
State of the playback controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param tlid: current track tlid
|
||||
:type tlid: int
|
||||
:param position: play position
|
||||
:type position: int
|
||||
:param state: playback state
|
||||
:type state: :class:`validation.PLAYBACK_STATES`
|
||||
"""
|
||||
|
||||
# The tlid of current playing track. Read-only.
|
||||
tlid = fields.Integer(min=1)
|
||||
|
||||
# The playback position. Read-only.
|
||||
position = fields.Integer(min=0)
|
||||
|
||||
# The playback state. Read-only.
|
||||
state = fields.Field(choices=validation.PLAYBACK_STATES)
|
||||
|
||||
|
||||
class TracklistState(ValidatedImmutableObject):
|
||||
|
||||
"""
|
||||
State of the tracklist controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param repeat: the repeat mode
|
||||
:type repeat: bool
|
||||
:param consume: the consume mode
|
||||
:type consume: bool
|
||||
:param random: the random mode
|
||||
:type random: bool
|
||||
:param single: the single mode
|
||||
:type single: bool
|
||||
:param next_tlid: the single mode
|
||||
:type next_tlid: bool
|
||||
:param tl_tracks: the single mode
|
||||
:type tl_tracks: list of :class:`TlTrack`
|
||||
"""
|
||||
|
||||
# The repeat mode. Read-only.
|
||||
repeat = fields.Boolean()
|
||||
|
||||
# The consume mode. Read-only.
|
||||
consume = fields.Boolean()
|
||||
|
||||
# The random mode. Read-only.
|
||||
random = fields.Boolean()
|
||||
|
||||
# The single mode. Read-only.
|
||||
single = fields.Boolean()
|
||||
|
||||
# The repeat mode. Read-only.
|
||||
next_tlid = fields.Integer(min=0)
|
||||
|
||||
# The list of tracks. Read-only.
|
||||
tl_tracks = fields.Collection(type=TlTrack, container=tuple)
|
||||
|
||||
|
||||
class CoreState(ValidatedImmutableObject):
|
||||
|
||||
"""
|
||||
State of all Core controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param history: State of the history controller
|
||||
:type history: :class:`HistorState`
|
||||
:param mixer: State of the mixer controller
|
||||
:type mixer: :class:`MixerState`
|
||||
:param playback: State of the playback controller
|
||||
:type playback: :class:`PlaybackState`
|
||||
:param tracklist: State of the tracklist controller
|
||||
:type tracklist: :class:`TracklistState`
|
||||
"""
|
||||
|
||||
# State of the history controller.
|
||||
history = fields.Field(type=HistoryState)
|
||||
|
||||
# State of the mixer controller.
|
||||
mixer = fields.Field(type=MixerState)
|
||||
|
||||
# State of the playback controller.
|
||||
playback = fields.Field(type=PlaybackState)
|
||||
|
||||
# State of the tracklist controller.
|
||||
tracklist = fields.Field(type=TracklistState)
|
||||
@ -9,6 +9,7 @@ import sys
|
||||
import mopidy
|
||||
|
||||
from mopidy import compat, local, models
|
||||
from mopidy import internal
|
||||
from mopidy.internal import timer
|
||||
from mopidy.local import search, storage, translator
|
||||
|
||||
@ -98,7 +99,7 @@ class JsonLibrary(local.Library):
|
||||
self._json_file)
|
||||
self._tracks = {}
|
||||
else:
|
||||
library = models.storage.load(self._json_file)
|
||||
library = internal.storage.load(self._json_file)
|
||||
self._tracks = dict((t.uri, t) for t in
|
||||
library.get('tracks', []))
|
||||
with timer.time_logger('Building browse cache'):
|
||||
@ -166,9 +167,9 @@ class JsonLibrary(local.Library):
|
||||
self._tracks.pop(uri, None)
|
||||
|
||||
def close(self):
|
||||
models.storage.save(self._json_file,
|
||||
{'version': mopidy.__version__,
|
||||
'tracks': self._tracks.values()})
|
||||
internal.storage.save(self._json_file,
|
||||
{'version': mopidy.__version__,
|
||||
'tracks': self._tracks.values()})
|
||||
|
||||
def clear(self):
|
||||
try:
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from mopidy import compat
|
||||
from mopidy.internal import validation
|
||||
from mopidy.models import fields
|
||||
from mopidy.models.immutable import ImmutableObject, ValidatedImmutableObject
|
||||
from mopidy.models.serialize import ModelJSONEncoder, model_json_decoder
|
||||
@ -9,8 +8,7 @@ from mopidy.models.serialize import ModelJSONEncoder, model_json_decoder
|
||||
__all__ = [
|
||||
'ImmutableObject', 'Ref', 'Image', 'Artist', 'Album', 'track', 'TlTrack',
|
||||
'Playlist', 'SearchResult', 'model_json_decoder', 'ModelJSONEncoder',
|
||||
'ValidatedImmutableObject', 'HistoryTrack', 'HistoryState', 'MixerState',
|
||||
'PlaybackState', 'TracklistState']
|
||||
'ValidatedImmutableObject']
|
||||
|
||||
|
||||
class Ref(ValidatedImmutableObject):
|
||||
@ -366,108 +364,3 @@ class SearchResult(ValidatedImmutableObject):
|
||||
|
||||
# The albums matching the search query. Read-only.
|
||||
albums = fields.Collection(type=Album, container=tuple)
|
||||
|
||||
|
||||
class HistoryTrack(ValidatedImmutableObject):
|
||||
"""
|
||||
A history track. Wraps a :class:`Ref` and it's timestamp.
|
||||
|
||||
:param timestamp: the timestamp
|
||||
:type timestamp: int
|
||||
:param track: the track reference
|
||||
:type track: :class:`Ref`
|
||||
"""
|
||||
|
||||
# The timestamp. Read-only.
|
||||
timestamp = fields.Integer()
|
||||
|
||||
# The track reference. Read-only.
|
||||
track = fields.Field(type=Ref)
|
||||
|
||||
|
||||
class HistoryState(ValidatedImmutableObject):
|
||||
"""
|
||||
State of the history controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param history: the track history
|
||||
:type history: list of :class:`HistoryTrack`
|
||||
"""
|
||||
|
||||
# The tracks. Read-only.
|
||||
history = fields.Collection(type=HistoryTrack, container=tuple)
|
||||
|
||||
|
||||
class MixerState(ValidatedImmutableObject):
|
||||
"""
|
||||
State of the mixer controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param volume: the volume
|
||||
:type volume: int
|
||||
"""
|
||||
|
||||
# The volume. Read-only.
|
||||
volume = fields.Integer(min=0, max=100)
|
||||
|
||||
|
||||
class PlaybackState(ValidatedImmutableObject):
|
||||
"""
|
||||
State of the playback controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param tlid: current track tlid
|
||||
:type tlid: int
|
||||
:param position: play position
|
||||
:type position: int
|
||||
:param state: playback state
|
||||
:type state: :class:`validation.PLAYBACK_STATES`
|
||||
"""
|
||||
|
||||
# The tlid of current playing track. Read-only.
|
||||
tlid = fields.Integer(min=1)
|
||||
|
||||
# The playback position. Read-only.
|
||||
position = fields.Integer(min=0)
|
||||
|
||||
# The playback state. Read-only.
|
||||
state = fields.Field(choices=validation.PLAYBACK_STATES)
|
||||
|
||||
|
||||
class TracklistState(ValidatedImmutableObject):
|
||||
|
||||
"""
|
||||
State of the tracklist controller.
|
||||
Internally used for import/export of current state.
|
||||
|
||||
:param repeat: the repeat mode
|
||||
:type repeat: bool
|
||||
:param consume: the consume mode
|
||||
:type consume: bool
|
||||
:param random: the random mode
|
||||
:type random: bool
|
||||
:param single: the single mode
|
||||
:type single: bool
|
||||
:param next_tlid: the single mode
|
||||
:type next_tlid: bool
|
||||
:param tl_tracks: the single mode
|
||||
:type tl_tracks: list of :class:`TlTrack`
|
||||
"""
|
||||
|
||||
# The repeat mode. Read-only.
|
||||
repeat = fields.Boolean()
|
||||
|
||||
# The consume mode. Read-only.
|
||||
consume = fields.Boolean()
|
||||
|
||||
# The random mode. Read-only.
|
||||
random = fields.Boolean()
|
||||
|
||||
# The single mode. Read-only.
|
||||
single = fields.Boolean()
|
||||
|
||||
# The repeat mode. Read-only.
|
||||
next_tlid = fields.Integer(min=0)
|
||||
|
||||
# The list of tracks. Read-only.
|
||||
tl_tracks = fields.Collection(type=TlTrack, container=tuple)
|
||||
|
||||
@ -4,9 +4,7 @@ import json
|
||||
|
||||
from mopidy.models import immutable
|
||||
|
||||
_MODELS = ['Ref', 'Artist', 'Album', 'Track', 'TlTrack', 'Playlist',
|
||||
'HistoryTrack', 'HistoryState', 'MixerState', 'PlaybackState',
|
||||
'TracklistState']
|
||||
_MODELS = ['Ref', 'Artist', 'Album', 'Track', 'TlTrack', 'Playlist']
|
||||
|
||||
|
||||
class ModelJSONEncoder(json.JSONEncoder):
|
||||
@ -46,4 +44,7 @@ def model_json_decoder(dct):
|
||||
model_name = dct.pop('__model__')
|
||||
if model_name in _MODELS:
|
||||
return getattr(models, model_name)(**dct)
|
||||
from mopidy import internal
|
||||
if model_name in internal.models._MODELS:
|
||||
return getattr(internal.models, model_name)(**dct)
|
||||
return dct
|
||||
|
||||
@ -4,7 +4,8 @@ import unittest
|
||||
|
||||
from mopidy import compat
|
||||
from mopidy.core import HistoryController
|
||||
from mopidy.models import Artist, HistoryState, HistoryTrack, Ref, Track
|
||||
from mopidy.internal.models import HistoryState, HistoryTrack
|
||||
from mopidy.models import Artist, Ref, Track
|
||||
|
||||
|
||||
class PlaybackHistoryTest(unittest.TestCase):
|
||||
|
||||
@ -7,7 +7,7 @@ import mock
|
||||
import pykka
|
||||
|
||||
from mopidy import core, mixer
|
||||
from mopidy.models import MixerState
|
||||
from mopidy.internal.models import MixerState
|
||||
from tests import dummy_mixer
|
||||
|
||||
|
||||
|
||||
@ -8,7 +8,8 @@ import pykka
|
||||
|
||||
from mopidy import backend, core
|
||||
from mopidy.internal import deprecation
|
||||
from mopidy.models import PlaybackState, Track
|
||||
from mopidy.internal.models import PlaybackState
|
||||
from mopidy.models import Track
|
||||
|
||||
from tests import dummy_audio
|
||||
|
||||
|
||||
@ -6,7 +6,8 @@ import mock
|
||||
|
||||
from mopidy import backend, core
|
||||
from mopidy.internal import deprecation
|
||||
from mopidy.models import TlTrack, Track, TracklistState
|
||||
from mopidy.internal.models import TracklistState
|
||||
from mopidy.models import TlTrack, Track
|
||||
|
||||
|
||||
class TracklistTest(unittest.TestCase):
|
||||
|
||||
200
tests/internal/test_models.py
Normal file
200
tests/internal/test_models.py
Normal file
@ -0,0 +1,200 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from mopidy.internal.models import (
|
||||
HistoryState, HistoryTrack, MixerState, PlaybackState, TracklistState)
|
||||
from mopidy.models import (
|
||||
ModelJSONEncoder, Ref, TlTrack, Track, model_json_decoder)
|
||||
|
||||
|
||||
class HistoryTrackTest(unittest.TestCase):
|
||||
|
||||
def test_track(self):
|
||||
track = Ref.track()
|
||||
result = HistoryTrack(track=track)
|
||||
self.assertEqual(result.track, track)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.track = None
|
||||
|
||||
def test_timestamp(self):
|
||||
timestamp = 1234
|
||||
result = HistoryTrack(timestamp=timestamp)
|
||||
self.assertEqual(result.timestamp, timestamp)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.timestamp = None
|
||||
|
||||
def test_to_json_and_back(self):
|
||||
result = HistoryTrack(track=Ref.track(), timestamp=1234)
|
||||
serialized = json.dumps(result, cls=ModelJSONEncoder)
|
||||
deserialized = json.loads(serialized, object_hook=model_json_decoder)
|
||||
self.assertEqual(result, deserialized)
|
||||
|
||||
|
||||
class HistoryStateTest(unittest.TestCase):
|
||||
|
||||
def test_history_list(self):
|
||||
history = (HistoryTrack(),
|
||||
HistoryTrack())
|
||||
result = HistoryState(history=history)
|
||||
self.assertEqual(result.history, history)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.history = None
|
||||
|
||||
def test_history_string_fail(self):
|
||||
history = 'not_a_valid_history'
|
||||
with self.assertRaises(TypeError):
|
||||
HistoryState(history=history)
|
||||
|
||||
def test_to_json_and_back(self):
|
||||
result = HistoryState(history=(HistoryTrack(), HistoryTrack()))
|
||||
serialized = json.dumps(result, cls=ModelJSONEncoder)
|
||||
deserialized = json.loads(serialized, object_hook=model_json_decoder)
|
||||
self.assertEqual(result, deserialized)
|
||||
|
||||
|
||||
class MixerStateTest(unittest.TestCase):
|
||||
|
||||
def test_volume(self):
|
||||
volume = 37
|
||||
result = MixerState(volume=volume)
|
||||
self.assertEqual(result.volume, volume)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.volume = None
|
||||
|
||||
def test_volume_invalid(self):
|
||||
volume = 105
|
||||
with self.assertRaises(ValueError):
|
||||
MixerState(volume=volume)
|
||||
|
||||
def test_to_json_and_back(self):
|
||||
result = MixerState(volume=77)
|
||||
serialized = json.dumps(result, cls=ModelJSONEncoder)
|
||||
deserialized = json.loads(serialized, object_hook=model_json_decoder)
|
||||
self.assertEqual(result, deserialized)
|
||||
|
||||
|
||||
class PlaybackStateTest(unittest.TestCase):
|
||||
|
||||
def test_position(self):
|
||||
position = 123456
|
||||
result = PlaybackState(position=position)
|
||||
self.assertEqual(result.position, position)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.position = None
|
||||
|
||||
def test_position_invalid(self):
|
||||
position = -1
|
||||
with self.assertRaises(ValueError):
|
||||
PlaybackState(position=position)
|
||||
|
||||
def test_tl_track(self):
|
||||
tlid = 42
|
||||
result = PlaybackState(tlid=tlid)
|
||||
self.assertEqual(result.tlid, tlid)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.tlid = None
|
||||
|
||||
def test_tl_track_none(self):
|
||||
tlid = None
|
||||
result = PlaybackState(tlid=tlid)
|
||||
self.assertEqual(result.tlid, tlid)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.tl_track = None
|
||||
|
||||
def test_tl_track_invalid(self):
|
||||
tl_track = Track()
|
||||
with self.assertRaises(TypeError):
|
||||
PlaybackState(tlid=tl_track)
|
||||
|
||||
def test_state(self):
|
||||
state = 'playing'
|
||||
result = PlaybackState(state=state)
|
||||
self.assertEqual(result.state, state)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.state = None
|
||||
|
||||
def test_state_invalid(self):
|
||||
state = 'not_a_state'
|
||||
with self.assertRaises(TypeError):
|
||||
PlaybackState(state=state)
|
||||
|
||||
def test_to_json_and_back(self):
|
||||
result = PlaybackState(state='playing', tlid=4321)
|
||||
serialized = json.dumps(result, cls=ModelJSONEncoder)
|
||||
deserialized = json.loads(serialized, object_hook=model_json_decoder)
|
||||
self.assertEqual(result, deserialized)
|
||||
|
||||
|
||||
class TracklistStateTest(unittest.TestCase):
|
||||
|
||||
def test_repeat_true(self):
|
||||
repeat = True
|
||||
result = TracklistState(repeat=repeat)
|
||||
self.assertEqual(result.repeat, repeat)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.repeat = None
|
||||
|
||||
def test_repeat_false(self):
|
||||
repeat = False
|
||||
result = TracklistState(repeat=repeat)
|
||||
self.assertEqual(result.repeat, repeat)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.repeat = None
|
||||
|
||||
def test_repeat_invalid(self):
|
||||
repeat = 33
|
||||
with self.assertRaises(TypeError):
|
||||
TracklistState(repeat=repeat)
|
||||
|
||||
def test_consume_true(self):
|
||||
val = True
|
||||
result = TracklistState(consume=val)
|
||||
self.assertEqual(result.consume, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.repeat = None
|
||||
|
||||
def test_random_true(self):
|
||||
val = True
|
||||
result = TracklistState(random=val)
|
||||
self.assertEqual(result.random, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.random = None
|
||||
|
||||
def test_single_true(self):
|
||||
val = True
|
||||
result = TracklistState(single=val)
|
||||
self.assertEqual(result.single, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.single = None
|
||||
|
||||
def test_next_tlid(self):
|
||||
val = 654
|
||||
result = TracklistState(next_tlid=val)
|
||||
self.assertEqual(result.next_tlid, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.next_tlid = None
|
||||
|
||||
def test_next_tlid_invalid(self):
|
||||
val = -1
|
||||
with self.assertRaises(ValueError):
|
||||
TracklistState(next_tlid=val)
|
||||
|
||||
def test_tracks(self):
|
||||
tracks = (TlTrack(), TlTrack())
|
||||
result = TracklistState(tl_tracks=tracks)
|
||||
self.assertEqual(result.tl_tracks, tracks)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.tl_tracks = None
|
||||
|
||||
def test_tracks_invalid(self):
|
||||
tracks = (Track(), Track())
|
||||
with self.assertRaises(TypeError):
|
||||
TracklistState(tl_tracks=tracks)
|
||||
|
||||
def test_to_json_and_back(self):
|
||||
result = TracklistState(tl_tracks=(TlTrack(), TlTrack()), next_tlid=4)
|
||||
serialized = json.dumps(result, cls=ModelJSONEncoder)
|
||||
deserialized = json.loads(serialized, object_hook=model_json_decoder)
|
||||
self.assertEqual(result, deserialized)
|
||||
@ -4,9 +4,8 @@ import json
|
||||
import unittest
|
||||
|
||||
from mopidy.models import (
|
||||
Album, Artist, HistoryState, HistoryTrack, Image, MixerState,
|
||||
ModelJSONEncoder, PlaybackState, Playlist,
|
||||
Ref, SearchResult, TlTrack, Track, TracklistState, model_json_decoder)
|
||||
Album, Artist, Image, ModelJSONEncoder, Playlist,
|
||||
Ref, SearchResult, TlTrack, Track, model_json_decoder)
|
||||
|
||||
|
||||
class InheritanceTest(unittest.TestCase):
|
||||
@ -1169,164 +1168,3 @@ class SearchResultTest(unittest.TestCase):
|
||||
self.assertDictEqual(
|
||||
{'__model__': 'SearchResult', 'uri': 'uri'},
|
||||
SearchResult(uri='uri').serialize())
|
||||
|
||||
|
||||
class HistoryTrackTest(unittest.TestCase):
|
||||
|
||||
def test_track(self):
|
||||
track = Ref.track()
|
||||
result = HistoryTrack(track=track)
|
||||
self.assertEqual(result.track, track)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.track = None
|
||||
|
||||
def test_timestamp(self):
|
||||
timestamp = 1234
|
||||
result = HistoryTrack(timestamp=timestamp)
|
||||
self.assertEqual(result.timestamp, timestamp)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.timestamp = None
|
||||
|
||||
|
||||
class HistoryStateTest(unittest.TestCase):
|
||||
|
||||
def test_history_list(self):
|
||||
history = (HistoryTrack(),
|
||||
HistoryTrack())
|
||||
result = HistoryState(history=history)
|
||||
self.assertEqual(result.history, history)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.history = None
|
||||
|
||||
def test_history_string_fail(self):
|
||||
history = 'not_a_valid_history'
|
||||
with self.assertRaises(TypeError):
|
||||
HistoryState(history=history)
|
||||
|
||||
|
||||
class MixerStateTest(unittest.TestCase):
|
||||
|
||||
def test_volume(self):
|
||||
volume = 37
|
||||
result = MixerState(volume=volume)
|
||||
self.assertEqual(result.volume, volume)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.volume = None
|
||||
|
||||
def test_volume_invalid(self):
|
||||
volume = 105
|
||||
with self.assertRaises(ValueError):
|
||||
MixerState(volume=volume)
|
||||
|
||||
|
||||
class PlaybackStateTest(unittest.TestCase):
|
||||
|
||||
def test_position(self):
|
||||
position = 123456
|
||||
result = PlaybackState(position=position)
|
||||
self.assertEqual(result.position, position)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.position = None
|
||||
|
||||
def test_position_invalid(self):
|
||||
position = -1
|
||||
with self.assertRaises(ValueError):
|
||||
PlaybackState(position=position)
|
||||
|
||||
def test_tl_track(self):
|
||||
tl_track = TlTrack()
|
||||
result = PlaybackState(tl_track=tl_track)
|
||||
self.assertEqual(result.tl_track, tl_track)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.tl_track = None
|
||||
|
||||
def test_tl_track_none(self):
|
||||
tl_track = None
|
||||
result = PlaybackState(tl_track=tl_track)
|
||||
self.assertEqual(result.tl_track, tl_track)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.tl_track = None
|
||||
|
||||
def test_tl_track_invalid(self):
|
||||
tl_track = Track()
|
||||
with self.assertRaises(TypeError):
|
||||
PlaybackState(tl_track=tl_track)
|
||||
|
||||
def test_state(self):
|
||||
state = 'playing'
|
||||
result = PlaybackState(state=state)
|
||||
self.assertEqual(result.state, state)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.state = None
|
||||
|
||||
def test_state_invalid(self):
|
||||
state = 'not_a_state'
|
||||
with self.assertRaises(TypeError):
|
||||
PlaybackState(state=state)
|
||||
|
||||
|
||||
class TracklistStateTest(unittest.TestCase):
|
||||
|
||||
def test_repeat_true(self):
|
||||
repeat = True
|
||||
result = TracklistState(repeat=repeat)
|
||||
self.assertEqual(result.repeat, repeat)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.repeat = None
|
||||
|
||||
def test_repeat_false(self):
|
||||
repeat = False
|
||||
result = TracklistState(repeat=repeat)
|
||||
self.assertEqual(result.repeat, repeat)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.repeat = None
|
||||
|
||||
def test_repeat_invalid(self):
|
||||
repeat = 33
|
||||
with self.assertRaises(TypeError):
|
||||
TracklistState(repeat=repeat)
|
||||
|
||||
def test_consume_true(self):
|
||||
val = True
|
||||
result = TracklistState(consume=val)
|
||||
self.assertEqual(result.consume, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.repeat = None
|
||||
|
||||
def test_random_true(self):
|
||||
val = True
|
||||
result = TracklistState(random=val)
|
||||
self.assertEqual(result.random, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.random = None
|
||||
|
||||
def test_single_true(self):
|
||||
val = True
|
||||
result = TracklistState(single=val)
|
||||
self.assertEqual(result.single, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.single = None
|
||||
|
||||
def test_next_tlid(self):
|
||||
val = 654
|
||||
result = TracklistState(next_tlid=val)
|
||||
self.assertEqual(result.next_tlid, val)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.next_tlid = None
|
||||
|
||||
def test_next_tlid_invalid(self):
|
||||
val = -1
|
||||
with self.assertRaises(ValueError):
|
||||
TracklistState(next_tlid=val)
|
||||
|
||||
def test_tracks(self):
|
||||
tracks = (TlTrack(), TlTrack())
|
||||
result = TracklistState(tl_tracks=tracks)
|
||||
self.assertEqual(result.tl_tracks, tracks)
|
||||
with self.assertRaises(AttributeError):
|
||||
result.tl_tracks = None
|
||||
|
||||
def test_tracks_invalid(self):
|
||||
tracks = (Track(), Track())
|
||||
with self.assertRaises(TypeError):
|
||||
TracklistState(tl_tracks=tracks)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user