diff options
Diffstat (limited to 'tests/api/test-dependency.c')
| -rw-r--r-- | tests/api/test-dependency.c | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/tests/api/test-dependency.c b/tests/api/test-dependency.c new file mode 100644 index 000000000000..edd00b620f18 --- /dev/null +++ b/tests/api/test-dependency.c @@ -0,0 +1,551 @@ +/* + * test-dependency.c + * Tests for the public libpkgconf dependency API. + * + * SPDX-License-Identifier: pkgconf + * + * Copyright (c) 2026 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include "test-api.h" + +static size_t +dependency_count(const pkgconf_list_t *list) +{ + size_t n = 0; + const pkgconf_node_t *iter; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, iter) + { + n++; + } + + return n; +} + +static const pkgconf_dependency_t * +dependency_at(const pkgconf_list_t *list, size_t index) +{ + const pkgconf_node_t *iter; + size_t i = 0; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, iter) + { + if (i++ == index) + return iter->data; + } + + return NULL; +} + +static void +test_comparator_lookup_known(void) +{ + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name("="), PKGCONF_CMP_EQUAL); + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name("!="), PKGCONF_CMP_NOT_EQUAL); + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name("<"), PKGCONF_CMP_LESS_THAN); + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name("<="), PKGCONF_CMP_LESS_THAN_EQUAL); + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name(">"), PKGCONF_CMP_GREATER_THAN); + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name(">="), PKGCONF_CMP_GREATER_THAN_EQUAL); +} + +static void +test_comparator_lookup_unknown(void) +{ + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name("~~"), PKGCONF_CMP_ANY); + TEST_ASSERT_EQ(pkgconf_pkg_comparator_lookup_by_name(""), PKGCONF_CMP_ANY); +} + +static void +test_comparator_roundtrip(void) +{ + pkgconf_pkg_comparator_t values[] = + { + PKGCONF_CMP_NOT_EQUAL, + PKGCONF_CMP_ANY, + PKGCONF_CMP_LESS_THAN, + PKGCONF_CMP_LESS_THAN_EQUAL, + PKGCONF_CMP_EQUAL, + PKGCONF_CMP_GREATER_THAN, + PKGCONF_CMP_GREATER_THAN_EQUAL, + }; + + for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(values); i++) + { + pkgconf_dependency_t dep = { 0 }; + dep.compare = values[i]; + + const char *str = pkgconf_pkg_get_comparator(&dep); + TEST_ASSERT_NONNULL(str); + + // ANY's value does not round-trip so do not check + if (values[i] == PKGCONF_CMP_ANY) + continue; + + pkgconf_pkg_comparator_t back = pkgconf_pkg_comparator_lookup_by_name(str); + TEST_ASSERT_EQ(back, values[i]); + } +} + +static void +test_parse_str_single(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_parse_str(client, &deps, "foo", 0); + + TEST_ASSERT_EQ(dependency_count(&deps), 1); + const pkgconf_dependency_t *d = dependency_at(&deps, 0); + TEST_ASSERT_NONNULL(d); + TEST_ASSERT_STRCMP_EQ(d->package, "foo"); + TEST_ASSERT_EQ(d->compare, PKGCONF_CMP_ANY); + TEST_ASSERT_NULL(d->version); + + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_parse_str_versioned(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_parse_str(client, &deps, "foo >= 1.2.3", 0); + + TEST_ASSERT_EQ(dependency_count(&deps), 1); + const pkgconf_dependency_t *d = dependency_at(&deps, 0); + TEST_ASSERT_NONNULL(d); + TEST_ASSERT_STRCMP_EQ(d->package, "foo"); + TEST_ASSERT_EQ(d->compare, PKGCONF_CMP_GREATER_THAN_EQUAL); + TEST_ASSERT_STRCMP_EQ(d->version, "1.2.3"); + + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_parse_str_multiple_space_separated(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_parse_str(client, &deps, "foo bar baz", 0); + + TEST_ASSERT_EQ(dependency_count(&deps), 3); + + const pkgconf_dependency_t *d0 = dependency_at(&deps, 0); + const pkgconf_dependency_t *d1 = dependency_at(&deps, 1); + const pkgconf_dependency_t *d2 = dependency_at(&deps, 2); + TEST_ASSERT_NONNULL(d0); + TEST_ASSERT_NONNULL(d1); + TEST_ASSERT_NONNULL(d2); + TEST_ASSERT_STRCMP_EQ(d0->package, "foo"); + TEST_ASSERT_STRCMP_EQ(d1->package, "bar"); + TEST_ASSERT_STRCMP_EQ(d2->package, "baz"); + + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_parse_str_multiple_comma_separated(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_parse_str(client, &deps, "foo, bar, baz", 0); + + TEST_ASSERT_EQ(dependency_count(&deps), 3); + + const pkgconf_dependency_t *d0 = dependency_at(&deps, 0); + const pkgconf_dependency_t *d1 = dependency_at(&deps, 1); + const pkgconf_dependency_t *d2 = dependency_at(&deps, 2); + TEST_ASSERT_NONNULL(d0); + TEST_ASSERT_NONNULL(d1); + TEST_ASSERT_NONNULL(d2); + TEST_ASSERT_STRCMP_EQ(d0->package, "foo"); + TEST_ASSERT_STRCMP_EQ(d1->package, "bar"); + TEST_ASSERT_STRCMP_EQ(d2->package, "baz"); + + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_parse_str_mixed_versioned_and_bare(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_parse_str(client, &deps, "foo >= 1.0, bar, baz < 2.0", 0); + + TEST_ASSERT_EQ(dependency_count(&deps), 3); + + const pkgconf_dependency_t *d0 = dependency_at(&deps, 0); + const pkgconf_dependency_t *d1 = dependency_at(&deps, 1); + const pkgconf_dependency_t *d2 = dependency_at(&deps, 2); + + TEST_ASSERT_NONNULL(d0); + TEST_ASSERT_NONNULL(d1); + TEST_ASSERT_NONNULL(d2); + + TEST_ASSERT_STRCMP_EQ(d0->package, "foo"); + TEST_ASSERT_EQ(d0->compare, PKGCONF_CMP_GREATER_THAN_EQUAL); + TEST_ASSERT_STRCMP_EQ(d0->version, "1.0"); + + TEST_ASSERT_STRCMP_EQ(d1->package, "bar"); + TEST_ASSERT_EQ(d1->compare, PKGCONF_CMP_ANY); + + TEST_ASSERT_STRCMP_EQ(d2->package, "baz"); + TEST_ASSERT_EQ(d2->compare, PKGCONF_CMP_LESS_THAN); + TEST_ASSERT_STRCMP_EQ(d2->version, "2.0"); + + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_parse_str_empty(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_parse_str(client, &deps, "", 0); + TEST_ASSERT_EQ(dependency_count(&deps), 0); + + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_parse_str_all_comparators(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_parse_str(client, &deps, "a = 1, b != 2, c < 3, d <= 4, e > 5, f >= 6", 0); + + TEST_ASSERT_EQ(dependency_count(&deps), 6); + + const pkgconf_dependency_t *d0 = dependency_at(&deps, 0); + const pkgconf_dependency_t *d1 = dependency_at(&deps, 1); + const pkgconf_dependency_t *d2 = dependency_at(&deps, 2); + const pkgconf_dependency_t *d3 = dependency_at(&deps, 3); + const pkgconf_dependency_t *d4 = dependency_at(&deps, 4); + const pkgconf_dependency_t *d5 = dependency_at(&deps, 5); + + TEST_ASSERT_NONNULL(d0); + TEST_ASSERT_NONNULL(d1); + TEST_ASSERT_NONNULL(d2); + TEST_ASSERT_NONNULL(d3); + TEST_ASSERT_NONNULL(d4); + TEST_ASSERT_NONNULL(d5); + + TEST_ASSERT_EQ(d0->compare, PKGCONF_CMP_EQUAL); + TEST_ASSERT_EQ(d1->compare, PKGCONF_CMP_NOT_EQUAL); + TEST_ASSERT_EQ(d2->compare, PKGCONF_CMP_LESS_THAN); + TEST_ASSERT_EQ(d3->compare, PKGCONF_CMP_LESS_THAN_EQUAL); + TEST_ASSERT_EQ(d4->compare, PKGCONF_CMP_GREATER_THAN); + TEST_ASSERT_EQ(d5->compare, PKGCONF_CMP_GREATER_THAN_EQUAL); + + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_dependency_add(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_t *d = pkgconf_dependency_add(client, &deps, "foo", "1.0", PKGCONF_CMP_GREATER_THAN_EQUAL, 0); + TEST_ASSERT_NONNULL(d); + TEST_ASSERT_STRCMP_EQ(d->package, "foo"); + TEST_ASSERT_STRCMP_EQ(d->version, "1.0"); + TEST_ASSERT_EQ(d->compare, PKGCONF_CMP_GREATER_THAN_EQUAL); + + TEST_ASSERT_EQ(dependency_count(&deps), 1); + + pkgconf_dependency_unref(client, d); + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_dependency_add_no_version(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_t *d = pkgconf_dependency_add(client, &deps, "foo", NULL, PKGCONF_CMP_ANY, 0); + TEST_ASSERT_NONNULL(d); + TEST_ASSERT_STRCMP_EQ(d->package, "foo"); + TEST_ASSERT_EQ(d->compare, PKGCONF_CMP_ANY); + + pkgconf_dependency_unref(client, d); + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_dependency_add_multiple(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_t *d0 = pkgconf_dependency_add(client, &deps, "foo", NULL, PKGCONF_CMP_ANY, 0); + pkgconf_dependency_t *d1 = pkgconf_dependency_add(client, &deps, "bar", "2.0", PKGCONF_CMP_EQUAL, 0); + pkgconf_dependency_t *d2 = pkgconf_dependency_add(client, &deps, "baz", "3.0", PKGCONF_CMP_LESS_THAN, 0); + + TEST_ASSERT_NONNULL(d0); + TEST_ASSERT_NONNULL(d1); + TEST_ASSERT_NONNULL(d2); + + TEST_ASSERT_EQ(dependency_count(&deps), 3); + + pkgconf_dependency_unref(client, d0); + pkgconf_dependency_unref(client, d1); + pkgconf_dependency_unref(client, d2); + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_dependency_collision_drops_flagged_newcomer(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_t *first = pkgconf_dependency_add(client, &deps, "foo", "1.0", PKGCONF_CMP_EQUAL, 0); + TEST_ASSERT_NONNULL(first); + TEST_ASSERT_EQ(dependency_count(&deps), 1); + + /* Adding the same dep WITH flags collides; the flagged newcomer + * is dropped in favour of the existing unflagged node, so _add + * returns NULL and the count stays at 1 */ + pkgconf_dependency_t *second = pkgconf_dependency_add(client, &deps, "foo", "1.0", PKGCONF_CMP_EQUAL, PKGCONF_PKG_DEPF_INTERNAL); + TEST_ASSERT_NULL(second); + TEST_ASSERT_EQ(dependency_count(&deps), 1); + + pkgconf_dependency_unref(client, first); + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_dependency_collision_drops_flagged_existing(void) +{ + pkgconf_client_t *client = test_client_new(); + pkgconf_list_t deps = PKGCONF_LIST_INITIALIZER; + + pkgconf_dependency_t *first = pkgconf_dependency_add(client, &deps, "foo", "1.0", PKGCONF_CMP_EQUAL, PKGCONF_PKG_DEPF_INTERNAL); + TEST_ASSERT_NONNULL(first); + TEST_ASSERT_EQ(dependency_count(&deps), 1); + + /* Adding the same dep UNFLAGGED collides; the existing flagged + * node is deleted and unref'd, the unflagged newcomer takes its + * place; count stays at 1, but the node is now the new one */ + pkgconf_dependency_t *second = pkgconf_dependency_add(client, &deps, "foo", "1.0", PKGCONF_CMP_EQUAL, 0); + TEST_ASSERT_NONNULL(second); + TEST_ASSERT_EQ(dependency_count(&deps), 1); + TEST_ASSERT_EQ(second->flags, 0); + + pkgconf_dependency_unref(client, first); + pkgconf_dependency_unref(client, second); + pkgconf_dependency_free(&deps); + pkgconf_client_free(client); +} + +static void +test_version_equal(void) +{ + TEST_ASSERT_EQ(pkgconf_compare_version("1.0", "1.0"), 0); + TEST_ASSERT_EQ(pkgconf_compare_version("1.2.3", "1.2.3"), 0); + TEST_ASSERT_EQ(pkgconf_compare_version("", ""), 0); +} + +static void +test_version_simple_numeric(void) +{ + TEST_ASSERT_LT(pkgconf_compare_version("1.0", "1.1"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.1", "1.0"), 0); + + TEST_ASSERT_LT(pkgconf_compare_version("1.0", "2.0"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("2.0", "1.0"), 0); + + TEST_ASSERT_LT(pkgconf_compare_version("1.2.3", "1.2.4"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.2.3", "1.3.0"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.2.3", "2.0.0"), 0); +} + +static void +test_version_numeric_segments_not_lexical(void) +{ + TEST_ASSERT_GT(pkgconf_compare_version("1.10", "1.9"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.100", "1.99"), 0); +} + +static void +test_version_different_lengths(void) +{ + TEST_ASSERT_LT(pkgconf_compare_version("1.0", "1.0.1"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0.1", "1.0"), 0); +} + +static void +test_version_alpha_suffix(void) +{ + TEST_ASSERT_GT(pkgconf_compare_version("1.0a", "1.0"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0alpha", "1.0"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0rc1", "1.0"), 0); + + TEST_ASSERT_LT(pkgconf_compare_version("1.0alpha", "1.0beta"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.0beta", "1.0rc"), 0); +} + +static void +test_version_tilde_prerelease(void) +{ + TEST_ASSERT_LT(pkgconf_compare_version("1.0~rc1", "1.0"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.0~alpha", "1.0"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.0~beta", "1.0~rc"), 0); + + TEST_ASSERT_LT(pkgconf_compare_version("1.0~rc1", "1.0rc1"), 0); +} + +static void +test_version_numeric_beats_alpha(void) +{ + TEST_ASSERT_GT(pkgconf_compare_version("1.0.1", "1.0a"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.0a", "1.0.1"), 0); +} + +static void +test_version_alpha_ordering(void) +{ + TEST_ASSERT_LT(pkgconf_compare_version("1.0a", "1.0b"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0b", "1.0a"), 0); +} + +static void +test_version_dotted_vs_hyphenated(void) +{ + TEST_ASSERT_EQ(pkgconf_compare_version("1.0-1", "1.0.1"), 0); +} + +static void +test_version_leading_zeros(void) +{ + TEST_ASSERT_EQ(pkgconf_compare_version("1.01", "1.1"), 0); + TEST_ASSERT_EQ(pkgconf_compare_version("01.0", "1.0"), 0); +} + +static void +test_version_trailing_zero_segments(void) +{ + /* Pkgconf does NOT treat "1.0" and "1.0.0" as equivalent. + * Even a zero trailing numeric segment is additional content that makes the version greater. + */ + TEST_ASSERT_LT(pkgconf_compare_version("1.0", "1.0.0"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0.0", "1.0"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1", "1.0"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1", "1.0.0.0"), 0); +} + +static void +test_version_null_handling(void) +{ + TEST_ASSERT_LT(pkgconf_compare_version(NULL, "1.0"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0", NULL), 0); + TEST_ASSERT_LT(pkgconf_compare_version(NULL, NULL), 0); +} + +static void +test_version_tilde_both_sides(void) +{ + TEST_ASSERT_LT(pkgconf_compare_version("1.0~", "1.0a"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0a", "1.0~"), 0); + + TEST_ASSERT_EQ(pkgconf_compare_version("1.0~rc", "1.0~rc"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.0~a", "1.0~b"), 0); + TEST_ASSERT_LT(pkgconf_compare_version("1.0~1", "1.0~2"), 0); +} + +static void +test_version_separator_equivalence(void) +{ + TEST_ASSERT_EQ(pkgconf_compare_version("1.0", "1-0"), 0); + TEST_ASSERT_EQ(pkgconf_compare_version("1.2.3", "1_2_3"), 0); + TEST_ASSERT_EQ(pkgconf_compare_version("1..2", "1.2"), 0); +} + +static void +test_version_case_insensitive(void) +{ + TEST_ASSERT_EQ(pkgconf_compare_version("1.0RC1", "1.0rc1"), 0); + TEST_ASSERT_EQ(pkgconf_compare_version("1.0A", "1.0a"), 0); +} + +static void +test_version_alpha_prefix(void) +{ + TEST_ASSERT_LT(pkgconf_compare_version("1.0alpha", "1.0alphabeta"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0alphabeta", "1.0alpha"), 0); + + TEST_ASSERT_LT(pkgconf_compare_version("1.0a", "1.0ab"), 0); + TEST_ASSERT_GT(pkgconf_compare_version("1.0ab", "1.0a"), 0); +} + +int +main(int argc, char *argv[]) +{ + (void) argc; + const char *basename = pkgconf_path_find_basename(argv[0]); + + TEST_RUN(basename, test_comparator_lookup_known); + TEST_RUN(basename, test_comparator_lookup_unknown); + TEST_RUN(basename, test_comparator_roundtrip); + + TEST_RUN(basename, test_parse_str_empty); + TEST_RUN(basename, test_parse_str_single); + TEST_RUN(basename, test_parse_str_versioned); + TEST_RUN(basename, test_parse_str_multiple_space_separated); + TEST_RUN(basename, test_parse_str_multiple_comma_separated); + TEST_RUN(basename, test_parse_str_mixed_versioned_and_bare); + TEST_RUN(basename, test_parse_str_all_comparators); + + TEST_RUN(basename, test_dependency_add); + TEST_RUN(basename, test_dependency_add_no_version); + TEST_RUN(basename, test_dependency_add_multiple); + TEST_RUN(basename, test_dependency_collision_drops_flagged_newcomer); + TEST_RUN(basename, test_dependency_collision_drops_flagged_existing); + + TEST_RUN(basename, test_version_equal); + TEST_RUN(basename, test_version_simple_numeric); + TEST_RUN(basename, test_version_numeric_segments_not_lexical); + TEST_RUN(basename, test_version_different_lengths); + TEST_RUN(basename, test_version_alpha_suffix); + TEST_RUN(basename, test_version_tilde_prerelease); + TEST_RUN(basename, test_version_numeric_beats_alpha); + TEST_RUN(basename, test_version_alpha_ordering); + TEST_RUN(basename, test_version_dotted_vs_hyphenated); + TEST_RUN(basename, test_version_leading_zeros); + TEST_RUN(basename, test_version_trailing_zero_segments); + TEST_RUN(basename, test_version_null_handling); + TEST_RUN(basename, test_version_tilde_both_sides); + TEST_RUN(basename, test_version_separator_equivalence); + TEST_RUN(basename, test_version_case_insensitive); + TEST_RUN(basename, test_version_alpha_prefix); + + return EXIT_SUCCESS; +} |
