use super::*;
use crate::{Goal, GoalStatus};
#[test]
fn test_goal_decomposition() {
let mut solver = HierarchicalGoalSolver::new();
for rule in default_decomposition_rules() {
solver.register_rule(rule);
}
let goal = Goal::maintain("temperature", 20.0..25.0);
let id = solver.add_goal(goal);
let result = solver.decompose(&id).unwrap();
assert!(result.subgoals.len() >= 2);
assert_eq!(result.parent_id, id);
for subgoal in &result.subgoals {
assert_eq!(subgoal.parent.as_ref(), Some(&id));
}
}
#[test]
fn test_progress_propagation() {
let mut solver = HierarchicalGoalSolver::new();
let parent = Goal::perform("complete_task");
let parent_id = solver.add_goal(parent);
let mut sub1 = Goal::perform("subtask1");
sub1.parent = Some(parent_id.clone());
sub1.set_progress(1.0);
sub1.mark_achieved();
let sub1_id = solver.add_goal(sub1);
let mut sub2 = Goal::perform("subtask2");
sub2.parent = Some(parent_id.clone());
sub2.set_progress(0.0);
let sub2_id = solver.add_goal(sub2);
if let Some(parent) = solver.get_goal_mut(&parent_id) {
parent.add_subgoal(&sub1_id);
parent.add_subgoal(&sub2_id);
}
let progress = solver.get_progress(&parent_id);
assert!((progress - 0.5).abs() < 0.01);
if let Some(sub) = solver.get_goal_mut(&sub2_id) {
sub.set_progress(1.0);
sub.mark_achieved();
}
let progress = solver.get_progress(&parent_id);
assert!((progress - 1.0).abs() < 0.01);
}
#[test]
fn test_executable_goals() {
let mut solver = HierarchicalGoalSolver::new();
let goal1 = Goal::perform("simple_task");
let id1 = solver.add_goal(goal1);
solver.activate_goal(&id1);
let parent = Goal::perform("complex_task");
let parent_id = solver.add_goal(parent.clone());
let mut sub1 = Goal::perform("subtask");
sub1.parent = Some(parent_id.clone());
let sub1_id = solver.add_goal(sub1);
if let Some(p) = solver.get_goal_mut(&parent_id) {
p.add_subgoal(&sub1_id);
p.activate();
}
let executable = solver.get_executable_goals();
assert!(executable.len() >= 1);
assert!(executable.iter().any(|g| g.id == id1));
}
#[test]
fn test_conflict_detection() {
let mut solver = HierarchicalGoalSolver::new();
let mut goal1 = Goal::maximize("efficiency");
goal1.activate();
let id1 = solver.add_goal(goal1);
solver.activate_goal(&id1);
let mut goal2 = Goal::minimize("efficiency");
goal2.activate();
let id2 = solver.add_goal(goal2);
solver.activate_goal(&id2);
let conflicts = solver.detect_conflicts();
assert!(!conflicts.is_empty());
assert!(conflicts.iter().any(|c| {
c.conflict_type == ConflictType::MutuallyExclusive
&& ((c.goal1 == id1 && c.goal2 == id2) || (c.goal1 == id2 && c.goal2 == id1))
}));
}
#[test]
fn test_conflict_resolution() {
let mut solver = HierarchicalGoalSolver::new();
let mut goal1 = Goal::perform("task1");
goal1.activate();
let id1 = solver.add_goal(goal1);
solver.activate_goal(&id1);
let mut goal2 = Goal::perform("task2");
goal2.activate();
let id2 = solver.add_goal(goal2);
solver.activate_goal(&id2);
let conflict = GoalConflict {
goal1: id1.clone(),
goal2: id2.clone(),
conflict_type: ConflictType::ResourceContention,
};
solver.resolve_conflict(&conflict, ConflictResolution::PrioritizeFirst);
assert_eq!(solver.get_goal(&id2).unwrap().status, GoalStatus::OnHold);
}
#[test]
fn test_mark_achieved_propagation() {
let mut solver = HierarchicalGoalSolver::new();
let parent = Goal::perform("parent_task");
let parent_id = solver.add_goal(parent);
let mut sub1 = Goal::perform("sub1");
sub1.parent = Some(parent_id.clone());
let sub1_id = solver.add_goal(sub1);
let mut sub2 = Goal::perform("sub2");
sub2.parent = Some(parent_id.clone());
let sub2_id = solver.add_goal(sub2);
if let Some(p) = solver.get_goal_mut(&parent_id) {
p.add_subgoal(&sub1_id);
p.add_subgoal(&sub2_id);
}
solver.mark_achieved(&sub1_id);
assert_eq!(
solver.get_goal(&sub1_id).unwrap().status,
GoalStatus::Achieved
);
assert_ne!(
solver.get_goal(&parent_id).unwrap().status,
GoalStatus::Achieved
);
let affected = solver.mark_achieved(&sub2_id);
assert!(affected.len() >= 2);
assert_eq!(
solver.get_goal(&parent_id).unwrap().status,
GoalStatus::Achieved
);
}
#[test]
fn test_decompose_all() {
let mut solver = HierarchicalGoalSolver::new();
for rule in default_decomposition_rules() {
solver.register_rule(rule);
}
let goal1 = Goal::maintain("temperature", 20.0..25.0);
solver.add_goal(goal1);
let goal2 = Goal::maximize("efficiency");
solver.add_goal(goal2);
let goal3 = Goal::avoid("overheating");
solver.add_goal(goal3);
let results = solver.decompose_all();
assert_eq!(results.len(), 3);
for result in results {
assert!(!result.subgoals.is_empty());
}
}
#[test]
fn test_goal_tree_operations() {
let mut tree = GoalTree::new();
tree.add_root("root1".to_string());
tree.add_root("root2".to_string());
tree.add_child("root1".to_string(), "child1".to_string());
tree.add_child("root1".to_string(), "child2".to_string());
tree.add_child("child1".to_string(), "grandchild1".to_string());
assert_eq!(tree.root_goals().len(), 2);
assert_eq!(tree.get_children(&"root1".to_string()).unwrap().len(), 2);
assert_eq!(tree.get_parent(&"child1".to_string()).unwrap(), "root1");
assert_eq!(
tree.get_parent(&"grandchild1".to_string()).unwrap(),
"child1"
);
}
#[test]
fn test_topological_sort() {
let mut tree = GoalTree::new();
tree.add_root("goal1".to_string());
tree.add_child("goal1".to_string(), "goal2".to_string());
tree.add_child("goal2".to_string(), "goal3".to_string());
tree.add_child("goal1".to_string(), "goal4".to_string());
let sorted = tree.topological_sort();
assert!(sorted.len() >= 3);
let pos1 = sorted.iter().position(|g| g == "goal1");
let pos2 = sorted.iter().position(|g| g == "goal2");
if let (Some(p1), Some(p2)) = (pos1, pos2) {
assert!(p1 > p2); }
}
#[test]
fn test_get_subgoals() {
let mut solver = HierarchicalGoalSolver::new();
let parent = Goal::perform("parent");
let parent_id = solver.add_goal(parent);
let mut sub1 = Goal::perform("sub1");
sub1.parent = Some(parent_id.clone());
let sub1_id = solver.add_goal(sub1);
let mut sub2 = Goal::perform("sub2");
sub2.parent = Some(parent_id.clone());
let sub2_id = solver.add_goal(sub2);
if let Some(p) = solver.get_goal_mut(&parent_id) {
p.add_subgoal(&sub1_id);
p.add_subgoal(&sub2_id);
}
let subgoals = solver.get_subgoals(&parent_id);
assert_eq!(subgoals.len(), 2);
}
#[test]
fn test_sequential_strategy() {
let strategy = SequentialStrategy {
name: "test_sequential".to_string(),
steps: vec![
"step1".to_string(),
"step2".to_string(),
"step3".to_string(),
],
};
let goal = Goal::perform("task");
assert!(strategy.can_decompose(&goal));
let subgoals = strategy.decompose(&goal);
assert_eq!(subgoals.len(), 3);
assert_eq!(subgoals[0].name, "step1");
assert_eq!(subgoals[1].name, "step2");
assert_eq!(subgoals[2].name, "step3");
for subgoal in subgoals {
assert_eq!(subgoal.parent.as_ref(), Some(&goal.id));
}
}
#[test]
fn test_parallel_strategy() {
let strategy = ParallelStrategy {
name: "test_parallel".to_string(),
tasks: vec![
"task1".to_string(),
"task2".to_string(),
"task3".to_string(),
],
};
let goal = Goal::perform("complex_task");
let subgoals = strategy.decompose(&goal);
assert_eq!(subgoals.len(), 3);
for subgoal in subgoals {
assert_eq!(subgoal.parent.as_ref(), Some(&goal.id));
}
}