Skip to content

Commit 52cca39

Browse files
Ionut Adrian CiolanCopilot
andcommitted
fff-c: add accessor unit tests (8 tests, 0 warnings)
Tests cover: - Null-pointer guards: every getter returns its zero-value (0 / false / null) when passed a null pointer — no UB, no crash - Data correctness: FffFileItem, FffGrepMatch, FffSearchResult and FffGrepResult getters return exactly the values stored in the struct All 8 tests pass clean under Rust 2024 edition (no unsafe-op warnings). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 714636d commit 52cca39

1 file changed

Lines changed: 260 additions & 0 deletions

File tree

crates/fff-c/src/accessors.rs

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)