Multiple mixins are allowed of course. Allowing multiple implementations lead to too many problems due to plugin names for referring to the APIs of SimpleApiPlugins or the assets of AssetPlugins. Hence __plugin_implementations__ has been deprecated in favor of __plugin_implementation__. The plugin subsystem will automatically copy the first implementation from __plugin_implementations__ to __plugin_implementation__ and log a deprecation warning. Adjusted documentation accordingly. Also added docs for helpers.
158 lines
5.4 KiB
Python
158 lines
5.4 KiB
Python
# coding=utf-8
|
|
from __future__ import absolute_import
|
|
|
|
__author__ = "Gina Häußge <osd@foosel.net>"
|
|
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
|
__copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms of the AGPLv3 License"
|
|
|
|
import unittest
|
|
import mock
|
|
|
|
import octoprint.slicing
|
|
|
|
class TestSlicingManager(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.addCleanup(self.cleanUp)
|
|
|
|
import tempfile
|
|
self.profile_path = tempfile.mkdtemp()
|
|
|
|
self.slicer_plugin = mock.MagicMock()
|
|
self.slicer_plugin.get_slicer_properties.return_value = dict(type="mock", name="Mock", same_device=True)
|
|
self.slicer_plugin.is_slicer_configured.return_value = True
|
|
|
|
# mock plugin manager
|
|
self.plugin_manager_patcher = mock.patch("octoprint.plugin.plugin_manager")
|
|
self.plugin_manager = self.plugin_manager_patcher.start()
|
|
self._mock_slicer_plugins(self.slicer_plugin)
|
|
|
|
# mock profile manager
|
|
self.printer_profile_manager = mock.MagicMock(spec=octoprint.printer.profile.PrinterProfileManager)
|
|
|
|
# mock settings
|
|
self.settings_patcher = mock.patch("octoprint.slicing.settings")
|
|
settings = self.settings_patcher.start()
|
|
self.settings = settings.return_value
|
|
|
|
self.slicing_manager = octoprint.slicing.SlicingManager(self.profile_path, self.printer_profile_manager)
|
|
self.slicing_manager.initialize()
|
|
|
|
def tearDown(self):
|
|
import shutil
|
|
shutil.rmtree(self.profile_path)
|
|
|
|
def cleanUp(self):
|
|
self.settings_patcher.stop()
|
|
self.plugin_manager_patcher.stop()
|
|
|
|
def _mock_slicer_plugins(self, *plugins):
|
|
def get_implementations(*types):
|
|
import octoprint.plugin
|
|
if octoprint.plugin.SlicerPlugin in types:
|
|
return plugins
|
|
return dict()
|
|
self.plugin_manager.return_value.get_implementations.side_effect = get_implementations
|
|
|
|
def test_registered_slicers(self):
|
|
self.assertEquals(["mock"], self.slicing_manager.registered_slicers)
|
|
|
|
def test_slicing_enabled(self):
|
|
self.assertTrue(self.slicing_manager.slicing_enabled)
|
|
|
|
def test_default_slicer(self):
|
|
def get(path):
|
|
if path == ["slicing", "defaultSlicer"]:
|
|
return "mock"
|
|
else:
|
|
return None
|
|
self.settings.get.side_effect = get
|
|
|
|
self.assertEquals("mock", self.slicing_manager.default_slicer)
|
|
|
|
def test_default_slicer_unknown(self):
|
|
def get(path):
|
|
if path == ["slicing", "defaultSlicer"]:
|
|
return "unknown"
|
|
else:
|
|
return None
|
|
self.settings.get.side_effect = get
|
|
|
|
self.assertIsNone(self.slicing_manager.default_slicer)
|
|
|
|
@mock.patch("threading.Thread")
|
|
@mock.patch("tempfile.NamedTemporaryFile")
|
|
@mock.patch("os.remove")
|
|
def test_slice(self, mocked_os_remove, mocked_tempfile, mocked_thread):
|
|
# mock temporary file
|
|
temp_file = mock.MagicMock()
|
|
temp_file.name = "tmp.file"
|
|
mocked_tempfile.return_value = temp_file
|
|
|
|
# mock retrieval of default profile
|
|
def get(path):
|
|
return dict()
|
|
self.settings.get.side_effect = get
|
|
|
|
default_profile = octoprint.slicing.SlicingProfile("mock", "default", dict(layer_height=0.2, fill_density=40))
|
|
self.slicer_plugin.get_slicer_default_profile.return_value = default_profile
|
|
|
|
# mock threading
|
|
class MockThread(object):
|
|
def __init__(self):
|
|
self.target = None
|
|
self.args = None
|
|
self.mock = None
|
|
|
|
def constructor(self, target=None, args=None):
|
|
self.target = target
|
|
self.args = args
|
|
self.mock = mock.MagicMock()
|
|
self.mock.start.side_effect = self.start
|
|
return self.mock
|
|
|
|
def start(self):
|
|
self.target(*self.args)
|
|
mock_thread = MockThread()
|
|
mocked_thread.side_effect = mock_thread.constructor
|
|
|
|
# mock slicing
|
|
self.slicer_plugin.do_slice.return_value = True, None
|
|
|
|
# mock printer profile manager
|
|
printer_profile = dict(_id="mock_printer", _name="Mock Printer Profile")
|
|
def get_printer_profile(printer_profile_id):
|
|
self.assertEquals("mock_printer", printer_profile_id)
|
|
return printer_profile
|
|
self.printer_profile_manager.get.side_effect = get_printer_profile
|
|
|
|
##~~ call tested method
|
|
slicer_name = "mock"
|
|
source_path = "prefix/source.file"
|
|
dest_path = "prefix/dest.file"
|
|
profile_name = "dummy_profile"
|
|
printer_profile_id = "mock_printer"
|
|
position = dict(x=10, y=20)
|
|
callback = mock.MagicMock()
|
|
callback_args = ("one", "two", "three")
|
|
callback_kwargs = dict(foo="bar")
|
|
overrides = dict(layer_height=0.5)
|
|
|
|
self.slicing_manager.slice(slicer_name, source_path, dest_path, profile_name, callback, printer_profile_id=printer_profile_id, position=position, callback_args=callback_args, callback_kwargs=callback_kwargs, overrides=overrides)
|
|
|
|
# assert that temporary profile was created properly
|
|
self.slicer_plugin.save_slicer_profile.assert_called_once_with("tmp.file", default_profile, overrides=overrides)
|
|
|
|
# assert that slicing thread was created properly
|
|
mocked_thread.assert_called_once_with(target=mock.ANY, args=(self.slicer_plugin, source_path, dest_path, profile_name, overrides, printer_profile, position, callback, callback_args, callback_kwargs))
|
|
self.assertTrue(mock_thread.mock.daemon)
|
|
mock_thread.mock.start.assert_called_once()
|
|
|
|
# assert that slicer was called correctly
|
|
self.slicer_plugin.do_slice.assert_called_once_with(source_path, printer_profile, machinecode_path=dest_path, profile_path="tmp.file", position=position, on_progress=None, on_progress_args=None, on_progress_kwargs=None)
|
|
|
|
# assert that temporary profile was deleted again
|
|
mocked_os_remove.assert_called_once_with("tmp.file")
|
|
|
|
# assert that callback was called property
|
|
callback.assert_called_once_with(*callback_args, **callback_kwargs)
|