use neo_devpack_solidity::runtime::execution::ExecutionContext;
use neo_devpack_solidity::runtime::RuntimeConfig;
#[test]
fn try_block_no_exception_normal_path() {
let code = [
0x3B, 0x00, 0x07, 0x11, 0x3D, 0x02, 0x40, 0x13, 0x3F, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
while !ctx.step().expect("step").halted {}
assert_eq!(ctx.return_data(), (3i64).to_le_bytes().to_vec());
}
#[test]
fn throw_without_try_block_propagates() {
let code = [
0x0C, 0x04, 0x45, 0x52, 0x52, 0x4F, 0x3A, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let err = loop {
match ctx.step() {
Ok(step) if step.halted => break None,
Ok(_) => continue,
Err(e) => break Some(e),
}
};
assert!(err.is_some(), "Should throw exception");
}
#[test]
fn try_catch_catches_exception() {
let code = [
0x3B, 0x04, 0x00, 0x3A, 0x11, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
while !ctx.step().expect("step").halted {}
assert_eq!(ctx.return_data(), (1i64).to_le_bytes().to_vec());
}
#[test]
fn assert_true_passes() {
let code = [
0x11, 0x39, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
while !ctx.step().expect("step").halted {}
}
#[test]
fn assert_false_throws() {
let code = [
0x10, 0x39, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let err = loop {
match ctx.step() {
Ok(step) if step.halted => break None,
Ok(_) => continue,
Err(e) => break Some(e),
}
};
assert!(err.is_some(), "ASSERT false should throw");
}
#[test]
fn nested_try_blocks() {
let code = [
0x3B, 0x00, 0x0E, 0x3B, 0x04, 0x00, 0x3A, 0x45, 0x11, 0x3D, 0x02, 0x3D, 0x02, 0x40, 0x13, 0x3F, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
while !ctx.step().expect("step").halted {}
assert_eq!(ctx.return_data(), (3i64).to_le_bytes().to_vec());
}
#[test]
fn finally_executes_after_normal_completion() {
let code = [
0x3B, 0x00, 0x07, 0x12, 0x3D, 0x02, 0x40, 0x13, 0x3F, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
while !ctx.step().expect("step").halted {}
assert_eq!(ctx.return_data(), (3i64).to_le_bytes().to_vec());
}
#[test]
fn finally_executes_after_exception() {
let code = [
0x3B, 0x00, 0x05, 0x3A, 0x40, 0x21, 0x3F, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let err = loop {
match ctx.step() {
Ok(step) if step.halted => break None,
Ok(_) => continue,
Err(e) => break Some(e),
}
};
assert!(err.is_some(), "should rethrow after finally");
}
#[test]
fn abort_terminates_immediately() {
let code = [
0x21, 0x38, 0x21, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let err = loop {
match ctx.step() {
Ok(step) if step.halted => break None,
Ok(_) => continue,
Err(e) => break Some(e),
}
};
assert!(err.is_some(), "ABORT should terminate with error");
}
#[test]
fn try_l_with_long_offsets() {
let code = [
0x3C, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x04, 0x54, 0x45, 0x53, 0x54, 0x3A, 0x11, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
while !ctx.step().expect("step").halted {}
}
#[test]
fn throw_with_empty_message() {
let code = [
0x0C, 0x00, 0x3A, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let err = loop {
match ctx.step() {
Ok(step) if step.halted => break None,
Ok(_) => continue,
Err(e) => break Some(e),
}
};
assert!(err.is_some(), "Should throw");
}
#[test]
fn endtry_without_try_throws() {
let code = [
0x3D, 0x01, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let result = ctx.step();
assert!(result.is_err(), "ENDTRY without TRY should error");
}
#[test]
fn exception_handling_consumes_gas() {
let config = RuntimeConfig {
gas_limit: 10_000,
..Default::default()
};
let code = [
0x3B, 0x0A, 0x00, 0x0C, 0x04, 0x45, 0x52, 0x52, 0x4F, 0x3A, 0x11, 0x40, ];
let mut ctx = ExecutionContext::new(&config).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let gas_before = ctx.gas_used();
while !ctx.step().expect("step").halted {}
let gas_after = ctx.gas_used();
assert!(gas_after > gas_before, "Gas should be consumed");
}
#[test]
fn exception_message_preserved_in_catch() {
let code = [
0x0C, 0x09, 0x54, 0x65, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x3B, 0x04, 0x00, 0x3A, 0x21, 0x3D, 0x02, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
while !ctx.step().expect("step").halted {}
}
#[test]
fn multiple_exceptions_in_sequence() {
let code = [
0x11, 0x39, 0x10, 0x39, 0x40, ];
let mut ctx = ExecutionContext::new(&RuntimeConfig::default()).expect("context init");
ctx.initialize(&code, &[]).expect("init");
let err = loop {
match ctx.step() {
Ok(step) if step.halted => break None,
Ok(_) => continue,
Err(e) => break Some(e),
}
};
assert!(err.is_some(), "Second ASSERT should fail");
}