From 4869619bb98abf021b34b2a8b758687adf3e2781 Mon Sep 17 00:00:00 2001 From: Jens Luetjen Date: Sun, 10 Jan 2016 13:24:14 +0100 Subject: [PATCH] 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 --- mopidy/core/actor.py | 34 ++--- mopidy/core/history.py | 8 +- mopidy/core/mixer.py | 4 +- mopidy/core/playback.py | 6 +- mopidy/core/tracklist.py | 9 +- mopidy/internal/models.py | 142 ++++++++++++++++++ mopidy/{models => internal}/storage.py | 0 mopidy/local/json.py | 9 +- mopidy/models/__init__.py | 109 +------------- mopidy/models/serialize.py | 7 +- tests/core/test_history.py | 3 +- tests/core/test_mixer.py | 2 +- tests/core/test_playback.py | 3 +- tests/core/test_tracklist.py | 3 +- tests/internal/test_models.py | 200 +++++++++++++++++++++++++ tests/models/test_models.py | 166 +------------------- 16 files changed, 390 insertions(+), 315 deletions(-) create mode 100644 mopidy/internal/models.py rename mopidy/{models => internal}/storage.py (100%) create mode 100644 tests/internal/test_models.py diff --git a/mopidy/core/actor.py b/mopidy/core/actor.py index 750b965b..8b9010ab 100644 --- a/mopidy/core/actor.py +++ b/mopidy/core/actor.py @@ -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.') diff --git a/mopidy/core/history.py b/mopidy/core/history.py index 7cd62131..3c0a2446 100644 --- a/mopidy/core/history.py +++ b/mopidy/core/history.py @@ -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: diff --git a/mopidy/core/mixer.py b/mopidy/core/mixer.py index 92938bf1..cceb0ebe 100644 --- a/mopidy/core/mixer.py +++ b/mopidy/core/mixer.py @@ -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) diff --git a/mopidy/core/playback.py b/mopidy/core/playback.py index 3a72e2e6..54d30230 100644 --- a/mopidy/core/playback.py +++ b/mopidy/core/playback.py @@ -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? diff --git a/mopidy/core/tracklist.py b/mopidy/core/tracklist.py index 50068d92..f99f3917 100644 --- a/mopidy/core/tracklist.py +++ b/mopidy/core/tracklist.py @@ -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() diff --git a/mopidy/internal/models.py b/mopidy/internal/models.py new file mode 100644 index 00000000..5214cc65 --- /dev/null +++ b/mopidy/internal/models.py @@ -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) diff --git a/mopidy/models/storage.py b/mopidy/internal/storage.py similarity index 100% rename from mopidy/models/storage.py rename to mopidy/internal/storage.py diff --git a/mopidy/local/json.py b/mopidy/local/json.py index de40e15b..378cab75 100644 --- a/mopidy/local/json.py +++ b/mopidy/local/json.py @@ -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: diff --git a/mopidy/models/__init__.py b/mopidy/models/__init__.py index 6df3e550..1e63d02f 100644 --- a/mopidy/models/__init__.py +++ b/mopidy/models/__init__.py @@ -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) diff --git a/mopidy/models/serialize.py b/mopidy/models/serialize.py index 08162db4..68d170c4 100644 --- a/mopidy/models/serialize.py +++ b/mopidy/models/serialize.py @@ -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 diff --git a/tests/core/test_history.py b/tests/core/test_history.py index 8c204270..65babde8 100644 --- a/tests/core/test_history.py +++ b/tests/core/test_history.py @@ -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): diff --git a/tests/core/test_mixer.py b/tests/core/test_mixer.py index dbfdd656..0b7b789b 100644 --- a/tests/core/test_mixer.py +++ b/tests/core/test_mixer.py @@ -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 diff --git a/tests/core/test_playback.py b/tests/core/test_playback.py index a6af330b..6d66bca6 100644 --- a/tests/core/test_playback.py +++ b/tests/core/test_playback.py @@ -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 diff --git a/tests/core/test_tracklist.py b/tests/core/test_tracklist.py index 40de840b..7b26f3d7 100644 --- a/tests/core/test_tracklist.py +++ b/tests/core/test_tracklist.py @@ -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): diff --git a/tests/internal/test_models.py b/tests/internal/test_models.py new file mode 100644 index 00000000..9c181bdd --- /dev/null +++ b/tests/internal/test_models.py @@ -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) diff --git a/tests/models/test_models.py b/tests/models/test_models.py index 0281fd65..35e77aef 100644 --- a/tests/models/test_models.py +++ b/tests/models/test_models.py @@ -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)