============================= test session starts ============================= platform win32 -- Python 3.11.18, pytest-9.2.0, pluggy-1.7.0 -- C:\GitRunner\actions-runner\_work\_temp\face-local-test-venv\Scripts\python.exe cachedir: .pytest_cache PySide6 6.10.3 -- Qt runtime 6.10.3 -- Qt compiled 6.10.3 rootdir: C:\GitRunner\actions-runner\_work\local_ai_face_recognizer\local_ai_face_recognizer configfile: pyproject.toml plugins: cov-7.1.0, qt-4.5.0, timeout-2.4.0 timeout: 120.0s timeout method: thread timeout func_only: False collecting ... collected 2017 items tests/test_accel.py::TestOnnxProviders::test_cuda_keeps_priority_on_windows PASSED [ 0%] tests/test_accel.py::TestOnnxProviders::test_windows_cpu_only_unchanged PASSED [ 0%] tests/test_accel.py::TestOnnxProviders::test_macos_adds_coreml_before_cpu PASSED [ 0%] tests/test_accel.py::TestOnnxProviders::test_macos_cuda_still_wins PASSED [ 0%] tests/test_accel.py::TestOnnxProviders::test_no_onnxruntime_falls_back_to_cpu PASSED [ 0%] tests/test_accel.py::TestClassify::test_cuda PASSED [ 0%] tests/test_accel.py::TestClassify::test_coreml_partial_offload PASSED [ 0%] tests/test_accel.py::TestClassify::test_cpu_only PASSED [ 0%] tests/test_accel.py::TestClassify::test_empty PASSED [ 0%] tests/test_accel.py::TestRegistry::test_summary_empty PASSED [ 0%] tests/test_accel.py::TestRegistry::test_summary_prefers_strongest PASSED [ 0%] tests/test_accel.py::TestRegistry::test_report_onnx_classifies PASSED [ 0%] tests/test_accel.py::TestRegistry::test_detail_lines PASSED [ 0%] tests/test_ai_face_detection_service.py::test_detect_images_persists_results PASSED [ 0%] tests/test_ai_face_detection_service.py::test_rerun_replaces_previous_rows PASSED [ 0%] tests/test_ai_face_detection_service.py::test_config_thresholds_are_forwarded_to_detector PASSED [ 0%] tests/test_ai_face_detection_service.py::test_missing_model_reports_error_without_raising PASSED [ 0%] tests/test_ai_face_detection_service.py::test_unreadable_image_is_counted_failed_and_batch_continues PASSED [ 0%] tests/test_ai_face_detection_service.py::test_disabled_config_is_a_noop PASSED [ 0%] tests/test_ai_face_detection_service.py::test_verifier_drops_unconfirmed_low_confidence PASSED [ 0%] tests/test_ai_face_detection_service.py::test_verification_disabled_keeps_all PASSED [ 1%] tests/test_ai_face_detection_service.py::test_without_detection_config_gate_is_skipped PASSED [ 1%] tests/test_ai_face_detection_service.py::test_verifier_keeps_confirmed_low_confidence PASSED [ 1%] tests/test_ai_face_detection_service.py::test_dropped_count_is_recorded_on_stats PASSED [ 1%] tests/test_ai_face_detection_service.py::test_landmark_geometry_flag_reaches_lazy_detector PASSED [ 1%] tests/test_ai_face_detection_service.py::test_classic_face_rows_and_flags_stay_untouched PASSED [ 1%] tests/test_alignment.py::TestSquareCrop::test_output_size_is_exact PASSED [ 1%] tests/test_alignment.py::TestSquareCrop::test_aspect_ratio_preserved_for_tall_box PASSED [ 1%] tests/test_alignment.py::TestSquareCrop::test_box_at_edge_is_padded_not_distorted PASSED [ 1%] tests/test_alignment.py::TestSquareCrop::test_degenerate_box_returns_none PASSED [ 1%] tests/test_alignment.py::TestAlign5pt::test_landmarks_map_to_template PASSED [ 1%] tests/test_alignment.py::TestAlign5pt::test_wrong_landmark_count_returns_none PASSED [ 1%] tests/test_alignment.py::TestMakeFaceCrop::test_legacy_mode_matches_old_behaviour PASSED [ 1%] tests/test_alignment.py::TestMakeFaceCrop::test_aligned_falls_back_to_square_without_landmarks PASSED [ 1%] tests/test_alignment.py::TestMakeFaceCrop::test_aligned_with_landmarks_warps_to_template PASSED [ 1%] tests/test_alignment.py::TestMakeFaceCrop::test_template_unchanged_module_constant PASSED [ 1%] tests/test_app_settings.py::TestAppQsettings::test_returns_ini_backed_settings PASSED [ 1%] tests/test_app_settings.py::TestAppQsettings::test_same_file_on_repeated_calls PASSED [ 1%] tests/test_app_settings.py::TestMigrateLegacySettings::test_no_op_when_new_file_exists PASSED [ 1%] tests/test_app_settings.py::TestMigrateLegacySettings::test_no_op_when_legacy_store_empty PASSED [ 1%] tests/test_app_settings.py::TestMigrateLegacySettings::test_migrates_keys_from_legacy_store PASSED [ 2%] tests/test_app_settings.py::TestMigrateLegacySettings::test_second_call_is_idempotent PASSED [ 2%] tests/test_astro_export_worker.py::TestAstroExportWorker::test_emits_finished_ok PASSED [ 2%] tests/test_astro_export_worker.py::TestAstroExportWorker::test_reports_progress PASSED [ 2%] tests/test_astro_export_worker.py::TestAstroExportWorker::test_node_error_kind PASSED [ 2%] tests/test_astro_export_worker.py::TestAstroExportWorker::test_generic_error_kind PASSED [ 2%] tests/test_audio_level_meter.py::test_level_to_fraction_clamps_and_scales PASSED [ 2%] tests/test_auto_assignments_tab_selection.py::test_revert_one_row_keeps_other_ticks PASSED [ 2%] tests/test_auto_assignments_tab_selection.py::test_changing_sort_keeps_ticks PASSED [ 2%] tests/test_build_release_workflow.py::test_release_artifact_download_inline_python_compiles PASSED [ 2%] tests/test_bulk_reassign.py::test_bulk_move_some_faces PASSED [ 2%] tests/test_bulk_reassign.py::test_bulk_move_is_atomic_with_missing_target PASSED [ 2%] tests/test_bulk_reassign.py::test_bulk_move_skips_missing_and_already_assigned PASSED [ 2%] tests/test_bulk_reassign.py::test_undo_restores_original_person PASSED [ 2%] tests/test_bulk_reassign.py::test_undo_recreates_emptied_unknown_cluster PASSED [ 2%] tests/test_cleanup_empty_unknown_persons.py::test_empty_unknown_person_is_deleted PASSED [ 2%] tests/test_cleanup_empty_unknown_persons.py::test_unknown_person_with_a_face_is_kept PASSED [ 2%] tests/test_cleanup_empty_unknown_persons.py::test_protected_unknown_person_is_never_deleted PASSED [ 2%] tests/test_cleanup_empty_unknown_persons.py::test_named_person_without_faces_is_kept PASSED [ 2%] tests/test_cleanup_empty_unknown_persons.py::test_multiple_empty_unknown_persons_all_deleted PASSED [ 2%] tests/test_cleanup_empty_unknown_persons.py::test_cleanup_is_a_noop_when_nothing_empty PASSED [ 3%] tests/test_cleanup_empty_unknown_persons.py::test_reassigning_last_face_cleans_up_source_unknown PASSED [ 3%] tests/test_cluster_panel_lazy.py::test_only_visible_rows_load PASSED [ 3%] tests/test_cluster_panel_lazy.py::test_widgets_without_loader_are_skipped PASSED [ 3%] tests/test_cluster_panel_lazy.py::test_reflow_keeps_grid_and_tiles_visible PASSED [ 3%] tests/test_cluster_panel_merge.py::test_merged_collapses_multi_group_to_one_tile PASSED [ 3%] tests/test_cluster_panel_merge.py::test_expanding_group_splices_members PASSED [ 3%] tests/test_cluster_panel_merge.py::test_unmerged_mode_shows_every_face PASSED [ 3%] tests/test_cluster_panel_merge.py::test_toggle_handler_flips_expansion PASSED [ 3%] tests/test_cluster_panel_smoke.py::test_show_sort_and_merge_roundtrip PASSED [ 3%] tests/test_cluster_panel_sort.py::test_date_ascending_orders_oldest_first_unknown_last PASSED [ 3%] tests/test_cluster_panel_sort.py::test_date_descending_orders_newest_first_unknown_last PASSED [ 3%] tests/test_cluster_panel_sort.py::test_original_order_preserved PASSED [ 3%] tests/test_cluster_panel_sort.py::test_quality_orders_best_first_unscored_as_usable PASSED [ 3%] tests/test_clustering.py::TestCosineDistance::test_identical_vectors PASSED [ 3%] tests/test_clustering.py::TestCosineDistance::test_orthogonal_vectors PASSED [ 3%] tests/test_clustering.py::TestCosineDistance::test_opposite_vectors PASSED [ 3%] tests/test_clustering.py::TestClusterEmbeddings::test_two_clear_clusters PASSED [ 3%] tests/test_clustering.py::TestClusterEmbeddings::test_empty_input PASSED [ 3%] tests/test_clustering.py::TestClusterEmbeddings::test_single_face_becomes_noise PASSED [ 3%] tests/test_clustering.py::TestClusterEmbeddings::test_same_pair_constraint_merges_clusters PASSED [ 4%] tests/test_clustering.py::TestCentroid::test_centroid_of_identical_vectors PASSED [ 4%] tests/test_clustering.py::TestCentroid::test_centroid_is_unit_length PASSED [ 4%] tests/test_clustering_service.py::TestOrphanCleanup::test_run_removes_orphan_auto_persons PASSED [ 4%] tests/test_clustering_service.py::TestOrphanCleanup::test_run_does_not_orphan_assigned_faces PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_returns_collage_data PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_cfx_extension_accepted PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_collage_uid PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_format_parsed PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_orientation PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_album_title PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_album_date PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_bg_color PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_spacing PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_node_count PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_node_geometry PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_node_theta_and_scale PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_node_theme PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_node_uid PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_node_src_raw PASSED [ 4%] tests/test_collage_parser.py::TestParseCollageFile::test_missing_src_marked PASSED [ 5%] tests/test_collage_parser.py::TestParseCollageFile::test_source_file_stored PASSED [ 5%] tests/test_collage_parser.py::TestParseCollageFile::test_file_not_found_raises PASSED [ 5%] tests/test_collage_parser.py::TestParsePartialXml::test_partial_xml_recovered PASSED [ 5%] tests/test_collage_parser.py::TestPathResolution::test_drive_prefix_stripped PASSED [ 5%] tests/test_collage_parser.py::TestPathResolution::test_lowercase_drive PASSED [ 5%] tests/test_collage_parser.py::TestPathResolution::test_no_drive_prefix PASSED [ 5%] tests/test_collage_parser.py::TestPathResolution::test_resolve_existing_file_relative_to_collage PASSED [ 5%] tests/test_collage_parser.py::TestPathResolution::test_resolve_by_filename_fallback PASSED [ 5%] tests/test_collage_parser.py::TestPathResolution::test_returns_none_if_not_found PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_full_canvas_no_zoom_face_center PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_node_in_top_left_quarter PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_zoom_clips_face_outside_view PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_zoom_100_square_full_image PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_returns_none_for_zero_dimensions PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_returns_none_for_zero_image PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_pixel_bbox_helper PASSED [ 5%] tests/test_collage_parser.py::TestProjectFaceToCollage::test_wide_image_cover_scale PASSED [ 5%] tests/test_collage_service.py::test_import_collage_persists_nodes PASSED [ 5%] tests/test_collage_service.py::test_import_collage_returns_existing_without_overwrite PASSED [ 5%] tests/test_collage_service.py::test_import_collage_overwrite_reimports PASSED [ 5%] tests/test_collage_service.py::test_import_collage_links_existing_images PASSED [ 6%] tests/test_collage_service.py::test_list_and_get_collages PASSED [ 6%] tests/test_collage_service.py::test_get_nodes_and_update_metadata PASSED [ 6%] tests/test_collage_service.py::test_update_node_metadata_missing_raises PASSED [ 6%] tests/test_collage_service.py::test_relink_images_after_scan PASSED [ 6%] tests/test_collage_service.py::test_relink_images_missing_collage_raises PASSED [ 6%] tests/test_collage_service.py::test_get_faces_for_node_and_projected_faces PASSED [ 6%] tests/test_collage_service.py::test_render_collage_image_returns_canvas PASSED [ 6%] tests/test_collage_service.py::test_render_collage_image_with_source_file PASSED [ 6%] tests/test_collage_service.py::test_export_annotated_collage_writes_files PASSED [ 6%] tests/test_config.py::test_false_positive_gate_keys_load PASSED [ 6%] tests/test_config.py::test_recognition_identity_guard_keys_round_trip PASSED [ 6%] tests/test_config.py::test_recognition_identity_guard_defaults PASSED [ 6%] tests/test_config.py::test_multistage_and_verify_all_keys_round_trip PASSED [ 6%] tests/test_config.py::test_multistage_defaults PASSED [ 6%] tests/test_config.py::test_false_positive_gate_defaults PASSED [ 6%] tests/test_config.py::test_load_config_resolves_relative_paths_against_config_location PASSED [ 6%] tests/test_config.py::test_frozen_bundle_defaults_use_user_data_dir PASSED [ 6%] tests/test_coral_detector.py::TestPureHelpers::test_find_edgetpu_lib_returns_string PASSED [ 6%] tests/test_coral_detector.py::TestPureHelpers::test_set_input_expands_batch_dim PASSED [ 6%] tests/test_coral_detector.py::TestPureHelpers::test_get_detections_parses_ssd_output PASSED [ 7%] tests/test_coral_detector.py::TestPureHelpers::test_get_detections_filters_low_scores PASSED [ 7%] tests/test_coral_detector.py::TestCoralDetectorLive::test_detect_on_blank_image SKIPPED [ 7%] tests/test_coral_detector.py::TestCoralDetectorLive::test_missing_model_raises SKIPPED [ 7%] tests/test_database.py::TestImageModel::test_create_and_retrieve PASSED [ 7%] tests/test_database.py::TestImageModel::test_unique_file_path PASSED [ 7%] tests/test_database.py::TestFaceEmbedding::test_embedding_roundtrip PASSED [ 7%] tests/test_database.py::TestFaceEmbedding::test_null_embedding_returns_none PASSED [ 7%] tests/test_database.py::TestPersonModel::test_create_person PASSED [ 7%] tests/test_database.py::TestPersonModel::test_cascade_delete_faces PASSED [ 7%] tests/test_database_package_service.py::test_export_contains_only_database_and_manifest PASSED [ 7%] tests/test_database_package_service.py::test_export_derives_missing_relative_paths_without_touching_live_db PASSED [ 7%] tests/test_database_package_service.py::test_export_reports_images_outside_the_library_root PASSED [ 7%] tests/test_database_package_service.py::test_validate_rejects_wrong_extension PASSED [ 7%] tests/test_database_package_service.py::test_validate_rejects_archive_without_manifest PASSED [ 7%] tests/test_database_package_service.py::test_validate_accepts_a_real_package PASSED [ 7%] tests/test_database_package_service.py::test_import_rebases_paths_onto_a_different_base_folder PASSED [ 7%] tests/test_database_package_service.py::test_import_counts_files_missing_at_the_rebased_location PASSED [ 7%] tests/test_database_package_service.py::test_import_clears_crop_paths_that_have_no_local_file PASSED [ 7%] tests/test_database_package_service.py::test_import_refuses_a_non_empty_destination PASSED [ 7%] tests/test_database_package_service.py::test_export_import_then_match_reattaches_a_moved_photo PASSED [ 8%] tests/test_database_package_service.py::test_import_rejects_a_traversal_member PASSED [ 8%] tests/test_debug_info.py::TestDecisionToDict::test_serialises_compact_summary PASSED [ 8%] tests/test_debug_info.py::TestDecisionToDict::test_limits_ranked_lists_to_eight PASSED [ 8%] tests/test_debug_info.py::TestComputeLayerActivations::test_returns_relu_hidden_layers PASSED [ 8%] tests/test_debug_info.py::TestComputeLayerActivations::test_empty_for_unfitted_mlp PASSED [ 8%] tests/test_debug_info.py::TestComputeLayerActivations::test_empty_for_non_mlp_object PASSED [ 8%] tests/test_debug_info.py::TestComputeDecisionPath::test_traces_winner_through_hidden_layers PASSED [ 8%] tests/test_debug_info.py::TestComputeDecisionPath::test_returns_none_without_activations PASSED [ 8%] tests/test_deep_accuracy.py::TestEvaluation::test_open_set_evaluation_reports_strangers_and_coverage PASSED [ 8%] tests/test_deep_accuracy.py::TestEvaluation::test_feedback_precision_counts_reviews_but_not_near_rows PASSED [ 8%] tests/test_deep_accuracy.py::TestQuickFixes::test_photo_day_groups_faces PASSED [ 8%] tests/test_deep_accuracy.py::TestQuickFixes::test_folds_never_exceed_photo_sessions PASSED [ 8%] tests/test_deep_accuracy.py::TestQuickFixes::test_fingerprint_tracks_embedding_values_and_embedder PASSED [ 8%] tests/test_deep_accuracy.py::TestQuickFixes::test_knn_gate_stops_a_mislabeled_face_attracting_lookalikes PASSED [ 8%] tests/test_deep_accuracy.py::TestQuickFixes::test_revert_writes_durable_veto PASSED [ 8%] tests/test_deep_accuracy.py::TestQuickFixes::test_legacy_corrections_are_backfilled_once PASSED [ 8%] tests/test_deep_accuracy.py::TestMetaClassifier::test_precision_threshold_picks_lowest_safe_cut PASSED [ 8%] tests/test_deep_accuracy.py::TestMetaClassifier::test_meta_model_is_fitted_when_lookalikes_slip_through PASSED [ 8%] tests/test_deep_accuracy.py::TestMetaClassifier::test_meta_model_stays_off_when_gates_already_reject_impostors PASSED [ 8%] tests/test_deep_accuracy.py::TestHygieneAndNegatives::test_mislabeled_face_is_flagged_and_excluded PASSED [ 9%] tests/test_deep_accuracy.py::TestHygieneAndNegatives::test_kept_label_is_never_flagged PASSED [ 9%] tests/test_deep_accuracy.py::TestHygieneAndNegatives::test_burst_duplicates_collapse PASSED [ 9%] tests/test_deep_accuracy.py::TestHygieneAndNegatives::test_unknown_class_is_learned PASSED [ 9%] tests/test_deep_accuracy.py::TestHygieneAndNegatives::test_dataset_collects_hard_negatives_and_unknown_background PASSED [ 9%] tests/test_deep_accuracy.py::TestHygieneAndNegatives::test_suspicious_label_review_flow PASSED [ 9%] tests/test_deep_accuracy.py::TestHygieneAndNegatives::test_keep_label_restores_training PASSED [ 9%] tests/test_deep_accuracy.py::TestContext::test_photo_before_birth_is_vetoed PASSED [ 9%] tests/test_deep_accuracy.py::TestContext::test_second_appearance_on_photo_needs_stronger_evidence PASSED [ 9%] tests/test_deep_accuracy.py::TestContext::test_unknown_group_vote_rescues_unsure_member PASSED [ 9%] tests/test_deep_accuracy.py::TestContext::test_mixed_unknown_group_never_votes PASSED [ 9%] tests/test_deep_accuracy.py::TestFlipTTA::test_symmetric_face_is_fully_consistent PASSED [ 9%] tests/test_deep_accuracy.py::TestFlipTTA::test_asymmetric_face_scores_lower_and_batches_match PASSED [ 9%] tests/test_deep_accuracy.py::TestFlipTTA::test_embedder_identity_tracks_tta PASSED [ 9%] tests/test_deep_accuracy.py::TestActiveLearning::test_unsure_face_becomes_near_suggestion_and_confirm_assigns PASSED [ 9%] tests/test_deep_accuracy.py::TestActiveLearning::test_rejecting_near_suggestion_vetoes_without_moving PASSED [ 9%] tests/test_deep_accuracy.py::TestActiveLearning::test_poor_review_precision_tightens_thresholds PASSED [ 9%] tests/test_deep_accuracy.py::TestActiveLearning::test_self_training_round_runs PASSED [ 9%] tests/test_deep_classifier.py::TestTraining::test_train_produces_ensemble_mode PASSED [ 9%] tests/test_deep_classifier.py::TestTraining::test_empty_dataset_stays_untrained PASSED [ 9%] tests/test_deep_classifier.py::TestTraining::test_single_person_uses_prototype_mode PASSED [ 10%] tests/test_deep_classifier.py::TestTraining::test_progress_callback_fires PASSED [ 10%] tests/test_deep_classifier.py::TestPrediction::test_known_person_is_recognized PASSED [ 10%] tests/test_deep_classifier.py::TestPrediction::test_each_person_maps_to_itself PASSED [ 10%] tests/test_deep_classifier.py::TestPrediction::test_stranger_is_rejected_as_outlier PASSED [ 10%] tests/test_deep_classifier.py::TestPrediction::test_missing_embedding_is_rejected PASSED [ 10%] tests/test_deep_classifier.py::TestPrediction::test_zero_vector_is_rejected PASSED [ 10%] tests/test_deep_classifier.py::TestClassProbabilities::test_distribution_covers_all_people_and_sums_to_one PASSED [ 10%] tests/test_deep_classifier.py::TestClassProbabilities::test_winner_matches_prediction PASSED [ 10%] tests/test_deep_classifier.py::TestClassProbabilities::test_untrained_returns_empty PASSED [ 10%] tests/test_deep_classifier.py::TestClassProbabilities::test_missing_embedding_returns_empty PASSED [ 10%] tests/test_deep_classifier.py::TestClassProbabilities::test_prototype_mode_returns_empty PASSED [ 10%] tests/test_deep_classifier.py::TestDecisionPath::test_debug_info_carries_winner_path PASSED [ 10%] tests/test_deep_classifier.py::TestDecisionPath::test_prototype_mode_has_no_path PASSED [ 10%] tests/test_deep_classifier.py::TestPersistence::test_save_and_load_roundtrip PASSED [ 10%] tests/test_deep_classifier.py::TestPersistence::test_saved_file_has_no_numpy_internal_refs PASSED [ 10%] tests/test_deep_classifier.py::TestPersistence::test_numpy_values_roundtrip PASSED [ 10%] tests/test_deep_classifier.py::TestPersistence::test_failed_save_keeps_previous_model PASSED [ 10%] tests/test_deep_classifier.py::TestPersistence::test_load_missing_file_returns_false PASSED [ 10%] tests/test_deep_classifier.py::TestPersistence::test_load_corrupt_file_returns_false PASSED [ 10%] tests/test_deep_classifier.py::TestOpenSetSafety::test_sim_floor_never_below_configured_min PASSED [ 11%] tests/test_deep_classifier.py::TestOpenSetSafety::test_small_cohort_stays_in_prototype_mode PASSED [ 11%] tests/test_deep_classifier.py::TestOpenSetSafety::test_ambiguous_face_between_two_lookalikes_is_rejected PASSED [ 11%] tests/test_deep_classifier.py::TestOpenSetSafety::test_imbalanced_tiny_cohort_does_not_avalanche PASSED [ 11%] tests/test_deep_classifier.py::TestContinualLearning::test_more_examples_increase_confidence PASSED [ 11%] tests/test_deep_classifier.py::TestContinualLearning::test_fingerprint_changes_with_data PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_overlap_resolution_runs_again_after_recognition PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_reembed_mode_recomputes_then_retrains PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_pipeline_result_carries_the_scanned_image_ids PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_invalid_mode_raises PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_valid_modes_construct PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_rebuild_reset_deletes_stale_model_file PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_ai_stage_threads_detection_config PASSED [ 11%] tests/test_deep_pipeline_worker.py::test_rebuild_reset_keeps_human_decisions PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_unknown_face_is_assigned_and_logged PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_debug_sample_face_reflects_current_model PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_debug_sample_face_none_without_model PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_assignment_decision_graph_is_lazy PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_assignment_records_decision_graph_with_debug PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_named_faces_are_never_touched PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_stranger_face_stays_unknown PASSED [ 11%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_low_confidence_detection_is_never_assigned PASSED [ 12%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_correction_veto_blocks_reassignment PASSED [ 12%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_unknown_group_face_can_be_promoted PASSED [ 12%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_unchanged_data_reuses_model PASSED [ 12%] tests/test_deep_recognition_service.py::TestTrainAndRecognize::test_changed_hidden_layers_force_retrain PASSED [ 12%] tests/test_deep_recognition_service.py::TestPerImageIdentityGuard::test_overlapping_second_box_same_person_is_skipped PASSED [ 12%] tests/test_deep_recognition_service.py::TestPerImageIdentityGuard::test_weaker_auto_incumbent_is_replaced PASSED [ 12%] tests/test_deep_recognition_service.py::TestPerImageIdentityGuard::test_human_incumbent_always_wins PASSED [ 12%] tests/test_deep_recognition_service.py::TestPerImageIdentityGuard::test_non_overlapping_second_appearance_is_kept PASSED [ 12%] tests/test_deep_recognition_service.py::TestPerImageIdentityGuard::test_two_fresh_dups_in_one_run_collapse_to_one PASSED [ 12%] tests/test_deep_recognition_service.py::TestPerImageIdentityGuard::test_guard_is_scoped_per_image PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_confirm_marks_face_as_trusted PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_confirm_assignments_batch_confirms_many PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_revert_assignments_batch_rejects_many PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_correct_assignments_batch_moves_many PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_correct_assignments_to_new_person_batch PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_correct_moves_face_and_records_corrections PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_correct_to_new_person PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_revert_restores_previous_state PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_revert_all_open_spares_reviewed_rows PASSED [ 12%] tests/test_deep_recognition_service.py::TestReviewActions::test_list_auto_assignments_returns_dto PASSED [ 13%] tests/test_deep_recognition_service.py::TestReviewActions::test_force_retrains_even_when_data_unchanged PASSED [ 13%] tests/test_deep_recognition_service.py::TestReviewActions::test_review_list_survives_train_only_run PASSED [ 13%] tests/test_deep_recognition_service.py::TestReviewActions::test_stale_assignment_is_hidden PASSED [ 13%] tests/test_deep_recognition_service.py::TestStaleModelGuards::test_delete_model_removes_file PASSED [ 13%] tests/test_deep_recognition_service.py::TestStaleModelGuards::test_recognition_skipped_when_all_people_vanished PASSED [ 13%] tests/test_deep_recognition_service.py::TestStaleModelGuards::test_face_skipped_when_predicted_person_deleted PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_artistic_suffix PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_stable_suffix PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_no_extra_suffix PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_not_deoldified_returns_none PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_case_insensitive_upper PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_case_insensitive_mixed PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_complex_name_from_requirements PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_empty_stem_returns_none PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalStem::test_stem_with_spaces PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_jpg_uppercase_artistic PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_jpg_lowercase_stable PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_no_extra_suffix PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_not_deoldified_returns_none PASSED [ 13%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_complex_name_from_requirements PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_valami_stable PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_jpeg_extension PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_png_extension PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestExtractOriginalFilename::test_preserves_original_extension_case PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestIsDeoldifiedPath::test_simple_deoldified_path PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestIsDeoldifiedPath::test_deoldified_path_with_artistic PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestIsDeoldifiedPath::test_normal_path_returns_false PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestIsDeoldifiedPath::test_filename_only_deoldified PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestIsDeoldifiedPath::test_filename_only_normal PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestIsDeoldifiedPath::test_case_insensitive PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindOriginalForDeoldified::test_finds_original_exact_extension PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindOriginalForDeoldified::test_finds_original_uppercase_extension_variant PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindOriginalForDeoldified::test_returns_none_when_original_not_in_db PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindOriginalForDeoldified::test_finds_original_in_different_folder PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindOriginalForDeoldified::test_returns_none_for_non_deoldified_image PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindDeoldifiedForOriginal::test_finds_colorized_variant PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindDeoldifiedForOriginal::test_returns_none_when_no_colorized_in_db PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindDeoldifiedForOriginal::test_different_folder_is_matched PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestFindDeoldifiedForOriginal::test_different_stem_not_matched PASSED [ 14%] tests/test_deoldified_pairing_service.py::TestPairingWithFaceData::test_original_has_faces_colorized_has_none PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairData::test_copies_faces_and_metadata_into_empty_side PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairData::test_skips_when_pair_already_in_sync PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairData::test_skips_when_both_sides_empty PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairData::test_copies_from_color_to_bw_when_bw_empty PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairData::test_metadata_only_counts_as_data PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairMerge::test_copies_the_face_added_later_to_the_filled_pair PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairMerge::test_overlapping_face_is_not_duplicated PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairMerge::test_unassigned_counterpart_gets_the_person PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairMerge::test_existing_assignment_is_never_overwritten PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairMerge::test_object_tag_moves_onto_the_original PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestSyncPairMerge::test_duplicate_object_tag_is_dropped_not_moved PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestExtractVariantLabel::test_artistic PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestExtractVariantLabel::test_stable PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestExtractVariantLabel::test_plain_deoldified PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestExtractVariantLabel::test_not_deoldified PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestExtractVariantLabel::test_case_insensitive PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestFindAllDeoldifiedForOriginal::test_returns_every_variant_sorted_by_label PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestGetComparisonGroup::test_group_from_original_orders_bw_first PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestGetComparisonGroup::test_group_from_a_colorized_variant_is_identical PASSED [ 15%] tests/test_deoldified_pairing_service.py::TestGetComparisonGroup::test_no_group_for_lone_image PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestGetComparisonGroup::test_variant_with_missing_file_is_dropped PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestGetComparisonGroup::test_no_group_when_every_variant_file_missing PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestGetComparisonGroup::test_stale_library_root_does_not_drop_variants PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestGetComparisonGroup::test_no_group_when_bw_original_file_missing PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestDeoldifiedIndex::test_lookup_from_original_stem_lists_variants_in_label_order PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestDeoldifiedIndex::test_lookup_is_case_insensitive PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestDeoldifiedIndex::test_unrelated_stem_has_no_group PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestDeoldifiedIndex::test_new_rows_are_invisible_until_invalidated PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestDeoldifiedIndex::test_unpaired_images_cost_no_queries_after_the_build PASSED [ 16%] tests/test_deoldified_pairing_service.py::TestDeoldifiedIndex::test_paired_image_still_resolves_its_group PASSED [ 16%] tests/test_detection_service.py::test_detection_service_persists_landmarks PASSED [ 16%] tests/test_detection_service.py::test_detection_service_uses_face_id_for_crop_naming PASSED [ 16%] tests/test_detection_service.py::test_redetection_does_not_duplicate_manual_face PASSED [ 16%] tests/test_detection_service.py::test_nested_detections_are_merged_into_one PASSED [ 16%] tests/test_detection_service.py::test_redetection_does_not_nest_unknown_inside_named_face PASSED [ 16%] tests/test_detection_service.py::test_adaptive_escalation_rescues_faded_photo PASSED [ 16%] tests/test_detection_service.py::test_adaptive_escalation_disabled_stays_strict PASSED [ 16%] tests/test_detection_service.py::test_adaptive_escalation_skipped_when_strict_succeeds PASSED [ 16%] tests/test_detectors.py::TestDetectionDataclass::test_clamp_within_bounds PASSED [ 16%] tests/test_detectors.py::TestDetectionDataclass::test_as_tuple PASSED [ 17%] tests/test_detectors.py::TestDetectionDataclass::test_x2_y2 PASSED [ 17%] tests/test_detectors.py::TestFactoryFallback::test_cpu_fallback_when_no_coral_config PASSED [ 17%] tests/test_detectors.py::TestFactoryFallback::test_cpu_fallback_when_coral_probe_fails PASSED [ 17%] tests/test_detectors.py::TestFactoryFallback::test_yunet_preferred_when_available PASSED [ 17%] tests/test_detectors.py::TestFaceDetectorInterface::test_detect_returns_list PASSED [ 17%] tests/test_detectors.py::TestFaceDetectorInterface::test_repr PASSED [ 17%] tests/test_detectors.py::TestCompositeDetector::test_secondary_adds_non_overlapping_face PASSED [ 17%] tests/test_detectors.py::TestCompositeDetector::test_collapses_primary_duplicate_via_anchor PASSED [ 17%] tests/test_detectors.py::TestCompositeDetector::test_secondary_with_center_inside_primary_not_added PASSED [ 17%] tests/test_detectors.py::TestCompositeDetector::test_get_reaches_member_detector PASSED [ 17%] tests/test_diagnostics.py::TestTFLiteDiagnostics::test_ok_true_when_active_backend_present PASSED [ 17%] tests/test_diagnostics.py::TestTFLiteDiagnostics::test_ok_false_without_backend PASSED [ 17%] tests/test_diagnostics.py::TestCheckTfliteBackend::test_reports_missing_model PASSED [ 17%] tests/test_diagnostics.py::TestCheckTfliteBackend::test_uses_first_available_backend_for_interpreter PASSED [ 17%] tests/test_diagnostics.py::TestPrintDiagnostics::test_prints_summary PASSED [ 17%] tests/test_diagnostics.py::TestDetectionComponentSelfTest::test_reports_haar_cascades_and_models PASSED [ 17%] tests/test_diagnostics.py::TestDetectionComponentSelfTest::test_write_self_test_exit_code_follows_ok PASSED [ 17%] tests/test_diagnostics.py::TestDetectionComponentSelfTest::test_failed_insightface_probe_fails_the_self_test PASSED [ 17%] tests/test_diagnostics.py::test_main_self_test_flag_exits_with_report PASSED [ 17%] tests/test_display_utils.py::TestEnumerateDisplays::test_returns_recording_display_info_list PASSED [ 17%] tests/test_display_utils.py::TestEnumerateDisplays::test_survives_broken_qgui PASSED [ 18%] tests/test_display_utils.py::TestEnumerateDisplays::test_windows_uses_the_native_probe PASSED [ 18%] tests/test_display_utils.py::TestEnumerateDisplays::test_windows_falls_back_to_qt_when_the_probe_is_empty PASSED [ 18%] tests/test_display_utils.py::TestActiveWindowBounds::test_returns_geometry_for_top_level_window PASSED [ 18%] tests/test_display_utils.py::TestActiveWindowBounds::test_none_for_missing_widget PASSED [ 18%] tests/test_display_utils.py::TestActiveWindowBounds::test_none_on_failure PASSED [ 18%] tests/test_drive_image_worker.py::TestResolveDriveFileId::test_returns_file_id PASSED [ 18%] tests/test_drive_image_worker.py::TestResolveDriveFileId::test_missing_record_returns_none PASSED [ 18%] tests/test_drive_image_worker.py::TestDownload::test_creates_parent_and_downloads PASSED [ 18%] tests/test_drive_image_worker.py::TestDriveThumbRunnable::test_uses_existing_local_file PASSED [ 18%] tests/test_drive_image_worker.py::TestDriveThumbRunnable::test_downloads_when_missing PASSED [ 18%] tests/test_drive_image_worker.py::TestDriveThumbRunnable::test_failed_without_remote_record PASSED [ 18%] tests/test_drive_image_worker.py::TestDriveFetchRunnable::test_ready_when_already_local PASSED [ 18%] tests/test_drive_image_worker.py::TestDriveFetchRunnable::test_downloads_and_emits_ready PASSED [ 18%] tests/test_drive_image_worker.py::TestDriveFetchRunnable::test_silent_failure_suppresses_signal PASSED [ 18%] tests/test_drive_image_worker.py::TestDriveFetchRunnable::test_non_silent_failure_emits PASSED [ 18%] tests/test_duplicate_unknown_face_finder.py::test_lists_known_face_overlapping_unknown PASSED [ 18%] tests/test_duplicate_unknown_face_finder.py::test_small_unknown_nested_in_large_known_is_listed PASSED [ 18%] tests/test_duplicate_unknown_face_finder.py::test_distant_unknown_is_not_listed PASSED [ 18%] tests/test_duplicate_unknown_face_finder.py::test_two_known_faces_are_never_listed PASSED [ 18%] tests/test_duplicate_unknown_face_finder.py::test_two_unknown_faces_are_never_listed PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_unknown_overlapping_multiple_known_faces_is_listed_once PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_delete_unknown_faces_deletes_only_still_unknown_records PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[None] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[ ] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[?] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[??] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[???] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[Unknown] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[unknown] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[UNKNOWN] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[Unknown 96] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[Unknown_96] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[unknown 1] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[Unknown_003] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[Ismeretlen] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[ismeretlen] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[Ismeretlen 5] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_true[ismeretlen_2] PASSED [ 19%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_false[Alice] PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_false[Bob Smith] PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_false[Kov\xe1cs B\xe9la] PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_false[John] PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_is_placeholder_name_false[Anna-M\xe1ria] PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_auto_named_person_overlapping_known_is_listed PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_auto_named_person_can_be_deleted PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_same_person_overlapping_duplicates_are_listed PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_same_auto_named_cluster_overlapping_duplicates_are_listed PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_protected_catch_all_overlaps_are_not_listed PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_same_person_non_overlapping_not_listed PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_embedding_same_face_two_unknown_clusters_is_found PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_embedding_overlapping_different_people_kept PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_embedding_same_face_no_overlap_kept PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_embedding_named_face_is_kept_as_reference PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_partially_intersecting_unknown_and_named_needs_cross_identity PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_intersecting_faces_of_two_unknown_clusters_are_listed PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_cross_identity_never_lists_two_named_faces PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_non_touching_boxes_stay_unlisted_at_loosest_level PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_named_same_person_duplicate_deletable_with_fresh_finder PASSED [ 20%] tests/test_duplicate_unknown_face_finder.py::test_named_same_person_duplicate_deletable_without_flag_set PASSED [ 21%] tests/test_duplicate_unknown_face_finder.py::test_lone_named_face_is_never_deleted PASSED [ 21%] tests/test_embedding_service.py::test_embed_face_single PASSED [ 21%] tests/test_embedding_service.py::test_embed_manual_face_makes_face_comparable PASSED [ 21%] tests/test_embedding_service.py::test_reembed_all_clears_and_recomputes PASSED [ 21%] tests/test_exif_write.py::test_write_gps_and_date_roundtrip PASSED [ 21%] tests/test_exif_write.py::test_gps_and_date_coexist PASSED [ 21%] tests/test_exif_write.py::test_write_gps_survives_windows_lock PASSED [ 21%] tests/test_exif_write.py::test_write_gps_quality_preserved_no_recompress_blowup PASSED [ 21%] tests/test_exif_write.py::test_missing_file_returns_false PASSED [ 21%] tests/test_export_service.py::test_face_bbox_for_export_clamps_pixel_boxes_to_image_bounds PASSED [ 21%] tests/test_export_service.py::test_face_bbox_for_export_accepts_normalized_legacy_boxes PASSED [ 21%] tests/test_export_service.py::test_exif_gps_dms_conversion_handles_direction_refs PASSED [ 21%] tests/test_export_service.py::test_legacy_export_html_is_removed PASSED [ 21%] tests/test_export_service.py::test_export_astro_bundle_structure PASSED [ 21%] tests/test_export_service.py::test_export_astro_family_tree_bundle PASSED [ 21%] tests/test_export_service.py::test_export_astro_compare_group_with_multiple_variants PASSED [ 21%] tests/test_export_service.py::test_export_astro_no_compare_group_for_lone_image PASSED [ 21%] tests/test_export_service.py::test_export_astro_faces_use_percentage_boxes PASSED [ 21%] tests/test_export_service.py::test_export_astro_includes_faceless_images PASSED [ 21%] tests/test_export_service.py::test_export_astro_writes_map_and_slideshow_parity_pages FAILED [ 22%] tests/test_export_service.py::test_export_astro_objects_bbox_and_point PASSED [ 22%] tests/test_export_service.py::test_export_astro_handles_image_with_no_objects PASSED [ 22%] tests/test_export_service.py::test_export_astro_object_collection_records PASSED [ 22%] tests/test_export_service.py::test_export_astro_slideshow_carries_objects_and_panel_data PASSED [ 22%] tests/test_export_service.py::test_export_astro_multiple_occurrences_same_object PASSED [ 22%] tests/test_export_service.py::test_assemble_update_package_without_csv PASSED [ 22%] tests/test_export_service.py::test_assemble_update_package_with_allowlist PASSED [ 22%] tests/test_face_crop_service.py::test_duplicate_crop_paths_are_repaired_per_face_id PASSED [ 22%] tests/test_face_crop_service.py::test_save_crop_for_face_does_not_reuse_another_face_path PASSED [ 22%] tests/test_face_crop_service.py::test_legacy_noncanonical_crop_path_is_migrated PASSED [ 22%] tests/test_face_crop_service.py::test_rapid_reassignments_keep_face_crop_sources_stable PASSED [ 22%] tests/test_face_date_service.py::test_photo_date_takes_priority PASSED [ 22%] tests/test_face_date_service.py::test_falls_back_to_estimated_and_flags_it PASSED [ 22%] tests/test_face_date_service.py::test_unknown_when_no_source PASSED [ 22%] tests/test_face_date_service.py::test_cache_returns_same_value_per_image_id PASSED [ 22%] tests/test_face_diagnostics_service.py::test_explain_missing_face_returns_none PASSED [ 22%] tests/test_face_diagnostics_service.py::test_no_embedding_verdict PASSED [ 22%] tests/test_face_diagnostics_service.py::test_strong_named_match_is_eligible PASSED [ 22%] tests/test_face_diagnostics_service.py::test_weak_match_left_for_clustering PASSED [ 22%] tests/test_face_diagnostics_service.py::test_unknown_centroids_are_scored PASSED [ 23%] tests/test_face_diagnostics_service.py::test_adaptive_threshold_lower_for_small_face PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_plausible_face_is_kept PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_impossible_unassigned_is_droppable PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_impossible_auto_assigned_is_droppable PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_impossible_manual_assignment_is_flagged_not_dropped PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_impossible_legacy_null_source_assignment_is_flagged PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_manual_backend_face_is_never_dropped PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_landmark_less_face_is_not_scanned PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_delete_faces_removes_rows_and_blobs PASSED [ 23%] tests/test_face_geometry_cleanup_service.py::test_scan_and_delete_convenience PASSED [ 23%] tests/test_face_grouping_service.py::test_similar_faces_group_together PASSED [ 23%] tests/test_face_grouping_service.py::test_faces_without_embedding_are_singletons PASSED [ 23%] tests/test_face_grouping_service.py::test_group_order_is_stable_and_members_keep_order PASSED [ 23%] tests/test_face_grouping_service.py::test_representative_prefers_higher_quality PASSED [ 23%] tests/test_face_grouping_service.py::test_threshold_controls_granularity PASSED [ 23%] tests/test_face_grouping_service.py::test_deoldified_variants_stack_despite_embedding_drift PASSED [ 23%] tests/test_face_grouping_service.py::test_same_photo_different_people_not_merged_by_pairing PASSED [ 23%] tests/test_face_grouping_service.py::test_pair_key_matches_browser_pairing PASSED [ 23%] tests/test_face_image_export_service.py::test_exports_one_file_per_face_with_pattern_names PASSED [ 23%] tests/test_face_image_export_service.py::test_missing_family_code_collapses_the_separator PASSED [ 23%] tests/test_face_image_export_service.py::test_original_mode_keeps_full_resolution_with_margin PASSED [ 24%] tests/test_face_image_export_service.py::test_original_mode_clamps_the_margin_at_the_image_edge PASSED [ 24%] tests/test_face_image_export_service.py::test_square_mode_uses_the_requested_edge_length PASSED [ 24%] tests/test_face_image_export_service.py::test_name_collisions_get_a_numeric_suffix PASSED [ 24%] tests/test_face_image_export_service.py::test_unknown_faces_can_be_excluded PASSED [ 24%] tests/test_face_image_export_service.py::test_only_face_ids_restricts_the_export_to_one_face PASSED [ 24%] tests/test_face_image_export_service.py::test_excluded_and_low_quality_faces_are_skipped PASSED [ 24%] tests/test_face_image_export_service.py::test_each_image_is_decoded_only_once PASSED [ 24%] tests/test_face_image_export_service.py::test_missing_source_file_is_reported_not_raised PASSED [ 24%] tests/test_face_image_export_service.py::test_preview_names_and_count_match_the_export PASSED [ 24%] tests/test_face_image_export_service.py::test_estimated_date_renders_as_an_approximate_value PASSED [ 24%] tests/test_face_image_export_service.py::test_no_faces_yields_an_empty_result PASSED [ 24%] tests/test_face_metadata_export_service.py::test_payload_known_person PASSED [ 24%] tests/test_face_metadata_export_service.py::test_payload_excludes_name_when_disabled PASSED [ 24%] tests/test_face_metadata_export_service.py::test_payload_unknown_person PASSED [ 24%] tests/test_face_metadata_export_service.py::test_protected_person_treated_as_unknown PASSED [ 24%] tests/test_face_metadata_export_service.py::test_notes_included_when_enabled PASSED [ 24%] tests/test_face_metadata_export_service.py::test_notes_excluded_when_disabled PASSED [ 24%] tests/test_face_metadata_export_service.py::test_empty_face_list_valid_payload PASSED [ 24%] tests/test_face_metadata_export_service.py::test_export_embeds_exif_comment_and_is_idempotent PASSED [ 24%] tests/test_face_metadata_export_service.py::test_export_xmp_when_exif_comment_disabled PASSED [ 25%] tests/test_face_metadata_export_service.py::test_existing_exif_preserved PASSED [ 25%] tests/test_face_metadata_export_service.py::test_raw_format_falls_back_to_sidecar PASSED [ 25%] tests/test_face_metadata_export_service.py::test_prefer_sidecar_only_never_touches_image PASSED [ 25%] tests/test_face_metadata_export_service.py::test_readonly_image_falls_back_to_sidecar SKIPPED [ 25%] tests/test_face_metadata_export_service.py::test_missing_image_record_returns_failed PASSED [ 25%] tests/test_face_metadata_export_service.py::test_missing_file_on_disk_returns_failed PASSED [ 25%] tests/test_face_metadata_export_service.py::test_stale_external_relative_path_falls_back_to_file_path PASSED [ 25%] tests/test_face_metadata_export_service.py::test_batch_summary_counts PASSED [ 25%] tests/test_face_metadata_export_service.py::test_no_overwrite_skips_when_block_exists PASSED [ 25%] tests/test_face_metadata_export_service.py::test_windows_lock_on_replace_still_embeds PASSED [ 25%] tests/test_face_metadata_export_service.py::test_macos_finder_comment_writes_xattr SKIPPED [ 25%] tests/test_face_metadata_export_service.py::test_png_xmp_roundtrip PASSED [ 25%] tests/test_face_metadata_export_service.py::test_cancel_stops_before_next_image PASSED [ 25%] tests/test_face_metadata_export_service.py::test_no_cancel_processes_all PASSED [ 25%] tests/test_face_metadata_export_service.py::test_exif_written_without_piexif PASSED [ 25%] tests/test_face_metadata_export_service.py::test_existing_exif_preserved_without_piexif PASSED [ 25%] tests/test_face_metadata_export_worker.py::TestFaceMetadataExportWorker::test_emits_summary_on_success PASSED [ 25%] tests/test_face_metadata_export_worker.py::TestFaceMetadataExportWorker::test_reports_progress PASSED [ 25%] tests/test_face_metadata_export_worker.py::TestFaceMetadataExportWorker::test_emits_failed_on_crash PASSED [ 25%] tests/test_face_metadata_export_worker.py::TestFaceMetadataExportWorker::test_cancel_delegates_to_token PASSED [ 26%] tests/test_face_quality_service.py::test_evaluate_high_quality_face PASSED [ 26%] tests/test_face_quality_service.py::test_evaluate_flags_low_confidence PASSED [ 26%] tests/test_face_quality_service.py::test_evaluate_flags_too_small PASSED [ 26%] tests/test_face_quality_service.py::test_evaluate_flags_bad_aspect_ratio PASSED [ 26%] tests/test_face_quality_service.py::test_evaluate_flags_blurry_crop PASSED [ 26%] tests/test_face_quality_service.py::test_evaluate_and_update_writes_orm_fields PASSED [ 26%] tests/test_face_quality_service.py::test_laplacian_variance_missing_file_returns_none PASSED [ 26%] tests/test_face_quality_service.py::test_laplacian_variance_invalid_image_returns_none PASSED [ 26%] tests/test_face_quality_service.py::test_laplacian_variance_sharp_image PASSED [ 26%] tests/test_face_quality_service.py::test_batch_evaluate_all_persists_and_reports_progress PASSED [ 26%] tests/test_face_timeline_view.py::test_empty_when_no_dates PASSED [ 26%] tests/test_face_timeline_view.py::test_one_marker_per_dated_group PASSED [ 26%] tests/test_face_timeline_view.py::test_bounds_use_birth_and_death PASSED [ 26%] tests/test_face_timeline_view.py::test_bounds_fall_back_to_photo_range PASSED [ 26%] tests/test_face_timeline_view.py::test_merged_group_marker_has_count_badge PASSED [ 26%] tests/test_face_verification_service.py::test_verify_rejects_when_no_face_refound PASSED [ 26%] tests/test_face_verification_service.py::test_verify_confirms_overlapping_face PASSED [ 26%] tests/test_face_verification_service.py::test_verify_refines_oversized_box PASSED [ 26%] tests/test_face_verification_service.py::test_verify_adopts_landmarks_for_landmark_less_detection PASSED [ 26%] tests/test_face_verification_service.py::test_verify_rejects_geometrically_implausible_crop PASSED [ 27%] tests/test_face_verification_service.py::test_verify_geometry_gate_honours_disable_flag PASSED [ 27%] tests/test_face_verification_service.py::test_verifier_unavailable_passes_through PASSED [ 27%] tests/test_face_verification_service.py::test_detection_service_drops_unverified_low_confidence PASSED [ 27%] tests/test_face_verification_service.py::test_detection_service_keeps_verified_low_confidence PASSED [ 27%] tests/test_face_verification_service.py::test_detection_service_exempts_high_confidence PASSED [ 27%] tests/test_face_verification_service.py::test_config_loads_verification_fields PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_root_person PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_spouse PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_child PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_child_spouse PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_grandchild PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_great_grandchild PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_stepchild_detected PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_own_child_not_stepchild PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_ancestor_father PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_ancestor_mother PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_ancestor_paternal_grandfather PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_ancestor_paternal_grandmother PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_ancestor_maternal_grandfather PASSED [ 27%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_ancestor_maternal_grandmother PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_sibling PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_sibling_child PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_sibling_spouse PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_friend PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_case_insensitive PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_invalid_no_path_before_suffix PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_invalid_b_with_digits_after PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_invalid_t_with_zero_sibling PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_invalid_f_with_digit_3 PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_invalid_child_off_root_marker PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_invalid_spouse_of_spouse PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_valid_spouse_of_root PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_valid_stepchild_via_spouse PASSED [ 28%] tests/test_family_code_interpreter.py::TestParseExtendedCode::test_invalid_empty PASSED [ 28%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_root_person_cikky PASSED [ 28%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_spouse_of_cikky PASSED [ 28%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_8th_child PASSED [ 28%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_8th_child_spouse PASSED [ 28%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_8th_child_1st_grandchild PASSED [ 28%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_grandchild_spouse PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_great_grandchild PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_own_child_not_stepchild PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeBasePaths::test_stepchild_annotation PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_father PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_mother PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_paternal_grandfather PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_paternal_grandmother PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_maternal_grandfather PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_maternal_grandmother PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_spouse_father PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_spouse_mother PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_child_spouse_father PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_grandchild_spouse_mother PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_deep_ancestor_depth3 PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeAncestors::test_deep_ancestor_depth4 PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeSiblings::test_first_sibling PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeSiblings::test_second_sibling PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeSiblings::test_sibling_spouse PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeSiblings::test_sibling_child PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeFriends::test_single_friend PASSED [ 29%] tests/test_family_code_interpreter.py::TestDescribeFriends::test_multi_comma PASSED [ 30%] tests/test_family_code_interpreter.py::TestDescribeFriends::test_multi_space PASSED [ 30%] tests/test_family_code_interpreter.py::TestDescribeFriends::test_multi_comma_space PASSED [ 30%] tests/test_family_code_interpreter.py::TestRangeNotation::test_expand_range PASSED [ 30%] tests/test_family_code_interpreter.py::TestRangeNotation::test_expand_short_range PASSED [ 30%] tests/test_family_code_interpreter.py::TestRangeNotation::test_expand_backwards_returns_empty PASSED [ 30%] tests/test_family_code_interpreter.py::TestRangeNotation::test_describe_range PASSED [ 30%] tests/test_family_code_interpreter.py::TestRangeNotation::test_describe_two_element_range PASSED [ 30%] tests/test_family_code_interpreter.py::TestRangeNotation::test_describe_single_element_range PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_valid_simple_codes PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_valid_ancestor_codes PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_valid_sibling_codes PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_valid_friend_codes PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_valid_range PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_valid_multi_code PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_invalid_b_with_number PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_invalid_empty PASSED [ 30%] tests/test_family_code_interpreter.py::TestValidateExtendedCode::test_lowercase_normalised PASSED [ 30%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_all_four_roots_registered PASSED [ 30%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_cikky PASSED [ 30%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_gabor PASSED [ 31%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_jerne PASSED [ 31%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_ildi PASSED [ 31%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_root_descendant PASSED [ 31%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_root_ancestor PASSED [ 31%] tests/test_family_code_interpreter.py::TestDefaultRootPersons::test_root_spouse PASSED [ 31%] tests/test_family_code_interpreter.py::TestCustomRootNames::test_override_root_name PASSED [ 31%] tests/test_family_code_interpreter.py::TestCustomRootNames::test_unknown_root_shows_letter PASSED [ 31%] tests/test_family_code_interpreter.py::TestCustomRootNames::test_i_and_j_roots PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_parse_first_spouse PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_parse_second_spouse PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_parse_spouse_brought_child PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_describe_first_spouse PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_describe_second_spouse PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_describe_spouse_brought_child PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_root_spouse_h PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_zero_spouse_still_valid PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_validate_h_codes PASSED [ 31%] tests/test_family_code_interpreter.py::TestMultiSpouseSuffix::test_invalid_h_zero_spouse_index PASSED [ 31%] tests/test_family_code_interpreter.py::TestParentBraces::test_parse_brace PASSED [ 31%] tests/test_family_code_interpreter.py::TestParentBraces::test_parse_brace_first_spouse PASSED [ 32%] tests/test_family_code_interpreter.py::TestParentBraces::test_describe_brace_second_spouse PASSED [ 32%] tests/test_family_code_interpreter.py::TestParentBraces::test_describe_brace_first_spouse PASSED [ 32%] tests/test_family_code_interpreter.py::TestParentBraces::test_brace_canonical_form PASSED [ 32%] tests/test_family_code_interpreter.py::TestParentBraces::test_brace_must_precede_child PASSED [ 32%] tests/test_family_code_interpreter.py::TestParentBraces::test_brace_not_before_spouse_zero PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_parse_root PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_parse_spouse PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_parse_child PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_parse_grandchild PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_root_case_preserved PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_numeric_root PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_describe_root PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_describe_spouse PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_describe_child PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_describe_grandchild PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_describe_short_root PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_describe_external_ancestor PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_validate_external_field PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_validate_external_empty PASSED [ 32%] tests/test_family_code_interpreter.py::TestExternalCodes::test_validate_external_rejects_internal PASSED [ 33%] tests/test_family_code_interpreter.py::TestExternalCodes::test_validate_external_rejects_empty_root PASSED [ 33%] tests/test_family_code_interpreter.py::TestExternalCodes::test_main_field_rejects_external PASSED [ 33%] tests/test_family_code_links.py::test_derive_root_spouse_and_children PASSED [ 33%] tests/test_family_code_links.py::test_derive_ancestors_siblings_and_numbered_spouses PASSED [ 33%] tests/test_family_code_links.py::test_derive_ignores_friend_and_multi_codes PASSED [ 33%] tests/test_family_code_links.py::test_regenerate_links_whole_family_from_codes PASSED [ 33%] tests/test_family_code_links.py::test_legacy_c_and_c0_codes_still_link PASSED [ 33%] tests/test_family_code_links.py::test_saving_a_referenced_person_links_dependents PASSED [ 33%] tests/test_family_code_schemes.py::test_builtin_scheme_mirrors_legacy_grammar PASSED [ 33%] tests/test_family_code_schemes.py::test_custom_letters_validate_and_describe PASSED [ 33%] tests/test_family_code_schemes.py::test_legacy_letters_rejected_under_custom_scheme PASSED [ 33%] tests/test_family_code_schemes.py::test_unlisted_roots_allowed_when_enabled PASSED [ 33%] tests/test_family_code_schemes.py::test_disabled_extras_are_rejected PASSED [ 33%] tests/test_family_code_schemes.py::test_enabled_extras_work_with_custom_letters PASSED [ 33%] tests/test_family_code_schemes.py::test_disabled_marker_means_letter_is_invalid PASSED [ 33%] tests/test_family_code_schemes.py::test_active_scheme_drives_default_validation PASSED [ 33%] tests/test_family_code_schemes.py::test_code_is_relational_marker PASSED [ 33%] tests/test_family_code_schemes.py::test_code_is_relational_marker_custom_letters PASSED [ 33%] tests/test_family_code_schemes.py::test_scheme_problems_detects_mistakes PASSED [ 33%] tests/test_family_code_schemes.py::test_scheme_example_codes_use_scheme_letters PASSED [ 34%] tests/test_family_code_schemes.py::test_store_save_list_get_delete PASSED [ 34%] tests/test_family_code_schemes.py::test_store_rejects_builtin_mutation PASSED [ 34%] tests/test_family_code_schemes.py::test_store_duplicate_creates_editable_copy PASSED [ 34%] tests/test_family_code_schemes.py::test_store_active_scheme_persistence PASSED [ 34%] tests/test_family_code_schemes.py::test_load_active_falls_back_when_file_missing PASSED [ 34%] tests/test_family_code_schemes.py::test_store_export_import_roundtrip PASSED [ 34%] tests/test_family_code_schemes.py::test_import_rejects_foreign_json PASSED [ 34%] tests/test_family_code_schemes.py::test_person_dialog_valid_code_is_canonicalised PASSED [ 34%] tests/test_family_code_schemes.py::test_person_dialog_invalid_code_saved_with_inline_marker PASSED [ 34%] tests/test_family_code_schemes.py::test_person_dialog_marker_toggles_live PASSED [ 34%] tests/test_family_code_schemes.py::test_person_dialog_flags_preexisting_invalid_code PASSED [ 34%] tests/test_family_code_schemes.py::test_person_dialog_help_follows_active_scheme PASSED [ 34%] tests/test_family_code_schemes.py::test_scheme_dialog_smoke PASSED [ 34%] tests/test_family_code_service.py::test_empty_family_code_is_accepted PASSED [ 34%] tests/test_family_code_service.py::test_family_code_examples_are_parsed PASSED [ 34%] tests/test_family_code_service.py::test_invalid_family_code_is_rejected PASSED [ 34%] tests/test_family_code_service.py::test_spec_defined_simple_codes_are_accepted PASSED [ 34%] tests/test_family_code_service.py::test_range_friend_code_is_valid PASSED [ 34%] tests/test_family_code_service.py::test_extended_family_codes_are_accepted PASSED [ 34%] tests/test_family_code_service.py::test_invalid_extended_family_codes_are_rejected PASSED [ 35%] tests/test_family_code_service.py::test_extended_codes_have_no_simple_tree_derivation PASSED [ 35%] tests/test_family_code_service.py::test_duplicate_family_code_is_allowed_at_db_level PASSED [ 35%] tests/test_family_code_service.py::test_service_reports_duplicate_family_code PASSED [ 35%] tests/test_family_code_service.py::test_friend_codes_are_not_unique PASSED [ 35%] tests/test_family_code_service.py::test_friend_codes_can_be_duplicated_at_db_level PASSED [ 35%] tests/test_family_code_service.py::test_identity_codes_not_unique_at_db_level_but_service_warns PASSED [ 35%] tests/test_family_code_service.py::test_migration_converts_legacy_unconditional_unique_index PASSED [ 35%] tests/test_family_code_service.py::test_identity_codes_remain_unique PASSED [ 35%] tests/test_family_code_service.py::test_family_code_relationships_are_derived PASSED [ 35%] tests/test_family_code_service.py::test_legacy_persons_without_family_code_survive_migration PASSED [ 35%] tests/test_family_code_suggest.py::test_first_spouse_uses_zero_suffix PASSED [ 35%] tests/test_family_code_suggest.py::test_further_marriages_are_numbered PASSED [ 35%] tests/test_family_code_suggest.py::test_spouse_of_a_spouse_record_is_none PASSED [ 35%] tests/test_family_code_suggest.py::test_child_uses_next_free_digit PASSED [ 35%] tests/test_family_code_suggest.py::test_child_of_spouse_resolves_to_base PASSED [ 35%] tests/test_family_code_suggest.py::test_child_of_numbered_spouse_is_braced PASSED [ 35%] tests/test_family_code_suggest.py::test_parent_code_is_derived PASSED [ 35%] tests/test_family_code_suggest.py::test_no_code_yields_no_suggestion PASSED [ 35%] tests/test_family_code_suggest.py::test_suggesters_follow_active_scheme PASSED [ 35%] tests/test_family_code_suggest.py::test_add_spouse_with_marriage_period PASSED [ 35%] tests/test_family_code_suggest.py::test_set_marriage_period_updates_existing PASSED [ 36%] tests/test_family_code_suggest.py::test_regenerate_links_from_codes PASSED [ 36%] tests/test_family_service.py::test_spouse_is_canonical_and_not_duplicated PASSED [ 36%] tests/test_family_service.py::test_parent_child_validation_and_sibling_derivation PASSED [ 36%] tests/test_family_service.py::test_search_images_for_two_people_modes PASSED [ 36%] tests/test_family_service.py::test_relationship_filter_blocks_unmatched_pairs PASSED [ 36%] tests/test_family_service.py::test_search_images_by_comma_name_terms_and_allow_others PASSED [ 36%] tests/test_family_service.py::test_parse_braced_child_derives_both_parents PASSED [ 36%] tests/test_family_service.py::test_parse_numbered_spouse_is_spouse_of_base PASSED [ 36%] tests/test_family_service.py::test_short_child_form_has_no_second_parent PASSED [ 36%] tests/test_family_service.py::test_link_derived_parents_braced_child PASSED [ 36%] tests/test_family_service.py::test_link_derived_parents_numbered_spouse PASSED [ 36%] tests/test_family_service.py::test_link_skips_missing_second_parent PASSED [ 36%] tests/test_family_service.py::test_is_parent_child_derives_second_parent_without_rows PASSED [ 36%] tests/test_family_service.py::test_update_person_auto_links_parents PASSED [ 36%] tests/test_family_service.py::test_search_images_by_criteria_detail_filters PASSED [ 36%] tests/test_family_tree_layout.py::test_no_boxes_overlap_in_a_row PASSED [ 36%] tests/test_family_tree_layout.py::test_siblings_are_contiguous_and_under_their_parents PASSED [ 36%] tests/test_family_tree_layout.py::test_couple_children_centred_below_the_couple PASSED [ 36%] tests/test_family_tree_layout.py::test_unrelated_branches_do_not_interleave PASSED [ 36%] tests/test_family_tree_layout.py::test_married_in_spouse_parents_sit_directly_above PASSED [ 37%] tests/test_family_tree_layout.py::test_married_in_spouse_parents_take_siblings_along PASSED [ 37%] tests/test_family_tree_layout.py::test_married_in_spouse_parents_drag_sibling_family_down PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_renders_hierarchy PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_non_member_filter PASSED [ 37%] tests/test_family_tree_panel.py::test_editor_lists_relationships PASSED [ 37%] tests/test_family_tree_panel.py::test_editor_remove_relationship PASSED [ 37%] tests/test_family_tree_panel.py::test_editor_member_toggle PASSED [ 37%] tests/test_family_tree_panel.py::test_editor_add_spouse_via_service PASSED [ 37%] tests/test_family_tree_panel.py::test_add_dialog_prefills_spouse_code PASSED [ 37%] tests/test_family_tree_panel.py::test_editor_add_new_spouse_creates_coded_person PASSED [ 37%] tests/test_family_tree_panel.py::test_editor_add_child_links_both_parents PASSED [ 37%] tests/test_family_tree_panel.py::test_editor_save_own_code PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_shows_whole_forest_by_default PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_regenerate_button PASSED [ 37%] tests/test_family_tree_panel.py::test_add_dialog_offers_only_opposite_gender PASSED [ 37%] tests/test_family_tree_panel.py::test_quick_add_spouse_stores_dates_and_gender PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_focus_shows_lineage_only PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_context_menu_focuses_person[None] PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_context_menu_focuses_person[C1] PASSED [ 37%] tests/test_family_tree_panel.py::test_panel_selection_populates_detail PASSED [ 38%] tests/test_family_tree_panel.py::test_panel_quick_add_child PASSED [ 38%] tests/test_family_tree_service.py::test_build_tree_assigns_generations PASSED [ 38%] tests/test_family_tree_service.py::test_build_tree_edges_reference_included_nodes PASSED [ 38%] tests/test_family_tree_service.py::test_build_tree_generation_limits PASSED [ 38%] tests/test_family_tree_service.py::test_build_graph_assigns_forest_generations PASSED [ 38%] tests/test_family_tree_service.py::test_build_lineage_excludes_collateral_relatives PASSED [ 38%] tests/test_family_tree_service.py::test_build_lineage_shows_couples_on_both_sides PASSED [ 38%] tests/test_family_tree_service.py::test_node_thumbnail_falls_back_to_face_crop PASSED [ 38%] tests/test_family_tree_service.py::test_coparent_inferred_for_single_parent_linked_children PASSED [ 38%] tests/test_family_tree_service.py::test_coparent_not_inferred_when_two_real_parents PASSED [ 38%] tests/test_family_tree_service.py::test_coparent_not_inferred_with_ambiguous_remarriage PASSED [ 38%] tests/test_family_tree_service.py::test_graph_carries_marriage_dates PASSED [ 38%] tests/test_family_tree_service.py::test_lone_person_tree PASSED [ 38%] tests/test_family_tree_service.py::test_non_members_excluded_when_disabled PASSED [ 38%] tests/test_family_tree_service.py::test_non_member_root_still_included PASSED [ 38%] tests/test_family_tree_service.py::test_lowest_common_ancestor PASSED [ 38%] tests/test_family_tree_service.py::test_relationship_path_kinds PASSED [ 38%] tests/test_family_tree_service.py::test_relationship_path_unrelated PASSED [ 38%] tests/test_family_tree_service.py::test_render_tree_json_shape PASSED [ 38%] tests/test_family_tree_service.py::test_settings_singleton_and_update PASSED [ 39%] tests/test_family_tree_service.py::test_is_family_tree_member_defaults_true PASSED [ 39%] tests/test_family_tree_view.py::test_set_graph_draws_one_box_per_person PASSED [ 39%] tests/test_family_tree_view.py::test_set_graph_empty_is_safe PASSED [ 39%] tests/test_family_tree_view.py::test_boxes_positioned_by_generation PASSED [ 39%] tests/test_family_tree_view.py::test_selected_and_focus_use_distinct_colors PASSED [ 39%] tests/test_family_tree_view.py::test_zoom_in_out_reset PASSED [ 39%] tests/test_family_tree_view.py::test_photos_are_loaded_lazily PASSED [ 39%] tests/test_family_tree_view.py::test_generation_numbers_in_left_gutter PASSED [ 39%] tests/test_family_tree_view.py::test_selection_marks_ancestors_and_descendants PASSED [ 39%] tests/test_family_tree_view.py::test_siblings_share_one_parent_bus PASSED [ 39%] tests/test_family_tree_view.py::test_selecting_child_highlights_only_its_path_up PASSED [ 39%] tests/test_family_tree_view.py::test_couple_has_no_extra_bridge_line PASSED [ 39%] tests/test_family_tree_view.py::test_marriage_date_label_is_drawn PASSED [ 39%] tests/test_family_tree_view.py::test_single_parent_siblings_share_one_bus PASSED [ 39%] tests/test_family_tree_view.py::test_selecting_single_parent_child_highlights_both_parents PASSED [ 39%] tests/test_family_tree_view.py::test_spouses_are_placed_side_by_side PASSED [ 39%] tests/test_family_tree_view.py::test_selecting_parent_highlights_all_children_down PASSED [ 39%] tests/test_filename_pattern.py::test_issue_example_full_pattern PASSED [ 39%] tests/test_filename_pattern.py::test_issue_example_missing_family_code_collapses_separators PASSED [ 39%] tests/test_filename_pattern.py::test_aliases_are_case_and_whitespace_insensitive PASSED [ 40%] tests/test_filename_pattern.py::test_every_token_has_a_unique_key_and_an_i18n_key PASSED [ 40%] tests/test_filename_pattern.py::test_unknown_tokens_are_reported_and_dropped PASSED [ 40%] tests/test_filename_pattern.py::test_adjacent_tokens_do_not_merge PASSED [ 40%] tests/test_filename_pattern.py::test_numeric_tokens_render_as_text PASSED [ 40%] tests/test_filename_pattern.py::test_all_tokens_empty_leaves_only_the_literal_text PASSED [ 40%] tests/test_filename_pattern.py::test_leading_and_trailing_separators_are_trimmed PASSED [ 40%] tests/test_filename_pattern.py::test_a_date_looking_tail_is_not_taken_for_an_extension PASSED [ 40%] tests/test_filename_pattern.py::test_pattern_without_tokens_is_returned_as_is PASSED [ 40%] tests/test_filename_pattern.py::test_safe_filename_strips_hostile_characters_but_keeps_dots PASSED [ 40%] tests/test_filename_pattern.py::test_safe_filename_falls_back_when_nothing_remains PASSED [ 40%] tests/test_filename_pattern.py::test_safe_pattern_filename_keeps_the_extension_when_the_stem_is_long PASSED [ 40%] tests/test_filename_pattern.py::test_a_value_containing_hash_is_not_re_scanned_for_tokens PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[1954.03.12-Precision.EXACT-earliest0-latest0-False] PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[1954-03-12-Precision.EXACT-earliest1-latest1-False] PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[1954.03-Precision.MONTH-earliest2-latest2-False] PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[1954-Precision.YEAR-earliest3-latest3-False] PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[1920 k\xf6r\xfcl-Precision.YEAR-earliest4-latest4-True] PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[kb. 1920-Precision.YEAR-earliest5-latest5-True] PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[~1920-Precision.YEAR-earliest6-latest6-True] PASSED [ 40%] tests/test_fuzzy_date.py::test_parse_matrix[1920?-Precision.YEAR-earliest7-latest7-True] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[cca 1920-Precision.YEAR-earliest8-latest8-True] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[1969-1976-Precision.RANGE-earliest9-latest9-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[1969\u20131976-Precision.RANGE-earliest10-latest10-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[1970-es \xe9vek-Precision.DECADE-earliest11-latest11-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[1970s-Precision.DECADE-earliest12-latest12-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[1945 el\u0151tt-Precision.BEFORE-None-latest13-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[1945 ut\xe1n-Precision.AFTER-earliest14-None-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[2008.05.03 14:22:01-Precision.EXACT-earliest15-latest15-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[2008:05:03 14:22:01-Precision.EXACT-earliest16-latest16-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[2008.05.03 14.22.01-Precision.EXACT-earliest17-latest17-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[2008:05:03-Precision.EXACT-earliest18-latest18-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_parse_matrix[1954-03-12T09:30-Precision.EXACT-earliest19-latest19-False] PASSED [ 41%] tests/test_fuzzy_date.py::test_unknown[] PASSED [ 41%] tests/test_fuzzy_date.py::test_unknown[ ] PASSED [ 41%] tests/test_fuzzy_date.py::test_unknown[None] PASSED [ 41%] tests/test_fuzzy_date.py::test_unknown[no date here] PASSED [ 41%] tests/test_fuzzy_date.py::test_unknown[??] PASSED [ 41%] tests/test_fuzzy_date.py::test_messy_year_extraction PASSED [ 41%] tests/test_fuzzy_date.py::test_estimated_with_question_mark_combo PASSED [ 41%] tests/test_fuzzy_date.py::test_sort_key_orders_chronologically PASSED [ 41%] tests/test_fuzzy_date.py::test_range_unordered_input_normalised PASSED [ 42%] tests/test_fuzzy_date.py::test_display_labels PASSED [ 42%] tests/test_gdrive_connectivity.py::TestIsOnline::test_returns_true_when_socket_connects PASSED [ 42%] tests/test_gdrive_connectivity.py::TestIsOnline::test_returns_false_on_os_error PASSED [ 42%] tests/test_gdrive_connectivity.py::TestRequireOnline::test_passes_when_online PASSED [ 42%] tests/test_gdrive_connectivity.py::TestRequireOnline::test_raises_gdrive_offline_error_when_offline PASSED [ 42%] tests/test_gdrive_credential_store.py::TestInMemoryStore::test_roundtrip PASSED [ 42%] tests/test_gdrive_credential_store.py::TestInMemoryStore::test_list_accounts PASSED [ 42%] tests/test_gdrive_credential_store.py::TestInMemoryStore::test_delete_is_idempotent PASSED [ 42%] tests/test_gdrive_credential_store.py::TestGoogleCredentialStoreFallback::test_save_and_load PASSED [ 42%] tests/test_gdrive_credential_store.py::TestGoogleCredentialStoreFallback::test_secret_not_in_plaintext_file PASSED [ 42%] tests/test_gdrive_credential_store.py::TestGoogleCredentialStoreFallback::test_account_index_lists_email PASSED [ 42%] tests/test_gdrive_credential_store.py::TestGoogleCredentialStoreFallback::test_delete_removes_credential PASSED [ 42%] tests/test_gdrive_credential_store.py::TestGoogleCredentialStoreFallback::test_roundtrip_survives_new_instance PASSED [ 42%] tests/test_gdrive_db_sync.py::TestGDriveDbSyncOpen::test_downloads_db_into_cache PASSED [ 42%] tests/test_gdrive_db_sync.py::TestGDriveDbSyncOpen::test_cleans_up_cache_on_download_failure PASSED [ 42%] tests/test_gdrive_db_sync.py::TestGDriveDbSyncOpen::test_raises_when_offline PASSED [ 42%] tests/test_gdrive_db_sync.py::TestGDriveDbSyncSync::test_uploads_local_copy PASSED [ 42%] tests/test_gdrive_db_sync.py::TestGDriveDbSyncSync::test_raises_without_open_session PASSED [ 42%] tests/test_gdrive_db_sync.py::TestGDriveDbSyncSync::test_raises_when_local_file_missing PASSED [ 42%] tests/test_gdrive_db_sync.py::TestGDriveDbSyncClose::test_releases_active_reference PASSED [ 43%] tests/test_gdrive_dotenv_loader.py::TestLoadDotenv::test_missing_file_is_silent PASSED [ 43%] tests/test_gdrive_dotenv_loader.py::TestLoadDotenv::test_loads_whitelisted_vars PASSED [ 43%] tests/test_gdrive_dotenv_loader.py::TestLoadDotenv::test_ignores_unknown_vars PASSED [ 43%] tests/test_gdrive_dotenv_loader.py::TestLoadDotenv::test_does_not_overwrite_existing PASSED [ 43%] tests/test_gdrive_dotenv_loader.py::TestLoadDotenv::test_strips_quotes_and_export_prefix PASSED [ 43%] tests/test_gdrive_dotenv_loader.py::TestLoadDotenv::test_ignores_comments_and_blanks PASSED [ 43%] tests/test_gdrive_dotenv_loader.py::TestLoadDotenv::test_malformed_lines_are_skipped PASSED [ 43%] tests/test_gdrive_drive_client.py::TestFromRaw::test_builds_drive_file PASSED [ 43%] tests/test_gdrive_drive_client.py::TestFromRaw::test_invalid_size_becomes_none PASSED [ 43%] tests/test_gdrive_drive_client.py::TestBuildDriveClient::test_returns_client_for_stored_account PASSED [ 43%] tests/test_gdrive_drive_client.py::TestBuildDriveClient::test_raises_when_no_credentials PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_account_property PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_list_folder_returns_drive_files PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_find_child_returns_none_when_missing PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_get_metadata PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_create_folder PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_upload_file_creates_new_remote_file PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_upload_file_updates_existing PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_upload_file_missing_source_raises PASSED [ 43%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_download_file_writes_bytes PASSED [ 44%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_delete_file_ignores_404 PASSED [ 44%] tests/test_gdrive_drive_client.py::TestGoogleDriveClient::test_offline_raises_without_retry PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestDriveScanBasic::test_empty_folder_returns_empty PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestDriveScanBasic::test_single_image_is_indexed PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestDriveScanBasic::test_image_downloaded_to_mirror PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestDriveScanBasic::test_non_image_not_indexed PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestDriveScanBasic::test_remote_image_record_created PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestDriveScanBasic::test_image_file_path_is_in_mirror PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestDriveScanBasic::test_multiple_images PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestSkipReservedFolders::test_database_folder_skipped PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestSkipReservedFolders::test_metadata_folder_skipped PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestSkipReservedFolders::test_images_in_regular_subfolder_included PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestIncrementalScan::test_unchanged_file_not_redownloaded PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestIncrementalScan::test_changed_file_redownloaded PASSED [ 44%] tests/test_gdrive_drive_scan_service.py::TestIncrementalScan::test_second_scan_same_image_no_duplicate_records PASSED [ 44%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_recognised_inputs[https://drive.google.com/drive/folders/1aBcDeFgHiJkLmNoPqRsTuVwXyZ?usp=sharing-1aBcDeFgHiJkLmNoPqRsTuVwXyZ] PASSED [ 44%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_recognised_inputs[https://drive.google.com/drive/u/0/folders/abcdef1234567890-abcdef1234567890] PASSED [ 44%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_recognised_inputs[https://drive.google.com/open?id=ZyXwVuTsRqPoNmLkJiHgFeDcBa987654-ZyXwVuTsRqPoNmLkJiHgFeDcBa987654] PASSED [ 44%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_recognised_inputs[1a2b3c4d5e6f7g8h9i0j-1a2b3c4d5e6f7g8h9i0j] PASSED [ 44%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_recognised_inputs[ abcdef1234567890 -abcdef1234567890] PASSED [ 45%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_invalid_inputs_return_none[] PASSED [ 45%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_invalid_inputs_return_none[ ] PASSED [ 45%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_invalid_inputs_return_none[not-a-url] PASSED [ 45%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_invalid_inputs_return_none[ftp://example.com/folder] PASSED [ 45%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_invalid_inputs_return_none[https://example.com/something] PASSED [ 45%] tests/test_gdrive_folder_url.py::TestParseFolderInput::test_invalid_inputs_return_none[https://drive.google.com/drive/] PASSED [ 45%] tests/test_gdrive_oauth_config.py::TestBundledConstants::test_scopes_include_drive_and_profile PASSED [ 45%] tests/test_gdrive_oauth_config.py::TestBundledConstants::test_redirect_host_is_localhost PASSED [ 45%] tests/test_gdrive_oauth_config.py::TestBundledConstants::test_client_id_is_non_empty PASSED [ 45%] tests/test_gdrive_oauth_config.py::TestIsConfigured::test_false_when_secret_is_placeholder PASSED [ 45%] tests/test_gdrive_oauth_config.py::TestIsConfigured::test_true_when_both_values_are_real PASSED [ 45%] tests/test_gdrive_oauth_config.py::TestClientConfig::test_installed_client_shape PASSED [ 45%] tests/test_gdrive_oauth_config.py::TestClientConfig::test_env_override_for_client_id PASSED [ 45%] tests/test_gdrive_oauth_flow.py::TestRunLoginFlow::test_raises_config_error_when_not_configured PASSED [ 45%] tests/test_gdrive_oauth_flow.py::TestRunLoginFlow::test_raises_offline_error_when_unreachable PASSED [ 45%] tests/test_gdrive_oauth_flow.py::TestRunLoginFlow::test_raises_config_error_when_oauthlib_missing PASSED [ 45%] tests/test_gdrive_oauth_flow.py::TestRunLoginFlow::test_success_returns_stored_credential PASSED [ 45%] tests/test_gdrive_oauth_flow.py::TestRunLoginFlow::test_cancelled_flow_raises_oauth_cancelled PASSED [ 45%] tests/test_gdrive_oauth_flow.py::TestRunLoginFlow::test_generic_failure_raises_runtime_error PASSED [ 45%] tests/test_gdrive_oauth_flow.py::TestRestoreCredentialsObject::test_rebuilds_google_credentials PASSED [ 46%] tests/test_gdrive_oauth_flow.py::TestRestoreCredentialsObject::test_invalid_expiry_is_ignored PASSED [ 46%] tests/test_gdrive_oauth_flow.py::TestFetchAccountEmail::test_returns_email_from_userinfo PASSED [ 46%] tests/test_gdrive_oauth_flow.py::TestFetchAccountEmail::test_falls_back_when_userinfo_fails PASSED [ 46%] tests/test_gdrive_preferences.py::TestGDrivePrefs::test_is_ready_requires_account_and_folder PASSED [ 46%] tests/test_gdrive_preferences.py::TestLoadSave::test_load_returns_defaults PASSED [ 46%] tests/test_gdrive_preferences.py::TestLoadSave::test_save_and_load_roundtrip PASSED [ 46%] tests/test_gdrive_preferences.py::TestLoadSave::test_load_normalises_empty_strings_to_none PASSED [ 46%] tests/test_gdrive_preferences.py::TestUpdateLastSync::test_writes_explicit_timestamp PASSED [ 46%] tests/test_gdrive_preferences.py::TestUpdateLastSync::test_writes_utc_now_when_omitted PASSED [ 46%] tests/test_gdrive_preferences.py::TestClearAccount::test_clears_account_and_folder_fields PASSED [ 46%] tests/test_gdrive_project_session.py::TestOpen::test_first_open_creates_folder_structure PASSED [ 46%] tests/test_gdrive_project_session.py::TestOpen::test_existing_db_is_downloaded PASSED [ 46%] tests/test_gdrive_project_session.py::TestLocking::test_fresh_lock_blocks_second_session PASSED [ 46%] tests/test_gdrive_project_session.py::TestLocking::test_stale_lock_is_overridden PASSED [ 46%] tests/test_gdrive_project_session.py::TestLocking::test_close_releases_lock PASSED [ 46%] tests/test_gdrive_project_session.py::TestSync::test_sync_uploads_dirty_db PASSED [ 46%] tests/test_gdrive_project_session.py::TestSync::test_sync_no_op_when_clean PASSED [ 46%] tests/test_gdrive_project_session.py::TestSync::test_failed_upload_keeps_local_db PASSED [ 46%] tests/test_gdrive_project_session.py::TestLockInfoStaleDetection::test_recent_heartbeat_is_fresh PASSED [ 46%] tests/test_gdrive_project_session.py::TestLockInfoStaleDetection::test_old_heartbeat_is_stale PASSED [ 47%] tests/test_gdrive_project_session.py::TestLockInfoStaleDetection::test_garbled_heartbeat_treated_as_stale PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestImageRef::test_local_ref_not_remote PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestImageRef::test_remote_ref_is_remote PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestImageRef::test_suffix PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestImageRef::test_frozen PASSED [ 47%] tests/test_gdrive_storage_provider.py::test_local_is_storage_provider PASSED [ 47%] tests/test_gdrive_storage_provider.py::test_gdrive_is_storage_provider PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_description_is_root_path PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_repr PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_iter_images_finds_jpg_png PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_iter_images_excludes_non_image PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_iter_images_empty_extensions PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_iter_images_local_path_set PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_open_image_yields_path PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_open_image_raises_if_no_local_path PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestLocalStorageProvider::test_iter_images_case_insensitive_extension PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_description_is_folder_name PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_description_fallback_to_id PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_repr PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_iter_images_finds_files PASSED [ 47%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_iter_images_skips_folders PASSED [ 48%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_iter_images_ref_has_remote_id PASSED [ 48%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_iter_images_ref_has_size PASSED [ 48%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_open_image_downloads_file PASSED [ 48%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_open_image_cleans_up_after_context PASSED [ 48%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_open_image_records_download_call PASSED [ 48%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_open_image_raises_without_remote_id PASSED [ 48%] tests/test_gdrive_storage_provider.py::TestGoogleDriveStorageProvider::test_open_image_cleans_up_on_exception PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[True-True] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[False-False] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[1-True] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[true-True] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[YES-True] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[on-True] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[0-False] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool[off-False] PASSED [ 48%] tests/test_geocoding_factory.py::test_read_bool_missing_uses_default PASSED [ 48%] tests/test_geocoding_factory.py::test_create_geocoding_service_force_offline PASSED [ 48%] tests/test_geocoding_factory.py::test_create_geocoding_service_force_online PASSED [ 48%] tests/test_geocoding_factory.py::test_create_geocoding_service_reads_settings_when_not_forced PASSED [ 48%] tests/test_geocoding_factory.py::test_create_geocoding_service_provider_failure_stays_offline PASSED [ 49%] tests/test_geocoding_service.py::test_online_suggest_then_cache_hit PASSED [ 49%] tests/test_geocoding_service.py::test_offline_uses_local_suggestions_only PASSED [ 49%] tests/test_geocoding_service.py::test_offline_never_calls_provider PASSED [ 49%] tests/test_geocoding_service.py::test_provider_error_degrades_gracefully PASSED [ 49%] tests/test_geocoding_service.py::test_geocode_cached PASSED [ 49%] tests/test_geocoding_service.py::test_record_address_use_upsert PASSED [ 49%] tests/test_geocoding_worker.py::test_to_dicts PASSED [ 49%] tests/test_geocoding_worker.py::TestSettlementSuggestWorker::test_emits_settlements PASSED [ 49%] tests/test_geocoding_worker.py::TestSettlementSuggestWorker::test_emits_failed_on_error PASSED [ 49%] tests/test_geocoding_worker.py::TestStreetSuggestWorker::test_emits_streets PASSED [ 49%] tests/test_geocoding_worker.py::TestGeocodeWorker::test_emits_geocode_result PASSED [ 49%] tests/test_geocoding_worker.py::TestGeocodeWorker::test_emits_none_when_not_found PASSED [ 49%] tests/test_github_release.py::test_build_release_notes_includes_issue_links_and_commit_comments PASSED [ 49%] tests/test_github_release.py::test_build_release_notes_handles_no_issue_references PASSED [ 49%] tests/test_github_release.py::test_find_previous_release_tag_prefers_latest_github_release PASSED [ 49%] tests/test_github_release.py::test_collect_commit_notes_reads_all_commits_since_previous_release PASSED [ 49%] tests/test_group_manager_widget.py::test_selecting_group_lists_members PASSED [ 49%] tests/test_group_manager_widget.py::test_remove_member_updates_db PASSED [ 49%] tests/test_group_manager_widget.py::test_name_edit_auto_saves_on_focus_out PASSED [ 49%] tests/test_group_manager_widget.py::test_switching_group_persists_pending_edit PASSED [ 50%] tests/test_group_manager_widget.py::test_save_button_persists PASSED [ 50%] tests/test_group_manager_widget.py::test_invalid_name_not_persisted_silently_on_switch PASSED [ 50%] tests/test_group_manager_widget.py::test_member_images_passes_person_image_paths PASSED [ 50%] tests/test_group_manager_widget.py::test_member_detail_opens_editor_and_refreshes PASSED [ 50%] tests/test_i18n.py::TestTranslation::test_t_english_default PASSED [ 50%] tests/test_i18n.py::TestTranslation::test_t_hungarian PASSED [ 50%] tests/test_i18n.py::TestTranslation::test_t_with_format_kwargs PASSED [ 50%] tests/test_i18n.py::TestTranslation::test_unknown_key_returns_key PASSED [ 50%] tests/test_i18n.py::TestTranslation::test_current_language PASSED [ 50%] tests/test_i18n.py::TestLanguagePrefs::test_set_language_persists PASSED [ 50%] tests/test_i18n.py::TestLanguagePrefs::test_set_language_rejects_unknown PASSED [ 50%] tests/test_i18n.py::TestLanguagePrefs::test_load_prefs_reads_file PASSED [ 50%] tests/test_i18n.py::TestLanguagePrefs::test_load_prefs_ignores_invalid_language PASSED [ 50%] tests/test_identity_repair_service.py::TestScan::test_fragments_of_same_person_are_proposed PASSED [ 50%] tests/test_identity_repair_service.py::TestScan::test_dissimilar_persons_not_proposed PASSED [ 50%] tests/test_identity_repair_service.py::TestScan::test_named_and_protected_persons_excluded PASSED [ 50%] tests/test_identity_repair_service.py::TestScan::test_diff_correction_suppresses_pair PASSED [ 50%] tests/test_identity_repair_service.py::TestApply::test_apply_merges_transitive_group PASSED [ 50%] tests/test_identity_repair_service.py::TestApply::test_apply_ignores_unknown_person_ids PASSED [ 50%] tests/test_identity_repair_service.py::test_scan_then_apply_roundtrip PASSED [ 51%] tests/test_identity_service.py::test_rename_person PASSED [ 51%] tests/test_identity_service.py::test_rename_protected_person_raises PASSED [ 51%] tests/test_identity_service.py::test_merge_persons_moves_faces_and_deletes_source PASSED [ 51%] tests/test_identity_service.py::test_merge_persons_respects_merge_exclusions PASSED [ 51%] tests/test_identity_service.py::test_merge_same_person_raises PASSED [ 51%] tests/test_identity_service.py::test_remove_face_from_cluster_splits_face PASSED [ 51%] tests/test_identity_service.py::test_reassign_face_moves_to_target PASSED [ 51%] tests/test_identity_service.py::test_delete_person_unassigns_faces PASSED [ 51%] tests/test_identity_service.py::test_delete_person_hard_deletes_faces PASSED [ 51%] tests/test_identity_service.py::test_delete_protected_person_raises PASSED [ 51%] tests/test_identity_service.py::test_exclude_face PASSED [ 51%] tests/test_identity_service.py::test_set_face_uncertainty_and_note PASSED [ 51%] tests/test_identity_service.py::test_set_face_merge_excluded PASSED [ 51%] tests/test_identity_service.py::test_set_and_clear_person_thumbnail PASSED [ 51%] tests/test_identity_service.py::test_record_same_and_different PASSED [ 51%] tests/test_identity_service.py::test_list_persons_and_get_faces_for_person PASSED [ 51%] tests/test_identity_service.py::test_cleanup_empty_unknown_persons PASSED [ 51%] tests/test_identity_service.py::test_merge_all_excluded_raises PASSED [ 51%] tests/test_identity_service.py::test_reassign_faces_bulk PASSED [ 51%] tests/test_identity_service.py::test_restore_face_assignments PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnorePersonForever::test_ignored_person_does_not_reappear_after_rerun PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnorePersonForever::test_plain_delete_lets_person_reappear PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnorePersonForever::test_named_person_cannot_be_ignored PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnorePersonForever::test_protected_person_cannot_be_ignored PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnoreFaceForever::test_single_face_ignore_keeps_person_with_remaining_faces PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnoreFaceForever::test_ignoring_last_face_removes_empty_unknown_person PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnoreFaceForever::test_named_person_face_cannot_be_ignored PASSED [ 52%] tests/test_ignored_face_service.py::TestIgnoreFaceForever::test_unassigned_face_can_be_ignored PASSED [ 52%] tests/test_ignored_face_service.py::TestSuppressionThreshold::test_dissimilar_new_face_is_not_suppressed PASSED [ 52%] tests/test_ignored_face_service.py::TestSuppressionThreshold::test_disabled_filter_suppresses_nothing PASSED [ 52%] tests/test_ignored_face_service.py::TestUnignore::test_unignore_makes_face_recognisable_again PASSED [ 52%] tests/test_ignored_face_service.py::TestUnignore::test_unignore_missing_entry_returns_false PASSED [ 52%] tests/test_ignored_face_service.py::TestListMetadata::test_entry_keeps_thumbnail_and_source_snapshot PASSED [ 52%] tests/test_image_browser_cursor_reset.py::test_reset_after_resize_drag_restores_cross PASSED [ 52%] tests/test_image_browser_cursor_reset.py::test_exit_edit_resets_cursor PASSED [ 52%] tests/test_image_browser_cursor_reset.py::test_cancel_resets_cursor PASSED [ 52%] tests/test_image_browser_cursor_reset.py::test_reset_drains_application_override_cursor PASSED [ 52%] tests/test_image_browser_deoldified_compare.py::test_compare_composite_left_right PASSED [ 52%] tests/test_image_browser_deoldified_compare.py::test_ensure_compare_bgr_resizes_right_to_left_shape PASSED [ 52%] tests/test_image_browser_deoldified_compare.py::test_picker_change_recomposes_with_chosen_variant PASSED [ 52%] tests/test_image_browser_deoldified_compare.py::test_combos_visible_only_in_compare_with_three_members PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_variant_picker_in_single_colorized_view PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_clear_for_new_image_keeps_remembered_mode_and_split PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_compare_dragged_updates_split_and_recomposites PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_drag_while_not_in_compare_only_records_split PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_single_colorized_view_falls_back_to_bw_when_variant_missing PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_label_compare_divider_x_requires_pixmap PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_opened_colorized_file_wins_over_remembered_bw PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_opened_bw_file_wins_over_remembered_color PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_compare_mode_survives_opening_another_image PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_view_bar_visible_with_pairing_setting_off PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_sync_button_hidden_when_pairing_off PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_sync_button_shown_when_pairing_on PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_face_data_not_rehomed_when_pairing_off PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_face_data_rehomed_when_pairing_on PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_bw_toggle_loads_original_pixels_with_pairing_off PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_no_bar_when_sibling_file_is_missing PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_missing_side_reports_and_offers_repair PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_missing_bw_side_reports_instead_of_silent_return PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_repair_button_emits_signal PASSED [ 53%] tests/test_image_browser_deoldified_compare.py::test_successful_load_clears_the_error_state PASSED [ 54%] tests/test_image_browser_place_create.py::test_create_typed_place_button PASSED [ 54%] tests/test_image_browser_place_create.py::test_create_typed_place_without_image PASSED [ 54%] tests/test_image_browser_place_create.py::test_create_typed_place_from_rename_field PASSED [ 54%] tests/test_image_browser_service.py::test_list_folders_empty_db PASSED [ 54%] tests/test_image_browser_service.py::test_list_folders_groups_by_parent PASSED [ 54%] tests/test_image_browser_service.py::test_list_folders_display_name_is_leaf PASSED [ 54%] tests/test_image_browser_service.py::test_list_folders_total_counts PASSED [ 54%] tests/test_image_browser_service.py::test_list_folders_face_counts PASSED [ 54%] tests/test_image_browser_service.py::test_list_folders_excluded_faces_not_counted PASSED [ 54%] tests/test_image_browser_service.py::test_list_images_returns_only_matching_folder PASSED [ 54%] tests/test_image_browser_service.py::test_list_images_does_not_include_subfolders PASSED [ 54%] tests/test_image_browser_service.py::test_list_images_face_stats PASSED [ 54%] tests/test_image_browser_service.py::test_list_images_empty_folder PASSED [ 54%] tests/test_image_browser_service.py::test_list_images_thumbnail_cache_key PASSED [ 54%] tests/test_image_browser_service.py::test_get_image_faces_returns_tuples PASSED [ 54%] tests/test_image_browser_service.py::test_get_image_faces_excludes_excluded PASSED [ 54%] tests/test_image_browser_service.py::test_get_image_faces_missing_image PASSED [ 54%] tests/test_image_browser_service.py::test_assignment_does_not_change_other_faces PASSED [ 54%] tests/test_image_browser_service.py::test_face_count_after_reassignment_stays_consistent PASSED [ 54%] tests/test_image_library_service.py::TestFromPosix::test_simple PASSED [ 55%] tests/test_image_library_service.py::TestFromPosix::test_backslash PASSED [ 55%] tests/test_image_library_service.py::TestFromPosix::test_deep PASSED [ 55%] tests/test_image_library_service.py::TestFromPosix::test_empty PASSED [ 55%] tests/test_image_library_service.py::TestFromPosix::test_unicode PASSED [ 55%] tests/test_image_library_service.py::TestLibraryRootAccess::test_not_configured_by_default PASSED [ 55%] tests/test_image_library_service.py::TestLibraryRootAccess::test_set_and_get PASSED [ 55%] tests/test_image_library_service.py::TestLibraryRootAccess::test_set_invalid_raises PASSED [ 55%] tests/test_image_library_service.py::TestLibraryRootAccess::test_persists_to_json PASSED [ 55%] tests/test_image_library_service.py::TestLibraryRootAccess::test_loads_from_json PASSED [ 55%] tests/test_image_library_service.py::TestLibraryRootAccess::test_missing_root_returns_path_not_none PASSED [ 55%] tests/test_image_library_service.py::TestMakeRelative::test_under_root PASSED [ 55%] tests/test_image_library_service.py::TestMakeRelative::test_deep_path PASSED [ 55%] tests/test_image_library_service.py::TestMakeRelative::test_outside_root_returns_none PASSED [ 55%] tests/test_image_library_service.py::TestMakeRelative::test_no_root_returns_none PASSED [ 55%] tests/test_image_library_service.py::TestMakeRelative::test_forward_slashes PASSED [ 55%] tests/test_image_library_service.py::TestMakeRelative::test_unicode_filename PASSED [ 55%] tests/test_image_library_service.py::TestResolvePath::test_relative_path_with_root PASSED [ 55%] tests/test_image_library_service.py::TestResolvePath::test_legacy_absolute_path PASSED [ 55%] tests/test_image_library_service.py::TestResolvePath::test_relative_without_root_falls_through_to_file_path PASSED [ 55%] tests/test_image_library_service.py::TestResolvePath::test_no_path_returns_none PASSED [ 56%] tests/test_image_library_service.py::TestIsPathUnderRoot::test_under PASSED [ 56%] tests/test_image_library_service.py::TestIsPathUnderRoot::test_not_under PASSED [ 56%] tests/test_image_library_service.py::TestIsPathUnderRoot::test_no_root PASSED [ 56%] tests/test_image_library_service.py::TestDetectCommonRoot::test_detects_common_prefix PASSED [ 56%] tests/test_image_library_service.py::TestDetectCommonRoot::test_no_records_returns_none PASSED [ 56%] tests/test_image_library_service.py::TestDetectCommonRoot::test_ignores_already_migrated PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_basic_migration PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_uses_posix_separators PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_skips_outside_root PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_validate_missing_files PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_no_validate_accepts_missing_files PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_idempotent PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_persists_library_root PASSED [ 56%] tests/test_image_library_service.py::TestMigration::test_progress_callback PASSED [ 56%] tests/test_image_library_service.py::TestSingleton::test_init_and_get PASSED [ 56%] tests/test_image_library_service.py::TestSingleton::test_reinit_on_db_change PASSED [ 56%] tests/test_image_library_service.py::TestSingleton::test_resolve_image_path_no_root PASSED [ 56%] tests/test_image_library_service.py::TestSingleton::test_resolve_image_path_with_root PASSED [ 56%] tests/test_image_library_service.py::TestSingleton::test_resolve_none_when_no_path PASSED [ 56%] tests/test_image_library_service.py::TestScanServiceIntegration::test_relink_by_relative_path PASSED [ 57%] tests/test_image_library_service.py::TestScanServiceIntegration::test_backfills_relative_path_on_existing_records PASSED [ 57%] tests/test_image_library_service.py::TestResolveExistingPath::test_falls_back_to_file_path_when_join_is_wrong PASSED [ 57%] tests/test_image_library_service.py::TestResolveExistingPath::test_prefers_relative_path_when_it_exists PASSED [ 57%] tests/test_image_library_service.py::TestResolveExistingPath::test_returns_primary_when_nothing_exists PASSED [ 57%] tests/test_image_library_service.py::TestResolveExistingPath::test_no_relative_path_uses_file_path PASSED [ 57%] tests/test_image_library_service.py::TestResolveExistingPath::test_module_helper_uses_global_service PASSED [ 57%] tests/test_image_library_service.py::TestCountResolvable::test_counts_only_rows_with_relative_path PASSED [ 57%] tests/test_image_library_service.py::TestCountResolvable::test_reports_zero_for_an_unrelated_root PASSED [ 57%] tests/test_image_metadata_dialog.py::test_typed_place_and_dates_are_saved PASSED [ 57%] tests/test_image_metadata_dialog.py::test_existing_place_is_preselected_and_kept PASSED [ 57%] tests/test_image_metadata_dialog.py::test_remove_place_clears_the_link PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_single_person_list_mode PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_multi_persons_list_mode PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_multi_persons_cols_mode PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_cols_mode_auto_expands_columns PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_empty_persons PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_no_gps_exports_empty PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_gps_from_exif PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_gps_image_coords_take_priority_over_exif PASSED [ 57%] tests/test_image_metadata_export_service.py::test_export_csv_no_exif_fallback_to_filename_date PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_no_date_returns_unknown PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_location_from_place PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_anonymous_place_not_exported_as_location PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_utf8_accents PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_escaping_comma_in_name PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_selective_fields PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_places_date_location_gps_before_person_columns PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_multiple_images PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_csv_progress_callback PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_xlsx_basic PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_xlsx_gps_numeric PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_xlsx_utf8_accents PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_xlsx_cols_mode_separate_person_columns PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_xlsx_uses_same_column_order_as_csv PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_xlsx_normalizes_extension PASSED [ 58%] tests/test_image_metadata_export_service.py::test_export_xlsx_large_batch PASSED [ 58%] tests/test_image_path_matcher.py::test_find_missing_lists_only_records_without_a_file PASSED [ 58%] tests/test_image_path_matcher.py::test_hash_match_is_confident PASSED [ 58%] tests/test_image_path_matcher.py::test_same_name_different_content_is_demoted_and_not_confident PASSED [ 58%] tests/test_image_path_matcher.py::test_without_hash_verification_deeper_path_ranks_first PASSED [ 58%] tests/test_image_path_matcher.py::test_no_candidate_when_the_name_is_nowhere PASSED [ 59%] tests/test_image_path_matcher.py::test_progress_and_checkpoint_are_called PASSED [ 59%] tests/test_image_path_matcher.py::test_apply_updates_absolute_and_relative_paths PASSED [ 59%] tests/test_image_path_matcher.py::test_apply_refuses_to_point_two_records_at_one_file PASSED [ 59%] tests/test_image_path_matcher.py::test_renamed_file_is_found_by_content_hash PASSED [ 59%] tests/test_image_path_repair_dialog.py::test_folders_are_prefilled_and_deduplicated PASSED [ 59%] tests/test_image_path_repair_dialog.py::test_confident_rows_are_pre_checked_and_others_are_not PASSED [ 59%] tests/test_image_path_repair_dialog.py::test_decisions_only_include_checked_rows_with_a_candidate PASSED [ 59%] tests/test_image_path_repair_dialog.py::test_select_helpers_toggle_rows PASSED [ 59%] tests/test_image_path_repair_dialog.py::test_rows_carry_their_image_id PASSED [ 59%] tests/test_intra_image_consistency_service.py::TestSplitUnknownHealing::test_two_unknowns_on_one_image_are_merged PASSED [ 59%] tests/test_intra_image_consistency_service.py::TestSplitUnknownHealing::test_whole_losing_unknown_is_absorbed PASSED [ 59%] tests/test_intra_image_consistency_service.py::TestSafety::test_different_people_not_merged PASSED [ 59%] tests/test_intra_image_consistency_service.py::TestSafety::test_diff_correction_blocks_merge PASSED [ 59%] tests/test_intra_image_consistency_service.py::TestSafety::test_two_named_persons_conflict_skipped PASSED [ 59%] tests/test_intra_image_consistency_service.py::TestSafety::test_unknown_pulled_into_named_anchor PASSED [ 59%] tests/test_intra_image_consistency_service.py::TestSafety::test_disabled_config_is_noop PASSED [ 59%] tests/test_intra_image_duplicate_service.py::test_overlapping_duplicate_of_assigned_face_is_removed PASSED [ 59%] tests/test_intra_image_duplicate_service.py::test_duplicate_of_manual_face_is_removed PASSED [ 59%] tests/test_intra_image_duplicate_service.py::test_non_overlapping_same_person_is_kept PASSED [ 59%] tests/test_intra_image_duplicate_service.py::test_overlapping_different_person_is_kept PASSED [ 60%] tests/test_intra_image_duplicate_service.py::test_duplicate_across_different_images_is_kept PASSED [ 60%] tests/test_intra_image_duplicate_service.py::test_two_unassigned_duplicates_keeps_the_better_box PASSED [ 60%] tests/test_intra_image_duplicate_service.py::test_two_unassigned_different_people_overlap_are_kept PASSED [ 60%] tests/test_intra_image_duplicate_service.py::test_disabled_config_is_a_noop PASSED [ 60%] tests/test_intra_image_duplicate_service.py::test_multiple_duplicates_all_removed PASSED [ 60%] tests/test_job_types.py::TestJobSpecOrdering::test_scoped_jobs_sort_before_full_jobs PASSED [ 60%] tests/test_job_types.py::TestJobSpecOrdering::test_same_priority_uses_fifo_sequence PASSED [ 60%] tests/test_job_types.py::TestJobSpecOrdering::test_job_id_is_generated_when_missing PASSED [ 60%] tests/test_job_types.py::TestJobStatusPercent::test_zero_when_total_not_positive PASSED [ 60%] tests/test_job_types.py::TestJobStatusPercent::test_rounds_processed_fraction PASSED [ 60%] tests/test_job_types.py::TestJobStatusPercent::test_caps_at_one_hundred PASSED [ 60%] tests/test_label_placement.py::TestPlaceLabels::test_empty_input PASSED [ 60%] tests/test_label_placement.py::TestPlaceLabels::test_single_label_prefers_above_center PASSED [ 60%] tests/test_label_placement.py::TestPlaceLabels::test_selected_face_wins_overlap PASSED [ 60%] tests/test_label_placement.py::TestPlaceLabels::test_zero_size_label_uses_face_origin PASSED [ 60%] tests/test_label_placement.py::TestPlaceLabels::test_leader_line_when_placement_not_adjacent PASSED [ 60%] tests/test_label_placement.py::TestPlaceLabels::test_clamps_position_within_image PASSED [ 60%] tests/test_landmark_geometry.py::TestPlausibleGeometryPasses::test_canonical_frontal_face PASSED [ 60%] tests/test_landmark_geometry.py::TestPlausibleGeometryPasses::test_mildly_tilted_face PASSED [ 60%] tests/test_landmark_geometry.py::TestPlausibleGeometryPasses::test_small_face_same_ratios PASSED [ 61%] tests/test_landmark_geometry.py::TestPlausibleGeometryPasses::test_profile_face_bunched_eyes PASSED [ 61%] tests/test_landmark_geometry.py::TestPlausibleGeometryPasses::test_three_quarter_view_nose_toward_eye PASSED [ 61%] tests/test_landmark_geometry.py::TestPlausibleGeometryPasses::test_wide_set_eyes_baby_face PASSED [ 61%] tests/test_landmark_geometry.py::TestPlausibleGeometryPasses::test_profile_face_with_tilt PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_vertical_eye_line PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_nose_above_eyes PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_mouth_collapsed_onto_eyes PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_inter_ocular_too_narrow PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_inter_ocular_too_wide PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_nose_off_to_the_side PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_mouth_corners_swapped PASSED [ 61%] tests/test_landmark_geometry.py::TestImplausibleGeometryRejected::test_scrambled_collinear_landmarks PASSED [ 61%] tests/test_landmark_geometry.py::TestFailOpen::test_none_landmarks_pass PASSED [ 61%] tests/test_landmark_geometry.py::TestFailOpen::test_wrong_count_passes PASSED [ 61%] tests/test_landmark_geometry.py::TestFailOpen::test_non_finite_passes PASSED [ 61%] tests/test_landmark_geometry.py::TestFailOpen::test_coincident_eyes_degenerate_passes PASSED [ 61%] tests/test_landmark_geometry.py::TestFailOpen::test_zero_size_box_passes PASSED [ 61%] tests/test_logging_setup.py::TestSetupLogging::test_adds_stderr_handler PASSED [ 61%] tests/test_logging_setup.py::TestSetupLogging::test_optional_file_handler PASSED [ 61%] tests/test_logging_setup.py::TestSetupLogging::test_second_call_is_idempotent PASSED [ 62%] tests/test_logging_setup.py::TestSetupLogging::test_quiets_noisy_third_party_loggers PASSED [ 62%] tests/test_logging_setup.py::TestQLogHandler::test_emits_formatted_message PASSED [ 62%] tests/test_logging_setup.py::TestQLogHandler::test_format_matches_setup_logging PASSED [ 62%] tests/test_main.py::test_parse_args_defaults PASSED [ 62%] tests/test_main.py::test_parse_args_debug_and_db PASSED [ 62%] tests/test_main.py::test_main_module_importable PASSED [ 62%] tests/test_main_window.py::test_main_window_construct PASSED [ 62%] tests/test_main_window.py::test_path_repair_dialog_opens_with_the_current_folders PASSED [ 62%] tests/test_main_window.py::test_scan_triggers_object_search_on_new_images PASSED [ 62%] tests/test_manual_thumbnail.py::test_set_person_thumbnail_marks_manual PASSED [ 62%] tests/test_manual_thumbnail.py::test_set_person_thumbnail_wrong_person_raises PASSED [ 62%] tests/test_manual_thumbnail.py::test_set_person_thumbnail_missing_crop_raises PASSED [ 62%] tests/test_manual_thumbnail.py::test_clear_manual_person_thumbnail_resets_to_auto PASSED [ 62%] tests/test_manual_thumbnail.py::test_auto_thumbnail_not_overwritten_by_face_crop_refresh PASSED [ 62%] tests/test_manual_thumbnail.py::test_exclude_face_clears_manual_thumbnail_fallback PASSED [ 62%] tests/test_manual_thumbnail.py::test_set_place_thumbnail_marks_manual PASSED [ 62%] tests/test_manual_thumbnail.py::test_set_place_thumbnail_missing_file_raises PASSED [ 62%] tests/test_manual_thumbnail.py::test_ensure_thumbnail_skips_manual PASSED [ 62%] tests/test_manual_thumbnail.py::test_ensure_thumbnail_falls_back_when_manual_file_gone PASSED [ 62%] tests/test_manual_thumbnail.py::test_clear_manual_place_thumbnail PASSED [ 63%] tests/test_manual_thumbnail.py::test_reassign_face_invalidates_manual_thumbnail PASSED [ 63%] tests/test_manual_thumbnail.py::test_merge_places_inherits_manual_thumbnail_flag PASSED [ 63%] tests/test_manual_thumbnail.py::test_merge_places_keeps_target_manual_thumbnail PASSED [ 63%] tests/test_match_scoring.py::test_normalised_unit_vector PASSED [ 63%] tests/test_match_scoring.py::test_normalised_none_and_zero PASSED [ 63%] tests/test_match_scoring.py::test_person_centroid_from_face_embeddings PASSED [ 63%] tests/test_match_scoring.py::test_person_centroid_none_without_embeddings PASSED [ 63%] tests/test_match_scoring.py::test_fallback_scores_ranks_similar_people PASSED [ 63%] tests/test_match_scoring.py::test_match_scores_for_embedding_uses_recognition_service PASSED [ 63%] tests/test_match_scoring.py::test_match_scores_for_embedding_falls_back_to_centroids PASSED [ 63%] tests/test_match_scoring.py::test_match_scores_for_face_missing_face_returns_empty PASSED [ 63%] tests/test_match_scoring.py::test_match_scores_for_face_uses_embedding PASSED [ 63%] tests/test_match_scoring.py::test_match_scores_for_faces_averages_selected_faces PASSED [ 63%] tests/test_match_scoring.py::test_match_scores_for_person_uses_profile_centroid PASSED [ 63%] tests/test_match_scoring.py::test_overlay_deep_without_model_keeps_scores PASSED [ 63%] tests/test_match_scoring_deep.py::TestDeepProbabilities::test_returns_distribution_for_known_query PASSED [ 63%] tests/test_match_scoring_deep.py::TestDeepProbabilities::test_no_config_uses_default_dir_and_misses PASSED [ 63%] tests/test_match_scoring_deep.py::TestDeepProbabilities::test_disabled_engine_returns_empty PASSED [ 63%] tests/test_match_scoring_deep.py::TestOverlay::test_overrides_known_keeps_unknown PASSED [ 63%] tests/test_match_scoring_deep.py::TestOverlay::test_no_model_returns_scores_unchanged PASSED [ 64%] tests/test_move_project_issues_ready.py::test_resolve_owner_type_uses_repository_owner_for_organizations PASSED [ 64%] tests/test_move_project_issues_ready.py::test_resolve_owner_type_uses_repository_owner_for_users PASSED [ 64%] tests/test_move_project_issues_ready.py::test_referenced_issue_numbers_falls_back_to_checked_out_commit PASSED [ 64%] tests/test_move_project_issues_ready.py::test_resolve_status_options_returns_requested_option_ids PASSED [ 64%] tests/test_move_project_issues_ready.py::test_move_project_issues_by_status_moves_only_issue_items_with_source_status PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_disabled_falls_back_to_single_verifier_keep PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_disabled_falls_back_to_single_verifier_drop PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_too_few_technologies_falls_back_to_yunet PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_quorum_confirms_keeps_face PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_single_confirmation_drops_face PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_quorum_without_yunet_keeps_original_box PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_all_reject_drops_face PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_weighted_technology_keeps_face_alone PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_lone_yunet_still_dropped_with_weighted_tech_present PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_min_confirmations_three PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_real_opencv_technologies_are_available PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_insightface_abstains_when_unavailable PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_haar_and_eyes_cannot_keep_texture_box PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_two_deep_detectors_keep_face PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_insightface_alone_meets_deep_quorum PASSED [ 64%] tests/test_multi_stage_face_validator.py::test_weak_votes_count_when_no_second_deep_detector PASSED [ 65%] tests/test_multi_stage_face_validator.py::test_min_strong_weight_zero_restores_legacy_vote PASSED [ 65%] tests/test_multi_stage_face_validator.py::test_insightface_veto_overrides_other_confirmations PASSED [ 65%] tests/test_multi_stage_face_validator.py::test_insightface_plain_reject_is_not_a_veto PASSED [ 65%] tests/test_multi_stage_face_validator.py::test_insightface_vote_confirm_reject_veto_bands PASSED [ 65%] tests/test_multi_stage_face_validator.py::test_insightface_vote_probes_below_prepared_threshold PASSED [ 65%] tests/test_multi_stage_face_validator.py::test_insightface_detector_lowers_scrfd_threshold_for_one_call PASSED [ 65%] tests/test_nominatim_provider.py::test_parse_settlements_dedup_and_names PASSED [ 65%] tests/test_nominatim_provider.py::test_parse_streets PASSED [ 65%] tests/test_nominatim_provider.py::test_geocode_downgrades_level_when_house_missing PASSED [ 65%] tests/test_nominatim_provider.py::test_geocode_full_address_level PASSED [ 65%] tests/test_nominatim_provider.py::test_geocode_empty_result PASSED [ 65%] tests/test_non_face.py::test_model_needs_enough_examples PASSED [ 65%] tests/test_non_face.py::test_model_flags_new_patterns_but_not_unseen_people PASSED [ 65%] tests/test_non_face.py::test_mark_not_face_hides_box_and_stores_example PASSED [ 65%] tests/test_non_face.py::test_apply_hides_similar_boxes_only PASSED [ 65%] tests/test_non_face.py::test_restored_box_becomes_face_example_and_stays PASSED [ 65%] tests/test_non_face.py::test_too_few_examples_restores_model_hidden PASSED [ 65%] tests/test_non_face.py::test_unignore_not_face_restores_the_box PASSED [ 65%] tests/test_non_face.py::test_face_examples_are_not_on_the_ignore_list PASSED [ 65%] tests/test_non_face.py::test_dialog_lists_marks_and_restores_model_hidden PASSED [ 66%] tests/test_object_feature_service.py::test_extract_features_returns_keypoints PASSED [ 66%] tests/test_object_feature_service.py::test_extract_features_rescales_keypoints_to_original_pixels PASSED [ 66%] tests/test_object_feature_service.py::test_extract_features_rejects_empty_input PASSED [ 66%] tests/test_object_feature_service.py::test_pack_unpack_roundtrip PASSED [ 66%] tests/test_object_feature_service.py::test_unpack_features_handles_missing_blobs PASSED [ 66%] tests/test_object_feature_service.py::test_params_hash_changes_with_settings PASSED [ 66%] tests/test_object_feature_service.py::test_crop_bbox_clamps_to_image PASSED [ 66%] tests/test_object_feature_service.py::test_ensure_image_features_caches_and_reuses PASSED [ 66%] tests/test_object_feature_service.py::test_stale_params_hash_forces_recompute PASSED [ 66%] tests/test_object_feature_service.py::test_unreadable_image_is_marked_not_retried PASSED [ 66%] tests/test_object_feature_service.py::test_ensure_image_features_honours_cancel PASSED [ 66%] tests/test_object_feature_service.py::test_ensure_patch_features_extracts_reference_crops PASSED [ 66%] tests/test_object_feature_service.py::test_invalidate_images_drops_image_and_crop_features PASSED [ 66%] tests/test_object_feature_service.py::test_reference_occurrences_only_returns_bbox_markings PASSED [ 66%] tests/test_object_match_worker.py::test_worker_reports_progress_and_finds_the_copy PASSED [ 66%] tests/test_object_match_worker.py::test_worker_does_not_hold_the_write_lock PASSED [ 66%] tests/test_object_match_worker.py::test_worker_stops_when_the_task_is_cancelled PASSED [ 66%] tests/test_object_match_worker.py::test_worker_batch_mode_covers_every_object PASSED [ 66%] tests/test_object_match_worker.py::test_worker_scope_limits_the_images PASSED [ 66%] tests/test_object_match_worker.py::test_review_dialog_lists_and_decides PASSED [ 67%] tests/test_object_match_worker.py::test_review_dialog_rejects_without_marking PASSED [ 67%] tests/test_object_match_worker.py::test_worker_invalidates_changed_images_first PASSED [ 67%] tests/test_object_match_worker.py::test_review_dialog_lists_auto_tags_with_undo PASSED [ 67%] tests/test_object_match_worker.py::test_review_dialog_confirms_auto_tags PASSED [ 67%] tests/test_object_match_worker.py::test_review_dialog_is_empty_when_nothing_pending PASSED [ 67%] tests/test_object_match_worker.py::test_collage_fixture_really_contains_a_shrunk_copy PASSED [ 67%] tests/test_object_matching_service.py::test_finds_the_same_region_at_any_size[1.0] PASSED [ 67%] tests/test_object_matching_service.py::test_finds_the_same_region_at_any_size[0.5] PASSED [ 67%] tests/test_object_matching_service.py::test_finds_the_same_region_at_any_size[0.25] PASSED [ 67%] tests/test_object_matching_service.py::test_unrelated_image_produces_no_match PASSED [ 67%] tests/test_object_matching_service.py::test_rotated_copy_is_still_found PASSED [ 67%] tests/test_object_matching_service.py::test_min_score_gate_rejects_weak_hits PASSED [ 67%] tests/test_object_matching_service.py::test_bbox_iou_basics PASSED [ 67%] tests/test_object_matching_service.py::test_find_object_suggests_the_shrunk_copy PASSED [ 67%] tests/test_object_matching_service.py::test_accepting_creates_an_ai_occurrence_and_a_new_reference PASSED [ 67%] tests/test_object_matching_service.py::test_rejected_pairing_is_never_suggested_again PASSED [ 67%] tests/test_object_matching_service.py::test_already_marked_image_is_not_suggested_again PASSED [ 67%] tests/test_object_matching_service.py::test_search_scope_limits_the_images_scanned PASSED [ 67%] tests/test_object_matching_service.py::test_object_without_bbox_reference_is_skipped PASSED [ 67%] tests/test_object_matching_service.py::test_find_all_objects_covers_every_matchable_object PASSED [ 68%] tests/test_object_matching_service.py::test_search_honours_cancellation PASSED [ 68%] tests/test_object_matching_service.py::test_accept_above_and_clear_pending PASSED [ 68%] tests/test_object_matching_service.py::test_clear_pending_leaves_no_negative_memory PASSED [ 68%] tests/test_object_matching_service.py::test_occurrences_stay_out_of_face_recognition PASSED [ 68%] tests/test_object_matching_service.py::test_confident_hit_is_tagged_automatically PASSED [ 68%] tests/test_object_matching_service.py::test_auto_tagging_respects_the_switch_and_the_threshold PASSED [ 68%] tests/test_object_matching_service.py::test_unconfirmed_auto_tag_is_not_a_reference_until_confirmed PASSED [ 68%] tests/test_object_matching_service.py::test_reverted_auto_tag_is_removed_and_never_proposed_again PASSED [ 68%] tests/test_object_matching_service.py::test_confirm_all_auto PASSED [ 68%] tests/test_object_service.py::test_create_and_find PASSED [ 68%] tests/test_object_service.py::test_create_empty_name_rejected PASSED [ 68%] tests/test_object_service.py::test_get_or_create_is_idempotent PASSED [ 68%] tests/test_object_service.py::test_update_object PASSED [ 68%] tests/test_object_service.py::test_delete_cascades_occurrences PASSED [ 68%] tests/test_object_service.py::test_add_occurrence_idempotent_same_point PASSED [ 68%] tests/test_object_service.py::test_same_object_multiple_images PASSED [ 68%] tests/test_object_service.py::test_add_occurrence_bbox PASSED [ 68%] tests/test_object_service.py::test_thumbnail_specs_and_manual PASSED [ 68%] tests/test_object_service.py::test_update_occurrence_bbox PASSED [ 68%] tests/test_object_service.py::test_update_and_remove_occurrence PASSED [ 69%] tests/test_object_service.py::test_occurrences_for_image PASSED [ 69%] tests/test_object_service.py::test_list_objects_counts PASSED [ 69%] tests/test_object_service.py::test_list_objects_name_filter PASSED [ 69%] tests/test_object_service.py::test_search_by_name_description_note_accent_insensitive PASSED [ 69%] tests/test_object_service.py::test_person_links_roles PASSED [ 69%] tests/test_object_service.py::test_person_link_protected_rejected PASSED [ 69%] tests/test_object_service.py::test_person_link_bad_role_rejected PASSED [ 69%] tests/test_object_service.py::test_remove_person_link PASSED [ 69%] tests/test_object_service.py::test_merge_preserves_everything PASSED [ 69%] tests/test_object_service.py::test_merge_dedups_duplicate_occurrence PASSED [ 69%] tests/test_object_service.py::test_merge_keeps_object_matching_history PASSED [ 69%] tests/test_object_service.py::test_merge_self_is_noop PASSED [ 69%] tests/test_object_service.py::test_universal_search_finds_object_images PASSED [ 69%] tests/test_object_service.py::test_search_suggestions_include_objects PASSED [ 69%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_assigned_box_wins_over_unknown PASSED [ 69%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_two_unknown_boxes_keep_one PASSED [ 69%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_two_different_named_persons_are_untouchable PASSED [ 69%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_non_overlapping_boxes_are_kept PASSED [ 69%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_embedding_guard_protects_close_faces PASSED [ 69%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_nested_box_is_resolved_by_containment PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_nested_box_removed_despite_mismatched_embedding PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_two_nested_unknowns_collapse_to_one PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_unknown_group_box_loses_to_named PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_two_auto_recognised_persons_resolved_by_confidence PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_human_box_beats_ai_duplicate_of_other_person PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_two_human_named_persons_still_untouchable PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_orphan_unknown_removed_after_overlap_delete PASSED [ 70%] tests/test_overlap_resolution_service.py::TestOverlapResolution::test_manual_box_beats_assigned_auto_box PASSED [ 70%] tests/test_perf.py::TestPerfRecord::test_avg_ms PASSED [ 70%] tests/test_perf.py::TestPerfRecord::test_avg_ms_zero_when_no_samples PASSED [ 70%] tests/test_perf.py::TestRecordAndTimedBlock::test_record_accumulates_stats PASSED [ 70%] tests/test_perf.py::TestRecordAndTimedBlock::test_timed_block_records_elapsed PASSED [ 70%] tests/test_perf.py::TestRecordAndTimedBlock::test_timed_decorator_records_function PASSED [ 70%] tests/test_perf.py::TestRecordAndTimedBlock::test_slow_operation_logs_warning PASSED [ 70%] tests/test_perf.py::TestPerfCounter::test_hit_miss_and_hit_rate PASSED [ 70%] tests/test_perf.py::TestPerfCounter::test_hit_rate_zero_without_samples PASSED [ 70%] tests/test_perf.py::TestResetAndReport::test_reset_clears_records_and_counters PASSED [ 70%] tests/test_perf.py::TestResetAndReport::test_report_includes_operations_and_rss PASSED [ 70%] tests/test_perf.py::TestMemoryMb::test_returns_non_negative_float PASSED [ 70%] tests/test_perf.py::TestAttachSqlTiming::test_records_sql_total PASSED [ 70%] tests/test_person_full_delete.py::TestFullDelete::test_hard_delete_removes_person_and_all_faces PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_hard_deleted_person_does_not_reappear_after_recluster PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_other_persons_and_faces_are_untouched PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_missing_crop_file_does_not_crash PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_delete_with_ignore_snapshots_embeddings_and_deletes_faces PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_ignored_person_suppressed_on_redetection PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_delete_without_ignore_adds_nothing_to_ignore_list PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_protected_person_cannot_be_deleted PASSED [ 71%] tests/test_person_full_delete.py::TestFullDelete::test_default_mode_still_unassigns_faces PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_create_returns_group PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_create_trims_whitespace PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_create_empty_name_raises PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_duplicate_name_raises PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_duplicate_name_case_insensitive PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_list_groups_alphabetical PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_rename_group PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_rename_to_existing_raises PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_delete_group PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_get_or_create_returns_existing PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_update_group_renames_and_adds_note PASSED [ 71%] tests/test_person_group_service.py::TestCreateGroup::test_update_group_note_only_keeps_name PASSED [ 72%] tests/test_person_group_service.py::TestCreateGroup::test_update_group_empty_description_clears_it PASSED [ 72%] tests/test_person_group_service.py::TestCreateGroup::test_update_group_empty_name_raises PASSED [ 72%] tests/test_person_group_service.py::TestCreateGroup::test_update_group_duplicate_name_raises PASSED [ 72%] tests/test_person_group_service.py::TestCreateGroup::test_update_group_same_name_allowed PASSED [ 72%] tests/test_person_group_service.py::TestCreateGroup::test_update_group_missing_raises PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_set_person_groups_single PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_set_person_multiple_groups PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_remove_person_from_group PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_clear_all_groups PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_multiple_persons_same_group PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_protected_person_cannot_have_groups PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_member_count_in_list PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_cascade_delete_group_removes_memberships PASSED [ 72%] tests/test_person_group_service.py::TestMembership::test_duplicate_group_ids_deduplicated PASSED [ 72%] tests/test_person_group_service.py::TestAddRemoveMember::test_add_creates_membership PASSED [ 72%] tests/test_person_group_service.py::TestAddRemoveMember::test_add_is_idempotent PASSED [ 72%] tests/test_person_group_service.py::TestAddRemoveMember::test_add_protected_person_raises PASSED [ 72%] tests/test_person_group_service.py::TestAddRemoveMember::test_add_unknown_group_raises PASSED [ 72%] tests/test_person_group_service.py::TestAddRemoveMember::test_remove_deletes_membership PASSED [ 72%] tests/test_person_group_service.py::TestAddRemoveMember::test_remove_non_member_returns_false PASSED [ 73%] tests/test_person_search.py::TestNormalize::test_lowercase PASSED [ 73%] tests/test_person_search.py::TestNormalize::test_accent_stripped PASSED [ 73%] tests/test_person_search.py::TestNormalize::test_mixed PASSED [ 73%] tests/test_person_search.py::TestNormalize::test_no_accents_unchanged PASSED [ 73%] tests/test_person_search.py::TestNormalize::test_empty PASSED [ 73%] tests/test_person_search.py::TestPersonEntry::test_normalized_computed PASSED [ 73%] tests/test_person_search.py::TestPersonEntry::test_display_text_defaults_to_name PASSED [ 73%] tests/test_person_search.py::TestPersonEntry::test_display_text_custom PASSED [ 73%] tests/test_person_search.py::TestPersonEntry::test_search_uses_name_not_display_text PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_empty_query_returns_all PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_whitespace_only_returns_all PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_case_insensitive PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_case_insensitive_upper PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_accent_insensitive_panni PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_partial_match PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_no_results PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_max_results PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_prefix_ranked_first PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_no_cross_contamination_same_name PASSED [ 73%] tests/test_person_search.py::TestSearchPersons::test_accent_o_variants PASSED [ 74%] tests/test_person_search.py::TestSearchPersons::test_accent_o_with_accented_query PASSED [ 74%] tests/test_person_search.py::TestSearchPersons::test_not_found_returns_empty PASSED [ 74%] tests/test_person_search.py::TestSearchPersons::test_subset_not_modified PASSED [ 74%] tests/test_person_search.py::TestFilterEntries::test_empty_query_returns_all_in_order PASSED [ 74%] tests/test_person_search.py::TestFilterEntries::test_preserves_incoming_order PASSED [ 74%] tests/test_person_search.py::TestFilterEntries::test_does_not_rerank_by_prefix PASSED [ 74%] tests/test_person_search.py::TestFilterEntries::test_filters_accent_insensitive PASSED [ 74%] tests/test_person_search.py::TestFilterEntries::test_no_results PASSED [ 74%] tests/test_person_search.py::TestFilterEntries::test_max_results PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectReset::test_preselect_best_match_selects_top_when_match_sorted PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectReset::test_preselect_returns_false_without_match_sort PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectReset::test_clear_selection_drops_previous_target PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectReset::test_set_current_by_id_scrolls_selection_into_view PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectReset::test_set_current_by_id_missing_target_scrolls_to_top PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectMatchSort::test_default_sort_orders_list_by_score_desc PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectMatchSort::test_default_sort_does_not_persist_global_preference PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectMatchSort::test_search_preserves_score_order PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectMatchSort::test_no_default_sort_keeps_name_order PASSED [ 74%] tests/test_person_search.py::TestPersonSearchSelectMatchSort::test_percentage_shown_when_match_sorted PASSED [ 74%] tests/test_person_search.py::TestPersonIsUnknown::test_named_person_is_not_unknown PASSED [ 75%] tests/test_person_search.py::TestPersonIsUnknown::test_auto_named_is_unknown PASSED [ 75%] tests/test_person_search.py::TestPersonIsUnknown::test_protected_is_unknown PASSED [ 75%] tests/test_person_search.py::TestPersonIsUnknown::test_missing_attributes_default_false PASSED [ 75%] tests/test_person_search.py::TestSearchPersonsHideUnknown::test_base_list_hides_unknown PASSED [ 75%] tests/test_person_search.py::TestSearchPersonsHideUnknown::test_base_list_keeps_unknown_when_flag_off PASSED [ 75%] tests/test_person_search.py::TestSearchPersonsHideUnknown::test_search_reveals_unknown PASSED [ 75%] tests/test_person_search.py::TestSearchPersonsHideUnknown::test_search_for_named_unaffected PASSED [ 75%] tests/test_person_search.py::TestPersonSearchSelectUnknown::test_base_list_hides_unknown_by_default PASSED [ 75%] tests/test_person_search.py::TestPersonSearchSelectUnknown::test_search_reveals_unknown PASSED [ 75%] tests/test_person_search.py::TestPersonSearchSelectUnknown::test_opt_out_shows_unknown_in_base PASSED [ 75%] tests/test_person_search.py::TestPersonSearchSelectUnknown::test_match_sort_keeps_unknown_visible PASSED [ 75%] tests/test_person_service.py::test_list_persons_with_counts PASSED [ 75%] tests/test_person_service.py::test_list_persons_thumbnail_fallback_to_crop PASSED [ 75%] tests/test_person_service.py::test_list_persons_filter_by_name_and_code PASSED [ 75%] tests/test_person_service.py::test_update_person_normalises_empty_strings PASSED [ 75%] tests/test_person_service.py::test_rename_rejects_empty_and_protected PASSED [ 75%] tests/test_person_service.py::test_set_thumbnail_from_face PASSED [ 75%] tests/test_person_service.py::test_set_thumbnail_missing_crop_raises PASSED [ 75%] tests/test_person_service.py::test_set_thumbnail_wrong_person_raises PASSED [ 75%] tests/test_person_service.py::test_set_thumbnail_face_not_found_raises PASSED [ 76%] tests/test_person_service.py::test_list_face_crops_orders_by_confidence_and_skips_excluded PASSED [ 76%] tests/test_person_service.py::test_list_images_for_person_returns_distinct_sorted_paths PASSED [ 76%] tests/test_person_service.py::test_list_persons_includes_groups_and_flags PASSED [ 76%] tests/test_person_service.py::test_list_persons_thumbnail_fallback_skips_excluded_faces PASSED [ 76%] tests/test_person_service.py::test_rename_person_clears_auto_named PASSED [ 76%] tests/test_person_service.py::test_update_person_ignores_unknown_and_name_fields PASSED [ 76%] tests/test_person_service.py::test_update_person_writes_all_editable_fields PASSED [ 76%] tests/test_person_service.py::test_update_person_with_family_code_links_derived_parents PASSED [ 76%] tests/test_person_service.py::test_link_derived_parents_swallows_errors PASSED [ 76%] tests/test_person_service.py::test_require_person_raises_for_missing_id PASSED [ 76%] tests/test_persons_panel_refresh.py::test_mark_stale_refreshes_when_visible PASSED [ 76%] tests/test_persons_panel_refresh.py::test_mark_stale_defers_until_shown_when_hidden PASSED [ 76%] tests/test_place_address.py::test_build_display_name_levels PASSED [ 76%] tests/test_place_address.py::test_build_place_label_keeps_original_name PASSED [ 76%] tests/test_place_address.py::test_classify_settlement_only PASSED [ 76%] tests/test_place_address.py::test_classify_with_street PASSED [ 76%] tests/test_place_address.py::test_classify_full_address PASSED [ 76%] tests/test_place_address.py::test_create_place_from_full_address PASSED [ 76%] tests/test_place_address.py::test_create_requires_settlement PASSED [ 76%] tests/test_place_address.py::test_set_manual_coordinates_overrides_source PASSED [ 76%] tests/test_place_address.py::test_update_place_address_rebuilds_display PASSED [ 77%] tests/test_place_address.py::test_custom_name_survives_address_save PASSED [ 77%] tests/test_place_address.py::test_update_place_address_without_name_falls_back_to_display PASSED [ 77%] tests/test_place_address.py::test_search_matches_structured_fields PASSED [ 77%] tests/test_place_address.py::test_find_places_by_settlement PASSED [ 77%] tests/test_place_address.py::test_migrate_display_name_backfill PASSED [ 77%] tests/test_place_search_select.py::test_enter_on_unmatched_query_requests_create PASSED [ 77%] tests/test_place_search_select.py::test_enter_on_matching_query_selects_existing PASSED [ 77%] tests/test_place_search_select.py::test_enter_on_empty_query_does_nothing PASSED [ 77%] tests/test_place_search_select.py::test_double_click_emits_place_double_clicked PASSED [ 77%] tests/test_place_search_select.py::test_double_click_without_selection_is_silent PASSED [ 77%] tests/test_place_service.py::test_place_create_and_image_link PASSED [ 77%] tests/test_place_service.py::test_assign_existing_place_by_name PASSED [ 77%] tests/test_place_service.py::test_scan_processes_exif_gps_without_blocking_import PASSED [ 77%] tests/test_place_service.py::test_exif_gps_processing_error_does_not_abort_scan PASSED [ 77%] tests/test_place_service.py::test_merge_places_moves_images_and_preserves_source_data PASSED [ 77%] tests/test_place_service.py::test_merge_anonymous_place_clears_is_anonymous PASSED [ 77%] tests/test_place_service.py::test_merge_anonymous_target_clears_is_anonymous PASSED [ 77%] tests/test_place_service.py::test_name_place_persists_across_sessions PASSED [ 77%] tests/test_place_service.py::test_list_persons_for_place_uses_face_assignments PASSED [ 77%] tests/test_place_service.py::test_place_filters_by_date_and_image_count PASSED [ 78%] tests/test_place_service.py::test_image_browser_filters_by_place PASSED [ 78%] tests/test_place_types.py::test_normalize_place_type_falls_back_to_area PASSED [ 78%] tests/test_place_types.py::test_default_radius_for_each_type PASSED [ 78%] tests/test_place_types.py::test_create_place_defaults_to_area PASSED [ 78%] tests/test_place_types.py::test_create_place_explicit_type_and_radius PASSED [ 78%] tests/test_place_types.py::test_set_coordinates_updates_place PASSED [ 78%] tests/test_place_types.py::test_set_coordinates_clears_when_both_none PASSED [ 78%] tests/test_place_types.py::test_set_coordinates_rejects_partial PASSED [ 78%] tests/test_place_types.py::test_set_coordinates_rejects_out_of_range PASSED [ 78%] tests/test_place_types.py::test_find_nearby_filters_by_type PASSED [ 78%] tests/test_place_types.py::test_find_nearby_use_place_radius PASSED [ 78%] tests/test_place_types.py::test_exif_prefers_exact_over_area PASSED [ 78%] tests/test_place_types.py::test_exif_falls_back_to_area_when_no_exact PASSED [ 78%] tests/test_place_types.py::test_exif_creates_anonymous_exact_when_nothing_matches PASSED [ 78%] tests/test_place_types.py::test_set_parent_and_descendants PASSED [ 78%] tests/test_place_types.py::test_set_parent_rejects_cycles PASSED [ 78%] tests/test_place_types.py::test_set_place_type_resets_radius_to_default PASSED [ 78%] tests/test_place_types.py::test_set_place_type_explicit_radius_wins PASSED [ 78%] tests/test_place_types.py::test_list_places_type_filter_and_summary PASSED [ 78%] tests/test_place_types.py::test_migrate_place_types_backfills_nulls PASSED [ 79%] tests/test_post_buffer_release.py::test_build_release_post_text_includes_successful_platforms_and_url PASSED [ 79%] tests/test_post_buffer_release.py::test_select_target_channel_prefers_named_match PASSED [ 79%] tests/test_post_buffer_release.py::test_empty_buffer_post_mode_falls_back_to_default PASSED [ 79%] tests/test_post_x_release.py::test_build_release_post_text_includes_successful_platforms_and_url PASSED [ 79%] tests/test_post_x_release.py::test_build_release_post_text_trims_variants_to_fit_limit PASSED [ 79%] tests/test_project_package_service.py::test_export_creates_facepack_with_expected_layout PASSED [ 79%] tests/test_project_package_service.py::test_export_handles_pre_1980_timestamp PASSED [ 79%] tests/test_project_package_service.py::test_import_restores_pre_1980_timestamp PASSED [ 79%] tests/test_project_package_service.py::test_external_image_with_windows_path_and_long_name PASSED [ 79%] tests/test_project_package_service.py::test_export_skips_foreign_windows_path_without_crashing PASSED [ 79%] tests/test_project_package_service.py::test_is_foreign_windows_path_detection SKIPPED [ 79%] tests/test_project_package_service.py::test_safe_file_exists_swallows_oserror PASSED [ 79%] tests/test_project_package_service.py::test_safe_component_truncates_to_byte_budget PASSED [ 79%] tests/test_project_package_service.py::test_export_appends_extension_when_missing PASSED [ 79%] tests/test_project_package_service.py::test_export_reports_missing_image_without_aborting PASSED [ 79%] tests/test_project_package_service.py::test_validate_rejects_wrong_extension PASSED [ 79%] tests/test_project_package_service.py::test_validate_rejects_missing_manifest PASSED [ 79%] tests/test_project_package_service.py::test_validate_rejects_bad_manifest_json PASSED [ 79%] tests/test_project_package_service.py::test_validate_accepts_real_package PASSED [ 79%] tests/test_project_package_service.py::test_import_round_trip PASSED [ 80%] tests/test_project_package_service.py::test_import_rejects_wrong_extension PASSED [ 80%] tests/test_project_package_service.py::test_import_rejects_non_empty_destination PASSED [ 80%] tests/test_project_package_service.py::test_import_refuses_path_traversal_archive PASSED [ 80%] tests/test_project_package_service.py::test_import_remaps_crop_paths PASSED [ 80%] tests/test_project_package_service.py::test_import_remaps_image_paths_with_relative_path PASSED [ 80%] tests/test_project_package_service.py::test_import_remaps_legacy_absolute_only_image FAILED [ 80%] tests/test_project_package_service.py::test_sanitize_component_handles_windows_illegal_names PASSED [ 80%] tests/test_project_package_service.py::test_sanitize_archive_path_splits_both_separators PASSED [ 80%] tests/test_project_package_service.py::test_safe_extract_sanitizes_illegal_member_name PASSED [ 80%] tests/test_project_package_service.py::test_remap_image_paths_matches_sanitized_extraction PASSED [ 80%] tests/test_recording_metadata.py::test_begin_writes_pessimistic_record PASSED [ 80%] tests/test_recording_metadata.py::test_finalize_clean_exit PASSED [ 80%] tests/test_recording_metadata.py::test_finalize_crashed_keeps_flag_and_errors PASSED [ 80%] tests/test_recording_metadata.py::test_resolution_omitted_when_unknown PASSED [ 80%] tests/test_recording_metadata.py::test_monitors_and_layout_round_trip PASSED [ 80%] tests/test_recording_timeline_log.py::test_format_timestamp_zero PASSED [ 80%] tests/test_recording_timeline_log.py::test_format_timestamp_whole_seconds PASSED [ 80%] tests/test_recording_timeline_log.py::test_format_timestamp_fractional_seconds PASSED [ 80%] tests/test_recording_timeline_log.py::test_format_timestamp_negative_clamped PASSED [ 80%] tests/test_recording_timeline_log.py::test_format_timestamp_rounding PASSED [ 81%] tests/test_recording_timeline_log.py::test_basic_srt_output PASSED [ 81%] tests/test_recording_timeline_log.py::test_person_line_omitted_when_no_person PASSED [ 81%] tests/test_recording_timeline_log.py::test_consecutive_identical_states_collapse PASSED [ 81%] tests/test_recording_timeline_log.py::test_person_change_on_same_image_starts_new_block PASSED [ 81%] tests/test_recording_timeline_log.py::test_partial_written_after_each_note PASSED [ 81%] tests/test_recording_timeline_log.py::test_finalize_removes_partial PASSED [ 81%] tests/test_recording_timeline_log.py::test_empty_timeline PASSED [ 81%] tests/test_recording_timeline_log.py::test_custom_person_prefix PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageCreation::test_can_create_remote_image PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageCreation::test_remote_image_repr PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageCreation::test_defaults PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageCreation::test_all_fields PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageRelationship::test_image_remote_image_backref PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageRelationship::test_remote_image_to_image_backref PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageRelationship::test_image_without_remote_has_none PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageRelationship::test_cascade_delete_removes_remote_image PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageRelationship::test_one_to_one_uniqueness PASSED [ 81%] tests/test_remote_image_model.py::TestRemoteImageRelationship::test_deleted_remote_flag_update PASSED [ 81%] tests/test_rerecognition_service.py::TestClassification::test_strong_match_is_auto PASSED [ 81%] tests/test_rerecognition_service.py::TestClassification::test_partial_match_is_suggest PASSED [ 82%] tests/test_rerecognition_service.py::TestClassification::test_far_match_is_none PASSED [ 82%] tests/test_rerecognition_service.py::TestClassification::test_named_faces_are_never_candidates PASSED [ 82%] tests/test_rerecognition_service.py::TestApplyAndUndo::test_auto_merge_moves_face_logs_and_cleans_up PASSED [ 82%] tests/test_rerecognition_service.py::TestApplyAndUndo::test_skips_merge_when_target_already_overlaps_on_image PASSED [ 82%] tests/test_rerecognition_service.py::TestApplyAndUndo::test_undo_restores_face_and_recreates_unknown PASSED [ 82%] tests/test_rerecognition_service.py::TestApplyAndUndo::test_latest_undoable_batch_tracks_state PASSED [ 82%] tests/test_rerecognition_worker.py::TestEmitProgress::test_throttles_intermediate_updates PASSED [ 82%] tests/test_rerecognition_worker.py::TestEmitProgress::test_routes_through_task_context PASSED [ 82%] tests/test_rerecognition_worker.py::TestExecute::test_nothing_to_do PASSED [ 82%] tests/test_rerecognition_worker.py::TestExecute::test_classifies_and_applies_auto PASSED [ 82%] tests/test_rerecognition_worker.py::TestExecute::test_cancel_mid_loop PASSED [ 82%] tests/test_rerecognition_worker.py::TestRun::test_run_emits_result PASSED [ 82%] tests/test_rerecognition_worker.py::TestRun::test_run_cancelled PASSED [ 82%] tests/test_rerecognition_worker.py::TestRun::test_run_failure_emits_failed PASSED [ 82%] tests/test_rerecognition_worker.py::TestRun::test_run_in_task_uses_ctx_token PASSED [ 82%] tests/test_rerecognition_worker.py::TestAiFaceDetection::test_skipped_when_disabled PASSED [ 82%] tests/test_rerecognition_worker.py::TestAiFaceDetection::test_populates_stats_when_enabled PASSED [ 82%] tests/test_rerecognition_worker.py::TestAiFaceDetection::test_detection_config_is_threaded_to_service PASSED [ 82%] tests/test_resource_governor.py::test_ramp_clamps_and_interpolates PASSED [ 82%] tests/test_resource_governor.py::test_no_throttle_when_idle PASSED [ 82%] tests/test_resource_governor.py::test_throttle_proportional_to_work_slice PASSED [ 83%] tests/test_resource_governor.py::test_throttle_scales_with_weight PASSED [ 83%] tests/test_resource_governor.py::test_workers_scale_down_with_load PASSED [ 83%] tests/test_resource_governor.py::test_disabled_override_means_full_speed PASSED [ 83%] tests/test_resource_governor.py::test_checkpoint_sleeps_under_load PASSED [ 83%] tests/test_resource_governor.py::test_checkpoint_no_sleep_when_idle PASSED [ 83%] tests/test_resource_governor.py::test_throttle_aborts_on_cancel PASSED [ 83%] tests/test_retroactive_verification_service.py::test_high_confidence_faces_are_exempt PASSED [ 83%] tests/test_retroactive_verification_service.py::test_verify_all_drops_exemption PASSED [ 83%] tests/test_retroactive_verification_service.py::test_manual_and_assigned_faces_are_flagged_not_dropped PASSED [ 83%] tests/test_scan_modes_dialog.py::test_dialog_has_two_tabs PASSED [ 83%] tests/test_scan_modes_dialog.py::test_dialog_builds_with_all_cards PASSED [ 83%] tests/test_scan_modes_dialog.py::test_workflow_signal_emitted PASSED [ 83%] tests/test_scan_modes_dialog.py::test_maintenance_signal_emitted PASSED [ 83%] tests/test_scan_modes_dialog.py::test_overlap_card_offers_every_sensitivity PASSED [ 83%] tests/test_scan_modes_dialog.py::test_overlap_sensitivity_choice_is_persisted PASSED [ 83%] tests/test_scan_service.py::TestHashFile::test_consistent_hash PASSED [ 83%] tests/test_scan_service.py::TestHashFile::test_different_content_different_hash PASSED [ 83%] tests/test_scan_service.py::TestScanService::test_scan_finds_correct_files PASSED [ 83%] tests/test_scan_service.py::TestScanService::test_rescan_skips_unchanged PASSED [ 83%] tests/test_scan_service.py::TestScanService::test_modified_file_requeued PASSED [ 84%] tests/test_scan_service.py::TestScanService::test_custom_extensions PASSED [ 84%] tests/test_scan_service.py::TestMovedFiles::test_moved_and_renamed_file_relinks_record PASSED [ 84%] tests/test_scan_service.py::TestMovedFiles::test_copy_with_original_present_gets_own_record PASSED [ 84%] tests/test_screen_recorder_args.py::test_quality_presets PASSED [ 84%] tests/test_screen_recorder_args.py::test_macos_args_mic_only PASSED [ 84%] tests/test_screen_recorder_args.py::test_macos_args_with_system_audio_mixes PASSED [ 84%] tests/test_screen_recorder_args.py::test_windows_args_mic_only PASSED [ 84%] tests/test_screen_recorder_args.py::test_windows_args_with_system_audio_mixes PASSED [ 84%] tests/test_screen_recorder_args.py::test_volume_and_mute_controls PASSED [ 84%] tests/test_screen_recorder_args.py::test_meter_adds_stdout_tap PASSED [ 84%] tests/test_screen_recorder_args.py::test_no_audio_uses_an PASSED [ 84%] tests/test_screen_recorder_args.py::test_unsupported_platform_raises PASSED [ 84%] tests/test_screen_recorder_args.py::test_forced_keyframes_at_segment_boundary PASSED [ 84%] tests/test_screen_recorder_args.py::test_cursor_disabled PASSED [ 84%] tests/test_screen_recorder_args.py::test_concat_list_escapes_quotes PASSED [ 84%] tests/test_screen_recorder_args.py::test_concat_args PASSED [ 84%] tests/test_screen_recorder_args.py::test_parse_avfoundation_devices PASSED [ 84%] tests/test_screen_recorder_args.py::test_parse_dshow_audio_devices PASSED [ 84%] tests/test_screen_recorder_args.py::test_pick_microphone_prefers_builtin_over_continuity PASSED [ 84%] tests/test_screen_recorder_args.py::test_pick_system_audio PASSED [ 85%] tests/test_screen_recorder_args.py::test_selected_displays_modes PASSED [ 85%] tests/test_screen_recorder_args.py::test_displays_bounding_box PASSED [ 85%] tests/test_screen_recorder_args.py::test_mode_from_value_defaults_to_all PASSED [ 85%] tests/test_screen_recorder_args.py::test_resolve_region_windows_all_is_bounding_box_crop PASSED [ 85%] tests/test_screen_recorder_args.py::test_resolve_region_windows_selected_single_monitor PASSED [ 85%] tests/test_screen_recorder_args.py::test_resolve_region_windows_active_window_uses_bounds PASSED [ 85%] tests/test_screen_recorder_args.py::test_capture_screen_indices_skips_cameras PASSED [ 85%] tests/test_screen_recorder_args.py::test_resolve_region_macos_all_uses_primary_av_index PASSED [ 85%] tests/test_screen_recorder_args.py::test_resolve_region_macos_falls_back_when_av_index_unknown PASSED [ 85%] tests/test_screen_recorder_args.py::test_resolve_region_macos_selected_uses_av_index PASSED [ 85%] tests/test_screen_recorder_args.py::test_resolve_region_macos_active_window_falls_back_to_primary PASSED [ 85%] tests/test_screen_recorder_args.py::test_effective_fps_reduces_for_multi_monitor PASSED [ 85%] tests/test_screen_recorder_args.py::test_build_args_windows_with_crop_region PASSED [ 85%] tests/test_screen_recorder_args.py::test_build_args_windows_with_window_title PASSED [ 85%] tests/test_screen_recorder_args.py::test_build_args_macos_region_overrides_screen_index PASSED [ 85%] tests/test_screen_recorder_args.py::test_parse_meter_peak_db PASSED [ 85%] tests/test_screen_recorder_args.py::test_parse_ffprobe_audio_present PASSED [ 85%] tests/test_screen_recorder_args.py::test_parse_ffprobe_audio_missing_and_zero_duration PASSED [ 85%] tests/test_screen_recorder_args.py::test_audio_diagnostics_explains_missing_system_audio PASSED [ 85%] tests/test_screen_recorder_args.py::test_audio_diagnostics_reports_mute PASSED [ 86%] tests/test_screen_recorder_args.py::test_probe_devices_honors_explicit_mic_override PASSED [ 86%] tests/test_screen_recorder_args.py::test_format_display_label PASSED [ 86%] tests/test_screen_recorder_args.py::test_resolve_region_uses_physical_pixels_on_windows PASSED [ 86%] tests/test_screen_recorder_args.py::test_windows_ddagrab_single_monitor_graph PASSED [ 86%] tests/test_screen_recorder_args.py::test_windows_ddagrab_audio_inputs_start_at_zero PASSED [ 86%] tests/test_screen_recorder_args.py::test_windows_ddagrab_active_window_crops_within_the_monitor PASSED [ 86%] tests/test_screen_recorder_args.py::test_windows_ddagrab_composites_multiple_monitors PASSED [ 86%] tests/test_screen_recorder_args.py::test_windows_gdigrab_stays_on_its_old_argument_shape PASSED [ 86%] tests/test_screen_recorder_args.py::test_windows_auto_prefers_ddagrab_when_outputs_are_known PASSED [ 86%] tests/test_screen_recorder_args.py::test_windows_falls_back_to_gdigrab_without_a_dxgi_index PASSED [ 86%] tests/test_screen_recorder_args.py::test_ddagrab_adapter_override PASSED [ 86%] tests/test_screen_recorder_args.py::test_selected_displays_accepts_a_legacy_qt_name PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_parse_blackdetect_sums_intervals PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_parse_blackdetect_without_intervals_is_zero PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_evaluate_black_probe_all_black PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_evaluate_black_probe_mostly_lit_is_not_black PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_evaluate_black_probe_without_frames_is_not_ok PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_parse_gdigrab_desktop_rect PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_parse_gdigrab_desktop_rect_without_banner PASSED [ 86%] tests/test_screen_recorder_black_probe.py::test_gdigrab_coordinate_scale_identity_when_unscaled PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_gdigrab_coordinate_scale_detects_system_scaling PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_gdigrab_coordinate_scale_rejects_inconsistent_axes PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_gdigrab_coordinate_scale_without_data_is_identity PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_scale_region_for_gdigrab_rounds_to_even PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_scale_region_for_gdigrab_is_a_noop_at_unity PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_scale_region_for_gdigrab_leaves_dda_targets_alone PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_build_preflight_args_ddagrab PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_build_preflight_args_gdigrab PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_build_black_scan_args_samples_the_head PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_build_black_scan_args_without_sample_limit PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_parse_ffprobe_video PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_parse_ffprobe_video_without_video_stream PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_video_validation_summary_flags_black PASSED [ 87%] tests/test_screen_recorder_black_probe.py::test_video_validation_summary_reports_ok PASSED [ 87%] tests/test_screen_recorder_service.py::test_is_macos_and_windows PASSED [ 87%] tests/test_screen_recorder_service.py::test_build_audio_filtergraph_single_source PASSED [ 87%] tests/test_screen_recorder_service.py::test_build_audio_filtergraph_mixes_two_sources PASSED [ 87%] tests/test_screen_recorder_service.py::test_build_audio_filtergraph_meter_tap PASSED [ 87%] tests/test_screen_recorder_service.py::test_build_audio_filtergraph_empty PASSED [ 87%] tests/test_screen_recorder_service.py::test_match_name_exact_and_substring PASSED [ 88%] tests/test_screen_recorder_service.py::test_match_av_index PASSED [ 88%] tests/test_screen_recorder_service.py::test_resolve_system_audio_macos_disabled PASSED [ 88%] tests/test_screen_recorder_service.py::test_resolve_system_audio_macos_explicit PASSED [ 88%] tests/test_screen_recorder_service.py::test_is_loopback PASSED [ 88%] tests/test_screen_recorder_service.py::test_resolve_ffmpeg_explicit PASSED [ 88%] tests/test_screen_recorder_service.py::test_resolve_ffprobe_next_to_ffmpeg PASSED [ 88%] tests/test_screen_recorder_service.py::test_list_audio_devices_empty_without_ffmpeg PASSED [ 88%] tests/test_screen_recorder_service.py::test_list_audio_devices_macos PASSED [ 88%] tests/test_screen_recorder_service.py::test_probe_screen_indices_non_macos PASSED [ 88%] tests/test_screen_recorder_service.py::test_validate_recording_audio_missing_ffprobe PASSED [ 88%] tests/test_screen_recorder_service.py::test_validate_recording_audio_missing_file PASSED [ 88%] tests/test_screen_recorder_service.py::test_validate_recording_audio_parses_probe_output PASSED [ 88%] tests/test_screen_recorder_service.py::TestScreenRecorderService::test_initial_state PASSED [ 88%] tests/test_screen_recorder_service.py::TestScreenRecorderService::test_start_pause_resume_stop PASSED [ 88%] tests/test_screen_recorder_service.py::TestScreenRecorderService::test_fail_when_ffmpeg_missing PASSED [ 88%] tests/test_screen_recorder_service.py::TestScreenRecorderService::test_segment_paths_sorted PASSED [ 88%] tests/test_screen_recorder_service.py::TestScreenRecorderService::test_audio_validation_summary_error PASSED [ 88%] tests/test_sface_embedder.py::TestGrayscaleHelpers::test_is_grayscale_detects_bw PASSED [ 88%] tests/test_sface_embedder.py::TestGrayscaleHelpers::test_is_grayscale_rejects_color PASSED [ 88%] tests/test_sface_embedder.py::TestGrayscaleHelpers::test_enhance_grayscale_returns_bgr PASSED [ 88%] tests/test_sface_embedder.py::TestSFaceEmbedder::test_missing_model_raises PASSED [ 89%] tests/test_sface_embedder.py::TestSFaceEmbedder::test_embed_returns_unit_vector PASSED [ 89%] tests/test_sface_embedder.py::TestSFaceEmbedder::test_grayscale_input_enhanced PASSED [ 89%] tests/test_shortcut_context.py::test_context_derived_from_category PASSED [ 89%] tests/test_shortcut_context.py::test_same_key_allowed_across_pages PASSED [ 89%] tests/test_shortcut_context.py::test_conflict_within_same_page PASSED [ 89%] tests/test_shortcut_context.py::test_global_shortcut_conflicts_across_pages PASSED [ 89%] tests/test_shortcut_context.py::test_active_context_gates_firing PASSED [ 89%] tests/test_shortcut_context.py::test_no_scoping_fallback_is_context_agnostic PASSED [ 89%] tests/test_shortcut_service.py::test_normalize_key_ignores_modifier_only PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_service_all_shortcuts_and_get PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_def_defaults_and_context PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_def_global_context PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_service_register_and_bind_fires_handler PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_service_unregister_removes_qshortcut PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_service_set_key_updates_binding PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_service_enabled_and_capturing_gate PASSED [ 89%] tests/test_shortcut_service.py::test_shortcut_service_find_conflict_and_save PASSED [ 89%] tests/test_shortcut_service.py::test_get_shortcut_service_returns_singleton PASSED [ 89%] tests/test_shortcut_service.py::test_is_input_widget[lineedit-True] PASSED [ 89%] tests/test_shortcut_service.py::test_is_input_widget[plain-False] PASSED [ 90%] tests/test_sidebar_panel.py::test_badge_is_drawn_on_a_copy PASSED [ 90%] tests/test_sidebar_panel.py::test_unknown_flag_follows_auto_named PASSED [ 90%] tests/test_sidebar_panel.py::test_naming_a_person_rebuilds_the_thumb PASSED [ 90%] tests/test_sidebar_panel.py::test_only_unknown_filter PASSED [ 90%] tests/test_sidebar_panel.py::test_filter_state_is_persisted PASSED [ 90%] tests/test_sparkle_bridge.py::TestBundlePath::test_returns_none_when_not_frozen PASSED [ 90%] tests/test_sparkle_bridge.py::TestBundlePath::test_returns_app_root_when_frozen PASSED [ 90%] tests/test_sparkle_bridge.py::TestIsSparkleAvailable::test_false_off_macos PASSED [ 90%] tests/test_sparkle_bridge.py::TestIsSparkleAvailable::test_false_on_macos_when_not_frozen PASSED [ 90%] tests/test_sparkle_bridge.py::TestIsSparkleAvailable::test_false_when_helper_missing PASSED [ 90%] tests/test_sparkle_bridge.py::TestIsSparkleAvailable::test_true_when_helper_present PASSED [ 90%] tests/test_sparkle_bridge.py::TestStartBackgroundUpdateCheck::test_false_off_macos PASSED [ 90%] tests/test_sparkle_bridge.py::TestStartBackgroundUpdateCheck::test_false_when_not_in_bundle PASSED [ 90%] tests/test_sparkle_bridge.py::TestStartBackgroundUpdateCheck::test_launches_helper_when_available PASSED [ 90%] tests/test_sparkle_bridge.py::TestCheckForUpdates::test_false_off_macos PASSED [ 90%] tests/test_sparkle_bridge.py::TestCheckForUpdates::test_launches_manual_check PASSED [ 90%] tests/test_subtitle_service.py::test_subtitle_path_for_video_uses_same_basename PASSED [ 90%] tests/test_subtitle_service.py::test_exact_recording_srt_is_selected PASSED [ 90%] tests/test_subtitle_service.py::test_language_suffix_is_not_selected PASSED [ 90%] tests/test_subtitle_service.py::test_unrelated_subtitle_is_not_selected PASSED [ 91%] tests/test_subtitle_service.py::test_dotted_language_suffix_is_not_selected PASSED [ 91%] tests/test_subtitle_service.py::test_case_difference_still_matches PASSED [ 91%] tests/test_subtitle_service.py::test_only_exact_match_is_selected_among_multiple_srt_files PASSED [ 91%] tests/test_subtitle_service.py::test_missing_matching_subtitle_returns_none PASSED [ 91%] tests/test_suggestion_service.py::TestCentroid::test_centroid_is_unit_length PASSED [ 91%] tests/test_suggestion_service.py::TestCentroid::test_centroid_of_identical_vectors PASSED [ 91%] tests/test_suggestion_service.py::TestCentroid::test_centroid_normalises_inputs PASSED [ 91%] tests/test_suggestion_service.py::TestCentroid::test_centroid_empty_raises PASSED [ 91%] tests/test_suggestion_service.py::TestGenerateSuggestions::test_unknown_matched_to_correct_person PASSED [ 91%] tests/test_suggestion_service.py::TestGenerateSuggestions::test_no_suggestion_below_threshold PASSED [ 91%] tests/test_suggestion_service.py::TestGenerateSuggestions::test_config_threshold_controls_output PASSED [ 91%] tests/test_suggestion_service.py::TestGenerateSuggestions::test_multiple_matches_are_ranked PASSED [ 91%] tests/test_suggestion_service.py::TestGenerateSuggestions::test_no_named_persons_yields_no_suggestions PASSED [ 91%] tests/test_suggestion_service.py::TestFaceFiltering::test_excluded_and_unembedded_faces_ignored PASSED [ 91%] tests/test_suggestion_service.py::TestFaceFiltering::test_person_with_only_excluded_faces_is_not_a_candidate PASSED [ 91%] tests/test_suggestion_service.py::TestApproveReject::test_rejected_pair_is_not_resuggested PASSED [ 91%] tests/test_suggestion_service.py::TestApproveReject::test_reject_records_different_correction PASSED [ 91%] tests/test_suggestion_service.py::TestApproveReject::test_approve_merges_unknown_into_named PASSED [ 91%] tests/test_task_manager.py::test_priority_ordering PASSED [ 91%] tests/test_task_manager.py::test_high_priority_preempts_running_low PASSED [ 92%] tests/test_task_manager.py::test_preempted_task_auto_resumes PASSED [ 92%] tests/test_task_manager.py::test_non_pausable_low_is_not_preempted PASSED [ 92%] tests/test_task_manager.py::test_manual_pause_frees_slot_and_resume_waits PASSED [ 92%] tests/test_task_manager.py::test_resume_reclaims_slot_from_lower_priority PASSED [ 92%] tests/test_task_manager.py::test_set_priority_reorders_queue PASSED [ 92%] tests/test_task_manager.py::test_counts_and_active_count PASSED [ 92%] tests/test_task_manager.py::test_transient_task_not_kept_in_history PASSED [ 92%] tests/test_task_manager.py::test_cancel_queued_task PASSED [ 92%] tests/test_task_manager.py::test_auto_cleanup_finished_tasks PASSED [ 92%] tests/test_tflite_embedder.py::TestLoadTfliteBackendSelection::test_ai_edge_litert_preferred PASSED [ 92%] tests/test_tflite_embedder.py::TestLoadTfliteBackendSelection::test_tflite_runtime_fallback PASSED [ 92%] tests/test_tflite_embedder.py::TestLoadTfliteBackendSelection::test_tensorflow_fallback PASSED [ 92%] tests/test_tflite_embedder.py::TestLoadTfliteBackendSelection::test_no_backend_raises_import_error PASSED [ 92%] tests/test_tflite_embedder.py::TestLoadTfliteBackendSelection::test_error_message_includes_attempted_backends PASSED [ 92%] tests/test_tflite_embedder.py::TestEmbedBatch::test_batch_matches_serial SKIPPED [ 92%] tests/test_tflite_embedder.py::TestEmbedBatch::test_empty_batch_returns_empty PASSED [ 92%] tests/test_tflite_embedder.py::TestHOGFallback::test_embed_returns_normalized_vector PASSED [ 92%] tests/test_theme.py::TestTheme::test_apply_palette_sets_colors PASSED [ 92%] tests/test_theme.py::TestTheme::test_apply_theme_sets_stylesheet PASSED [ 92%] tests/test_thumbnail_cache.py::test_missing_path_returns_none PASSED [ 93%] tests/test_thumbnail_cache.py::test_loads_and_caches PASSED [ 93%] tests/test_thumbnail_cache.py::test_different_size_is_separate_entry PASSED [ 93%] tests/test_thumbnail_cache.py::test_mtime_change_invalidates PASSED [ 93%] tests/test_thumbnail_worker.py::TestThumbnailRunnable::test_emits_ready_with_scaled_image PASSED [ 93%] tests/test_thumbnail_worker.py::TestThumbnailRunnable::test_failed_when_image_missing PASSED [ 93%] tests/test_thumbnail_worker.py::TestThumbnailRunnable::test_failed_on_zero_dimensions PASSED [ 93%] tests/test_thumbnail_worker.py::TestThumbnailRunnable::test_failed_on_exception PASSED [ 93%] tests/test_truncation.py::test_clamp_inside_image_records_no_truncation PASSED [ 93%] tests/test_truncation.py::test_clamp_records_half_box_outside_image PASSED [ 93%] tests/test_truncation.py::test_clamp_keeps_the_larger_recorded_fraction PASSED [ 93%] tests/test_truncation.py::test_whole_face_is_kept PASSED [ 93%] tests/test_truncation.py::test_slightly_cut_face_is_kept PASSED [ 93%] tests/test_truncation.py::test_half_face_box_is_rejected PASSED [ 93%] tests/test_truncation.py::test_eye_outside_image_is_rejected PASSED [ 93%] tests/test_truncation.py::test_landmark_less_detector_judged_by_box_only PASSED [ 93%] tests/test_truncation.py::test_detection_service_drops_half_face PASSED [ 93%] tests/test_truncation.py::test_detection_service_filter_can_be_disabled PASSED [ 93%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[ai_visualization_window] PASSED [ 93%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[auto_assignments_tab] PASSED [ 93%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[auto_merge_review_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[collage_node_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[export_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[face_diagnostics_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[face_image_export_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[family_code_scheme_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[gdrive_settings_tab] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[group_manager_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[identity_repair_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[ignored_faces_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[image_library_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[manual_face_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[merge_decision_graph_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[merge_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[move_faces_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[object_info_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[object_merge_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[object_picker_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[overlapping_unknown_faces_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[person_info_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[place_edit_dialog] PASSED [ 94%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[place_merge_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[rename_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[rerecognition_history_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[rerecognition_review_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[scan_modes_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[settings_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[shortcuts_settings_tab] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[suggestion_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[suggestion_viewer] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[task_manager_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[tpu_status_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[update_dialog] PASSED [ 95%] tests/test_ui_dialogs_smoke.py::test_overlapping_match_dataclass_importable PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[collage_panel] PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[family_search_panel] PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[groups_panel] PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[locations_panel] PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[log_panel] PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[objects_panel] PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[preview_panel] PASSED [ 95%] tests/test_ui_panels_smoke.py::test_panel_module_smoke[sidebar_panel] PASSED [ 96%] tests/test_ui_panels_smoke.py::test_preview_panel_accepts_config PASSED [ 96%] tests/test_ui_panels_smoke.py::test_objects_panel_imports_cleanly PASSED [ 96%] tests/test_ui_panels_smoke.py::test_collage_panel_has_empty_state PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[address_autocomplete_edit] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[audio_level_meter] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[collapsible_section] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[face_timeline_view] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[flow_layout] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[group_chip_select] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[object_gallery_widget] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[person_search_select] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[place_gallery_widget] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[place_map_picker_widget] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[place_map_widget] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[place_search_select] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[recording_controls] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[selectable_face_grid] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_widget_module_smoke[universal_search_bar] PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_place_search_select_accepts_places PASSED [ 96%] tests/test_ui_widgets_smoke.py::test_universal_search_bar_empty_tokens PASSED [ 97%] tests/test_unknown_merge_service.py::test_scatter_moves_siblings_keeps_selected_manual PASSED [ 97%] tests/test_unknown_merge_service.py::test_pending_records_source_and_cleans_unknown PASSED [ 97%] tests/test_unknown_merge_service.py::test_named_source_does_not_scatter PASSED [ 97%] tests/test_unknown_merge_service.py::test_protected_catchall_does_not_scatter PASSED [ 97%] tests/test_unknown_merge_service.py::test_auto_confirm_high_similarity_clears_only_matching PASSED [ 97%] tests/test_unknown_merge_service.py::test_auto_confirm_needs_reference_face PASSED [ 97%] tests/test_unknown_merge_service.py::test_auto_confirm_disabled_keeps_pending PASSED [ 97%] tests/test_unknown_merge_service.py::test_confirm_auto_merge_clears_markers PASSED [ 97%] tests/test_unknown_merge_service.py::test_move_auto_merge_clears_markers PASSED [ 97%] tests/test_unknown_merge_service.py::test_bulk_reassign_clears_pending PASSED [ 97%] tests/test_unknown_merge_service.py::test_list_and_count_pending PASSED [ 97%] tests/test_unknown_merge_service.py::test_pending_face_gets_decision_graph PASSED [ 97%] tests/test_unknown_merge_service.py::test_list_pending_exposes_decision_and_bbox PASSED [ 97%] tests/test_unknown_merge_service.py::test_decision_recorded_when_auto_merge_disabled PASSED [ 97%] tests/test_unknown_merge_service.py::test_old_row_without_decision_is_tolerated PASSED [ 97%] tests/test_unknown_merge_service.py::test_delete_face_removes_row PASSED [ 97%] tests/test_unknown_merge_service.py::test_reject_to_unknown_recreates_source_cluster PASSED [ 97%] tests/test_unknown_merge_service.py::test_reject_to_unknown_reuses_surviving_source PASSED [ 97%] tests/test_unknown_merge_service.py::test_reject_to_unknown_falls_back_to_new_name PASSED [ 97%] tests/test_unknown_person_reset_service.py::test_reset_deletes_auto_named_persons_and_unassigns_their_faces PASSED [ 98%] tests/test_unknown_person_reset_service.py::test_reset_is_a_noop_without_auto_named_persons PASSED [ 98%] tests/test_unknown_person_reset_service.py::test_delete_face_data_hard_deletes_the_unknown_faces PASSED [ 98%] tests/test_unknown_person_reset_service.py::test_delete_face_data_does_not_require_unassigning PASSED [ 98%] tests/test_unknown_person_reset_service.py::test_delete_face_data_unlinks_crop_files PASSED [ 98%] tests/test_unknown_person_reset_service.py::test_delete_face_data_off_keeps_the_face_rows PASSED [ 98%] tests/test_unknown_person_reset_service.py::test_reset_with_every_option_off_changes_nothing PASSED [ 98%] tests/test_update_service.py::test_parse_version[1.2.3-expected0] PASSED [ 98%] tests/test_update_service.py::test_parse_version[v2.0.1-expected1] PASSED [ 98%] tests/test_update_service.py::test_parse_version[bad-expected2] PASSED [ 98%] tests/test_update_service.py::test_parse_version[1.2-expected3] PASSED [ 98%] tests/test_update_service.py::test_is_newer[1.2.3-1.2.2-True] PASSED [ 98%] tests/test_update_service.py::test_is_newer[1.2.3-1.2.3-False] PASSED [ 98%] tests/test_update_service.py::test_is_newer[1.2.2-1.2.3-False] PASSED [ 98%] tests/test_update_service.py::test_is_newer[v2.0.0-1.9.9-True] PASSED [ 98%] tests/test_update_service.py::test_pick_asset_macos_dmg_preferred PASSED [ 98%] tests/test_update_service.py::test_pick_asset_windows_exe_preferred PASSED [ 98%] tests/test_update_service.py::test_pick_asset_linux_arch_match PASSED [ 98%] tests/test_update_service.py::test_pick_asset_no_match_returns_none PASSED [ 98%] tests/test_update_service.py::test_fetch_latest_release_success PASSED [ 98%] tests/test_update_service.py::test_fetch_latest_release_network_error_returns_none PASSED [ 99%] tests/test_update_service.py::test_fetch_latest_release_no_matching_asset_returns_none PASSED [ 99%] tests/test_windows_display_probe.py::test_merge_joins_on_device_name PASSED [ 99%] tests/test_windows_display_probe.py::test_merge_falls_back_to_matching_rectangle PASSED [ 99%] tests/test_windows_display_probe.py::test_merge_falls_back_to_ordinal_when_counts_match PASSED [ 99%] tests/test_windows_display_probe.py::test_merge_leaves_unmatched_monitor_unknown PASSED [ 99%] tests/test_windows_display_probe.py::test_override_wins_over_automatic_match PASSED [ 99%] tests/test_windows_display_probe.py::test_malformed_override_is_ignored PASSED [ 99%] tests/test_windows_display_probe.py::test_merge_does_not_mutate_its_input PASSED [ 99%] tests/test_windows_display_probe.py::test_virtual_desktop_rect_spans_every_monitor PASSED [ 99%] tests/test_windows_display_probe.py::test_virtual_desktop_rect_without_monitors PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetParsing::test_missing_model_raises PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetParsing::test_backend_name PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetParsing::test_none_result_returns_empty PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetParsing::test_landmarks_parsed_in_arcface_order PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetParsing::test_input_size_and_threshold_pushed_to_net PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetParsing::test_small_faces_filtered PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetGeometry::test_geometry_keeps_valid_landmarks PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetGeometry::test_geometry_rejects_scrambled_landmarks PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetGeometry::test_geometry_disabled_keeps_scrambled PASSED [ 99%] tests/test_yunet_detector.py::TestYuNetRealModel::test_loads_and_runs SKIPPED [100%] ================================== FAILURES =================================== ___________ test_export_astro_writes_map_and_slideshow_parity_pages ___________ ..\..\_temp\uv-python\cpython-3.11-windows-x86_64-none\Lib\zoneinfo\_common.py:12: in load_tzdata return resources.files(package_name).joinpath(resource_name).open("rb") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ..\..\_temp\uv-python\cpython-3.11-windows-x86_64-none\Lib\importlib\resources\_common.py:22: in files return from_package(get_package(package)) ^^^^^^^^^^^^^^^^^^^^ ..\..\_temp\uv-python\cpython-3.11-windows-x86_64-none\Lib\importlib\resources\_common.py:53: in get_package resolved = resolve(package) ^^^^^^^^^^^^^^^^ ..\..\_temp\uv-python\cpython-3.11-windows-x86_64-none\Lib\importlib\resources\_common.py:44: in resolve return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ..\..\_temp\uv-python\cpython-3.11-windows-x86_64-none\Lib\importlib\__init__.py:126: in import_module return _bootstrap._gcd_import(name[level:], package, level) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E ModuleNotFoundError: No module named 'tzdata' During handling of the above exception, another exception occurred: tests\test_export_service.py:358: in test_export_astro_writes_map_and_slideshow_parity_pages _data_dir, project = _run_astro(session, tmp_path) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tests\test_export_service.py:40: in _run_astro data_dir = ExportService(session).export_astro( app\services\export_service.py:565: in export_astro _write_map_export_files(public_dir, map_records, flags=standalone_flags, app_version=APP_VERSION) app\services\export_service.py:1358: in _write_map_export_files footer_html = _build_standalone_footer_html(app_version) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ app\services\export_service.py:1318: in _build_standalone_footer_html now_budapest = datetime.now(zoneinfo.ZoneInfo("Europe/Budapest")) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ..\..\_temp\uv-python\cpython-3.11-windows-x86_64-none\Lib\zoneinfo\_common.py:24: in load_tzdata raise ZoneInfoNotFoundError(f"No time zone found with key {key}") E zoneinfo._common.ZoneInfoNotFoundError: 'No time zone found with key Europe/Budapest' ----------------------------- Captured log setup ------------------------------ WARNING app.perf:perf.py:92 SLOW sql.total: 68 ms WARNING app.perf:perf.py:171 SLOW SQL 68 ms: CREATE TABLE places ( id INTEGER NOT NULL, name VARCHAR(255) NOT NULL, latitude FLOAT, longitude FLOAT, thumbn ________________ test_import_remaps_legacy_absolute_only_image ________________ tests\test_project_package_service.py:533: in test_import_remaps_legacy_absolute_only_image assert resolved is not None and resolved.exists() E AssertionError: assert (WindowsPath('C:/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/restored/images/_external/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/external/legacy.jpg') is not None and False) E + where False = exists() E + where exists = WindowsPath('C:/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/restored/images/_external/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/external/legacy.jpg').exists ------------------------------ Captured log call ------------------------------ WARNING app.perf:perf.py:92 SLOW sql.total: 52 ms WARNING app.perf:perf.py:171 SLOW SQL 52 ms: CREATE TABLE places ( id INTEGER NOT NULL, name VARCHAR(255) NOT NULL, latitude FLOAT, longitude FLOAT, thumbn WARNING app.perf:perf.py:92 SLOW sql.total: 70 ms WARNING app.perf:perf.py:171 SLOW SQL 70 ms: DROP INDEX IF EXISTS ix_persons_family_code ============================== warnings summary =============================== tests/test_accel.py::TestOnnxProviders::test_cuda_keeps_priority_on_windows C:\GitRunner\actions-runner\_work\_temp\face-local-test-venv\Lib\site-packages\_pytest\unraisableexception.py:63: PytestUnraisableExceptionWarning: Exception ignored in: Traceback (most recent call last): File "C:\GitRunner\actions-runner\_work\_temp\face-local-test-venv\Lib\site-packages\ai_edge_litert\interpreter.py", line 121, in __del__ if self._library is not None: ^^^^^^^^^^^^^ AttributeError: 'Delegate' object has no attribute '_library' Enable tracemalloc to get traceback where the object was allocated. See https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings for more info. warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) tests/test_debug_info.py::TestComputeLayerActivations::test_returns_relu_hidden_layers tests/test_debug_info.py::TestComputeDecisionPath::test_returns_none_without_activations C:\GitRunner\actions-runner\_work\_temp\face-local-test-venv\Lib\site-packages\sklearn\neural_network\_multilayer_perceptron.py:785: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (1) reached and the optimization hasn't converged yet. warnings.warn( tests/test_deep_accuracy.py: 20 warnings tests/test_deep_classifier.py: 51 warnings tests/test_deep_recognition_service.py: 2 warnings tests/test_match_scoring_deep.py: 9 warnings C:\GitRunner\actions-runner\_work\_temp\face-local-test-venv\Lib\site-packages\sklearn\neural_network\_multilayer_perceptron.py:785: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (300) reached and the optimization hasn't converged yet. warnings.warn( tests/test_family_code_schemes.py::test_person_dialog_valid_code_is_canonicalised tests/test_family_code_schemes.py::test_person_dialog_invalid_code_saved_with_inline_marker tests/test_family_code_schemes.py::test_person_dialog_marker_toggles_live tests/test_family_code_schemes.py::test_person_dialog_flags_preexisting_invalid_code tests/test_family_code_schemes.py::test_person_dialog_help_follows_active_scheme tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[person_info_dialog] C:\GitRunner\actions-runner\_work\local_ai_face_recognizer\local_ai_face_recognizer\app\ui\dialogs\person_info_dialog.py:74: SAWarning: Column-expression-level unary distinct() should not be used outside of an aggregate function. For general 'SELECT DISTINCT' support use select().distinct(). rows = session.execute( tests/test_ui_dialogs_smoke.py::test_dialog_module_smoke[collage_node_dialog] C:\GitRunner\actions-runner\_work\local_ai_face_recognizer\local_ai_face_recognizer\app\ui\dialogs\collage_node_dialog.py:111: SAWarning: Column-expression-level unary distinct() should not be used outside of an aggregate function. For general 'SELECT DISTINCT' support use select().distinct(). rows = session.execute( -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html - generated xml file: C:\GitRunner\actions-runner\_work\local_ai_face_recognizer\local_ai_face_recognizer\test-reports\windows-x64\latest.junit.xml - =============================== tests coverage ================================ ______________ coverage: platform win32, python 3.11.18-final-0 _______________ Name Stmts Miss Cover Missing ---------------------------------------------------------------------------------- app\accel.py 73 7 90% 66-71, 76, 165 app\clustering\clusterer.py 42 2 95% 108, 110 app\config.py 397 27 93% 1059, 1428, 1609-1611, 1634, 1647-1658, 1663, 1670, 1677, 1684, 1690-1701 app\crash_diagnostics.py 130 130 0% 27-252 app\db\database.py 241 38 84% 216-217, 241-302, 369-372, 819, 865, 871, 946, 1004, 1020-1037, 1066, 1099-1101, 1111 app\db\models.py 603 31 95% 120, 195, 372, 401, 430, 485, 533, 730-732, 735, 741, 839, 847, 896, 966, 1006, 1091, 1149, 1198, 1247, 1325, 1417, 1486, 1531, 1563, 1705, 1767, 1821, 1856, 1928 app\deep\classifier.py 776 57 93% 185, 189, 285, 343, 346, 406, 472, 540, 575-576, 672, 705, 711, 718, 721, 734, 746, 799, 801, 888-889, 964, 969, 1031-1033, 1035, 1041-1042, 1077, 1094-1095, 1112, 1144-1166, 1201-1203, 1207, 1296, 1330, 1403 app\deep\dataset.py 237 21 91% 89-90, 230, 234, 247, 249, 252, 254, 261, 263, 265, 329, 401, 441, 444, 446, 471, 486-487, 497, 503 app\deep\debug_info.py 87 4 95% 144-145, 162, 218 app\deep\evaluation.py 144 20 86% 59, 112-116, 122, 129, 157, 166, 173, 177-179, 184, 191, 199-202, 205, 211 app\deep\non_face.py 56 1 98% 96 app\detectors\base.py 56 7 88% 97-103 app\detectors\composite_detector.py 69 4 94% 90-91, 140, 147 app\detectors\coral_detector.py 101 51 50% 27, 42, 45, 63-69, 71, 79-89, 128-141, 145, 153-192 app\detectors\cpu_detector.py 112 69 38% 71-90, 105-113, 124, 148-152, 159-176, 181-184, 190-221, 225-245 app\detectors\factory.py 117 78 33% 26, 82-113, 125-178, 194-201, 214-227, 234-245, 268-274 app\detectors\insightface_detector.py 87 48 45% 57, 76, 82-84, 89-105, 145-179, 184-191, 195-206 app\detectors\landmark_geometry.py 86 5 94% 121, 133, 149, 163, 176 app\detectors\truncation.py 18 1 94% 47 app\detectors\yunet_detector.py 77 7 91% 105-109, 119, 139, 175 app\diagnostics.py 149 47 68% 59-62, 67-71, 78-81, 95-96, 114, 124-126, 132, 140-147, 193, 199-201, 222-235, 244-245, 251-254 app\embeddings\alignment.py 71 7 90% 104, 113, 149, 179-180, 188, 198 app\embeddings\base.py 14 1 93% 43 app\embeddings\coreml_embedder.py 79 54 32% 56-75, 85, 88-91, 99, 106-118, 122-133, 137-138, 155-176 app\embeddings\insightface_embedder.py 68 68 0% 18-151 app\embeddings\tflite_embedder.py 154 50 68% 95-99, 106-124, 180-185, 210, 215-217, 228-258, 262-266, 294-298, 332, 340 app\embeddings\tta.py 41 5 88% 30, 48, 52, 55, 79 app\gdrive\cache.py 115 51 56% 62-75, 83-98, 115-122, 154, 173-183, 193, 197-207 app\gdrive\credential_store.py 242 82 66% 65, 69, 171-179, 191-196, 205-207, 212-215, 227-246, 249-250, 253-254, 257-262, 273-274, 290-301, 311-312, 327-329, 337-338, 345-346, 361-362, 383-384, 405-409, 415-416 app\gdrive\dotenv_loader.py 38 2 95% 89-90 app\gdrive\drive_client.py 185 43 77% 207-210, 306, 316-318, 321-333, 337-356, 372-394 app\gdrive\drive_scan_service.py 106 3 97% 113-114, 251 app\gdrive\oauth_flow.py 61 6 90% 102, 130-131, 141, 158-159 app\gdrive\project_session.py 331 49 85% 112, 192, 239, 251-252, 299-303, 309-310, 331-332, 336, 362, 367-381, 416-420, 474, 488-489, 496, 499, 503-509, 522, 528-531, 551-553, 563-565, 580-581 app\jobs\cancellation.py 30 2 93% 63, 74 app\logging_setup.py 36 2 94% 86-87 app\main.py 87 69 21% 59-189, 193 app\paths.py 86 40 53% 33-36, 52-57, 62-67, 72-74, 89-91, 107-108, 121, 127-130, 155-167, 185, 189-190 app\perf.py 120 10 92% 138-148, 167 app\services\_image_dup_geometry.py 47 3 94% 65, 69, 100 app\services\ai_face_detection_service.py 139 13 91% 107, 156-161, 163, 242-243, 247-249, 291, 332 app\services\clustering_service.py 229 48 79% 106-107, 133-134, 148, 214, 220, 227-233, 238, 339-346, 356-357, 390-391, 437, 464-475, 488-495 app\services\collage_parser.py 179 29 84% 104-109, 124-125, 139-140, 168-169, 218, 236-239, 243-245, 252, 265-274 app\services\collage_service.py 218 10 95% 152, 260, 277, 350-351, 481, 503-505, 536 app\services\database_package_service.py 329 45 86% 204, 248-250, 256-257, 366-367, 370-371, 383-385, 392, 400-402, 439, 460, 569-570, 582, 598-604, 619, 626, 629, 651, 661, 674-675, 685-688, 724, 738, 744, 747, 749, 770-773 app\services\deep_recognition_service.py 869 145 83% 214-215, 217, 288-289, 304, 321, 324, 327, 334-336, 360-367, 369, 381-388, 396-422, 484-485, 488-497, 513, 524-525, 545, 581-582, 616, 639-640, 777, 781, 783, 785, 863, 867-871, 909, 912, 918, 948, 994-1017, 1021-1045, 1104, 1191-1192, 1198, 1200-1202, 1237, 1243-1248, 1256, 1300, 1340-1397, 1403, 1468, 1471, 1484, 1499, 1504, 1518, 1582, 1646, 1649, 1699-1700, 1736, 1747, 1766, 1772, 1812-1813, 1849, 1878, 1891-1893 app\services\deoldified_pairing_service.py 324 34 90% 150, 231, 268, 271, 282, 340, 370-379, 385, 422, 454, 480-484, 552, 555, 570-582, 701 app\services\dependency_installer.py 73 51 30% 68-73, 77, 87-112, 121-147, 152-160 app\services\detection_run_logger.py 99 77 22% 38-48, 55-61, 68-70, 79-80, 100-113, 121-157, 162, 169-173, 176-179, 182-183, 186-196, 200 app\services\detection_service.py 263 52 80% 83, 111, 172-173, 182-183, 191-194, 201, 213-214, 216-217, 222-223, 236, 246-262, 282-285, 288-290, 380, 395, 462-463, 476, 508, 515-519, 535, 574, 580-585, 614, 616, 642-643 app\services\docker_export_service.py 128 86 33% 35, 64-119, 126-135, 161-178, 185-192, 199-210, 213-214, 221-238, 241-246, 249 app\services\duplicate_unknown_face_finder.py 256 17 93% 54-57, 217, 219, 332, 345, 386, 392, 411, 415, 438, 489, 499, 528, 590 app\services\embedding_service.py 159 38 76% 75, 83, 127, 156, 158-170, 184, 187, 196-221, 262-263, 305, 323-328, 340, 399-401 app\services\export_service.py 805 262 67% 77-101, 120-136, 166-203, 252, 313, 318, 343, 355, 553-556, 608-626, 653-655, 675-677, 683-685, 741, 752, 756, 772, 801-858, 869-973, 989-990, 1010-1023, 1032-1047, 1051-1054, 1058-1062, 1065-1092, 1096-1103, 1128, 1148, 1191, 1236, 1280-1281, 1283, 1285, 1302, 1319-1320, 1359-1362, 1721-1731, 1736, 1791, 1798, 1810, 1823-1831, 1859, 1862, 1865, 1879, 1889-1890, 1924-1925, 1990-1992, 1995, 2004-2008, 2023-2024, 2035-2036, 2106, 2111-2121, 2126, 3409-3411 app\services\face_crop_service.py 89 14 84% 44, 48-49, 52-57, 78, 101, 107-108, 174-176, 193, 198 app\services\face_date_service.py 33 2 94% 32, 68 app\services\face_diagnostics_service.py 88 3 97% 151, 154, 181 app\services\face_geometry_cleanup_service.py 96 11 89% 116, 178, 184, 210-217 app\services\face_grouping_service.py 98 7 93% 51, 158-159, 167-169, 184 app\services\face_image_export_service.py 191 30 84% 138, 161, 187-188, 201-203, 208-211, 216, 338, 352-354, 361-363, 376, 395, 402-406, 411-414 app\services\face_metadata_export_service.py 177 27 85% 127, 141-145, 149-154, 242, 265-266, 322, 343-344, 354-356, 383-384, 419, 456-458 app\services\face_quality_service.py 69 1 99% 143 app\services\face_verification_service.py 110 18 84% 51, 118-121, 127-128, 132-133, 155-158, 161, 195, 218-220 app\services\family_code_interpreter.py 380 32 92% 225, 291, 311, 321, 347-349, 435, 446, 469, 472, 486, 489, 494, 528, 548-549, 573, 576, 580-581, 686-687, 699, 717, 726-727, 789-790, 800, 818, 831 app\services\family_code_schemes.py 227 20 91% 148, 159, 169, 209-212, 217, 233-236, 348-350, 355, 371, 388-390, 402, 410, 426, 438, 475 app\services\family_service.py 675 127 81% 160-161, 163, 173, 189, 343, 351, 353, 374, 422-431, 434-435, 438-439, 442-443, 446-447, 479, 503, 516, 531, 534-535, 541-546, 554-559, 570, 583, 599, 633, 660, 677, 690, 742, 767, 834-835, 840, 864, 882, 924-930, 934-947, 972, 1012, 1014-1015, 1048, 1087-1094, 1116, 1127-1133, 1238-1254, 1287, 1311, 1320, 1329, 1337, 1356, 1365-1369, 1398-1400, 1417, 1436, 1445-1446, 1450-1454, 1458-1462 app\services\family_tree_layout.py 182 2 99% 55, 238 app\services\family_tree_service.py 366 12 97% 86, 129, 295, 304, 350, 399, 433, 628-629, 640, 715, 727 app\services\filename_pattern.py 74 4 95% 102, 130, 173, 203 app\services\geocoding\factory.py 43 3 93% 53-55 app\services\geocoding\nominatim_provider.py 105 30 71% 65-67, 70-82, 93, 135, 151, 171, 178, 196, 202, 215-228, 239-240 app\services\geocoding_service.py 131 34 74% 58, 69, 93, 97, 103-109, 123, 132-134, 144-156, 168, 242-243, 265-267 app\services\gmail_check.py 36 36 0% 10-89 app\services\identity_repair_service.py 187 12 94% 132, 148, 187, 254, 256, 259, 262, 265, 267, 271, 319, 323 app\services\identity_service.py 331 16 95% 289-291, 372-375, 412-413, 488, 522, 571, 579, 583, 763, 824 app\services\ignored_face_service.py 153 9 94% 80, 130, 132, 152, 154, 267, 274, 326, 330 app\services\image_browser_service.py 103 10 90% 147-162, 167-174, 256 app\services\image_library_service.py 217 28 87% 80, 100-102, 116-118, 135, 269, 311, 365-369, 419-420, 444-447, 477-481, 490-491, 515-516 app\services\image_metadata_export_service.py 200 10 95% 137-138, 173-174, 219-220, 262, 274-275, 345 app\services\image_path_matcher.py 318 33 90% 233, 249, 274-275, 281, 296, 364, 369-371, 404-406, 463, 470, 527, 542, 544, 557-558, 566-570, 588-590, 596-598, 632-633, 687-688 app\services\intra_image_consistency_service.py 199 18 91% 83, 128-129, 161-165, 175, 215, 219, 241, 253, 294, 344, 351-354, 389, 415, 419 app\services\intra_image_duplicate_service.py 122 15 88% 111, 119-123, 131, 135, 161-162, 167, 172, 194-196, 219, 257, 261 app\services\local_server_export_service.py 150 101 33% 66-160, 190-212, 234-235, 261-290 app\services\match_scoring.py 139 35 75% 87, 121, 139, 152-154, 192, 197-199, 206-210, 233-236, 240-242, 261, 266, 269, 273-275, 298-308 app\services\multi_stage_face_validator.py 233 60 74% 84, 97-100, 206, 218-225, 235-239, 266, 294, 303, 346-348, 352-353, 358-362, 377-378, 381-394, 407-408, 411-434 app\services\non_face_service.py 170 10 94% 93, 95-96, 105, 117, 149-151, 318, 338 app\services\object_feature_service.py 301 19 94% 138, 142, 144-145, 147, 184, 243, 246, 255, 259, 294, 304, 381, 571, 579, 619-622 app\services\object_matching_service.py 318 31 90% 154, 156, 170, 195, 202, 207, 230, 233, 324-325, 354-355, 379-380, 396, 496-506, 562, 613, 632-633, 700 app\services\object_service.py 326 24 93% 121, 258-260, 279, 293, 369-372, 488, 515, 526, 538, 620, 629-632, 721, 747, 788, 810, 878 app\services\overlap_resolution_service.py 174 16 91% 92, 99, 101-105, 144, 181-189, 209, 225, 252, 323, 327 app\services\person_group_service.py 119 7 94% 112, 123, 126, 165, 178, 211, 259 app\services\place_service.py 429 29 93% 218-219, 243, 282, 341-342, 353, 357, 385, 473, 486, 488, 490, 492, 521, 533, 619, 682, 686, 688, 690, 692, 702, 730, 766, 775, 789, 848, 854 app\services\project_package_service.py 578 113 80% 102, 197, 200, 251-252, 285-287, 301, 354, 370-371, 381-384, 432-433, 438-442, 464-468, 542-543, 546-547, 568, 575, 577, 585-587, 642, 648-653, 659, 665, 743, 751, 756, 759, 778-781, 785-788, 820, 829-832, 835-838, 852-855, 881, 896, 930-935, 964, 974, 977, 980, 1005, 1009, 1042, 1045, 1047, 1060-1062, 1082-1084, 1089, 1106-1107, 1123-1126, 1147, 1150, 1154-1156, 1165-1171, 1178-1179, 1235-1238 app\services\recording_metadata.py 64 4 94% 92, 96, 143-144 app\services\recording_timeline_log.py 65 4 94% 141-142, 183-184 app\services\rerecognition_service.py 216 34 84% 188, 208, 228-235, 258, 299, 319, 357, 362, 410-433, 452-455, 459-464, 477, 509, 535 app\services\retroactive_verification_service.py 155 52 66% 115-119, 156-158, 165-167, 171-173, 179, 191-196, 202, 239-257, 273-277, 290-292, 294-298, 300-302, 312, 319-328 app\services\scan_service.py 150 17 89% 124, 145-146, 171-173, 221-223, 289, 291, 320-321, 337-338, 345-346 app\services\screen_recorder_service.py 1140 299 74% 194, 278, 348, 366, 371, 430, 445, 468, 486, 495, 501, 525, 636-637, 649-667, 705, 725, 763-764, 968, 1012-1015, 1122, 1141, 1214-1215, 1267-1268, 1270, 1295-1296, 1323, 1391, 1403-1408, 1453, 1489-1490, 1517-1559, 1617-1619, 1644-1673, 1679, 1691, 1714-1715, 1732-1745, 1776-1778, 1783-1786, 1854, 1858, 1863, 1879, 1911, 1950-1952, 1955-1959, 1962-1970, 1973-2007, 2011, 2014-2020, 2024, 2031, 2039, 2042-2046, 2053, 2064-2085, 2095-2103, 2107, 2113-2115, 2121-2151, 2154-2173, 2177-2196, 2199-2213, 2216-2236, 2239-2240, 2243-2245, 2249-2254, 2265-2268, 2278 app\services\server_settings_store.py 345 345 0% 20-702 app\services\shortcut_service.py 196 26 87% 119-121, 127, 170, 205, 233, 253, 268, 307-311, 345-353, 368, 371-372 app\services\subtitle_service.py 20 3 85% 27-28, 32 app\services\suggestion_service.py 148 8 95% 131, 199, 225-229, 296-298, 302 app\services\unknown_merge_service.py 222 13 94% 143, 146, 195, 282, 299, 442, 469, 506, 524, 563, 630-632 app\services\unknown_person_reset_service.py 66 4 94% 146-149 app\services\update_service.py 201 122 39% 63, 66, 111-115, 127-131, 136, 147-185, 190-209, 214-271, 276-326, 331-412, 417-437, 442-468 app\services\vector_scoring.py 155 41 74% 51-52, 76-77, 197, 223, 228-232, 254, 284, 286, 319, 323, 337, 362-404, 415 app\services\windows_display_probe.py 169 27 84% 128, 173-175, 197, 233, 258-280, 287-289, 300, 313-314, 319-326 app\tasks\manager.py 369 72 80% 78-79, 97-99, 103-105, 123, 145, 184-185, 188-203, 278-282, 288-292, 297, 306-309, 312-315, 334, 396, 398, 400, 420-422, 436-441, 470-471, 485-498, 620, 634, 665 app\tasks\resource_governor.py 102 10 90% 69, 79-80, 107-109, 118-119, 135-136 app\ui\dialogs\ai_visualization_window.py 739 526 29% 62-65, 68-107, 154-168, 182-203, 225-229, 239-346, 359-360, 363-386, 418-424, 442-451, 463-517, 523, 527-529, 533, 538-562, 573-668, 673-712, 716-747, 765-769, 772-789, 799-800, 808-828, 841-859, 1006-1012, 1018, 1021-1036, 1041-1045, 1048-1054, 1057-1068, 1071-1075, 1078-1088, 1095-1212 app\ui\dialogs\auto_assignments_tab.py 578 213 63% 80-92, 112-185, 239, 313-355, 599-601, 625-626, 651-655, 667-674, 681-693, 721-723, 729-744, 747-748, 761-768, 771, 776-813, 826-828, 844-847, 859-860, 864-870, 887-890, 893-909, 912-915, 918-958, 961-971, 977-980 app\ui\dialogs\auto_merge_review_dialog.py 204 121 41% 57-59, 62-64, 131-134, 145, 150-204, 207-211, 214, 218-220, 224-229, 236-238, 241-263, 266-281, 285-287, 290-300, 303-307, 310-312 app\ui\dialogs\collage_node_dialog.py 90 19 79% 115-117, 148-153, 160-175 app\ui\dialogs\docker_export_dialog.py 121 121 0% 2-201 app\ui\dialogs\export_dialog.py 592 240 59% 458, 461-475, 486-500, 511-544, 554-601, 617-657, 673-716, 731-735, 738-754, 757-773, 785-789, 792-827, 838-851, 858-860, 867-874, 877-889, 892-921, 931-958, 961-964, 967-970, 973-976, 979-982, 985-988, 991-994, 997-1000, 1003-1006, 1009-1012 app\ui\dialogs\face_diagnostics_dialog.py 63 13 79% 100-112 app\ui\dialogs\face_image_export_dialog.py 208 41 80% 174-176, 225-229, 232-237, 244-247, 264-267, 277, 302-305, 308-315, 336-345 app\ui\dialogs\family_code_scheme_dialog.py 572 139 76% 353-354, 361, 364-369, 376-387, 471, 483, 520-523, 530-532, 538-544, 548-558, 562-567, 571-572, 581, 607-608, 630, 657, 684-689, 695, 698, 701-703, 712-714, 718-730, 735, 737, 740-742, 747-764, 768-777, 781-794, 798-817, 827-828, 832-834 app\ui\dialogs\family_tree_editor_dialog.py 440 104 76% 71, 79, 112, 210-218, 225, 235-237, 242-243, 248-256, 300-331, 334, 337, 340, 438-439, 482, 505-507, 518, 521, 526, 530, 536-557, 579-584, 588-593, 597, 611, 614, 631-635, 657-664, 681-684 app\ui\dialogs\folder_list_dialog.py 57 57 0% 3-103 app\ui\dialogs\gdrive_settings_tab.py 309 139 55% 61-72, 82-84, 87-99, 257-261, 268-271, 292, 313, 316, 327-349, 352-362, 365-370, 373-376, 379-401, 408-441, 444-457, 460-462, 465-473, 480-488, 491-493, 500-501 app\ui\dialogs\group_manager_dialog.py 455 163 64% 55-78, 81-82, 85-86, 89, 101-124, 282-284, 321, 339-341, 343-344, 379-381, 404-408, 411-419, 444, 456, 458-464, 472, 484, 492, 497-499, 517, 529, 535-537, 541-601, 605, 608, 617-623, 630, 638-654, 657-686, 710, 715 app\ui\dialogs\identity_repair_dialog.py 93 38 59% 55, 58, 89, 106-133, 137-153, 156-160 app\ui\dialogs\ignored_faces_dialog.py 107 7 93% 111, 117, 137-139, 159, 167 app\ui\dialogs\image_library_dialog.py 106 58 45% 81-88, 92, 106-108, 111-122, 143-151, 154-178, 181-188, 191-193, 196-209 app\ui\dialogs\image_metadata_dialog.py 165 37 78% 119, 131-133, 148-152, 156-172, 203-204, 218-221, 231-235 app\ui\dialogs\image_path_repair_dialog.py 360 109 70% 213, 222-226, 229-230, 240-278, 281-285, 289, 294, 299-300, 313-319, 322-327, 393, 395-409, 420, 427, 438, 443, 456-475, 490, 498-528, 531-546, 554-557 app\ui\dialogs\local_server_export_dialog.py 190 190 0% 2-292 app\ui\dialogs\manual_face_dialog.py 239 165 31% 43-47, 50-54, 57-59, 62-71, 74, 77-78, 81-88, 105-120, 123-152, 155-164, 168-196, 199, 202-265, 268, 276-285, 343-349, 352-362, 365-367 app\ui\dialogs\merge_decision_graph_dialog.py 350 286 18% 70-75, 85-114, 145-169, 177-181, 191-202, 214-217, 241-271, 276-316, 321-329, 332-334, 337-343, 347-385, 388-400, 404-411, 420-429, 443-461, 466-494, 497-508, 513-559, 564-587, 590, 594-596, 600-603 app\ui\dialogs\merge_dialog.py 51 17 67% 60, 81, 85-86, 114-128, 131-135, 138-139, 143 app\ui\dialogs\object_info_dialog.py 58 13 78% 70, 74-75, 78-92 app\ui\dialogs\object_match_review_dialog.py 197 15 92% 104, 107, 202, 261, 268-271, 282, 297-303, 310 app\ui\dialogs\object_merge_dialog.py 34 5 85% 39, 54-55, 59-60 app\ui\dialogs\object_picker_dialog.py 115 39 66% 115-117, 119-124, 127-129, 132-134, 143-145, 148-168, 174, 178 app\ui\dialogs\overlapping_unknown_faces_dialog.py 282 156 45% 62-74, 87-104, 118, 125-156, 159-162, 165-168, 194-196, 203-215, 218-235, 250-274, 280-288, 293-294, 297-298, 329, 332, 376, 378, 414-435, 443-445, 450-453, 456-458, 461-462, 465-473 app\ui\dialogs\person_info_dialog.py 331 67 80% 57-106, 268-269, 311-313, 363-368, 466-467, 512-516, 523, 526, 529-530, 536, 539, 542, 545, 548, 551, 554, 557, 560, 563, 577-608 app\ui\dialogs\person_picker_dialog.py 107 40 63% 86-87, 99, 104-105, 123, 143-147, 150-157, 164-165, 172-175, 178-179, 183-186, 189-190, 193-199, 207, 211-213, 217 app\ui\dialogs\place_edit_dialog.py 319 152 52% 167-170, 217-256, 263-264, 271-277, 280-284, 287-291, 294-298, 301-305, 308, 311-320, 323-329, 338-350, 355, 369-371, 375, 378-383, 387, 399-407, 414-420, 426-431, 442, 447-448, 451-464, 467-473, 490-492 app\ui\dialogs\place_merge_dialog.py 57 8 86% 42, 45, 50, 73-77 app\ui\dialogs\rename_dialog.py 20 1 95% 46 app\ui\dialogs\rerecognition_history_dialog.py 106 51 52% 87-89, 92-106, 111-122, 125-144, 155-171 app\ui\dialogs\rerecognition_review_dialog.py 153 60 61% 144-171, 175-178, 183-188, 195-201, 206-223, 226-227, 230-242 app\ui\dialogs\scan_modes_dialog.py 190 13 93% 301, 365-374, 385-387 app\ui\dialogs\server_profile_dialogs.py 279 279 0% 14-422 app\ui\dialogs\settings_dialog.py 1321 664 50% 54-56, 65-66, 79-98, 107-108, 111-119, 132-134, 137-192, 205-207, 210-222, 239-240, 243-273, 295-296, 305, 348-352, 363-371, 575-669, 673-722, 726-831, 835-846, 850-862, 866-872, 875-878, 882-883, 887-889, 992-1014, 1022-1027, 1030-1042, 1047-1051, 1208, 1299, 1317-1324, 1459, 1496-1506, 1512-1531, 1535-1538, 1541-1553, 1556-1567, 1572-1612, 1621-1625, 1628-1629, 1644-1660, 1663-1666, 1669-1672, 1682-1685, 1691-1696, 1707, 1710-1728, 1738-1759, 1762-1814, 1821-1827, 1830-1836, 1843-1883, 1887-1931, 1941-2078, 2081, 2084 app\ui\dialogs\shortcuts_settings_tab.py 207 83 60% 97-98, 106-113, 116-122, 125-127, 131-135, 140, 224, 228-238, 241-245, 248-261, 264, 267-275, 280-309 app\ui\dialogs\suggestion_dialog.py 136 68 50% 49-52, 61-70, 73-97, 100-104, 107-114, 122-124, 128, 132, 162-163, 167-169, 181-182, 185-186, 189-196, 259, 262-263, 268 app\ui\dialogs\suggestion_viewer.py 334 235 30% 67-74, 99, 123-129, 142-157, 173-189, 192-194, 197-205, 208-225, 228-254, 275-308, 317-340, 360-396, 399-401, 404-416, 421-425, 453-458, 511-516, 531-532, 542, 545-547, 552-560, 578-616 app\ui\dialogs\task_manager_dialog.py 527 297 44% 124-125, 128-129, 132-138, 141-144, 147-190, 203-210, 244-254, 263-264, 296-298, 341-344, 353-362, 386-388, 391-393, 398, 400-401, 407-419, 425-459, 477-478, 483-544, 643-647, 654, 661-734, 737-796, 801-804, 820-821, 824-826, 829-832, 835, 839-846 app\ui\dialogs\tpu_status_dialog.py 176 55 69% 38, 123, 150-151, 161-162, 170, 174-175, 193-203, 228-229, 232-250, 285, 288, 293, 298-299, 301, 307, 309-311, 375-382, 385-390 app\ui\dialogs\update_dialog.py 131 61 53% 39-40, 43-53, 86-91, 153-170, 173-180, 183-206, 209-212, 215-226 app\ui\display_utils.py 64 11 83% 56-57, 71-77, 97, 103-105, 148 app\ui\gc_guard.py 21 21 0% 23-66 app\ui\helpers\face_image_export.py 28 28 0% 8-70 app\ui\helpers\label_placement.py 69 1 99% 120 app\ui\helpers\not_a_face.py 21 21 0% 8-41 app\ui\helpers\thumbnail_cache.py 51 5 90% 61, 70, 76, 81-85 app\ui\i18n\__init__.py 59 16 73% 21-25, 59-64, 70-71, 79, 82-83 app\ui\main_window\deep_model.py 67 52 22% 26-30, 33-52, 60-74, 78-85, 92-117, 122-147 app\ui\main_window\export.py 318 271 15% 35-63, 78-99, 106-110, 119-173, 177-253, 257-322, 333-352, 361-435, 440-533, 537-593, 610, 617, 620-623 app\ui\main_window\face_actions.py 255 213 16% 28-34, 38-52, 57-62, 67-72, 77-79, 84-93, 98-99, 104-126, 131-146, 151-156, 163-174, 178-197, 202-214, 221-309, 316-383, 391-411 app\ui\main_window\folders.py 55 42 24% 21-34, 40-65, 68-72, 76-80 app\ui\main_window\gdrive.py 228 190 17% 30-44, 53-55, 57-59, 65-89, 94, 99-102, 107-152, 157-186, 190-196, 201-213, 222-248, 253-272, 277-289, 298-348 app\ui\main_window\helpers.py 9 4 56% 11-13, 18-20 app\ui\main_window\maintenance.py 321 281 12% 39-69, 80-137, 146-240, 244-264, 274-282, 287, 291-292, 299-311, 315-349, 359-364, 370-398, 416-490, 497-571, 575-587, 595-629 app\ui\main_window\objects.py 197 145 26% 27-50, 55-77, 82-85, 94-101, 107-115, 136-138, 163-212, 234-235, 245-248, 257-261, 266-300, 304-307, 312-318, 326-330, 333-335, 340-349 app\ui\main_window\persons.py 325 257 21% 37-87, 91-153, 162-167, 171-186, 190-248, 256-260, 264-345, 348-363, 367-404, 413-461, 524, 529-540, 546-548, 580-581, 589-598 app\ui\main_window\pipeline.py 233 174 25% 38-49, 57-69, 77-91, 95-97, 101-103, 107-109, 113-123, 127-137, 141-151, 155-202, 212-231, 235-236, 241-243, 246-250, 255-257, 260-263, 270-314, 318-319, 327, 331-340, 344, 348-350, 354-356, 360-372, 376-377, 385-418, 425-427 app\ui\main_window\recording.py 295 225 24% 33-180, 194-209, 215-252, 255-262, 265-308, 311-318, 321, 324, 328-333, 337-341, 345-347, 351-352, 357-364, 368-379, 392, 395-396, 399-414, 427-451, 462-464, 469-470, 474-482, 489-493, 503, 506, 562-566, 574-602 app\ui\main_window\tasks.py 63 42 33% 24-25, 30, 52, 55-87, 101-129 app\ui\main_window\tray_updates.py 54 32 41% 30, 37, 41-49, 66-85, 89-95, 102-104 app\ui\main_window\ui_build.py 338 20 94% 160-163, 385-388, 532-545 app\ui\main_window\window.py 114 21 82% 145, 148-152, 164-180, 183-188, 191-196 app\ui\panels\cluster_panel.py 637 251 61% 68-77, 80-100, 103-107, 110-117, 125-127, 166, 178, 195, 207-210, 225-244, 249-264, 274-276, 279-280, 283-285, 288-289, 305-346, 353-363, 366-371, 374-407, 410-414, 417-424, 432-434, 471, 475, 484-485, 490-491, 494-498, 501-502, 666, 688, 709, 721-729, 733-734, 769-770, 817, 840, 908, 944, 948, 952-957, 960-972, 983, 994-1004, 1010-1013 app\ui\panels\collage_panel.py 282 182 35% 73-83, 86-88, 91-93, 96-98, 107-113, 132-136, 139-140, 143-144, 147-148, 235-262, 266-270, 278-282, 285-286, 289-371, 379-422, 430-460, 469-473 app\ui\panels\family_search_panel.py 224 75 67% 231-233, 242-247, 254-298, 316-317, 320-321, 324-326, 329-344, 347-359, 362-368, 376-378, 384-386 app\ui\panels\family_tree_panel.py 377 60 84% 287-296, 319-320, 324-325, 353-355, 387-388, 398-399, 413-416, 466-477, 486-490, 495, 501-536, 540-547 app\ui\panels\groups_panel.py 16 1 94% 33 app\ui\panels\image_browser\canvas.py 126 98 22% 28-45, 49-64, 69-77, 80-91, 94-97, 109, 142-163, 166-171, 174-185 app\ui\panels\image_browser\deoldified.py 280 39 86% 52, 120, 163-165, 171, 181, 199, 201, 239-240, 277, 279, 287-292, 307, 316-320, 325, 328, 336, 362, 365, 400-404, 418, 429-440, 443-448, 462-464 app\ui\panels\image_browser\face_editing.py 296 271 8% 35-61, 67-118, 124-170, 174-196, 204-208, 212-216, 220-224, 230-241, 246-328, 336-346, 350-379, 383-399, 402-424, 429-485 app\ui\panels\image_browser\helpers.py 263 178 32% 72-102, 111-126, 138-147, 170-193, 228-316, 323-327, 331-336, 344-350, 354-359, 398-399, 403, 407, 418-472 app\ui\panels\image_browser\image_loading.py 254 230 9% 39, 53-74, 78-82, 96-131, 138-322, 337-401, 406-412, 416-447 app\ui\panels\image_browser\inline_editor.py 197 167 15% 39-56, 62-71, 78, 81-148, 151-152, 168-190, 194-199, 212-218, 221-226, 229-242, 245-263, 266-307, 314-331, 334-347 app\ui\panels\image_browser\interaction.py 386 355 8% 32-73, 76-203, 211-213, 217-228, 234-244, 248-252, 256-283, 289-314, 321-334, 337-343, 356-385, 393-401, 405, 409-423, 431-469, 477-510, 514-519, 522-564, 567-573 app\ui\panels\image_browser\metadata.py 240 163 32% 33-42, 45-54, 57-66, 70-76, 79-86, 110-113, 116, 120, 127, 130-159, 169-172, 209-210, 214-243, 260-262, 264-266, 268-270, 284, 297-334, 338-347, 351-371 app\ui\panels\image_browser\navigation.py 92 77 16% 20-24, 27-32, 35-39, 42-47, 50-54, 57-62, 67, 70, 74-88, 91-136 app\ui\panels\image_browser\panel.py 778 125 84% 934, 943-944, 982-1011, 1029-1033, 1037-1042, 1046-1066, 1071-1085, 1091-1097, 1105-1113, 1120-1123, 1126-1148, 1151-1165, 1173-1175, 1179-1181, 1184-1186 app\ui\panels\image_browser\person_actions.py 121 99 18% 25-38, 41-55, 58-70, 73-88, 101-105, 108, 129-136, 139-167, 170-177 app\ui\panels\image_browser\rerecognition.py 112 92 18% 23-33, 36-40, 45-76, 87-88, 91-95, 98-99, 104-149, 152-165, 168-175, 178-196, 201-207, 211-214 app\ui\panels\image_browser\search.py 96 77 20% 38-43, 46-52, 55-145, 148-150, 155, 158-160 app\ui\panels\image_browser\tree.py 244 216 11% 57-103, 115-147, 151-154, 158-165, 168-170, 177-202, 205-207, 215-229, 232-266, 275-357, 372-379, 383-394, 398-420, 428-453 app\ui\panels\image_browser\widgets.py 758 449 41% 55-56, 59-62, 123-126, 129-132, 135-136, 212, 215-218, 221-223, 240-257, 268-270, 274, 278, 281-283, 286-288, 291-294, 301, 304-310, 313-322, 325-329, 336, 352-372, 457-463, 467-475, 483-484, 488-490, 494-495, 508-509, 515-520, 523-535, 576, 582, 608, 613, 632, 665, 668, 676-677, 689-734, 739-779, 782-818, 821-870, 904, 943-946, 951-991, 994-997, 1000-1011, 1016-1067, 1077-1140, 1143-1173 app\ui\panels\locations_panel.py 439 229 48% 76-81, 298-301, 324, 327-332, 337-357, 361-364, 369-380, 383-390, 393-439, 442-452, 455-469, 480-486, 489-528, 532-541, 544-546, 549-592, 600-616, 623-664, 677-696 app\ui\panels\log_panel.py 44 1 98% 89 app\ui\panels\objects_panel.py 531 271 49% 73, 81-83, 110-145, 149, 153, 157, 330-333, 338-350, 356, 360, 386-396, 403-410, 413-417, 420-425, 428-433, 448-508, 603-614, 619-623, 632-647, 650-668, 671-676, 679-699, 702-721, 724-737, 745-780, 784-795, 799-801 app\ui\panels\persons_panel.py 610 304 50% 93-95, 104-125, 128-130, 142-175, 178-179, 182, 440, 477, 479, 518-522, 525-533, 536-543, 550-557, 561-592, 609-619, 629-652, 655-660, 665-669, 672-673, 676-701, 704-706, 709-741, 744-777, 784-811, 825, 828-903, 908-926, 929-930 app\ui\panels\preview_panel.py 1060 770 27% 86-111, 120-210, 214-217, 233-248, 260-275, 278-280, 283-291, 294-311, 314-346, 419-424, 434-439, 442-443, 451-455, 458-466, 469-472, 475-479, 482-484, 495-503, 506-516, 519-525, 532-549, 552-561, 564-577, 580-582, 585-590, 597-603, 613-653, 657-790, 796-843, 1087-1091, 1094-1099, 1102-1106, 1109-1114, 1118, 1132-1211, 1215-1220, 1223-1250, 1254, 1259, 1266-1279, 1282-1286, 1293-1296, 1299, 1310-1392, 1396-1428, 1431-1437, 1440-1444, 1449-1457, 1461, 1464-1487, 1490-1498, 1501-1516, 1520-1533, 1536-1537, 1540-1545, 1548-1549, 1552-1553, 1556-1561, 1565-1577, 1581-1585, 1589-1600, 1605-1620 app\ui\panels\sidebar_panel.py 290 83 71% 85, 100-125, 163-179, 186-205, 208-234, 242-244, 278, 295-297, 308-309, 316-317, 320-322, 423, 433-434, 439-440, 525, 531-532, 535-536 app\ui\widgets\address_autocomplete_edit.py 100 55 45% 82, 85-88, 95-99, 102, 109-116, 119-136, 143-153, 156-157, 160-164, 167-168 app\ui\widgets\audio_level_meter.py 85 45 47% 58-62, 74-84, 89, 94-96, 99-132 app\ui\widgets\collapsible_section.py 31 3 90% 63-65 app\ui\widgets\eliding_label.py 28 3 89% 42, 48-49 app\ui\widgets\face_timeline_view.py 265 49 82% 168, 225, 233-238, 245, 250-254, 310-311, 314-315, 329-337, 340-345, 348-364, 367-373 app\ui\widgets\family_tree_view.py 540 55 90% 213, 220-222, 233, 244, 248-249, 252-253, 268, 278, 320, 361, 396, 400, 441, 454-455, 462, 507-510, 640, 682, 705, 710, 728, 731-734, 737-741, 749, 754-757, 760-768, 815, 817, 821, 828 app\ui\widgets\flow_layout.py 82 11 87% 44-46, 52, 62, 65-69, 130 app\ui\widgets\group_chip_select.py 265 149 44% 52-74, 103-110, 166-167, 170-172, 270-274, 281-283, 291-295, 308-310, 317-346, 357, 360-371, 380, 383-404, 407-413, 416-419, 422-429, 432-452, 460-465, 473-494 app\ui\widgets\object_gallery_widget.py 163 72 56% 42, 59-71, 81-87, 90-91, 96-99, 102-104, 107, 157-159, 165-177, 180-195, 202, 205-213, 216, 229, 232, 235 app\ui\widgets\person_search_select.py 264 62 77% 252-258, 294, 298, 388-389, 396-397, 413-416, 427-430, 433-438, 449-482, 494, 498-500 app\ui\widgets\place_gallery_widget.py 195 100 49% 39-45, 49-50, 56, 59-63, 66-68, 71, 75, 87-89, 98, 101-103, 110-138, 141-143, 200-202, 208-222, 225-230, 233-243, 250, 253-262, 265-267, 270-272, 275-277 app\ui\widgets\place_map_picker_widget.py 98 31 68% 28-29, 143, 147, 184-185, 192, 194, 222-228, 235-240, 245-249, 252-253, 256-257, 262 app\ui\widgets\place_map_widget.py 64 34 47% 16-17, 126-137, 175-192, 203-227, 230-233 app\ui\widgets\place_search_select.py 167 20 88% 49, 114-115, 120, 190-193, 206-216, 223 app\ui\widgets\recording_controls.py 70 9 87% 65, 99-101, 110-118 app\ui\widgets\selectable_face_grid.py 96 51 47% 73-92, 103-104, 111-113, 117-133, 136-145 app\ui\widgets\universal_search_bar.py 299 147 51% 108, 111, 117, 120, 138, 150-159, 174-209, 213, 361-362, 374, 383, 390-401, 404-417, 424-428, 431-440, 447-473, 476-481, 484-491, 499-528, 533-534 app\updater\sparkle_bridge.py 55 10 82% 62-63, 76-78, 92, 96, 106-108 app\utils\exif.py 213 86 60% 20, 32-34, 51, 57, 59-60, 102-107, 123-133, 138-158, 163-177, 192-193, 197-199, 220-222, 235-236, 240-242, 265-267, 287, 309-322 app\utils\fuzzy_date.py 143 5 97% 114, 134, 147, 151, 247 app\utils\image_metadata.py 336 87 74% 145-146, 155-165, 183-221, 238-239, 243-246, 263-265, 317-322, 329, 383-384, 405, 423-432, 486-505, 538-539, 557-558, 565, 576-577, 583, 589, 591, 599-600, 602, 605-606, 650-651, 654-660 app\utils\image_utils.py 123 31 75% 58-59, 70, 75-76, 80-81, 106-110, 244, 269-270, 273-275, 299-300, 302-305, 320-334 app\workers\deep_pipeline_worker.py 485 291 40% 142, 166-167, 174-179, 187-190, 193-195, 199-235, 243-244, 248-260, 269, 272, 275, 278, 296-302, 328-330, 342, 386, 452-491, 505-557, 572-609, 625, 646, 650-652, 731-759, 762-763, 774-797, 801-817, 820-838, 865-925, 932-945, 956-980, 983-996, 1008, 1014-1019, 1022-1075, 1079-1099, 1102-1116, 1119-1129, 1132-1142 app\workers\dependency_installer_worker.py 33 33 0% 17-74 app\workers\drive_image_worker.py 93 8 91% 70-72, 90-91, 183-185 app\workers\geocoding_worker.py 71 6 92% 84-86, 112-114 app\workers\manual_face_worker.py 28 28 0% 15-60 app\workers\match_score_worker.py 69 69 0% 19-153 app\workers\rerecognition_worker.py 145 45 69% 74, 136-137, 140-141, 195-275 ---------------------------------------------------------------------------------- TOTAL 49884 17894 64% 54 files skipped due to complete coverage. Coverage XML written to file test-reports\windows-x64\coverage.xml Coverage JSON written to file test-reports\windows-x64\coverage.json =========================== short test summary info =========================== SKIPPED [1] tests\test_coral_detector.py:102: Coral Edge TPU unavailable SKIPPED [1] tests\test_coral_detector.py:114: Coral Edge TPU unavailable SKIPPED [1] tests\test_face_metadata_export_service.py:309: POSIX permission semantics SKIPPED [1] tests\test_face_metadata_export_service.py:448: macOS Finder comment SKIPPED [1] tests\test_project_package_service.py:275: Foreign-path detection only applies off Windows SKIPPED [1] tests\test_tflite_embedder.py:197: real MobileFaceNet TFLite model not available SKIPPED [1] tests\test_yunet_detector.py:159: OpenCV 4.6.0 cannot run the YuNet ONNX smoke test on this runner: OpenCV(4.6.0) D:\a\opencv-python\opencv-python\opencv\modules\dnn\src\net_impl.cpp:280: error: (-204:Requested object was not found) Layer with requested id=-1 not found in function 'cv::dnn::dnn4_v20220524::Net::Impl::getLayerData' FAILED tests/test_export_service.py::test_export_astro_writes_map_and_slideshow_parity_pages - zoneinfo._common.ZoneInfoNotFoundError: 'No time zone found with key Europe/Budapest' FAILED tests/test_project_package_service.py::test_import_remaps_legacy_absolute_only_image - AssertionError: assert (WindowsPath('C:/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/restored/images/_external/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/external/legacy.jpg') is not None and False) + where False = exists() + where exists = WindowsPath('C:/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/restored/images/_external/Windows/ServiceProfiles/NetworkService/AppData/Local/Temp/pytest-of-DESKTOP-3Q8R7SJ$/pytest-111/test_import_remaps_legacy_abso0/external/legacy.jpg').exists ===== 2 failed, 2008 passed, 7 skipped, 92 warnings in 1165.95s (0:19:25) =====