A long time ago, in a galaxy far far away...
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from imagepipeline.core.pipeline import Pipeline
|
||||
from imagepipeline.core.resume import materialize_external_outputs, step_outputs_complete
|
||||
from imagepipeline.core.step import StepDefinition
|
||||
from imagepipeline.modules.imagemagick_grayscale import ImageMagickGrayscale
|
||||
from imagepipeline.modules.registry import get_module
|
||||
from imagepipeline.utils.gmic import split_gmic_command
|
||||
from tests.conftest import make_png
|
||||
|
||||
|
||||
class TestGmicCommandSplit:
|
||||
def test_splits_command_and_arguments(self) -> None:
|
||||
assert split_gmic_command("-gcd_stereo_img 0,0,2.028,1,1.714,3.06,4,1,0") == [
|
||||
"-gcd_stereo_img",
|
||||
"0,0,2.028,1,1.714,3.06,4,1,0",
|
||||
]
|
||||
|
||||
def test_preserves_quoted_empty_argument(self) -> None:
|
||||
parts = split_gmic_command('-fx_custom_gradient 0,0,0,"",1,0')
|
||||
assert parts == ["-fx_custom_gradient", "0,0,0,,1,0"]
|
||||
|
||||
|
||||
class TestPipelineResume:
|
||||
@pytest.mark.skipif(not shutil.which("magick"), reason="ImageMagick not installed")
|
||||
def test_continue_skips_completed_steps(self, input_dir: Path, output_base: Path, capsys) -> None:
|
||||
with Pipeline(
|
||||
name="resume_test",
|
||||
input_dir=input_dir,
|
||||
output_base=output_base,
|
||||
verbose=True,
|
||||
) as p:
|
||||
first = p.step("imagemagick_grayscale", inputs="input")
|
||||
p.step("imagemagick_grayscale", inputs=first)
|
||||
root = p.run()
|
||||
|
||||
capsys.readouterr()
|
||||
|
||||
with Pipeline(
|
||||
name="resume_test",
|
||||
input_dir=input_dir,
|
||||
output_base=output_base,
|
||||
verbose=True,
|
||||
continue_from=root,
|
||||
) as p:
|
||||
step_a = p.step("imagemagick_grayscale", inputs="input")
|
||||
p.step("imagemagick_grayscale", inputs=step_a)
|
||||
resumed_root = p.run()
|
||||
|
||||
assert resumed_root == root
|
||||
output = capsys.readouterr().out
|
||||
assert "Skipped step imagemagick_grayscale_01" in output
|
||||
assert "Skipped step imagemagick_grayscale_02" in output
|
||||
|
||||
@pytest.mark.skipif(not shutil.which("magick"), reason="ImageMagick not installed")
|
||||
def test_existing_outputs_reuse_external_folder(
|
||||
self, input_dir: Path, output_base: Path, tmp_path: Path, capsys
|
||||
) -> None:
|
||||
external = tmp_path / "external_rembg"
|
||||
external.mkdir()
|
||||
for src in input_dir.iterdir():
|
||||
if src.is_file():
|
||||
make_png(external / src.name, width=4, height=4, rgb=(10, 20, 30))
|
||||
|
||||
with Pipeline(
|
||||
name="external_test",
|
||||
input_dir=input_dir,
|
||||
output_base=output_base,
|
||||
verbose=True,
|
||||
existing_outputs={"imagemagick_grayscale_01": external},
|
||||
) as p:
|
||||
reused = p.step("imagemagick_grayscale", inputs="input")
|
||||
p.step("imagemagick_grayscale", inputs=reused)
|
||||
root = p.run()
|
||||
|
||||
output = capsys.readouterr().out
|
||||
assert "Reused external output for imagemagick_grayscale_01" in output
|
||||
assert (root / "imagemagick_grayscale_01" / "photo_a.png").exists()
|
||||
|
||||
|
||||
class TestMaterializeExternal:
|
||||
def test_links_files_by_stem(self, tmp_path: Path) -> None:
|
||||
external = tmp_path / "external"
|
||||
external.mkdir()
|
||||
make_png(external / "photo_a.png")
|
||||
output_dir = tmp_path / "out"
|
||||
step = StepDefinition(
|
||||
step_id="imagemagick_grayscale_01",
|
||||
module_name="imagemagick_grayscale",
|
||||
module=ImageMagickGrayscale,
|
||||
input_refs=["input"],
|
||||
params={},
|
||||
output_dir_name="imagemagick_grayscale_01",
|
||||
)
|
||||
input_paths = [tmp_path / "photo_a.png"]
|
||||
paths = materialize_external_outputs(
|
||||
external,
|
||||
output_dir,
|
||||
step,
|
||||
matched_groups=[[path] for path in input_paths],
|
||||
input_paths=input_paths,
|
||||
params={},
|
||||
)
|
||||
assert len(paths) == 1
|
||||
assert paths[0].name == "photo_a.png"
|
||||
assert paths[0].is_symlink()
|
||||
|
||||
def test_step_outputs_complete(self, tmp_path: Path) -> None:
|
||||
output_dir = tmp_path / "done"
|
||||
output_dir.mkdir()
|
||||
make_png(output_dir / "photo.png")
|
||||
assert step_outputs_complete([output_dir / "photo.png"])
|
||||
assert not step_outputs_complete([output_dir / "missing.png"])
|
||||
@@ -6,6 +6,10 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from imagepipeline.core.params import validate_params
|
||||
from imagepipeline.modules.color_to_alpha import (
|
||||
ColorToAlphaModule,
|
||||
build_color_to_alpha_args,
|
||||
)
|
||||
from imagepipeline.modules.composite import CompositeModule
|
||||
from imagepipeline.modules.crop_square import CropSquareModule
|
||||
from imagepipeline.modules.darktable_style import DarktableStyleModule
|
||||
@@ -14,6 +18,10 @@ from imagepipeline.modules.imagemagick_fill import (
|
||||
build_fill_arguments,
|
||||
)
|
||||
from imagepipeline.modules.imagemagick_grayscale import ImageMagickGrayscale
|
||||
from imagepipeline.modules.imagemagick_resize import (
|
||||
ImageMagickResizeModule,
|
||||
build_resize_arguments,
|
||||
)
|
||||
from imagepipeline.modules.gmic_grayscale import GmicGrayscale
|
||||
from imagepipeline.modules.registry import get_module, list_modules
|
||||
from imagepipeline.modules.rembg import RembgModule
|
||||
@@ -31,6 +39,8 @@ class TestModuleRegistration:
|
||||
"darktable_style",
|
||||
"imagemagick_grayscale",
|
||||
"imagemagick_fill",
|
||||
"color_to_alpha",
|
||||
"imagemagick_resize",
|
||||
"crop_square",
|
||||
):
|
||||
assert name in names
|
||||
@@ -42,6 +52,85 @@ class TestModuleRegistration:
|
||||
assert get_module("darktable_style") is DarktableStyleModule
|
||||
assert get_module("crop_square") is CropSquareModule
|
||||
assert get_module("imagemagick_fill") is ImageMagickFillModule
|
||||
assert get_module("color_to_alpha") is ColorToAlphaModule
|
||||
|
||||
|
||||
class TestImageMagickResize:
|
||||
def test_build_resize_arguments(self) -> None:
|
||||
assert build_resize_arguments(max_edge=2000) == [
|
||||
"-auto-orient",
|
||||
"-resize",
|
||||
"2000x2000>",
|
||||
]
|
||||
|
||||
def test_rejects_non_positive_max_edge(self) -> None:
|
||||
with pytest.raises(ValueError, match="positive"):
|
||||
build_resize_arguments(max_edge=0)
|
||||
|
||||
def test_default_max_edge(self) -> None:
|
||||
params = ImageMagickResizeModule.validate_module_params({})
|
||||
assert params["max_edge"] == 2000
|
||||
|
||||
|
||||
class TestColorToAlpha:
|
||||
def test_build_args_exact(self) -> None:
|
||||
assert build_color_to_alpha_args(color="#00ff00", fuzz=0.0) == [
|
||||
"-alpha",
|
||||
"on",
|
||||
"-transparent",
|
||||
"#00ff00",
|
||||
]
|
||||
|
||||
def test_build_args_with_fuzz(self) -> None:
|
||||
assert build_color_to_alpha_args(color="ffffff", fuzz=2.5) == [
|
||||
"-alpha",
|
||||
"on",
|
||||
"-fuzz",
|
||||
"2.5%",
|
||||
"-transparent",
|
||||
"#ffffff",
|
||||
]
|
||||
|
||||
def test_requires_color(self) -> None:
|
||||
with pytest.raises(ValueError, match="required"):
|
||||
ColorToAlphaModule.validate_module_params({})
|
||||
|
||||
@pytest.mark.skipif(not has_magick, reason="ImageMagick not installed")
|
||||
def test_makes_matching_color_transparent(self, tmp_path: Path) -> None:
|
||||
from imagepipeline.core.context import ModuleContext
|
||||
from imagepipeline.utils.subprocess import run_command
|
||||
|
||||
src = tmp_path / "green.png"
|
||||
output_dir = tmp_path / "out"
|
||||
output_dir.mkdir()
|
||||
magick = shutil.which("magick") or shutil.which("convert")
|
||||
run_command([magick, "-size", "8x8", "xc:#00ff00", str(src)])
|
||||
|
||||
ctx = ModuleContext(
|
||||
input_paths=[src],
|
||||
matched_groups=[],
|
||||
output_dir=output_dir,
|
||||
params=ColorToAlphaModule.validate_module_params({"color": "#00ff00"}),
|
||||
pipeline_output_root=tmp_path,
|
||||
step_id="color_to_alpha_01",
|
||||
logger=None,
|
||||
)
|
||||
ColorToAlphaModule().run(ctx)
|
||||
|
||||
dst = output_dir / "green.png"
|
||||
assert dst.is_file()
|
||||
result = run_command(
|
||||
[
|
||||
magick,
|
||||
str(dst),
|
||||
"-alpha",
|
||||
"extract",
|
||||
"-format",
|
||||
"%[fx:mean]",
|
||||
"info:",
|
||||
]
|
||||
)
|
||||
assert float(result.stdout.strip()) == 0.0
|
||||
|
||||
|
||||
class TestImageMagickFill:
|
||||
|
||||
Reference in New Issue
Block a user