use fusevm::op::file_test;
use fusevm::{ChunkBuilder, Op, VMResult, Value, VM};
fn run(b: ChunkBuilder) -> VMResult {
VM::new(b.build()).run()
}
#[test]
fn jump_if_true_keep_preserves_top_when_taken() {
let mut b = ChunkBuilder::new();
b.emit(Op::LoadInt(7), 1);
b.emit(Op::JumpIfTrueKeep(3), 1);
b.emit(Op::LoadInt(999), 1);
match run(b) {
VMResult::Ok(Value::Int(7)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn jump_if_true_keep_falls_through_with_falsy() {
let mut b = ChunkBuilder::new();
b.emit(Op::LoadInt(0), 1);
b.emit(Op::JumpIfTrueKeep(3), 1);
b.emit(Op::LoadInt(42), 1);
match run(b) {
VMResult::Ok(Value::Int(42)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn jump_if_false_keep_short_circuits_or_chain() {
let mut b = ChunkBuilder::new();
b.emit(Op::LoadInt(0), 1);
b.emit(Op::JumpIfFalseKeep(4), 1);
b.emit(Op::Pop, 1);
b.emit(Op::LoadInt(99), 1);
match run(b) {
VMResult::Ok(Value::Int(0)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn call_unknown_function_returns_error() {
let mut b = ChunkBuilder::new();
let n = b.add_name("not_defined");
b.emit(Op::Call(n, 0), 1);
match run(b) {
VMResult::Error(msg) => assert!(msg.contains("undefined function")),
other => panic!("got {:?}", other),
}
}
#[test]
fn call_invokes_registered_subroutine_and_returns_value() {
let mut b = ChunkBuilder::new();
let n = b.add_name("twice");
b.emit(Op::LoadInt(21), 1);
let call_pos = b.emit(Op::Call(n, 1), 1);
let _ = call_pos;
b.emit(Op::Return, 1);
let sub_ip = b.current_pos();
b.emit(Op::LoadInt(2), 1);
b.emit(Op::Mul, 1);
b.emit(Op::ReturnValue, 1);
b.add_sub_entry(n, sub_ip);
match run(b) {
VMResult::Ok(Value::Int(42)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn call_with_plain_return_truncates_to_stack_base() {
let mut b = ChunkBuilder::new();
let n = b.add_name("noisy");
b.emit(Op::LoadInt(100), 1); b.emit(Op::Call(n, 0), 1);
b.emit(Op::Return, 1);
let sub_ip = b.current_pos();
b.emit(Op::LoadInt(999), 1);
b.emit(Op::LoadInt(888), 1);
b.emit(Op::Return, 1);
b.add_sub_entry(n, sub_ip);
match run(b) {
VMResult::Ok(Value::Int(100)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn test_file_exists_on_cargo_toml_true() {
let mut b = ChunkBuilder::new();
let c = b.add_constant(Value::str("Cargo.toml"));
b.emit(Op::LoadConst(c), 1);
b.emit(Op::TestFile(file_test::EXISTS), 1);
match run(b) {
VMResult::Ok(Value::Bool(true)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn test_file_is_file_on_cargo_toml_true() {
let mut b = ChunkBuilder::new();
let c = b.add_constant(Value::str("Cargo.toml"));
b.emit(Op::LoadConst(c), 1);
b.emit(Op::TestFile(file_test::IS_FILE), 1);
match run(b) {
VMResult::Ok(Value::Bool(true)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn test_file_is_dir_on_src_true() {
let mut b = ChunkBuilder::new();
let c = b.add_constant(Value::str("src"));
b.emit(Op::LoadConst(c), 1);
b.emit(Op::TestFile(file_test::IS_DIR), 1);
match run(b) {
VMResult::Ok(Value::Bool(true)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn test_file_exists_on_nonexistent_path_false() {
let mut b = ChunkBuilder::new();
let c = b.add_constant(Value::str("/no/such/path/zzz_fusevm_test"));
b.emit(Op::LoadConst(c), 1);
b.emit(Op::TestFile(file_test::EXISTS), 1);
match run(b) {
VMResult::Ok(Value::Bool(false)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn test_file_is_file_on_directory_returns_false() {
let mut b = ChunkBuilder::new();
let c = b.add_constant(Value::str("src"));
b.emit(Op::LoadConst(c), 1);
b.emit(Op::TestFile(file_test::IS_FILE), 1);
match run(b) {
VMResult::Ok(Value::Bool(false)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn range_descending_yields_empty() {
let mut b = ChunkBuilder::new();
b.emit(Op::LoadInt(5), 1);
b.emit(Op::LoadInt(2), 1);
b.emit(Op::Range, 1);
match run(b) {
VMResult::Ok(Value::Array(a)) => assert!(a.is_empty()),
other => panic!("got {:?}", other),
}
}
#[test]
fn range_single_element_when_from_equals_to() {
let mut b = ChunkBuilder::new();
b.emit(Op::LoadInt(7), 1);
b.emit(Op::LoadInt(7), 1);
b.emit(Op::Range, 1);
match run(b) {
VMResult::Ok(Value::Array(a)) => {
assert_eq!(a.len(), 1);
assert_eq!(a[0].to_int(), 7);
}
other => panic!("got {:?}", other),
}
}
#[test]
fn slot_is_scoped_to_current_frame() {
let mut b = ChunkBuilder::new();
b.emit(Op::PushFrame, 1);
b.emit(Op::LoadInt(100), 1);
b.emit(Op::SetSlot(0), 1);
b.emit(Op::PushFrame, 1);
b.emit(Op::LoadInt(50), 1);
b.emit(Op::SetSlot(0), 1);
b.emit(Op::PopFrame, 1);
b.emit(Op::GetSlot(0), 1);
match run(b) {
VMResult::Ok(Value::Int(100)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn here_string_pops_target_value() {
let mut b = ChunkBuilder::new();
b.emit(Op::LoadInt(7), 1);
let s = b.add_constant(Value::str("input"));
b.emit(Op::LoadConst(s), 1);
b.emit(Op::HereString, 1);
match run(b) {
VMResult::Ok(Value::Int(7)) => {}
other => panic!("got {:?}", other),
}
}
#[test]
fn chunk_builder_set_source_persists() {
let mut b = ChunkBuilder::new();
b.set_source("test.fuse");
b.emit(Op::LoadInt(0), 1);
let c = b.build();
assert_eq!(c.source.as_str(), "test.fuse");
}
#[test]
fn chunk_builder_add_block_range_grows_block_table() {
let mut b = ChunkBuilder::new();
let i0 = b.add_block_range(0, 10);
let i1 = b.add_block_range(20, 30);
assert_eq!(i0, 0);
assert_eq!(i1, 1);
}