bquarkz 565fc0e451 dev/pbs (#8)
Co-authored-by: Nilton Constantino <nilton.constantino@visma.com>
Reviewed-on: #8
2026-02-03 15:28:30 +00:00

270 lines
9.5 KiB
Rust

use std::collections::{BTreeMap, HashMap};
use std::path::PathBuf;
use tempfile::tempdir;
use prometeu_compiler::building::output::{compile_project, CompileError, ExportKey, ExportMetadata};
use prometeu_compiler::building::plan::{BuildStep, BuildTarget};
use prometeu_compiler::common::files::FileManager;
use prometeu_compiler::deps::resolver::ProjectId;
use prometeu_compiler::semantics::export_surface::ExportSurfaceKind;
use prometeu_compiler::building::output::CompiledModule;
use std::fs;
#[test]
fn test_local_vs_dependency_conflict() {
let dir = tempdir().unwrap();
let project_dir = dir.path().to_path_buf();
// Dependency: sdk
let dep_id = ProjectId { name: "sdk-impl".to_string(), version: "1.0.0".to_string() };
let mut dep_exports = BTreeMap::new();
dep_exports.insert(ExportKey {
module_path: "math".to_string(), // normalized path
symbol_name: "Vector".to_string(),
kind: ExportSurfaceKind::DeclareType,
}, ExportMetadata {
func_idx: None,
is_host: false,
ty: None,
});
let dep_module = CompiledModule {
project_id: dep_id.clone(),
target: BuildTarget::Main,
exports: dep_exports,
imports: vec![],
const_pool: vec![],
code: vec![],
function_metas: vec![],
debug_info: None,
symbols: vec![],
};
let mut dep_modules = HashMap::new();
dep_modules.insert(dep_id.clone(), dep_module);
// Main project has a LOCAL module named "sdk/math"
// By creating a file in src/main/modules/sdk/math/, the module path becomes "sdk/math"
fs::create_dir_all(project_dir.join("src/main/modules/sdk/math")).unwrap();
fs::write(project_dir.join("src/main/modules/sdk/math/local.pbs"), "pub declare struct Vector(x: int)").unwrap();
let main_id = ProjectId { name: "main".to_string(), version: "0.1.0".to_string() };
let mut deps = BTreeMap::new();
deps.insert("sdk".to_string(), dep_id.clone());
let step = BuildStep {
project_id: main_id,
project_dir,
target: BuildTarget::Main,
sources: vec![PathBuf::from("src/main/modules/sdk/math/local.pbs")],
deps,
};
let mut file_manager = FileManager::new();
let result = compile_project(step, &dep_modules, &mut file_manager);
match result {
Err(CompileError::DuplicateExport { symbol, .. }) => {
assert_eq!(symbol, "Vector");
},
_ => panic!("Expected DuplicateExport error, got {:?}", result),
}
}
#[test]
fn test_aliased_dependency_conflict() {
let dir = tempdir().unwrap();
let project_dir = dir.path().to_path_buf();
// Dependency 1: exports "b/c:Vector"
let dep1_id = ProjectId { name: "p1".to_string(), version: "1.0.0".to_string() };
let mut dep1_exports = BTreeMap::new();
dep1_exports.insert(ExportKey {
module_path: "b/c".to_string(),
symbol_name: "Vector".to_string(),
kind: ExportSurfaceKind::DeclareType,
}, ExportMetadata {
func_idx: None,
is_host: false,
ty: None,
});
let dep1_module = CompiledModule {
project_id: dep1_id.clone(),
target: BuildTarget::Main,
exports: dep1_exports,
imports: vec![],
const_pool: vec![],
code: vec![],
function_metas: vec![],
debug_info: None,
symbols: vec![],
};
// Dependency 2: exports "c:Vector"
let dep2_id = ProjectId { name: "p2".to_string(), version: "1.0.0".to_string() };
let mut dep2_exports = BTreeMap::new();
dep2_exports.insert(ExportKey {
module_path: "c".to_string(),
symbol_name: "Vector".to_string(),
kind: ExportSurfaceKind::DeclareType,
}, ExportMetadata {
func_idx: None,
is_host: false,
ty: None,
});
let dep2_module = CompiledModule {
project_id: dep2_id.clone(),
target: BuildTarget::Main,
exports: dep2_exports,
imports: vec![],
const_pool: vec![],
code: vec![],
function_metas: vec![],
debug_info: None,
symbols: vec![],
};
let mut dep_modules = HashMap::new();
dep_modules.insert(dep1_id.clone(), dep1_module);
dep_modules.insert(dep2_id.clone(), dep2_module);
let main_id = ProjectId { name: "main".to_string(), version: "0.1.0".to_string() };
let mut deps = BTreeMap::new();
deps.insert("a".to_string(), dep1_id.clone());
deps.insert("a/b".to_string(), dep2_id.clone());
let step = BuildStep {
project_id: main_id,
project_dir,
target: BuildTarget::Main,
sources: vec![],
deps,
};
let mut file_manager = FileManager::new();
let result = compile_project(step, &dep_modules, &mut file_manager);
match result {
Err(CompileError::DuplicateExport { symbol, .. }) => {
assert_eq!(symbol, "Vector");
},
_ => panic!("Expected DuplicateExport error, got {:?}", result),
}
}
#[test]
fn test_mixed_main_test_modules() {
let dir = tempdir().unwrap();
let project_dir = dir.path().to_path_buf();
fs::create_dir_all(project_dir.join("src/main/modules/math")).unwrap();
fs::write(project_dir.join("src/main/modules/math/Vector.pbs"), "pub declare struct Vector(x: int)").unwrap();
fs::create_dir_all(project_dir.join("src/test/modules/foo")).unwrap();
fs::write(project_dir.join("src/test/modules/foo/Test.pbs"), "pub declare struct Test(x: int)").unwrap();
let project_id = ProjectId { name: "mixed".to_string(), version: "0.1.0".to_string() };
let step = BuildStep {
project_id,
project_dir,
target: BuildTarget::Main,
sources: vec![
PathBuf::from("src/main/modules/math/Vector.pbs"),
PathBuf::from("src/test/modules/foo/Test.pbs"),
],
deps: BTreeMap::new(),
};
let mut file_manager = FileManager::new();
let compiled = compile_project(step, &HashMap::new(), &mut file_manager).unwrap();
// Both should be in exports with normalized paths
assert!(compiled.exports.keys().any(|k| k.module_path == "math"));
assert!(compiled.exports.keys().any(|k| k.module_path == "foo"));
}
#[test]
fn test_module_merging_same_directory() {
let dir = tempdir().unwrap();
let project_dir = dir.path().to_path_buf();
fs::create_dir_all(project_dir.join("src/main/modules/gfx")).unwrap();
fs::write(project_dir.join("src/main/modules/gfx/api.pbs"), "pub declare struct Gfx(id: int)").unwrap();
fs::write(project_dir.join("src/main/modules/gfx/colors.pbs"), "pub declare struct Color(r: int)").unwrap();
let project_id = ProjectId { name: "merge".to_string(), version: "0.1.0".to_string() };
let step = BuildStep {
project_id,
project_dir,
target: BuildTarget::Main,
sources: vec![
PathBuf::from("src/main/modules/gfx/api.pbs"),
PathBuf::from("src/main/modules/gfx/colors.pbs"),
],
deps: BTreeMap::new(),
};
let mut file_manager = FileManager::new();
let compiled = compile_project(step, &HashMap::new(), &mut file_manager).unwrap();
// Both should be in the same module "gfx"
assert!(compiled.exports.keys().any(|k| k.module_path == "gfx" && k.symbol_name == "Gfx"));
assert!(compiled.exports.keys().any(|k| k.module_path == "gfx" && k.symbol_name == "Color"));
}
#[test]
fn test_duplicate_symbol_in_same_module_different_files() {
let dir = tempdir().unwrap();
let project_dir = dir.path().to_path_buf();
fs::create_dir_all(project_dir.join("src/main/modules/gfx")).unwrap();
fs::write(project_dir.join("src/main/modules/gfx/a.pbs"), "pub declare struct Gfx(id: int)").unwrap();
fs::write(project_dir.join("src/main/modules/gfx/b.pbs"), "pub declare struct Gfx(id: int)").unwrap();
let project_id = ProjectId { name: "dup".to_string(), version: "0.1.0".to_string() };
let step = BuildStep {
project_id,
project_dir,
target: BuildTarget::Main,
sources: vec![
PathBuf::from("src/main/modules/gfx/a.pbs"),
PathBuf::from("src/main/modules/gfx/b.pbs"),
],
deps: BTreeMap::new(),
};
let mut file_manager = FileManager::new();
let result = compile_project(step, &HashMap::new(), &mut file_manager);
assert!(result.is_err());
// Should be a frontend error (duplicate symbol)
}
#[test]
fn test_root_module_merging() {
let dir = tempdir().unwrap();
let project_dir = dir.path().to_path_buf();
fs::create_dir_all(project_dir.join("src/main/modules")).unwrap();
fs::write(project_dir.join("src/main/modules/main.pbs"), "pub declare struct Main(id: int)").unwrap();
fs::write(project_dir.join("src/main/modules/utils.pbs"), "pub declare struct Utils(id: int)").unwrap();
let project_id = ProjectId { name: "root-merge".to_string(), version: "0.1.0".to_string() };
let step = BuildStep {
project_id,
project_dir,
target: BuildTarget::Main,
sources: vec![
PathBuf::from("src/main/modules/main.pbs"),
PathBuf::from("src/main/modules/utils.pbs"),
],
deps: BTreeMap::new(),
};
let mut file_manager = FileManager::new();
let compiled = compile_project(step, &HashMap::new(), &mut file_manager).unwrap();
// Both should be in the root module ""
assert!(compiled.exports.keys().any(|k| k.module_path == "" && k.symbol_name == "Main"));
assert!(compiled.exports.keys().any(|k| k.module_path == "" && k.symbol_name == "Utils"));
}