# This file is part of beets. # Copyright 2016, Adrian Sampson. # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. """Tests for the 'beatport' plugin. """ import unittest from datetime import timedelta from beets import library from beets.test import _common from beets.test.helper import TestHelper from beetsplug import beatport class BeatportTest(_common.TestCase, TestHelper): def _make_release_response(self): """Returns a dict that mimics a response from the beatport API. The results were retrieved from: https://oauth-api.beatport.com/catalog/3/releases?id=1742984 The list of elements on the returned dict is incomplete, including just those required for the tests on this class. """ results = { "id": 1742984, "type": "release", "name": "Charade", "slug": "charade", "releaseDate": "2016-04-11", "publishDate": "2016-04-11", "audioFormat": "", "category": "Release", "currentStatus": "General Content", "catalogNumber": "GR089", "description": "", "label": { "id": 24539, "name": "Gravitas Recordings", "type": "label", "slug": "gravitas-recordings", }, "artists": [ { "id": 326158, "name": "Supersillyus", "slug": "supersillyus", "type": "artist", } ], "genres": [ {"id": 9, "name": "Breaks", "slug": "breaks", "type": "genre"} ], } return results def _make_tracks_response(self): """Return a list that mimics a response from the beatport API. The results were retrieved from: https://oauth-api.beatport.com/catalog/3/tracks?releaseId=1742984 The list of elements on the returned list is incomplete, including just those required for the tests on this class. """ results = [ { "id": 7817567, "type": "track", "sku": "track-7817567", "name": "Mirage a Trois", "trackNumber": 1, "mixName": "Original Mix", "title": "Mirage a Trois (Original Mix)", "slug": "mirage-a-trois-original-mix", "releaseDate": "2016-04-11", "publishDate": "2016-04-11", "currentStatus": "General Content", "length": "7:05", "lengthMs": 425421, "bpm": 90, "key": { "standard": { "letter": "G", "sharp": False, "flat": False, "chord": "minor", }, "shortName": "Gmin", }, "artists": [ { "id": 326158, "name": "Supersillyus", "slug": "supersillyus", "type": "artist", } ], "genres": [ { "id": 9, "name": "Breaks", "slug": "breaks", "type": "genre", } ], "subGenres": [ { "id": 209, "name": "Glitch Hop", "slug": "glitch-hop", "type": "subgenre", } ], "release": { "id": 1742984, "name": "Charade", "type": "release", "slug": "charade", }, "label": { "id": 24539, "name": "Gravitas Recordings", "type": "label", "slug": "gravitas-recordings", "status": True, }, }, { "id": 7817568, "type": "track", "sku": "track-7817568", "name": "Aeon Bahamut", "trackNumber": 2, "mixName": "Original Mix", "title": "Aeon Bahamut (Original Mix)", "slug": "aeon-bahamut-original-mix", "releaseDate": "2016-04-11", "publishDate": "2016-04-11", "currentStatus": "General Content", "length": "7:38", "lengthMs": 458000, "bpm": 100, "key": { "standard": { "letter": "G", "sharp": False, "flat": False, "chord": "major", }, "shortName": "Gmaj", }, "artists": [ { "id": 326158, "name": "Supersillyus", "slug": "supersillyus", "type": "artist", } ], "genres": [ { "id": 9, "name": "Breaks", "slug": "breaks", "type": "genre", } ], "subGenres": [ { "id": 209, "name": "Glitch Hop", "slug": "glitch-hop", "type": "subgenre", } ], "release": { "id": 1742984, "name": "Charade", "type": "release", "slug": "charade", }, "label": { "id": 24539, "name": "Gravitas Recordings", "type": "label", "slug": "gravitas-recordings", "status": True, }, }, { "id": 7817569, "type": "track", "sku": "track-7817569", "name": "Trancendental Medication", "trackNumber": 3, "mixName": "Original Mix", "title": "Trancendental Medication (Original Mix)", "slug": "trancendental-medication-original-mix", "releaseDate": "2016-04-11", "publishDate": "2016-04-11", "currentStatus": "General Content", "length": "1:08", "lengthMs": 68571, "bpm": 141, "key": { "standard": { "letter": "F", "sharp": False, "flat": False, "chord": "major", }, "shortName": "Fmaj", }, "artists": [ { "id": 326158, "name": "Supersillyus", "slug": "supersillyus", "type": "artist", } ], "genres": [ { "id": 9, "name": "Breaks", "slug": "breaks", "type": "genre", } ], "subGenres": [ { "id": 209, "name": "Glitch Hop", "slug": "glitch-hop", "type": "subgenre", } ], "release": { "id": 1742984, "name": "Charade", "type": "release", "slug": "charade", }, "label": { "id": 24539, "name": "Gravitas Recordings", "type": "label", "slug": "gravitas-recordings", "status": True, }, }, { "id": 7817570, "type": "track", "sku": "track-7817570", "name": "A List of Instructions for When I'm Human", "trackNumber": 4, "mixName": "Original Mix", "title": "A List of Instructions for When I'm Human (Original Mix)", "slug": "a-list-of-instructions-for-when-im-human-original-mix", "releaseDate": "2016-04-11", "publishDate": "2016-04-11", "currentStatus": "General Content", "length": "6:57", "lengthMs": 417913, "bpm": 88, "key": { "standard": { "letter": "A", "sharp": False, "flat": False, "chord": "minor", }, "shortName": "Amin", }, "artists": [ { "id": 326158, "name": "Supersillyus", "slug": "supersillyus", "type": "artist", } ], "genres": [ { "id": 9, "name": "Breaks", "slug": "breaks", "type": "genre", } ], "subGenres": [ { "id": 209, "name": "Glitch Hop", "slug": "glitch-hop", "type": "subgenre", } ], "release": { "id": 1742984, "name": "Charade", "type": "release", "slug": "charade", }, "label": { "id": 24539, "name": "Gravitas Recordings", "type": "label", "slug": "gravitas-recordings", "status": True, }, }, { "id": 7817571, "type": "track", "sku": "track-7817571", "name": "The Great Shenanigan", "trackNumber": 5, "mixName": "Original Mix", "title": "The Great Shenanigan (Original Mix)", "slug": "the-great-shenanigan-original-mix", "releaseDate": "2016-04-11", "publishDate": "2016-04-11", "currentStatus": "General Content", "length": "9:49", "lengthMs": 589875, "bpm": 123, "key": { "standard": { "letter": "E", "sharp": False, "flat": True, "chord": "major", }, "shortName": "E♭maj", }, "artists": [ { "id": 326158, "name": "Supersillyus", "slug": "supersillyus", "type": "artist", } ], "genres": [ { "id": 9, "name": "Breaks", "slug": "breaks", "type": "genre", } ], "subGenres": [ { "id": 209, "name": "Glitch Hop", "slug": "glitch-hop", "type": "subgenre", } ], "release": { "id": 1742984, "name": "Charade", "type": "release", "slug": "charade", }, "label": { "id": 24539, "name": "Gravitas Recordings", "type": "label", "slug": "gravitas-recordings", "status": True, }, }, { "id": 7817572, "type": "track", "sku": "track-7817572", "name": "Charade", "trackNumber": 6, "mixName": "Original Mix", "title": "Charade (Original Mix)", "slug": "charade-original-mix", "releaseDate": "2016-04-11", "publishDate": "2016-04-11", "currentStatus": "General Content", "length": "7:05", "lengthMs": 425423, "bpm": 123, "key": { "standard": { "letter": "A", "sharp": False, "flat": False, "chord": "major", }, "shortName": "Amaj", }, "artists": [ { "id": 326158, "name": "Supersillyus", "slug": "supersillyus", "type": "artist", } ], "genres": [ { "id": 9, "name": "Breaks", "slug": "breaks", "type": "genre", } ], "subGenres": [ { "id": 209, "name": "Glitch Hop", "slug": "glitch-hop", "type": "subgenre", } ], "release": { "id": 1742984, "name": "Charade", "type": "release", "slug": "charade", }, "label": { "id": 24539, "name": "Gravitas Recordings", "type": "label", "slug": "gravitas-recordings", "status": True, }, }, ] return results def setUp(self): self.setup_beets() self.load_plugins("beatport") self.lib = library.Library(":memory:") # Set up 'album'. response_release = self._make_release_response() self.album = beatport.BeatportRelease(response_release) # Set up 'tracks'. response_tracks = self._make_tracks_response() self.tracks = [beatport.BeatportTrack(t) for t in response_tracks] # Set up 'test_album'. self.test_album = self.mk_test_album() # Set up 'test_tracks' self.test_tracks = self.test_album.items() def tearDown(self): self.unload_plugins() self.teardown_beets() def mk_test_album(self): items = [_common.item() for _ in range(6)] for item in items: item.album = "Charade" item.catalognum = "GR089" item.label = "Gravitas Recordings" item.artist = "Supersillyus" item.year = 2016 item.comp = False item.label_name = "Gravitas Recordings" item.genre = "Glitch Hop" item.year = 2016 item.month = 4 item.day = 11 item.mix_name = "Original Mix" items[0].title = "Mirage a Trois" items[1].title = "Aeon Bahamut" items[2].title = "Trancendental Medication" items[3].title = "A List of Instructions for When I'm Human" items[4].title = "The Great Shenanigan" items[5].title = "Charade" items[0].length = timedelta(minutes=7, seconds=5).total_seconds() items[1].length = timedelta(minutes=7, seconds=38).total_seconds() items[2].length = timedelta(minutes=1, seconds=8).total_seconds() items[3].length = timedelta(minutes=6, seconds=57).total_seconds() items[4].length = timedelta(minutes=9, seconds=49).total_seconds() items[5].length = timedelta(minutes=7, seconds=5).total_seconds() items[0].url = "mirage-a-trois-original-mix" items[1].url = "aeon-bahamut-original-mix" items[2].url = "trancendental-medication-original-mix" items[3].url = "a-list-of-instructions-for-when-im-human-original-mix" items[4].url = "the-great-shenanigan-original-mix" items[5].url = "charade-original-mix" counter = 0 for item in items: counter += 1 item.track_number = counter items[0].bpm = 90 items[1].bpm = 100 items[2].bpm = 141 items[3].bpm = 88 items[4].bpm = 123 items[5].bpm = 123 items[0].initial_key = "Gmin" items[1].initial_key = "Gmaj" items[2].initial_key = "Fmaj" items[3].initial_key = "Amin" items[4].initial_key = "E♭maj" items[5].initial_key = "Amaj" for item in items: self.lib.add(item) album = self.lib.add_album(items) album.store() return album # Test BeatportRelease. def test_album_name_applied(self): self.assertEqual(self.album.name, self.test_album["album"]) def test_catalog_number_applied(self): self.assertEqual( self.album.catalog_number, self.test_album["catalognum"] ) def test_label_applied(self): self.assertEqual(self.album.label_name, self.test_album["label"]) def test_category_applied(self): self.assertEqual(self.album.category, "Release") def test_album_url_applied(self): self.assertEqual( self.album.url, "https://beatport.com/release/charade/1742984" ) # Test BeatportTrack. def test_title_applied(self): for track, test_track in zip(self.tracks, self.test_tracks): self.assertEqual(track.name, test_track.title) def test_mix_name_applied(self): for track, test_track in zip(self.tracks, self.test_tracks): self.assertEqual(track.mix_name, test_track.mix_name) def test_length_applied(self): for track, test_track in zip(self.tracks, self.test_tracks): self.assertEqual( int(track.length.total_seconds()), int(test_track.length) ) def test_track_url_applied(self): # Specify beatport ids here because an 'item.id' is beets-internal. ids = [ 7817567, 7817568, 7817569, 7817570, 7817571, 7817572, ] # Concatenate with 'id' to pass strict equality test. for track, test_track, id in zip(self.tracks, self.test_tracks, ids): self.assertEqual( track.url, "https://beatport.com/track/" + test_track.url + "/" + str(id), ) def test_bpm_applied(self): for track, test_track in zip(self.tracks, self.test_tracks): self.assertEqual(track.bpm, test_track.bpm) def test_initial_key_applied(self): for track, test_track in zip(self.tracks, self.test_tracks): self.assertEqual(track.initial_key, test_track.initial_key) def test_genre_applied(self): for track, test_track in zip(self.tracks, self.test_tracks): self.assertEqual(track.genre, test_track.genre) class BeatportResponseEmptyTest(_common.TestCase, TestHelper): def _make_tracks_response(self): results = [ { "id": 7817567, "name": "Mirage a Trois", "genres": [ { "id": 9, "name": "Breaks", "slug": "breaks", "type": "genre", } ], "subGenres": [ { "id": 209, "name": "Glitch Hop", "slug": "glitch-hop", "type": "subgenre", } ], } ] return results def setUp(self): self.setup_beets() self.load_plugins("beatport") self.lib = library.Library(":memory:") # Set up 'tracks'. self.response_tracks = self._make_tracks_response() self.tracks = [beatport.BeatportTrack(t) for t in self.response_tracks] # Make alias to be congruent with class `BeatportTest`. self.test_tracks = self.response_tracks def tearDown(self): self.unload_plugins() self.teardown_beets() def test_response_tracks_empty(self): response_tracks = [] tracks = [beatport.BeatportTrack(t) for t in response_tracks] self.assertEqual(tracks, []) def test_sub_genre_empty_fallback(self): """No 'sub_genre' is provided. Test if fallback to 'genre' works.""" self.response_tracks[0]["subGenres"] = [] tracks = [beatport.BeatportTrack(t) for t in self.response_tracks] self.test_tracks[0]["subGenres"] = [] self.assertEqual( tracks[0].genre, self.test_tracks[0]["genres"][0]["name"] ) def test_genre_empty(self): """No 'genre' is provided. Test if 'sub_genre' is applied.""" self.response_tracks[0]["genres"] = [] tracks = [beatport.BeatportTrack(t) for t in self.response_tracks] self.test_tracks[0]["genres"] = [] self.assertEqual( tracks[0].genre, self.test_tracks[0]["subGenres"][0]["name"] ) def suite(): return unittest.TestLoader().loadTestsFromName(__name__) if __name__ == "__main__": unittest.main(defaultTest="suite")