diff --git a/config b/config index 4162743a..63adc747 100644 --- a/config +++ b/config @@ -34,16 +34,16 @@ class Config: if self.TEST: env.Append( CCFLAGS = [ "-DCONFIG_TEST" ]) def reset(self, TEST = False): - self.AUDIO = False - self.DATABASE = False - self.DECK = False - self.FILE = False - self.FILTER = False - self.GROUP = False - self.IDLE = False - self.LIBRARY = False - self.PLAYLIST = False - self.TEST = TEST + self.AUDIO = False + self.DATABASE = False + self.DECK = False + self.FILE = False + self.FILTER = False + self.IDLE = False + self.LIBRARY = False + self.PLAYLIST = False + self.PLAYQUEUE = False + self.TEST = TEST self.reconfigure() CONFIG = Config() diff --git a/design.txt b/design.txt index e4a13c3d..0e7867e7 100644 --- a/design.txt +++ b/design.txt @@ -496,9 +496,9 @@ Idle queue: (lib/idle.cpp) Playlists: (lib/playlist.cpp) - Playlists are going to be a new feature in Ocarina 6 and can compare - directly to Gmail-style labels. Ocarina 6.0 will support two different - playlists that the user can add songs to: banned and favorites. + Playlists are a new feature in Ocarina 6 and are modeled after Gmail + labels. Ocarina 6.0 will support two different playlists that the + user can add tracks to: banned and favorites. Future releases will add support for more playlists. @@ -508,24 +508,35 @@ Playlists: (lib/playlist.cpp) - Default playlists: Favorites: The user will add music they really like to this playlist. + Banned: The user should add music they do not like to this playlist. - Tracks should be removed from the Library playlist when they - are banned and added back to the playlist when they are + Tracks should be removed from the Library playqueue when they + are banned and added back to the playqueue when they are un-banned. - API + void playlist :: init(): + Load the playlist database. + void playlist :: add(name, track_id); - playlist_idx.insert(name, track_id); + Add the track_id to the playlist named "name". Save the + database to disk. + Throw -EEXIST if "name" does not exist. void playlist :: del(name, track_id); - playlist_idx.delete(name, track_id) + Remove the track_id from the playlist named "name". Save the + database to disk. Attempting to remove a track_id that does + not exist is not an error. + Throw -EEXIST if "name" does not exist. - void void playlist :: list(list &); - return playlist_idx.keys(); + const std::set &playlist :: get_tracks(name); + Return the set of tracks representing the requested group. + Throw -EEXIST if "name" does not exist. - void playlist :: get_tracks(name); - return playlist_idx[name] + void playlist :: clear(); + This function only exists if CONFIG_TEST is enabled. Clear + (reset) the playlist database. diff --git a/design/playlist.txt b/design/playlist.txt index 76156284..748a35e2 100644 --- a/design/playlist.txt +++ b/design/playlist.txt @@ -10,9 +10,9 @@ database Playlists: (lib/playlist.cpp) - Playlists are going to be a new feature in Ocarina 6 and can compare - directly to Gmail-style labels. Ocarina 6.0 will support two different - playlists that the user can add songs to: banned and favorites. + Playlists are a new feature in Ocarina 6 and are modeled after Gmail + labels. Ocarina 6.0 will support two different playlists that the + user can add tracks to: banned and favorites. Future releases will add support for more playlists. @@ -22,21 +22,32 @@ Playlists: (lib/playlist.cpp) - Default playlists: Favorites: The user will add music they really like to this playlist. + Banned: The user should add music they do not like to this playlist. - Tracks should be removed from the Library playlist when they - are banned and added back to the playlist when they are + Tracks should be removed from the Library playqueue when they + are banned and added back to the playqueue when they are un-banned. - API + void playlist :: init(): + Load the playlist database. + void playlist :: add(name, track_id); - playlist_idx.insert(name, track_id); + Add the track_id to the playlist named "name". Save the + database to disk. + Throw -EEXIST if "name" does not exist. void playlist :: del(name, track_id); - playlist_idx.delete(name, track_id) + Remove the track_id from the playlist named "name". Save the + database to disk. Attempting to remove a track_id that does + not exist is not an error. + Throw -EEXIST if "name" does not exist. - void void playlist :: list(list &); - return playlist_idx.keys(); + const std::set &playlist :: get_tracks(name); + Return the set of tracks representing the requested group. + Throw -EEXIST if "name" does not exist. - void playlist :: get_tracks(name); - return playlist_idx[name] + void playlist :: clear(); + This function only exists if CONFIG_TEST is enabled. Clear + (reset) the playlist database. diff --git a/include/database.h b/include/database.h index 3c4cd6b1..7a93f835 100644 --- a/include/database.h +++ b/include/database.h @@ -82,7 +82,7 @@ static inline void index_insert(Database &db, { try { db.find(key).insert(val); - } catch (...) { + } catch (int error) { db.insert(IndexEntry(key, val)); } } diff --git a/include/playlist.h b/include/playlist.h index 9c49eaa4..eacbae95 100644 --- a/include/playlist.h +++ b/include/playlist.h @@ -1,21 +1,24 @@ /* * Copyright 2013 (c) Anna Schumaker. */ -#ifndef OCARINA_GROUP_H -#define OCARINA_GROUP_H +#ifndef OCARINA_PLAYLIST_H +#define OCARINA_PLAYLIST_H #include #include #include -namespace group +namespace playlist { + void init(); void add(const std::string &, unsigned int); void del(const std::string &, unsigned int); - void list(std::list &); const std::set &get_tracks(const std::string &); +#ifdef CONFIG_TEST + void clear(); +#endif /* CONFIG_TEST */ }; -#endif /* OCARINA_GROUP_H */ +#endif /* OCARINA_PLAYLIST_H */ diff --git a/lib/Sconscript b/lib/Sconscript index 097a7c66..6215d222 100644 --- a/lib/Sconscript +++ b/lib/Sconscript @@ -20,9 +20,9 @@ modules = { "DECK" : Module("deck.cpp", depends = [ "PLAYQUEUE" ]), "FILE" : Module("file.cpp", package = "glib-2.0"), "FILTER" : Module("filter.cpp", depends = [ "DATABASE" ]), - "PLAYLIST" : Module("playlist.cpp", depends = [ "DATABASE" ]), "IDLE" : Module("idle.cpp"), "LIBRARY" : Module("library.cpp", package = "taglib", depends = [ "DATABASE", "IDLE" ]), + "PLAYLIST" : Module("playlist.cpp", depends = [ "DATABASE" ]), "PLAYQUEUE" : Module("playlist.cpp", depends = [ "FILE" ]), ########################### diff --git a/lib/playlist.cpp b/lib/playlist.cpp index c11e6c98..35abd90a 100644 --- a/lib/playlist.cpp +++ b/lib/playlist.cpp @@ -2,45 +2,50 @@ * Copyright 2013 (c) Anna Schumaker. */ #include -#include +#include +#include static std::set empty_set; -Database group_index(""); +static Database playlist_db("playlist.db"); -void group :: add(const std::string &name, unsigned int track_id) +void playlist :: init() { - if ((name == "All Music") || (name == "Library") || (name == "Banned")) { + playlist_db.load(); +} + +void playlist :: add(const std::string &name, unsigned int track_id) +{ + if ((name == "Banned") || (name == "Favorites")) { + index_insert(playlist_db, name, track_id); + playlist_db.save(); + } else + throw -EEXIST; +} + +void playlist :: del(const std::string &name, unsigned int track_id) +{ + if ((name == "Banned") || (name == "Favorites")) { + index_remove(playlist_db, name, track_id); + playlist_db.save(); + } else + throw -EEXIST; +} + +const std::set &playlist :: get_tracks(const std::string &name) +{ + if ((name == "Banned") || (name == "Favorites")) { try { - index_insert(group_index, name, track_id); - } catch (...) { - return; + return playlist_db.find(name).values; + } catch (int error) { + return empty_set; } } + throw -EEXIST; } -void group :: del(const std::string &name, unsigned int track_id) +#ifdef CONFIG_TEST +void playlist :: clear() { - if ((name == "All Music") || (name == "Library") || (name == "Banned")) { - try { - index_remove(group_index, name, track_id); - } catch (...) { - return; - } - } -} - -void group :: list(std::list &res) -{ - res.push_back("All Music"); - res.push_back("Library"); - res.push_back("Banned"); -} - -const std::set &group :: get_tracks(const std::string &name) -{ - try { - return group_index.find(name).values; - } catch (...) { - return empty_set; - } + playlist_db.clear(); } +#endif /* CONFIG_TEST */ diff --git a/tests/Sconscript b/tests/Sconscript index 9280f150..e301e9b1 100644 --- a/tests/Sconscript +++ b/tests/Sconscript @@ -57,8 +57,9 @@ rm_test_dir(xdg.BaseDirectory.xdg_data_home); # # Read SConscript files # -scripts = [ "audio", "database", "deck", "file", "filter", "group", "idle", - "index", "library", "playlist", "print" ] +#scripts = [ "audio", "database", "deck", "file", "filter", "group", "idle", +# "index", "library", "playlist", "print" ] +scripts = [ "print", "file", "database", "index", "filter", "idle", "playlist" ] for s in scripts: CONFIG.reset(TEST = True) SConscript("%s/Sconscript" % s) diff --git a/tests/playlist/Sconscript b/tests/playlist/Sconscript index bf58917a..4c0ff2c3 100644 --- a/tests/playlist/Sconscript +++ b/tests/playlist/Sconscript @@ -1,6 +1,6 @@ #!/usr/bin/python Import("Test", "CONFIG") -CONFIG.GROUP = True +CONFIG.PLAYLIST = True -Test("group", "group.cpp") +Test("playlist", "playlist.cpp") diff --git a/tests/playlist/group.cpp b/tests/playlist/group.cpp deleted file mode 100644 index 71980def..00000000 --- a/tests/playlist/group.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013 (c) Anna Schumaker. - */ -#include -#include - - -void list_tracks(const std::string &name) -{ - std::set tracks = group :: get_tracks(name); - std::set::iterator it; - - print("Group \"%s\": ", name.c_str()); - for (it = tracks.begin(); it != tracks.end(); it++) { - if (it != tracks.begin()) - print(", "); - print("%u", *it); - } - - print("\n"); -} - -/* - * Add songs to different groups - */ -void test_0() -{ - for (unsigned int i = 0; i < 128; i++) { - group :: add("All Music", i); - if (i % 3 == 0) - group :: add("Banned", i); - else - group :: add("Library", i); - } -} - -/* - * Find group names - */ -void test_1() -{ - std::list groups; - std::list::iterator it; - - group :: list(groups); - for (it = groups.begin(); it != groups.end(); it++) - print("Found group: %s\n", it->c_str()); - print("\n"); -} - -/* - * Find tracks in a group - */ -void test_2() -{ - list_tracks("All Music"); - list_tracks("Library"); - list_tracks("Banned"); -} - -/* - * Delete tracks from a group - */ -void test_3() -{ - print("\n"); - for (unsigned int i = 0; i < 30; i+=3) - group :: del("Banned", i); - list_tracks("Banned"); -} - -/* - * Do stuff with groups that don't exist - */ -void test_4() -{ - print("\n"); - for (unsigned int i = 0; i < 10; i++) - group :: add("No Group", i); - - list_tracks("No Group"); - - for (unsigned int i = 0; i < 10; i+=2) - group :: del("No Group", i); - - list_tracks("No Group"); -} - -int main(int argc, char **argv) -{ - test_0(); - test_1(); - test_2(); - test_3(); - test_4(); - return 0; -} diff --git a/tests/playlist/group.good b/tests/playlist/group.good deleted file mode 100644 index 1567685f..00000000 --- a/tests/playlist/group.good +++ /dev/null @@ -1,12 +0,0 @@ -Found group: All Music -Found group: Library -Found group: Banned - -Group "All Music": 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127 -Group "Library": 1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 100, 101, 103, 104, 106, 107, 109, 110, 112, 113, 115, 116, 118, 119, 121, 122, 124, 125, 127 -Group "Banned": 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 - -Group "Banned": 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 - -Group "No Group": -Group "No Group": diff --git a/tests/playlist/playlist.cpp b/tests/playlist/playlist.cpp new file mode 100644 index 00000000..41b12f80 --- /dev/null +++ b/tests/playlist/playlist.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2013 (c) Anna Schumaker. + */ +#include +#include +#include + + +void list_tracks(const std::string &name) +{ + std::set tracks = playlist :: get_tracks(name); + std::set::iterator it; + + print("Playlist \"%s\": ", name.c_str()); + for (it = tracks.begin(); it != tracks.end(); it++) { + if (it != tracks.begin()) + print(", "); + print("%u", *it); + } + + print("\n"); +} + +void check_error(int expected, int error) +{ + if (expected != error) + print("Exception error: expected %d actual %d", expected, error); +} + +/* + * Add songs to different playlists + */ +void test_0() +{ + for (unsigned int i = 0; i < 128; i++) { + switch (i % 3) { + case 0: + playlist :: add("Banned", i); + break; + case 1: + playlist :: add("Favorites", i); + break; + default: + try { + playlist :: add("No Such Playlist", i); + } catch (int error) { + check_error(-EEXIST, error); + } + } + } +} + +/* + * Find tracks in a playlist + */ +void test_1() +{ + list_tracks("Banned"); + list_tracks("Favorites"); + + try { + list_tracks("No Such Playlist"); + } catch (int error) { + check_error(-EEXIST, error); + } +} + +/* + * Delete tracks from a playlist + */ +void test_2() +{ + print("\n"); + for (unsigned int i = 0; i < 30; i+=3) + playlist :: del("Banned", i); + list_tracks("Banned"); + + try { + playlist :: del("No Such Playlist", 2); + } catch (int error) { + check_error(-EEXIST, error); + } +} + +/* + * Check persistence of playlists + */ +void test_3() +{ + print("\n"); + + playlist :: clear(); + playlist :: init(); + + list_tracks("Banned"); + list_tracks("Favorites"); + + try { + list_tracks("No Schu Playlist"); + } catch (int error) { + check_error(-EEXIST, error); + } +} + +int main(int argc, char **argv) +{ + test_0(); + test_1(); + test_2(); + test_3(); + return 0; +} diff --git a/tests/playlist/playlist.good b/tests/playlist/playlist.good new file mode 100644 index 00000000..96e86e10 --- /dev/null +++ b/tests/playlist/playlist.good @@ -0,0 +1,7 @@ +Playlist "Banned": 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 +Playlist "Favorites": 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127 + +Playlist "Banned": 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 + +Playlist "Banned": 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126 +Playlist "Favorites": 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124, 127