def __init__(self, Kind, library): super(LibraryBrowser, self).__init__(dialog=False) self.set_default_size(600, 400) self.enable_window_tracking("browser_" + Kind.__name__) self.set_border_width(6) self.set_title(Kind.name + " - Quod Libet") self.add(gtk.VBox(spacing=6)) view = SongList(library, update=True) self.add_accel_group(view.accelerators) self.songlist = view sw = gtk.ScrolledWindow() sw.set_shadow_type(gtk.SHADOW_IN) sw.add(view) sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) self.browser = browser = Kind(library, False) if browser.reordered: view.enable_drop() elif browser.dropped: view.enable_drop(False) if browser.accelerators: self.add_accel_group(browser.accelerators) self.__container = browser.pack(sw) self.child.pack_start(self.__container) self.__statusbar = gtk.Label() self.__statusbar.set_text(_("No time information")) self.__statusbar.set_alignment(1.0, 0.5) self.__statusbar.set_ellipsize(pango.ELLIPSIZE_START) self.child.pack_end(self.__statusbar, expand=False) browser.connect('songs-selected', self.__browser_cb) browser.finalize(False) view.connect('popup-menu', self.__menu, library) view.connect('drag-data-received', self.__drag_data_recv) view.connect('row-activated', self.__enqueue) view.get_selection().connect('changed', self.__set_time) if browser.headers is not None: view.connect('columns-changed', self.__cols_changed, browser) self.__cols_changed(view, browser) sw.show_all() for c in self.child.get_children(): c.show() self.child.show() self.show() self.__set_pane_size()
class TSongList(TestCase): HEADERS = ["acolumn", "~#lastplayed", "~foo~bar", "~#rating", "~#length", "~dirname", "~#track"] def setUp(self): config.init() self.songlist = SongList(SongLibrary()) self.orders_changed = 0 def orders_changed_cb(*args): self.orders_changed += 1 self.songlist.connect("orders-changed", orders_changed_cb) def test_set_all_column_headers(self): SongList.set_all_column_headers(self.HEADERS) headers = [col.header_name for col in self.songlist.get_columns()] self.failUnlessEqual(headers, self.HEADERS) def test_set_column_headers(self): self.songlist.set_column_headers(self.HEADERS) headers = [col.header_name for col in self.songlist.get_columns()] self.failUnlessEqual(headers, self.HEADERS) def test_drop(self): self.songlist.enable_drop() self.songlist.disable_drop() def test_sort_by(self): self.songlist.set_column_headers(["one", "two", "three"]) for key, order in [("one", True), ("two", False), ("three", False)]: self.songlist.set_sort_orders([(key, order)]) self.failUnlessEqual( self.songlist.get_sort_orders(), [(key, order)]) self.songlist.toggle_column_sort(self.songlist.get_columns()[-1]) self.failUnlessEqual( self.songlist.get_sort_orders(), [("three", True)]) def test_sort_orders(self): s = self.songlist s.set_column_headers(["foo", "quux", "bar"]) values = [("foo", True), ("bar", False)] s.set_sort_orders(values) self.assertEqual(s.get_sort_orders(), values) s.toggle_column_sort(s.get_columns()[1], replace=False) self.assertEqual(s.get_sort_orders(), values + [("quux", False)]) s.toggle_column_sort(s.get_columns()[1], replace=True) self.assertEqual(s.get_sort_orders(), [("quux", False)]) def test_toggle_sort(self): s = self.songlist s.set_column_headers(["foo"]) self.assertEqual(self.orders_changed, 1) s.toggle_column_sort(s.get_columns()[0], replace=True) self.assertEqual(self.orders_changed, 2) self.assertEqual(s.get_sort_orders(), [("foo", False)]) s.toggle_column_sort(s.get_columns()[0], replace=True) self.assertEqual(self.orders_changed, 3) self.assertEqual(s.get_sort_orders(), [("foo", True)]) def test_clear_sort(self): s = self.songlist s.set_column_headers(["foo"]) s.toggle_column_sort(s.get_columns()[0], replace=True) self.assertTrue(s.get_sort_orders()) s.clear_sort() self.assertFalse(s.get_sort_orders()) def test_not_sortable(self): s = self.songlist s.sortable = False s.set_column_headers(["foo"]) s.toggle_column_sort(s.get_columns()[0]) self.assertEqual(self.orders_changed, 0) self.assertFalse(s.get_sort_orders()) def test_find_default_sort_column(self): s = self.songlist self.assertTrue(s.find_default_sort_column() is None) s.set_column_headers(["~#track"]) self.assertTrue(s.find_default_sort_column()) def test_inline_search_state(self): self.assertEqual(self.songlist.get_search_column(), 0) self.assertTrue(self.songlist.get_enable_search()) def test_set_songs(self): self.songlist.set_songs([], sorted=True) self.songlist.set_songs([], sorted=False) self.songlist.set_songs([], scroll_select=True) self.songlist.set_songs([], scroll_select=False) self.songlist.set_songs([], scroll=True) self.songlist.set_songs([], scroll=False) def test_set_songs_restore_select(self): song = AudioFile({"~filename": "/dev/null"}) self.songlist.add_songs([song]) sel = self.songlist.get_selection() sel.select_path(Gtk.TreePath.new_first()) self.songlist.set_songs([song], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), [song]) song2 = AudioFile({"~filename": "/dev/null"}) self.songlist.set_songs([song2], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), []) def test_set_songs_no_restore_select(self): song = AudioFile({"~filename": "/dev/null"}) self.songlist.add_songs([song]) model = self.songlist.get_model() model.go_to(song) self.assertIs(model.current, song) # only restore if there was a selected one self.songlist.set_songs([song], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), []) def test_get_selected_songs(self): song = AudioFile({"~filename": "/dev/null"}) self.songlist.add_songs([song]) sel = self.songlist.get_selection() sel.select_path(Gtk.TreePath.new_first()) self.assertEqual(self.songlist.get_selected_songs(), [song]) self.assertEqual(self.songlist.get_first_selected_song(), song) sel.unselect_all() self.assertEqual(self.songlist.get_selected_songs(), []) self.assertIs(self.songlist.get_first_selected_song(), None) def test_add_songs(self): song = AudioFile({"~filename": "/dev/null"}) # unsorted self.songlist.add_songs([song]) self.songlist.add_songs([song]) # sorted self.songlist.set_column_headers(["foo"]) self.songlist.toggle_column_sort(self.songlist.get_columns()[0]) self.songlist.add_songs([]) self.songlist.add_songs([song]) self.songlist.add_songs([song]) self.assertEqual(self.songlist.get_songs(), [song] * 4) def test_header_menu(self): from quodlibet import browsers from quodlibet.library import SongLibrary, SongLibrarian song = AudioFile({"~filename": fsnative(u"/dev/null")}) song.sanitize() self.songlist.set_songs([song]) library = SongLibrary() library.librarian = SongLibrarian() browser = browsers.get("SearchBar")(library) self.songlist.set_column_headers(["foo"]) self.assertFalse(self.songlist.Menu("foo", browser, library)) sel = self.songlist.get_selection() sel.select_all() self.assertTrue(self.songlist.Menu("foo", browser, library)) def test_get_columns_migrates(self): self.failIf(config.get("settings", "headers", None)) self.failIf(config.get("settings", "columns", None)) headers = "~album ~#replaygain_track_gain foobar" config.set("settings", "headers", headers) columns = get_columns() self.failUnlessEqual(columns, ["~album", "~#replaygain_track_gain", "foobar"]) self.failIf(config.get("settings", "headers", None)) def test_get_set_columns(self): self.failIf(config.get("settings", "headers", None)) self.failIf(config.get("settings", "columns", None)) columns = ["first", "won't", "two words", "4"] set_columns(columns) self.failUnlessEqual(columns, get_columns()) columns += ["~~another~one"] set_columns(columns) self.failUnlessEqual(columns, get_columns()) self.failIf(config.get("settings", "headers", None)) def test_header_tag_split(self): self.assertEqual(header_tag_split("foo"), ["foo"]) self.assertEqual(header_tag_split("~foo~bar"), ["foo", "bar"]) self.assertEqual(header_tag_split("<foo>"), ["foo"]) self.assertEqual(header_tag_split("<~foo~bar>"), ["foo", "bar"]) self.assertEqual(header_tag_split("pattern <~foo~bar>"), ["foo", "bar"]) def test_get_sort_tag(self): self.assertEqual(get_sort_tag("~#track"), "") self.assertEqual(get_sort_tag("artist"), "artistsort") self.assertEqual(get_sort_tag("date"), "date") self.assertEqual(get_sort_tag("~artist~date"), "~artistsort~date") self.assertEqual(get_sort_tag("~date~artist"), "~date~artistsort") self.assertEqual(get_sort_tag("composer"), "composersort") def tearDown(self): self.songlist.destroy() config.quit()
class TEventPlugins(TestCase): def setUp(self): self.tempdir = mkdtemp() self.pm = PluginManager(folders=[self.tempdir]) self.lib = SongLibrarian() lib = SongLibrary() lib.librarian = self.lib self.songlist = SongList(library=lib) self.player = player.init_player("nullbe", self.lib) self.handler = EventPluginHandler(librarian=self.lib, player=self.player, songlist=self.songlist) self.pm.register_handler(self.handler) self.pm.rescan() self.assertEquals(self.pm.plugins, []) def tearDown(self): self.pm.quit() shutil.rmtree(self.tempdir) def create_plugin(self, name='', funcs=None): fd, fn = mkstemp(suffix='.py', text=True, dir=self.tempdir) file = os.fdopen(fd, 'w') file.write("from quodlibet.plugins.events import EventPlugin\n") file.write("log = []\n") file.write("class %s(EventPlugin):\n" % name) indent = ' ' file.write("%spass\n" % indent) if name: file.write("%sPLUGIN_ID = %r\n" % (indent, name)) file.write("%sPLUGIN_NAME = %r\n" % (indent, name)) for f in (funcs or []): file.write("%sdef %s(s, *args): log.append((%r, args))\n" % (indent, f, f)) file.flush() file.close() def _get_calls(self, plugin): mod = sys.modules[plugin.cls.__module__] return mod.log def test_found(self): self.create_plugin(name='Name') self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) def test_player_paused(self): self.create_plugin(name='Name', funcs=["plugin_on_paused"]) self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) plugin = self.pm.plugins[0] self.pm.enable(plugin, True) self.player.emit("paused") self.failUnlessEqual([("plugin_on_paused", tuple())], self._get_calls(plugin)) def test_lib_changed(self): self.create_plugin(name='Name', funcs=["plugin_on_changed"]) self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) plugin = self.pm.plugins[0] self.pm.enable(plugin, True) self.lib.emit("changed", [None]) self.failUnlessEqual([("plugin_on_changed", ([None], ))], self._get_calls(plugin)) def test_songs_selected(self): self.create_plugin(name='Name', funcs=["plugin_on_songs_selected"]) self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) plugin = self.pm.plugins[0] self.pm.enable(plugin, True) self.songlist.emit("selection-changed", self.songlist.get_selection()) self.failUnlessEqual(self._get_calls(plugin), [("plugin_on_songs_selected", ([], ))])
class TSongList(TestCase): HEADERS = [ "acolumn", "~#lastplayed", "~foo~bar", "~#rating", "~#length", "~dirname", "~#track" ] def setUp(self): config.init() self.lib = SongFileLibrary() self.songlist = SongList(self.lib) assert not self.lib.librarian, "not expecting a librarian - leaky test?" self.orders_changed = 0 self.songs_removed: List[Set] = [] def orders_changed_cb(*args): self.orders_changed += 1 def orders_removed_cb(songlist, removed): self.songs_removed.append(removed) self.__sigs = [ self.songlist.connect("orders-changed", orders_changed_cb), self.songlist.connect("songs-removed", orders_removed_cb) ] def test_set_all_column_headers(self): SongList.set_all_column_headers(self.HEADERS) headers = [col.header_name for col in self.songlist.get_columns()] self.failUnlessEqual(headers, self.HEADERS) def test_set_column_headers(self): self.songlist.set_column_headers(self.HEADERS) headers = [col.header_name for col in self.songlist.get_columns()] self.failUnlessEqual(headers, self.HEADERS) def test_drop(self): self.songlist.enable_drop() self.songlist.disable_drop() def test_sort_by(self): self.songlist.set_column_headers(["one", "two", "three"]) for key, order in [("one", True), ("two", False), ("three", False)]: self.songlist.set_sort_orders([(key, order)]) self.failUnlessEqual(self.songlist.get_sort_orders(), [(key, order)]) self.songlist.toggle_column_sort(self.songlist.get_columns()[-1]) self.failUnlessEqual(self.songlist.get_sort_orders(), [("three", True)]) def test_sort_orders(self): s = self.songlist s.set_column_headers(["foo", "quux", "bar"]) values = [("foo", True), ("bar", False)] s.set_sort_orders(values) self.assertEqual(s.get_sort_orders(), values) s.toggle_column_sort(s.get_columns()[1], replace=False) self.assertEqual(s.get_sort_orders(), values + [("quux", False)]) s.toggle_column_sort(s.get_columns()[1], replace=True) self.assertEqual(s.get_sort_orders(), [("quux", False)]) def test_toggle_sort(self): s = self.songlist s.set_column_headers(["foo"]) self.assertEqual(self.orders_changed, 1) s.toggle_column_sort(s.get_columns()[0], replace=True) self.assertEqual(self.orders_changed, 2) self.assertEqual(s.get_sort_orders(), [("foo", False)]) s.toggle_column_sort(s.get_columns()[0], replace=True) self.assertEqual(self.orders_changed, 3) self.assertEqual(s.get_sort_orders(), [("foo", True)]) def test_clear_sort(self): s = self.songlist s.set_column_headers(["foo"]) s.toggle_column_sort(s.get_columns()[0], replace=True) self.assertTrue(s.get_sort_orders()) s.clear_sort() self.assertFalse(s.get_sort_orders()) def test_not_sortable(self): s = self.songlist s.sortable = False s.set_column_headers(["foo"]) s.toggle_column_sort(s.get_columns()[0]) self.assertEqual(self.orders_changed, 0) self.assertFalse(s.get_sort_orders()) def test_find_default_sort_column(self): s = self.songlist self.assertTrue(s.find_default_sort_column() is None) s.set_column_headers(["~#track"]) self.assertTrue(s.find_default_sort_column()) def test_inline_search_state(self): self.assertEqual(self.songlist.get_search_column(), 0) self.assertTrue(self.songlist.get_enable_search()) def test_set_songs(self): self.songlist.set_songs([], sorted=True) self.songlist.set_songs([], sorted=False) self.songlist.set_songs([], scroll_select=True) self.songlist.set_songs([], scroll_select=False) self.songlist.set_songs([], scroll=True) self.songlist.set_songs([], scroll=False) def test_set_songs_restore_select(self): song = AudioFile({"~filename": "/dev/null"}) self.songlist.add_songs([song]) sel = self.songlist.get_selection() sel.select_path(Gtk.TreePath.new_first()) self.songlist.set_songs([song], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), [song]) song2 = AudioFile({"~filename": "/dev/null"}) self.songlist.set_songs([song2], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), []) def test_set_songs_no_restore_select(self): song = AudioFile({"~filename": "/dev/null"}) self.songlist.add_songs([song]) model = self.songlist.get_model() model.go_to(song) self.assertIs(model.current, song) # only restore if there was a selected one self.songlist.set_songs([song], scroll_select=True) self.assertEqual(self.songlist.get_selected_songs(), []) def test_get_selected_songs(self): song = AudioFile({"~filename": "/dev/null"}) self.songlist.add_songs([song]) sel = self.songlist.get_selection() sel.select_path(Gtk.TreePath.new_first()) self.assertEqual(self.songlist.get_selected_songs(), [song]) self.assertEqual(self.songlist.get_first_selected_song(), song) sel.unselect_all() self.assertEqual(self.songlist.get_selected_songs(), []) self.assertIs(self.songlist.get_first_selected_song(), None) def test_add_songs(self): song = AudioFile({"~filename": "/dev/null"}) # unsorted self.songlist.add_songs([song]) self.songlist.add_songs([song]) # sorted self.songlist.set_column_headers(["foo"]) self.songlist.toggle_column_sort(self.songlist.get_columns()[0]) self.songlist.add_songs([]) self.songlist.add_songs([song]) self.songlist.add_songs([song]) self.assertEqual(self.songlist.get_songs(), [song] * 4) def test_remove_songs(self): song = AudioFile({"~filename": "/dev/null"}) song.sanitize() self.lib.add([song]) assert song in self.lib, "Broken library?" self.songlist.add_songs([song]) assert set(self.songlist.get_songs()) == {song} self.lib.remove([song]) assert not list(self.lib), "Didn't get removed" run_gtk_loop() assert self.songs_removed == [{song} ], f"Signal not emitted: {self.__sigs}" def test_header_menu(self): song = AudioFile({"~filename": fsnative(u"/dev/null")}) song.sanitize() self.songlist.set_songs([song]) library = self.lib librarian = SongLibrarian() librarian.register(self.lib, "test") self.lib.librarian = librarian browser = TrackList(library) self.songlist.set_column_headers(["foo"]) self.assertFalse(self.songlist.Menu("foo", browser, library)) sel = self.songlist.get_selection() sel.select_all() self.assertTrue(self.songlist.Menu("foo", browser, library)) librarian.destroy() self.lib.librarian = None def test_get_columns_migrated(self): self.failIf(config.get("settings", "headers", None)) columns = "~album,~#replaygain_track_gain,foobar" config.set("settings", "columns", columns) self.failUnlessEqual(get_columns(), ["~album", "~#replaygain_track_gain", "foobar"]) self.failIf(config.get("settings", "headers", None)) def test_get_set_columns(self): self.failIf(config.get("settings", "headers", None)) self.failIf(config.get("settings", "columns", None)) columns = ["first", "won't", "two words", "4"] set_columns(columns) self.failUnlessEqual(columns, get_columns()) columns += ["~~another~one"] set_columns(columns) self.failUnlessEqual(columns, get_columns()) self.failIf(config.get("settings", "headers", None)) def test_header_tag_split(self): self.assertEqual(header_tag_split("foo"), ["foo"]) self.assertEqual(header_tag_split("~foo~bar"), ["foo", "bar"]) self.assertEqual(header_tag_split("<foo>"), ["foo"]) self.assertEqual(header_tag_split("<~foo~bar>"), ["foo", "bar"]) self.assertEqual(header_tag_split("pattern <~foo~bar>"), ["foo", "bar"]) def test_get_sort_tag(self): self.assertEqual(get_sort_tag("~#track"), "") self.assertEqual(get_sort_tag("artist"), "artistsort") self.assertEqual(get_sort_tag("date"), "date") self.assertEqual(get_sort_tag("~artist~date"), "~artistsort~date") self.assertEqual(get_sort_tag("~date~artist"), "~date~artistsort") self.assertEqual(get_sort_tag("composer"), "composersort") self.assertEqual(get_sort_tag("originalartist"), "originalartistsort") def test_check_sensible_menu_items(self): col = SongListColumn("title") menu = self.songlist._menu(col) submenus = [item.get_submenu() for item in menu.get_children()] names = { item.get_label() for child in submenus if child and not isinstance(child, Gtk.SeparatorMenuItem) for item in child.get_children() } assert {"Title", "Genre", "Comment", "Artist"} < names def tearDown(self): for sig in self.__sigs: self.songlist.disconnect(sig) self.songlist.destroy() self.lib.destroy() config.quit()
class TEventPlugins(TestCase): def setUp(self): self.tempdir = mkdtemp() self.pm = PluginManager(folders=[self.tempdir]) self.lib = SongLibrarian() lib = SongLibrary() lib.librarian = self.lib self.songlist = SongList(library=lib) self.player = player.init_player("nullbe", self.lib) self.handler = EventPluginHandler( librarian=self.lib, player=self.player, songlist=self.songlist) self.pm.register_handler(self.handler) self.pm.rescan() self.assertEquals(self.pm.plugins, []) def tearDown(self): self.pm.quit() shutil.rmtree(self.tempdir) def create_plugin(self, name='', funcs=None): fd, fn = mkstemp(suffix='.py', text=True, dir=self.tempdir) file = os.fdopen(fd, 'w') file.write("from quodlibet.plugins.events import EventPlugin\n") file.write("log = []\n") file.write("class %s(EventPlugin):\n" % name) indent = ' ' file.write("%spass\n" % indent) if name: file.write("%sPLUGIN_ID = %r\n" % (indent, name)) file.write("%sPLUGIN_NAME = %r\n" % (indent, name)) for f in (funcs or []): file.write("%sdef %s(s, *args): log.append((%r, args))\n" % (indent, f, f)) file.flush() file.close() def _get_calls(self, plugin): mod = sys.modules[plugin.cls.__module__] return mod.log def test_found(self): self.create_plugin(name='Name') self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) def test_player_paused(self): self.create_plugin(name='Name', funcs=["plugin_on_paused"]) self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) plugin = self.pm.plugins[0] self.pm.enable(plugin, True) self.player.emit("paused") self.failUnlessEqual([("plugin_on_paused", tuple())], self._get_calls(plugin)) def test_lib_changed(self): self.create_plugin(name='Name', funcs=["plugin_on_changed"]) self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) plugin = self.pm.plugins[0] self.pm.enable(plugin, True) self.lib.emit("changed", [None]) self.failUnlessEqual([("plugin_on_changed", ([None],))], self._get_calls(plugin)) def test_songs_selected(self): self.create_plugin(name='Name', funcs=["plugin_on_songs_selected"]) self.pm.rescan() self.assertEquals(len(self.pm.plugins), 1) plugin = self.pm.plugins[0] self.pm.enable(plugin, True) self.songlist.emit("selection-changed", self.songlist.get_selection()) self.failUnlessEqual(self._get_calls(plugin), [("plugin_on_songs_selected", ([], ))])