diff --git a/tools/blender/run.py b/tools/blender/run.py index ac04fa1..8a62841 100644 --- a/tools/blender/run.py +++ b/tools/blender/run.py @@ -235,7 +235,7 @@ def prepare(root, job): continue require((left["scene_name"], left["frame_index"]) == (right["scene_name"], right["frame_index"]), "continuous 경계의 Scene/논리 프레임이 다릅니다") - if media_by_id: + if left["id"] in media_by_id and right["id"] in media_by_id: require(media_by_id[left["id"]]["path"] == media_by_id[right["id"]]["path"], "continuous 끝점은 같은 실제 PNG 경로를 공유해야 합니다") objects = list(job.get("require_object", [])) diff --git a/tools/blender/test_runner.py b/tools/blender/test_runner.py index 96a983b..1bd1b38 100644 --- a/tools/blender/test_runner.py +++ b/tools/blender/test_runner.py @@ -384,6 +384,51 @@ class RequestTests(unittest.TestCase): with self.subTest(stage=input_stage, change=change), self.assertRaises(ValueError): run.prepare(ROOT, job) + def test_partial_11_allows_the_next_endpoint(self): + folder, project, artifacts, job = endpoint_fixture(motion=True) + images = artifacts["11-images"]["payload"] + images["result"]["frames"] = images["result"]["frames"][:1] + for frame in images["request"]["frames"]: + frame["scene_name"] = "SceneA" + artifacts["08-scenes"]["payload"]["connections"][0]["mode"] = "continuous" + pointer = next(s for s in project["stages"] if s["stage_id"] == "11-images") + pointer["status"] = "draft" + job.update(artifact=pointer["artifact_path"], frame_id="c1-end", frame=10, + start=None, end=None, cut_id=[]) + write(folder / "project.json", project) + write(folder / "stages/08-scenes/r2/artifact.json", artifacts["08-scenes"]) + write(ROOT / job["artifact"], artifacts["11-images"]) + self.assertEqual(run.prepare(ROOT, job)["frames"], [10]) + + def test_partial_11_rejects_different_completed_boundary_paths(self): + folder, project, artifacts, job = endpoint_fixture(motion=True) + images = artifacts["11-images"]["payload"] + images["result"]["frames"] = images["result"]["frames"][1:3] + for frame in images["request"]["frames"]: + frame["scene_name"] = "SceneA" + artifacts["08-scenes"]["payload"]["connections"][0]["mode"] = "continuous" + pointer = next(s for s in project["stages"] if s["stage_id"] == "11-images") + pointer["status"] = "draft" + job.update(artifact=pointer["artifact_path"], frame_id="c1-end", frame=10, + start=None, end=None, cut_id=[]) + write(folder / "project.json", project) + write(folder / "stages/08-scenes/r2/artifact.json", artifacts["08-scenes"]) + write(ROOT / job["artifact"], artifacts["11-images"]) + with self.assertRaisesRegex(ValueError, "같은 실제 PNG"): + run.prepare(ROOT, job) + + def test_12_rejects_partial_or_unverified_11_marked_complete(self): + for change in ("partial", "unverified"): + folder, project, artifacts, job = endpoint_fixture(motion=True) + frames = artifacts["11-images"]["payload"]["result"]["frames"] + if change == "partial": + artifacts["11-images"]["payload"]["result"]["frames"] = frames[:1] + else: + frames[1]["media"]["verified"] = False + write(folder / "stages/11-images/r2/artifact.json", artifacts["11-images"]) + with self.subTest(change=change), self.assertRaisesRegex(ValueError, "완료 선행 결과"): + run.prepare(ROOT, job) + if __name__ == "__main__": unittest.main()