use std::sync::Arc;
use sim_kernel::{
Args, CapabilitySet, Cx, DefaultFactory, NoopEvalPolicy, ObjectEncoding, ReadPolicy, Symbol,
TrustLevel, Value, read_construct_capability,
};
use crate::{OverrideTable, construct_override_table, install_override_table_lib};
fn test_cx() -> Cx {
Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory))
}
fn table(cx: &mut Cx, entries: &[(&str, Value)]) -> Value {
cx.new_table(
entries
.iter()
.map(|(key, value)| (Symbol::new(*key), value.clone()))
.collect(),
)
.unwrap()
}
fn read_policy(capabilities: &[sim_kernel::CapabilityName]) -> ReadPolicy {
ReadPolicy {
trust: TrustLevel::Untrusted,
capabilities: capabilities
.iter()
.cloned()
.fold(CapabilitySet::new(), |set, capability| {
set.grant(capability)
}),
}
}
#[test]
fn override_table_front_shadows_and_writes_to_front() {
let mut cx = test_cx();
let back_a = cx.factory().string("back-a".to_owned()).unwrap();
let back_b = cx.factory().string("back-b".to_owned()).unwrap();
let back = table(&mut cx, &[("a", back_a), ("b", back_b)]);
let front_b = cx.factory().string("front-b".to_owned()).unwrap();
let front_c = cx.factory().string("front-c".to_owned()).unwrap();
let front = table(&mut cx, &[("b", front_b)]);
let value = construct_override_table(&mut cx, vec![front.clone(), back.clone()]).unwrap();
let override_table = value.object().as_table_impl().unwrap();
assert_eq!(
override_table
.get(&mut cx, Symbol::new("b"))
.unwrap()
.object()
.as_expr(&mut cx)
.unwrap(),
sim_kernel::Expr::String("front-b".to_owned())
);
assert_eq!(
override_table
.get(&mut cx, Symbol::new("a"))
.unwrap()
.object()
.as_expr(&mut cx)
.unwrap(),
sim_kernel::Expr::String("back-a".to_owned())
);
override_table
.set(&mut cx, Symbol::new("c"), front_c)
.unwrap();
assert!(
front
.object()
.as_table_impl()
.unwrap()
.has(&mut cx, Symbol::new("c"))
.unwrap()
);
assert!(
!back
.object()
.as_table_impl()
.unwrap()
.has(&mut cx, Symbol::new("c"))
.unwrap()
);
}
#[test]
fn override_table_constructor_class_and_read_construct_share_path() {
let mut cx = test_cx();
install_override_table_lib(&mut cx).unwrap();
let front_value = cx.factory().bool(true).unwrap();
let back_value = cx.factory().nil().unwrap();
let front = table(&mut cx, &[("front", front_value)]);
let back = table(&mut cx, &[("back", back_value)]);
let via_class = cx
.call_class(
&Symbol::new("OverrideTable"),
Args::new(vec![front.clone(), back.clone()]),
)
.unwrap();
assert!(via_class.object().as_table_impl().is_some());
let denied = cx.read_construct(
&Symbol::new("OverrideTable"),
vec![front.clone(), back.clone()],
);
assert!(matches!(
denied,
Err(sim_kernel::Error::CapabilityDenied { capability })
if capability == read_construct_capability()
));
cx.grant(read_construct_capability());
let via_read = cx
.read_construct(&Symbol::new("OverrideTable"), vec![front, back])
.unwrap();
let encoding = via_read
.object()
.as_object_encoder()
.unwrap()
.object_encoding(&mut cx)
.unwrap();
assert!(matches!(
encoding,
ObjectEncoding::Constructor { class, args }
if class == Symbol::new("OverrideTable") && args.len() == 2
));
let _ = read_policy(&[read_construct_capability()]);
}
#[test]
fn override_table_rejects_non_table_layers() {
let cx = test_cx();
assert!(matches!(
OverrideTable::new(vec![cx.factory().bool(true).unwrap()]),
Err(sim_kernel::Error::Eval(message)) if message.contains("every layer must be a table")
));
}