|
13 | 13 | import os |
14 | 14 | import sys |
15 | 15 | import tempfile |
16 | | -from unittest.mock import patch |
| 16 | +import types |
| 17 | +from unittest.mock import MagicMock, patch |
17 | 18 |
|
18 | 19 | import pytest |
19 | 20 |
|
@@ -585,6 +586,118 @@ def on_prog(pct, msg=""): |
585 | 586 | generate_consistent_scene("scene prompt", profile, on_progress=on_prog) |
586 | 587 | assert len(progress_calls) >= 2 |
587 | 588 |
|
| 589 | + def test_ip_adapter_loads_embeddings_without_pickle(self): |
| 590 | + from opencut.core import character_consistency |
| 591 | + |
| 592 | + fake_diffusers = types.ModuleType("diffusers") |
| 593 | + |
| 594 | + class DummyPipeline: |
| 595 | + @classmethod |
| 596 | + def from_pretrained(cls, *_args, **_kwargs): |
| 597 | + return cls() |
| 598 | + |
| 599 | + def to(self, _device): |
| 600 | + return self |
| 601 | + |
| 602 | + fake_diffusers.StableDiffusionPipeline = DummyPipeline |
| 603 | + fake_torch = types.ModuleType("torch") |
| 604 | + fake_torch.float16 = object() |
| 605 | + fake_torch.cuda = types.SimpleNamespace(is_available=lambda: False) |
| 606 | + |
| 607 | + with patch.dict(sys.modules, {"diffusers": fake_diffusers, "torch": fake_torch}), \ |
| 608 | + patch("opencut.core.character_consistency.ensure_package", return_value=True), \ |
| 609 | + patch("numpy.load", return_value={}) as load_mock: |
| 610 | + result = character_consistency._generate_with_ip_adapter( |
| 611 | + "hero in a city", |
| 612 | + "embeddings.npz", |
| 613 | + "out.mp4", |
| 614 | + 1.0, |
| 615 | + ) |
| 616 | + |
| 617 | + assert result == "" |
| 618 | + load_mock.assert_called_once_with("embeddings.npz", allow_pickle=False) |
| 619 | + |
| 620 | + |
| 621 | +class TestDepthSegmentCompose: |
| 622 | + def test_depth_archives_load_without_pickle(self): |
| 623 | + from opencut.core import compose_depth_segment |
| 624 | + |
| 625 | + fake_cv2 = types.ModuleType("cv2") |
| 626 | + fake_cv2.CAP_PROP_FPS = 5 |
| 627 | + fake_cv2.CAP_PROP_FRAME_WIDTH = 3 |
| 628 | + fake_cv2.CAP_PROP_FRAME_HEIGHT = 4 |
| 629 | + fake_cv2.VideoWriter_fourcc = lambda *_args: 0 |
| 630 | + |
| 631 | + class DummyCapture: |
| 632 | + def __init__(self, _path): |
| 633 | + pass |
| 634 | + |
| 635 | + def get(self, prop): |
| 636 | + if prop == fake_cv2.CAP_PROP_FPS: |
| 637 | + return 24 |
| 638 | + if prop == fake_cv2.CAP_PROP_FRAME_WIDTH: |
| 639 | + return 1920 |
| 640 | + if prop == fake_cv2.CAP_PROP_FRAME_HEIGHT: |
| 641 | + return 1080 |
| 642 | + return 0 |
| 643 | + |
| 644 | + def read(self): |
| 645 | + return False, None |
| 646 | + |
| 647 | + def release(self): |
| 648 | + pass |
| 649 | + |
| 650 | + class DummyWriter: |
| 651 | + def __init__(self, *_args): |
| 652 | + pass |
| 653 | + |
| 654 | + def write(self, _frame): |
| 655 | + pass |
| 656 | + |
| 657 | + def release(self): |
| 658 | + pass |
| 659 | + |
| 660 | + fake_cv2.VideoCapture = DummyCapture |
| 661 | + fake_cv2.VideoWriter = DummyWriter |
| 662 | + |
| 663 | + fake_segment = types.ModuleType("opencut.core.segment_sam2") |
| 664 | + fake_segment.segment_video = lambda **_kwargs: types.SimpleNamespace(mask_count=0) |
| 665 | + fake_depth = types.ModuleType("opencut.core.depth_anything_v2") |
| 666 | + fake_depth.estimate_depth = lambda **_kwargs: types.SimpleNamespace(frames_processed=0) |
| 667 | + |
| 668 | + depth_archive = MagicMock() |
| 669 | + depth_archive.__getitem__.return_value = [] |
| 670 | + |
| 671 | + with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as source: |
| 672 | + source.write(b"fake") |
| 673 | + video_path = source.name |
| 674 | + |
| 675 | + output_path = "" |
| 676 | + try: |
| 677 | + with patch.dict( |
| 678 | + sys.modules, |
| 679 | + { |
| 680 | + "cv2": fake_cv2, |
| 681 | + "opencut.core.segment_sam2": fake_segment, |
| 682 | + "opencut.core.depth_anything_v2": fake_depth, |
| 683 | + }, |
| 684 | + ), patch("opencut.core.compose_depth_segment.check_composite_available", return_value=True), \ |
| 685 | + patch("numpy.load", return_value=depth_archive) as load_mock: |
| 686 | + result = compose_depth_segment.compose( |
| 687 | + video_path=video_path, |
| 688 | + prompts=[{"point": [10, 10]}], |
| 689 | + effects=["depth_parallax"], |
| 690 | + ) |
| 691 | + output_path = result.output |
| 692 | + |
| 693 | + assert result.frames_processed == 0 |
| 694 | + load_mock.assert_called_once() |
| 695 | + assert load_mock.call_args.kwargs["allow_pickle"] is False |
| 696 | + finally: |
| 697 | + os.unlink(video_path) |
| 698 | + if output_path and os.path.isfile(output_path): |
| 699 | + os.unlink(output_path) |
| 700 | + |
588 | 701 |
|
589 | 702 | # =================================================================== |
590 | 703 | # Motion Brush - Dataclasses |
|
0 commit comments