myxce/src/scheduler/testsuite/meson.build
Rongsong Shen dceafc6b12
feat(scheduler-core): build normal-mode driver event translation path (task 5.1)
Adds driver.h/driver.c: xce_sched_driver_translate_pending_events()
drains core->pend_updates via the existing dataq-swap pattern (same
mechanism sched_update() uses), translates all 10 xce_sched_update_
request_t op types into the matching xce_core_event_t, and pushes the
batch through xce_core_push_events(). Scoped to xce_sched_core_t*
alone -- pend_updates already lives on core since task 4.1, and this
task is translation-only (no decision publishing, no mode-selection,
no recording -- all later work). handler.c (the enqueue side) is
untouched. First task in Group 5 (Core: Normal-Mode Driver).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dw5WnN3DXStaEaPYfACU1
2026-08-04 04:39:17 +00:00

368 lines
20 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)
# 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'])