import tempfile import unittest from pathlib import Path from unittest import mock from gnome_extension_manager.core import ( REPOSITORY_BINARIES, REQUIRED_BINARIES, MissingBinaryError, build_extension, default_extensions_dir, gnome_extensions, run, sync_repository, ) class TestRequiredBinaries(unittest.TestCase): def test_declares_every_external_command_the_package_calls(self): declared = set(REQUIRED_BINARIES) | set(REPOSITORY_BINARIES) self.assertEqual(declared, {"gnome-extensions", "git", "make"}) def test_repository_binaries_are_not_declared_as_always_required(self): self.assertFalse(set(REQUIRED_BINARIES) & set(REPOSITORY_BINARIES)) def test_missing_binary_is_reported_with_its_name(self): with mock.patch( "gnome_extension_manager.core.subprocess.run" ) as subprocess_run: subprocess_run.side_effect = FileNotFoundError with self.assertRaises(MissingBinaryError) as caught: run(["git", "clone", "x", "y"]) self.assertEqual(caught.exception.binary, "git") class TestDefaultExtensionsDir(unittest.TestCase): def test_points_at_the_per_user_gnome_directory(self): self.assertEqual( default_extensions_dir(), Path.home() / ".local" / "share" / "gnome-shell" / "extensions", ) class TestSyncRepository(unittest.TestCase): def test_missing_folder_is_cloned(self): with ( tempfile.TemporaryDirectory() as base, mock.patch("gnome_extension_manager.core.run", return_value=0) as run, ): folder = Path(base) / "ext" self.assertEqual(sync_repository(folder, "https://example.com/ext.git"), 0) run.assert_called_once_with( ["git", "clone", "https://example.com/ext.git", str(folder)] ) def test_existing_checkout_is_pulled(self): with tempfile.TemporaryDirectory() as base: folder = Path(base) / "ext" (folder / ".git").mkdir(parents=True) with mock.patch("gnome_extension_manager.core.run", return_value=0) as run: self.assertEqual(sync_repository(folder, "https://e.example/x"), 0) run.assert_called_once_with(["git", "pull"], cwd=str(folder)) def test_existing_folder_without_git_is_left_alone(self): with tempfile.TemporaryDirectory() as base: folder = Path(base) / "ext" folder.mkdir() with mock.patch("gnome_extension_manager.core.run") as run: self.assertEqual(sync_repository(folder, "https://e.example/x"), 0) run.assert_not_called() def test_failed_clone_propagates_the_exit_code(self): with ( tempfile.TemporaryDirectory() as base, mock.patch("gnome_extension_manager.core.run", return_value=128), ): self.assertEqual( sync_repository(Path(base) / "ext", "https://e.example/x"), 128 ) class TestBuildExtension(unittest.TestCase): def test_extension_without_makefile_is_kept_in_place(self): with tempfile.TemporaryDirectory() as base: folder = Path(base) / "ext" folder.mkdir() with mock.patch("gnome_extension_manager.core.run") as run: self.assertEqual(build_extension(folder), 0) run.assert_not_called() self.assertTrue(folder.exists()) def test_makefile_extension_is_built_and_the_checkout_removed(self): with tempfile.TemporaryDirectory() as base: folder = Path(base) / "ext" folder.mkdir() (folder / "Makefile").write_text("install:\n\ttrue\n") with mock.patch("gnome_extension_manager.core.run", return_value=0) as run: self.assertEqual(build_extension(folder), 0) self.assertEqual(run.call_args.args[0], ["make", "install"]) self.assertFalse(folder.exists()) def test_failed_build_propagates_the_exit_code(self): with tempfile.TemporaryDirectory() as base: folder = Path(base) / "ext" folder.mkdir() (folder / "Makefile").write_text("install:\n\tfalse\n") with mock.patch("gnome_extension_manager.core.run", return_value=2): self.assertEqual(build_extension(folder), 2) class TestGnomeExtensions(unittest.TestCase): def test_delegates_to_the_gnome_extensions_tool(self): with mock.patch("gnome_extension_manager.core.run", return_value=0) as run: gnome_extensions("enable", "dash-to-dock@example") run.assert_called_once_with( ["gnome-extensions", "enable", "dash-to-dock@example"] ) if __name__ == "__main__": unittest.main()