#[cfg(test)]
mod tests {
use std::{path::Path, sync::Arc};
use emmylua_parser::VisibilityKind;
use crate::{
Emmyrc, EmmyrcWorkspaceModuleMap, FileId, WorkspaceId,
db_index::{
module::{LuaModuleIndex, ModuleVisibility},
traits::LuaIndex,
},
};
fn create_module() -> LuaModuleIndex {
let mut m = LuaModuleIndex::new();
m.set_module_extract_patterns(["?.lua".to_string(), "?/init.lua".to_string()].to_vec());
m
}
#[test]
fn test_basic() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let module_info = m.get_module(file_id).unwrap();
assert_eq!(module_info.name, "test");
assert_eq!(module_info.full_module_name, "test");
assert_eq!(module_info.visible, ModuleVisibility::Default);
let file_id = FileId { id: 2 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test2/init.lua");
let module_info = m.get_module(file_id).unwrap();
assert_eq!(module_info.name, "test2");
assert_eq!(module_info.full_module_name, "test2");
assert_eq!(module_info.visible, ModuleVisibility::Default);
let file_id = FileId { id: 3 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test3/hhhhiii.lua");
let module_info = m.get_module(file_id).unwrap();
assert_eq!(module_info.name, "hhhhiii");
assert_eq!(module_info.full_module_name, "test3.hhhhiii");
assert_eq!(module_info.visible, ModuleVisibility::Default);
}
#[test]
fn test_multi_workspace() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
m.add_workspace_root(
Path::new("C:/Users/username/Downloads").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let module_info = m.get_module(file_id).unwrap();
assert_eq!(module_info.name, "test");
assert_eq!(module_info.full_module_name, "test");
assert_eq!(module_info.visible, ModuleVisibility::Default);
let file_id = FileId { id: 2 };
m.add_module_by_path(file_id, "C:/Users/username/Downloads/test2/init.lua");
let module_info = m.get_module(file_id).unwrap();
assert_eq!(module_info.name, "test2");
assert_eq!(module_info.full_module_name, "test2");
assert_eq!(module_info.visible, ModuleVisibility::Default);
let file_id = FileId { id: 3 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test3/hhhhiii.lua");
let module_info = m.get_module(file_id).unwrap();
assert_eq!(module_info.name, "hhhhiii");
assert_eq!(module_info.full_module_name, "test3.hhhhiii");
assert_eq!(module_info.visible, ModuleVisibility::Default);
}
#[test]
fn test_find_module() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let module_info = m.find_module("test").unwrap();
assert_eq!(module_info.name, "test");
assert_eq!(module_info.full_module_name, "test");
assert_eq!(module_info.visible, ModuleVisibility::Default);
let file_id = FileId { id: 2 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test2/init.lua");
let module_info = m.find_module("test2").unwrap();
assert_eq!(module_info.name, "test2");
assert_eq!(module_info.full_module_name, "test2");
assert_eq!(module_info.visible, ModuleVisibility::Default);
let file_id = FileId { id: 3 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test3/hhhhiii.lua");
let module_info = m.find_module("test3.hhhhiii").unwrap();
assert_eq!(module_info.name, "hhhhiii");
assert_eq!(module_info.full_module_name, "test3.hhhhiii");
assert_eq!(module_info.visible, ModuleVisibility::Default);
let not_found = m.find_module("test3.hhhhiii.notfound");
assert!(not_found.is_none());
}
#[test]
fn test_find_module_node() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let file_id = FileId { id: 2 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test/aaa.lua");
let file_id = FileId { id: 3 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test/hhhhiii.lua");
let module_node = m.find_module_node("test").unwrap();
assert_eq!(module_node.children.len(), 2);
let first_child = module_node.children.get("aaa");
assert!(first_child.is_some());
let second_child = module_node.children.get("hhhhiii");
assert!(second_child.is_some());
}
#[test]
fn test_set_module_visibility() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
m.set_module_visibility(file_id, ModuleVisibility::Hide);
let module_info = m.get_module(file_id).unwrap();
assert_eq!(module_info.visible, ModuleVisibility::Hide);
}
#[test]
fn test_remove_module() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
m.remove(file_id);
let module_info = m.get_module(file_id);
assert!(module_info.is_none());
let file_id = FileId { id: 2 };
m.add_module_by_path(
file_id,
"C:/Users/username/Documents/test2/aaa/bbb/cccc/dddd.lua",
);
m.remove(file_id);
let module_info = m.get_module(file_id);
assert!(module_info.is_none());
let module_node = m.find_module_node("test2.aaa");
assert!(module_node.is_none());
}
#[test]
fn test_require_fuzzy_match_honors_segment_boundaries() {
let mut m = LuaModuleIndex::new();
m.update_config(Arc::new(Emmyrc::default()));
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(
file_id,
"C:/Users/username/Documents/nvim-cmp/lua/cmp/utils/event.lua",
);
assert!(m.find_module("pckr.event").is_none());
let module_info = m.find_module("event").unwrap();
assert_eq!(module_info.full_module_name, "nvim-cmp.lua.cmp.utils.event");
}
#[test]
fn test_require_fuzzy_match_prefers_shortest_prefix_independent_of_insert_order() {
const PLUGIN_ENTRY: &str = "C:/Users/username/Documents/plugin/treesitter-context.lua";
const LUA_ENTRY: &str = "C:/Users/username/Documents/lua/treesitter-context.lua";
for paths in [[PLUGIN_ENTRY, LUA_ENTRY], [LUA_ENTRY, PLUGIN_ENTRY]] {
let mut m = LuaModuleIndex::new();
m.update_config(Arc::new(Emmyrc::default()));
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
for (file_id, path) in [FileId { id: 1 }, FileId { id: 2 }].into_iter().zip(paths) {
m.add_module_by_path(file_id, path);
}
let module_info = m.find_module("treesitter-context").unwrap();
assert_eq!(module_info.full_module_name, "lua.treesitter-context");
}
}
#[test]
fn test_module_map_applies_to_factorio_require_paths() {
let mut config = Emmyrc::default();
config.workspace.module_map = vec![
EmmyrcWorkspaceModuleMap {
pattern: "^__(.*)__(.*)$".to_string(),
replace: "$1$2".to_string(),
},
EmmyrcWorkspaceModuleMap {
pattern: "^(.*)\\.lua$".to_string(),
replace: "$1".to_string(),
},
];
let mut m = LuaModuleIndex::new();
m.update_config(Arc::new(config));
m.add_workspace_root(
Path::new("C:/Users/username/Documents/mods").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(
file_id,
"C:/Users/username/Documents/mods/signalstrings/signalstrings.lua",
);
for module_path in [
"__signalstrings__/signalstrings.lua",
"__signalstrings__.signalstrings",
"__signalstrings__/signalstrings",
] {
let module_info = m.find_module(module_path).unwrap();
assert_eq!(module_info.file_id, file_id);
assert_eq!(module_info.full_module_name, "signalstrings.signalstrings");
}
}
#[test]
fn test_module_map_keeps_configured_rule_order() {
let mut config = Emmyrc::default();
config.workspace.module_map = vec![
EmmyrcWorkspaceModuleMap {
pattern: "^foo$".to_string(),
replace: "bar".to_string(),
},
EmmyrcWorkspaceModuleMap {
pattern: "^bar$".to_string(),
replace: "baz".to_string(),
},
];
let mut m = LuaModuleIndex::new();
m.update_config(Arc::new(config));
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/bar.lua");
let module_info = m.find_module("foo").unwrap();
assert_eq!(module_info.file_id, file_id);
assert_eq!(module_info.full_module_name, "baz");
}
#[test]
fn test_module_map_exact_match_has_priority_over_fuzzy_match() {
let mut config = Emmyrc::default();
config.workspace.module_map = vec![EmmyrcWorkspaceModuleMap {
pattern: "^foo$".to_string(),
replace: "bar.baz".to_string(),
}];
let mut m = LuaModuleIndex::new();
m.update_config(Arc::new(config));
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let mapped_file_id = FileId { id: 1 };
m.add_module_by_path(mapped_file_id, "C:/Users/username/Documents/bar/baz.lua");
let fuzzy_file_id = FileId { id: 2 };
m.add_module_by_path(fuzzy_file_id, "C:/Users/username/Documents/x/foo.lua");
let module_info = m.find_module("foo").unwrap();
assert_eq!(module_info.file_id, mapped_file_id);
assert_eq!(module_info.full_module_name, "bar.baz");
}
#[test]
fn test_merge_visibility_treats_default_as_neutral_state() {
assert_eq!(
ModuleVisibility::Default.merge(ModuleVisibility::Default),
ModuleVisibility::Default
);
assert_eq!(
ModuleVisibility::Default.merge(ModuleVisibility::Internal),
ModuleVisibility::Internal
);
assert_eq!(
ModuleVisibility::Default.merge(ModuleVisibility::Public),
ModuleVisibility::Public
);
assert_eq!(
ModuleVisibility::Internal.merge(ModuleVisibility::Internal),
ModuleVisibility::Internal
);
assert_eq!(
ModuleVisibility::Public.merge(ModuleVisibility::Internal),
ModuleVisibility::Internal
);
assert_eq!(
ModuleVisibility::Internal.merge(ModuleVisibility::Public),
ModuleVisibility::Public
);
assert_eq!(
ModuleVisibility::Public.merge(ModuleVisibility::Default),
ModuleVisibility::Public
);
assert_eq!(
ModuleVisibility::Internal.merge(ModuleVisibility::Default),
ModuleVisibility::Internal
);
assert_eq!(
ModuleVisibility::Hide.merge(ModuleVisibility::Public),
ModuleVisibility::Hide
);
assert_eq!(
ModuleVisibility::Public.merge(ModuleVisibility::Hide),
ModuleVisibility::Hide
);
}
#[test]
fn test_module_visibility_source_has_higher_priority_than_return_visibility() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
m.set_module_visibility(file_id, ModuleVisibility::Hide);
let module_info = m.get_module_mut(file_id).unwrap();
module_info.merge_visibility(VisibilityKind::Public);
assert_eq!(module_info.visible, ModuleVisibility::Hide);
module_info.merge_visibility(VisibilityKind::Internal);
assert_eq!(module_info.visible, ModuleVisibility::Hide);
}
#[test]
fn test_return_visibility_uses_latest_explicit_state() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let module_info = m.get_module_mut(file_id).unwrap();
assert_eq!(module_info.visible, ModuleVisibility::Default);
module_info.merge_visibility(VisibilityKind::Internal);
assert_eq!(module_info.visible, ModuleVisibility::Internal);
module_info.merge_visibility(VisibilityKind::Public);
assert_eq!(module_info.visible, ModuleVisibility::Public);
}
#[test]
fn test_explicit_internal_can_narrow_public_default_visibility() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let module_info = m.get_module_mut(file_id).unwrap();
assert_eq!(module_info.visible, ModuleVisibility::Default);
module_info.merge_visibility(VisibilityKind::Internal);
assert_eq!(module_info.visible, ModuleVisibility::Internal);
}
#[test]
fn test_explicit_public_preserves_default_public_visibility() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let module_info = m.get_module_mut(file_id).unwrap();
assert_eq!(module_info.visible, ModuleVisibility::Default);
module_info.merge_visibility(VisibilityKind::Public);
assert_eq!(module_info.visible, ModuleVisibility::Public);
}
#[test]
fn test_default_public_visibility_is_requireable_across_workspaces() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
let file_id = FileId { id: 1 };
m.add_module_by_path(file_id, "C:/Users/username/Documents/test.lua");
let module_info = m.get_module(file_id).unwrap();
assert!(module_info.is_requireable_from(WorkspaceId::MAIN));
assert!(module_info.is_requireable_from(WorkspaceId { id: 99 }));
}
#[test]
fn test_sibling_packages_under_same_parent_keep_distinct_package_scopes() {
let mut m = create_module();
m.add_workspace_root_with_import(
Path::new("C:/Users/username/Documents/module").into(),
crate::WorkspaceImport::Package("socket".into()),
WorkspaceId { id: 3 },
);
m.add_workspace_root_with_import(
Path::new("C:/Users/username/Documents/module").into(),
crate::WorkspaceImport::Package("net".into()),
WorkspaceId { id: 4 },
);
let socket_file = FileId { id: 1 };
let net_file = FileId { id: 2 };
m.add_module_by_path(
socket_file,
"C:/Users/username/Documents/module/socket/init.lua",
);
m.add_module_by_path(net_file, "C:/Users/username/Documents/module/net/init.lua");
m.set_module_visibility(socket_file, ModuleVisibility::Internal);
m.set_module_visibility(net_file, ModuleVisibility::Internal);
let socket_info = m.get_module(socket_file).unwrap();
let net_info = m.get_module(net_file).unwrap();
assert_eq!(socket_info.full_module_name, "socket");
assert_eq!(net_info.full_module_name, "net");
assert_eq!(socket_info.workspace_id, WorkspaceId { id: 3 });
assert_eq!(net_info.workspace_id, WorkspaceId { id: 4 });
assert_ne!(socket_info.workspace_id, net_info.workspace_id);
assert!(!socket_info.is_requireable_from(net_info.workspace_id));
assert!(!net_info.is_requireable_from(socket_info.workspace_id));
}
#[test]
fn test_find_module_prefers_non_hidden_candidate_when_multiple_modules_share_name() {
let mut m = create_module();
m.add_workspace_root(
Path::new("C:/Users/username/Documents").into(),
WorkspaceId::MAIN,
);
m.add_workspace_root(
Path::new("C:/Users/username/Downloads").into(),
WorkspaceId::MAIN,
);
let hidden_file_id = FileId { id: 1 };
m.add_module_by_path(hidden_file_id, "C:/Users/username/Documents/test.lua");
m.set_module_visibility(hidden_file_id, ModuleVisibility::Hide);
let visible_file_id = FileId { id: 2 };
m.add_module_by_path(visible_file_id, "C:/Users/username/Downloads/test.lua");
let module_info = m.find_module("test").unwrap();
assert_eq!(module_info.file_id, visible_file_id);
assert_eq!(module_info.visible, ModuleVisibility::Default);
}
}