/
githubmirror
/
pkgconf
Обзор
Документация
Войти
/
githubmirror
/
pkgconf
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/api/test-fragment.c
508 строк
14 KB
Ariadne Conill
libpkgconf: fragment: splice fragment lists in order to avoid unnecessary fragment copies
24 июл 2026, 22:05
24 июл 2026, 22:05
fe900bb
Код
Авторство
О чём код?
/* * test-fragment.c * Tests for the public libpkgconf fragment 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 fragment_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_fragment_t * fragment_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; } /* * Assert that a list is internally consistent: it holds `expect` nodes, its * cached length agrees, the terminators are NULL, and it walks to the same * length in both directions. Relinking nodes between lists is easy to get * subtly wrong in a way that only a structural check like this catches. */ static void assert_list_integrity(const pkgconf_list_t *list, size_t expect) { const pkgconf_node_t *iter; size_t forward = 0, reverse = 0; TEST_ASSERT_EQ(list->length, expect); PKGCONF_FOREACH_LIST_ENTRY(list->head, iter) { if (iter->next == NULL) TEST_ASSERT_TRUE(iter == list->tail); forward++; } PKGCONF_FOREACH_LIST_ENTRY_REVERSE(list->tail, iter) { if (iter->prev == NULL) TEST_ASSERT_TRUE(iter == list->head); reverse++; } TEST_ASSERT_EQ(forward, expect); TEST_ASSERT_EQ(reverse, expect); if (expect == 0) { TEST_ASSERT_NULL(list->head); TEST_ASSERT_NULL(list->tail); } else { TEST_ASSERT_NULL(list->head->prev); TEST_ASSERT_NULL(list->tail->next); } } /* * Render a fragment list to a newly-allocated C string for assertions. * Caller frees. */ static char * render_to_string(const pkgconf_list_t *list) { pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER; pkgconf_fragment_render_buf(list, &buf, false, NULL, ' '); if (pkgconf_buffer_str(&buf) == NULL) return strdup(""); char *out = strdup(pkgconf_buffer_str(&buf)); pkgconf_buffer_finalize(&buf); return out; } static void test_fragment_parse_cflags(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; TEST_ASSERT_TRUE(pkgconf_fragment_parse(client, &frags, &vars, "-I/usr/include -DFOO=1", 0)); TEST_ASSERT_EQ(fragment_count(&frags), 2); const pkgconf_fragment_t *first = fragment_at(&frags, 0); TEST_ASSERT_NONNULL(first); TEST_ASSERT_EQ(first->type, 'I'); TEST_ASSERT_STRCMP_EQ(first->data, "/usr/include"); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_parse_libs(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; TEST_ASSERT_TRUE(pkgconf_fragment_parse(client, &frags, &vars, "-L/usr/lib -lfoo -lbar", 0)); TEST_ASSERT_EQ(fragment_count(&frags), 3); const pkgconf_fragment_t *f0 = fragment_at(&frags, 0); const pkgconf_fragment_t *f1 = fragment_at(&frags, 1); const pkgconf_fragment_t *f2 = fragment_at(&frags, 2); TEST_ASSERT_NONNULL(f0); TEST_ASSERT_NONNULL(f1); TEST_ASSERT_NONNULL(f2); TEST_ASSERT_EQ(f0->type, 'L'); TEST_ASSERT_STRCMP_EQ(f0->data, "/usr/lib"); TEST_ASSERT_EQ(f1->type, 'l'); TEST_ASSERT_STRCMP_EQ(f1->data, "foo"); TEST_ASSERT_EQ(f2->type, 'l'); TEST_ASSERT_STRCMP_EQ(f2->data, "bar"); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_parse_empty(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; TEST_ASSERT_TRUE(pkgconf_fragment_parse(client, &frags, &vars, "", 0)); TEST_ASSERT_EQ(fragment_count(&frags), 0); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_add_single(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_fragment_add(client, &frags, &vars, "-I/opt/include", 0); TEST_ASSERT_EQ(fragment_count(&frags), 1); const pkgconf_fragment_t *f = fragment_at(&frags, 0); TEST_ASSERT_NONNULL(f); TEST_ASSERT_EQ(f->type, 'I'); TEST_ASSERT_STRCMP_EQ(f->data, "/opt/include"); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_render_cflags(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_fragment_parse(client, &frags, &vars, "-I/usr/include -I/opt/include", 0); char *rendered = render_to_string(&frags); TEST_ASSERT_NONNULL(rendered); TEST_ASSERT_STRCMP_EQ(rendered, "-I/usr/include -I/opt/include"); free(rendered); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_render_libs(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_fragment_parse(client, &frags, &vars, "-L/usr/lib -lfoo", 0); char *rendered = render_to_string(&frags); TEST_ASSERT_NONNULL(rendered); TEST_ASSERT_STRCMP_EQ(rendered, "-L/usr/lib -lfoo"); free(rendered); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_render_empty(void) { pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; char *rendered = render_to_string(&frags); TEST_ASSERT_NONNULL(rendered); TEST_ASSERT_EMPTY_STRING(rendered); free(rendered); } // Filter predicate: keep only -I (include) fragments. static bool filter_only_includes(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data) { (void) client; (void) data; return frag->type == 'I'; } // Filter predicate: keep only -l (library name) fragments. static bool filter_only_libnames(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data) { (void) client; (void) data; return frag->type == 'l'; } // Filter predicate: keep nothing. static bool filter_nothing(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data) { (void) client; (void) frag; (void) data; return false; } static void test_fragment_filter_only_includes(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t src = PKGCONF_LIST_INITIALIZER; pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_fragment_parse(client, &src, &vars, "-I/usr/include -L/usr/lib -lfoo -I/opt/include", 0); pkgconf_fragment_filter(client, &dst, &src, filter_only_includes, NULL); TEST_ASSERT_EQ(fragment_count(&dst), 2); const pkgconf_fragment_t *f0 = fragment_at(&dst, 0); const pkgconf_fragment_t *f1 = fragment_at(&dst, 1); TEST_ASSERT_NONNULL(f0); TEST_ASSERT_NONNULL(f1); TEST_ASSERT_EQ(f0->type, 'I'); TEST_ASSERT_STRCMP_EQ(f0->data, "/usr/include"); TEST_ASSERT_EQ(f1->type, 'I'); TEST_ASSERT_STRCMP_EQ(f1->data, "/opt/include"); pkgconf_fragment_free(&dst); pkgconf_fragment_free(&src); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_filter_only_libnames(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t src = PKGCONF_LIST_INITIALIZER; pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_fragment_parse(client, &src, &vars, "-L/usr/lib -lfoo -lbar -I/usr/include", 0); pkgconf_fragment_filter(client, &dst, &src, filter_only_libnames, NULL); TEST_ASSERT_EQ(fragment_count(&dst), 2); const pkgconf_fragment_t *f0 = fragment_at(&dst, 0); const pkgconf_fragment_t *f1 = fragment_at(&dst, 1); TEST_ASSERT_NONNULL(f0); TEST_ASSERT_NONNULL(f1); TEST_ASSERT_EQ(f0->type, 'l'); TEST_ASSERT_STRCMP_EQ(f0->data, "foo"); TEST_ASSERT_EQ(f1->type, 'l'); TEST_ASSERT_STRCMP_EQ(f1->data, "bar"); pkgconf_fragment_free(&dst); pkgconf_fragment_free(&src); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_filter_keeps_nothing(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t src = PKGCONF_LIST_INITIALIZER; pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_fragment_parse(client, &src, &vars, "-I/usr/include -lfoo", 0); pkgconf_fragment_filter(client, &dst, &src, filter_nothing, NULL); TEST_ASSERT_EQ(fragment_count(&dst), 0); pkgconf_fragment_free(&dst); pkgconf_fragment_free(&src); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_filter_splice_moves_matches(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t src = PKGCONF_LIST_INITIALIZER; pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; /* the trailing fragment deliberately does not match: a moved node which * keeps a stale forward link would splice the rest of src onto dst. */ pkgconf_fragment_parse(client, &src, &vars, "-I/usr/include -L/usr/lib -I/opt/include -lfoo", 0); pkgconf_fragment_filter_splice(client, &dst, &src, filter_only_includes, NULL); /* the matches move out of src, in order; the rest stay behind */ assert_list_integrity(&dst, 2); assert_list_integrity(&src, 2); TEST_ASSERT_EQ(fragment_count(&dst), 2); TEST_ASSERT_EQ(fragment_count(&src), 2); const pkgconf_fragment_t *f0 = fragment_at(&dst, 0); const pkgconf_fragment_t *f1 = fragment_at(&dst, 1); TEST_ASSERT_NONNULL(f0); TEST_ASSERT_NONNULL(f1); TEST_ASSERT_EQ(f0->type, 'I'); TEST_ASSERT_STRCMP_EQ(f0->data, "/usr/include"); TEST_ASSERT_EQ(f1->type, 'I'); TEST_ASSERT_STRCMP_EQ(f1->data, "/opt/include"); const pkgconf_fragment_t *r0 = fragment_at(&src, 0); const pkgconf_fragment_t *r1 = fragment_at(&src, 1); TEST_ASSERT_NONNULL(r0); TEST_ASSERT_NONNULL(r1); TEST_ASSERT_EQ(r0->type, 'L'); TEST_ASSERT_EQ(r1->type, 'l'); pkgconf_fragment_free(&dst); pkgconf_fragment_free(&src); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_splice_list(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t src = PKGCONF_LIST_INITIALIZER; pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_fragment_parse(client, &dst, &vars, "-I/usr/include", 0); pkgconf_fragment_parse(client, &src, &vars, "-lfoo -lbar", 0); /* splicing onto a non-empty list appends and empties the source */ pkgconf_list_splice(&dst, &src); assert_list_integrity(&dst, 3); assert_list_integrity(&src, 0); TEST_ASSERT_EQ(fragment_count(&dst), 3); TEST_ASSERT_EQ(fragment_count(&src), 0); char *rendered = render_to_string(&dst); TEST_ASSERT_STRCMP_EQ(rendered, "-I/usr/include -lfoo -lbar"); free(rendered); TEST_ASSERT_STRCMP_EQ(((const pkgconf_fragment_t *) dst.tail->data)->data, "bar"); /* splicing an empty source, or a list onto itself, is a no-op */ pkgconf_list_splice(&dst, &src); pkgconf_list_splice(&dst, &dst); assert_list_integrity(&dst, 3); /* splicing onto an empty destination adopts the source wholesale */ pkgconf_list_splice(&src, &dst); assert_list_integrity(&src, 3); assert_list_integrity(&dst, 0); pkgconf_list_splice(&dst, &src); assert_list_integrity(&dst, 3); pkgconf_fragment_free(&dst); pkgconf_fragment_free(&src); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_has_system_dir_matches(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_path_add("/usr/include", &client->filter_includedirs, false); pkgconf_fragment_parse(client, &frags, &vars, "-I/usr/include -I/opt/include", 0); const pkgconf_fragment_t *system = fragment_at(&frags, 0); const pkgconf_fragment_t *other = fragment_at(&frags, 1); TEST_ASSERT_NONNULL(system); TEST_ASSERT_NONNULL(other); TEST_ASSERT_TRUE(pkgconf_fragment_has_system_dir(client, system)); TEST_ASSERT_FALSE(pkgconf_fragment_has_system_dir(client, other)); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } static void test_fragment_has_system_dir_libs(void) { pkgconf_client_t *client = test_client_new(); pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; pkgconf_path_add("/usr/lib", &client->filter_libdirs, false); pkgconf_fragment_parse(client, &frags, &vars, "-L/usr/lib -L/opt/lib", 0); const pkgconf_fragment_t *system = fragment_at(&frags, 0); const pkgconf_fragment_t *other = fragment_at(&frags, 1); TEST_ASSERT_NONNULL(system); TEST_ASSERT_NONNULL(other); TEST_ASSERT_TRUE(pkgconf_fragment_has_system_dir(client, system)); TEST_ASSERT_FALSE(pkgconf_fragment_has_system_dir(client, other)); pkgconf_fragment_free(&frags); pkgconf_variable_list_free(&vars); pkgconf_client_free(client); } int main(int argc, char *argv[]) { (void) argc; const char *basename = pkgconf_path_find_basename(argv[0]); TEST_RUN(basename, test_fragment_parse_empty); TEST_RUN(basename, test_fragment_parse_cflags); TEST_RUN(basename, test_fragment_parse_libs); TEST_RUN(basename, test_fragment_add_single); TEST_RUN(basename, test_fragment_render_empty); TEST_RUN(basename, test_fragment_render_cflags); TEST_RUN(basename, test_fragment_render_libs); TEST_RUN(basename, test_fragment_filter_only_includes); TEST_RUN(basename, test_fragment_filter_only_libnames); TEST_RUN(basename, test_fragment_filter_keeps_nothing); TEST_RUN(basename, test_fragment_filter_splice_moves_matches); TEST_RUN(basename, test_fragment_splice_list); TEST_RUN(basename, test_fragment_has_system_dir_matches); TEST_RUN(basename, test_fragment_has_system_dir_libs); return EXIT_SUCCESS; }