import json import sys import tempfile import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(REPO_ROOT)) from approach.ape_stage import extract_image_id, run_ape_stage class ApeStageTests(unittest.TestCase): def test_extract_image_id_requires_documented_basename_shape(self): self.assertEqual(extract_image_id("123_4.jpg"), 123004) with self.assertRaises(ValueError): extract_image_id("123_4_view_a.png") with self.assertRaises(ValueError): extract_image_id("123_view_a.png") def test_multiple_records_are_enriched_and_checkpointed_as_valid_json(self): calls = [] def fake_inference(**kwargs): calls.append(kwargs) category = kwargs["text_prompt"].split(":", 1)[0] return [ { "category_name": category, "bbox": [1, 2, 3, 4], "score": 0.9, } ] records = [ {"question_id": 0, "text": {"objects": {"button": "round red"}}}, {"question_id": 1, "text": {"objects": {"lever": "long silver"}}}, ] questions = { 0: {"image": "123_4.jpg"}, 1: {"image": "456_78.jpg"}, } with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) output_path = root / "predictions.json" results, errors = run_ape_stage( records=records, questions=questions, images_dir=root / "images", output_path=output_path, inference=fake_inference, inference_kwargs={"confidence_threshold": 0.15}, resume=False, ) on_disk = json.loads(output_path.read_text()) self.assertEqual(errors, []) self.assertEqual(results, on_disk) self.assertEqual(len(results), 2) self.assertEqual(results[0]["image_id"], 123004) self.assertEqual(results[0]["category_id"], "button") self.assertEqual(results[1]["image_id"], 456078) self.assertEqual(results[1]["category_id"], "lever") self.assertEqual(calls[0]["confidence_threshold"], 0.15) def test_resume_does_not_duplicate_completed_images(self): calls = [] def fake_inference(**kwargs): calls.append(kwargs) return [ { "category_name": "button", "bbox": [1, 2, 3, 4], "score": 0.9, } ] records = [{"question_id": 0, "text": {"objects": {"button": "red"}}}] questions = {0: {"image": "123_4.jpg"}} with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) output_path = root / "predictions.json" first, _ = run_ape_stage( records, questions, root, output_path, fake_inference, resume=False, ) second, _ = run_ape_stage( records, questions, root, output_path, fake_inference, resume=True, ) self.assertEqual(first, second) self.assertEqual(len(calls), 1) def test_resume_tracks_completed_images_with_zero_detections(self): calls = [] def empty_inference(**kwargs): calls.append(kwargs) return [] records = [{"question_id": "0", "text": {"objects": {"button": "red"}}}] questions = {0: {"image": "123_4.jpg"}} with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) output_path = root / "predictions.json" first, _ = run_ape_stage( records, questions, root, output_path, empty_inference, resume=False, ) second, _ = run_ape_stage( records, questions, root, output_path, empty_inference, resume=True, ) progress = json.loads((root / "predictions.progress.json").read_text()) self.assertEqual(first, []) self.assertEqual(second, []) self.assertEqual(calls, [calls[0]]) self.assertEqual(progress["completed_image_ids"], [123004]) def test_errors_are_recorded_without_corrupting_predictions(self): def failing_inference(**kwargs): raise RuntimeError("synthetic detector failure") records = [{"question_id": 0, "text": {"objects": {"button": "red"}}}] questions = {0: {"image": "123_4.jpg"}} with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) output_path = root / "predictions.json" error_path = root / "errors.json" results, errors = run_ape_stage( records, questions, root, output_path, failing_inference, error_path=error_path, resume=False, ) self.assertEqual(json.loads(output_path.read_text()), []) self.assertEqual(json.loads(error_path.read_text()), errors) self.assertEqual(results, []) self.assertEqual(errors[0]["question_id"], 0) self.assertEqual(errors[0]["error_type"], "RuntimeError") self.assertNotIn("traceback", errors[0]) if __name__ == "__main__": unittest.main()