use miden_assembly::SourceSpan;
use miden_processor::advice::AdviceError;
use miden_processor::operation::OperationError;
use miden_processor::{ExecutionError, ExecutionOptions, ExecutionOutput, Felt, MemoryError};
use miden_tx::TransactionExecutorError;
use crate::executor::CodeExecutor;
use crate::{ExecError, assert_execution_error, assert_transaction_executor_error};
fn op_error(err: OperationError) -> ExecutionError {
ExecutionError::OperationError {
label: SourceSpan::default(),
source_file: None,
err,
}
}
fn failed_assertion(err_code: u64) -> ExecutionError {
op_error(OperationError::FailedAssertion {
err_code: Felt::new_unchecked(err_code),
err_msg: None,
})
}
fn exec_err(err: ExecutionError) -> Result<(), ExecError> {
Err(ExecError::new(err))
}
fn tx_err(err: ExecutionError) -> Result<(), TransactionExecutorError> {
Err(TransactionExecutorError::TransactionProgramExecutionFailed(err))
}
async fn run_masm(src: &str) -> Result<ExecutionOutput, ExecError> {
CodeExecutor::with_default_host().run(src).await
}
async fn run_masm_with_options(
src: &str,
options: ExecutionOptions,
) -> Result<ExecutionOutput, ExecError> {
CodeExecutor::with_default_host().execution_options(options).run(src).await
}
#[test]
fn assert_execution_error_matches_outer_variant() {
let r = exec_err(ExecutionError::CycleLimitExceeded(42));
assert_execution_error!(r, matches ExecutionError::CycleLimitExceeded(_));
let r = exec_err(ExecutionError::OutputStackOverflow(7));
assert_execution_error!(r, matches ExecutionError::OutputStackOverflow(_));
}
#[test]
fn assert_execution_error_matches_inner_operation_variant() {
let r = exec_err(op_error(OperationError::DivideByZero));
assert_execution_error!(
r,
matches ExecutionError::OperationError { err: OperationError::DivideByZero, .. }
);
let r = exec_err(op_error(OperationError::LogArgumentZero));
assert_execution_error!(
r,
matches ExecutionError::OperationError { err: OperationError::LogArgumentZero, .. }
);
}
#[test]
fn assert_execution_error_matches_with_guard() {
let r = exec_err(failed_assertion(0x1234));
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::FailedAssertion { err_code, .. },
..
} if err_code == Felt::new_unchecked(0x1234)
);
}
#[test]
#[should_panic(expected = "Execution was unexpectedly successful")]
fn assert_execution_error_matches_panics_on_ok() {
let r: Result<(), ExecError> = Ok(());
assert_execution_error!(r, matches ExecutionError::CycleLimitExceeded(_));
}
#[test]
#[should_panic(expected = "did not match")]
fn assert_execution_error_matches_panics_on_pattern_mismatch() {
let r = exec_err(ExecutionError::CycleLimitExceeded(1));
assert_execution_error!(r, matches ExecutionError::OutputStackOverflow(_));
}
#[test]
#[should_panic(expected = "did not match")]
fn assert_execution_error_matches_panics_on_guard_mismatch() {
let r = exec_err(failed_assertion(0x1234));
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::FailedAssertion { err_code, .. },
..
} if err_code == Felt::new_unchecked(0xdead)
);
}
#[test]
fn assert_transaction_executor_error_matches_outer_variant() {
let r = tx_err(ExecutionError::CycleLimitExceeded(42));
assert_transaction_executor_error!(r, matches ExecutionError::CycleLimitExceeded(_));
}
#[test]
fn assert_transaction_executor_error_matches_inner_operation_variant() {
let r = tx_err(op_error(OperationError::DivideByZero));
assert_transaction_executor_error!(
r,
matches ExecutionError::OperationError { err: OperationError::DivideByZero, .. }
);
}
#[test]
fn assert_transaction_executor_error_matches_with_guard() {
let r = tx_err(failed_assertion(0xabcd));
assert_transaction_executor_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::FailedAssertion { err_code, .. },
..
} if err_code == Felt::new_unchecked(0xabcd)
);
}
#[test]
#[should_panic(expected = "did not match")]
fn assert_transaction_executor_error_matches_panics_on_pattern_mismatch() {
let r = tx_err(ExecutionError::CycleLimitExceeded(1));
assert_transaction_executor_error!(r, matches ExecutionError::OutputStackOverflow(_));
}
#[tokio::test]
async fn divide_by_zero() {
let r = run_masm("begin push.5 push.0 div end").await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::DivideByZero,
..
}
);
}
#[tokio::test]
async fn log_argument_zero() {
let r = run_masm("begin push.0 ilog2 end").await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::LogArgumentZero,
..
}
);
}
#[tokio::test]
async fn not_binary_value() {
let r = run_masm("begin push.2 push.1 and end").await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::NotBinaryValue { .. },
..
}
);
}
#[tokio::test]
async fn not_binary_value_if() {
let r = run_masm("begin push.2 if.true push.0 drop else push.0 drop end end").await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::NotBinaryValueIf { .. },
..
}
);
}
#[tokio::test]
async fn not_binary_value_loop() {
let r = run_masm("begin push.2 while.true push.0 end end").await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::NotBinaryValueLoop { .. },
..
}
);
}
#[tokio::test]
async fn not_u32_values() {
let r = run_masm("begin push.4294967296 u32assert end").await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::NotU32Values { .. },
..
}
);
}
#[tokio::test]
async fn vm_failed_assertion() {
let r = run_masm(r#"begin push.0 assert.err="custom failure" end"#).await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::FailedAssertion { .. },
..
}
);
}
#[tokio::test]
async fn output_stack_overflow() {
let r = run_masm("begin push.1 push.1 push.1 push.1 push.1 end").await;
assert_execution_error!(r, matches ExecutionError::OutputStackOverflow(_));
}
#[tokio::test]
async fn cycle_limit_exceeded() {
let body = "push.0 ".repeat(100);
let src = format!("begin {body} end");
let options = ExecutionOptions::new(Some(64), 64, 4096, false, false)
.expect("max_cycles=64 satisfies MIN_TRACE_LEN");
let r = run_masm_with_options(&src, options).await;
assert_execution_error!(r, matches ExecutionError::CycleLimitExceeded(_));
}
#[tokio::test]
async fn memory_unaligned_word_access() {
let r = run_masm("begin push.1 mem_loadw_be end").await;
assert_execution_error!(
r,
matches ExecutionError::MemoryError {
err: MemoryError::UnalignedWordAccess { .. },
..
}
);
}
#[tokio::test]
async fn advice_error_empty_stack() {
let r = run_masm("begin adv_push end").await;
assert_execution_error!(
r,
matches ExecutionError::AdviceError {
err: AdviceError::StackReadFailed,
..
}
);
}
#[tokio::test]
async fn invalid_stack_depth_on_return() {
let src = "
proc bad
push.1 push.2 push.3
end
begin
call.bad
end
";
let r = run_masm(src).await;
assert_execution_error!(
r,
matches ExecutionError::OperationError {
err: OperationError::InvalidStackDepthOnReturn { .. },
..
}
);
}
#[tokio::test]
async fn event_error_unregistered() {
let src = r#"
const MY_EVENT=event("miden::testing::asserts::unregistered")
begin
emit.MY_EVENT
end
"#;
let r = run_masm(src).await;
assert_execution_error!(r, matches ExecutionError::EventError { .. });
}
#[tokio::test]
async fn procedure_not_found_via_dynexec() {
let r = run_masm("begin push.0.0.0.0 dynexec end").await;
assert_execution_error!(r, matches ExecutionError::ProcedureNotFound { .. });
}