use super::*;
use crate::ast::{Include, SourceLocation, WordDef};
use crate::stdlib_embed;
use std::path::{Path, PathBuf};
#[test]
fn test_collision_detection_no_collision() {
let words = vec![
WordDef {
name: "foo".to_string(),
effect: None,
body: vec![],
source: Some(SourceLocation::new(PathBuf::from("a.seq"), 1)),
allowed_lints: vec![],
},
WordDef {
name: "bar".to_string(),
effect: None,
body: vec![],
source: Some(SourceLocation::new(PathBuf::from("b.seq"), 1)),
allowed_lints: vec![],
},
];
assert!(check_collisions(&words).is_ok());
}
#[test]
fn test_collision_detection_with_collision() {
let words = vec![
WordDef {
name: "foo".to_string(),
effect: None,
body: vec![],
source: Some(SourceLocation::new(PathBuf::from("a.seq"), 1)),
allowed_lints: vec![],
},
WordDef {
name: "foo".to_string(),
effect: None,
body: vec![],
source: Some(SourceLocation::new(PathBuf::from("b.seq"), 5)),
allowed_lints: vec![],
},
];
let result = check_collisions(&words);
assert!(result.is_err());
let error = result.unwrap_err();
assert!(error.contains("foo"));
assert!(error.contains("a.seq"));
assert!(error.contains("b.seq"));
assert!(error.contains("multiple times"));
}
#[test]
fn test_collision_detection_same_file_different_lines() {
let words = vec![
WordDef {
name: "foo".to_string(),
effect: None,
body: vec![],
source: Some(SourceLocation::new(PathBuf::from("a.seq"), 1)),
allowed_lints: vec![],
},
WordDef {
name: "foo".to_string(),
effect: None,
body: vec![],
source: Some(SourceLocation::new(PathBuf::from("a.seq"), 5)),
allowed_lints: vec![],
},
];
let result = check_collisions(&words);
assert!(result.is_err());
}
#[test]
fn test_embedded_stdlib_imath_available() {
assert!(stdlib_embed::has_stdlib("imath"));
}
#[test]
fn test_embedded_stdlib_resolution() {
let resolver = Resolver::new(None);
let include = Include::Std("imath".to_string());
let result = resolver.resolve_include(&include, Path::new("."));
assert!(result.is_ok());
match result.unwrap() {
ResolvedInclude::Embedded(name, content) => {
assert_eq!(name, "imath");
assert!(content.contains("abs"));
}
ResolvedInclude::FilePath(_) => panic!("Expected embedded, got file path"),
}
}
#[test]
fn test_nonexistent_stdlib_module() {
let resolver = Resolver::new(None);
let include = Include::Std("nonexistent".to_string());
let result = resolver.resolve_include(&include, Path::new("."));
assert!(result.is_err());
assert!(result.unwrap_err().contains("not found"));
}
#[test]
fn test_resolver_with_no_stdlib_path() {
let resolver = Resolver::new(None);
assert!(resolver.stdlib_path.is_none());
}
#[test]
fn test_double_include_prevention_embedded() {
let mut resolver = Resolver::new(None);
let result1 = resolver.process_embedded_include(
"imath",
stdlib_embed::get_stdlib("imath").unwrap(),
Path::new("."),
);
assert!(result1.is_ok());
let content1 = result1.unwrap();
assert!(!content1.words.is_empty());
let result2 = resolver.process_embedded_include(
"imath",
stdlib_embed::get_stdlib("imath").unwrap(),
Path::new("."),
);
assert!(result2.is_ok());
let content2 = result2.unwrap();
assert!(content2.words.is_empty());
assert!(content2.unions.is_empty());
}
#[test]
fn test_cross_directory_include_allowed() {
use std::fs;
use tempfile::tempdir;
let temp = tempdir().unwrap();
let root = temp.path();
let src = root.join("src");
let src_lib = src.join("lib");
let tests = root.join("tests");
fs::create_dir_all(&src_lib).unwrap();
fs::create_dir_all(&tests).unwrap();
fs::write(src_lib.join("helper.seq"), ": helper ( -- Int ) 42 ;\n").unwrap();
let resolver = Resolver::new(None);
let include = Include::Relative("../src/lib/helper".to_string());
let result = resolver.resolve_include(&include, &tests);
assert!(
result.is_ok(),
"Cross-directory include should succeed: {:?}",
result.err()
);
match result.unwrap() {
ResolvedInclude::FilePath(path) => {
assert!(path.ends_with("helper.seq"));
}
ResolvedInclude::Embedded(_, _) => panic!("Expected file path, got embedded"),
}
}
#[test]
fn test_dotdot_within_same_directory_structure() {
use std::fs;
use tempfile::tempdir;
let temp = tempdir().unwrap();
let project = temp.path();
let deep = project.join("a").join("b").join("c");
fs::create_dir_all(&deep).unwrap();
fs::write(project.join("a").join("target.seq"), ": target ( -- ) ;\n").unwrap();
let resolver = Resolver::new(None);
let include = Include::Relative("../../target".to_string());
let result = resolver.resolve_include(&include, &deep);
assert!(
result.is_ok(),
"Include with .. should work: {:?}",
result.err()
);
}
#[test]
fn test_empty_include_path_rejected() {
let resolver = Resolver::new(None);
let include = Include::Relative("".to_string());
let result = resolver.resolve_include(&include, Path::new("."));
assert!(result.is_err(), "Empty include path should be rejected");
assert!(
result.unwrap_err().contains("cannot be empty"),
"Error should mention empty path"
);
}