/* test_joblist_display.c -- xce-test-cli-ux task 2.5 remediation * * Unity unit tests for joblist_display.c's worker_status_str()/ * job_state_str()/format_job_time(), extracted from test.c (see * joblist_display.h for the full extraction rationale). All three fixes * these functions carry (bug-1711) were already verified correct by review * against the pre-extraction code; this file is the permanent, automated * proof that (a) the fixes behave as intended and (b) the extraction did not * change any observed behavior -- in particular, that the pre-existing * branches these fixes sit alongside do NOT regress into the new fallback * branches. * * Standalone Unity main (this binary is its own test executable -- see * meson.build's joblist_display_unit_test), mirroring * resval_typeresolve_unit_test's/host_runusage_unit_test's own pattern. */ #include #include #include #include #include "ctrlstatus_fmt.h" /* ctrlstatus_format_time_ms -- used directly * below to prove format_job_time() genuinely * delegates to it (byte-identical output), * rather than a parallel reimplementation that * could silently drift. */ #include "joblist_display.h" #include "xce/types.h" /* XCE_WORKER_*, JOB_STAT_* bit definitions */ /* ------------------------------------------------------------------ */ /* worker_status_str() */ /* ------------------------------------------------------------------ */ /* Requirement 11.1's non-regression proof: admin-set CLOSED must still take * priority over the dynamic UNAVAIL bit when both are set (task 8.6's * config-upgrade-snapshot soak relies on this precedence). */ static void test_worker_status_closed_over_unavail_priority(void) { char* rc = worker_status_str(XCE_WORKER_CLOSED | XCE_WORKER_UNAVAIL); TEST_ASSERT_EQUAL_STRING_MESSAGE( "CLOSED", rc, "CLOSED must take priority over UNAVAIL when both bits are set"); } static void test_worker_status_unavail_alone(void) { TEST_ASSERT_EQUAL_STRING("UNAVAIL", worker_status_str(XCE_WORKER_UNAVAIL)); } static void test_worker_status_lockedu_alone(void) { TEST_ASSERT_EQUAL_STRING("LOCKU", worker_status_str(XCE_WORKER_LOCKEDU)); } static void test_worker_status_lockedw_alone(void) { TEST_ASSERT_EQUAL_STRING("LOCKW", worker_status_str(XCE_WORKER_LOCKEDW)); } static void test_worker_status_busy_alone(void) { TEST_ASSERT_EQUAL_STRING("BUSY", worker_status_str(XCE_WORKER_BUSY)); } static void test_worker_status_zero_is_ok(void) { TEST_ASSERT_EQUAL_STRING("OK", worker_status_str(0)); } /* Requirement 11.1 (bug-1711's actual fix): an unrecognized status bit * (XCE_WORKER_CLOSED_BY_ADMIN -- a real bit in xce/types.h, but not one * worker_status_str() checks for) must fall through to "UNKNOWN", correctly * spelled -- previously misspelled "UNKOWN". */ static void test_worker_status_unrecognized_bit_is_unknown(void) { char* rc = worker_status_str(XCE_WORKER_CLOSED_BY_ADMIN); TEST_ASSERT_EQUAL_STRING_MESSAGE( "UNKNOWN", rc, "an unrecognized status bit must report correctly-spelled UNKNOWN " "(Requirement 11.1)"); } /* ------------------------------------------------------------------ */ /* job_state_str() */ /* ------------------------------------------------------------------ */ static void test_job_state_pend(void) { TEST_ASSERT_EQUAL_STRING("PEND", job_state_str(JOB_STAT_PEND)); } static void test_job_state_pend_psusp(void) { TEST_ASSERT_EQUAL_STRING( "PSUSP", job_state_str(JOB_STAT_PEND | JOB_STAT_PSUSP)); } static void test_job_state_run(void) { TEST_ASSERT_EQUAL_STRING("RUN", job_state_str(JOB_STAT_RUN)); } /* Non-regression proof (review's specific request): RUN+USUSP must still * resolve to "USUSP", not fall through to the new RUN+EXIT fallback. */ static void test_job_state_run_ususp(void) { TEST_ASSERT_EQUAL_STRING_MESSAGE( "USUSP", job_state_str(JOB_STAT_RUN | JOB_STAT_USUSP), "RUN+USUSP must not regress into the new RUN+EXIT fallback"); } /* Non-regression proof (review's specific request): RUN+SSUSP must still * resolve to "SSUSP", not fall through to the new RUN+EXIT fallback. */ static void test_job_state_run_ssusp(void) { TEST_ASSERT_EQUAL_STRING_MESSAGE( "SSUSP", job_state_str(JOB_STAT_RUN | JOB_STAT_SSUSP), "RUN+SSUSP must not regress into the new RUN+EXIT fallback"); } /* Requirement 11.7 (bug-1711's actual fix): RUN+EXIT (e.g. a worker-reported * FAIL received without a prior FINISH) must report the raw-state fallback * "RUN(0x%x)", mirroring print_jobinfo's own state_raw guard for the same * ambiguity, instead of silently printing plain "RUN". */ static void test_job_state_run_exit_fallback(void) { int state = JOB_STAT_RUN | JOB_STAT_EXIT; char expected[32]; char* rc = job_state_str(state); snprintf(expected, sizeof(expected), "RUN(0x%x)", state); TEST_ASSERT_EQUAL_STRING_MESSAGE( expected, rc, "RUN+EXIT must report the raw-state fallback RUN(0x%x) " "(Requirement 11.7)"); } static void test_job_state_exit(void) { TEST_ASSERT_EQUAL_STRING("EXIT", job_state_str(JOB_STAT_EXIT)); } static void test_job_state_done(void) { TEST_ASSERT_EQUAL_STRING("DONE", job_state_str(JOB_STAT_DONE)); } static void test_job_state_error(void) { TEST_ASSERT_EQUAL_STRING("ERROR", job_state_str(JOB_STAT_ERR)); } /* Default/unknown branch: no recognized bit set at all. */ static void test_job_state_unknown_default(void) { TEST_ASSERT_EQUAL_STRING("UNKNOWN", job_state_str(0)); } /* ------------------------------------------------------------------ */ /* format_job_time() */ /* ------------------------------------------------------------------ */ /* Requirement 11.5: a zero job time_t (event has not happened yet) must * print "-", not a misleading 1970-01-01 epoch date. */ static void test_format_job_time_zero_is_dash(void) { char buf[32]; const char* rc = format_job_time((time_t)0, buf, sizeof(buf)); TEST_ASSERT_EQUAL_STRING("-", rc); } /* Requirement 11.5's delegation proof: format_job_time() is supposed to be a * thin seconds->milliseconds adapter directly in front of * ctrlstatus_format_time_ms() -- this asserts the two are BYTE-IDENTICAL for * the equivalent millisecond input, proving genuine delegation rather than a * parallel reimplementation that could silently drift from ctrlstatus's own * formatting. */ static void test_format_job_time_matches_ctrlstatus_formatter(void) { time_t t = (time_t)1700000000; /* arbitrary fixed nonzero epoch */ char via_format_job_time[32]; char via_ctrlstatus_direct[32]; const char* rc1; char* rc2; rc1 = format_job_time(t, via_format_job_time, sizeof(via_format_job_time)); rc2 = ctrlstatus_format_time_ms((uint64_t)t * 1000u, via_ctrlstatus_direct, sizeof(via_ctrlstatus_direct)); TEST_ASSERT_NOT_NULL(rc1); TEST_ASSERT_NOT_NULL(rc2); TEST_ASSERT_EQUAL_STRING_MESSAGE( rc2, rc1, "format_job_time() must be byte-identical to a direct " "ctrlstatus_format_time_ms() call on the equivalent millisecond " "input -- it must delegate, not reimplement"); } /* Bonus branch coverage: format_job_time()'s own "-" fallback when the * underlying formatter declines (buffer too small per * ctrlstatus_format_time_ms's documented >= 24-byte contract). */ static void test_format_job_time_buffer_too_small_is_dash(void) { char buf[4]; /* below ctrlstatus_format_time_ms's 24-byte floor */ const char* rc = format_job_time((time_t)1700000000, buf, sizeof(buf)); TEST_ASSERT_EQUAL_STRING("-", rc); } /* ------------------------------------------------------------------ */ /* Standalone Unity main (this binary is its own test executable -- see */ /* meson.build's joblist_display_unit_test) */ /* ------------------------------------------------------------------ */ void setUp(void) { return; } void tearDown(void) { return; } int main(void) { printf("\n>> xce-test-cli-ux task 2.5 remediation: joblist_display " "worker_status_str/job_state_str/format_job_time\n\n"); UNITY_BEGIN(); RUN_TEST(test_worker_status_closed_over_unavail_priority); RUN_TEST(test_worker_status_unavail_alone); RUN_TEST(test_worker_status_lockedu_alone); RUN_TEST(test_worker_status_lockedw_alone); RUN_TEST(test_worker_status_busy_alone); RUN_TEST(test_worker_status_zero_is_ok); RUN_TEST(test_worker_status_unrecognized_bit_is_unknown); RUN_TEST(test_job_state_pend); RUN_TEST(test_job_state_pend_psusp); RUN_TEST(test_job_state_run); RUN_TEST(test_job_state_run_ususp); RUN_TEST(test_job_state_run_ssusp); RUN_TEST(test_job_state_run_exit_fallback); RUN_TEST(test_job_state_exit); RUN_TEST(test_job_state_done); RUN_TEST(test_job_state_error); RUN_TEST(test_job_state_unknown_default); RUN_TEST(test_format_job_time_zero_is_dash); RUN_TEST(test_format_job_time_matches_ctrlstatus_formatter); RUN_TEST(test_format_job_time_buffer_too_small_is_dash); return UNITY_END(); }