use shimforge::{Session, mock};
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_long};
use std::ptr;
use std::sync::{Mutex, MutexGuard};
static TEST_LOCK: Mutex<()> = Mutex::new(());
fn serial() -> MutexGuard<'static, ()> {
TEST_LOCK.lock().unwrap_or_else(|error| error.into_inner())
}
unsafe extern "C" {
fn getenv(name: *const c_char) -> *mut c_char;
fn strtol(text: *const c_char, end: *mut *mut c_char, base: c_int) -> c_long;
}
fn deployment_slot() -> Option<String> {
let key = CString::new("DEPLOY_SLOT").unwrap();
let value = unsafe { getenv(key.as_ptr()) };
if value.is_null() {
return None;
}
Some(
unsafe { CStr::from_ptr(value) }
.to_string_lossy()
.into_owned(),
)
}
fn leading_number(text: &CStr) -> (c_long, usize) {
let mut end: *mut c_char = ptr::null_mut();
let value = unsafe { strtol(text.as_ptr(), &mut end, 10) };
let consumed = unsafe { end.offset_from(text.as_ptr()) };
(value, consumed as usize)
}
#[test]
fn an_environment_lookup_returns_a_chosen_string() {
let _serial = serial();
let mut session = Session::new();
let lookup = mock!(
session,
getenv,
unsafe extern "C" fn(*const c_char) -> *mut c_char
);
lookup.expect().returning(|_| c"canary".as_ptr().cast_mut());
assert_eq!(deployment_slot().as_deref(), Some("canary"));
}
#[test]
fn an_environment_lookup_can_match_the_requested_key() {
let _serial = serial();
let mut session = Session::new();
let lookup = mock!(
session,
getenv,
unsafe extern "C" fn(*const c_char) -> *mut c_char
);
lookup
.expect()
.with(|name| {
let name = unsafe { CStr::from_ptr(*name) };
name == c"DEPLOY_SLOT"
})
.once()
.returning(|_| c"blue".as_ptr().cast_mut());
lookup.expect().returning(|_| ptr::null_mut());
assert_eq!(deployment_slot().as_deref(), Some("blue"));
let other = CString::new("PATH").unwrap();
assert!(unsafe { getenv(other.as_ptr()) }.is_null());
session.restore();
assert_eq!(deployment_slot(), None);
}
#[test]
fn a_parser_writes_through_its_output_pointer_and_returns_a_value() {
let _serial = serial();
let mut session = Session::new();
let parse = mock!(
session,
strtol,
unsafe extern "C" fn(*const c_char, *mut *mut c_char, c_int) -> c_long
);
parse
.expect()
.with(|_, end, base| !end.is_null() && *base == 10)
.once()
.returning(|text, end, _| {
unsafe { *end = text.cast_mut().add(4) };
815
});
assert_eq!(leading_number(c"1234 units"), (815, 4));
session.restore();
assert_eq!(leading_number(c"1234 units"), (1234, 4));
}