484 lines
20 KiB
Python
484 lines
20 KiB
Python
# Copyright 2023 (c) Anna Schumaker.
|
|
"""Tests our Tracklist Selection OSD."""
|
|
import pathlib
|
|
import unittest
|
|
import emmental.buttons
|
|
import emmental.tracklist.selection
|
|
import tests.util
|
|
import unittest.mock
|
|
from gi.repository import Gtk
|
|
from gi.repository import Adw
|
|
|
|
|
|
class TestPlaylistRow(unittest.TestCase):
|
|
"""Test the PlaylistRow Widget."""
|
|
|
|
def setUp(self):
|
|
"""Set up common variables."""
|
|
cover = tests.util.COVER_JPG
|
|
self.row = emmental.tracklist.selection.PlaylistRow("name", cover)
|
|
|
|
def test_init(self):
|
|
"""Test that the PlaylistRow Widget is set up properly."""
|
|
self.assertIsInstance(self.row, Gtk.ListBoxRow)
|
|
self.assertIsInstance(self.row.props.child, Gtk.Box)
|
|
|
|
self.assertEqual(self.row.name, "name")
|
|
self.assertEqual(self.row.image, tests.util.COVER_JPG)
|
|
|
|
self.assertEqual(self.row.props.child.props.margin_start, 6)
|
|
self.assertEqual(self.row.props.child.props.margin_end, 6)
|
|
self.assertEqual(self.row.props.child.props.margin_top, 6)
|
|
self.assertEqual(self.row.props.child.props.margin_bottom, 6)
|
|
self.assertEqual(self.row.props.child.props.spacing, 6)
|
|
|
|
def test_label(self):
|
|
"""Test the PlaylistRow Widget label."""
|
|
self.assertIsInstance(self.row._label, Gtk.Label)
|
|
self.assertEqual(self.row._label.props.label, "name")
|
|
self.assertEqual(self.row._icon.get_next_sibling(), self.row._label)
|
|
|
|
def test_icon(self):
|
|
"""Test the PlaylistRow Widget icon."""
|
|
self.assertIsInstance(self.row._icon, Adw.Avatar)
|
|
self.assertEqual(self.row.props.child.get_first_child(),
|
|
self.row._icon)
|
|
self.assertEqual(self.row._icon.get_size(), 32)
|
|
self.assertEqual(self.row._icon.get_text(), "name")
|
|
self.assertEqual(self.row._icon.get_icon_name(),
|
|
"playlist2-symbolic")
|
|
|
|
fav = emmental.tracklist.selection.PlaylistRow("Favorite Tracks", None)
|
|
self.assertEqual(fav._icon.props.icon_name, "heart-filled-symbolic")
|
|
|
|
queue = emmental.tracklist.selection.PlaylistRow("Queued Tracks", None)
|
|
self.assertEqual(queue._icon.props.icon_name, "music-queue-symbolic")
|
|
|
|
def test_image(self):
|
|
"""Test the PlaylistRow Widget image."""
|
|
self.assertIsNotNone(self.row._icon.props.custom_image)
|
|
|
|
none = emmental.tracklist.selection.PlaylistRow("none", None)
|
|
self.assertIsNone(none.image)
|
|
self.assertIsNone(none._icon.props.custom_image)
|
|
|
|
later = emmental.tracklist.selection.PlaylistRow("later", None)
|
|
later.image = tests.util.COVER_JPG
|
|
self.assertIsNotNone(later._icon.props.custom_image)
|
|
|
|
path = pathlib.Path("/a/b/c.jpg")
|
|
inval = emmental.tracklist.selection.PlaylistRow("inval", path)
|
|
self.assertIsNone(inval._icon.props.custom_image)
|
|
|
|
|
|
class TestUserTracksFilter(tests.util.TestCase):
|
|
"""Test the UserTracksFilter."""
|
|
|
|
def setUp(self):
|
|
"""Set up common variables."""
|
|
super().setUp()
|
|
self.sql.playlists.load(now=True)
|
|
self.filter = emmental.tracklist.selection.UserTracksFilter()
|
|
self.db_plist = self.sql.playlists.create("Test Playlist")
|
|
|
|
def test_init(self):
|
|
"""Test that the filter is initialized properly."""
|
|
self.assertIsInstance(self.filter, Gtk.Filter)
|
|
self.assertIsNone(self.filter.playlist)
|
|
|
|
def test_changed(self):
|
|
"""Test the filter changed property."""
|
|
changed = unittest.mock.Mock()
|
|
self.filter.connect("changed", changed)
|
|
self.filter.playlist = self.db_plist
|
|
changed.assert_called_with(self.filter, Gtk.FilterChange.DIFFERENT)
|
|
|
|
def test_match(self):
|
|
"""Test the filter match function."""
|
|
self.assertTrue(self.filter.match(self.db_plist))
|
|
|
|
self.db_plist.user_tracks = False
|
|
self.assertFalse(self.filter.match(self.db_plist))
|
|
|
|
self.filter.playlist = self.db_plist
|
|
self.db_plist.user_tracks = True
|
|
self.assertFalse(self.filter.match(self.db_plist))
|
|
|
|
|
|
class TestPlaylistView(tests.util.TestCase):
|
|
"""Test the PlaylistView widget."""
|
|
|
|
def setUp(self):
|
|
"""Set up common variables."""
|
|
super().setUp()
|
|
self.sql.playlists.load(now=True)
|
|
self.view = emmental.tracklist.selection.PlaylistView(self.sql)
|
|
|
|
def test_init(self):
|
|
"""Test that the Playlist View is set up properly."""
|
|
self.assertIsInstance(self.view, Gtk.ListBox)
|
|
self.assertEqual(self.view.props.selection_mode,
|
|
Gtk.SelectionMode.NONE)
|
|
self.assertTrue(self.view.has_css_class("boxed-list"))
|
|
|
|
def test_filter_model(self):
|
|
"""Test that the filter model has been connected correctly."""
|
|
self.assertIsInstance(self.view._filtered, Gtk.FilterListModel)
|
|
self.assertIsInstance(self.view._filtered.get_filter(),
|
|
emmental.tracklist.selection.UserTracksFilter)
|
|
self.assertEqual(self.view._filtered.get_model(), self.sql.playlists)
|
|
|
|
self.view.playlist = self.sql.playlists.collection
|
|
self.assertEqual(self.view._filtered.get_filter().playlist,
|
|
self.sql.playlists.collection)
|
|
|
|
def test_create_func(self):
|
|
"""Test that the PlaylistView creates PlaylistRows correctly."""
|
|
row = self.view.get_row_at_index(0)
|
|
self.assertIsInstance(row, emmental.tracklist.selection.PlaylistRow)
|
|
self.assertEqual(row.name, "Favorite Tracks")
|
|
self.assertEqual(row.image, None)
|
|
|
|
self.sql.playlists.favorites.image = tests.util.COVER_JPG
|
|
self.assertEqual(row.image, tests.util.COVER_JPG)
|
|
|
|
def test_activate(self):
|
|
"""Test activating a Playlist Row for adding tracks."""
|
|
selected = unittest.mock.Mock()
|
|
self.view.connect("playlist-selected", selected)
|
|
|
|
self.view.emit("row-activated", self.view.get_row_at_index(0))
|
|
selected.assert_called_with(self.view, self.sql.playlists.favorites)
|
|
|
|
|
|
class TestMoveButtons(unittest.TestCase):
|
|
"""Test the Tracklist move up & down buttons."""
|
|
|
|
def setUp(self):
|
|
"""Set up common variables."""
|
|
self.move = emmental.tracklist.selection.MoveButtons()
|
|
|
|
def test_init(self):
|
|
"""Test that the move buttons were set up properly."""
|
|
self.assertIsInstance(self.move, Gtk.Box)
|
|
self.assertTrue(self.move.has_css_class("emmental-move-buttons"))
|
|
self.assertTrue(self.move.has_css_class("large-icons"))
|
|
self.assertTrue(self.move.has_css_class("linked"))
|
|
|
|
def test_move_down(self):
|
|
"""Test the move down button."""
|
|
self.assertIsInstance(self.move._down, Gtk.Button)
|
|
self.assertEqual(self.move._down.get_icon_name(), "go-down-symbolic")
|
|
self.assertEqual(self.move._down.get_tooltip_text(),
|
|
"move selected track down")
|
|
self.assertTrue(self.move._down.has_css_class("opaque"))
|
|
self.assertTrue(self.move._down.has_css_class("pill"))
|
|
self.assertTrue(self.move._down.get_hexpand())
|
|
|
|
move_down = unittest.mock.Mock()
|
|
self.move.connect("move-down", move_down)
|
|
self.move._down.emit("clicked")
|
|
move_down.assert_called()
|
|
|
|
self.assertEqual(self.move._up.get_next_sibling(), self.move._down)
|
|
|
|
def test_can_move_down(self):
|
|
"""Test the can-move-down property."""
|
|
self.assertFalse(self.move.can_move_down)
|
|
self.assertFalse(self.move._down.get_sensitive())
|
|
|
|
self.move.can_move_down = True
|
|
self.assertTrue(self.move._down.get_sensitive())
|
|
self.move.can_move_down = False
|
|
self.assertFalse(self.move._down.get_sensitive())
|
|
|
|
def test_move_up(self):
|
|
"""Test the move up button."""
|
|
self.assertIsInstance(self.move._up, Gtk.Button)
|
|
self.assertEqual(self.move._up.get_icon_name(), "go-up-symbolic")
|
|
self.assertEqual(self.move._up.get_tooltip_text(),
|
|
"move selected track up")
|
|
self.assertTrue(self.move._up.has_css_class("opaque"))
|
|
self.assertTrue(self.move._up.has_css_class("pill"))
|
|
self.assertTrue(self.move._up.get_hexpand())
|
|
|
|
move_up = unittest.mock.Mock()
|
|
self.move.connect("move-up", move_up)
|
|
self.move._up.emit("clicked")
|
|
move_up.assert_called()
|
|
|
|
self.assertEqual(self.move.get_first_child(), self.move._up)
|
|
|
|
def test_can_move_up(self):
|
|
"""Test the can-move-up property."""
|
|
self.assertFalse(self.move.can_move_up)
|
|
self.assertFalse(self.move._up.get_sensitive())
|
|
|
|
self.move.can_move_up = True
|
|
self.assertTrue(self.move._up.get_sensitive())
|
|
self.move.can_move_up = False
|
|
self.assertFalse(self.move._up.get_sensitive())
|
|
|
|
|
|
class TestOsd(tests.util.TestCase):
|
|
"""Test the Tracklist OSD."""
|
|
|
|
def setUp(self):
|
|
"""Set up common variables."""
|
|
super().setUp()
|
|
self.db_plist = self.sql.playlists.create("Test Playlist")
|
|
self.playlist = emmental.playlist.playlist.Playlist(self.sql,
|
|
self.db_plist)
|
|
self.model = Gtk.StringList.new(["Test", "OSD", "Strings"])
|
|
self.selection = Gtk.MultiSelection(model=self.model)
|
|
self.osd = emmental.tracklist.selection.OSD(self.sql, self.selection)
|
|
|
|
def test_init(self):
|
|
"""Test that the OSD is set up properly."""
|
|
self.assertIsInstance(self.osd, Gtk.Overlay)
|
|
self.assertIsInstance(self.osd._sizegroup, Gtk.SizeGroup)
|
|
self.assertEqual(self.osd.selection, self.selection)
|
|
self.assertEqual(self.osd.sql, self.sql)
|
|
self.assertIsNone(self.osd.playlist)
|
|
|
|
self.assertEqual(self.osd._sizegroup.get_mode(),
|
|
Gtk.SizeGroupMode.HORIZONTAL)
|
|
|
|
def test_add_button(self):
|
|
"""Test the add tracks button."""
|
|
self.assertIsInstance(self.osd._add, emmental.buttons.PopoverButton)
|
|
self.assertIsInstance(self.osd._add.get_child(), Adw.ButtonContent)
|
|
self.assertIsInstance(self.osd._add.popover_child,
|
|
emmental.tracklist.selection.PlaylistView)
|
|
self.assertEqual(self.osd._add.get_child().get_icon_name(),
|
|
"list-add-symbolic")
|
|
self.assertEqual(self.osd._add.get_child().get_label(), "Add")
|
|
self.assertEqual(self.osd._add.get_tooltip_text(),
|
|
"add selected tracks to a playlist")
|
|
self.assertEqual(self.osd._add.get_halign(), Gtk.Align.START)
|
|
self.assertEqual(self.osd._add.get_valign(), Gtk.Align.END)
|
|
self.assertEqual(self.osd._add.get_margin_start(), 16)
|
|
self.assertEqual(self.osd._add.get_margin_bottom(), 16)
|
|
self.assertEqual(self.osd._add.get_direction(), Gtk.ArrowType.UP)
|
|
self.assertFalse(self.osd._add.get_visible())
|
|
|
|
self.assertTrue(self.osd._add.has_css_class("suggested-action"))
|
|
self.assertTrue(self.osd._add.has_css_class("pill"))
|
|
|
|
self.assertIn(self.osd._add, self.osd)
|
|
self.assertIn(self.osd._add, self.osd._sizegroup.get_widgets())
|
|
|
|
self.osd.playlist = self.playlist
|
|
self.osd.reset()
|
|
self.assertEqual(self.osd._add.popover_child.playlist, self.db_plist)
|
|
|
|
def test_add_button_visible(self):
|
|
"""Test the add button visiblity."""
|
|
self.assertFalse(self.osd._add.get_visible())
|
|
|
|
self.selection.select_item(0, True)
|
|
self.assertFalse(self.osd._add.get_visible())
|
|
|
|
self.osd.playlist = self.playlist
|
|
self.selection.select_item(1, True)
|
|
self.assertTrue(self.osd._remove.get_visible())
|
|
|
|
def test_add_button_activate(self):
|
|
"""Test activating a playlist in the add button."""
|
|
with unittest.mock.patch.object(self.db_plist,
|
|
"add_track") as mock_add:
|
|
self.selection.select_all()
|
|
with unittest.mock.patch.object(self.sql, "commit") as mock_commit:
|
|
self.osd._add.popover_child.emit("playlist-selected",
|
|
self.db_plist)
|
|
mock_add.assert_has_calls([unittest.mock.call(self.model[0]),
|
|
unittest.mock.call(self.model[1]),
|
|
unittest.mock.call(self.model[2])])
|
|
mock_commit.assert_called_once()
|
|
self.assertEqual(self.osd.n_selected, 0)
|
|
|
|
def test_remove_button(self):
|
|
"""Test the remove tracks button."""
|
|
self.assertIsInstance(self.osd._remove, Gtk.Button)
|
|
self.assertIsInstance(self.osd._remove.get_child(), Adw.ButtonContent)
|
|
self.assertEqual(self.osd._remove.get_child().get_icon_name(),
|
|
"list-remove-symbolic")
|
|
self.assertEqual(self.osd._remove.get_child().get_label(), "Remove")
|
|
self.assertEqual(self.osd._remove.get_tooltip_text(),
|
|
"remove selected tracks")
|
|
self.assertEqual(self.osd._remove.get_halign(), Gtk.Align.END)
|
|
self.assertEqual(self.osd._remove.get_valign(), Gtk.Align.END)
|
|
self.assertEqual(self.osd._remove.get_margin_end(), 16)
|
|
self.assertEqual(self.osd._remove.get_margin_bottom(), 16)
|
|
|
|
self.assertTrue(self.osd._remove.has_css_class("destructive-action"))
|
|
self.assertTrue(self.osd._remove.has_css_class("pill"))
|
|
|
|
self.assertIn(self.osd._remove, self.osd)
|
|
self.assertIn(self.osd._remove, self.osd._sizegroup.get_widgets())
|
|
|
|
def test_remove_button_visible(self):
|
|
"""Test the remove button visiblity."""
|
|
self.assertFalse(self.osd._remove.get_visible())
|
|
|
|
self.selection.select_item(0, True)
|
|
self.assertFalse(self.osd._remove.get_visible())
|
|
|
|
self.db_plist.user_tracks = False
|
|
self.osd.playlist = self.playlist
|
|
self.selection.select_item(1, True)
|
|
self.assertFalse(self.osd._remove.get_visible())
|
|
|
|
self.db_plist.user_tracks = True
|
|
self.selection.select_item(2, True)
|
|
self.assertTrue(self.osd._remove.get_visible())
|
|
|
|
def test_remove_button_click(self):
|
|
"""Test clicking the remove button."""
|
|
with unittest.mock.patch.object(self.db_plist,
|
|
"remove_track") as mock_remove:
|
|
with unittest.mock.patch.object(self.sql, "commit") as mock_commit:
|
|
self.selection.select_all()
|
|
self.osd._remove.emit("clicked")
|
|
mock_remove.assert_not_called()
|
|
mock_commit.assert_not_called()
|
|
|
|
self.osd.playlist = self.playlist
|
|
self.selection.select_all()
|
|
self.osd._remove.emit("clicked")
|
|
mock_remove.assert_has_calls([unittest.mock.call(self.model[i])
|
|
for i in range(3)])
|
|
mock_commit.assert_called_once()
|
|
|
|
def test_move_buttons(self):
|
|
"""Test the move buttons."""
|
|
self.assertIsInstance(self.osd._move,
|
|
emmental.tracklist.selection.MoveButtons)
|
|
self.assertEqual(self.osd._move.get_halign(), Gtk.Align.CENTER)
|
|
self.assertEqual(self.osd._move.get_valign(), Gtk.Align.END)
|
|
self.assertEqual(self.osd._move.get_margin_bottom(), 16)
|
|
|
|
self.assertIn(self.osd._move, self.osd._sizegroup.get_widgets())
|
|
self.assertIn(self.osd._move, self.osd)
|
|
|
|
def test_move_button_clicks(self):
|
|
"""Test clicking the move buttons."""
|
|
set_incremental = unittest.mock.Mock()
|
|
self.model.set_incremental = set_incremental
|
|
self.osd.playlist = self.playlist
|
|
self.selection.select_item(1, True)
|
|
|
|
with unittest.mock.patch.object(self.playlist,
|
|
"move_track_down") as mock_move_down:
|
|
with unittest.mock.patch.object(self.sql, "commit") as mock_commit:
|
|
self.osd._move.emit("move-down")
|
|
mock_move_down.assert_called_with(self.model[1])
|
|
set_incremental.assert_has_calls([unittest.mock.call(False),
|
|
unittest.mock.call(True)])
|
|
mock_commit.assert_called_once()
|
|
|
|
set_incremental.reset_mock()
|
|
with unittest.mock.patch.object(self.playlist,
|
|
"move_track_up") as mock_move_up:
|
|
with unittest.mock.patch.object(self.sql, "commit") as mock_commit:
|
|
self.osd._move.emit("move-up")
|
|
mock_move_up.assert_called_with(self.model[1])
|
|
set_incremental.assert_has_calls([unittest.mock.call(False),
|
|
unittest.mock.call(True)])
|
|
mock_commit.assert_called_once()
|
|
|
|
def test_move_buttons_sensitive(self):
|
|
"""Test the move button sensitivity."""
|
|
self.assertFalse(self.osd._move.get_sensitive())
|
|
|
|
self.osd.playlist = self.playlist
|
|
self.selection.select_item(0, True)
|
|
self.assertTrue(self.osd._move.get_sensitive())
|
|
self.assertTrue(self.osd._move.can_move_down)
|
|
self.assertFalse(self.osd._move.can_move_up)
|
|
|
|
self.selection.select_item(1, True)
|
|
self.assertTrue(self.osd._move.get_sensitive())
|
|
self.assertTrue(self.osd._move.can_move_down)
|
|
self.assertTrue(self.osd._move.can_move_up)
|
|
|
|
self.selection.select_item(2, True)
|
|
self.assertTrue(self.osd._move.get_sensitive())
|
|
self.assertFalse(self.osd._move.can_move_down)
|
|
self.assertTrue(self.osd._move.can_move_up)
|
|
|
|
self.selection.select_item(1, False)
|
|
self.assertFalse(self.osd._move.get_sensitive())
|
|
|
|
def test_move_buttons_visible(self):
|
|
"""Test the move button visibility."""
|
|
self.assertFalse(self.osd._move.get_visible())
|
|
|
|
self.selection.select_item(0, True)
|
|
self.assertFalse(self.osd._move.get_visible())
|
|
|
|
self.osd.playlist = self.playlist
|
|
self.selection.select_item(1, True)
|
|
self.assertTrue(self.osd._move.get_visible())
|
|
|
|
self.db_plist.tracks_movable = False
|
|
self.selection.select_item(2, True)
|
|
self.assertFalse(self.osd._move.get_visible())
|
|
|
|
def test_selection_properties(self):
|
|
"""Test updating properties when the selection changes."""
|
|
self.assertFalse(self.osd.have_selected)
|
|
self.assertEqual(self.osd.n_selected, 0)
|
|
|
|
self.selection.select_item(1, True)
|
|
self.assertTrue(self.osd.have_selected)
|
|
self.assertEqual(self.osd.n_selected, 1)
|
|
|
|
def test_clear_selection(self):
|
|
"""Test the clear_selection() function."""
|
|
self.selection.select_item(1, True)
|
|
self.osd.clear_selection()
|
|
self.assertEqual(self.selection.get_selection().get_size(), 0)
|
|
self.assertEqual(self.osd.n_selected, 0)
|
|
self.assertFalse(self.osd.have_selected)
|
|
|
|
self.osd.playlist = self.playlist
|
|
self.assertEqual(self.selection.get_selection().get_size(), 0)
|
|
self.assertEqual(self.osd.n_selected, 0)
|
|
self.assertFalse(self.osd.have_selected)
|
|
|
|
def test_reset(self):
|
|
"""Test the reset() function."""
|
|
self.osd.have_selected = True
|
|
|
|
with unittest.mock.patch.object(self.selection,
|
|
"unselect_all") as mock_unselect:
|
|
self.osd.reset()
|
|
mock_unselect.assert_called()
|
|
self.assertFalse(self.osd.have_selected)
|
|
|
|
def test_accelerators(self):
|
|
"""Test that the accelerators list is set up properly."""
|
|
entries = [("remove-selected-tracks", self.osd._remove.activate,
|
|
["Delete"], self.osd._remove, "visible"),
|
|
("move-track-up", self.osd._move._up.activate,
|
|
["<Control>Up"], self.osd._move, "can-move-up"),
|
|
("move-track-down", self.osd._move._down.activate,
|
|
["<Control>Down"], self.osd._move, "can-move-down")]
|
|
|
|
accels = self.osd.accelerators
|
|
self.assertIsInstance(accels, list)
|
|
|
|
for i, (name, func, accel, gobject, prop) in enumerate(entries):
|
|
with self.subTest(action=name):
|
|
self.assertIsInstance(accels[i], emmental.action.ActionEntry)
|
|
self.assertEqual(accels[i].name, name)
|
|
self.assertEqual(accels[i].func, func)
|
|
self.assertEqual(accels[i].accels, accel)
|
|
|
|
if gobject and prop:
|
|
enabled = gobject.get_property(prop)
|
|
self.assertEqual(accels[i].enabled, enabled)
|
|
gobject.set_property(prop, not enabled)
|
|
self.assertEqual(accels[i].enabled, not enabled)
|