solverforge-solver 0.8.6

Solver engine for SolverForge
Documentation
use solverforge_core::score::SoftScore;
use solverforge_core::PlanningSolution;

use super::super::{SolverEvent, SolverLifecycleState, SolverManager};
use super::resume_support::DeterministicResumeSolution;

#[test]
fn retained_job_exact_resume_matches_uninterrupted_execution_after_boundary() {
    static MANAGER: SolverManager<DeterministicResumeSolution> = SolverManager::new();

    let uninterrupted = DeterministicResumeSolution::new();
    let uninterrupted_gate = uninterrupted.gate.clone();
    let (uninterrupted_job_id, mut uninterrupted_receiver) = MANAGER
        .solve(uninterrupted)
        .expect("uninterrupted job should start");

    match uninterrupted_receiver
        .blocking_recv()
        .expect("uninterrupted best solution event")
    {
        SolverEvent::BestSolution { metadata, solution } => {
            assert_eq!(metadata.snapshot_revision, Some(1));
            assert_eq!(solution.value, 10);
        }
        other => panic!("unexpected event: {other:?}"),
    }

    match uninterrupted_receiver
        .blocking_recv()
        .expect("uninterrupted progress event")
    {
        SolverEvent::Progress { metadata } => {
            assert_eq!(metadata.snapshot_revision, Some(1));
            assert_eq!(metadata.current_score, Some(SoftScore::of(10)));
            assert_eq!(metadata.best_score, Some(SoftScore::of(10)));
            assert_eq!(metadata.telemetry.step_count, 1);
        }
        other => panic!("unexpected event: {other:?}"),
    }

    uninterrupted_gate.allow_next_step();

    let uninterrupted_boundary_snapshot = match uninterrupted_receiver
        .blocking_recv()
        .expect("uninterrupted boundary snapshot event")
    {
        SolverEvent::BestSolution { metadata, solution } => {
            assert_eq!(metadata.snapshot_revision, Some(2));
            assert_eq!(metadata.current_score, Some(SoftScore::of(12)));
            assert_eq!(metadata.best_score, Some(SoftScore::of(12)));
            assert_eq!(metadata.telemetry.step_count, 2);
            assert_eq!(solution.value, 12);
            solution
        }
        other => panic!("unexpected event: {other:?}"),
    };

    let uninterrupted_post_boundary = match uninterrupted_receiver
        .blocking_recv()
        .expect("uninterrupted post-boundary progress")
    {
        SolverEvent::Progress { metadata } => (
            metadata.snapshot_revision,
            metadata.current_score,
            metadata.best_score,
            metadata.telemetry.step_count,
        ),
        other => panic!("unexpected event: {other:?}"),
    };

    let uninterrupted_completed = match uninterrupted_receiver
        .blocking_recv()
        .expect("uninterrupted completed event")
    {
        SolverEvent::Completed { metadata, solution } => (
            metadata.snapshot_revision,
            metadata.current_score,
            metadata.best_score,
            metadata.terminal_reason,
            metadata.telemetry.step_count,
            solution.value,
        ),
        other => panic!("unexpected event: {other:?}"),
    };

    let resumed = DeterministicResumeSolution::new();
    let resumed_gate = resumed.gate.clone();
    let (resumed_job_id, mut resumed_receiver) =
        MANAGER.solve(resumed).expect("resumed job should start");

    match resumed_receiver
        .blocking_recv()
        .expect("resumed best solution event")
    {
        SolverEvent::BestSolution { metadata, solution } => {
            assert_eq!(metadata.snapshot_revision, Some(1));
            assert_eq!(solution.value, 10);
        }
        other => panic!("unexpected event: {other:?}"),
    }

    match resumed_receiver
        .blocking_recv()
        .expect("resumed progress event")
    {
        SolverEvent::Progress { metadata } => {
            assert_eq!(metadata.snapshot_revision, Some(1));
            assert_eq!(metadata.current_score, Some(SoftScore::of(10)));
            assert_eq!(metadata.best_score, Some(SoftScore::of(10)));
            assert_eq!(metadata.telemetry.step_count, 1);
        }
        other => panic!("unexpected event: {other:?}"),
    }

    MANAGER
        .pause(resumed_job_id)
        .expect("pause should be accepted");

    match resumed_receiver
        .blocking_recv()
        .expect("pause requested event")
    {
        SolverEvent::PauseRequested { metadata } => {
            assert_eq!(
                metadata.lifecycle_state,
                SolverLifecycleState::PauseRequested
            );
        }
        other => panic!("unexpected event: {other:?}"),
    }

    resumed_gate.allow_next_step();

    match resumed_receiver
        .blocking_recv()
        .expect("paused boundary snapshot event")
    {
        SolverEvent::Paused { metadata } => {
            assert_eq!(metadata.snapshot_revision, Some(2));
            assert_eq!(metadata.current_score, Some(SoftScore::of(12)));
            assert_eq!(metadata.best_score, Some(SoftScore::of(12)));
            assert_eq!(metadata.telemetry.step_count, 2);
        }
        other => panic!("unexpected event: {other:?}"),
    }

    let resumed_boundary_snapshot = MANAGER
        .get_snapshot(resumed_job_id, Some(2))
        .expect("paused boundary snapshot");
    assert_eq!(resumed_boundary_snapshot.snapshot_revision, 2);
    assert_eq!(
        resumed_boundary_snapshot.current_score,
        Some(SoftScore::of(12))
    );
    assert_eq!(
        resumed_boundary_snapshot.best_score,
        Some(SoftScore::of(12))
    );
    assert_eq!(resumed_boundary_snapshot.telemetry.step_count, 2);
    assert_eq!(resumed_boundary_snapshot.solution.value, 12);
    assert_eq!(
        resumed_boundary_snapshot.solution.score(),
        Some(SoftScore::of(12))
    );

    assert_eq!(
        uninterrupted_boundary_snapshot.value,
        resumed_boundary_snapshot.solution.value
    );
    assert_eq!(
        uninterrupted_boundary_snapshot.score(),
        resumed_boundary_snapshot.solution.score()
    );

    MANAGER
        .resume(resumed_job_id)
        .expect("resume should be accepted");

    match resumed_receiver.blocking_recv().expect("resumed event") {
        SolverEvent::Resumed { metadata } => {
            assert_eq!(metadata.snapshot_revision, Some(2));
            assert_eq!(metadata.best_score, Some(SoftScore::of(12)));
        }
        other => panic!("unexpected event: {other:?}"),
    }

    let resumed_post_boundary = match resumed_receiver
        .blocking_recv()
        .expect("resumed post-boundary progress")
    {
        SolverEvent::Progress { metadata } => (
            metadata.snapshot_revision,
            metadata.current_score,
            metadata.best_score,
            metadata.telemetry.step_count,
        ),
        other => panic!("unexpected event: {other:?}"),
    };

    let resumed_completed = match resumed_receiver
        .blocking_recv()
        .expect("resumed completed event")
    {
        SolverEvent::Completed { metadata, solution } => (
            metadata.snapshot_revision,
            metadata.current_score,
            metadata.best_score,
            metadata.terminal_reason,
            metadata.telemetry.step_count,
            solution.value,
        ),
        other => panic!("unexpected event: {other:?}"),
    };

    assert_eq!(resumed_post_boundary, uninterrupted_post_boundary);
    assert_eq!(resumed_completed, uninterrupted_completed);

    let uninterrupted_final_snapshot = MANAGER
        .get_snapshot(uninterrupted_job_id, None)
        .expect("uninterrupted final snapshot");
    let resumed_final_snapshot = MANAGER
        .get_snapshot(resumed_job_id, None)
        .expect("resumed final snapshot");

    assert_eq!(
        uninterrupted_final_snapshot.snapshot_revision,
        resumed_final_snapshot.snapshot_revision
    );
    assert_eq!(
        uninterrupted_final_snapshot.current_score,
        resumed_final_snapshot.current_score
    );
    assert_eq!(
        uninterrupted_final_snapshot.best_score,
        resumed_final_snapshot.best_score
    );
    assert_eq!(
        uninterrupted_final_snapshot.solution.value,
        resumed_final_snapshot.solution.value
    );

    MANAGER
        .delete(uninterrupted_job_id)
        .expect("delete uninterrupted job");
    MANAGER.delete(resumed_job_id).expect("delete resumed job");
}