Add sim_verify.c (sim_decision_eq/sim_log_mismatch: real structural comparison of dispatch/pend decision payloads, not a jobid shortcut) and wire it into sim_loop.c's ReplaySession drain loop via decode_ReplaySession_ptr() (first real consumer of the generated capnp decode path). --sim-verify now detects and reports genuine decision divergence with logical-time + detail, per Req 5 AC 15/18.
649 lines
35 KiB
Meson
649 lines
35 KiB
Meson
scheduler_test_src = [
|
|
'test.c',
|
|
]
|
|
|
|
scheduler_test_deps = [libxce_deps, librtti_dep, unity_dep, libxce_dep]
|
|
|
|
incdir = include_directories('../include', '../../include', '.', '../src', '../../lib')
|
|
|
|
scheduler_test = executable('sched_test',
|
|
scheduler_test_src,
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for the notify single-job in-place update path
|
|
# (sched_update_existing_job). It #includes ../src/schedobj.c directly, so it
|
|
# is a standalone TU with its own main() — built as a separate executable.
|
|
sched_job_update_test = executable('sched_job_update_test',
|
|
['test_job_update.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for the notify/dataset enqueue handlers. It #includes
|
|
# ../src/handler.c directly (enq_pend_update + the static handlers), so it is a
|
|
# standalone TU with its own main() — built as a separate executable.
|
|
sched_handlers_test = executable('sched_handlers_test',
|
|
['test_handlers.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for the compact job-array push receive/expand path
|
|
# (job-array-single-wal-record task 5.3). It also #includes ../src/handler.c
|
|
# directly, but additionally needs a REAL sched->jobs kvmgr (sched_get_job/
|
|
# sched_add_job) to exercise the per-element dedup check for real — kept as
|
|
# its own binary rather than folded into sched_handlers_test so that file's
|
|
# "self-contained, only sched_init_config stubbed" story stays accurate.
|
|
sched_job_array_test = executable('sched_job_array_test',
|
|
['test_job_array_expand.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Cross-daemon integration test (job-array-single-wal-record task 5.5): the
|
|
# REAL xce_encode()/xce_decode()/extract_data() -> scheduler_req_handler()
|
|
# chain (the top-level function vtask_entry() registers as its handler),
|
|
# proving one-push/N-tasks/log-once and redelivery-safety under genuinely
|
|
# re-decoded wire bytes -- one layer more real than sched_job_array_test's
|
|
# own wire-dispatch case, which stops at sched_handle_dataset(). Kept as its
|
|
# own binary for the same reason sched_job_array_test is separate from
|
|
# sched_handlers_test: it needs a REAL sched->jobs kvmgr for the dedup check.
|
|
sched_job_array_wire_e2e_test = executable('sched_job_array_wire_e2e_test',
|
|
['test_job_array_wire_e2e.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Compile-check for the job picker plugin contract, xce_job_picker_iface_t
|
|
# (scheduler-core spec, task 1.1). Only #includes plugin.h -- no picker
|
|
# implementation exists yet (that's task 2.x). -Werror on incompatible
|
|
# pointer types so a hook signature drift in plugin.h fails the build, not
|
|
# just an assertion: the test's real content is "this compiles at all".
|
|
sched_picker_iface_test = executable('sched_picker_iface_test',
|
|
['test_picker_iface.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
c_args: ['-Werror=incompatible-pointer-types'],
|
|
install: false)
|
|
|
|
# White-box test for the picker plugin registration category and the
|
|
# job_elect_fn compatibility wrapper (scheduler-core spec, task 1.2). It
|
|
# #includes ../src/plugin.c directly (same white-box pattern as
|
|
# sched_handlers_test/sched_job_array_test) so it exercises the real
|
|
# plugin_register() and the real legacy_picker_wrapper_iface, and needs a
|
|
# REAL sched->plugins kvmgr (same setup scheduler.c's sched_plugin_init()
|
|
# uses) to register plugins through it for real.
|
|
sched_picker_registration_test = executable('sched_picker_registration_test',
|
|
['test_plugin_picker_registration.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for the picker configuration fields added to
|
|
# xce_scheduler_t/xce_sched_queue_t/xce_sched_project_t (scheduler-core
|
|
# spec, task 1.3). It #includes ../src/schedobj.c directly (same white-box
|
|
# pattern as sched_job_update_test) so it exercises the real
|
|
# make_sched_queue/make_sched_project/sched_set_default_job_pick_plugin,
|
|
# not a re-implementation of any of them.
|
|
sched_picker_config_fields_test = executable('sched_picker_config_fields_test',
|
|
['test_picker_config_fields.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for builtin_fcfs's xce_job_picker_iface_t lifecycle hooks
|
|
# (scheduler-core spec, task 2.1). It #includes ../src/plugins/builtin_fcfs.c
|
|
# directly (same white-box pattern as sched_picker_registration_test) so it
|
|
# exercises the real builtin_fcfs_init()/builtin_fcfs_fini() and the real
|
|
# private fcfs_plugin_data_t layout, not a re-implementation of either.
|
|
sched_picker_fcfs_test = executable('sched_picker_fcfs_test',
|
|
['test_picker_fcfs.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for builtin_fcfs's session hooks (begin_session/next_job/
|
|
# end_session) and post-allocation hooks (on_dispatch/on_pend) (scheduler-core
|
|
# spec, task 2.3). Split out of test_picker_fcfs.c rather than appended to it
|
|
# -- that file was already ~765 lines after tasks 2.1/2.2, against this
|
|
# project's 800-line ceiling. Also #includes ../src/plugins/builtin_fcfs.c
|
|
# directly (same white-box pattern as sched_picker_fcfs_test), and supplies
|
|
# its own sched_queue_is_ready()/sched_job_is_ready() test doubles since this
|
|
# translation unit does not link scheduler.c (see the file header comment).
|
|
sched_picker_fcfs_session_test = executable('sched_picker_fcfs_session_test',
|
|
['test_picker_fcfs_session.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for wiring builtin_fcfs in as the registered default picker
|
|
# and removing the hardcoded "xce_sched_fcfs" picker string (scheduler-core
|
|
# spec, task 2.4). It #includes ../src/plugin.c, ../src/schedobj.c, and
|
|
# ../src/plugins/builtin_fcfs.c directly (same white-box pattern as the
|
|
# other picker tests) so it exercises the real plugin_register(),
|
|
# register_builtin_fcfs(), sched_set_default_job_pick_plugin(), and
|
|
# sched_resolve_job_picker() together -- not a re-implementation of any of
|
|
# them. See the file's own header comment for why sched_job_picker() itself
|
|
# (scheduler.c) is covered at the resolved-plugin level rather than by
|
|
# whole-file-including scheduler.c.
|
|
sched_picker_wiring_test = executable('sched_picker_wiring_test',
|
|
['test_picker_wiring.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Regression guard for the core scheduling loop boundary (scheduler-core
|
|
# spec, task 2.5, Req 1 AC 1 / AC 7): "the core loop calls only
|
|
# picker->next_job() ... no project/queue/job iteration or ordering logic
|
|
# remains in the loop itself." Does NOT #include scheduler.c (that file's
|
|
# external dependency surface is why test_picker_wiring.c's own header
|
|
# comment already ruled out driving sched_job_picker() directly) -- instead
|
|
# it reads scheduler.c's real on-disk text at test run time and mechanically
|
|
# checks sched_job_picker()'s body for required picker-interface call sites
|
|
# and absent direct-traversal symbols. XCE_SCHEDULER_C_PATH bakes in the
|
|
# absolute path via meson.current_source_dir() so the check has no
|
|
# dependency on the test's cwd at run time.
|
|
sched_picker_core_loop_boundary_test = executable(
|
|
'sched_picker_core_loop_boundary_test',
|
|
['test_picker_core_loop_boundary.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
c_args: ['-DXCE_SCHEDULER_C_PATH="'
|
|
+ (meson.current_source_dir() / '..' / 'src' / 'scheduler.c')
|
|
+ '"'],
|
|
install: false)
|
|
|
|
# Regression guard for Req 6 AC 2 (scheduler-core spec, task 2.6): "All pend
|
|
# reason allocation shall go through make_pendreason() (lib/preason.c).
|
|
# Callers never calloc(sizeof(xce_pendreason_t)) directly." Same genre as
|
|
# sched_picker_core_loop_boundary_test above (reads real on-disk source at
|
|
# test run time rather than linking the huge dependency surface a live
|
|
# scheduler pulls in), but scans every non-testsuite .c/.h file under
|
|
# src/scheduler for the forbidden pattern rather than one function's body.
|
|
# XCE_SCHEDULER_ROOT_PATH bakes in src/scheduler's absolute path;
|
|
# the scanned file list itself lives in the test source (see that file's
|
|
# header comment for why a fixed list, not a directory walk).
|
|
sched_pendreason_factory_audit_test = executable(
|
|
'sched_pendreason_factory_audit_test',
|
|
['test_picker_pendreason_factory_audit.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
c_args: ['-DXCE_SCHEDULER_ROOT_PATH="'
|
|
+ (meson.current_source_dir() / '..')
|
|
+ '"'],
|
|
install: false)
|
|
|
|
# Regression guard for task 4.1's Observable (scheduler-core spec, Req
|
|
# 5.1/5.2): "the core structure compiles and links with zero symbols from
|
|
# the networking/API libraries ... a build-time or link-time check
|
|
# confirms this." Same source-text-scan genre as
|
|
# sched_picker_core_loop_boundary_test / sched_pendreason_factory_audit_test
|
|
# above, PLUS a genuine link-time proof: it inspects the undefined-symbol
|
|
# table (via nm(1)) of sched_core_isolation_lib (defined in the parent
|
|
# meson.build -- core.c compiled alone, isolated from the rest of the
|
|
# scheduler). link_with establishes the build-order dependency so that
|
|
# archive exists before this test runs; XCE_CORE_LIB_PATH bakes in its
|
|
# absolute path the same way XCE_SCHEDULER_C_PATH does above.
|
|
sched_core_isolation_test = executable(
|
|
'sched_core_isolation_test',
|
|
['test_core_isolation.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
link_with: [sched_core_isolation_lib],
|
|
c_args: ['-DXCE_CORE_H_PATH="'
|
|
+ (meson.current_source_dir() / '..' / 'include' / 'core.h')
|
|
+ '"',
|
|
'-DXCE_CORE_C_PATH="'
|
|
+ (meson.current_source_dir() / '..' / 'src' / 'core.c')
|
|
+ '"',
|
|
'-DXCE_CORE_LIB_PATH="'
|
|
+ sched_core_isolation_lib.full_path()
|
|
+ '"'],
|
|
install: false)
|
|
|
|
# Regression guard for task 4.2's jbuf-capacity half (scheduler-core spec,
|
|
# Req 5 AC 7): "cbuf capacity=1 serialization ... this capacity is a
|
|
# determinism invariant, not a tuning knob." Same white-box #include
|
|
# pattern as test_picker_config_fields.c (pulls the real
|
|
# xce_sched_core_init()/xce_sched_core_fini() from ../src/core.c into this
|
|
# TU) PLUS the source-text-scan genre used by test_picker_core_loop_boundary_test/
|
|
# test_core_isolation_test above -- a real runtime call proving
|
|
# core.jbuf.ncap == 1, and a scan of core.c's actual on-disk text proving
|
|
# the cbuf_init() capacity argument is the literal 1 (never a variable),
|
|
# that the former `njobs` local is gone, and that the separate/unrelated
|
|
# `ndecisions` FIXME was left untouched. XCE_CORE_C_PATH reuses the same
|
|
# absolute-path constant as sched_core_isolation_test above.
|
|
sched_core_jbuf_capacity_pin_test = executable(
|
|
'sched_core_jbuf_capacity_pin_test',
|
|
['test_core_jbuf_capacity_pin.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
c_args: ['-DXCE_CORE_C_PATH="'
|
|
+ (meson.current_source_dir() / '..' / 'src' / 'core.c')
|
|
+ '"'],
|
|
install: false)
|
|
|
|
# Contract test for the core event interface and batch event push
|
|
# (scheduler-core spec, task 4.3, Req 5 AC 4 / AC 8): xce_core_push_events()
|
|
# fires the resolved picker's corresponding state-change hook exactly once
|
|
# per event, synchronously, before it returns, and rejects with -EBUSY (no
|
|
# event applied) when a session is active. Same white-box #include pattern
|
|
# as test_core_jbuf_capacity_pin.c/test_core_isolation.c (pulls the real
|
|
# ../src/core.c into this TU) -- see the test's own header comment for why
|
|
# it provides its own real sched_resolve_job_picker() rather than
|
|
# #include-ing schedobj.c.
|
|
sched_core_events_test = executable(
|
|
'sched_core_events_test',
|
|
['test_core_events.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for the session-start signal and decision callback protocol
|
|
# (scheduler-core spec, task 4.4, Req 5 AC 5 / AC 6): xce_core_go() signals
|
|
# the picker thread non-blockingly and rejects reentrant calls with
|
|
# -EINVAL; decisions placed on core->decisions reach the driver-registered
|
|
# on_decision callback once each, in order, followed by the end-of-session
|
|
# NULL marker terminating the drain loop; xce_core_end_session() clears
|
|
# session_active. Same white-box #include pattern as test_core_events.c
|
|
# (pulls ../src/core.c, ../src/core_session.c, and ../src/decision.c into
|
|
# this TU) -- see the test's own header comment for why this file needs no
|
|
# picker-plugin fixture (xce_core_go()/xce_core_end_session() never call
|
|
# sched_resolve_job_picker()).
|
|
sched_core_session_test = executable(
|
|
'sched_core_session_test',
|
|
['test_core_session.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for clock virtualization and session timing (scheduler-core
|
|
# spec, task 4.5, Req 5 AC 3 / AC 12): xce_core_now()'s two-anchor + speed
|
|
# formula for all four documented modes, xce_core_tlog_format()'s tlog
|
|
# integration (frozen virtual time shown instead of info->time's real
|
|
# wall-clock read), and session_start/session_duration_ns tracking across a
|
|
# real xce_core_go()/xce_core_end_session() cycle. Same white-box #include
|
|
# pattern as test_core_session.c (pulls ../src/core.c, ../src/core_session.c,
|
|
# and ../src/decision.c into this TU) PLUS ../src/core_clock.c (this task's
|
|
# new file -- see that file's own header comment for why xce_core_now()/
|
|
# the tlog format function live there, not in core.c).
|
|
sched_core_clock_test = executable(
|
|
'sched_core_clock_test',
|
|
['test_clock.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Core contract test for the full push-events -> start-session ->
|
|
# drain-decisions -> end-session cycle (scheduler-core spec, task 4.6, Req 5
|
|
# AC 1 / AC 2), against a real picker thread + a stand-in allocator (this
|
|
# test's own driver thread) driving a minimal stub picker plugin through
|
|
# xce_core_push_events()/xce_core_go()/xce_core_end_session() end to end --
|
|
# also proving, mid-cycle against a GENUINELY active session, both the
|
|
# reentrant xce_core_go() rejection (Req 5 AC 5) and the xce_core_push_events()
|
|
# -EBUSY rejection (Req 5 AC 8) compose correctly with the rest of the
|
|
# cycle. Same white-box #include pattern as test_core_events.c/
|
|
# test_core_session.c (pulls ../src/core.c, ../src/core_events.c,
|
|
# ../src/core_session.c, ../src/core_clock.c, and ../src/decision.c into
|
|
# this TU) -- see the test's own header comment for the allocator-thread-
|
|
# vs-direct-jbuf-drain design call.
|
|
sched_core_full_cycle_test = executable(
|
|
'sched_core_full_cycle_test',
|
|
['test_core_full_cycle.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for the normal-mode driver's controller event translation
|
|
# path (scheduler-core spec, task 5.1, Req 5 AC 8 / AC 9's translation
|
|
# half): xce_sched_driver_translate_pending_events() drains core->pend_updates
|
|
# (the same dataq-swap mechanism handler.c already populates and
|
|
# scheduler.c's sched_update() already drains), translates each dequeued
|
|
# xce_sched_update_request_t into the matching xce_core_event_t for all 10
|
|
# op types, and pushes the batch through a genuine xce_core_push_events()
|
|
# call. Same white-box #include pattern as test_core_events.c (pulls the
|
|
# real ../src/core.c and ../src/core_events.c into this TU) PLUS the new
|
|
# ../src/driver.c under test -- see that test's own header comment for why
|
|
# it provides its own real sched_resolve_job_picker() rather than
|
|
# #include-ing schedobj.c.
|
|
sched_driver_translate_test = executable(
|
|
'sched_driver_translate_test',
|
|
['test_driver_translate.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for the normal-mode driver's decision-publish callback
|
|
# (scheduler-core spec, task 5.2, Req 5 AC 6 / AC 9's publishing half):
|
|
# xce_sched_driver_on_decision() forwards DISPATCH/PEND decisions to
|
|
# sched_publish_dispatch()/sched_publish_pend_reason() exactly once each,
|
|
# with the same single-item njobs=1/nreasons=1 wrapping; PREEMPT fails
|
|
# loudly (-ENOSYS, no publish) rather than fabricating a nonexistent
|
|
# xcecli_preempt() call; NULL driver_ctx/decision each return -EINVAL, no
|
|
# publish. Same white-box #include pattern as test_driver_translate.c
|
|
# (pulls ../src/core.c, ../src/core_clock.c, ../src/core_events.c, and the
|
|
# new ../src/driver.c into this TU) PLUS ../src/decision.c for the real
|
|
# make_dispatch_decision()/make_pend_decision()/make_preempt_decision()
|
|
# constructors this file's fixtures use. sched_publish_dispatch()/
|
|
# sched_publish_pend_reason() are SPY test doubles (this file never links
|
|
# scheduler.c), same rationale as test_driver_translate.c's own stubs.
|
|
sched_driver_decision_cb_test = executable(
|
|
'sched_driver_decision_cb_test',
|
|
['test_driver_decision_cb.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Safety-property proof for the begin/end-session ordering seam task 5.2
|
|
# introduced (Req 1 AC 7 / Req 5 AC 9): xce_sched_driver_run_normal_mode_round()
|
|
# (driver.c) must call plugin_begin_session()/sched_account_jobs() strictly
|
|
# AFTER xce_core_begin_session()'s resval reset and strictly BEFORE
|
|
# xce_core_signal_go() wakes the picker thread -- otherwise the picker could
|
|
# observe a session whose resource-in-use accounting was reset to zero but
|
|
# not yet re-summed from running jobs. Drives the REAL
|
|
# xce_sched_driver_run_normal_mode_round() (white-box #include of
|
|
# ../src/driver.c, same TU as test_driver_translate.c/
|
|
# test_driver_decision_cb.c, PLUS ../src/core_session.c for the real
|
|
# xce_core_begin_session()/xce_core_signal_go()/xce_core_end_session()
|
|
# under test) against a real picker pthread (same design as
|
|
# test_core_full_cycle.c's test_picker_thread_fn -- see that file's own
|
|
# header comment for the full "why a real thread, not a sequential drain"
|
|
# rationale: jbuf's hardcoded capacity-1 rendezvous requires one). Uses the
|
|
# REAL plugin_begin_session()/plugin_end_session() (../src/plugin.c) driven
|
|
# through a genuinely test-registered xce_sched_plugin_t whose
|
|
# begin_session_fn/end_session_fn set/check an ordering flag -- per this
|
|
# task's own suggested technique, this is a stronger proof than spying on
|
|
# plugin_begin_session() itself. sched_account_jobs() (scheduler.c,
|
|
# xce_scheduler_t*-coupled, pulls in resource.c's sched_charge_account())
|
|
# is instead a SPY test double recording an order index -- this test's
|
|
# target is the DRIVER's ordering discipline, not sched_account_jobs()'s
|
|
# own resource-accounting correctness (out of this task's scope, same
|
|
# "spy the xce_scheduler_t*-coupled collaborator" rationale
|
|
# test_driver_decision_cb.c/test_driver_translate.c already established for
|
|
# sched_publish_dispatch()/sched_publish_pend_reason()).
|
|
sched_driver_session_ordering_test = executable(
|
|
'sched_driver_session_ordering_test',
|
|
['test_driver_session_ordering.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Cross-core behavioral-equivalence proof for
|
|
# xce_sched_core_save_snapshot()/xce_sched_core_load_snapshot()
|
|
# (scheduler-core spec, task 8.1, Req 5 AC 21 / Req 6 AC 5): save immediately
|
|
# followed by load into a freshly-initialized, completely separate core
|
|
# produces a core whose observable scheduling behavior (next session's
|
|
# decisions, given the same events) is identical to the pre-save core, plus
|
|
# direct unit-level proof of both functions' -EBUSY (mid-session)/-EINVAL
|
|
# (NULL args) guards. Same white-box #include pattern as
|
|
# test_core_full_cycle.c (../src/core.c, ../src/core_clock.c,
|
|
# ../src/core_events.c, ../src/core_session.c, ../src/decision.c) PLUS the
|
|
# three files task 8.1's own 800-line-cap split produced:
|
|
# ../src/snapshot.c, ../src/snapshot_wire.c, ../src/snapshot_load.c. See
|
|
# that test's own header comment for the two-independent-picker-instance
|
|
# design (xce_sched_plugin_t->data-scoped state) that lets this test drive
|
|
# two fully independent xce_sched_core_t instances through the identical
|
|
# session protocol without cross-contaminating a shared global.
|
|
sched_snapshot_save_load_test = executable(
|
|
'sched_snapshot_save_load_test',
|
|
['test_snapshot_save_load.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# White-box test for the recording-directory path-validation function
|
|
# (scheduler-core spec, task 9.1, Req 5 AC 10 "Record file path
|
|
# validation"): rejects a recordFile containing a path separator or a ".."
|
|
# segment BEFORE any filesystem access, and confirms containment beneath
|
|
# record_dir via a genuine canonical-path (realpath()) comparison rather
|
|
# than a textual prefix match -- including a symlink-escape case and the
|
|
# "record_dir vs record_dir-evil sibling" prefix-match trap named in this
|
|
# task's own security brief. Same white-box #include pattern as
|
|
# test_job_update.c/test_handlers.c (pulls the real
|
|
# ../src/recording.c into this TU) -- the function under test has no
|
|
# scheduler-struct fixture dependency at all, so this is the simplest
|
|
# white-box test in this directory.
|
|
sched_recording_path_validation_test = executable(
|
|
'sched_recording_path_validation_test',
|
|
['test_recording_path_validation.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for the record start/stop lifecycle and the driver-loop
|
|
# recording-write hooks (scheduler-core spec, task 9.2, Req 5 AC 10): start
|
|
# writes a decodable ReplayHeader then SnapshotEntry; a full start -> submit
|
|
# event batch -> complete one real session (real picker + stand-in
|
|
# allocator threads, same rendezvous design as
|
|
# sched_driver_session_ordering_test) -> stop cycle produces exactly the
|
|
# expected message stream ending in ReplayEvent{endSession}; recording
|
|
# inactive costs zero file I/O; double-start/stop-when-not-recording fail
|
|
# cleanly; a TOCTOU-style symlink/hard-link swap between path validation
|
|
# and the eventual open() is rejected. Same white-box #include pattern as
|
|
# sched_driver_session_ordering_test (core.c/core_clock.c/core_events.c/
|
|
# core_session.c/driver.c/plugin.c) PLUS ../src/recording.c/
|
|
# recording_events.c/recording_session.c (the code under test) PLUS
|
|
# ../src/snapshot.c/snapshot_wire.c (task 8.1's real
|
|
# xce_sched_core_save_snapshot(), needed by sched_record_start()).
|
|
sched_record_lifecycle_test = executable(
|
|
'sched_record_lifecycle_test',
|
|
['test_record_lifecycle.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for the scheduler-side OPCode_schedAdmin request handler
|
|
# (scheduler-core spec, task 9.4, Req 5 AC 10): decodes a forwarded
|
|
# SchedAdminRequest, dispatches by action to sched_record_start()/
|
|
# sched_record_stop()/sched_record_status(), and sends back a
|
|
# SchedAdminReply matching requirements.md's field-semantics table.
|
|
# recordStatus is proven synchronous (answered directly on the handler
|
|
# thread, both idle and while genuinely recording); recordStart/recordStop
|
|
# are proven deferred (enqueued, not executed, until a subsequent
|
|
# xce_sched_driver_translate_pending_events() drain actually performs the
|
|
# real sched_record_start()/sched_record_stop() call and sends the reply).
|
|
# Same white-box #include pattern as sched_driver_translate_test
|
|
# (core.c/core_clock.c/core_events.c/driver.c) PLUS ../src/recording.c/
|
|
# recording_events.c/snapshot.c/snapshot_wire.c (real record start/stop/
|
|
# status/write, task 9.1/9.2/9.3) PLUS ../src/handler.c (the code under
|
|
# test). xcecli_reply_schedadmin() is a SPY test double, same pattern
|
|
# sched_driver_decision_cb_test already establishes for
|
|
# sched_publish_dispatch()/sched_publish_pend_reason().
|
|
sched_admin_request_test = executable(
|
|
'sched_admin_request_test',
|
|
['test_admin_request.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for simulation-mode startup (scheduler-core spec, task 11.1,
|
|
# Req 5 AC 13): opening and validating the replay file (first message must
|
|
# be ReplayHeader, first data message must be SnapshotEntry), locating the
|
|
# most recent SnapshotEntry at or before --sim-start-ts, loading it via the
|
|
# real xce_sched_core_load_snapshot(), and setting the virtual clock's
|
|
# anchors from the loaded snapshot's timestamp and requested speed. Same
|
|
# white-box #include pattern as sched_snapshot_save_load_test (../src/core.c,
|
|
# ../src/core_clock.c, ../src/snapshot.c, ../src/snapshot_wire.c,
|
|
# ../src/snapshot_load.c) PLUS ../src/recording.c (used only to build real
|
|
# `.replay` fixtures via sched_record_start()/sched_record_stop(), not under
|
|
# test here) PLUS the new ../src/sim.c under test. No driver.c/plugin.c/
|
|
# core_events.c/core_session.c/decision.c: this task stops at "ready-to-run"
|
|
# (task 11.2 owns the event loop), so no real scheduling session is ever
|
|
# driven.
|
|
sched_sim_startup_test = executable(
|
|
'sched_sim_startup_test',
|
|
['test_sim_startup.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Contract test for the simulation-mode event loop and virtual clock
|
|
# advancement (scheduler-core spec, task 11.2, Req 5 AC 14/18): the virtual
|
|
# clock jumps to each ReplayEvent's own timestamp, in exact file order
|
|
# (including a same-timestamp pair), at the moment it is queued; each
|
|
# ReplaySession boundary triggers one complete push-events ->
|
|
# start-session -> drain-decisions -> end-session round, in file order
|
|
# across multiple rounds. Same white-box #include pattern as
|
|
# sched_sim_startup_test (core.c, core_clock.c, recording.c, sim.c,
|
|
# snapshot.c/snapshot_wire.c/snapshot_load.c) PLUS core_events.c,
|
|
# core_session.c, decision.c (the real session/decision machinery this
|
|
# task's event loop drives) PLUS plugin.c, plugins/builtin_fcfs.c (task
|
|
# 11.2's own new core.h-scoped picker registration path,
|
|
# plugin_register_core()/builtin_fcfs_new_plugin() -- deliberately NOT
|
|
# schedobj.c, which needs a genuine xce_scheduler_t* this driver has none
|
|
# of, Req 5 AC 17) PLUS the new ../src/sim_loop.c under test.
|
|
sched_sim_event_loop_test = executable(
|
|
'sched_sim_event_loop_test',
|
|
['test_sim_event_loop.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Regression guard for task 11.3's Observable (scheduler-core spec, Req 5 AC
|
|
# 17, "Simulation isolation"): "a build-time or structural check confirms no
|
|
# controller-facing symbol is reachable from the simulation driver's compiled
|
|
# object", plus the AC's own "confirmed by the struct definition itself, not
|
|
# runtime checks alone" clause. Same source-text-scan + nm(1) link-time-proof
|
|
# genre as sched_core_isolation_test above, PLUS a third, narrower structural
|
|
# layer scanning ONLY xce_sched_sim_driver_t's own struct body text for
|
|
# forbidden field types -- see the test's own header comment for why a
|
|
# comment-stripping pass is required first (sim.h's own boundary doc comment
|
|
# legitimately names xcecli_ctx_t/xce_cluster_config_t in prose). link_with
|
|
# establishes the build-order dependency on sched_sim_isolation_lib (parent
|
|
# meson.build -- sim.c AND sim_loop.c compiled together, isolated from the
|
|
# rest of the scheduler); XCE_SIM_*_PATH bake in absolute paths the same way
|
|
# XCE_CORE_*_PATH does above.
|
|
sched_sim_isolation_test = executable(
|
|
'sched_sim_isolation_test',
|
|
['test_sim_isolation.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
link_with: [sched_sim_isolation_lib],
|
|
c_args: ['-DXCE_SIM_H_PATH="'
|
|
+ (meson.current_source_dir() / '..' / 'include' / 'sim.h')
|
|
+ '"',
|
|
'-DXCE_SIM_C_PATH="'
|
|
+ (meson.current_source_dir() / '..' / 'src' / 'sim.c')
|
|
+ '"',
|
|
'-DXCE_SIM_LOOP_C_PATH="'
|
|
+ (meson.current_source_dir() / '..' / 'src' / 'sim_loop.c')
|
|
+ '"',
|
|
'-DXCE_SIM_LIB_PATH="'
|
|
+ sched_sim_isolation_lib.full_path()
|
|
+ '"'],
|
|
install: false)
|
|
|
|
# Contract test for decision verification (scheduler-core spec, task 11.4,
|
|
# Req 5 AC 15/18): each core-emitted decision is compared against the
|
|
# corresponding recorded decision at the same logical position when
|
|
# --sim-verify is set, and any mismatch is reported with the exact logical
|
|
# time and decision detail. Unit-level: sim_decision_eq()/sim_log_mismatch()
|
|
# (new ../src/sim_verify.c, task 11.4's sibling file to sim_loop.c) driven
|
|
# directly against hand-built DISPATCH/PEND/PREEMPT decision pairs --
|
|
# including DISPATCH, which sim_loop.c's own real picker thread can never
|
|
# produce live (Req 5 AC 17-adjacent scope boundary: real resource-matching
|
|
# allocation belongs to the not-yet-designed scheduler-allocation spec), so
|
|
# this is the only way to prove that comparator for real. Integration-level:
|
|
# a full sched_sim_driver_run() run against a real QUEUE_UPDATE+JOBS_PUSH
|
|
# fixture feeding the real builtin_fcfs picker to a genuine live PEND
|
|
# decision, --sim-verify both on (self-match reports zero mismatches,
|
|
# altered-copy reports the exact mismatch) and off (no mismatch reported
|
|
# even against an altered fixture) -- mismatch output observed via the same
|
|
# STDOUT_FILENO dup()/dup2() capture technique
|
|
# test_apply_correctness_verification.c's own
|
|
# test_apply_divergent_loud_fence_log_omits_false_peer_hwm_clause
|
|
# establishes for ERROR()'s tlog early-print path. Same white-box #include
|
|
# pattern as sched_sim_event_loop_test (core.c, core_clock.c, core_events.c,
|
|
# core_session.c, decision.c, plugin.c, plugins/builtin_fcfs.c, recording.c,
|
|
# sim.c, sim_loop.c, snapshot.c/snapshot_wire.c/snapshot_load.c) PLUS the
|
|
# new ../src/sim_verify.c under test.
|
|
sched_sim_verify_test = executable(
|
|
'sched_sim_verify_test',
|
|
['test_sim_verify.c'],
|
|
include_directories:[incdir],
|
|
dependencies: [scheduler_test_deps],
|
|
install: false)
|
|
|
|
# Register as meson tests so `meson test --suite scheduler` runs them all.
|
|
# detect_leaks=0 matches the suite convention (fixtures are not torn down).
|
|
test('scheduler test', scheduler_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler job-update test', sched_job_update_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler handlers test', sched_handlers_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler job-array expand test', sched_job_array_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler job-array wire e2e test', sched_job_array_wire_e2e_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler picker iface test', sched_picker_iface_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler picker registration test', sched_picker_registration_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler picker config fields test', sched_picker_config_fields_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler picker fcfs test', sched_picker_fcfs_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler picker fcfs session test', sched_picker_fcfs_session_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler picker wiring test', sched_picker_wiring_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler picker core loop boundary test',
|
|
sched_picker_core_loop_boundary_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler pendreason factory audit test',
|
|
sched_pendreason_factory_audit_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler core isolation test',
|
|
sched_core_isolation_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler core jbuf capacity pin test',
|
|
sched_core_jbuf_capacity_pin_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler core events test',
|
|
sched_core_events_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler core session test',
|
|
sched_core_session_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler core clock test',
|
|
sched_core_clock_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler core full cycle test',
|
|
sched_core_full_cycle_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler driver translate test',
|
|
sched_driver_translate_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler driver decision cb test',
|
|
sched_driver_decision_cb_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler driver session ordering test',
|
|
sched_driver_session_ordering_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler snapshot save load test',
|
|
sched_snapshot_save_load_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler recording path validation test',
|
|
sched_recording_path_validation_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler record lifecycle test',
|
|
sched_record_lifecycle_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler admin request test',
|
|
sched_admin_request_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler sim startup test',
|
|
sched_sim_startup_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler sim event loop test',
|
|
sched_sim_event_loop_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler sim isolation test',
|
|
sched_sim_isolation_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
|
test('scheduler sim verify test',
|
|
sched_sim_verify_test, suite: 'scheduler',
|
|
env: ['ASAN_OPTIONS=detect_leaks=0'])
|