Adds xce_version_field()/xce_compose_version() (utils.h/utils.c) to compose a version string carrying commit id + build time, rendering "unknown" for either field when its underlying meson-supplied define is empty -- ready for controller/scheduler/worker to consume. MK_VERSION/STR (the pre-existing, compile-time-only macro pair) can't express this empty-to-"unknown" substitution at the preprocessor level, so this is a runtime mechanism instead. Round 1 review REJECTED an earlier version of this task for silently orphaning MK_VERSION/STR (the task explicitly asked to "extend" them) and for leaving an undocumented architectural trap: argp_program_version is a file-scope const char* initialized at compile time, which cannot call a runtime function -- consuming code must instead compute into a static buffer inside main() and assign the pointer before argp_parse() runs. Round 2 adds an explicit SUPERSEDED doc comment on MK_VERSION/STR plus NOTE FOR CALLERS guidance covering both points, and was APPROVED. Also expands task 3.2's boundary to include controller/testsuite/test.c (a third MK_VERSION/STR call site discovered during review) so no caller of the superseded macro survives implementation. 5 new unit tests in test_utils.c (RED->GREEN, TDD). Task 3.1/13 of build-time-versioning. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
207 lines
6.1 KiB
C
207 lines
6.1 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unity/unity.h>
|
|
|
|
#include "utils.h"
|
|
#include "xce/types.h"
|
|
|
|
static void test_timewindow_config_dup()
|
|
{
|
|
/* clang-format off */
|
|
xce_timewindow_config_t win1 = {
|
|
.nsegs = 2,
|
|
.segs = (xce_timewindow_seg_config_t*[]) {
|
|
&(xce_timewindow_seg_config_t) {
|
|
.tz = "GMT-8",
|
|
.open = &(xce_timewindow_time_t) {
|
|
.day = 0,
|
|
.hour = 9,
|
|
.min = 0 },
|
|
.close = &(xce_timewindow_time_t) {
|
|
.day = 0,
|
|
.hour = 17,
|
|
.min = 59 } },
|
|
&(xce_timewindow_seg_config_t) {
|
|
.tz = "GMT-8",
|
|
.open = &(xce_timewindow_time_t) {
|
|
.day = 5,
|
|
.hour = 21,
|
|
.min = 0 },
|
|
.close = &(xce_timewindow_time_t) {
|
|
.day = 7,
|
|
.hour = 20,
|
|
.min = 59 } } }
|
|
};
|
|
/* clang-format on */
|
|
xce_timewindow_config_t* win2 = NULL;
|
|
|
|
win2 = dup_timewindow_config(&win1);
|
|
|
|
TEST_ASSERT_TRUE(win2 != NULL);
|
|
TEST_ASSERT_TRUE(win2->nsegs == 2);
|
|
TEST_ASSERT_TRUE((strcmp(win2->segs[0]->tz, "GMT-8") == 0));
|
|
TEST_ASSERT_TRUE(win2->segs[0]->open->day == 0);
|
|
TEST_ASSERT_TRUE(win2->segs[0]->open->hour == 9);
|
|
TEST_ASSERT_TRUE(win2->segs[0]->open->min == 0);
|
|
TEST_ASSERT_TRUE(win2->segs[0]->close->day == 0);
|
|
TEST_ASSERT_TRUE(win2->segs[0]->close->hour == 17);
|
|
TEST_ASSERT_TRUE(win2->segs[0]->close->min == 59);
|
|
|
|
TEST_ASSERT_TRUE((strcmp(win2->segs[1]->tz, "GMT-8") == 0));
|
|
TEST_ASSERT_TRUE(win2->segs[1]->open->day == 5);
|
|
TEST_ASSERT_TRUE(win2->segs[1]->open->hour == 21);
|
|
TEST_ASSERT_TRUE(win2->segs[1]->open->min == 0);
|
|
TEST_ASSERT_TRUE(win2->segs[1]->close->day == 7);
|
|
TEST_ASSERT_TRUE(win2->segs[1]->close->hour == 20);
|
|
TEST_ASSERT_TRUE(win2->segs[1]->close->min == 59);
|
|
|
|
free_timewindow_config(win2);
|
|
|
|
return;
|
|
}
|
|
|
|
void test_dup_threshold()
|
|
{
|
|
/* clang-format off */
|
|
xce_res_threshold_t* threshold[2] = {
|
|
&(xce_res_threshold_t) {
|
|
.name = "r15s",
|
|
.sched = 1.0,
|
|
.stop = 5.0 },
|
|
&(xce_res_threshold_t) {
|
|
.name = "r1m",
|
|
.sched = 0.9,
|
|
.stop = 4.5 }
|
|
};
|
|
/* clang-format on */
|
|
xce_res_threshold_t** threshold2 = NULL;
|
|
|
|
threshold2 = dup_resource_thresholds(threshold, 2);
|
|
|
|
TEST_ASSERT_TRUE(threshold2 != NULL);
|
|
|
|
TEST_ASSERT_TRUE(strcmp(threshold[0]->name, threshold2[0]->name) == 0);
|
|
TEST_ASSERT_TRUE(strcmp(threshold[1]->name, threshold2[1]->name) == 0);
|
|
TEST_ASSERT_TRUE(float_equal(threshold[0]->sched, threshold2[0]->sched));
|
|
TEST_ASSERT_TRUE(float_equal(threshold[0]->stop, threshold2[0]->stop));
|
|
TEST_ASSERT_TRUE(float_equal(threshold[1]->sched, threshold2[1]->sched));
|
|
TEST_ASSERT_TRUE(float_equal(threshold[1]->stop, threshold2[1]->stop));
|
|
|
|
free_resource_thresholds(threshold2, 2);
|
|
return;
|
|
}
|
|
|
|
void test_roundup(void)
|
|
{
|
|
TEST_ASSERT_TRUE(ROUNDUP(1, 3) == 8);
|
|
TEST_ASSERT_TRUE(ROUNDUP(9, 4) == 16);
|
|
TEST_ASSERT_TRUE(ROUNDUP(31, 5) == 32);
|
|
}
|
|
|
|
void test_extract_bits(void)
|
|
{
|
|
uint64_t id = 0xfefefefeffff;
|
|
|
|
TEST_ASSERT_TRUE(EXTRACT_BITS(id, 0, 4) == 0xf);
|
|
TEST_ASSERT_TRUE(EXTRACT_BITS(id, 16, 4) == 0xe);
|
|
TEST_ASSERT_TRUE(EXTRACT_BITS(id, 16, 8) == 0xfe);
|
|
TEST_ASSERT_TRUE(EXTRACT_BITS(id, 20, 12) == 0xfef);
|
|
TEST_ASSERT_TRUE(EXTRACT_BITS(id, 44, 4) == 0xf);
|
|
TEST_ASSERT_TRUE(EXTRACT_BITS(id, 40, 8) == 0xfe);
|
|
}
|
|
|
|
static void do_parse_jobid_test(uint64_t jobid)
|
|
{
|
|
uint64_t jobid2;
|
|
int rc;
|
|
char buf[256];
|
|
|
|
rc = fmt_jobid(buf, sizeof(buf), jobid);
|
|
printf("\t\tCheck jobid %s\n", buf);
|
|
|
|
TEST_ASSERT_TRUE(rc > 0);
|
|
rc = parse_jobid(&jobid2, buf);
|
|
TEST_ASSERT_TRUE(rc >= 0);
|
|
TEST_ASSERT_TRUE(jobid == jobid2);
|
|
}
|
|
|
|
void test_parse_jobid1(void)
|
|
{
|
|
uint64_t jobid = 0x400691c2fd498003;
|
|
|
|
do_parse_jobid_test(jobid);
|
|
}
|
|
|
|
void test_parse_jobid2(void)
|
|
{
|
|
uint64_t jobid = 0x691d3c1d00013;
|
|
|
|
do_parse_jobid_test(jobid);
|
|
}
|
|
|
|
/* build-time-versioning task 3.1: xce_version_field()'s empty/NULL ->
|
|
* "unknown" fallback, used to keep the composed --version string free of
|
|
* blank gaps when VERSION_COMMIT_ID/VERSION_BUILD_TIME are unset. */
|
|
void test_version_field_unknown_fallback(void)
|
|
{
|
|
TEST_ASSERT_EQUAL_STRING("unknown", xce_version_field(NULL));
|
|
TEST_ASSERT_EQUAL_STRING("unknown", xce_version_field(""));
|
|
}
|
|
|
|
void test_version_field_passthrough(void)
|
|
{
|
|
TEST_ASSERT_EQUAL_STRING("a1b2c3d", xce_version_field("a1b2c3d"));
|
|
TEST_ASSERT_EQUAL_STRING("2026-08-20T10:00:00Z",
|
|
xce_version_field("2026-08-20T10:00:00Z"));
|
|
}
|
|
|
|
void test_compose_version_both_set(void)
|
|
{
|
|
char buf[128];
|
|
int n;
|
|
|
|
n = xce_compose_version(buf, sizeof(buf), "xce v0.0.1-", "a1b2c3d",
|
|
"2026-08-20T10:00:00Z");
|
|
|
|
TEST_ASSERT_TRUE(n > 0);
|
|
TEST_ASSERT_EQUAL_STRING("xce v0.0.1-a1b2c3d built 2026-08-20T10:00:00Z",
|
|
buf);
|
|
}
|
|
|
|
void test_compose_version_both_unset(void)
|
|
{
|
|
char buf[128];
|
|
|
|
xce_compose_version(buf, sizeof(buf), "xce v0.0.1-", "", "");
|
|
|
|
/* both fields must render as the literal word "unknown", never a
|
|
* blank/garbled gap (e.g. NOT "xce v0.0.1- built "). */
|
|
TEST_ASSERT_EQUAL_STRING("xce v0.0.1-unknown built unknown", buf);
|
|
}
|
|
|
|
void test_compose_version_null_fields(void)
|
|
{
|
|
char buf[128];
|
|
|
|
xce_compose_version(buf, sizeof(buf), "xce v0.0.1-", NULL, NULL);
|
|
|
|
TEST_ASSERT_EQUAL_STRING("xce v0.0.1-unknown built unknown", buf);
|
|
}
|
|
|
|
void test_utils(void)
|
|
{
|
|
printf("\n>> utils test\n\n");
|
|
|
|
RUN_TEST(test_timewindow_config_dup);
|
|
RUN_TEST(test_dup_threshold);
|
|
RUN_TEST(test_roundup);
|
|
RUN_TEST(test_extract_bits);
|
|
RUN_TEST(test_parse_jobid1);
|
|
RUN_TEST(test_parse_jobid2);
|
|
RUN_TEST(test_version_field_unknown_fallback);
|
|
RUN_TEST(test_version_field_passthrough);
|
|
RUN_TEST(test_compose_version_both_set);
|
|
RUN_TEST(test_compose_version_both_unset);
|
|
RUN_TEST(test_compose_version_null_fields);
|
|
}
|