Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions core/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,3 @@ pub use install::AstInstaller;
pub(crate) fn bytes_to_name(bytes: &[u8]) -> String {
String::from_utf8_lossy(bytes).to_string()
}

#[cfg(test)]
mod tests {
use super::bytes_to_name;

#[test]
fn test_bytes_to_name_valid_utf8() {
assert_eq!(bytes_to_name(b"hello"), "hello");
}

#[test]
fn test_bytes_to_name_invalid_utf8_replaced() {
assert_eq!(bytes_to_name(b"hello\xff"), "hello\u{FFFD}");
}
}
30 changes: 0 additions & 30 deletions core/src/env/box_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,33 +87,3 @@ impl BoxManager {
self.boxes.is_empty()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_add_run_prevents_duplicates() {
let mut manager = BoxManager::new();

let id = BoxId(0);
manager.add_run(id);
manager.add_run(id); // Should be ignored

assert_eq!(manager.run_queue.len(), 1);
}

#[test]
fn test_pop_run() {
let mut manager = BoxManager::new();

let id1 = BoxId(0);
let id2 = BoxId(1);
manager.add_run(id1);
manager.add_run(id2);

assert_eq!(manager.pop_run(), Some(id1));
assert_eq!(manager.pop_run(), Some(id2));
assert_eq!(manager.pop_run(), None);
}
}
50 changes: 0 additions & 50 deletions core/src/env/local_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,53 +40,3 @@ impl LocalEnv {
self.locals.iter()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_local_env_default() {
let lenv = LocalEnv::default();
assert_eq!(lenv.get_var("x"), None);
}

#[test]
fn test_local_env() {
let mut lenv = LocalEnv::new();

lenv.new_var("x".to_string(), VertexId(1));
lenv.new_var("y".to_string(), VertexId(2));

assert_eq!(lenv.get_var("x"), Some(VertexId(1)));
assert_eq!(lenv.get_var("y"), Some(VertexId(2)));
assert_eq!(lenv.get_var("z"), None);
}

#[test]
fn test_local_env_remove_var() {
let mut lenv = LocalEnv::new();

lenv.new_var("e".to_string(), VertexId(1));
assert_eq!(lenv.get_var("e"), Some(VertexId(1)));

lenv.remove_var("e");
assert_eq!(lenv.get_var("e"), None);
}

#[test]
fn test_local_env_remove_nonexistent() {
let mut lenv = LocalEnv::new();
lenv.remove_var("x"); // should not panic
}

#[test]
fn test_local_env_override() {
let mut lenv = LocalEnv::new();

lenv.new_var("x".to_string(), VertexId(1));
lenv.new_var("x".to_string(), VertexId(2)); // Override

assert_eq!(lenv.get_var("x"), Some(VertexId(2)));
}
}
152 changes: 0 additions & 152 deletions core/src/env/method_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,6 @@ pub struct ResolutionContext<'a> {
pub extensions: &'a HashMap<String, Vec<String>>,
}

impl<'a> ResolutionContext<'a> {
#[cfg(test)]
pub fn empty() -> Self {
use std::sync::LazyLock;
static EMPTY_VEC_MAP: LazyLock<HashMap<String, Vec<String>>> =
LazyLock::new(HashMap::new);
static EMPTY_STRING_MAP: LazyLock<HashMap<String, String>> =
LazyLock::new(HashMap::new);
Self {
inclusions: &EMPTY_VEC_MAP,
superclass_map: &EMPTY_STRING_MAP,
extensions: &EMPTY_VEC_MAP,
}
}
}

/// Method information
#[derive(Debug, Clone)]
pub struct MethodInfo {
Expand Down Expand Up @@ -187,139 +171,3 @@ impl MethodRegistry {
.find_map(|ty| self.methods.get(&(ty, method_key.clone())))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_register_and_resolve() {
let mut registry = MethodRegistry::new();
registry.register(Type::string(), "length", Type::integer());

let info = registry.resolve(&Type::string(), "length", &ResolutionContext::empty()).unwrap();
assert_eq!(info.return_type.base_class_name(), Some("Integer"));
}

#[test]
fn test_resolve_not_found() {
let registry = MethodRegistry::new();
assert!(registry.resolve(&Type::string(), "unknown", &ResolutionContext::empty()).is_none());
}

#[test]
fn test_register_user_method_and_resolve() {
let mut registry = MethodRegistry::new();
let return_vtx = VertexId(42);
registry.register_user_method(Type::instance("User"), "name", return_vtx, vec![], None);

let info = registry.resolve(&Type::instance("User"), "name", &ResolutionContext::empty()).unwrap();
assert_eq!(info.return_vertex, Some(VertexId(42)));
assert_eq!(info.return_type, Type::Bot);
}

#[test]
fn test_register_user_method_with_param_vertices() {
let mut registry = MethodRegistry::new();
let return_vtx = VertexId(10);
let param_vtxs = vec![VertexId(20), VertexId(21)];
registry.register_user_method(
Type::instance("Calc"),
"add",
return_vtx,
param_vtxs,
None,
);

let info = registry.resolve(&Type::instance("Calc"), "add", &ResolutionContext::empty()).unwrap();
assert_eq!(info.return_vertex, Some(VertexId(10)));
let pvs = info.param_vertices.as_ref().unwrap();
assert_eq!(pvs.len(), 2);
assert_eq!(pvs[0], VertexId(20));
assert_eq!(pvs[1], VertexId(21));
}

// --- Types that skip fallback ---

#[test]
fn test_singleton_type_skips_fallback() {
let mut registry = MethodRegistry::new();
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
assert!(registry.resolve(&Type::singleton("User"), "puts", &ResolutionContext::empty()).is_none());
}

#[test]
fn test_nil_type_skips_fallback() {
let mut registry = MethodRegistry::new();
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
assert!(registry.resolve(&Type::Nil, "puts", &ResolutionContext::empty()).is_none());
}

#[test]
fn test_union_type_skips_fallback() {
let mut registry = MethodRegistry::new();
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
let union_ty = Type::Union(vec![Type::string(), Type::integer()]);
assert!(registry.resolve(&union_ty, "puts", &ResolutionContext::empty()).is_none());
}

#[test]
fn test_bot_type_skips_fallback() {
let mut registry = MethodRegistry::new();
registry.register(Type::instance("Kernel"), "puts", Type::Nil);
assert!(registry.resolve(&Type::Bot, "puts", &ResolutionContext::empty()).is_none());
}

#[test]
fn test_singleton_type_skips_include_fallback() {
// include adds instance methods only — Singleton (class-level) should NOT resolve
let mut registry = MethodRegistry::new();
registry.register(Type::instance("Greetable"), "greet", Type::string());

let mut inclusions = HashMap::new();
inclusions.insert("User".to_string(), vec!["Greetable".to_string()]);
let ctx = ResolutionContext {
inclusions: &inclusions,
superclass_map: &HashMap::new(),
extensions: &HashMap::new(),
};

assert!(registry.resolve(&Type::singleton("User"), "greet", &ctx).is_none());
}

#[test]
fn test_resolve_circular_inheritance_no_infinite_loop() {
// Circular inheritance: A < B < A (should not infinite loop)
let registry = MethodRegistry::new();

let mut superclass_map = HashMap::new();
superclass_map.insert("A".to_string(), "B".to_string());
superclass_map.insert("B".to_string(), "A".to_string());
let ctx = ResolutionContext {
inclusions: &HashMap::new(),
superclass_map: &superclass_map,
extensions: &HashMap::new(),
};

// Should not hang; just returns None
assert!(registry.resolve(&Type::instance("A"), "missing", &ctx).is_none());
}

#[test]
fn test_resolve_extend_does_not_affect_instance() {
let mut registry = MethodRegistry::new();
registry.register(Type::instance("ClassMethods"), "find", Type::string());

let mut extensions = HashMap::new();
extensions.insert("User".to_string(), vec!["ClassMethods".to_string()]);
let ctx = ResolutionContext {
inclusions: &HashMap::new(),
superclass_map: &HashMap::new(),
extensions: &extensions,
};

// Instance type should NOT resolve extended module methods
assert!(registry.resolve(&Type::instance("User"), "find", &ctx).is_none());
}

}
Loading