From 6dc0f03c90efa249f709076a4dc8a1ab9d197c5f Mon Sep 17 00:00:00 2001 From: Andreas Schmidt Date: Mon, 10 Apr 2023 15:59:56 +0200 Subject: [PATCH] add: convenience functions, tests; fix: np checks --- include/semverreq.h | 9 +++-- src/semver.c | 16 ++++++-- src/semverreq.c | 52 ++++++++++++++++++++++++++ test/semver-test.c | 54 +++++++++++++++++++++------ test/semverreq-test.c | 86 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+), 18 deletions(-) diff --git a/include/semverreq.h b/include/semverreq.h index 9e0fdfa..e55d18c 100644 --- a/include/semverreq.h +++ b/include/semverreq.h @@ -36,9 +36,10 @@ extern "C" { * Both input strings must be valid semver version and requirements strings. * @param[in] version_str e.g.: "3.24.2" * @param[in] versionreq_str, e.g. ">=3.20.0 <4.0.0" - * @return 0 if version matches requirements, != 0 otherwise; > 0 indicates an error in input parameters + * @param[out] res: 1 if version matches requirements, 0 otherwise; + * @return err: 0 = successful operation, != 0 indicates an error in input parameters */ -int semver_matches(const char *version_str, const char *versionreq_str); +int semver_matches(const char *version_str, const char *versionreq_str, int *res); /** * semver_version_req @@ -125,7 +126,9 @@ int semver_version_req_snprint(semver_version_req self, char *buf, size_t sz); int semver_version_req_sprint(semver_version_req self, char *buf); /** - * semver_version_req_matches checks, if `v` is within the bounds of `self` + * semver_version_req_matches checks, if `v` is within the bounds of `self`. + * self and v must be correctly set up, otherwise the result may be wrong. + * @returns 1 if it matches, 0 otherwise */ int semver_version_req_matches(semver_version_req self, semver_version v); diff --git a/src/semver.c b/src/semver.c index 3781fc3..cc50cce 100644 --- a/src/semver.c +++ b/src/semver.c @@ -42,6 +42,7 @@ int semver_cmp(const char *_a, const char *_b, int *res) { return 2; } if(!b) { + semver_version_delete(a); return 3; } @@ -134,11 +135,15 @@ semver_version semver_version_from(unsigned long major, unsigned long minor, semver_version semver_version_from_string(const char *s) { int k; - semver_version_impl *res = (semver_version_impl *)semver_version_new(); + semver_version_impl *res = 0; + if (!s) { + return 0; + } + res = (semver_version_impl *)semver_version_new(); k = semver_version_from_string_impl(res, s); if (k != SEMVER_OK) { semver_version_delete(res); - return NULL; + return 0; } return (semver_version )res; } @@ -146,9 +151,14 @@ semver_version semver_version_from_string(const char *s) { semver_version_wrapped semver_version_from_string_wrapped(const char *s) { int k; semver_version_wrapped res; - semver_version_impl *impl = (semver_version_impl *)semver_version_new(); + semver_version_impl *impl = 0; res.err = 0; res.unwrap.result = 0; + if (!s) { + res.err = 1; + return res; + } + impl = (semver_version_impl *)semver_version_new(); k = semver_version_from_string_impl(impl, s); if (k != SEMVER_OK) { res.err = 1; diff --git a/src/semverreq.c b/src/semverreq.c index 33b03a7..50e8ed0 100644 --- a/src/semverreq.c +++ b/src/semverreq.c @@ -30,6 +30,37 @@ #include "semver.h" #include "semverreq.h" +int semver_matches(const char *version_str, const char *versionreq_str, int *res) { + semver_version_req r = 0; + semver_version v = 0; + int err = 1; + + if (!res || !version_str || !versionreq_str ) { + goto end_delete; + } + + v = semver_version_from_string(version_str); + if (!v) { + goto end_delete; + } + + r = semver_version_req_from_string(versionreq_str); + if (!r) { + goto end_delete; + } + + *res = semver_version_req_matches(r, v); + err = 0; + +end_delete: + if(v) { + semver_version_delete(v); + } + if(r) { + semver_version_req_delete(r); + } + return err; +} #define SEMVERREQ_NEW(obj, type) \ do { \ @@ -523,3 +554,24 @@ int semver_version_req_sprint(semver_version_req _self, char *buf) { return sprintf(buf, "%s%s %s%s", lower_cmp, buf1, upper_cmp, buf2); } + +int semver_version_req_matches(semver_version_req _self, semver_version v) { + int cl, cu; + + semver_version_req_impl self = (semver_version_req_impl)_self; + + cl = semver_version_cmp(v, self->lower); + if (cl < 0 || (cl == 0 && !self->lower_including)) { + return 0; + /* v is not compatible with lower bound */ + } + + cu = semver_version_cmp(v, self->upper); + if (cu > 0 || (cu == 0 && !self->upper_including)) { + return 0; + /* v is not compatible with upper bound */ + } + + /* v is compatible with both lower and upper bound */ + return 1; +} diff --git a/test/semver-test.c b/test/semver-test.c index 06f81df..b12f2aa 100644 --- a/test/semver-test.c +++ b/test/semver-test.c @@ -316,20 +316,25 @@ void test_semver_copy() { void test_semver_cmp3() { - /* - * https://semver.org/spec/v2.0.0.html 11.4.4 - * 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 - * < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0. - */ const char *arr[] = { - "1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-alpha.beta", - "1.0.0-beta", "1.0.0-beta.2", "1.0.0-beta.11", - "1.0.0-rc.1", "1.0.0" - }; - - size_t i; + "0.0.0", + "0.0.1", "0.0.2", + "0.1.0", "0.1.1-alpha", "0.1.1", + "0.9.0", "0.9.9-beta", "0.9.9", + "1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-alpha.beta", + "1.0.0-beta", "1.0.0-beta.2", "1.0.0-beta.11", + "1.0.0-rc.1", "1.0.0", + "1.0.1-alpha", "1.0.1", "1.0.2", + "1.1.0", "1.1.1", + "1.9.0", + "2.0.0", + "99.99.99" + }; + + size_t i, j; int comp_res, r; - for (i = 1; i < sizeof(arr) / sizeof(const char *); i++) { + size_t n = sizeof(arr) / sizeof(const char *); + for (i = 1; i < n; i++) { comp_res = 99; r = semver_cmp(arr[i - 1], arr[i], &comp_res); @@ -345,8 +350,32 @@ void test_semver_cmp3() { TEST_ASSERT_EQUAL(0, comp_res); } + for (i = 0, j=n-1; i < n/2; i++, j--) { + comp_res = 99; + + r = semver_cmp(arr[i], arr[j], &comp_res); + TEST_ASSERT_EQUAL(0, r); + TEST_ASSERT_LESS_THAN(0, comp_res); + + r = semver_cmp(arr[j], arr[i], &comp_res); + TEST_ASSERT_EQUAL(0, r); + TEST_ASSERT_GREATER_THAN(0, comp_res); + + } } +void test_semver_cmp3_invalid() { + int comp_res, r; + + r = semver_cmp(0, 0, 0); + TEST_ASSERT_NOT_EQUAL(0, r); + + r = semver_cmp("not-valid", 0, &comp_res); + TEST_ASSERT_NOT_EQUAL(0, r); + + r = semver_cmp("1.2.3", "in-valid", &comp_res); + TEST_ASSERT_NOT_EQUAL(0, r); +} void run_semver_tests() { int i; @@ -360,5 +389,6 @@ void run_semver_tests() { RUN_TEST(test_semver_prerelease_cmp); RUN_TEST(test_semver_copy); RUN_TEST(test_semver_cmp3); + RUN_TEST(test_semver_cmp3_invalid); } } diff --git a/test/semverreq-test.c b/test/semverreq-test.c index 25a1b25..70c7d8b 100644 --- a/test/semverreq-test.c +++ b/test/semverreq-test.c @@ -233,6 +233,87 @@ void test_wb_parse_version_req_2nd(void) { } } +typedef struct { + const char *v; + const char *r; + const int res; +} exp5_t; + +void test_semverreq_match_exact() { + const exp5_t tests[] = { + { "0.0.1", 0, 1 }, + { "1.45.3-alpha", 0, 1 }, + { "1.45.3-beta+some", 0, 1 }, + { "1.45.3", 0, 1 }, + }; + const size_t n = sizeof(tests)/sizeof(exp5_t); + int res, err; + size_t i; + char buf[255]; + + for (i = 0; i < n; i++) { + res = 99; + + memset(buf, 0, sizeof(buf)); + buf[0] = '='; + strcpy(&buf[1], tests[i].v); + err = semver_matches(tests[i].v, buf, &res); + + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT_NOT_EQUAL(99, res); + TEST_ASSERT_GREATER_THAN(0, res); + } +} + +void test_semverreq_match_range() { + const exp5_t tests[] = { + { "0.0.0", ">=0.0.1 <1.0.0", 0 }, + { "0.0.1-alpha", ">=0.0.1 <1.0.0", 0 }, + { "0.0.1", ">=0.0.1 <1.0.0", 1 }, + { "0.0.1", ">0.0.1 <1.0.0", 0 }, + { "0.0.2", ">0.0.1 <1.0.0", 1 }, + { "0.1.0", ">0.0.1 <1.0.0", 1 }, + { "0.9.9-alpha", ">0.0.1 <1.0.0", 1 }, + { "1.0.0", ">0.0.1 <1.0.0", 0 }, + { "1.0.0", ">0.0.1 <=1.0.0", 1 }, + { "1.3.0", ">=1.3.0 <2.0.0", 1 }, + { "1.45.3", ">=1.3.0 <2.0.0", 1 }, + { "2.0.0", ">=1.3.0 <2.0.0", 0 } + }; + const size_t n = sizeof(tests)/sizeof(exp5_t); + int res, err; + size_t i; + + for (i = 0; i < n; i++) { + res = 99; + err = semver_matches(tests[i].v, tests[i].r, &res); + + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT_NOT_EQUAL(99, res); + TEST_ASSERT_EQUAL(tests[i].res, res); + } +} + +void test_semverreq_match_range_ops() { + const exp5_t tests[] = { + { "0.1.3", "~0.1.0", 1 }, + { "0.1.3", "~0.1.1", 1 }, + { "0.1.3", "~0.0.1", 0 }, + }; + const size_t n = sizeof(tests)/sizeof(exp5_t); + int res, err; + size_t i; + + for (i = 0; i < n; i++) { + res = 99; + err = semver_matches(tests[i].v, tests[i].r, &res); + + TEST_ASSERT_EQUAL(0, err); + TEST_ASSERT_NOT_EQUAL(99, res); + TEST_ASSERT_EQUAL(tests[i].res, res); + } +} + void run_semverreq_tests(void) { /* explicitly constructed semverreqs should print correctly */ RUN_TEST(test_semverreq_print); @@ -247,4 +328,9 @@ void run_semverreq_tests(void) { /* paring should work correctly */ RUN_TEST(test_semverreq_parse); + + /* high level function should work correctly */ + RUN_TEST(test_semverreq_match_exact); + RUN_TEST(test_semverreq_match_range); + RUN_TEST(test_semverreq_match_range_ops); }