import unittest from unittest.mock import patch from app import batch_runner from app.config import settings class BatchRunnerCliTests(unittest.TestCase): def test_readme_options_are_parsed(self): args = batch_runner.parse_args( [ "--input", "/tmp/input", "--output", "/tmp/output", "--sample-interval", "5", "--no-recursive", "--disable-detection", "--disable-classification", "--disable-density", "--det-conf-thres", "0.15", "--det-iou-thres", "0.40", "--det-second-conf-thres", "0.45", "--min-box-area-ratio", "0.0001", "--max-box-area-ratio", "0.10", "--max-box-aspect-ratio", "4.0", "--detection-roi-y-min-ratio", "0.08", "--detection-roi-y-max-ratio", "0.82", "--cls-show-conf-thres", "0.60", "--large-box-area-ratio", "0.003", "--large-box-cls-show-conf-thres", "0.30", "--show-large-box-name-always", "false", "--dense-scene-count-thres", "30", "--hide-cls-name-in-dense-scene", "false", "--crop-padding-ratio", "0.10", "--max-detections", "75", "--uniform-species-color", "true", "--species-name-remap-min-confidence", "0.80", "--binary-dark-species-name", "东方白鹳", "--binary-light-species-name", "白琵鹭", "--binary-dark-pixel-threshold", "85", "--binary-dark-ratio-threshold", "0.12", "--dark-override-species-name", "雁鸭类", "--dark-override-species-class-id", "60", "--dark-override-pixel-threshold", "75", "--dark-override-ratio-threshold", "0.30", "--dark-override-min-box-area-ratio", "0.00001", "--dark-override-max-box-area-ratio", "0.002", "--dark-override-roi-y-min-ratio", "0.60", "--dark-override-roi-y-max-ratio", "0.95", "--region-override-species-name", "白头鹤", "--region-override-species-class-id", "28", "--region-override-source-species-names", "白鹤,白头鹤,白枕鹤", "--region-override-roi-y-min-ratio", "0.24", "--region-override-roi-y-max-ratio", "0.45", "--enable-crane-appearance-correction", "--crane-white-ratio-threshold", "0.33", "--crane-dark-ratio-threshold", "0.29", "--crane-tail-white-ratio-threshold", "0.14", "--crane-uncertain-hide-label", "false", ] ) self.assertEqual(args.input, "/tmp/input") self.assertEqual(args.output, "/tmp/output") self.assertEqual(args.sample_interval, 5) self.assertFalse(args.recursive) self.assertFalse(args.enable_detection) self.assertFalse(args.enable_classification) self.assertFalse(args.enable_density) self.assertEqual(args.det_conf_thres, 0.15) self.assertEqual(args.det_iou_thres, 0.40) self.assertEqual(args.det_second_conf_thres, 0.45) self.assertEqual(args.min_box_area_ratio, 0.0001) self.assertEqual(args.max_box_area_ratio, 0.10) self.assertEqual(args.max_box_aspect_ratio, 4.0) self.assertEqual(args.detection_roi_y_min_ratio, 0.08) self.assertEqual(args.detection_roi_y_max_ratio, 0.82) self.assertEqual(args.cls_show_conf_thres, 0.60) self.assertEqual(args.large_box_area_ratio, 0.003) self.assertEqual(args.large_box_cls_show_conf_thres, 0.30) self.assertFalse(args.show_large_box_name_always) self.assertEqual(args.dense_scene_count_thres, 30) self.assertFalse(args.hide_cls_name_in_dense_scene) self.assertEqual(args.crop_padding_ratio, 0.10) self.assertEqual(args.max_detections, 75) self.assertTrue(args.uniform_species_color) self.assertEqual(args.species_name_remap_min_confidence, 0.80) self.assertEqual(args.binary_dark_species_name, "东方白鹳") self.assertEqual(args.binary_light_species_name, "白琵鹭") self.assertEqual(args.binary_dark_pixel_threshold, 85) self.assertEqual(args.binary_dark_ratio_threshold, 0.12) self.assertEqual(args.dark_override_species_name, "雁鸭类") self.assertEqual(args.dark_override_species_class_id, 60) self.assertEqual(args.dark_override_pixel_threshold, 75) self.assertEqual(args.dark_override_ratio_threshold, 0.30) self.assertEqual(args.dark_override_min_box_area_ratio, 0.00001) self.assertEqual(args.dark_override_max_box_area_ratio, 0.002) self.assertEqual(args.dark_override_roi_y_min_ratio, 0.60) self.assertEqual(args.dark_override_roi_y_max_ratio, 0.95) self.assertEqual(args.region_override_species_name, "白头鹤") self.assertEqual(args.region_override_species_class_id, 28) self.assertEqual( args.region_override_source_species_names, "白鹤,白头鹤,白枕鹤", ) self.assertEqual(args.region_override_roi_y_min_ratio, 0.24) self.assertEqual(args.region_override_roi_y_max_ratio, 0.45) self.assertTrue(args.enable_crane_appearance_correction) self.assertEqual(args.crane_white_ratio_threshold, 0.33) self.assertEqual(args.crane_dark_ratio_threshold, 0.29) self.assertEqual(args.crane_tail_white_ratio_threshold, 0.14) self.assertFalse(args.crane_uncertain_hide_label) def test_readme_defaults_enable_recursive_and_all_models(self): args = batch_runner.parse_args(["--input", "/tmp/input"]) self.assertTrue(args.recursive) self.assertEqual(args.enable_detection, settings.enable_detection) self.assertEqual( args.enable_classification, settings.enable_classification, ) self.assertEqual(args.enable_density, settings.enable_density) self.assertEqual(args.sample_interval, settings.video_sample_interval) self.assertEqual(args.max_box_area_ratio, 1.0) self.assertEqual( args.uniform_species_color, settings.uniform_species_color, ) def test_main_applies_cli_values_before_running_batch(self): setting_names = ( "enable_detection", "enable_classification", "enable_density", "det_conf_thres", "hide_cls_name_in_dense_scene", "max_detections", "uniform_species_color", ) original_values = { name: getattr(settings, name) for name in setting_names } try: with patch.object( batch_runner, "run_batch", return_value={"success": True}, ) as run_batch_mock: result = batch_runner.main( [ "--input", "/tmp/input", "--output", "/tmp/output", "--disable-detection", "--disable-classification", "--disable-density", "--det-conf-thres", "0.15", "--hide-cls-name-in-dense-scene", "false", "--max-detections", "75", "--uniform-species-color", "true", ] ) self.assertEqual(result, {"success": True}) self.assertFalse(settings.enable_detection) self.assertFalse(settings.enable_classification) self.assertFalse(settings.enable_density) self.assertEqual(settings.det_conf_thres, 0.15) self.assertFalse(settings.hide_cls_name_in_dense_scene) self.assertEqual(settings.max_detections, 75) self.assertTrue(settings.uniform_species_color) run_batch_mock.assert_called_once_with( input_path="/tmp/input", output_base="/tmp/output", recursive=True, sample_interval=2, ) finally: for name, value in original_values.items(): object.__setattr__(settings, name, value) if __name__ == "__main__": unittest.main()