# 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 test import _common from test.helper import TestHelper from datetime import timedelta from beetsplug import beatport from beets import library 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')