mod analyzer;
mod graph;
mod types;
pub use analyzer::DependencyAnalyzer;
pub use graph::AssemblyDependencyGraph;
pub use types::{
AssemblyDependency, DependencyResolutionState, DependencyResolveContext, DependencySource,
DependencyType, VersionRequirement,
};
#[cfg(test)]
mod tests {
use std::{sync::Arc, thread};
use super::*;
use crate::{
metadata::{dependencies::DependencyType, identity::AssemblyVersion},
test::helpers::dependencies::{
create_test_assembly_ref, create_test_dependency, create_test_identity,
},
};
#[test]
fn test_cross_module_integration() {
let graph = Arc::new(AssemblyDependencyGraph::new());
let analyzer = DependencyAnalyzer::new(graph.clone());
let app = create_test_identity("MyApplication", 1, 0);
let system_dep = create_test_dependency("System", DependencyType::Reference);
let core_dep = create_test_dependency("System.Core", DependencyType::Reference);
analyzer
.dependency_graph()
.add_dependency_with_source(&app, system_dep)
.unwrap();
analyzer
.dependency_graph()
.add_dependency_with_source(&app, core_dep)
.unwrap();
assert_eq!(graph.assembly_count(), 3); assert_eq!(graph.dependency_count(), 2);
let deps = graph.get_dependencies(&app);
assert_eq!(deps.len(), 2);
let order = graph.topological_order().unwrap();
assert_eq!(order.len(), 3);
let app_pos = order
.iter()
.position(|id| id.name == "MyApplication")
.unwrap();
assert_eq!(app_pos, 2); }
#[test]
fn test_cycle_detection_across_modules() {
let graph = Arc::new(AssemblyDependencyGraph::new());
let a = create_test_identity("AssemblyA", 1, 0);
let b = create_test_identity("AssemblyB", 1, 0);
let c = create_test_identity("AssemblyC", 1, 0);
let b_dep = AssemblyDependency {
source: DependencySource::AssemblyRef(create_test_assembly_ref("AssemblyB")),
target_identity: b.clone(),
dependency_type: DependencyType::Reference,
version_requirement: VersionRequirement::Compatible,
is_optional: false,
resolution_state: DependencyResolutionState::Unresolved,
};
let c_dep = AssemblyDependency {
source: DependencySource::AssemblyRef(create_test_assembly_ref("AssemblyC")),
target_identity: c.clone(),
dependency_type: DependencyType::Friend, version_requirement: VersionRequirement::Exact,
is_optional: true,
resolution_state: DependencyResolutionState::Unresolved,
};
let a_dep = AssemblyDependency {
source: DependencySource::AssemblyRef(create_test_assembly_ref("AssemblyA")),
target_identity: a.clone(),
dependency_type: DependencyType::TypeForwarding, version_requirement: VersionRequirement::Minimum(AssemblyVersion::new(1, 0, 0, 0)),
is_optional: false,
resolution_state: DependencyResolutionState::Unresolved,
};
graph.add_dependency_with_source(&a, b_dep).unwrap();
graph.add_dependency_with_source(&b, c_dep).unwrap();
graph.add_dependency_with_source(&c, a_dep).unwrap();
let cycles = graph.find_cycles().unwrap();
assert!(cycles.is_some());
let cycle = cycles.unwrap();
assert!(cycle.len() >= 3);
let topo_result = graph.topological_order();
assert!(topo_result.is_ok());
let order = topo_result.unwrap();
assert_eq!(order.len(), 3); }
#[test]
fn test_concurrent_operations_integration() {
let graph = Arc::new(AssemblyDependencyGraph::new());
let mut handles = vec![];
for i in 0..5 {
let graph_clone = graph.clone();
let handle = thread::spawn(move || {
let app = create_test_identity(&format!("App{}", i), 1, 0);
let lib_dep = create_test_dependency("SharedLib", DependencyType::Reference);
let core_dep = create_test_dependency("System.Core", DependencyType::Reference);
graph_clone
.add_dependency_with_source(&app, lib_dep)
.unwrap();
graph_clone
.add_dependency_with_source(&app, core_dep)
.unwrap();
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(graph.assembly_count(), 7); assert_eq!(graph.dependency_count(), 10);
let cycles = graph.find_cycles().unwrap();
assert!(cycles.is_none());
let order = graph.topological_order().unwrap();
assert_eq!(order.len(), 7);
}
}