use alloc::string::ToString;
use std::sync::Arc;
use miden_assembly::DefaultSourceManager;
use miden_mast_package::Package;
use super::*;
use crate::exec::Executor;
#[test]
fn callstack_tracks_nested_frame_events() {
use crate::event::{FRAME_END_EVENT, FRAME_START_EVENT};
let source_manager = Arc::new(DefaultSourceManager::default());
let program = miden_assembly::Assembler::new(source_manager.clone())
.assemble_program(
"program",
format!(
r#"
proc inner
emit.event("{FRAME_START_EVENT}")
nop
emit.event("{FRAME_END_EVENT}")
end
proc outer
emit.event("{FRAME_START_EVENT}")
exec.inner
emit.event("{FRAME_END_EVENT}")
end
begin
emit.event("{FRAME_START_EVENT}")
exec.outer
emit.event("{FRAME_END_EVENT}")
end
"#
),
)
.map(Arc::<Package>::from)
.unwrap();
let mut executor = Executor::new(Vec::<Felt>::new()).into_debug(program, source_manager);
let mut max_depth = 0;
let mut saw_inner = false;
let mut snapshots = Vec::new();
for _ in 0..64 {
executor.step().unwrap();
let frames = executor.callstack.frames();
max_depth = max_depth.max(frames.len());
snapshots.push(
frames
.iter()
.map(|frame| {
frame
.procedure("")
.map(|name| name.to_string())
.unwrap_or_else(|| "<unknown>".to_string())
})
.collect::<Vec<_>>(),
);
saw_inner |= frames.len() >= 3
&& frames
.last()
.and_then(|frame| frame.procedure(""))
.is_some_and(|name| name.contains("inner"));
if saw_inner || executor.stopped {
break;
}
}
assert!(
max_depth >= 3,
"expected nested main -> outer -> inner frames, max depth was {max_depth}"
);
assert!(
saw_inner,
"expected innermost frame to resolve to inner; snapshots: {snapshots:?}"
);
}