use super::*;
use std::ptr;
const MINIMAL_IFC: &str = "ISO-10303-21;
HEADER;
FILE_DESCRIPTION((''),'2;1');
FILE_NAME('minimal.ifc','2026-01-01T00:00:00',(''),(''),'ifc-lite','ifc-lite','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROJECT('0YvctVUKr0kugbFTf53O9L',$,'Smoke Test',$,$,$,$,$,$);
ENDSEC;
END-ISO-10303-21;
";
fn temp_path(tag: &str) -> std::path::PathBuf {
std::env::temp_dir().join(format!("ifc_lite_ffi_smoke_{}_{tag}.ifc", std::process::id()))
}
#[test]
fn null_pointers_return_code_1() {
let mut out_ptr: *mut u8 = ptr::null_mut();
let mut out_len: usize = 0;
let path = b"/nonexistent/whatever.ifc";
unsafe {
assert_eq!(
ifc_lite_parse(ptr::null(), 0, &mut out_ptr, &mut out_len),
1
);
assert_eq!(
ifc_lite_parse(path.as_ptr(), path.len(), ptr::null_mut(), &mut out_len),
1
);
assert_eq!(
ifc_lite_parse(path.as_ptr(), path.len(), &mut out_ptr, ptr::null_mut()),
1
);
}
}
#[test]
fn invalid_utf8_path_returns_code_1() {
let bad = [0xff_u8, 0xfe, 0xfd];
let mut out_ptr: *mut u8 = ptr::null_mut();
let mut out_len: usize = 0;
unsafe {
assert_eq!(
ifc_lite_parse(bad.as_ptr(), bad.len(), &mut out_ptr, &mut out_len),
1
);
}
}
#[test]
fn nonexistent_file_returns_code_2() {
let path = temp_path("does_not_exist");
let _ = std::fs::remove_file(&path);
let path_str = path.to_str().unwrap();
let mut out_ptr: *mut u8 = ptr::null_mut();
let mut out_len: usize = 0;
unsafe {
assert_eq!(
ifc_lite_parse(path_str.as_ptr(), path_str.len(), &mut out_ptr, &mut out_len),
2
);
}
}
#[test]
fn parses_minimal_ifc_then_frees() {
let path = temp_path("minimal");
std::fs::write(&path, MINIMAL_IFC).unwrap();
let path_str = path.to_str().unwrap();
let mut out_ptr: *mut u8 = ptr::null_mut();
let mut out_len: usize = 0;
let code = unsafe {
ifc_lite_parse(path_str.as_ptr(), path_str.len(), &mut out_ptr, &mut out_len)
};
let _ = std::fs::remove_file(&path);
assert_eq!(code, 0, "well-formed minimal IFC should parse");
assert!(!out_ptr.is_null(), "success must hand back a buffer");
assert!(out_len > 0, "buffer must be non-empty");
let json = unsafe { slice::from_raw_parts(out_ptr, out_len) };
let parsed: serde_json::Value = serde_json::from_slice(json).unwrap();
assert!(parsed.is_object(), "response must be a JSON object");
unsafe { ifc_lite_free(out_ptr, out_len) };
}
#[test]
fn parse_ex_maps_every_filter_mode() {
let path = temp_path("ex");
std::fs::write(&path, MINIMAL_IFC).unwrap();
let path_str = path.to_str().unwrap();
for mode in [0_i32, 1, 2, 99] {
let mut out_ptr: *mut u8 = ptr::null_mut();
let mut out_len: usize = 0;
let code = unsafe {
ifc_lite_parse_ex(
path_str.as_ptr(),
path_str.len(),
mode,
&mut out_ptr,
&mut out_len,
)
};
assert_eq!(code, 0, "opening_filter_mode {mode} should parse");
assert!(!out_ptr.is_null());
unsafe { ifc_lite_free(out_ptr, out_len) };
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn free_tolerates_null_and_zero_len() {
unsafe {
ifc_lite_free(ptr::null_mut(), 0);
ifc_lite_free(ptr::null_mut(), 16);
}
}