296 lines
10 KiB
Rust
296 lines
10 KiB
Rust
use prometeu_compiler::building::output::CompiledModule;
|
|
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::ProjectKey;
|
|
use prometeu_compiler::semantics::export_surface::ExportSurfaceKind;
|
|
use prometeu_compiler::frontends::pbs::adapter::PbsFrontendAdapter;
|
|
use prometeu_analysis::ids::ProjectId;
|
|
use std::collections::{BTreeMap, HashMap};
|
|
use std::path::PathBuf;
|
|
use tempfile::tempdir;
|
|
|
|
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_key = ProjectKey { name: "sdk-impl".to_string(), version: "1.0.0".to_string() };
|
|
let dep_id = ProjectId(0);
|
|
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,
|
|
project_key: dep_key.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<ProjectId, CompiledModule> = HashMap::new();
|
|
dep_modules.insert(dep_id, 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_key = ProjectKey { name: "main".to_string(), version: "0.1.0".to_string() };
|
|
let main_id = ProjectId(1);
|
|
let mut deps: BTreeMap<String, ProjectId> = BTreeMap::new();
|
|
deps.insert("sdk".to_string(), ProjectId(0));
|
|
|
|
let step = BuildStep {
|
|
project_id: main_id,
|
|
project_key: main_key,
|
|
project_dir,
|
|
target: BuildTarget::Main,
|
|
sources: vec![PathBuf::from("src/main/modules/sdk/math/local.pbs")],
|
|
deps,
|
|
};
|
|
|
|
let mut file_manager = FileManager::new();
|
|
let fe = PbsFrontendAdapter;
|
|
let result = compile_project(step, &dep_modules, &fe, &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_key = ProjectKey { name: "p1".to_string(), version: "1.0.0".to_string() };
|
|
let dep1_id = ProjectId(0);
|
|
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,
|
|
project_key: dep1_key.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_key = ProjectKey { name: "p2".to_string(), version: "1.0.0".to_string() };
|
|
let dep2_id = ProjectId(1);
|
|
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,
|
|
project_key: dep2_key.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<ProjectId, CompiledModule> = HashMap::new();
|
|
dep_modules.insert(dep1_id, dep1_module);
|
|
dep_modules.insert(dep2_id, dep2_module);
|
|
|
|
let main_key = ProjectKey { name: "main".to_string(), version: "0.1.0".to_string() };
|
|
let main_id = ProjectId(2);
|
|
let mut deps: BTreeMap<String, ProjectId> = BTreeMap::new();
|
|
deps.insert("a".to_string(), ProjectId(0));
|
|
deps.insert("a/b".to_string(), ProjectId(1));
|
|
|
|
let step = BuildStep {
|
|
project_id: main_id,
|
|
project_key: main_key,
|
|
project_dir,
|
|
target: BuildTarget::Main,
|
|
sources: vec![],
|
|
deps,
|
|
};
|
|
|
|
let mut file_manager = FileManager::new();
|
|
let fe = PbsFrontendAdapter;
|
|
let result = compile_project(step, &dep_modules, &fe, &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_key = ProjectKey { name: "mixed".to_string(), version: "0.1.0".to_string() };
|
|
let project_id = ProjectId(0);
|
|
let step = BuildStep {
|
|
project_id,
|
|
project_key,
|
|
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 fe = PbsFrontendAdapter;
|
|
let compiled = compile_project(step, &HashMap::new(), &fe, &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_key = ProjectKey { name: "merge".to_string(), version: "0.1.0".to_string() };
|
|
let project_id = ProjectId(0);
|
|
let step = BuildStep {
|
|
project_id,
|
|
project_key,
|
|
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 fe = PbsFrontendAdapter;
|
|
let compiled = compile_project(step, &HashMap::new(), &fe, &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_key = ProjectKey { name: "dup".to_string(), version: "0.1.0".to_string() };
|
|
let project_id = ProjectId(0);
|
|
let step = BuildStep {
|
|
project_id,
|
|
project_key,
|
|
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 fe = PbsFrontendAdapter;
|
|
let result = compile_project(step, &HashMap::new(), &fe, &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_key = ProjectKey { name: "root-merge".to_string(), version: "0.1.0".to_string() };
|
|
let project_id = ProjectId(0);
|
|
let step = BuildStep {
|
|
project_id,
|
|
project_key,
|
|
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 fe = PbsFrontendAdapter;
|
|
let compiled = compile_project(step, &HashMap::new(), &fe, &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"));
|
|
}
|