diff --git a/plugins/python/plugin_importer/plugin_importer.py b/plugins/python/plugin_importer/plugin_importer.py --- a/plugins/python/plugin_importer/plugin_importer.py +++ b/plugins/python/plugin_importer/plugin_importer.py @@ -132,7 +132,8 @@ def get_source_module(self, name): namelist = self.archive.namelist() for filename in namelist: - if filename.endswith('/%s/' % name): + if (filename.endswith('/%s/' % name) + or filename == '%s/' % name): # Sanity check: There should be an __init__.py inside if ('%s__init__.py' % filename) in namelist: return filename diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_missing_keys_desktop_file/plugin/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_desktop_file/plugin/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=foo +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_init_file/plugin/foo/foo.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=wrong_name +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_no_matching_plugindir/plugin/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.action @@ -0,0 +1 @@ +this is wrong diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=foo +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_action_file/plugin/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo.desktop @@ -0,0 +1 @@ +this is wrong \ No newline at end of file diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/fail_unparsable_desktop_file/plugin/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/foo.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=foo +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_nested/plugin/somedir/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=foo +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_no_action/plugin/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.action b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=foo +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_simple/plugin/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.action b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.action new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.action @@ -0,0 +1,19 @@ + + + + Foo + + + + Foo... + + + + 0 + 0 + + false + + + + diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.desktop b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.desktop new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Service +ServiceTypes=Krita/PythonPlugin +X-KDE-Library=foo +X-Python-2-Compatible=false +Name=Foo diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/__init__.py b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/__init__.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/__init__.py @@ -0,0 +1 @@ +print('hello') diff --git a/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/foo.py b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/foo.py new file mode 100644 --- /dev/null +++ b/plugins/python/plugin_importer/tests/fixtures/success_toplevel/foo/foo.py @@ -0,0 +1 @@ +print('foo') diff --git a/plugins/python/plugin_importer/tests/test_plugin_importer.py b/plugins/python/plugin_importer/tests/test_plugin_importer.py --- a/plugins/python/plugin_importer/tests/test_plugin_importer.py +++ b/plugins/python/plugin_importer/tests/test_plugin_importer.py @@ -1,10 +1,14 @@ import os import pytest from tempfile import TemporaryDirectory -from unittest import TestCase +from unittest import mock, TestCase +from zipfile import ZipFile - -from .. plugin_importer import PluginImporter, PluginReadError +from .. plugin_importer import ( + NoPluginsFoundException, + PluginImporter, + PluginReadError, +) class PluginImporterTestCase(TestCase): @@ -19,8 +23,26 @@ @property def zip_filename(self): - return os.path.join( - self.resources_dir.name, 'plugin.zip') + return os.path.join(self.plugin_dir.name, 'plugin.zip') + + def zip_plugin(self, dirname): + """Zip a directory into the temporary directory.""" + testroot = os.path.dirname(os.path.realpath(__file__)) + src = os.path.join(testroot, 'fixtures', dirname) + + with ZipFile(self.zip_filename, 'w') as plugin_zip: + for root, dirs, files in os.walk(src): + dirname = root.replace(src, '') + for filename in files + dirs: + plugin_zip.write( + filename=os.path.join(root, filename), + arcname=os.path.join(dirname, filename)) + + def assert_in_resources_dir(self, *path): + assert os.path.exists(os.path.join(self.resources_dir.name, *path)) + + def assert_not_in_resources_dir(self, *path): + assert not os.path.exists(os.path.join(self.resources_dir.name, *path)) def test_zipfile_doesnt_exist(self): with pytest.raises(PluginReadError): @@ -35,3 +57,141 @@ PluginImporter(self.zip_filename, self.resources_dir.name, lambda x: True) + + def test_simple_plugin_success(self): + self.zip_plugin('success_simple') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + imported = importer.import_all() + assert len(imported) == 1 + self.assert_in_resources_dir('pykrita', 'foo') + self.assert_in_resources_dir('pykrita', 'foo', '__init__.py') + self.assert_in_resources_dir('pykrita', 'foo', 'foo.py') + self.assert_in_resources_dir('pykrita', 'foo.desktop') + self.assert_in_resources_dir('actions', 'foo.action') + + def test_toplevel_plugin_success(self): + self.zip_plugin('success_toplevel') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + imported = importer.import_all() + assert len(imported) == 1 + self.assert_in_resources_dir('pykrita', 'foo') + self.assert_in_resources_dir('pykrita', 'foo', '__init__.py') + self.assert_in_resources_dir('pykrita', 'foo', 'foo.py') + self.assert_in_resources_dir('pykrita', 'foo.desktop') + self.assert_in_resources_dir('actions', 'foo.action') + + def test_nested_plugin_success(self): + self.zip_plugin('success_nested') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + imported = importer.import_all() + assert len(imported) == 1 + self.assert_in_resources_dir('pykrita', 'foo') + self.assert_in_resources_dir('pykrita', 'foo', '__init__.py') + self.assert_in_resources_dir('pykrita', 'foo', 'foo.py') + self.assert_in_resources_dir('pykrita', 'foo.desktop') + self.assert_in_resources_dir('actions', 'foo.action') + + def test_no_action_success(self): + self.zip_plugin('success_no_action') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + imported = importer.import_all() + assert len(imported) == 1 + self.assert_in_resources_dir('pykrita', 'foo') + self.assert_in_resources_dir('pykrita', 'foo', '__init__.py') + self.assert_in_resources_dir('pykrita', 'foo', 'foo.py') + self.assert_in_resources_dir('pykrita', 'foo.desktop') + self.assert_not_in_resources_dir('actions', 'foo.action') + + def test_overwrite_existing(self): + self.zip_plugin('success_simple') + plugin_dir = os.path.join(self.resources_dir.name, 'pykrita', 'foo') + os.makedirs(plugin_dir) + init_file = os.path.join(plugin_dir, '__init__.py') + with open(init_file, 'w') as f: + f.write('# existing module') + + confirm_callback = mock.MagicMock(return_value=True) + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + confirm_callback) + imported = importer.import_all() + assert len(imported) == 1 + assert confirm_callback.called + + with open(init_file, 'r') as f: + assert f.read().strip() == "print('hello')" + + def test_dont_overwrite_existing(self): + self.zip_plugin('success_simple') + plugin_dir = os.path.join(self.resources_dir.name, 'pykrita', 'foo') + os.makedirs(plugin_dir) + init_file = os.path.join(plugin_dir, '__init__.py') + with open(init_file, 'w') as f: + f.write('# existing module') + + confirm_callback = mock.MagicMock(return_value=False) + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + confirm_callback) + imported = importer.import_all() + assert len(imported) == 0 + assert confirm_callback.called + + with open(init_file, 'r') as f: + assert f.read().strip() == '# existing module' + + def test_missing_desktop_file(self): + self.zip_plugin('fail_no_desktop_file') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + with pytest.raises(NoPluginsFoundException): + importer.import_all() + + def test_unparsable_desktop_file(self): + self.zip_plugin('fail_unparsable_desktop_file') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + with pytest.raises(PluginReadError): + importer.import_all() + + def test_missing_keys_in_desktop_file(self): + self.zip_plugin('fail_missing_keys_desktop_file') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + with pytest.raises(PluginReadError): + importer.import_all() + + def test_no_matching_plugindir(self): + self.zip_plugin('fail_no_matching_plugindir') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + with pytest.raises(NoPluginsFoundException): + importer.import_all() + + def test_no_init_file(self): + self.zip_plugin('fail_no_init_file') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + with pytest.raises(NoPluginsFoundException): + importer.import_all() + + def test_unparsable_action_file(self): + self.zip_plugin('fail_unparsable_action_file') + importer = PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + with pytest.raises(PluginReadError): + importer.import_all()