From 6779535cf12270e0f27def724bda1de60a2112bd Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 14 Mar 2024 10:43:44 -0400 Subject: [PATCH] listenbrainz: Early startup handling We can't craft a Listen object from a Track before the database is almost entirely loaded, and attempting to do so will cause the ListenBrainz thread to crash. So let's just defer any ListenBrainz operations until the database has marked itself as "loaded" so we know everything will work. Signed-off-by: Anna Schumaker --- emmental/listenbrainz/__init__.py | 2 +- tests/listenbrainz/test_listenbrainz.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/emmental/listenbrainz/__init__.py b/emmental/listenbrainz/__init__.py index 95fd599..f8113f9 100644 --- a/emmental/listenbrainz/__init__.py +++ b/emmental/listenbrainz/__init__.py @@ -64,7 +64,7 @@ class ListenBrainz(GObject.GObject): return GLib.SOURCE_CONTINUE def __idle_work(self) -> bool: - if self._thread.ready.is_set(): + if self.sql.loaded and self._thread.ready.is_set(): self.__check_result() return self.__parse_task(*self._queue.pop()) return GLib.SOURCE_CONTINUE diff --git a/tests/listenbrainz/test_listenbrainz.py b/tests/listenbrainz/test_listenbrainz.py index 42874fb..90c9e5b 100644 --- a/tests/listenbrainz/test_listenbrainz.py +++ b/tests/listenbrainz/test_listenbrainz.py @@ -50,6 +50,21 @@ class TestListenBrainz(tests.util.TestCase): self.assertEqual(self.listenbrainz.sql, self.sql) self.assertIsNone(self.listenbrainz._timeout_id) + def test_early_idle_work(self, mock_idle_add: unittest.mock.Mock, + mock_source_remove: unittest.mock.Mock, + mock_stdout: io.StringIO): + """Test __idle_work() before the database has finished loading.""" + with unittest.mock.patch.object(self.listenbrainz._thread.ready, + "is_set") as mock_is_set: + self.assertEqual(self.listenbrainz._ListenBrainz__idle_work(), + GLib.SOURCE_CONTINUE) + mock_is_set.assert_not_called() + + self.sql.loaded = True + self.assertEqual(self.listenbrainz._ListenBrainz__idle_work(), + GLib.SOURCE_REMOVE) + mock_is_set.assert_called() + def test_stop(self, mock_idle_add: unittest.mock.Mock, mock_source_remove: unittest.mock.Mock, mock_stdout: io.StringIO): @@ -72,6 +87,7 @@ class TestListenBrainz(tests.util.TestCase): self.assertTrue(self.listenbrainz.valid_token) self.assertTrue(self.listenbrainz.offline) + self.sql.loaded = True idle_work = self.listenbrainz._ListenBrainz__idle_work with unittest.mock.patch.object(self.listenbrainz._thread, "set_user_token") as mock_set_token: @@ -115,6 +131,7 @@ class TestListenBrainz(tests.util.TestCase): mock_source_remove: unittest.mock.Mock, mock_stdout: io.StringIO): """Test clearing the user-token property.""" + self.sql.loaded = True idle_work = self.listenbrainz._ListenBrainz__idle_work with unittest.mock.patch.object(self.listenbrainz._thread, "clear_user_token") as mock_clear: @@ -139,6 +156,7 @@ class TestListenBrainz(tests.util.TestCase): """Test setting the now-playing property.""" self.assertIsNone(self.listenbrainz.now_playing) + self.sql.loaded = True self.listenbrainz.user_token = "abcde" self.listenbrainz.valid_token = True self.listenbrainz.offline = False @@ -212,6 +230,7 @@ class TestListenBrainz(tests.util.TestCase): emmental.listenbrainz.listen.Listen(self.track, listenid=2, listened_at=ts2)] + self.sql.loaded = True self.listenbrainz.user_token = "abcde" self.listenbrainz.valid_token = True self.listenbrainz._queue.pop()