#[path = "../common/mod.rs"]
mod common;
use std::path::{Path, PathBuf};
use common::*;
fn temp_module_root(prefix: &str) -> PathBuf {
let unique = format!(
"{prefix}_{}_{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("clock should be valid")
.as_nanos()
);
let root = std::env::temp_dir().join(unique);
std::fs::create_dir_all(&root).expect("temp module root should be created");
root
}
fn write_source(path: &Path, source: &str, description: &str) {
std::fs::write(path, source).unwrap_or_else(|err| panic!("{description} should write: {err}"));
}
fn remove_module_root(root: &Path) {
let _ = std::fs::remove_dir_all(root);
}
#[test]
fn compile_source_file_module_override_path_redirects_import_spec() {
let root = temp_module_root("vm_rustscript_module_override_test");
let override_module_path = root.join("edge_http_upstream_override.rss");
write_source(
&override_module_path,
r#"
pub fn as_stream() {
"override-body";
}
"#,
"override module source",
);
let main_path = root.join("main.rss");
write_source(
&main_path,
r#"
use edge::http::upstream as upstream;
upstream::as_stream();
"#,
"main source",
);
let options = CompileSourceFileOptions::new()
.with_module_override_path("edge/http/upstream.rss", &override_module_path);
let compiled =
compile_source_file_with_options(&main_path, options).expect("compile should succeed");
assert!(
compiled.functions.is_empty(),
"override module functions should be inlined into root program"
);
let mut vm = Vm::new(compiled.program);
let status = vm.run().expect("vm should run");
assert_eq!(status, VmStatus::Halted);
assert_eq!(vm.stack(), &[Value::string("override-body")]);
remove_module_root(&root);
}
#[test]
fn compile_source_file_rustscript_named_import_is_selective() {
let root = temp_module_root("vm_rustscript_selective_import_test");
let module_path = root.join("module.rss");
write_source(
&module_path,
r#"
pub fn add_one(x) {
x + 1;
}
pub fn add_two(x) {
x + 2;
}
"#,
"module source",
);
let main_path = root.join("main.rss");
write_source(
&main_path,
r#"
use module::{add_one};
add_two(40);
"#,
"main source",
);
let err = match compile_source_file(main_path.as_path()) {
Ok(_) => panic!("selective import should not expose unlisted exports"),
Err(err) => err,
};
assert!(
matches!(
err,
vm::SourcePathError::Source(vm::SourceError::Parse(vm::ParseError { ref message, .. }))
if message.contains("unknown function 'add_two'")
),
"expected unknown function error, got {err:?}"
);
remove_module_root(&root);
}
#[test]
fn compile_source_file_rustscript_named_import_preserves_generic_function_type_params() {
let root = temp_module_root("vm_rustscript_generic_named_import_test");
let module_path = root.join("module.rss");
write_source(
&module_path,
r#"
struct Box<T> { value: T }
pub fn wrap<T>(value: T) {
let copied = value;
let out: Box<T> = { value: copied };
out
}
"#,
"module source",
);
let main_path = root.join("main.rss");
write_source(
&main_path,
r#"
use module::{wrap};
let wrapped = wrap::<string>("hello");
wrapped.value.length + 1;
"#,
"main source",
);
let compiled = compile_source_file(main_path.as_path()).expect("compile should succeed");
assert!(
compiled.functions.is_empty(),
"generic imported RustScript functions should inline without host imports"
);
let mut vm = Vm::new(compiled.program);
let status = vm.run().expect("vm should run");
assert_eq!(status, VmStatus::Halted);
assert_eq!(vm.stack(), &[Value::Int(6)]);
remove_module_root(&root);
}
#[test]
fn compile_source_file_rustscript_module_exports_only_pub_functions() {
let root = temp_module_root("vm_rustscript_pub_export_test");
let module_path = root.join("module.rss");
write_source(
&module_path,
r#"
fn private_add(x) {
x + 1;
}
pub fn public_add(x) {
private_add(x);
}
"#,
"module source",
);
let ok_main_path = root.join("main_ok.rss");
write_source(
&ok_main_path,
r#"
use module;
public_add(41);
"#,
"ok main source",
);
let compiled = compile_source_file(ok_main_path.as_path()).expect("compile should succeed");
assert!(
compiled.functions.is_empty(),
"pure RustScript function module should not require host imports"
);
let mut vm = Vm::new(compiled.program);
let status = vm.run().expect("vm should run");
assert_eq!(status, VmStatus::Halted);
assert_eq!(vm.stack(), &[Value::Int(42)]);
let bad_main_path = root.join("main_bad.rss");
write_source(
&bad_main_path,
r#"
use module;
private_add(41);
"#,
"bad main source",
);
let err = match compile_source_file(bad_main_path.as_path()) {
Ok(_) => panic!("private import should fail"),
Err(err) => err,
};
assert!(
matches!(
err,
vm::SourcePathError::Source(vm::SourceError::Parse(vm::ParseError { ref message, .. }))
if message.contains("unknown function 'private_add'")
),
"expected unknown function error, got {err:?}"
);
remove_module_root(&root);
}
#[test]
fn rss_function_definition_is_inlined_without_host_imports() {
let source = r#"
fn eq(lhs, rhs) {
lhs == rhs;
}
fn is_empty(value) {
eq(value, "");
}
pub fn non_empty(value) {
eq(is_empty(value), false);
}
non_empty("x");
"#;
let compiled = compile_source(source).expect("compile should succeed");
assert!(
compiled.functions.is_empty(),
"rss-defined functions should not be emitted as host imports"
);
let mut vm = Vm::new(compiled.program);
let status = vm.run().expect("vm should run");
assert_eq!(status, VmStatus::Halted);
assert_eq!(vm.stack(), &[Value::Bool(true)]);
}
#[test]
fn compile_source_file_imported_module_slice_hidden_bindings_work() {
let root = temp_module_root("vm_rustscript_imported_slice_test");
let module_path = root.join("module.rss");
write_source(
&module_path,
r#"
pub fn tail_len(text) {
text[1:].length + 1;
}
"#,
"module source",
);
let main_path = root.join("main.rss");
write_source(
&main_path,
r#"
use module;
tail_len("abcd");
"#,
"main source",
);
let compiled = compile_source_file(main_path.as_path()).expect("compile should succeed");
let mut vm = Vm::new(compiled.program);
let status = vm.run().expect("vm should run");
assert_eq!(status, VmStatus::Halted);
assert_eq!(vm.stack(), &[Value::Int(4)]);
remove_module_root(&root);
}
#[test]
fn compile_source_file_imported_module_dynamic_slice_end_bindings_work() {
let root = temp_module_root("vm_rustscript_imported_dynamic_slice_test");
let module_path = root.join("module.rss");
write_source(
&module_path,
r#"
pub fn first_hex(text, i) {
let hex_lookup = {
"a": 10,
"b": 11
};
hex_lookup[text[i:(i + 1)]];
}
"#,
"module source",
);
let main_path = root.join("main.rss");
write_source(
&main_path,
r#"
use module;
first_hex("ab", 0);
"#,
"main source",
);
let compiled = compile_source_file(main_path.as_path()).expect("compile should succeed");
let mut vm = Vm::new(compiled.program);
let status = vm.run().expect("vm should run");
assert_eq!(status, VmStatus::Halted);
assert_eq!(vm.stack(), &[Value::Int(10)]);
remove_module_root(&root);
}