Files
AnyImage/tests/anyimage/operators/test_clipboard_cache.py
nan 198fcbf55b
Some checks failed
Test / test (push) Has been cancelled
Initial public release
2026-09-19 21:31:36 +08:00

112 lines
4.9 KiB
Python

"""Verify cache identity against edits to real Blender images."""
from io import BytesIO
from types import SimpleNamespace
import bpy
import numpy as np
import pytest
from PIL import Image
from anyimage.common import image as images
from anyimage.common.selection import SelectionMask
from anyimage.operators.clipboard_image import actions
from anyimage.operators.mask_tool import EditImageAlpha
@pytest.fixture
def clipboard():
bpy.ops.wm.read_factory_settings(use_empty=True)
stream = BytesIO()
Image.fromarray(np.array([[[100, 150, 200, 255], [50, 80, 120, 0]]], np.uint8)).save(stream, format="PNG")
yield stream.getvalue()
bpy.ops.wm.read_factory_settings(use_empty=True)
@pytest.mark.parametrize("edit", ["paint", "packed", "hidden_rgb", "size", "space", "alpha", "unreadable"])
def test_changed_cache_is_skipped_without_modifying_candidate(clipboard, edit, monkeypatch):
original, reused = actions.acquire_packed_image(clipboard, ".png")
expected = images.image_pixels(original)
assert not reused
assert actions.acquire_packed_image(clipboard, ".png") == (original, True)
if edit in {"paint", "packed"}:
original.pixels[0] = 1
if edit == "packed":
original.pack()
elif edit == "hidden_rgb":
original.pixels[4] = 1
elif edit == "size":
original.scale(4, 2)
elif edit == "space":
original.colorspace_settings.name = "Non-Color"
elif edit == "alpha":
original.alpha_mode = "PREMUL"
else:
read = actions._clipboard_content_hash
def fail_candidate(image):
if image == original:
raise RuntimeError("Unreadable candidate")
return read(image)
monkeypatch.setattr(actions, "_clipboard_content_hash", fail_candidate)
state = images.image_content_state(original)
current_pixels = original.pixels[:]
replacement, reused = actions.acquire_packed_image(clipboard, ".png")
assert replacement != original and not reused
np.testing.assert_array_equal(images.image_pixels(replacement), expected)
assert (original.colorspace_settings.name, original.alpha_mode) == (state["colorspace"], state["alpha_mode"])
assert bytes(original.packed_file.data) == state["packed"]
np.testing.assert_array_equal(original.pixels[:], current_pixels)
@pytest.mark.parametrize("shared", [False, True])
def test_mask_edit_then_paste_preserves_original_clipboard_content(clipboard, shared):
original, _ = actions.acquire_packed_image(clipboard, ".png")
expected = images.image_pixels(original)
owner = bpy.data.objects.new("Clipboard Empty", None)
owner.empty_display_type = "IMAGE"
owner.data = original
bpy.context.collection.objects.link(owner)
if shared:
other = owner.copy()
bpy.context.collection.objects.link(other)
mask = SelectionMask(np.full((1, 2), .5, np.float32), (0, 0, 2, 1))
assert EditImageAlpha._apply_mask(SimpleNamespace(mode="SUBTRACT"), bpy.context, owner, mask) == {"FINISHED"}
edited = owner.data
edited_pixels = images.image_pixels(edited)
assert edited.packed_file
assert (edited == original) == (not shared)
pasted, reused = actions.acquire_packed_image(clipboard, ".png")
assert reused == shared
assert pasted != edited
np.testing.assert_array_equal(images.image_pixels(pasted), expected)
np.testing.assert_array_equal(images.image_pixels(edited), edited_pixels)
def test_painted_clipboard_plane_keeps_reference_node_and_brush_cache(clipboard, monkeypatch):
original, _ = actions.acquire_packed_image(clipboard, ".png")
expected = images.image_pixels(original)
monkeypatch.setattr(actions, "_place_in_view", lambda *_: None)
monkeypatch.setattr(actions, "_select_only", lambda *_: None)
obj = actions.add_image_plane(bpy.context, original, shadeless=False)
color = next(n.image for n in obj.data.materials[0].node_tree.nodes if n.type == "TEX_IMAGE")
assert color != original and actions.CLIPBOARD_HASH_PROPERTY not in color
assert actions.CLIPBOARD_CONTENT_PROPERTY not in color
color.pixels[0] = 1
color.pack()
for _ in range(3):
pasted, reused = actions.acquire_packed_image(clipboard, ".png")
assert pasted == original and reused
np.testing.assert_array_equal(images.image_pixels(pasted), expected)
def test_failed_clipboard_plane_placement_releases_created_data(clipboard, monkeypatch):
original, _ = actions.acquire_packed_image(clipboard, ".png")
before = {name: set(getattr(bpy.data, name)) for name in ("images", "objects", "meshes", "materials")}
def fail(*_):
raise RuntimeError("Placement failed")
monkeypatch.setattr(actions, "_place_in_view", fail)
with pytest.raises(RuntimeError, match="Placement failed"):
actions.add_image_plane(bpy.context, original)
for name, items in before.items():
assert set(getattr(bpy.data, name)) == items