#![cfg(feature = "nix-store-c")] #![cfg(test)] use core::ffi::{c_char, c_uint, c_void}; use std::ffi::CString; use std::{ptr, slice, str}; use serial_test::serial; use nixide_sys::*; #[test] #[serial] fn libstore_init_and_open_free() { unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init(ctx); assert_eq!(err, NixErr::Ok); // Open the default store (NULL URI, NULL params) let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); assert!(!store.is_null()); // Free the store and context nix_store_free(store); nix_c_context_free(ctx); } } #[test] #[serial] fn parse_and_clone_free_store_path() { unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init(ctx); assert_eq!(err, NixErr::Ok); let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); assert!(!store.is_null()); // Parse a store path (I'm using a dummy path, will likely be invalid but // should not segfault) XXX: store_path may be null if path is invalid, // but should not crash let path_str = CString::new("/nix/store/dummy-path").unwrap(); let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); if !store_path.is_null() { // Clone and free let cloned = nix_store_path_clone(store_path); assert!(!cloned.is_null()); nix_store_path_free(cloned); nix_store_path_free(store_path); } nix_store_free(store); nix_c_context_free(ctx); } } #[test] #[serial] fn store_get_uri_and_storedir() { unsafe extern "C" fn string_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) { let s = unsafe { slice::from_raw_parts(start.cast::(), n as usize) }; let s = str::from_utf8(s).unwrap(); let out = user_data.cast::>(); unsafe { *out = Some(s.to_string()) }; } unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init(ctx); assert_eq!(err, NixErr::Ok); let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); assert!(!store.is_null()); let mut uri: Option = None; let res = nix_store_get_uri(ctx, store, Some(string_callback), (&raw mut uri).cast()); assert_eq!(res, NixErr::Ok); assert!(uri.is_some()); let mut storedir: Option = None; let res = nix_store_get_storedir( ctx, store, Some(string_callback), (&raw mut storedir).cast(), ); assert_eq!(res, NixErr::Ok); assert!(storedir.is_some()); nix_store_free(store); nix_c_context_free(ctx); } } #[test] #[serial] fn libstore_init_no_load_config() { unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init_no_load_config(ctx); assert_eq!(err, NixErr::Ok); nix_c_context_free(ctx); } } #[test] #[serial] fn store_is_valid_path_and_real_path() { unsafe extern "C" fn string_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) { let s = unsafe { slice::from_raw_parts(start.cast::(), n as usize) }; let s = str::from_utf8(s).unwrap(); let out = user_data.cast::>(); unsafe { *out = Some(s.to_string()) }; } unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init(ctx); assert_eq!(err, NixErr::Ok); let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); assert!(!store.is_null()); // Use a dummy path (should not be valid, but should not crash) let path_str = CString::new("/nix/store/dummy-path").unwrap(); let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); if !store_path.is_null() { let valid = nix_store_is_valid_path(ctx, store, store_path); assert!(!valid, "Dummy path should not be valid"); let mut real_path: Option = None; let res = nix_store_real_path( ctx, store, store_path, Some(string_callback), (&raw mut real_path).cast(), ); // May fail, but should not crash assert!(res == NixErr::Ok || res == NixErr::Unknown); nix_store_path_free(store_path); } nix_store_free(store); nix_c_context_free(ctx); } } #[test] #[serial] fn store_path_name() { unsafe extern "C" fn string_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) { let s = unsafe { slice::from_raw_parts(start.cast::(), n as usize) }; let s = str::from_utf8(s).unwrap(); let out = user_data.cast::>(); unsafe { *out = Some(s.to_string()) }; } unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init(ctx); assert_eq!(err, NixErr::Ok); let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); assert!(!store.is_null()); let path_str = CString::new("/nix/store/foo-bar-baz").unwrap(); let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); if !store_path.is_null() { let mut name: Option = None; nix_store_path_name(store_path, Some(string_callback), (&raw mut name).cast()); // Should extract the name part ("foo-bar-baz") assert!(name.as_deref().unwrap_or("").contains("foo-bar-baz")); nix_store_path_free(store_path); } nix_store_free(store); nix_c_context_free(ctx); } } #[test] #[serial] fn store_get_version() { unsafe extern "C" fn string_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) { let s = unsafe { slice::from_raw_parts(start.cast::(), n as usize) }; let s = str::from_utf8(s).unwrap(); let out = user_data.cast::>(); unsafe { *out = Some(s.to_string()) }; } unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init(ctx); assert_eq!(err, NixErr::Ok); let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); assert!(!store.is_null()); let mut version: Option = None; let res = nix_store_get_version(ctx, store, Some(string_callback), (&raw mut version).cast()); assert_eq!(res, NixErr::Ok); // Version may be empty for dummy stores, but should not crash assert!(version.is_some()); nix_store_free(store); nix_c_context_free(ctx); } } #[test] #[serial] fn store_realise_and_copy_closure() { unsafe extern "C" fn realise_callback( _userdata: *mut c_void, outname: *const c_char, out: *const StorePath, ) { // Just check that callback is called with non-null pointers assert!(!outname.is_null()); assert!(!out.is_null()); } unsafe { let ctx = nix_c_context_create(); assert!(!ctx.is_null()); let err = nix_libstore_init(ctx); assert_eq!(err, NixErr::Ok); let store = nix_store_open(ctx, ptr::null(), ptr::null_mut()); assert!(!store.is_null()); // Use a dummy path (should not crash, may not realise) let path_str = CString::new("/nix/store/dummy-path").unwrap(); let store_path = nix_store_parse_path(ctx, store, path_str.as_ptr()); if !store_path.is_null() { // Realise (should fail, but must not crash) let _ = nix_store_realise( ctx, store, store_path, ptr::null_mut(), Some(realise_callback), ); // Copy closure to same store (should fail, but must not crash) let _ = nix_store_copy_closure(ctx, store, store, store_path); nix_store_path_free(store_path); } nix_store_free(store); nix_c_context_free(ctx); } }