Add comprehensive tests for image processing and mask functionalities

- Introduced tests for color image handling, ensuring source preservation and premultiplied alpha usage.
- Implemented tests for brush stroke operations, validating segment boundaries and brush anti-aliasing.
- Added tests for frame anti-aliasing, confirming correct rendering behavior with various alpha values and depth settings.
- Created tests for frame projection, ensuring accurate overlap calculations and half-plane clipping.
- Developed tests for image premultiplied retention, verifying behavior during loading and packing processes.
- Added undo functionality tests for mask operations, ensuring single transaction behavior during edits.
This commit is contained in:
2026-09-05 21:33:11 +08:00
parent bbaa4bff46
commit 1994d38ccd
111 changed files with 7691 additions and 2341 deletions

View File

@@ -8,7 +8,7 @@ sys.path.insert(0, str(SOURCE_ROOT))
from anyimage.common.selection import SelectionPath
from anyimage.common import viewport
from anyimage.common.viewport import CropSelectionGesture
from anyimage.common.viewport import ImageGesture
from anyimage.operators.cutout_tool import interaction
from anyimage.operators.cutout_tool import main as cutout_main
@@ -64,7 +64,6 @@ def test_cutout_pie_uses_two_local_shapes_without_ai(monkeypatch):
object(),
source,
SelectionPath(points=((0, 0), (10, 0), (0, 10))),
refine_selection=True,
)
assert [call[2].shape for call in window_manager.pie.calls] == [
@@ -79,7 +78,6 @@ def test_cutout_pie_uses_two_local_shapes_without_ai(monkeypatch):
call[0] == "anyimage.cutout_selection_to_shape"
for call in window_manager.pie.calls
)
assert not any(call[2].refine_selection for call in window_manager.pie.calls)
assert not any(call[2].generate_normal for call in window_manager.pie.calls)
assert all(call[2].point_spacing == 32 for call in window_manager.pie.calls)
assert all(call[2].inward_axis == "NEGATIVE_Z" for call in window_manager.pie.calls)
@@ -111,9 +109,7 @@ def test_cutout_pie_passes_image_lasso_options_to_all_ai_shapes(monkeypatch):
source,
SelectionPath(
points=((0, 0), (10, 0), (0, 10)),
invert=True,
),
refine_selection=True,
)
assert [call[2].shape for call in window_manager.pie.calls] == [
@@ -122,16 +118,16 @@ def test_cutout_pie_passes_image_lasso_options_to_all_ai_shapes(monkeypatch):
"DEPTH_BALLOON",
"DEPTH_SURFACE",
]
assert all(call[2].refine_selection for call in window_manager.pie.calls)
assert all(call[2].generate_normal for call in window_manager.pie.calls)
assert len(serialized) == 1
assert set(json.loads(serialized[0])) == {"points", "invert"}
assert set(json.loads(serialized[0])) == {"points"}
assert all(
call[2].selection_path_json == serialized[0]
for call in window_manager.pie.calls
)
assert all(
SelectionPath.from_json(call[2].selection_path_json).invert
SelectionPath.from_json(call[2].selection_path_json).points
== ((0.0, 0.0), (10.0, 0.0), (0.0, 10.0))
for call in window_manager.pie.calls
)
assert all(call[2].point_spacing == 16 for call in window_manager.pie.calls)
@@ -163,7 +159,6 @@ def test_cutout_reuses_image_lasso_gesture_and_double_click_selects_all(monkeypa
)
monkeypatch.setattr(cutout_main, "video_total_frames", lambda _image: 1)
monkeypatch.setattr(cutout_main, "require_image_empty", lambda _name: source)
monkeypatch.setattr(cutout_main, "apply_crop_settings", lambda *_args: None)
monkeypatch.setattr(cutout_main, "serialize_matrix", lambda _matrix: "matrix")
monkeypatch.setattr(
cutout_main,
@@ -180,7 +175,6 @@ def test_cutout_reuses_image_lasso_gesture_and_double_click_selects_all(monkeypa
operator = SimpleNamespace(
full_image=True,
refine_selection=False,
report=lambda *_args: None,
)
source.matrix_world = object()
@@ -192,7 +186,7 @@ def test_cutout_reuses_image_lasso_gesture_and_double_click_selects_all(monkeypa
operator, context, SimpleNamespace(value="DOUBLE_CLICK")
)
assert issubclass(cutout_main.SelectCutoutSelection, CropSelectionGesture)
assert issubclass(cutout_main.SelectCutoutSelection, ImageGesture)
assert result == {"FINISHED"}
selection_path = SelectionPath.from_json(window_manager.pie.calls[0][2].selection_path_json)
assert selection_path.points == (
@@ -205,16 +199,25 @@ def test_cutout_reuses_image_lasso_gesture_and_double_click_selects_all(monkeypa
assert not hasattr(cutout_main, "pending_selection")
def test_cutout_tool_prioritizes_double_click_before_lasso_press():
def test_cutout_tool_prioritizes_double_click_before_lasso_drag():
assert cutout_main.CutoutTool.bl_keymap[0][1]["value"] == "DOUBLE_CLICK"
assert cutout_main.CutoutTool.bl_keymap[1][1]["value"] == "PRESS"
assert cutout_main.CutoutTool.bl_keymap[1][1]["value"] == "CLICK_DRAG"
assert [entry[0] for entry in cutout_main.CutoutTool.bl_keymap[2:]] == [
"view3d.select",
"view3d.select",
]
assert cutout_main.CutoutTool.bl_keymap[2][2] == {
"properties": [("deselect_all", False)]
}
assert cutout_main.CutoutTool.bl_keymap[3][2] == {
"properties": [("toggle", True)]
}
def test_cutout_f_does_not_toggle_invert(monkeypatch):
def test_cutout_f_has_no_option_shortcut(monkeypatch):
operator = SimpleNamespace(
gesture="LASSO",
active_tool_id="anyimage.cutout_lasso",
invert=False,
)
monkeypatch.setattr(viewport, "active_view3d_tool_id", lambda _context: None)
@@ -225,7 +228,6 @@ def test_cutout_f_does_not_toggle_invert(monkeypatch):
)
assert result == {"PASS_THROUGH"}
assert operator.invert is False
def test_cutout_status_has_no_letter_shortcut():