Skip to content

Commit ae2ede9

Browse files
committed
Add tests for MultiUseSandbox from_snapshot
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 1d1139c commit ae2ede9

1 file changed

Lines changed: 218 additions & 0 deletions

File tree

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2809,4 +2809,222 @@ mod tests {
28092809
}
28102810
let _ = std::fs::remove_file(&path);
28112811
}
2812+
2813+
/// Tests for [`MultiUseSandbox::from_snapshot`] in-memory.
2814+
mod from_snapshot {
2815+
use std::sync::Arc;
2816+
2817+
use hyperlight_testing::simple_guest_as_string;
2818+
2819+
use crate::func::Registerable;
2820+
use crate::sandbox::SandboxConfiguration;
2821+
use crate::sandbox::snapshot::Snapshot;
2822+
use crate::{GuestBinary, HostFunctions, MultiUseSandbox, UninitializedSandbox};
2823+
2824+
fn make_sandbox() -> MultiUseSandbox {
2825+
let path = simple_guest_as_string().unwrap();
2826+
UninitializedSandbox::new(GuestBinary::FilePath(path), None)
2827+
.unwrap()
2828+
.evolve()
2829+
.unwrap()
2830+
}
2831+
2832+
/// Sandbox with an extra `Add(i32, i32) -> i32` host function.
2833+
fn make_sandbox_with_add() -> MultiUseSandbox {
2834+
let path = simple_guest_as_string().unwrap();
2835+
let mut u = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
2836+
u.register_host_function("Add", |a: i32, b: i32| Ok(a + b))
2837+
.unwrap();
2838+
u.evolve().unwrap()
2839+
}
2840+
2841+
fn host_funcs_with_matching_add() -> HostFunctions {
2842+
let mut hf = HostFunctions::default();
2843+
hf.register_host_function("Add", |a: i32, b: i32| Ok(a + b))
2844+
.unwrap();
2845+
hf
2846+
}
2847+
2848+
#[test]
2849+
fn round_trip_running_sandbox() {
2850+
let mut sbox = make_sandbox();
2851+
sbox.call::<i32>("AddToStatic", 11i32).unwrap();
2852+
let snapshot = sbox.snapshot().unwrap();
2853+
let mut sbox2 =
2854+
MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None).unwrap();
2855+
assert_eq!(sbox2.call::<i32>("GetStatic", ()).unwrap(), 11);
2856+
let echoed: String = sbox2.call("Echo", "hi".to_string()).unwrap();
2857+
assert_eq!(echoed, "hi");
2858+
}
2859+
2860+
#[test]
2861+
fn round_trip_pre_init_snapshot() {
2862+
let path = simple_guest_as_string().unwrap();
2863+
let snap =
2864+
Snapshot::from_env(GuestBinary::FilePath(path), SandboxConfiguration::default())
2865+
.unwrap();
2866+
let mut sbox =
2867+
MultiUseSandbox::from_snapshot(Arc::new(snap), HostFunctions::default(), None)
2868+
.unwrap();
2869+
assert_eq!(sbox.call::<i32>("GetStatic", ()).unwrap(), 0);
2870+
}
2871+
2872+
/// Sandboxes built from clones of one `Arc<Snapshot>` share
2873+
/// `sandbox_id` (so both can `restore` to it) but are
2874+
/// memory-isolated from each other.
2875+
#[test]
2876+
fn arc_clone_isolation_and_restore_compat() {
2877+
let mut sbox = make_sandbox();
2878+
sbox.call::<i32>("AddToStatic", 3i32).unwrap();
2879+
let snapshot = sbox.snapshot().unwrap();
2880+
2881+
let mut a =
2882+
MultiUseSandbox::from_snapshot(snapshot.clone(), HostFunctions::default(), None)
2883+
.unwrap();
2884+
let mut b =
2885+
MultiUseSandbox::from_snapshot(snapshot.clone(), HostFunctions::default(), None)
2886+
.unwrap();
2887+
assert_eq!(a.call::<i32>("GetStatic", ()).unwrap(), 3);
2888+
assert_eq!(b.call::<i32>("GetStatic", ()).unwrap(), 3);
2889+
2890+
a.call::<i32>("AddToStatic", 7i32).unwrap();
2891+
assert_eq!(a.call::<i32>("GetStatic", ()).unwrap(), 10);
2892+
assert_eq!(b.call::<i32>("GetStatic", ()).unwrap(), 3);
2893+
2894+
a.restore(snapshot.clone()).unwrap();
2895+
b.restore(snapshot).unwrap();
2896+
assert_eq!(a.call::<i32>("GetStatic", ()).unwrap(), 3);
2897+
assert_eq!(b.call::<i32>("GetStatic", ()).unwrap(), 3);
2898+
}
2899+
2900+
#[test]
2901+
fn accepts_matching_host_functions() {
2902+
let mut sbox = make_sandbox_with_add();
2903+
sbox.call::<i32>("AddToStatic", 5i32).unwrap();
2904+
let snap = sbox.snapshot().unwrap();
2905+
let mut sbox2 =
2906+
MultiUseSandbox::from_snapshot(snap, host_funcs_with_matching_add(), None).unwrap();
2907+
assert_eq!(sbox2.call::<i32>("GetStatic", ()).unwrap(), 5);
2908+
}
2909+
2910+
#[test]
2911+
fn rejects_missing_host_function() {
2912+
let mut sbox = make_sandbox_with_add();
2913+
let snap = sbox.snapshot().unwrap();
2914+
let err = MultiUseSandbox::from_snapshot(snap, HostFunctions::default(), None)
2915+
.expect_err("missing `Add` must be rejected");
2916+
let msg = format!("{}", err);
2917+
assert!(msg.contains("Add"), "got: {}", msg);
2918+
}
2919+
2920+
#[test]
2921+
fn rejects_signature_mismatch() {
2922+
let mut sbox = make_sandbox_with_add();
2923+
let snap = sbox.snapshot().unwrap();
2924+
let mut hf = HostFunctions::default();
2925+
hf.register_host_function("Add", |a: String, b: String| Ok(format!("{a}{b}")))
2926+
.unwrap();
2927+
let err = MultiUseSandbox::from_snapshot(snap, hf, None)
2928+
.expect_err("signature mismatch on `Add` must be rejected");
2929+
let msg = format!("{}", err);
2930+
assert!(msg.contains("Add"), "got: {}", msg);
2931+
}
2932+
2933+
/// Supplied host-function set may be a strict superset of the
2934+
/// snapshot's required set.
2935+
#[test]
2936+
fn accepts_extra_host_functions() {
2937+
let mut sbox = make_sandbox_with_add();
2938+
sbox.call::<i32>("AddToStatic", 9i32).unwrap();
2939+
let snap = sbox.snapshot().unwrap();
2940+
let mut hf = host_funcs_with_matching_add();
2941+
hf.register_host_function("Mul", |a: i32, b: i32| Ok(a * b))
2942+
.unwrap();
2943+
let mut sbox2 = MultiUseSandbox::from_snapshot(snap, hf, None).unwrap();
2944+
assert_eq!(sbox2.call::<i32>("GetStatic", ()).unwrap(), 9);
2945+
}
2946+
2947+
/// A sandbox built via `from_snapshot` can itself be snapshotted
2948+
/// and restored, and its snapshots are restore-compatible with it.
2949+
#[test]
2950+
fn re_snapshot_after_from_snapshot() {
2951+
let mut sbox = make_sandbox();
2952+
sbox.call::<i32>("AddToStatic", 4i32).unwrap();
2953+
let snap1 = sbox.snapshot().unwrap();
2954+
2955+
let mut sbox2 =
2956+
MultiUseSandbox::from_snapshot(snap1, HostFunctions::default(), None).unwrap();
2957+
sbox2.call::<i32>("AddToStatic", 6i32).unwrap();
2958+
let snap2 = sbox2.snapshot().unwrap();
2959+
2960+
sbox2.call::<i32>("AddToStatic", 100i32).unwrap();
2961+
assert_eq!(sbox2.call::<i32>("GetStatic", ()).unwrap(), 110);
2962+
2963+
sbox2.restore(snap2.clone()).unwrap();
2964+
assert_eq!(sbox2.call::<i32>("GetStatic", ()).unwrap(), 10);
2965+
2966+
let mut sbox3 =
2967+
MultiUseSandbox::from_snapshot(snap2, HostFunctions::default(), None).unwrap();
2968+
assert_eq!(sbox3.call::<i32>("GetStatic", ()).unwrap(), 10);
2969+
}
2970+
2971+
/// The host function closure supplied to `from_snapshot` (not the
2972+
/// original sandbox's closure) is the one invoked at runtime.
2973+
#[test]
2974+
fn supplied_host_function_is_callable() {
2975+
let path = simple_guest_as_string().unwrap();
2976+
let mut u = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
2977+
u.register_host_function("Echo42", || Ok(1i64)).unwrap();
2978+
let mut sbox = u.evolve().unwrap();
2979+
let snap = sbox.snapshot().unwrap();
2980+
2981+
let mut hf = HostFunctions::default();
2982+
hf.register_host_function("Echo42", || Ok(42i64)).unwrap();
2983+
let mut sbox2 = MultiUseSandbox::from_snapshot(snap, hf, None).unwrap();
2984+
2985+
let got: i64 = sbox2
2986+
.call(
2987+
"CallGivenParamlessHostFuncThatReturnsI64",
2988+
"Echo42".to_string(),
2989+
)
2990+
.unwrap();
2991+
assert_eq!(got, 42);
2992+
}
2993+
2994+
/// Pre-init snapshots record no required host functions, so any
2995+
/// `HostFunctions` set is accepted.
2996+
#[test]
2997+
fn pre_init_snapshot_accepts_arbitrary_host_functions() {
2998+
let path = simple_guest_as_string().unwrap();
2999+
let snap =
3000+
Snapshot::from_env(GuestBinary::FilePath(path), SandboxConfiguration::default())
3001+
.unwrap();
3002+
let mut hf = HostFunctions::default();
3003+
hf.register_host_function("Unrelated", |a: i32| Ok(a + 1))
3004+
.unwrap();
3005+
let mut sbox = MultiUseSandbox::from_snapshot(Arc::new(snap), hf, None).unwrap();
3006+
assert_eq!(sbox.call::<i32>("GetStatic", ()).unwrap(), 0);
3007+
}
3008+
3009+
/// Snapshots taken from a sandbox built via `from_snapshot`
3010+
/// must continue the generation counter of the snapshot they
3011+
/// were constructed from, matching `restore`.
3012+
#[test]
3013+
fn snapshot_generation_propagates() {
3014+
let mut sbox = make_sandbox();
3015+
sbox.call::<i32>("AddToStatic", 1i32).unwrap();
3016+
let snap1 = sbox.snapshot().unwrap();
3017+
let gen1 = snap1.snapshot_generation();
3018+
sbox.call::<i32>("AddToStatic", 1i32).unwrap();
3019+
let snap2 = sbox.snapshot().unwrap();
3020+
let gen2 = snap2.snapshot_generation();
3021+
assert_eq!(gen2, gen1 + 1);
3022+
3023+
let mut sbox2 =
3024+
MultiUseSandbox::from_snapshot(snap2, HostFunctions::default(), None).unwrap();
3025+
sbox2.call::<i32>("AddToStatic", 1i32).unwrap();
3026+
let snap3 = sbox2.snapshot().unwrap();
3027+
assert_eq!(snap3.snapshot_generation(), gen2 + 1);
3028+
}
3029+
}
28123030
}

0 commit comments

Comments
 (0)