Skip to main content

rez_next_solver/astar/
test_framework.rs

1//! Test framework for A* search implementation
2
3use super::search_state::{ConflictType, DependencyConflict, SearchState};
4use rez_next_package::{Package, PackageRequirement};
5
6/// Test the basic functionality of SearchState
7pub fn test_search_state_basic() -> Result<(), String> {
8    let state = SearchState::new_initial(vec![]);
9    if state.depth != 0 {
10        return Err("Initial state should have depth 0".to_string());
11    }
12    if !state.resolved_packages.is_empty() {
13        return Err("Initial state should have no resolved packages".to_string());
14    }
15    if !state.conflicts.is_empty() {
16        return Err("Initial state should have no conflicts".to_string());
17    }
18    if !state.is_goal() {
19        return Err("Empty state should be a goal".to_string());
20    }
21    Ok(())
22}
23
24/// Test SearchState with requirements
25pub fn test_search_state_with_requirements() -> Result<(), String> {
26    let req = PackageRequirement::new("python".to_string());
27    let state = SearchState::new_initial(vec![req]);
28
29    if state.is_goal() {
30        return Err("State with pending requirements should not be a goal".to_string());
31    }
32    if state.pending_requirements.len() != 1 {
33        return Err("State should have exactly 1 pending requirement".to_string());
34    }
35    Ok(())
36}
37
38/// Test state transitions
39pub fn test_state_transition() -> Result<(), String> {
40    let req = PackageRequirement::new("python".to_string());
41    let parent = SearchState::new_initial(vec![req.clone()]);
42
43    let pkg = Package::new("python".to_string());
44    let mut child = SearchState::new_from_parent(&parent, pkg, vec![], 1.0);
45    child.remove_requirement(&req);
46
47    if !child.resolved_packages.contains_key("python") {
48        return Err("Python should be resolved in child state".to_string());
49    }
50    if child.depth != 1 {
51        return Err(format!("Child depth should be 1, got {}", child.depth));
52    }
53
54    Ok(())
55}
56
57/// Test conflict detection
58pub fn test_conflict_detection() -> Result<(), String> {
59    let mut state = SearchState::new_initial(vec![]);
60    state.add_conflict(DependencyConflict::new(
61        "conflicting_pkg".to_string(),
62        vec!["req_a".to_string(), "req_b".to_string()],
63        1.0,
64        ConflictType::VersionConflict,
65    ));
66
67    if state.conflicts.is_empty() {
68        return Err("State should have a version conflict".to_string());
69    }
70    // VersionConflict alone doesn't make state invalid
71    if !state.is_valid() {
72        return Err("State with only VersionConflict should still be valid".to_string());
73    }
74
75    let mut state2 = SearchState::new_initial(vec![]);
76    state2.add_conflict(DependencyConflict::new(
77        "missing_pkg".to_string(),
78        vec![],
79        1.0,
80        ConflictType::MissingPackage,
81    ));
82    if state2.is_valid() {
83        return Err("State with MissingPackage conflict should be invalid".to_string());
84    }
85
86    Ok(())
87}
88
89/// Run all framework tests
90pub fn run_framework_tests() -> Result<(), String> {
91    test_search_state_basic()?;
92    test_search_state_with_requirements()?;
93    test_state_transition()?;
94    test_conflict_detection()?;
95    Ok(())
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::astar::StatePool;
102
103    #[test]
104    fn test_run_framework_tests() {
105        run_framework_tests().expect("All framework tests should pass");
106    }
107
108    #[test]
109    fn test_state_pool_basic() {
110        let mut pool = StatePool::new(5);
111        assert_eq!(pool.size(), 0);
112        let s = pool.get_state();
113        pool.return_state(s);
114        assert_eq!(pool.size(), 1);
115    }
116}