Files
version/tests/version.c
T

456 lines
14 KiB
C
Raw Normal View History

2026-09-12 10:47:02 -05:00
#include "malunal/microtest.h"
#include "malunal/version.h"
/* -------------------------------------------------------------------------
* parse_version(): successful parses
* ---------------------------------------------------------------------- */
MICROTEST(parse_version, basic_triple_succeeds) {
strview_t view = strview_from_cstr("1.2.3");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(ver.major, (malunal_size_t)1);
MICROTEST_EXPECT_EQ(ver.minor, (malunal_size_t)2);
MICROTEST_EXPECT_EQ(ver.patch, (malunal_size_t)3);
MICROTEST_EXPECT_EQ(strview_length(&ver.preview), (malunal_size_t)0);
MICROTEST_EXPECT_EQ(strview_length(&ver.build), (malunal_size_t)0);
}
MICROTEST(parse_version, all_zero_succeeds) {
strview_t view = strview_from_cstr("0.0.0");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(ver.major, (malunal_size_t)0);
MICROTEST_EXPECT_EQ(ver.minor, (malunal_size_t)0);
MICROTEST_EXPECT_EQ(ver.patch, (malunal_size_t)0);
}
MICROTEST(parse_version, multi_digit_components_succeed) {
strview_t view = strview_from_cstr("123.456.789");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(ver.major, (malunal_size_t)123);
MICROTEST_EXPECT_EQ(ver.minor, (malunal_size_t)456);
MICROTEST_EXPECT_EQ(ver.patch, (malunal_size_t)789);
}
MICROTEST(parse_version, preview_only_succeeds) {
strview_t view = strview_from_cstr("1.2.3-alpha");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(strview_length(&ver.preview), (malunal_size_t)5);
MICROTEST_EXPECT_TRUE(memcmp(ver.preview.beg, "alpha", 5) == 0);
MICROTEST_EXPECT_EQ(strview_length(&ver.build), (malunal_size_t)0);
}
MICROTEST(parse_version, dotted_preview_succeeds) {
strview_t view = strview_from_cstr("1.2.3-alpha.1");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(strview_length(&ver.preview), (malunal_size_t)7);
MICROTEST_EXPECT_TRUE(memcmp(ver.preview.beg, "alpha.1", 7) == 0);
}
MICROTEST(parse_version, build_only_succeeds) {
strview_t view = strview_from_cstr("1.2.3+build.5");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(strview_length(&ver.preview), (malunal_size_t)0);
MICROTEST_EXPECT_EQ(strview_length(&ver.build), (malunal_size_t)7);
MICROTEST_EXPECT_TRUE(memcmp(ver.build.beg, "build.5", 7) == 0);
}
MICROTEST(parse_version, preview_and_build_succeed) {
strview_t view = strview_from_cstr("1.2.3-alpha.1+build.5");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(ver.major, (malunal_size_t)1);
MICROTEST_EXPECT_EQ(ver.minor, (malunal_size_t)2);
MICROTEST_EXPECT_EQ(ver.patch, (malunal_size_t)3);
MICROTEST_EXPECT_TRUE(memcmp(ver.preview.beg, "alpha.1", 7) == 0);
MICROTEST_EXPECT_TRUE(memcmp(ver.build.beg, "build.5", 7) == 0);
}
MICROTEST(parse_version, single_digit_zero_component_is_not_leading_zero) {
/* A lone "0" is a valid component; the leading-zero rule only forbids
* *multiple* digits starting with zero (e.g. "01"). */
strview_t view = strview_from_cstr("0.2.3");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(ver.major, (malunal_size_t)0);
}
/* -------------------------------------------------------------------------
* parse_version(): error cases
* ---------------------------------------------------------------------- */
MICROTEST(parse_version, null_verstr_reports_null_input) {
version_t ver;
error_t err = parse_version(null, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_NULL_INPUT);
}
MICROTEST(parse_version, null_out_reports_null_input) {
strview_t view = strview_from_cstr("1.2.3");
error_t err = parse_version(&view, null);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_NULL_INPUT);
}
MICROTEST(parse_version, null_backing_data_succeeds_as_zero_version) {
strview_t view = { null, null };
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NULL(err.domain);
MICROTEST_EXPECT_EQ(ver.major, (malunal_size_t)0);
MICROTEST_EXPECT_EQ(ver.minor, (malunal_size_t)0);
MICROTEST_EXPECT_EQ(ver.patch, (malunal_size_t)0);
}
MICROTEST(parse_version, leading_zero_in_major_fails) {
strview_t view = strview_from_cstr("01.2.3");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_LEADING_ZERO);
}
MICROTEST(parse_version, leading_zero_in_minor_fails) {
strview_t view = strview_from_cstr("1.02.3");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_LEADING_ZERO);
}
MICROTEST(parse_version, leading_zero_in_patch_fails) {
strview_t view = strview_from_cstr("1.2.03");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_LEADING_ZERO);
}
MICROTEST(parse_version, missing_minor_and_patch_fails) {
strview_t view = strview_from_cstr("1.2");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_MISSING_PART);
}
MICROTEST(parse_version, missing_patch_digits_fails) {
strview_t view = strview_from_cstr("1.2.");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_INVALID_NUMBER);
}
MICROTEST(parse_version, fourth_numeric_component_fails) {
strview_t view = strview_from_cstr("1.2.3.4");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_UNEXPECTED_CHAR);
}
MICROTEST(parse_version, empty_preview_fails) {
strview_t view = strview_from_cstr("1.2.3-");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_MISSING_PART);
}
MICROTEST(parse_version, empty_build_fails) {
strview_t view = strview_from_cstr("1.2.3+");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_MISSING_PART);
}
MICROTEST(parse_version, negative_major_fails) {
strview_t view = strview_from_cstr("-1.2.3");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_INVALID_NUMBER);
}
MICROTEST(parse_version, non_numeric_components_fail) {
strview_t view = strview_from_cstr("a.b.c");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_INVALID_NUMBER);
}
MICROTEST(parse_version, overflowing_major_fails) {
strview_t view = strview_from_cstr("99999999999999999999.2.3");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_VALUE_OVERFLOW);
}
MICROTEST(parse_version, overflowing_patch_fails) {
strview_t view = strview_from_cstr("1.2.99999999999999999999");
version_t ver;
error_t err = parse_version(&view, &ver);
MICROTEST_EXPECT_NOT_NULL(err.domain);
MICROTEST_EXPECT_EQ(err.code, (malunal_int32_t)VERSION_ERROR_VALUE_OVERFLOW);
}
/* -------------------------------------------------------------------------
* version_compare(): core number precedence
* ---------------------------------------------------------------------- */
static
version_t
must_parse(malunal_cstr_t s) {
strview_t view = strview_from_cstr(s);
version_t ver;
parse_version(&view, &ver);
return ver;
}
MICROTEST(version_compare, identical_versions_are_equal) {
version_t a = must_parse("1.2.3");
version_t b = must_parse("1.2.3");
MICROTEST_EXPECT_EQ(version_compare(&a, &b), 0);
}
MICROTEST(version_compare, is_reflexive) {
version_t a = must_parse("1.2.3");
MICROTEST_EXPECT_EQ(version_compare(&a, &a), 0);
}
MICROTEST(version_compare, greater_major_wins) {
version_t a = must_parse("2.0.0");
version_t b = must_parse("1.9.9");
MICROTEST_EXPECT_GT(version_compare(&a, &b), 0);
MICROTEST_EXPECT_LT(version_compare(&b, &a), 0);
}
MICROTEST(version_compare, greater_minor_wins_when_major_equal) {
version_t a = must_parse("1.3.0");
version_t b = must_parse("1.2.9");
MICROTEST_EXPECT_GT(version_compare(&a, &b), 0);
}
MICROTEST(version_compare, greater_patch_wins_when_major_minor_equal) {
version_t a = must_parse("1.2.4");
version_t b = must_parse("1.2.3");
MICROTEST_EXPECT_GT(version_compare(&a, &b), 0);
}
MICROTEST(version_compare, numeric_comparison_not_lexical) {
/* 1.2.10 > 1.2.9 numerically, even though "10" < "9" lexically. */
version_t a = must_parse("1.2.10");
version_t b = must_parse("1.2.9");
MICROTEST_EXPECT_GT(version_compare(&a, &b), 0);
}
/* -------------------------------------------------------------------------
* version_compare(): pre-release precedence
* ---------------------------------------------------------------------- */
MICROTEST(version_compare, preview_has_lower_precedence_than_release) {
version_t preview = must_parse("1.0.0-alpha");
version_t release = must_parse("1.0.0");
MICROTEST_EXPECT_LT(version_compare(&preview, &release), 0);
MICROTEST_EXPECT_GT(version_compare(&release, &preview), 0);
}
MICROTEST(version_compare, more_preview_identifiers_has_higher_precedence) {
version_t shorter = must_parse("1.0.0-alpha");
version_t longer = must_parse("1.0.0-alpha.1");
MICROTEST_EXPECT_LT(version_compare(&shorter, &longer), 0);
}
MICROTEST(version_compare, numeric_identifier_has_lower_precedence_than_alphanumeric) {
version_t numeric = must_parse("1.0.0-alpha.1");
version_t alpha = must_parse("1.0.0-alpha.beta");
MICROTEST_EXPECT_LT(version_compare(&numeric, &alpha), 0);
}
MICROTEST(version_compare, numeric_preview_identifiers_compare_numerically) {
/* "10" > "2" numerically, even though "10" < "2" lexically. */
version_t ten = must_parse("1.0.0-rc.10");
version_t two = must_parse("1.0.0-rc.2");
MICROTEST_EXPECT_GT(version_compare(&ten, &two), 0);
}
MICROTEST(version_compare, alphanumeric_preview_identifiers_compare_lexically) {
version_t a = must_parse("1.0.0-alpha");
version_t b = must_parse("1.0.0-beta");
MICROTEST_EXPECT_LT(version_compare(&a, &b), 0);
}
MICROTEST(version_compare, identical_previews_are_equal) {
version_t a = must_parse("1.0.0-alpha.1");
version_t b = must_parse("1.0.0-alpha.1");
MICROTEST_EXPECT_EQ(version_compare(&a, &b), 0);
}
/* -------------------------------------------------------------------------
* version_compare(): build metadata is ignored
* ---------------------------------------------------------------------- */
MICROTEST(version_compare, differing_build_metadata_is_ignored) {
version_t a = must_parse("1.0.0+build.1");
version_t b = must_parse("1.0.0+build.2");
MICROTEST_EXPECT_EQ(version_compare(&a, &b), 0);
}
MICROTEST(version_compare, build_metadata_ignored_with_preview_present) {
version_t a = must_parse("1.0.0-alpha+build.1");
version_t b = must_parse("1.0.0-alpha+build.2");
MICROTEST_EXPECT_EQ(version_compare(&a, &b), 0);
}
/* -------------------------------------------------------------------------
* version_string()
* ---------------------------------------------------------------------- */
MICROTEST(version_string, basic_triple_round_trips) {
version_t ver = must_parse("1.2.3");
malunal_char_t buffer[32];
malunal_int32_t n = version_string(&ver, buffer, sizeof(buffer));
MICROTEST_EXPECT_GT(n, 0);
MICROTEST_EXPECT_TRUE(strcmp(buffer, "1.2.3") == 0);
}
MICROTEST(version_string, includes_preview) {
version_t ver = must_parse("1.2.3-alpha.1");
malunal_char_t buffer[32];
malunal_int32_t n = version_string(&ver, buffer, sizeof(buffer));
MICROTEST_EXPECT_GT(n, 0);
MICROTEST_EXPECT_TRUE(strcmp(buffer, "1.2.3-alpha.1") == 0);
}
MICROTEST(version_string, includes_build) {
version_t ver = must_parse("1.2.3+build.5");
malunal_char_t buffer[32];
malunal_int32_t n = version_string(&ver, buffer, sizeof(buffer));
MICROTEST_EXPECT_GT(n, 0);
MICROTEST_EXPECT_TRUE(strcmp(buffer, "1.2.3+build.5") == 0);
}
MICROTEST(version_string, includes_preview_and_build) {
version_t ver = must_parse("1.2.3-alpha.1+build.5");
malunal_char_t buffer[32];
malunal_int32_t n = version_string(&ver, buffer, sizeof(buffer));
MICROTEST_EXPECT_GT(n, 0);
MICROTEST_EXPECT_TRUE(strcmp(buffer, "1.2.3-alpha.1+build.5") == 0);
}
MICROTEST(version_string, too_small_buffer_fails) {
version_t ver = must_parse("1.2.3-alpha.1+build.5");
malunal_char_t buffer[5];
malunal_int32_t n = version_string(&ver, buffer, sizeof(buffer));
MICROTEST_EXPECT_EQ(n, -1);
}
MICROTEST(version_string, exact_size_buffer_succeeds) {
/* "1.2.3" is 5 characters plus a null terminator. */
version_t ver = must_parse("1.2.3");
malunal_char_t buffer[6];
malunal_int32_t n = version_string(&ver, buffer, sizeof(buffer));
MICROTEST_EXPECT_EQ(n, 5);
MICROTEST_EXPECT_TRUE(strcmp(buffer, "1.2.3") == 0);
}
MICROTEST(version_string, one_byte_too_small_fails) {
version_t ver = must_parse("1.2.3");
malunal_char_t buffer[5];
malunal_int32_t n = version_string(&ver, buffer, sizeof(buffer));
MICROTEST_EXPECT_EQ(n, -1);
}
MICROTEST(version_string, null_self_fails) {
malunal_char_t buffer[32];
malunal_int32_t n = version_string(null, buffer, sizeof(buffer));
MICROTEST_EXPECT_EQ(n, -1);
}
MICROTEST(version_string, null_buffer_fails) {
version_t ver = must_parse("1.2.3");
malunal_int32_t n = version_string(&ver, null, 32);
MICROTEST_EXPECT_EQ(n, -1);
}
MICROTEST(version_string, zero_length_fails) {
version_t ver = must_parse("1.2.3");
malunal_char_t buffer[32];
malunal_int32_t n = version_string(&ver, buffer, 0);
MICROTEST_EXPECT_EQ(n, -1);
}
MICROTEST_MAIN()