| 1 |
program test_devloop_jobs |
| 2 |
use fgof_devloop, only : & |
| 3 |
FGOF_DEVLOOP_JOB_ACTION_NONE, & |
| 4 |
FGOF_DEVLOOP_JOB_ACTION_RESTART, & |
| 5 |
FGOF_DEVLOOP_JOB_ACTION_START, & |
| 6 |
attach_devloop_job, & |
| 7 |
attach_devloop_pipeline_members, & |
| 8 |
devloop_change_trigger, & |
| 9 |
devloop_job_restart_plan, & |
| 10 |
devloop_manual_trigger, & |
| 11 |
devloop_service_job, & |
| 12 |
job_continue_result, & |
| 13 |
job_exit_result, & |
| 14 |
job_stop_result, & |
| 15 |
observe_devloop_job, & |
| 16 |
release_devloop_job |
| 17 |
use fgof_devloop_types, only : & |
| 18 |
devloop_job_plan, & |
| 19 |
devloop_job_spec, & |
| 20 |
devloop_job_state |
| 21 |
use fgof_jobs, only : & |
| 22 |
FGOF_JOBS_SIGNAL_SCOPE_GROUP, & |
| 23 |
FGOF_JOBS_TERMINAL_HANDOFF_ALWAYS, & |
| 24 |
FGOF_JOBS_TERMINAL_HANDOFF_NEVER |
| 25 |
implicit none |
| 26 |
|
| 27 |
call test_service_restart_plan() |
| 28 |
call test_terminal_handoff_plan() |
| 29 |
call test_pipeline_wait_observation() |
| 30 |
|
| 31 |
contains |
| 32 |
|
| 33 |
subroutine test_service_restart_plan() |
| 34 |
type(devloop_job_spec) :: spec |
| 35 |
type(devloop_job_state) :: job |
| 36 |
type(devloop_job_plan) :: plan |
| 37 |
|
| 38 |
spec = devloop_service_job("serve", label="local service", & |
| 39 |
terminal_handoff=FGOF_JOBS_TERMINAL_HANDOFF_NEVER) |
| 40 |
job = attach_devloop_job(spec, pid=501, process_group=501) |
| 41 |
|
| 42 |
if (.not. job%configured) error stop "attached devloop job should be configured" |
| 43 |
if (.not. job%attached) error stop "attached devloop job should record attachment" |
| 44 |
if (.not. job%running) error stop "attached devloop job should be running" |
| 45 |
if (.not. job%cleanup_needed) error stop "owned devloop job should need cleanup" |
| 46 |
if (.not. job%owns_process_group) error stop "service job should own its process group" |
| 47 |
if (job%terminal_handoff_required) error stop "never-handoff service should not require terminal handoff" |
| 48 |
if (job%label /= "local service") error stop "job label should be preserved" |
| 49 |
|
| 50 |
plan = devloop_job_restart_plan(job, devloop_change_trigger(1, "source changed")) |
| 51 |
if (plan%action /= FGOF_DEVLOOP_JOB_ACTION_RESTART) error stop "running job should restart on changes" |
| 52 |
if (.not. plan%should_restart) error stop "restart plan should mark restart" |
| 53 |
if (.not. plan%should_stop) error stop "restart plan should stop existing job" |
| 54 |
if (.not. plan%should_start) error stop "restart plan should start replacement job" |
| 55 |
if (.not. plan%cleanup_needed) error stop "restart plan should expose cleanup need" |
| 56 |
if (plan%pid /= 501) error stop "restart plan should preserve pid" |
| 57 |
if (plan%process_group /= 501) error stop "restart plan should preserve process group" |
| 58 |
if (plan%signal_scope /= FGOF_JOBS_SIGNAL_SCOPE_GROUP) error stop "restart plan should preserve signal scope" |
| 59 |
|
| 60 |
call release_devloop_job(job) |
| 61 |
if (.not. job%released) error stop "release should mark devloop job released" |
| 62 |
if (job%cleanup_needed) error stop "release should clear cleanup obligations" |
| 63 |
if (.not. job%running) error stop "release should not alter runtime state" |
| 64 |
|
| 65 |
plan = devloop_job_restart_plan(job, devloop_change_trigger(1, "source changed")) |
| 66 |
if (plan%action /= FGOF_DEVLOOP_JOB_ACTION_NONE) error stop "released job should not plan action" |
| 67 |
if (plan%should_stop) error stop "released job should not stop" |
| 68 |
if (plan%should_start) error stop "released job should not start" |
| 69 |
if (plan%should_restart) error stop "released job should not restart" |
| 70 |
if (plan%reason /= "job released") error stop "released job reason should be explicit" |
| 71 |
end subroutine test_service_restart_plan |
| 72 |
|
| 73 |
subroutine test_terminal_handoff_plan() |
| 74 |
type(devloop_job_spec) :: spec |
| 75 |
type(devloop_job_state) :: job |
| 76 |
type(devloop_job_plan) :: plan |
| 77 |
|
| 78 |
spec = devloop_service_job("foreground", background=.false., & |
| 79 |
terminal_handoff=FGOF_JOBS_TERMINAL_HANDOFF_ALWAYS, & |
| 80 |
release_on_handoff=.true.) |
| 81 |
job = attach_devloop_job(spec, pid=601, process_group=601) |
| 82 |
plan = devloop_job_restart_plan(job, devloop_manual_trigger("handoff")) |
| 83 |
|
| 84 |
if (.not. plan%terminal_handoff_required) error stop "foreground jobs should surface terminal handoff" |
| 85 |
if (.not. plan%should_release) error stop "release-on-handoff should be explicit" |
| 86 |
if (plan%action /= FGOF_DEVLOOP_JOB_ACTION_NONE) error stop "release-on-handoff should not restart" |
| 87 |
if (plan%should_stop) error stop "release-on-handoff should not stop the job" |
| 88 |
if (plan%should_start) error stop "release-on-handoff should not start a replacement" |
| 89 |
if (plan%should_restart) error stop "release-on-handoff should not request restart" |
| 90 |
if (plan%reason /= "release for terminal handoff") then |
| 91 |
error stop "release-on-handoff reason should be explicit" |
| 92 |
end if |
| 93 |
end subroutine test_terminal_handoff_plan |
| 94 |
|
| 95 |
subroutine test_pipeline_wait_observation() |
| 96 |
type(devloop_job_spec) :: spec |
| 97 |
type(devloop_job_state) :: job |
| 98 |
type(devloop_job_plan) :: plan |
| 99 |
|
| 100 |
spec = devloop_service_job("pipeline-head") |
| 101 |
job = attach_devloop_job(spec, pid=701, process_group=701) |
| 102 |
call attach_devloop_pipeline_members(job, [701, 702, 703]) |
| 103 |
|
| 104 |
call observe_devloop_job(job, job_exit_result(0, pid=701, process_group=701)) |
| 105 |
if (job%finished) error stop "one finished pipeline member should not finish the service" |
| 106 |
if (.not. job%handle%members(1)%finished) error stop "first pipeline member should finish" |
| 107 |
if (job%handle%members(2)%finished) error stop "untouched pipeline member should stay live" |
| 108 |
|
| 109 |
call observe_devloop_job(job, job_stop_result(20, pid=702, process_group=701)) |
| 110 |
if (.not. job%handle%members(1)%finished) error stop "group stop should preserve finished member" |
| 111 |
if (job%handle%members(1)%stopped) error stop "group stop should not stop finished member" |
| 112 |
if (.not. job%handle%members(2)%stopped) error stop "group stop should stop live member" |
| 113 |
if (.not. job%stopped) error stop "group stop should mark service stopped" |
| 114 |
|
| 115 |
call observe_devloop_job(job, job_continue_result(pid=701, process_group=701)) |
| 116 |
if (job%handle%members(1)%running) error stop "group continue should not restart finished member" |
| 117 |
if (.not. job%handle%members(2)%running) error stop "group continue should resume live member" |
| 118 |
if (.not. job%running) error stop "continued pipeline should run" |
| 119 |
|
| 120 |
call observe_devloop_job(job, job_exit_result(0, pid=702, process_group=701)) |
| 121 |
call observe_devloop_job(job, job_exit_result(0, pid=703, process_group=701)) |
| 122 |
if (.not. job%finished) error stop "all terminal members should finish service" |
| 123 |
if (job%cleanup_needed) error stop "finished service should not need cleanup" |
| 124 |
|
| 125 |
plan = devloop_job_restart_plan(job, devloop_change_trigger(2, "restart finished")) |
| 126 |
if (plan%action /= FGOF_DEVLOOP_JOB_ACTION_START) error stop "finished service should start fresh" |
| 127 |
if (.not. plan%should_start) error stop "finished service restart should start" |
| 128 |
if (plan%should_stop) error stop "finished service restart should not stop old job" |
| 129 |
end subroutine test_pipeline_wait_observation |
| 130 |
|
| 131 |
end program test_devloop_jobs |
| 132 |
|