1 /* 2 * test-variable.c 3 * Tests for the public libpkgconf variable API. 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2025 pkgconf authors (see AUTHORS). 8 * 9 * Permission to use, copy, modify, and/or distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * This software is provided 'as is' and without any warranty, express or 14 * implied. In no event shall the authors be liable for any damages arising 15 * from the use of this software. 16 */ 17 18 #include <libpkgconf/stdinc.h> 19 #include <libpkgconf/libpkgconf.h> 20 #include "test-api.h" 21 22 /* 23 * Install a value on a variable by compiling it as bytecode. 24 * Mirrors what the parser does when it reads a .pc field. 25 */ 26 static void 27 seed_variable(pkgconf_list_t *vars, const char *key, const char *value) 28 { 29 pkgconf_variable_t *v = pkgconf_variable_get_or_create(vars, key); 30 TEST_ASSERT_NONNULL(v); 31 32 pkgconf_buffer_reset(&v->bcbuf); 33 pkgconf_bytecode_compile(&v->bcbuf, value); 34 pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf); 35 } 36 37 static void 38 test_variable_new_and_free(void) 39 { 40 pkgconf_variable_t *v = pkgconf_variable_new("prefix"); 41 42 TEST_ASSERT_NONNULL(v); 43 TEST_ASSERT_STRCMP_EQ(v->key, "prefix"); 44 45 pkgconf_variable_free(v); 46 } 47 48 static void 49 test_variable_get_or_create_creates(void) 50 { 51 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 52 53 pkgconf_variable_t *v = pkgconf_variable_get_or_create(&vars, "prefix"); 54 TEST_ASSERT_NONNULL(v); 55 TEST_ASSERT_STRCMP_EQ(v->key, "prefix"); 56 57 pkgconf_variable_list_free(&vars); 58 } 59 60 static void 61 test_variable_get_or_create_returns_existing(void) 62 { 63 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 64 65 pkgconf_variable_t *first = pkgconf_variable_get_or_create(&vars, "libdir"); 66 TEST_ASSERT_NONNULL(first); 67 68 // A second call with the same key should return the same object, not create a duplicate. 69 pkgconf_variable_t *second = pkgconf_variable_get_or_create(&vars, "libdir"); 70 TEST_ASSERT_NONNULL(second); 71 TEST_ASSERT_EQ(first, second); 72 73 pkgconf_variable_list_free(&vars); 74 } 75 76 static void 77 test_variable_find_present(void) 78 { 79 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 80 81 pkgconf_variable_t *created = pkgconf_variable_get_or_create(&vars, "prefix"); 82 TEST_ASSERT_NONNULL(created); 83 84 pkgconf_variable_t *found = pkgconf_variable_find(&vars, "prefix"); 85 TEST_ASSERT_NONNULL(found); 86 TEST_ASSERT_EQ(created, found); 87 88 pkgconf_variable_list_free(&vars); 89 } 90 91 static void 92 test_variable_find_absent(void) 93 { 94 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 95 96 pkgconf_variable_t *found = pkgconf_variable_find(&vars, "nonexistent"); 97 TEST_ASSERT_NULL(found); 98 99 pkgconf_variable_list_free(&vars); 100 } 101 102 static void 103 test_variable_find_among_many(void) 104 { 105 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 106 107 pkgconf_variable_get_or_create(&vars, "prefix"); 108 pkgconf_variable_get_or_create(&vars, "exec_prefix"); 109 pkgconf_variable_get_or_create(&vars, "libdir"); 110 pkgconf_variable_get_or_create(&vars, "includedir"); 111 112 TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "prefix")); 113 TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "exec_prefix")); 114 TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "libdir")); 115 TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "includedir")); 116 TEST_ASSERT_NULL(pkgconf_variable_find(&vars, "notpresent")); 117 118 pkgconf_variable_list_free(&vars); 119 } 120 121 static void 122 test_variable_delete(void) 123 { 124 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 125 126 pkgconf_variable_t *v = pkgconf_variable_get_or_create(&vars, "prefix"); 127 TEST_ASSERT_NONNULL(v); 128 TEST_ASSERT_NONNULL(pkgconf_variable_find(&vars, "prefix")); 129 130 pkgconf_variable_delete(&vars, v); 131 TEST_ASSERT_NULL(pkgconf_variable_find(&vars, "prefix")); 132 133 pkgconf_variable_list_free(&vars); 134 } 135 136 static void 137 test_variable_eval_str_plain(void) 138 { 139 pkgconf_client_t *client = test_client_new(); 140 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 141 bool saw_sysroot = false; 142 143 seed_variable(&vars, "version", "1.2.3"); 144 145 pkgconf_variable_t *v = pkgconf_variable_find(&vars, "version"); 146 TEST_ASSERT_NONNULL(v); 147 148 char *out = pkgconf_variable_eval_str(client, &vars, v, &saw_sysroot); 149 TEST_ASSERT_NONNULL(out); 150 TEST_ASSERT_STRCMP_EQ(out, "1.2.3"); 151 152 free(out); 153 pkgconf_variable_list_free(&vars); 154 pkgconf_client_free(client); 155 } 156 157 static void 158 test_variable_eval_str_with_reference(void) 159 { 160 pkgconf_client_t *client = test_client_new(); 161 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 162 bool saw_sysroot = false; 163 164 // Standard pkg-config layout: libdir is derived from prefix. 165 seed_variable(&vars, "prefix", "/opt/foo"); 166 seed_variable(&vars, "libdir", "${prefix}/lib"); 167 168 pkgconf_variable_t *libdir = pkgconf_variable_find(&vars, "libdir"); 169 TEST_ASSERT_NONNULL(libdir); 170 171 char *out = pkgconf_variable_eval_str(client, &vars, libdir, &saw_sysroot); 172 TEST_ASSERT_NONNULL(out); 173 TEST_ASSERT_STRCMP_EQ(out, "/opt/foo/lib"); 174 175 free(out); 176 pkgconf_variable_list_free(&vars); 177 pkgconf_client_free(client); 178 } 179 180 static void 181 test_variable_eval_str_chained(void) 182 { 183 pkgconf_client_t *client = test_client_new(); 184 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 185 bool saw_sysroot = false; 186 187 seed_variable(&vars, "prefix", "/usr/local"); 188 seed_variable(&vars, "exec_prefix", "${prefix}"); 189 seed_variable(&vars, "includedir", "${exec_prefix}/include"); 190 191 pkgconf_variable_t *includedir = pkgconf_variable_find(&vars, "includedir"); 192 TEST_ASSERT_NONNULL(includedir); 193 194 char *out = pkgconf_variable_eval_str(client, &vars, includedir, &saw_sysroot); 195 TEST_ASSERT_NONNULL(out); 196 TEST_ASSERT_STRCMP_EQ(out, "/usr/local/include"); 197 198 free(out); 199 pkgconf_variable_list_free(&vars); 200 pkgconf_client_free(client); 201 } 202 203 static void 204 test_variable_list_free_handles_empty(void) 205 { 206 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 207 208 // Freeing an empty list should be a no-op and not crash. Mostly an ASan/leak smoke test. 209 pkgconf_variable_list_free(&vars); 210 } 211 212 static void 213 test_variable_list_free_handles_many(void) 214 { 215 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 216 217 /* Add a mix of bare and value-carrying variables, then free the whole list at once. 218 * Mostly an ASan/leak smoke test. */ 219 pkgconf_variable_get_or_create(&vars, "a"); 220 seed_variable(&vars, "b", "/path/b"); 221 pkgconf_variable_get_or_create(&vars, "c"); 222 seed_variable(&vars, "d", "${b}/sub"); 223 224 pkgconf_variable_list_free(&vars); 225 } 226 227 int 228 main(int argc, char *argv[]) 229 { 230 (void) argc; 231 const char *basename = pkgconf_path_find_basename(argv[0]); 232 233 TEST_RUN(basename, test_variable_new_and_free); 234 TEST_RUN(basename, test_variable_get_or_create_creates); 235 TEST_RUN(basename, test_variable_get_or_create_returns_existing); 236 TEST_RUN(basename, test_variable_find_present); 237 TEST_RUN(basename, test_variable_find_absent); 238 TEST_RUN(basename, test_variable_find_among_many); 239 TEST_RUN(basename, test_variable_delete); 240 TEST_RUN(basename, test_variable_eval_str_plain); 241 TEST_RUN(basename, test_variable_eval_str_with_reference); 242 TEST_RUN(basename, test_variable_eval_str_chained); 243 TEST_RUN(basename, test_variable_list_free_handles_empty); 244 TEST_RUN(basename, test_variable_list_free_handles_many); 245 246 return EXIT_SUCCESS; 247 } 248