lardon3d/tests/test_tui_model.c
fy59 b84f860d86 refactor(core): freeze global maintenance baseline
Complete the A-to-Z Lardon3D maintenance and coherence pass.

Generalize host resource policy, remove the global CPU12 ceiling, preserve
host CPU/RAM reserves, scale Task capabilities through the Resource Governor,
and validate deterministic parallel GV execution.

Migrate Project DB to v23 with data-driven camera, lens, optical configuration
and calibration profiles, including manual lenses without EXIF.

Integrate safe optional LARDON SSD swap/scratch control with Governor and F10
drain/safe-to-unplug semantics.

Refactor the ncurses TUI into a runtime observatory with durable progress,
elapsed time, smoothed ETA, throughput, resource telemetry, Governor state,
optics workflow, colors and compact/no-color fallbacks.

Reconcile Queue lifetime, persistence, concurrency, comments, tests,
README, AGENTS and canonical documentation.

GLOBAL_MAINTENANCE_AUDIT=PASS/FROZEN
2026-09-01 08:00:47 +02:00

383 lines
15 KiB
C

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lardon3d/tui_model.h>
#define CHECK(condition) \
do { \
if (!(condition)) { \
(void)fprintf(stderr, "TUI model failure line %d: %s\n", \
__LINE__, #condition); \
return false; \
} \
} while (0)
/* tui_model only needs the bounded state label; production supplies the same
* public Task helper without changing any model decision. */
const char *
lardon3d_task_state_name(Lardon3DTaskState state)
{
static const char *const names[] = {
"PENDING", "RUNNING", "PAUSED", "CANCELLED", "FAILED", "COMPLETED",
};
return state >= TASK_PENDING && state <= TASK_COMPLETED
? names[state] : "UNKNOWN";
}
static Lardon3DTaskObservation
typed_task(const char *kind, Lardon3DTaskState state, unsigned int progress)
{
Lardon3DTaskObservation task = {
.id = 7,
.has_task_kind = true,
.task_kind_version = 1,
.progress = progress,
.state = state,
};
(void)snprintf(task.name, sizeof(task.name), "test task");
(void)snprintf(task.task_kind, sizeof(task.task_kind), "%s", kind);
(void)snprintf(task.message, sizeof(task.message), "observed");
return task;
}
static bool
test_viewports_and_palette(void)
{
CHECK(lardon3d_tui_viewport_classify(30, 100)
== LARDON3D_TUI_VIEWPORT_FULL);
CHECK(lardon3d_tui_viewport_classify(20, 72)
== LARDON3D_TUI_VIEWPORT_COMPACT);
CHECK(lardon3d_tui_viewport_classify(15, 60)
== LARDON3D_TUI_VIEWPORT_COMPACT);
CHECK(lardon3d_tui_viewport_classify(14, 60)
== LARDON3D_TUI_VIEWPORT_TOO_SMALL);
CHECK(lardon3d_tui_viewport_classify(15, 59)
== LARDON3D_TUI_VIEWPORT_TOO_SMALL);
Lardon3DTuiPalette palette;
lardon3d_tui_palette_plan(false, 0, &palette);
CHECK(!palette.color_enabled);
CHECK(palette.color_pair[LARDON3D_TUI_SEMANTIC_ERROR] == 0);
CHECK((palette.attributes[LARDON3D_TUI_SEMANTIC_ERROR]
& LARDON3D_TUI_STYLE_BOLD) != 0);
CHECK((palette.attributes[LARDON3D_TUI_SEMANTIC_DIM]
& LARDON3D_TUI_STYLE_DIM) != 0);
lardon3d_tui_palette_plan(true, 4, &palette);
CHECK(palette.color_enabled);
CHECK(palette.color_pair[LARDON3D_TUI_SEMANTIC_HEALTHY] == 1);
CHECK(palette.color_pair[LARDON3D_TUI_SEMANTIC_ERROR] == 3);
CHECK(palette.color_pair[LARDON3D_TUI_SEMANTIC_GPU] == 0);
lardon3d_tui_palette_plan(true, 16, &palette);
CHECK(palette.color_pair[LARDON3D_TUI_SEMANTIC_GPU] == 4);
CHECK(palette.color_pair[LARDON3D_TUI_SEMANTIC_CPU] == 5);
CHECK(palette.color_pair[LARDON3D_TUI_SEMANTIC_SSD] == 6);
return true;
}
static bool
test_stage_matrix(void)
{
static const struct {
const char *kind;
Lardon3DTuiStage stage;
} cases[] = {
{"acquisition_campaign.run", LARDON3D_TUI_STAGE_ACQUISITION},
{"raw.develop", LARDON3D_TUI_STAGE_RAW},
{"photo_quality.triage", LARDON3D_TUI_STAGE_QUALITY},
{"features.extract", LARDON3D_TUI_STAGE_FEATURES},
{"features.extract.sift", LARDON3D_TUI_STAGE_FEATURES},
{"features.extract.rootsift", LARDON3D_TUI_STAGE_FEATURES},
{"visual_index.update", LARDON3D_TUI_STAGE_VISUAL_INDEX},
{"candidate_pair.generate", LARDON3D_TUI_STAGE_CANDIDATE},
{"matcher.run", LARDON3D_TUI_STAGE_MATCHER},
{"geometric_verifier.run", LARDON3D_TUI_STAGE_GV},
{"track_builder.run", LARDON3D_TUI_STAGE_TRACKS},
{"sparse_sfm.run", LARDON3D_TUI_STAGE_SPARSE_SFM},
{"incremental_reconstruction.run", LARDON3D_TUI_STAGE_SPARSE_SFM},
};
for (size_t index = 0; index < sizeof(cases) / sizeof(cases[0]); ++index) {
Lardon3DTaskObservation task = typed_task(
cases[index].kind, TASK_RUNNING, 25);
Lardon3DTuiStageView stages[LARDON3D_TUI_STAGE_COUNT];
lardon3d_tui_stage_views_build(true, &task, 1,
LARDON3D_RESOURCE_PRESSURE_GREEN, stages);
CHECK(stages[cases[index].stage].state
== LARDON3D_TUI_STAGE_RUNNING);
CHECK(stages[LARDON3D_TUI_STAGE_DENSE].state
== LARDON3D_TUI_STAGE_NOT_APPLICABLE);
}
static const struct {
Lardon3DTaskState task_state;
Lardon3DResourcePressure pressure;
Lardon3DTuiStageState expected;
} states[] = {
{TASK_PENDING, LARDON3D_RESOURCE_PRESSURE_GREEN,
LARDON3D_TUI_STAGE_QUEUED},
{TASK_PENDING, LARDON3D_RESOURCE_PRESSURE_YELLOW,
LARDON3D_TUI_STAGE_THROTTLED},
{TASK_RUNNING, LARDON3D_RESOURCE_PRESSURE_RED,
LARDON3D_TUI_STAGE_RUNNING},
{TASK_PAUSED, LARDON3D_RESOURCE_PRESSURE_GREEN,
LARDON3D_TUI_STAGE_BLOCKED},
{TASK_CANCELLED, LARDON3D_RESOURCE_PRESSURE_GREEN,
LARDON3D_TUI_STAGE_BLOCKED},
{TASK_COMPLETED, LARDON3D_RESOURCE_PRESSURE_GREEN,
LARDON3D_TUI_STAGE_COMPLETE},
{TASK_FAILED, LARDON3D_RESOURCE_PRESSURE_GREEN,
LARDON3D_TUI_STAGE_FAILED},
};
for (size_t index = 0; index < sizeof(states) / sizeof(states[0]); ++index) {
Lardon3DTaskObservation task = typed_task(
"raw.develop", states[index].task_state, 20);
Lardon3DTuiStageView stages[LARDON3D_TUI_STAGE_COUNT];
lardon3d_tui_stage_views_build(true, &task, 1,
states[index].pressure, stages);
CHECK(stages[LARDON3D_TUI_STAGE_RAW].state
== states[index].expected);
}
/* Live execution wins over a newer pending record of the same kind; the
* Queue deliberately exposes newest live submissions first. */
Lardon3DTaskObservation concurrent[2] = {
typed_task("matcher.run", TASK_PENDING, 0),
typed_task("matcher.run", TASK_RUNNING, 25),
};
Lardon3DTuiStageView concurrent_stages[LARDON3D_TUI_STAGE_COUNT];
lardon3d_tui_stage_views_build(true, concurrent, 2,
LARDON3D_RESOURCE_PRESSURE_GREEN, concurrent_stages);
CHECK(concurrent_stages[LARDON3D_TUI_STAGE_MATCHER].state
== LARDON3D_TUI_STAGE_RUNNING);
Lardon3DTuiStageView empty[LARDON3D_TUI_STAGE_COUNT];
lardon3d_tui_stage_views_build(false, NULL, 0,
LARDON3D_RESOURCE_PRESSURE_GREEN, empty);
CHECK(empty[LARDON3D_TUI_STAGE_ACQUISITION].state
== LARDON3D_TUI_STAGE_NOT_READY);
CHECK(empty[LARDON3D_TUI_STAGE_DENSE].state
== LARDON3D_TUI_STAGE_NOT_APPLICABLE);
return true;
}
static bool
test_progress_eta(void)
{
Lardon3DTuiProgressTracker tracker = {0};
Lardon3DTuiProgressView view;
Lardon3DTuiProgressSample sample = {
.task_id = 42,
.monotonic_ns = UINT64_C(1000000000),
.task_state = TASK_RUNNING,
.typed_task = true,
.progress_percent = 20,
.durable_counts_known = true,
.durable_completed = 20,
.durable_total = 100,
};
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.percentage == 20);
CHECK(view.eta_state == LARDON3D_TUI_ETA_CALCULATING);
CHECK(view.resumed_prefix_excluded);
CHECK(!view.throughput_known);
sample.monotonic_ns += UINT64_C(1000000000);
sample.durable_completed = 30;
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.eta_state == LARDON3D_TUI_ETA_CALCULATING);
CHECK(!view.throughput_known);
sample.monotonic_ns += UINT64_C(1000000000);
sample.durable_completed = 50;
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.eta_state == LARDON3D_TUI_ETA_KNOWN);
CHECK(view.eta_seconds == 4);
CHECK(view.units_per_second > 13.4 && view.units_per_second < 13.6);
sample.monotonic_ns += UINT64_C(5000000000);
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.eta_state == LARDON3D_TUI_ETA_STALLED);
sample.pressure_limited = true;
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.eta_state == LARDON3D_TUI_ETA_THROTTLED);
sample.pressure_limited = false;
sample.task_state = TASK_COMPLETED;
sample.durable_completed = 100;
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.percentage == 100);
CHECK(view.eta_state == LARDON3D_TUI_ETA_COMPLETE);
CHECK(view.eta_seconds == 0);
sample = (Lardon3DTuiProgressSample) {
.task_id = 44,
.monotonic_ns = UINT64_C(19000000000),
.task_state = TASK_RUNNING,
.typed_task = true,
.progress_percent = 99,
.durable_counts_known = true,
.durable_completed = UINT64_MAX - 1,
.durable_total = UINT64_MAX,
};
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.percentage == 99);
sample = (Lardon3DTuiProgressSample) {
.task_id = 45,
.monotonic_ns = UINT64_C(30000000000),
.task_state = TASK_PENDING,
.typed_task = true,
.progress_percent = 0,
.durable_counts_known = true,
.durable_total = 100,
};
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
sample.monotonic_ns += UINT64_C(10000000000);
sample.task_state = TASK_RUNNING;
sample.durable_completed = 10;
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(!view.throughput_known
&& view.eta_state == LARDON3D_TUI_ETA_CALCULATING);
sample.monotonic_ns += UINT64_C(5000000000);
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.eta_state == LARDON3D_TUI_ETA_STALLED);
sample = (Lardon3DTuiProgressSample) {
.task_id = 43,
.monotonic_ns = UINT64_C(20000000000),
.task_state = TASK_PENDING,
.progress_percent = 0,
};
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.eta_state == LARDON3D_TUI_ETA_INDETERMINATE);
CHECK(!view.durable_counts_known);
sample = (Lardon3DTuiProgressSample) {
.task_id = 46,
.monotonic_ns = UINT64_C(40000000000),
.task_state = TASK_COMPLETED,
.typed_task = true,
.progress_percent = 100,
.durable_counts_known = true,
.durable_completed = 2,
.durable_total = 7,
};
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.integrity_error);
CHECK(view.durable_counts_known && view.completed == 2 && view.total == 7);
CHECK(view.percentage_known && view.percentage == 28);
CHECK(view.eta_state == LARDON3D_TUI_ETA_INDETERMINATE);
sample = (Lardon3DTuiProgressSample) {
.task_id = 47,
.monotonic_ns = UINT64_C(41000000000),
.task_state = TASK_COMPLETED,
.typed_task = true,
.progress_percent = 100,
};
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(!view.percentage_known && !view.runtime_percentage);
CHECK(view.eta_state == LARDON3D_TUI_ETA_INDETERMINATE);
sample = (Lardon3DTuiProgressSample) {
.task_id = 48,
.monotonic_ns = UINT64_C(42000000000),
.task_state = TASK_COMPLETED,
.progress_percent = 100,
};
CHECK(lardon3d_tui_progress_update(&tracker, &sample, &view));
CHECK(view.percentage_known && view.percentage == 100);
CHECK(view.runtime_percentage);
CHECK(view.eta_state == LARDON3D_TUI_ETA_COMPLETE);
CHECK(!lardon3d_tui_progress_update(&tracker,
&(Lardon3DTuiProgressSample) {
.task_id = 1,
.progress_percent = 101,
}, &view));
return true;
}
static bool
test_durable_stage_truth_and_keys(void)
{
Lardon3DTaskObservation incomplete = typed_task(
"raw.develop", TASK_COMPLETED, 100);
incomplete.durable_progress_known = true;
incomplete.durable_completed = 2;
incomplete.durable_total = 7;
Lardon3DTuiStageView stages[LARDON3D_TUI_STAGE_COUNT];
lardon3d_tui_stage_views_build(true, &incomplete, 1,
LARDON3D_RESOURCE_PRESSURE_GREEN, stages);
CHECK(stages[LARDON3D_TUI_STAGE_RAW].state
== LARDON3D_TUI_STAGE_BLOCKED);
CHECK(strstr(stages[LARDON3D_TUI_STAGE_RAW].reason, "2/7") != NULL);
Lardon3DTaskObservation unknown = typed_task(
"raw.develop", TASK_COMPLETED, 100);
lardon3d_tui_stage_views_build(true, &unknown, 1,
LARDON3D_RESOURCE_PRESSURE_GREEN, stages);
CHECK(stages[LARDON3D_TUI_STAGE_RAW].state
== LARDON3D_TUI_STAGE_COMPLETE);
CHECK(strstr(stages[LARDON3D_TUI_STAGE_RAW].reason,
"indeterminate") != NULL);
Lardon3DTuiKeyContract keys = lardon3d_tui_key_contract(
LARDON3D_TUI_INTERACTION_TEXT_INPUT);
CHECK(keys.enter && keys.escape && keys.f10);
CHECK(!keys.cancel_import && !keys.quit && !keys.navigate);
keys = lardon3d_tui_key_contract(
LARDON3D_TUI_INTERACTION_IMPORT_RUNNING);
CHECK(keys.cancel_import && keys.f10);
CHECK(!keys.enter && !keys.escape && !keys.quit && !keys.navigate);
keys = lardon3d_tui_key_contract(LARDON3D_TUI_INTERACTION_IDLE);
CHECK(keys.escape && keys.f10 && keys.quit && keys.navigate);
CHECK(!keys.enter && !keys.cancel_import);
return true;
}
static bool
test_labels_and_optics(void)
{
CHECK(strcmp(lardon3d_tui_pressure_name(
LARDON3D_RESOURCE_PRESSURE_GREEN), "GREEN") == 0);
CHECK(strcmp(lardon3d_tui_pressure_name(
LARDON3D_RESOURCE_PRESSURE_YELLOW), "YELLOW") == 0);
CHECK(strcmp(lardon3d_tui_pressure_name(
LARDON3D_RESOURCE_PRESSURE_RED), "RED") == 0);
for (int backend = LARDON3D_TUI_GPU_BACKEND_UNKNOWN;
backend <= LARDON3D_TUI_GPU_BACKEND_MIXED; ++backend) {
CHECK(strcmp(lardon3d_tui_gpu_backend_name(
(Lardon3DTuiGpuBackendStatus)backend), "UNKNOWN") != 0
|| backend == LARDON3D_TUI_GPU_BACKEND_UNKNOWN);
}
CHECK(lardon3d_tui_optics_classify(false, false, false, false, 0,
false, false) == LARDON3D_TUI_OPTICS_NO_PROJECT);
CHECK(lardon3d_tui_optics_classify(true, false, false, true, 0,
false, false) == LARDON3D_TUI_OPTICS_UNRESOLVED);
CHECK(lardon3d_tui_optics_classify(true, true, true, true, 0,
false, false) == LARDON3D_TUI_OPTICS_CONFIGURATION_ONLY);
CHECK(lardon3d_tui_optics_classify(true, true, true, false, 2,
false, false) == LARDON3D_TUI_OPTICS_SELECTION_REQUIRED);
CHECK(lardon3d_tui_optics_classify(true, true, true, false, 1,
true, true) == LARDON3D_TUI_OPTICS_SELECTED);
CHECK(lardon3d_tui_optics_classify(true, true, true, false, 1,
true, false) == LARDON3D_TUI_OPTICS_INCOMPATIBLE);
CHECK(strstr(lardon3d_tui_optics_status_explanation(
LARDON3D_TUI_OPTICS_CONFIGURATION_ONLY, true),
"Manual/no-EXIF") != NULL);
return true;
}
int
main(void)
{
return test_viewports_and_palette()
&& test_stage_matrix()
&& test_progress_eta()
&& test_durable_stage_truth_and_keys()
&& test_labels_and_optics()
? EXIT_SUCCESS : EXIT_FAILURE;
}