diff --git a/src/include/version.h b/src/include/version.h new file mode 100644 index 0000000..3eccde3 --- /dev/null +++ b/src/include/version.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2022 Barcelona Supercomputing Center (BSC) + * SPDX-License-Identifier: MIT */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include "common.h" + +static inline int +version_parse(const char *version, int tuple[3]) +{ + char buf[64]; + + if (strlen(version) >= 64) { + err("parse_version: version too long: %s\n", version); + return -1; + } + + strcpy(buf, version); + + char *str = buf; + char *which[] = { "major", "minor", "patch" }; + char *delim[] = { ".", ".", ".-" }; + char *save = NULL; + + for (int i = 0; i < 3; i++) { + char *num = strtok_r(str, delim[i], &save); + + /* Subsequent calls need NULL as string */ + str = NULL; + + if (num == NULL) { + err("parse_version: missing %s number: %s\n", + which[i], version); + return -1; + } + + errno = 0; + char *endptr = NULL; + int v = (int) strtol(num, &endptr, 10); + + if (errno != 0 || endptr == num || endptr[0] != '\0') { + err("parse_version: failed to parse %s number: %s\n", + which[i], version); + return -1; + } + + if (v < 0) { + err("parse_version: invalid negative %s number: %s\n", + which[i], version); + return -1; + } + + tuple[i] = v; + } + + return 0; +} diff --git a/src/rt/ovni.c b/src/rt/ovni.c index 4de2a2c..6bf4ed8 100644 --- a/src/rt/ovni.c +++ b/src/rt/ovni.c @@ -17,6 +17,7 @@ #include "compat.h" #include "ovni.h" #include "parson.h" +#include "version.h" /* Data per process */ struct ovni_rproc rproc = {0}; @@ -24,57 +25,6 @@ struct ovni_rproc rproc = {0}; /* Data per thread */ _Thread_local struct ovni_rthread rthread = {0}; -static int -parse_version(const char *version, int tuple[3]) -{ - char buf[64]; - - if (strlen(version) >= 64) { - err("parse_version: version too long: %s\n", version); - return -1; - } - - strcpy(buf, version); - - char *str = buf; - char *which[] = { "major", "minor", "patch" }; - char *delim[] = { ".", ".", ".-" }; - char *save = NULL; - - for (int i = 0; i < 3; i++) { - char *num = strtok_r(str, delim[i], &save); - - /* Subsequent calls need NULL as string */ - str = NULL; - - if (num == NULL) { - err("parse_version: missing %s number: %s\n", - which[i], version); - return -1; - } - - errno = 0; - char *endptr = NULL; - int v = (int) strtol(num, &endptr, 10); - - if (errno != 0 || endptr == num || endptr[0] != '\0') { - err("parse_version: failed to parse %s number: %s\n", - which[i], version); - return -1; - } - - if (v < 0) { - err("parse_version: invalid negative %s number: %s\n", - which[i], version); - return -1; - } - - tuple[i] = v; - } - - return 0; -} - void ovni_version_check_str(const char *version) { if (version == NULL) @@ -83,10 +33,10 @@ void ovni_version_check_str(const char *version) int provided[3]; int expected[3]; - if (parse_version(version, provided) != 0) + if (version_parse(version, provided) != 0) die("failed to parse provided version \"%s\"\n", version); - if (parse_version(OVNI_LIB_VERSION, expected) != 0) + if (version_parse(OVNI_LIB_VERSION, expected) != 0) die("failed to parse expected version \"%s\"\n", OVNI_LIB_VERSION); /* Match the major */ diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index acffe39..b6575cc 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -1,4 +1,4 @@ # Copyright (c) 2022 Barcelona Supercomputing Center (BSC) # SPDX-License-Identifier: GPL-3.0-or-later -# Example: unit_test(abc.c) +unit_test(version.c) diff --git a/test/unit/version.c b/test/unit/version.c new file mode 100644 index 0000000..105c24e --- /dev/null +++ b/test/unit/version.c @@ -0,0 +1,45 @@ +#include "version.h" + +struct testcase { + int rc; + char *version; + int tuple[3]; +}; + +int main(void) +{ + struct testcase cases[] = { + /* Good */ + { 0, "0.0.0", { 0, 0, 0 } }, + { 0, "1.0.0", { 1, 0, 0 } }, + { 0, "0.1.0", { 0, 1, 0 } }, + { 0, "0.0.1", { 0, 0, 1 } }, + { 0, "1.2.3-rc1", { 1, 2, 3 } }, + /* Bad */ + { -1, "-1.0.0", { 0, 0, 0 } }, + { -1, "1.2", { 0, 0, 0 } }, + { -1, "1", { 0, 0, 0 } }, + { -1, "1.O.O", { 0, 0, 0 } }, + { -1, "1.2.3rc", { 0, 0, 0 } }, + }; + + int n = sizeof(cases) / sizeof(cases[0]); + + for (int i = 0; i < n; i++) { + struct testcase *c = &cases[i]; + int tuple[3] = { 0 }; + + if (version_parse(c->version, tuple) != c->rc) + die("wrong return value\n"); + + if (c->rc != 0) + continue; + + for (int j = 0; j < 3; j++) { + if (tuple[j] != c->tuple[j]) + die("wrong parsed version\n"); + } + } + + return 0; +}