181 lines
5.6 KiB
Rust
181 lines
5.6 KiB
Rust
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
use crate::interp::*;
|
|
|
|
#[allow(unused_imports)]
|
|
use super::helpers::*;
|
|
|
|
fn tmp_dir(tag: &str) -> PathBuf {
|
|
let p = std::env::temp_dir().join(format!("acord-loader-{}-{}", tag, std::process::id()));
|
|
let _ = fs::remove_dir_all(&p);
|
|
fs::create_dir_all(&p).unwrap();
|
|
p
|
|
}
|
|
|
|
#[test]
|
|
fn load_single_cord_file() {
|
|
let dir = tmp_dir("single");
|
|
fs::write(dir.join("mathy.cord"), "let pi_squared = 9.8696\nfn double(x) { x * 2 }").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.add_module_path("mathy", dir.join("mathy.cord"));
|
|
i.exec("use mathy").unwrap();
|
|
|
|
assert!(matches!(i.get_var("pi_squared"), Some(Value::Number(n)) if (n - 9.8696).abs() < 1e-9));
|
|
let v = i.eval("double(21)").unwrap();
|
|
assert!(matches!(v, Value::Number(n) if n == 42.0));
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn load_dir_with_mod_cord() {
|
|
let dir = tmp_dir("withmod");
|
|
let lib = dir.join("lib");
|
|
fs::create_dir_all(&lib).unwrap();
|
|
fs::write(lib.join("mod.cord"), "fn shout(s) { s + \"!\" }").unwrap();
|
|
fs::write(lib.join("other.cord"), "// ignored when mod.cord present").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.add_module_path("lib", &lib);
|
|
i.exec("use lib").unwrap();
|
|
|
|
let v = i.eval("shout(\"hi\")").unwrap();
|
|
assert!(matches!(v, Value::Str(ref s) if s == "hi!"));
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn load_dir_concats_cord_files_alphabetically() {
|
|
let dir = tmp_dir("concat");
|
|
let lib = dir.join("lib");
|
|
fs::create_dir_all(&lib).unwrap();
|
|
fs::write(lib.join("a.cord"), "let a_val = 1").unwrap();
|
|
fs::write(lib.join("b.cord"), "let b_val = 2").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.add_module_path("lib", &lib);
|
|
i.exec("use lib").unwrap();
|
|
|
|
assert!(matches!(i.get_var("a_val"), Some(Value::Number(n)) if n == 1.0));
|
|
assert!(matches!(i.get_var("b_val"), Some(Value::Number(n)) if n == 2.0));
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn use_item_imports_only_named_binding() {
|
|
let dir = tmp_dir("filter");
|
|
fs::write(dir.join("utils.cord"), "let alpha = 1\nlet beta = 2\nlet gamma = 3").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.add_module_path("utils", dir.join("utils.cord"));
|
|
i.exec("use utils::beta").unwrap();
|
|
|
|
assert!(i.get_var("alpha").is_none());
|
|
assert!(matches!(i.get_var("beta"), Some(Value::Number(n)) if n == 2.0));
|
|
assert!(i.get_var("gamma").is_none());
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn use_wildcard_imports_everything() {
|
|
let dir = tmp_dir("wildcard");
|
|
fs::write(dir.join("utils.cord"), "let alpha = 1\nlet beta = 2").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.add_module_path("utils", dir.join("utils.cord"));
|
|
i.exec("use utils::*").unwrap();
|
|
|
|
assert!(matches!(i.get_var("alpha"), Some(Value::Number(n)) if n == 1.0));
|
|
assert!(matches!(i.get_var("beta"), Some(Value::Number(n)) if n == 2.0));
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn sub_namespace_override_loads_distinct_source() {
|
|
let dir = tmp_dir("subns");
|
|
fs::write(dir.join("main.cord"), "let from_main = true").unwrap();
|
|
fs::write(dir.join("sub.cord"), "let from_sub = true").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.add_module_path("lib", dir.join("main.cord"));
|
|
i.add_module_subpath("lib", "extra", dir.join("sub.cord"));
|
|
i.exec("use lib::extra").unwrap();
|
|
|
|
assert!(matches!(i.get_var("from_sub"), Some(Value::Bool(true))));
|
|
assert!(i.get_var("from_main").is_none());
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn unregistered_module_no_ops() {
|
|
let mut i = Interpreter::new();
|
|
let result = i.exec("use spice");
|
|
assert!(result.is_ok());
|
|
assert!(i.spice_enabled());
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_item_errors() {
|
|
let dir = tmp_dir("missing");
|
|
fs::write(dir.join("mod.cord"), "let present = 1").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.add_module_path("mod", dir.join("mod.cord"));
|
|
let err = i.exec("use mod::absent").unwrap_err();
|
|
assert!(err.contains("absent"));
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|
|
|
|
#[test]
|
|
fn exports_carry_methods_and_traits() {
|
|
let mut a = Interpreter::new();
|
|
a.exec("trait Named { fn show(self) }").unwrap();
|
|
a.exec("impl Named for Thing { fn show(self) { return self.name } }").unwrap();
|
|
a.exec("impl Thing { fn new(name) { return {__type: \"Thing\", name: name} } }").unwrap();
|
|
|
|
let exports = a.exports();
|
|
assert!(exports.traits.contains_key("Named"));
|
|
assert!(exports.methods.contains_key(&("Thing".to_string(), "show".to_string())));
|
|
assert!(exports.methods.contains_key(&("Thing".to_string(), "new".to_string())));
|
|
|
|
let mut b = Interpreter::new();
|
|
b.import_all(&exports);
|
|
let v = b.eval("Thing::new(\"box\").show()").unwrap();
|
|
assert!(matches!(v, Value::Str(ref s) if s == "box"));
|
|
}
|
|
|
|
#[test]
|
|
fn load_module_hooks_inherit_into_subinterp() {
|
|
use std::rc::Rc;
|
|
|
|
struct ConstHook;
|
|
impl InterpreterHook for ConstHook {
|
|
fn name(&self) -> &str { "const_hook" }
|
|
fn try_call(&self, _i: &mut Interpreter, name: &str, _args: &[Expr], _depth: u32)
|
|
-> Option<Result<Value, String>>
|
|
{
|
|
if name == "magic" { Some(Ok(Value::Number(99.0))) } else { None }
|
|
}
|
|
}
|
|
|
|
let dir = tmp_dir("hookinherit");
|
|
fs::write(dir.join("lib.cord"), "let secret = magic()").unwrap();
|
|
|
|
let mut i = Interpreter::new();
|
|
i.register_hook(Rc::new(ConstHook)).unwrap();
|
|
i.add_module_path("lib", dir.join("lib.cord"));
|
|
i.exec("use lib").unwrap();
|
|
|
|
assert!(matches!(i.get_var("secret"), Some(Value::Number(n)) if n == 99.0));
|
|
|
|
fs::remove_dir_all(&dir).ok();
|
|
}
|