string-util: introduce strverscmp_improved()

Unfortunately, strverscmp() from libc or str_verscmp() do not correctly
handle pre-release version, e.g. 247 vs 247~rc1.

This implement a new comparison function, which is based on the RPM's
rpmvercmp().
This commit is contained in:
Yu Watanabe
2021-02-09 14:24:58 +09:00
parent e5bc5f1f5a
commit 87b7d9b6ff
3 changed files with 239 additions and 0 deletions
+79
View File
@@ -886,6 +886,84 @@ static void test_string_contains_word(void) {
assert_se(!string_contains_word("a:b:cc", ":#", ":cc"));
}
static void test_strverscmp_improved_one(const char *newer, const char *older) {
log_info("/* %s(%s, %s) */", __func__, strnull(newer), strnull(older));
assert_se(strverscmp_improved(newer, newer) == 0);
assert_se(strverscmp_improved(newer, older) > 0);
assert_se(strverscmp_improved(older, newer) < 0);
assert_se(strverscmp_improved(older, older) == 0);
}
static void test_strverscmp_improved(void) {
static const char * const versions[] = {
"",
"~1",
"ab",
"abb",
"abc",
"0001",
"002",
"12",
"122",
"122.9",
"123~rc1",
"123",
"123-a",
"123-a.1",
"123-a1",
"123-a1.1",
"123-3",
"123-3.1",
"123^patch1",
"123^1",
"123.a-1"
"123.1-1",
"123a-1",
"124",
NULL,
};
const char * const *p, * const *q;
STRV_FOREACH(p, versions)
STRV_FOREACH(q, p + 1)
test_strverscmp_improved_one(*q, *p);
test_strverscmp_improved_one("123.45-67.89", "123.45-67.88");
test_strverscmp_improved_one("123.45-67.89a", "123.45-67.89");
test_strverscmp_improved_one("123.45-67.89", "123.45-67.ab");
test_strverscmp_improved_one("123.45-67.89", "123.45-67.9");
test_strverscmp_improved_one("123.45-67.89", "123.45-67");
test_strverscmp_improved_one("123.45-67.89", "123.45-66.89");
test_strverscmp_improved_one("123.45-67.89", "123.45-9.99");
test_strverscmp_improved_one("123.45-67.89", "123.42-99.99");
test_strverscmp_improved_one("123.45-67.89", "123-99.99");
/* '~' : pre-releases */
test_strverscmp_improved_one("123.45-67.89", "123~rc1-99.99");
test_strverscmp_improved_one("123-45.67.89", "123~rc1-99.99");
test_strverscmp_improved_one("123~rc2-67.89", "123~rc1-99.99");
test_strverscmp_improved_one("123^aa2-67.89", "123~rc1-99.99");
test_strverscmp_improved_one("123aa2-67.89", "123~rc1-99.99");
/* '-' : separator between version and release. */
test_strverscmp_improved_one("123.45-67.89", "123-99.99");
test_strverscmp_improved_one("123^aa2-67.89", "123-99.99");
test_strverscmp_improved_one("123aa2-67.89", "123-99.99");
/* '^' : patch releases */
test_strverscmp_improved_one("123.45-67.89", "123^45-67.89");
test_strverscmp_improved_one("123^aa2-67.89", "123^aa1-99.99");
test_strverscmp_improved_one("123aa2-67.89", "123^aa2-67.89");
/* '.' : point release */
test_strverscmp_improved_one("123aa2-67.89", "123.aa2-67.89");
test_strverscmp_improved_one("123.ab2-67.89", "123.aa2-67.89");
/* invalid characters */
assert_se(strverscmp_improved("123_aa2-67.89", "123aa+2-67.89") == 0);
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
@@ -923,6 +1001,7 @@ int main(int argc, char *argv[]) {
test_string_extract_line();
test_string_contains_word_strv();
test_string_contains_word();
test_strverscmp_improved();
return 0;
}