-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcruntime.rs
More file actions
111 lines (99 loc) · 3.53 KB
/
Copy pathcruntime.rs
File metadata and controls
111 lines (99 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! C runtime entry points are mocked like any other native function.
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;
}
/// Business code that reads one setting straight from the process environment.
fn deployment_slot() -> Option<String> {
let key = CString::new("DEPLOY_SLOT").unwrap();
// SAFETY: the key is a valid C string and the result is copied before use.
let value = unsafe { getenv(key.as_ptr()) };
if value.is_null() {
return None;
}
// SAFETY: a non-null result points at a C string owned by the environment.
Some(
unsafe { CStr::from_ptr(value) }
.to_string_lossy()
.into_owned(),
)
}
/// Business code that parses a prefix and reports how much it consumed.
fn leading_number(text: &CStr) -> (c_long, usize) {
let mut end: *mut c_char = ptr::null_mut();
// SAFETY: both pointers are valid and the base is in range.
let value = unsafe { strtol(text.as_ptr(), &mut end, 10) };
// SAFETY: the callee reports a position inside the same string.
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| {
// SAFETY: callers of getenv always pass a valid C string.
let name = unsafe { CStr::from_ptr(*name) };
name == c"DEPLOY_SLOT"
})
.once()
.returning(|_| c"blue".as_ptr().cast_mut());
// Every other key keeps reporting an unset variable.
lookup.expect().returning(|_| ptr::null_mut());
assert_eq!(deployment_slot().as_deref(), Some("blue"));
let other = CString::new("PATH").unwrap();
// SAFETY: the key is a valid C string.
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, _| {
// SAFETY: the caller supplies a writable slot and a long enough string.
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));
}