#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()