@@ -640,3 +640,263 @@ pub unsafe extern "C" fn fff_grep_result_get_regex_fallback_error(
640640 }
641641 unsafe { ( * r) . regex_fallback_error }
642642}
643+
644+ // ── Tests ─────────────────────────────────────────────────────────────────────
645+
646+ #[ cfg( test) ]
647+ mod tests {
648+ use super :: * ;
649+ use std:: ffi:: CString ;
650+ use std:: ptr;
651+
652+ // ── helpers ──────────────────────────────────────────────────────────────
653+
654+ fn make_file_item ( path : & str , name : & str ) -> FffFileItem {
655+ FffFileItem {
656+ relative_path : CString :: new ( path) . unwrap ( ) . into_raw ( ) ,
657+ file_name : CString :: new ( name) . unwrap ( ) . into_raw ( ) ,
658+ git_status : ptr:: null_mut ( ) ,
659+ size : 1024 ,
660+ modified : 1_700_000_000 ,
661+ access_frecency_score : 10 ,
662+ modification_frecency_score : 20 ,
663+ total_frecency_score : 30 ,
664+ is_binary : false ,
665+ }
666+ }
667+
668+ unsafe fn free_file_item ( item : & mut FffFileItem ) {
669+ unsafe {
670+ if !item. relative_path . is_null ( ) {
671+ drop ( CString :: from_raw ( item. relative_path ) ) ;
672+ }
673+ if !item. file_name . is_null ( ) {
674+ drop ( CString :: from_raw ( item. file_name ) ) ;
675+ }
676+ if !item. git_status . is_null ( ) {
677+ drop ( CString :: from_raw ( item. git_status ) ) ;
678+ }
679+ }
680+ }
681+
682+ fn make_grep_match ( path : & str , line : & str ) -> FffGrepMatch {
683+ FffGrepMatch {
684+ relative_path : CString :: new ( path) . unwrap ( ) . into_raw ( ) ,
685+ file_name : CString :: new ( "file.rs" ) . unwrap ( ) . into_raw ( ) ,
686+ git_status : ptr:: null_mut ( ) ,
687+ line_content : CString :: new ( line) . unwrap ( ) . into_raw ( ) ,
688+ match_ranges : ptr:: null_mut ( ) ,
689+ context_before : ptr:: null_mut ( ) ,
690+ context_after : ptr:: null_mut ( ) ,
691+ size : 512 ,
692+ modified : 1_600_000_000 ,
693+ total_frecency_score : 5 ,
694+ access_frecency_score : 6 ,
695+ modification_frecency_score : 7 ,
696+ line_number : 42 ,
697+ byte_offset : 100 ,
698+ col : 8 ,
699+ match_ranges_count : 0 ,
700+ context_before_count : 0 ,
701+ context_after_count : 0 ,
702+ fuzzy_score : 0 ,
703+ has_fuzzy_score : false ,
704+ is_binary : false ,
705+ is_definition : true ,
706+ }
707+ }
708+
709+ unsafe fn free_grep_match ( m : & mut FffGrepMatch ) {
710+ unsafe {
711+ if !m. relative_path . is_null ( ) {
712+ drop ( CString :: from_raw ( m. relative_path ) ) ;
713+ }
714+ if !m. file_name . is_null ( ) {
715+ drop ( CString :: from_raw ( m. file_name ) ) ;
716+ }
717+ if !m. line_content . is_null ( ) {
718+ drop ( CString :: from_raw ( m. line_content ) ) ;
719+ }
720+ }
721+ }
722+
723+ fn make_search_result ( count : u32 , total : u32 , files : u32 ) -> FffSearchResult {
724+ FffSearchResult {
725+ items : ptr:: null_mut ( ) ,
726+ scores : ptr:: null_mut ( ) ,
727+ count,
728+ total_matched : total,
729+ total_files : files,
730+ location : crate :: ffi_types:: FffLocation {
731+ tag : 0 ,
732+ line : 0 ,
733+ col : 0 ,
734+ end_line : 0 ,
735+ end_col : 0 ,
736+ } ,
737+ }
738+ }
739+
740+ fn make_grep_result ( ) -> FffGrepResult {
741+ FffGrepResult {
742+ items : ptr:: null_mut ( ) ,
743+ count : 3 ,
744+ total_matched : 10 ,
745+ total_files_searched : 50 ,
746+ total_files : 200 ,
747+ filtered_file_count : 80 ,
748+ next_file_offset : 51 ,
749+ regex_fallback_error : ptr:: null_mut ( ) ,
750+ }
751+ }
752+
753+ // ── null-guard tests: every function returns its zero-value on NULL ───────
754+
755+ #[ test]
756+ fn null_file_item_returns_null_or_zero ( ) {
757+ let null: * const FffFileItem = ptr:: null ( ) ;
758+ unsafe {
759+ assert ! ( fff_file_item_get_relative_path( null) . is_null( ) ) ;
760+ assert ! ( fff_file_item_get_file_name( null) . is_null( ) ) ;
761+ assert ! ( fff_file_item_get_git_status( null) . is_null( ) ) ;
762+ assert_eq ! ( fff_file_item_get_size( null) , 0 ) ;
763+ assert_eq ! ( fff_file_item_get_modified( null) , 0 ) ;
764+ assert_eq ! ( fff_file_item_get_access_frecency_score( null) , 0 ) ;
765+ assert_eq ! ( fff_file_item_get_modification_frecency_score( null) , 0 ) ;
766+ assert_eq ! ( fff_file_item_get_total_frecency_score( null) , 0 ) ;
767+ assert ! ( !fff_file_item_get_is_binary( null) ) ;
768+ }
769+ }
770+
771+ #[ test]
772+ fn null_grep_match_returns_null_or_zero ( ) {
773+ let null: * const FffGrepMatch = ptr:: null ( ) ;
774+ unsafe {
775+ assert ! ( fff_grep_match_get_relative_path( null) . is_null( ) ) ;
776+ assert ! ( fff_grep_match_get_file_name( null) . is_null( ) ) ;
777+ assert ! ( fff_grep_match_get_git_status( null) . is_null( ) ) ;
778+ assert ! ( fff_grep_match_get_line_content( null) . is_null( ) ) ;
779+ assert_eq ! ( fff_grep_match_get_line_number( null) , 0 ) ;
780+ assert_eq ! ( fff_grep_match_get_byte_offset( null) , 0 ) ;
781+ assert_eq ! ( fff_grep_match_get_col( null) , 0 ) ;
782+ assert_eq ! ( fff_grep_match_get_size( null) , 0 ) ;
783+ assert_eq ! ( fff_grep_match_get_modified( null) , 0 ) ;
784+ assert_eq ! ( fff_grep_match_get_total_frecency_score( null) , 0 ) ;
785+ assert_eq ! ( fff_grep_match_get_access_frecency_score( null) , 0 ) ;
786+ assert_eq ! ( fff_grep_match_get_modification_frecency_score( null) , 0 ) ;
787+ assert_eq ! ( fff_grep_match_get_match_ranges_count( null) , 0 ) ;
788+ assert_eq ! ( fff_grep_match_get_context_before_count( null) , 0 ) ;
789+ assert_eq ! ( fff_grep_match_get_context_after_count( null) , 0 ) ;
790+ assert ! ( !fff_grep_match_get_has_fuzzy_score( null) ) ;
791+ assert_eq ! ( fff_grep_match_get_fuzzy_score( null) , 0 ) ;
792+ assert ! ( !fff_grep_match_get_is_binary( null) ) ;
793+ assert ! ( !fff_grep_match_get_is_definition( null) ) ;
794+ assert ! ( fff_grep_match_get_context_before( null, 0 ) . is_null( ) ) ;
795+ assert ! ( fff_grep_match_get_context_after( null, 0 ) . is_null( ) ) ;
796+ assert ! ( fff_grep_match_get_match_range( null, 0 ) . is_null( ) ) ;
797+ }
798+ }
799+
800+ #[ test]
801+ fn null_search_result_returns_zero ( ) {
802+ let null: * const FffSearchResult = ptr:: null ( ) ;
803+ unsafe {
804+ assert_eq ! ( fff_search_result_get_count( null) , 0 ) ;
805+ assert_eq ! ( fff_search_result_get_total_matched( null) , 0 ) ;
806+ assert_eq ! ( fff_search_result_get_total_files( null) , 0 ) ;
807+ }
808+ }
809+
810+ #[ test]
811+ fn null_grep_result_returns_zero_or_null ( ) {
812+ let null: * const FffGrepResult = ptr:: null ( ) ;
813+ unsafe {
814+ assert_eq ! ( fff_grep_result_get_count( null) , 0 ) ;
815+ assert_eq ! ( fff_grep_result_get_total_matched( null) , 0 ) ;
816+ assert_eq ! ( fff_grep_result_get_total_files_searched( null) , 0 ) ;
817+ assert_eq ! ( fff_grep_result_get_total_files( null) , 0 ) ;
818+ assert_eq ! ( fff_grep_result_get_filtered_file_count( null) , 0 ) ;
819+ assert_eq ! ( fff_grep_result_get_next_file_offset( null) , 0 ) ;
820+ assert ! ( fff_grep_result_get_regex_fallback_error( null) . is_null( ) ) ;
821+ }
822+ }
823+
824+ // ── data correctness tests ────────────────────────────────────────────────
825+
826+ #[ test]
827+ fn file_item_getters_return_correct_values ( ) {
828+ let mut item = make_file_item ( "src/main.rs" , "main.rs" ) ;
829+ let p = & item as * const FffFileItem ;
830+ unsafe {
831+ let path = std:: ffi:: CStr :: from_ptr ( fff_file_item_get_relative_path ( p) ) ;
832+ assert_eq ! ( path. to_str( ) . unwrap( ) , "src/main.rs" ) ;
833+
834+ let name = std:: ffi:: CStr :: from_ptr ( fff_file_item_get_file_name ( p) ) ;
835+ assert_eq ! ( name. to_str( ) . unwrap( ) , "main.rs" ) ;
836+
837+ assert ! ( fff_file_item_get_git_status( p) . is_null( ) ) ;
838+ assert_eq ! ( fff_file_item_get_size( p) , 1024 ) ;
839+ assert_eq ! ( fff_file_item_get_modified( p) , 1_700_000_000 ) ;
840+ assert_eq ! ( fff_file_item_get_access_frecency_score( p) , 10 ) ;
841+ assert_eq ! ( fff_file_item_get_modification_frecency_score( p) , 20 ) ;
842+ assert_eq ! ( fff_file_item_get_total_frecency_score( p) , 30 ) ;
843+ assert ! ( !fff_file_item_get_is_binary( p) ) ;
844+
845+ free_file_item ( & mut item) ;
846+ }
847+ }
848+
849+ #[ test]
850+ fn grep_match_getters_return_correct_values ( ) {
851+ let mut m = make_grep_match ( "src/lib.rs" , "fn hello()" ) ;
852+ let p = & m as * const FffGrepMatch ;
853+ unsafe {
854+ let path = std:: ffi:: CStr :: from_ptr ( fff_grep_match_get_relative_path ( p) ) ;
855+ assert_eq ! ( path. to_str( ) . unwrap( ) , "src/lib.rs" ) ;
856+
857+ let line = std:: ffi:: CStr :: from_ptr ( fff_grep_match_get_line_content ( p) ) ;
858+ assert_eq ! ( line. to_str( ) . unwrap( ) , "fn hello()" ) ;
859+
860+ assert_eq ! ( fff_grep_match_get_line_number( p) , 42 ) ;
861+ assert_eq ! ( fff_grep_match_get_byte_offset( p) , 100 ) ;
862+ assert_eq ! ( fff_grep_match_get_col( p) , 8 ) ;
863+ assert_eq ! ( fff_grep_match_get_size( p) , 512 ) ;
864+ assert_eq ! ( fff_grep_match_get_modified( p) , 1_600_000_000 ) ;
865+ assert_eq ! ( fff_grep_match_get_total_frecency_score( p) , 5 ) ;
866+ assert_eq ! ( fff_grep_match_get_access_frecency_score( p) , 6 ) ;
867+ assert_eq ! ( fff_grep_match_get_modification_frecency_score( p) , 7 ) ;
868+ assert_eq ! ( fff_grep_match_get_match_ranges_count( p) , 0 ) ;
869+ assert ! ( !fff_grep_match_get_has_fuzzy_score( p) ) ;
870+ assert ! ( !fff_grep_match_get_is_binary( p) ) ;
871+ assert ! ( fff_grep_match_get_is_definition( p) ) ;
872+
873+ free_grep_match ( & mut m) ;
874+ }
875+ }
876+
877+ #[ test]
878+ fn search_result_getters_return_correct_values ( ) {
879+ let r = make_search_result ( 5 , 20 , 100 ) ;
880+ let p = & r as * const FffSearchResult ;
881+ unsafe {
882+ assert_eq ! ( fff_search_result_get_count( p) , 5 ) ;
883+ assert_eq ! ( fff_search_result_get_total_matched( p) , 20 ) ;
884+ assert_eq ! ( fff_search_result_get_total_files( p) , 100 ) ;
885+ }
886+ }
887+
888+ #[ test]
889+ fn grep_result_getters_return_correct_values ( ) {
890+ let r = make_grep_result ( ) ;
891+ let p = & r as * const FffGrepResult ;
892+ unsafe {
893+ assert_eq ! ( fff_grep_result_get_count( p) , 3 ) ;
894+ assert_eq ! ( fff_grep_result_get_total_matched( p) , 10 ) ;
895+ assert_eq ! ( fff_grep_result_get_total_files_searched( p) , 50 ) ;
896+ assert_eq ! ( fff_grep_result_get_total_files( p) , 200 ) ;
897+ assert_eq ! ( fff_grep_result_get_filtered_file_count( p) , 80 ) ;
898+ assert_eq ! ( fff_grep_result_get_next_file_offset( p) , 51 ) ;
899+ assert ! ( fff_grep_result_get_regex_fallback_error( p) . is_null( ) ) ;
900+ }
901+ }
902+ }
0 commit comments