extern crate alloc;
use alloc::string::String;
use keleusma::{Arena, ArenaHandle, EpochSaturated, KString, Stale, Value};
#[test]
fn kstring_round_trips_through_arena() {
let arena = Arena::with_capacity(256);
let handle: KString = KString::alloc(&arena, "boundary").unwrap();
let s = handle.get(&arena).unwrap();
assert_eq!(s, "boundary");
}
#[test]
fn kstring_handle_is_copy() {
let arena = Arena::with_capacity(256);
let handle = KString::alloc(&arena, "shared").unwrap();
let copy = handle;
assert_eq!(handle.get(&arena).unwrap(), "shared");
assert_eq!(copy.get(&arena).unwrap(), "shared");
}
#[test]
fn kstring_returns_stale_after_reset() {
let mut arena = Arena::with_capacity(256);
let handle = KString::alloc(&arena, "ephemeral").unwrap();
assert_eq!(handle.get(&arena).unwrap(), "ephemeral");
arena.reset().unwrap();
let result = handle.get(&arena);
assert!(matches!(result, Err(Stale)));
}
#[test]
fn arena_epoch_advances_on_each_reset() {
let mut arena = Arena::with_capacity(64);
assert_eq!(arena.epoch(), 0);
arena.reset().unwrap();
assert_eq!(arena.epoch(), 1);
arena.reset().unwrap();
assert_eq!(arena.epoch(), 2);
}
#[test]
fn epoch_saturation_is_a_hard_halt() {
let mut arena = Arena::with_capacity(16);
arena.reset().unwrap();
arena.reset().unwrap();
arena.reset().unwrap();
assert_eq!(arena.epoch(), 3);
assert_eq!(arena.epoch_remaining(), u64::MAX - 3);
}
#[test]
fn force_reset_epoch_recovers_a_saturated_arena() {
let mut arena = Arena::with_capacity(16);
arena.reset().unwrap();
arena.reset().unwrap();
assert_eq!(arena.epoch(), 2);
unsafe {
arena.force_reset_epoch();
}
assert_eq!(arena.epoch(), 0);
arena.reset().unwrap();
assert_eq!(arena.epoch(), 1);
}
#[test]
fn arena_handle_is_not_send() {
fn assert_not_send<T: Send>() {}
let _: fn() = assert_not_send::<i32>;
}
#[test]
fn boundary_copy_out_pattern() {
let mut arena = Arena::with_capacity(256);
let handle = KString::alloc(&arena, "carry me").unwrap();
let owned: String = String::from(handle.get(&arena).unwrap());
arena.reset().unwrap();
assert!(matches!(handle.get(&arena), Err(Stale)));
assert_eq!(owned, "carry me");
}
#[test]
fn arena_handle_generic_supports_other_unsized_types() {
fn _expect_arena_handle<T: ?Sized>(_: &ArenaHandle<T>) {}
let arena = Arena::with_capacity(64);
let handle = KString::alloc(&arena, "x").unwrap();
_expect_arena_handle(handle.as_handle());
}
#[test]
fn epoch_saturated_error_is_documented() {
let a: EpochSaturated = EpochSaturated;
let b: EpochSaturated = EpochSaturated;
assert_eq!(a, b);
}
#[test]
fn value_kstr_type_name_is_kstr() {
let arena = Arena::with_capacity(64);
let handle = KString::alloc(&arena, "hi").unwrap();
let v = Value::KStr(handle);
assert_eq!(v.type_name(), "KStr");
}
#[test]
fn value_kstr_resolves_through_arena() {
let arena = Arena::with_capacity(64);
let handle = KString::alloc(&arena, "via Value::KStr").unwrap();
let v = Value::KStr(handle);
let s = v.as_str_with_arena(&arena).unwrap().unwrap();
assert_eq!(s, "via Value::KStr");
}
#[test]
fn value_kstr_returns_stale_after_reset() {
let mut arena = Arena::with_capacity(64);
let handle = KString::alloc(&arena, "ephemeral").unwrap();
let v = Value::KStr(handle);
assert_eq!(v.as_str_with_arena(&arena).unwrap().unwrap(), "ephemeral");
arena.reset().unwrap();
let result = v.as_str_with_arena(&arena);
assert!(matches!(result, Err(Stale)));
}
#[test]
fn value_kstr_counts_as_dynstr_for_cross_yield_prohibition() {
let arena = Arena::with_capacity(64);
let handle = KString::alloc(&arena, "x").unwrap();
let v = Value::KStr(handle);
assert!(v.contains_dynstr());
}
#[test]
fn value_kstr_inside_tuple_is_detected() {
let arena = Arena::with_capacity(64);
let handle = KString::alloc(&arena, "y").unwrap();
let v = Value::Tuple(alloc::vec![Value::Int(1), Value::KStr(handle)]);
assert!(v.contains_dynstr());
}
#[test]
fn value_kstr_equality_uses_epoch_identity() {
let mut arena = Arena::with_capacity(128);
let h0 = KString::alloc(&arena, "shared").unwrap();
let v0 = Value::KStr(h0);
arena.reset().unwrap();
let h1 = KString::alloc(&arena, "shared").unwrap();
let v1 = Value::KStr(h1);
assert_ne!(v0, v1);
}
#[test]
fn value_as_str_returns_none_for_kstr_without_arena() {
let arena = Arena::with_capacity(64);
let handle = KString::alloc(&arena, "nope").unwrap();
let v = Value::KStr(handle);
assert!(v.as_str().is_none());
}