aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2019-04-07 19:07:31 +0000
committerMike Crute <mike@crute.us>2019-04-07 19:07:31 +0000
commitf394e36d82a85aff881812163ade0ec87888c86b (patch)
tree7257972752510871bb1245dd951dfbee950fe65f
parent88204c0999c49b79c6cf63a15f29dee5eadc31d6 (diff)
downloadpydora-f394e36d82a85aff881812163ade0ec87888c86b.tar.bz2
pydora-f394e36d82a85aff881812163ade0ec87888c86b.tar.xz
pydora-f394e36d82a85aff881812163ade0ec87888c86b.zip
Remove 'no cover' pragmas
-rw-r--r--pandora/client.py48
-rw-r--r--pandora/models/_base.py2
-rw-r--r--pandora/models/ad.py10
-rw-r--r--pandora/models/playlist.py20
-rw-r--r--pandora/models/search.py2
-rw-r--r--pandora/models/station.py2
-rw-r--r--setup.cfg6
-rw-r--r--tests/test_pandora/test_client.py134
-rw-r--r--tests/test_pandora/test_models.py93
9 files changed, 261 insertions, 56 deletions
diff --git a/pandora/client.py b/pandora/client.py
index bb30738..e0d3da9 100644
--- a/pandora/client.py
+++ b/pandora/client.py
@@ -102,14 +102,14 @@ class APIClient(BaseAPIClient):
102 This is what clients should actually use. 102 This is what clients should actually use.
103 """ 103 """
104 104
105 def get_station_list(self): # pragma: no cover 105 def get_station_list(self):
106 from .models.station import StationList 106 from .models.station import StationList
107 107
108 return StationList.from_json(self, 108 return StationList.from_json(self,
109 self("user.getStationList", 109 self("user.getStationList",
110 includeStationArtUrl=True)) 110 includeStationArtUrl=True))
111 111
112 def get_station_list_checksum(self): # pragma: no cover 112 def get_station_list_checksum(self):
113 return self("user.getStationListChecksum")["checksum"] 113 return self("user.getStationListChecksum")["checksum"]
114 114
115 def get_playlist(self, station_token, additional_urls=None): 115 def get_playlist(self, station_token, additional_urls=None):
@@ -142,13 +142,13 @@ class APIClient(BaseAPIClient):
142 142
143 return playlist 143 return playlist
144 144
145 def get_bookmarks(self): # pragma: no cover 145 def get_bookmarks(self):
146 from .models.bookmark import BookmarkList 146 from .models.bookmark import BookmarkList
147 147
148 return BookmarkList.from_json(self, 148 return BookmarkList.from_json(self,
149 self("user.getBookmarks")) 149 self("user.getBookmarks"))
150 150
151 def get_station(self, station_token): # pragma: no cover 151 def get_station(self, station_token):
152 from .models.station import Station 152 from .models.station import Station
153 153
154 return Station.from_json(self, 154 return Station.from_json(self,
@@ -156,25 +156,25 @@ class APIClient(BaseAPIClient):
156 stationToken=station_token, 156 stationToken=station_token,
157 includeExtendedAttributes=True)) 157 includeExtendedAttributes=True))
158 158
159 def add_artist_bookmark(self, track_token): # pragma: no cover 159 def add_artist_bookmark(self, track_token):
160 return self("bookmark.addArtistBookmark", 160 return self("bookmark.addArtistBookmark",
161 trackToken=track_token) 161 trackToken=track_token)
162 162
163 def add_song_bookmark(self, track_token): # pragma: no cover 163 def add_song_bookmark(self, track_token):
164 return self("bookmark.addSongBookmark", 164 return self("bookmark.addSongBookmark",
165 trackToken=track_token) 165 trackToken=track_token)
166 166
167 def delete_song_bookmark(self, bookmark_token): # pragma: no cover 167 def delete_song_bookmark(self, bookmark_token):
168 return self("bookmark.deleteSongBookmark", 168 return self("bookmark.deleteSongBookmark",
169 bookmarkToken=bookmark_token) 169 bookmarkToken=bookmark_token)
170 170
171 def delete_artist_bookmark(self, bookmark_token): # pragma: no cover 171 def delete_artist_bookmark(self, bookmark_token):
172 return self("bookmark.deleteArtistBookmark", 172 return self("bookmark.deleteArtistBookmark",
173 bookmarkToken=bookmark_token) 173 bookmarkToken=bookmark_token)
174 174
175 def search(self, search_text, 175 def search(self, search_text,
176 include_near_matches=False, 176 include_near_matches=False,
177 include_genre_stations=False): # pragma: no cover 177 include_genre_stations=False):
178 from .models.search import SearchResult 178 from .models.search import SearchResult
179 179
180 return SearchResult.from_json( 180 return SearchResult.from_json(
@@ -185,12 +185,12 @@ class APIClient(BaseAPIClient):
185 includeGenreStations=include_genre_stations) 185 includeGenreStations=include_genre_stations)
186 ) 186 )
187 187
188 def add_feedback(self, track_token, positive): # pragma: no cover 188 def add_feedback(self, track_token, positive):
189 return self("station.addFeedback", 189 return self("station.addFeedback",
190 trackToken=track_token, 190 trackToken=track_token,
191 isPositive=positive) 191 isPositive=positive)
192 192
193 def add_music(self, music_token, station_token): # pragma: no cover 193 def add_music(self, music_token, station_token):
194 return self("station.addMusic", 194 return self("station.addMusic",
195 musicToken=music_token, 195 musicToken=music_token,
196 stationToken=station_token) 196 stationToken=station_token)
@@ -213,15 +213,15 @@ class APIClient(BaseAPIClient):
213 return Station.from_json(self, 213 return Station.from_json(self,
214 self("station.createStation", **kwargs)) 214 self("station.createStation", **kwargs))
215 215
216 def delete_feedback(self, feedback_id): # pragma: no cover 216 def delete_feedback(self, feedback_id):
217 return self("station.deleteFeedback", 217 return self("station.deleteFeedback",
218 feedbackId=feedback_id) 218 feedbackId=feedback_id)
219 219
220 def delete_music(self, seed_id): # pragma: no cover 220 def delete_music(self, seed_id):
221 return self("station.deleteMusic", 221 return self("station.deleteMusic",
222 seedId=seed_id) 222 seedId=seed_id)
223 223
224 def delete_station(self, station_token): # pragma: no cover 224 def delete_station(self, station_token):
225 return self("station.deleteStation", 225 return self("station.deleteStation",
226 stationToken=station_token) 226 stationToken=station_token)
227 227
@@ -234,37 +234,37 @@ class APIClient(BaseAPIClient):
234 234
235 return genre_stations 235 return genre_stations
236 236
237 def get_genre_stations_checksum(self): # pragma: no cover 237 def get_genre_stations_checksum(self):
238 return self("station.getGenreStationsChecksum")["checksum"] 238 return self("station.getGenreStationsChecksum")["checksum"]
239 239
240 def rename_station(self, station_token, name): # pragma: no cover 240 def rename_station(self, station_token, name):
241 return self("station.renameStation", 241 return self("station.renameStation",
242 stationToken=station_token, 242 stationToken=station_token,
243 stationName=name) 243 stationName=name)
244 244
245 def explain_track(self, track_token): # pragma: no cover 245 def explain_track(self, track_token):
246 return self("track.explainTrack", 246 return self("track.explainTrack",
247 trackToken=track_token) 247 trackToken=track_token)
248 248
249 def set_quick_mix(self, *args): # pragma: no cover 249 def set_quick_mix(self, *args):
250 return self("user.setQuickMix", 250 return self("user.setQuickMix",
251 quickMixStationIds=args) 251 quickMixStationIds=args)
252 252
253 def sleep_song(self, track_token): # pragma: no cover 253 def sleep_song(self, track_token):
254 return self("user.sleepSong", 254 return self("user.sleepSong",
255 trackToken=track_token) 255 trackToken=track_token)
256 256
257 def share_station(self, station_id, station_token, *emails): # pragma: nc 257 def share_station(self, station_id, station_token, *emails):
258 return self("station.shareStation", 258 return self("station.shareStation",
259 stationId=station_id, 259 stationId=station_id,
260 stationToken=station_token, 260 stationToken=station_token,
261 emails=emails) 261 emails=emails)
262 262
263 def transform_shared_station(self, station_token): # pragma: no cover 263 def transform_shared_station(self, station_token):
264 return self("station.transformSharedStation", 264 return self("station.transformSharedStation",
265 stationToken=station_token) 265 stationToken=station_token)
266 266
267 def share_music(self, music_token, *emails): # pragma: no cover 267 def share_music(self, music_token, *emails):
268 return self("music.shareMusic", 268 return self("music.shareMusic",
269 musicToken=music_token, 269 musicToken=music_token,
270 email=emails[0]) 270 email=emails[0])
@@ -282,13 +282,13 @@ class APIClient(BaseAPIClient):
282 ad_item.ad_token = ad_token 282 ad_item.ad_token = ad_token
283 return ad_item 283 return ad_item
284 284
285 def get_ad_metadata(self, ad_token): # pragma: no cover 285 def get_ad_metadata(self, ad_token):
286 return self("ad.getAdMetadata", 286 return self("ad.getAdMetadata",
287 adToken=ad_token, 287 adToken=ad_token,
288 returnAdTrackingTokens=True, 288 returnAdTrackingTokens=True,
289 supportAudioAds=True) 289 supportAudioAds=True)
290 290
291 def register_ad(self, station_id, tokens): # pragma: no cover 291 def register_ad(self, station_id, tokens):
292 return self("ad.registerAd", 292 return self("ad.registerAd",
293 stationId=station_id, 293 stationId=station_id,
294 adTrackingTokens=tokens) 294 adTrackingTokens=tokens)
diff --git a/pandora/models/_base.py b/pandora/models/_base.py
index 5689cdf..6975d0a 100644
--- a/pandora/models/_base.py
+++ b/pandora/models/_base.py
@@ -38,7 +38,7 @@ class SyntheticField(namedtuple("SyntheticField", ["field"])):
38 payload. 38 payload.
39 """ 39 """
40 40
41 def formatter(self, api_client, data, newval): # pragma: no cover 41 def formatter(self, api_client, data, newval):
42 """Format Value for Model 42 """Format Value for Model
43 43
44 The return value of this method is used as a value for the field in the 44 The return value of this method is used as a value for the field in the
diff --git a/pandora/models/ad.py b/pandora/models/ad.py
index 040c76b..ad4b7b0 100644
--- a/pandora/models/ad.py
+++ b/pandora/models/ad.py
@@ -35,17 +35,17 @@ class AdItem(PlaylistModel):
35 raise exc 35 raise exc
36 return super().prepare_playback() 36 return super().prepare_playback()
37 37
38 def thumbs_up(self): # pragma: no cover 38 def thumbs_up(self):
39 return 39 return
40 40
41 def thumbs_down(self): # pragma: no cover 41 def thumbs_down(self):
42 return 42 return
43 43
44 def bookmark_song(self): # pragma: no cover 44 def bookmark_song(self):
45 return 45 return
46 46
47 def bookmark_artist(self): # pragma: no cover 47 def bookmark_artist(self):
48 return 48 return
49 49
50 def sleep(self): # pragma: no cover 50 def sleep(self):
51 return 51 return
diff --git a/pandora/models/playlist.py b/pandora/models/playlist.py
index bd135db..38afb00 100644
--- a/pandora/models/playlist.py
+++ b/pandora/models/playlist.py
@@ -119,19 +119,19 @@ class PlaylistModel(PandoraModel):
119 """ 119 """
120 return self 120 return self
121 121
122 def thumbs_up(self): # pragma: no cover 122 def thumbs_up(self):
123 raise NotImplementedError 123 raise NotImplementedError
124 124
125 def thumbs_down(self): # pragma: no cover 125 def thumbs_down(self):
126 raise NotImplementedError 126 raise NotImplementedError
127 127
128 def bookmark_song(self): # pragma: no cover 128 def bookmark_song(self):
129 raise NotImplementedError 129 raise NotImplementedError
130 130
131 def bookmark_artist(self): # pragma: no cover 131 def bookmark_artist(self):
132 raise NotImplementedError 132 raise NotImplementedError
133 133
134 def sleep(self): # pragma: no cover 134 def sleep(self):
135 raise NotImplementedError 135 raise NotImplementedError
136 136
137 137
@@ -175,19 +175,19 @@ class PlaylistItem(PlaylistModel):
175 def is_ad(self): 175 def is_ad(self):
176 return self.ad_token is not None 176 return self.ad_token is not None
177 177
178 def thumbs_up(self): # pragma: no cover 178 def thumbs_up(self):
179 return self._api_client.add_feedback(self.track_token, True) 179 return self._api_client.add_feedback(self.track_token, True)
180 180
181 def thumbs_down(self): # pragma: no cover 181 def thumbs_down(self):
182 return self._api_client.add_feedback(self.track_token, False) 182 return self._api_client.add_feedback(self.track_token, False)
183 183
184 def bookmark_song(self): # pragma: no cover 184 def bookmark_song(self):
185 return self._api_client.add_song_bookmark(self.track_token) 185 return self._api_client.add_song_bookmark(self.track_token)
186 186
187 def bookmark_artist(self): # pragma: no cover 187 def bookmark_artist(self):
188 return self._api_client.add_artist_bookmark(self.track_token) 188 return self._api_client.add_artist_bookmark(self.track_token)
189 189
190 def sleep(self): # pragma: no cover 190 def sleep(self):
191 return self._api_client.sleep_song(self.track_token) 191 return self._api_client.sleep_song(self.track_token)
192 192
193 193
diff --git a/pandora/models/search.py b/pandora/models/search.py
index 89def8e..94e6ee6 100644
--- a/pandora/models/search.py
+++ b/pandora/models/search.py
@@ -24,7 +24,7 @@ class SearchResultItem(PandoraModel):
24 def is_genre_station(self): 24 def is_genre_station(self):
25 return isinstance(self, GenreStationSearchResultItem) 25 return isinstance(self, GenreStationSearchResultItem)
26 26
27 def create_station(self): # pragma: no cover 27 def create_station(self):
28 raise NotImplementedError 28 raise NotImplementedError
29 29
30 @classmethod 30 @classmethod
diff --git a/pandora/models/station.py b/pandora/models/station.py
index d4f4846..a1880ec 100644
--- a/pandora/models/station.py
+++ b/pandora/models/station.py
@@ -104,7 +104,7 @@ class GenreStation(PandoraModel):
104 token = Field("stationToken") 104 token = Field("stationToken")
105 category = Field("categoryName") 105 category = Field("categoryName")
106 106
107 def get_playlist(self): # pragma: no cover 107 def get_playlist(self):
108 raise NotImplementedError("Genre stations do not have playlists. " 108 raise NotImplementedError("Genre stations do not have playlists. "
109 "Create a real station using the token.") 109 "Create a real station using the token.")
110 110
diff --git a/setup.cfg b/setup.cfg
index 2c43f4f..1c8c41f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -4,9 +4,3 @@ validate = release
4 4
5[coverage:run] 5[coverage:run]
6branch = True 6branch = True
7
8[coverage:report]
9# Support "no cover" and "nc" to handle long lines
10exclude_lines =
11 pragma: no cover
12 pragma: nc
diff --git a/tests/test_pandora/test_client.py b/tests/test_pandora/test_client.py
index 9945bff..69eac65 100644
--- a/tests/test_pandora/test_client.py
+++ b/tests/test_pandora/test_client.py
@@ -3,6 +3,10 @@ from unittest.mock import Mock, call, patch
3 3
4from pandora import errors 4from pandora import errors
5from pandora.models.ad import AdItem 5from pandora.models.ad import AdItem
6from pandora.models.station import Station
7from pandora.models.station import StationList
8from pandora.models.search import SearchResult
9from pandora.models.bookmark import BookmarkList
6from pandora.models.playlist import AdditionalAudioUrl 10from pandora.models.playlist import AdditionalAudioUrl
7from pandora.client import APIClient, BaseAPIClient 11from pandora.client import APIClient, BaseAPIClient
8from tests.test_pandora.test_models import TestAdItem 12from tests.test_pandora.test_models import TestAdItem
@@ -253,3 +257,133 @@ class TestAdditionalUrls(TestCase):
253 includeTrackLength=True, 257 includeTrackLength=True,
254 stationToken='token_mock', 258 stationToken='token_mock',
255 xplatformAdCapable=True)]) 259 xplatformAdCapable=True)])
260
261
262# On the surface this test class seems dumb because it's mostly just exercising
263# pass-throughs to the transport but it exists to ensure no subtle errors get
264# introduced to API client methods that will only be spotted at runtime (import
265# errors, etc...)
266class TestAPIClientExhaustive(TestCase):
267
268 def setUp(self):
269 self.transport = Mock()
270 self.api = APIClient(self.transport, "puser", "ppass", "device")
271
272 def test_register_ad(self):
273 self.api.register_ad("sid", "tokens")
274 self.transport.assert_called_with(
275 "ad.registerAd", stationId="sid", adTrackingTokens="tokens")
276
277 def test_share_music(self):
278 self.api.share_music("token", "foo@example.com")
279 self.transport.assert_called_with(
280 "music.shareMusic", musicToken="token", email="foo@example.com")
281
282 def test_transform_shared_station(self):
283 self.api.transform_shared_station("token")
284 self.transport.assert_called_with(
285 "station.transformSharedStation", stationToken="token")
286
287 def test_share_station(self):
288 self.api.share_station("sid", "token", "foo@example.com")
289 self.transport.assert_called_with(
290 "station.shareStation", stationId="sid", stationToken="token",
291 emails=("foo@example.com",))
292
293 def test_sleep_song(self):
294 self.api.sleep_song("token")
295 self.transport.assert_called_with("user.sleepSong", trackToken="token")
296
297 def test_set_quick_mix(self):
298 self.api.set_quick_mix("id")
299 self.transport.assert_called_with(
300 "user.setQuickMix", quickMixStationIds=("id",))
301
302 def test_explain_track(self):
303 self.api.explain_track("token")
304 self.transport.assert_called_with(
305 "track.explainTrack", trackToken="token")
306
307 def test_rename_station(self):
308 self.api.rename_station("token", "name")
309 self.transport.assert_called_with(
310 "station.renameStation", stationToken="token", stationName="name")
311
312 def test_delete_station(self):
313 self.api.delete_station("token")
314 self.transport.assert_called_with(
315 "station.deleteStation", stationToken="token")
316
317 def test_delete_music(self):
318 self.api.delete_music("seed")
319 self.transport.assert_called_with("station.deleteMusic", seedId="seed")
320
321 def test_delete_feedback(self):
322 self.api.delete_feedback("id")
323 self.transport.assert_called_with(
324 "station.deleteFeedback", feedbackId="id")
325
326 def test_add_music(self):
327 self.api.add_music("mt", "st")
328 self.transport.assert_called_with(
329 "station.addMusic", musicToken="mt", stationToken="st")
330
331 def test_add_feedback(self):
332 self.api.add_feedback("token", False)
333 self.transport.assert_called_with(
334 "station.addFeedback", trackToken="token", isPositive=False)
335
336 def test_add_artist_bookmark(self):
337 self.api.add_artist_bookmark("tt")
338 self.transport.assert_called_with(
339 "bookmark.addArtistBookmark", trackToken="tt")
340
341 def test_add_song_bookmark(self):
342 self.api.add_song_bookmark("tt")
343 self.transport.assert_called_with(
344 "bookmark.addSongBookmark", trackToken="tt")
345
346 def test_delete_song_bookmark(self):
347 self.api.delete_song_bookmark("bt")
348 self.transport.assert_called_with(
349 "bookmark.deleteSongBookmark", bookmarkToken="bt")
350
351 def test_delete_artist_bookmark(self):
352 self.api.delete_artist_bookmark("bt")
353 self.transport.assert_called_with(
354 "bookmark.deleteArtistBookmark", bookmarkToken="bt")
355
356 def test_get_station_list_checksum(self):
357 self.transport.return_value = {"checksum": "foo"}
358 self.assertEqual("foo", self.api.get_station_list_checksum())
359 self.transport.assert_called_with("user.getStationListChecksum")
360
361 # The following methods use the bare minimum JSON required to construct the
362 # models for more detailed model tests look at test_models instead
363
364 def test_get_station_list(self):
365 self.transport.return_value = {"stations": []}
366 self.assertIsInstance(self.api.get_station_list(), StationList)
367 self.transport.assert_called_with(
368 "user.getStationList", includeStationArtUrl=True)
369
370 def test_get_bookmarks(self):
371 self.transport.return_value = {}
372 self.assertIsInstance(self.api.get_bookmarks(), BookmarkList)
373 self.transport.assert_called_with("user.getBookmarks")
374
375 def test_get_station(self):
376 self.transport.return_value = {}
377 self.assertIsInstance(self.api.get_station("st"), Station)
378 self.transport.assert_called_with(
379 "station.getStation", stationToken="st",
380 includeExtendedAttributes=True)
381
382 def test_search(self):
383 self.transport.return_value = {}
384 self.assertIsInstance(self.api.search(
385 "text", include_near_matches=True, include_genre_stations=True),
386 SearchResult)
387 self.transport.assert_called_with(
388 "music.search", searchText="text", includeNearMatches=True,
389 includeGenreStations=True)
diff --git a/tests/test_pandora/test_models.py b/tests/test_pandora/test_models.py
index 0d37ca0..cb650df 100644
--- a/tests/test_pandora/test_models.py
+++ b/tests/test_pandora/test_models.py
@@ -277,6 +277,11 @@ class TestPlaylistItemModel(TestCase):
277 AUDIO_URL_NO_MAP = {"audioUrl": "foo"} 277 AUDIO_URL_NO_MAP = {"audioUrl": "foo"}
278 WEIRD_FORMAT = {"audioUrlMap": {"highQuality": {}}} 278 WEIRD_FORMAT = {"audioUrlMap": {"highQuality": {}}}
279 279
280 def setUp(self):
281 self.client = Mock()
282 self.playlist = plm.PlaylistItem(self.client)
283 self.playlist.track_token = "token"
284
280 def test_audio_url_without_map(self): 285 def test_audio_url_without_map(self):
281 item = plm.PlaylistItem.from_json(Mock(), self.AUDIO_URL_NO_MAP) 286 item = plm.PlaylistItem.from_json(Mock(), self.AUDIO_URL_NO_MAP)
282 self.assertEqual(item.bitrate, 64) 287 self.assertEqual(item.bitrate, 64)
@@ -293,20 +298,57 @@ class TestPlaylistItemModel(TestCase):
293 self.assertIsNone(item.encoding) 298 self.assertIsNone(item.encoding)
294 self.assertIsNone(item.audio_url) 299 self.assertIsNone(item.audio_url)
295 300
301 def test_thumbs_up(self):
302 self.playlist.thumbs_up()
303 self.client.add_feedback.assert_called_with("token", True)
304
305 def test_thumbs_down(self):
306 self.playlist.thumbs_down()
307 self.client.add_feedback.assert_called_with("token", False)
308
309 def test_bookmark_song(self):
310 self.playlist.bookmark_song()
311 self.client.add_song_bookmark.assert_called_with("token")
312
313 def test_bookmark_artist(self):
314 self.playlist.bookmark_artist()
315 self.client.add_artist_bookmark.assert_called_with("token")
316
317 def test_sleep_song(self):
318 self.playlist.sleep()
319 self.client.sleep_song.assert_called_with("token")
320
296 321
297class TestPlaylistModel(TestCase): 322class TestPlaylistModel(TestCase):
298 323
324 def setUp(self):
325 self.client = Mock()
326 self.playlist = plm.PlaylistModel(self.client)
327
299 def test_unplayable_get_is_playable(self): 328 def test_unplayable_get_is_playable(self):
300 playlist = plm.PlaylistModel(Mock()) 329 self.playlist.audio_url = ""
301 playlist.audio_url = "" 330 self.assertFalse(self.playlist.get_is_playable())
302 self.assertFalse(playlist.get_is_playable())
303 331
304 def test_playable_get_is_playable(self): 332 def test_playable_get_is_playable(self):
305 client = Mock() 333 self.playlist.audio_url = "foo"
306 playlist = plm.PlaylistModel(client) 334 self.playlist.get_is_playable()
307 playlist.audio_url = "foo" 335 self.client.transport.test_url.assert_called_with("foo")
308 playlist.get_is_playable() 336
309 client.transport.test_url.assert_called_with("foo") 337 def test_not_implemented_interface_methods(self):
338 with self.assertRaises(NotImplementedError):
339 self.playlist.thumbs_up()
340
341 with self.assertRaises(NotImplementedError):
342 self.playlist.thumbs_down()
343
344 with self.assertRaises(NotImplementedError):
345 self.playlist.bookmark_song()
346
347 with self.assertRaises(NotImplementedError):
348 self.playlist.bookmark_artist()
349
350 with self.assertRaises(NotImplementedError):
351 self.playlist.sleep()
310 352
311 353
312class TestAdItem(TestCase): 354class TestAdItem(TestCase):
@@ -393,6 +435,13 @@ class TestAdItem(TestCase):
393 assert self.result.register_ad.called 435 assert self.result.register_ad.called
394 assert super_mock.called 436 assert super_mock.called
395 437
438 def test_noop_methods(self):
439 self.assertIsNone(self.result.thumbs_up())
440 self.assertIsNone(self.result.thumbs_down())
441 self.assertIsNone(self.result.bookmark_song())
442 self.assertIsNone(self.result.bookmark_artist())
443 self.assertIsNone(self.result.sleep())
444
396 445
397class TestSearchResultItem(TestCase): 446class TestSearchResultItem(TestCase):
398 447
@@ -471,6 +520,12 @@ class TestSearchResultItem(TestCase):
471 sm.SearchResultItem.from_json( 520 sm.SearchResultItem.from_json(
472 self.api_client_mock, self.UNKNOWN_JSON_DATA) 521 self.api_client_mock, self.UNKNOWN_JSON_DATA)
473 522
523 def test_interface(self):
524 result = sm.SearchResultItem(self.api_client_mock)
525
526 with self.assertRaises(NotImplementedError):
527 result.create_station()
528
474 529
475class TestArtistSearchResultItem(TestCase): 530class TestArtistSearchResultItem(TestCase):
476 531
@@ -642,6 +697,19 @@ class TestGenreStationList(TestCase):
642 self.assertTrue(stations.has_changed()) 697 self.assertTrue(stations.has_changed())
643 698
644 699
700class TestGenreStation(TestCase):
701
702 TEST_DATA = {"categoryName": "foo", "stations": []}
703
704 def test_get_playlist_throws_exception(self):
705 api_client = Mock()
706 genre_station = stm.GenreStation.from_json(api_client, self.TEST_DATA)
707
708 with self.assertRaisesRegex(
709 NotImplementedError, "Genre stations do not have playlists.*"):
710 genre_station.get_playlist()
711
712
645class TestStationList(TestCase): 713class TestStationList(TestCase):
646 714
647 TEST_DATA = { 715 TEST_DATA = {
@@ -694,3 +762,12 @@ class TestPandoraType(TestCase):
694 def test_it_returns_genre_for_unknown_string(self): 762 def test_it_returns_genre_for_unknown_string(self):
695 pt = plm.PandoraType.from_string("FOO") 763 pt = plm.PandoraType.from_string("FOO")
696 self.assertIs(plm.PandoraType.GENRE, pt) 764 self.assertIs(plm.PandoraType.GENRE, pt)
765
766
767class TestSyntheticField(TestCase):
768
769 def test_interface(self):
770 sf = m.SyntheticField(field="foo")
771
772 with self.assertRaises(NotImplementedError):
773 sf.formatter(None, None, None)