Add tests for clipboard image functionality and mesh plane asset creation
- Implemented a comprehensive test suite for the clipboard image module, covering various functionalities including operator registration, context handling, and image processing. - Created a new mesh plane asset with customizable parameters and integrated it into the geometry node system. - Developed a validation script to ensure the correct behavior of the mesh plane asset, including input handling and output verification.
This commit is contained in:
@@ -90,8 +90,12 @@ class AddonTest(unittest.TestCase):
|
||||
cls.depth_build = importlib.import_module(
|
||||
"anyimage.operators.depth_plane.build"
|
||||
)
|
||||
cls.mesh_plane = importlib.import_module(
|
||||
"anyimage.operators.mesh_plane"
|
||||
)
|
||||
cls.image_background = importlib.import_module("anyimage.operators.background")
|
||||
cls.image_mesh = importlib.import_module("anyimage.operators.surface")
|
||||
cls.image_lasso = importlib.import_module("anyimage.operators.image_lasso")
|
||||
cls.balloon = importlib.import_module("anyimage.operators.balloon.operator")
|
||||
cls.balloon_geometry = importlib.import_module(
|
||||
"anyimage.operators.balloon.geometry"
|
||||
@@ -514,65 +518,230 @@ class AddonTest(unittest.TestCase):
|
||||
{"models": ["MOGE2_VITS_NORMAL", "BEN2_BASE"]},
|
||||
)
|
||||
|
||||
def test_image_menu_offers_required_model_repair_when_missing(self):
|
||||
calls = []
|
||||
|
||||
class Layout:
|
||||
operator_context = ""
|
||||
enabled = True
|
||||
|
||||
def __init__(self, enabled=True):
|
||||
self.enabled = enabled
|
||||
|
||||
def row(self):
|
||||
return Layout(self.enabled)
|
||||
|
||||
def operator(self, identifier, **_options):
|
||||
calls.append((identifier, self.enabled))
|
||||
return SimpleNamespace()
|
||||
|
||||
def separator(self):
|
||||
calls.append(("separator", self.enabled))
|
||||
|
||||
menu = self.addon.AnyImageImageMenu()
|
||||
menu.layout = Layout()
|
||||
def test_ai_status_checks_environment_and_required_model_files(self):
|
||||
properties = self.addon.properties
|
||||
with (
|
||||
patch.object(self.addon.runtime, "environment_ready", return_value=True),
|
||||
patch.object(
|
||||
self.addon.runtime,
|
||||
"environment_ready",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"anyimage.properties.required_models_ready",
|
||||
return_value=False,
|
||||
properties,
|
||||
"model_catalog",
|
||||
return_value=(
|
||||
{"key": "MOGE2_VITS_NORMAL", "ready": True},
|
||||
{"key": "BEN2_BASE", "ready": False},
|
||||
),
|
||||
),
|
||||
):
|
||||
menu.draw(None)
|
||||
status = properties.ai_status()
|
||||
self.assertTrue(status["environment_ready"])
|
||||
self.assertEqual(status["missing_models"], ("BEN2_BASE",))
|
||||
self.assertFalse(status["ready"])
|
||||
self.assertEqual(properties.ai_setup_label(status), "Download Required Models…")
|
||||
|
||||
self.assertEqual(
|
||||
calls[0],
|
||||
("anyimage.activate_balloon_tool", True),
|
||||
with (
|
||||
patch.object(self.addon.runtime, "environment_ready", return_value=False),
|
||||
patch.object(
|
||||
properties,
|
||||
"model_catalog",
|
||||
return_value=tuple(
|
||||
{"key": key, "ready": True}
|
||||
for key in properties.REQUIRED_MODEL_KEYS
|
||||
),
|
||||
),
|
||||
):
|
||||
status = properties.ai_status()
|
||||
self.assertFalse(status["ready"])
|
||||
self.assertEqual(properties.ai_setup_label(status), "Set Up AI Environment…")
|
||||
|
||||
def test_setup_ai_environment_selects_the_needed_install_step(self):
|
||||
calls = []
|
||||
ops = SimpleNamespace(
|
||||
anyimage=SimpleNamespace(
|
||||
install_environment=lambda *_args: calls.append("official")
|
||||
or {"FINISHED"},
|
||||
install_environment_mirror=lambda *_args: calls.append("mirror")
|
||||
or {"FINISHED"},
|
||||
download_required_models=lambda *_args: calls.append("models")
|
||||
or {"FINISHED"},
|
||||
)
|
||||
)
|
||||
operator = self.model_operators.SetupAIEnvironment()
|
||||
with patch.object(self.fake_bpy, "ops", ops, create=True):
|
||||
operator.use_mirror = True
|
||||
with patch.object(
|
||||
self.model_operators,
|
||||
"ai_status",
|
||||
return_value={
|
||||
"environment_ready": False,
|
||||
"missing_models": ("MOGE2_VITS_NORMAL", "BEN2_BASE"),
|
||||
"ready": False,
|
||||
},
|
||||
):
|
||||
self.assertEqual(operator.execute(None), {"FINISHED"})
|
||||
|
||||
with patch.object(
|
||||
self.model_operators,
|
||||
"ai_status",
|
||||
return_value={
|
||||
"environment_ready": True,
|
||||
"missing_models": ("BEN2_BASE",),
|
||||
"ready": False,
|
||||
},
|
||||
):
|
||||
self.assertEqual(operator.execute(None), {"FINISHED"})
|
||||
|
||||
self.assertEqual(calls, ["mirror", "models"])
|
||||
|
||||
def test_ai_environment_ui_types_are_registered(self):
|
||||
self.assertIn(self.model_operators.SetupAIEnvironment, self.addon.CLASSES)
|
||||
self.assertIn(
|
||||
("anyimage.download_required_models", True),
|
||||
calls,
|
||||
self.model_operators.OpenAIEnvironmentSettings,
|
||||
self.addon.CLASSES,
|
||||
)
|
||||
self.assertIn(("anyimage.convert_to_depth_plane", False), calls)
|
||||
self.assertIn(("anyimage.convert_to_surface", False), calls)
|
||||
self.assertIn(("anyimage.remove_image_background", False), calls)
|
||||
panel = importlib.import_module("anyimage.panel")
|
||||
self.assertFalse(hasattr(panel, "ModelsPanel"))
|
||||
|
||||
def test_image_menu_keeps_disabled_features_when_environment_is_missing(self):
|
||||
def test_mesh_plane_operator_is_registered_with_material_options(self):
|
||||
operator = self.mesh_plane.ConvertToMeshPlane
|
||||
self.assertIn(operator, self.addon.CLASSES)
|
||||
self.assertEqual(operator.bl_idname, "anyimage.convert_to_mesh_plane")
|
||||
self.assertFalse(operator.__annotations__["shadeless"]["default"])
|
||||
self.assertFalse(operator.__annotations__["keep_original"]["default"])
|
||||
self.assertIn("invoke", operator.__dict__)
|
||||
self.assertNotIn("draw", operator.__dict__)
|
||||
self.assertIn("REGISTER", operator.bl_options)
|
||||
self.assertIn("UNDO", operator.bl_options)
|
||||
|
||||
image_empty = SimpleNamespace(
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=object(),
|
||||
)
|
||||
mesh_plane = SimpleNamespace(
|
||||
type="MESH",
|
||||
modifiers=[
|
||||
SimpleNamespace(node_group=SimpleNamespace(name="O Mesh Plane"))
|
||||
],
|
||||
)
|
||||
plain_mesh = SimpleNamespace(type="MESH", modifiers=[])
|
||||
self.assertTrue(operator.poll(SimpleNamespace(object=image_empty)))
|
||||
self.assertTrue(operator.poll(SimpleNamespace(object=mesh_plane)))
|
||||
self.assertFalse(operator.poll(SimpleNamespace(object=plain_mesh)))
|
||||
|
||||
def test_mesh_plane_redo_keeps_the_original_world_matrix(self):
|
||||
operator = self.mesh_plane.ConvertToMeshPlane()
|
||||
operator.shadeless = False
|
||||
operator.keep_original = True
|
||||
operator.source_object_name = ""
|
||||
original_matrix = (
|
||||
(1.0, 0.0, 0.0, 3.0),
|
||||
(0.0, 1.0, 0.0, -2.0),
|
||||
(0.0, 0.0, 1.0, 7.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
source = SimpleNamespace(
|
||||
name="Source Image",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=object(),
|
||||
matrix_world=original_matrix,
|
||||
)
|
||||
context = SimpleNamespace(object=source)
|
||||
matrices = []
|
||||
|
||||
def capture_build(*_args, **kwargs):
|
||||
matrices.append(kwargs["matrix_world"])
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
self.mesh_plane.bpy.data.objects,
|
||||
"get",
|
||||
return_value=source,
|
||||
),
|
||||
patch.object(
|
||||
self.mesh_plane,
|
||||
"restore_matrix",
|
||||
side_effect=lambda values: tuple(values),
|
||||
),
|
||||
patch.object(
|
||||
self.mesh_plane,
|
||||
"build_mesh_plane",
|
||||
side_effect=capture_build,
|
||||
),
|
||||
):
|
||||
self.assertEqual(operator.invoke(context, None), {"FINISHED"})
|
||||
source.matrix_world = (
|
||||
(1.0, 0.0, 0.0, 0.0),
|
||||
(0.0, 1.0, 0.0, 0.0),
|
||||
(0.0, 0.0, 1.0, 0.0),
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
)
|
||||
self.assertEqual(operator.execute(context), {"FINISHED"})
|
||||
|
||||
expected = self.mesh_plane.flatten_matrix(original_matrix)
|
||||
self.assertEqual(matrices, [expected, expected])
|
||||
|
||||
def test_mesh_plane_uses_a_single_quad_and_geometry_nodes(self):
|
||||
import inspect
|
||||
|
||||
mesh_source = inspect.getsource(self.mesh_plane.create_mesh_plane_mesh)
|
||||
processing_source = inspect.getsource(
|
||||
self.mesh_plane.add_mesh_plane_processing
|
||||
)
|
||||
self.assertIn("((0, 1, 2, 3),)", mesh_source)
|
||||
self.assertIn('("Subdivide", max(0, int(subdivisions)))', processing_source)
|
||||
self.assertIn('("Thickness", max(0.0, float(thickness)))', processing_source)
|
||||
self.assertIn('("Material", material)', processing_source)
|
||||
|
||||
def test_image_menu_keeps_ai_features_clickable_and_offers_models(self):
|
||||
calls = []
|
||||
labels = {}
|
||||
|
||||
class Layout:
|
||||
operator_context = ""
|
||||
enabled = True
|
||||
|
||||
def __init__(self, enabled=True):
|
||||
self.enabled = enabled
|
||||
def operator(self, identifier, **options):
|
||||
calls.append((identifier, self.enabled))
|
||||
labels[identifier] = options.get("text")
|
||||
return SimpleNamespace()
|
||||
|
||||
def row(self):
|
||||
return Layout(self.enabled)
|
||||
def separator(self):
|
||||
calls.append(("separator", self.enabled))
|
||||
|
||||
menu = self.addon.AnyImageImageMenu()
|
||||
menu.layout = Layout()
|
||||
with patch(
|
||||
"anyimage.properties.ai_status",
|
||||
return_value={
|
||||
"environment_ready": True,
|
||||
"missing_models": ("BEN2_BASE",),
|
||||
"ready": False,
|
||||
},
|
||||
):
|
||||
menu.draw(None)
|
||||
|
||||
self.assertEqual(
|
||||
calls,
|
||||
[
|
||||
("anyimage.convert_to_mesh_plane", True),
|
||||
("anyimage.convert_to_depth_plane", True),
|
||||
("anyimage.convert_to_surface", True),
|
||||
("anyimage.remove_image_background", True),
|
||||
("separator", True),
|
||||
("anyimage.setup_ai_environment", True),
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
labels["anyimage.setup_ai_environment"],
|
||||
"Download Required Models…",
|
||||
)
|
||||
|
||||
def test_image_menu_opens_environment_settings_when_ai_is_ready(self):
|
||||
calls = []
|
||||
|
||||
class Layout:
|
||||
operator_context = ""
|
||||
enabled = True
|
||||
|
||||
def operator(self, identifier, **_options):
|
||||
calls.append((identifier, self.enabled))
|
||||
@@ -583,29 +752,18 @@ class AddonTest(unittest.TestCase):
|
||||
|
||||
menu = self.addon.AnyImageImageMenu()
|
||||
menu.layout = Layout()
|
||||
with (
|
||||
patch.object(
|
||||
self.addon.runtime,
|
||||
"environment_ready",
|
||||
return_value=False,
|
||||
),
|
||||
patch(
|
||||
"anyimage.properties.required_models_ready",
|
||||
return_value=False,
|
||||
),
|
||||
with patch(
|
||||
"anyimage.properties.ai_status",
|
||||
return_value={
|
||||
"environment_ready": True,
|
||||
"missing_models": (),
|
||||
"ready": True,
|
||||
},
|
||||
):
|
||||
menu.draw(None)
|
||||
|
||||
self.assertEqual(
|
||||
calls[0],
|
||||
("anyimage.activate_balloon_tool", True),
|
||||
)
|
||||
self.assertIn(("anyimage.install_environment", True), calls)
|
||||
self.assertIn(("anyimage.install_environment_mirror", True), calls)
|
||||
self.assertNotIn(("anyimage.download_required_models", True), calls)
|
||||
self.assertIn(("anyimage.convert_to_depth_plane", False), calls)
|
||||
self.assertIn(("anyimage.convert_to_surface", False), calls)
|
||||
self.assertIn(("anyimage.remove_image_background", False), calls)
|
||||
self.assertIn(("anyimage.open_ai_environment_settings", True), calls)
|
||||
self.assertNotIn(("anyimage.setup_ai_environment", True), calls)
|
||||
|
||||
def test_balloon_contour_is_smooth_and_evenly_resampled(self):
|
||||
import numpy as np
|
||||
@@ -621,6 +779,157 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertGreater(self.balloon_geometry.polygon_area(contour), 0.0)
|
||||
self.assertLess(float(lengths.std() / lengths.mean()), 0.15)
|
||||
|
||||
def test_image_lasso_clears_rgba_outside_the_selection(self):
|
||||
import numpy as np
|
||||
|
||||
top_down = np.full((3, 3, 4), (0.2, 0.4, 0.6, 1.0), dtype=np.float32)
|
||||
source = np.flipud(top_down).ravel()
|
||||
pixels, bounds = self.image_lasso.extract_lasso_pixels(
|
||||
source,
|
||||
(3, 3),
|
||||
((0, 0), (3, 0), (0, 3)),
|
||||
)
|
||||
result = np.flipud(np.asarray(pixels).reshape((3, 3, 4)))
|
||||
|
||||
self.assertEqual(bounds, (0, 0, 3, 3))
|
||||
np.testing.assert_allclose(result[2, 2], (0.0, 0.0, 0.0, 0.0))
|
||||
self.assertEqual(float(result[0, 0, 3]), 1.0)
|
||||
|
||||
def test_image_lasso_clears_hidden_rgb_from_source_transparency(self):
|
||||
import numpy as np
|
||||
|
||||
top_down = np.full((2, 2, 4), (0.2, 0.4, 0.6, 1.0), dtype=np.float32)
|
||||
top_down[0, 0, 3] = 0.0
|
||||
pixels, _bounds = self.image_lasso.extract_lasso_pixels(
|
||||
np.flipud(top_down).ravel(),
|
||||
(2, 2),
|
||||
((0, 0), (2, 0), (2, 2), (0, 2)),
|
||||
)
|
||||
result = np.flipud(np.asarray(pixels).reshape((2, 2, 4)))
|
||||
|
||||
np.testing.assert_allclose(result[0, 0], (0.0, 0.0, 0.0, 0.0))
|
||||
|
||||
def test_image_lasso_keeps_out_of_image_canvas_transparent(self):
|
||||
import numpy as np
|
||||
|
||||
top_down = np.ones((2, 2, 4), dtype=np.float32)
|
||||
pixels, bounds = self.image_lasso.extract_lasso_pixels(
|
||||
np.flipud(top_down).ravel(),
|
||||
(2, 2),
|
||||
((-1, -1), (2, -1), (2, 2), (-1, 2)),
|
||||
)
|
||||
result = np.flipud(np.asarray(pixels).reshape((3, 3, 4)))
|
||||
|
||||
self.assertEqual(bounds, (-1, -1, 2, 2))
|
||||
self.assertEqual(float(result[0, 0, 3]), 0.0)
|
||||
self.assertEqual(float(result[1, 1, 3]), 1.0)
|
||||
|
||||
def test_image_lasso_rejects_a_fully_out_of_image_region(self):
|
||||
import numpy as np
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "does not overlap"):
|
||||
self.image_lasso.extract_lasso_pixels(
|
||||
np.ones(2 * 2 * 4, dtype=np.float32),
|
||||
(2, 2),
|
||||
((3, 3), (4, 3), (4, 4), (3, 4)),
|
||||
)
|
||||
|
||||
def test_image_lasso_crops_to_visible_alpha_with_pixel_padding(self):
|
||||
import numpy as np
|
||||
|
||||
rgba = np.zeros((4, 5, 4), dtype=np.float32)
|
||||
rgba[1:3, 2:4] = (0.2, 0.4, 0.6, 1.0)
|
||||
pixels, bounds = self.image_lasso.crop_pixels_to_alpha(
|
||||
np.flipud(rgba).ravel(),
|
||||
(10, 20, 15, 24),
|
||||
padding=1,
|
||||
)
|
||||
result = np.flipud(np.asarray(pixels).reshape((4, 4, 4)))
|
||||
|
||||
self.assertEqual(bounds, (11, 20, 15, 24))
|
||||
self.assertEqual(result.shape, (4, 4, 4))
|
||||
self.assertTrue(np.all(result[[0, -1], :, 3] == 0.0))
|
||||
self.assertTrue(np.all(result[:, [0, -1], 3] == 0.0))
|
||||
self.assertTrue(np.all(result[1:3, 1:3, 3] == 1.0))
|
||||
|
||||
def test_image_lasso_tool_and_operators_are_registered(self):
|
||||
self.assertIn(self.image_lasso.ExtractImageLasso, self.addon.CLASSES)
|
||||
self.assertIn(self.image_lasso.AnalyzeImageLasso, self.addon.CLASSES)
|
||||
self.assertIn(self.image_lasso.ActivateImageLassoTool, self.addon.CLASSES)
|
||||
self.assertNotIn(self.image_lasso.ImageLassoTool, self.addon.CLASSES)
|
||||
self.assertNotIn(self.image_lasso.ImageBoxTool, self.addon.CLASSES)
|
||||
self.assertEqual(
|
||||
self.image_lasso.ImageLassoTool.bl_operator,
|
||||
self.image_lasso.ExtractImageLasso.bl_idname,
|
||||
)
|
||||
self.assertEqual(
|
||||
self.image_lasso.ImageLassoTool.bl_icon,
|
||||
"ops.sculpt.lasso_mask",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.image_lasso.ImageBoxTool.bl_icon,
|
||||
"ops.sculpt.border_mask",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.image_lasso.ImageBoxTool.bl_keymap[0][2]["properties"],
|
||||
[("selection_shape", "BOX")],
|
||||
)
|
||||
self.assertEqual(
|
||||
self.image_lasso.ImageLassoTool.bl_keymap[0][2]["properties"],
|
||||
[("selection_shape", "LASSO")],
|
||||
)
|
||||
self.assertNotIn("bl_cursor", self.image_lasso.ImageBoxTool.__dict__)
|
||||
self.assertNotIn("bl_cursor", self.image_lasso.ImageLassoTool.__dict__)
|
||||
self.assertEqual(
|
||||
self.image_lasso.ExtractImageLasso.__annotations__["selection_shape"][
|
||||
"default"
|
||||
],
|
||||
"BOX",
|
||||
)
|
||||
operator = self.image_lasso.ExtractImageLasso
|
||||
self.assertIn("keep_original", operator.__annotations__)
|
||||
self.assertFalse(operator.__annotations__["keep_original"]["default"])
|
||||
self.assertIn("padding", operator.__annotations__)
|
||||
self.assertEqual(operator.__annotations__["padding"]["default"], 0)
|
||||
settings = self.addon.properties.AnyImageSettings.__annotations__
|
||||
self.assertFalse(settings["image_lasso_keep_original"]["default"])
|
||||
self.assertEqual(settings["image_lasso_padding"]["default"], 0)
|
||||
|
||||
def test_image_box_builds_a_rectangular_screen_path(self):
|
||||
self.assertEqual(
|
||||
self.image_lasso.box_screen_path(((2, 3), (8, 9))),
|
||||
((2, 3), (8, 3), (8, 9), (2, 9)),
|
||||
)
|
||||
self.assertEqual(
|
||||
self.image_lasso.box_screen_path(((2, 3), (3, 9))),
|
||||
(),
|
||||
)
|
||||
|
||||
def test_image_extract_selection_uses_white_dashed_segments(self):
|
||||
segments = self.image_lasso.dashed_line_segments(
|
||||
((0, 0), (10, 0)), dash_length=3.0, gap_length=2.0
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
segments,
|
||||
((0.0, 0.0), (3.0, 0.0), (5.0, 0.0), (8.0, 0.0)),
|
||||
)
|
||||
|
||||
def test_image_extract_activation_defaults_to_box(self):
|
||||
import inspect
|
||||
|
||||
source = inspect.getsource(self.image_lasso.ActivateImageLassoTool.execute)
|
||||
self.assertIn("name=ImageBoxTool.bl_idname", source)
|
||||
|
||||
def test_keep_original_restores_the_source_empty_selection(self):
|
||||
import inspect
|
||||
|
||||
execute_source = inspect.getsource(self.image_lasso.ExtractImageLasso.execute)
|
||||
response_source = inspect.getsource(self.image_lasso.AnalyzeImageLasso.response)
|
||||
expected = "source_object if self.keep_original else target"
|
||||
self.assertIn(expected, execute_source)
|
||||
self.assertIn(expected, response_source)
|
||||
|
||||
def test_balloon_interior_points_have_uniform_spacing(self):
|
||||
import numpy as np
|
||||
|
||||
@@ -909,6 +1218,10 @@ class AddonTest(unittest.TestCase):
|
||||
response_source = inspect.getsource(self.balloon.AnalyzeBalloon.response)
|
||||
tool_source = inspect.getsource(self.balloon.BalloonTool.draw_settings)
|
||||
self.assertIn("bpy.ops.anyimage.analyze_balloon", execute_source)
|
||||
self.assertIn("ready = ai_ready()", invoke_source)
|
||||
self.assertIn("status = ai_status()", tool_source)
|
||||
self.assertIn('"anyimage.setup_ai_environment"', tool_source)
|
||||
self.assertIn("ai_setup_label(status)", tool_source)
|
||||
self.assertIn("prepare_image_input(", execute_source)
|
||||
self.assertIn(
|
||||
"settings.balloon_contour_analysis",
|
||||
@@ -1360,7 +1673,7 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertIn(expected, original_depth_source)
|
||||
self.assertIn(expected, depth_source)
|
||||
|
||||
def test_image_operators_require_environment_and_required_models(self):
|
||||
def test_image_operators_stay_clickable_until_server_is_busy(self):
|
||||
context = SimpleNamespace(
|
||||
object=SimpleNamespace(data=SimpleNamespace()),
|
||||
)
|
||||
@@ -1370,31 +1683,13 @@ class AddonTest(unittest.TestCase):
|
||||
(self.image_background, self.image_background.RemoveImageBackground),
|
||||
)
|
||||
|
||||
states = (
|
||||
(False, True, False),
|
||||
(True, False, False),
|
||||
(True, True, True),
|
||||
)
|
||||
for environment_ready, models_ready, expected in states:
|
||||
for busy, expected in ((False, True), (True, False)):
|
||||
with (
|
||||
self.subTest(
|
||||
environment_ready=environment_ready,
|
||||
models_ready=models_ready,
|
||||
expected=expected,
|
||||
),
|
||||
patch.object(
|
||||
self.addon.runtime,
|
||||
"environment_ready",
|
||||
return_value=environment_ready,
|
||||
),
|
||||
self.subTest(busy=busy),
|
||||
patch.object(
|
||||
self.addon.runtime,
|
||||
"server_busy",
|
||||
return_value=False,
|
||||
),
|
||||
patch(
|
||||
"anyimage.properties.required_models_ready",
|
||||
return_value=models_ready,
|
||||
return_value=busy,
|
||||
),
|
||||
):
|
||||
for module, operator in operators:
|
||||
@@ -1500,6 +1795,14 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertEqual(attribute, "server")
|
||||
|
||||
def test_runtime_registers_system_operators_and_status_bar(self):
|
||||
self.assertIn(
|
||||
self.addon.clipboard_image.PasteClipboardImage,
|
||||
self.addon.CLASSES,
|
||||
)
|
||||
self.assertIn(
|
||||
self.addon.clipboard_image.TrackNativeCopy,
|
||||
self.addon.CLASSES,
|
||||
)
|
||||
self.addon.register()
|
||||
system_classes = self.addon.runtime.operator_classes()
|
||||
|
||||
@@ -1507,12 +1810,26 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
self.registered_tools,
|
||||
[
|
||||
(
|
||||
self.image_lasso.ImageBoxTool,
|
||||
{
|
||||
"group": True,
|
||||
"separator": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
self.image_lasso.ImageLassoTool,
|
||||
{
|
||||
"after": {self.image_lasso.ImageBoxTool.bl_idname},
|
||||
"separator": False,
|
||||
},
|
||||
),
|
||||
(
|
||||
self.balloon.BalloonTool,
|
||||
{
|
||||
"separator": False,
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
||||
self.assertEqual(len(self.status_bar_draws), 1)
|
||||
|
||||
Reference in New Issue
Block a user