1 /* 2 * test-tuple.c 3 * Tests for the public libpkgconf tuple 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 static void 23 test_tuple_add_and_find(void) 24 { 25 pkgconf_client_t *client = test_client_new(); 26 pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER; 27 28 /* parse=false means store the value verbatim, no bytecode compile. 29 * That's the right mode for storing already-evaluated values. */ 30 pkgconf_tuple_t *t = pkgconf_tuple_add(client, &tuples, "prefix", "/opt/foo", false, 0); 31 TEST_ASSERT_NONNULL(t); 32 33 const char *found = pkgconf_tuple_find(client, &tuples, "prefix"); 34 TEST_ASSERT_NONNULL(found); 35 TEST_ASSERT_STRCMP_EQ(found, "/opt/foo"); 36 37 pkgconf_tuple_free(&tuples); 38 pkgconf_client_free(client); 39 } 40 41 static void 42 test_tuple_find_absent(void) 43 { 44 pkgconf_client_t *client = test_client_new(); 45 pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER; 46 47 const char *found = pkgconf_tuple_find(client, &tuples, "nonexistent"); 48 TEST_ASSERT_EMPTY_STRING(found); 49 50 pkgconf_tuple_free(&tuples); 51 pkgconf_client_free(client); 52 } 53 54 static void 55 test_tuple_add_multiple(void) 56 { 57 pkgconf_client_t *client = test_client_new(); 58 pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER; 59 60 pkgconf_tuple_add(client, &tuples, "prefix", "/usr", false, 0); 61 pkgconf_tuple_add(client, &tuples, "exec_prefix", "/usr/local", false, 0); 62 pkgconf_tuple_add(client, &tuples, "libdir", "/usr/lib", false, 0); 63 64 TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find(client, &tuples, "prefix"), "/usr"); 65 TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find(client, &tuples, "exec_prefix"), "/usr/local"); 66 TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find(client, &tuples, "libdir"), "/usr/lib"); 67 TEST_ASSERT_EMPTY_STRING(pkgconf_tuple_find(client, &tuples, "datadir")); 68 69 pkgconf_tuple_free(&tuples); 70 pkgconf_client_free(client); 71 } 72 73 static void 74 test_tuple_global_add_and_find(void) 75 { 76 pkgconf_client_t *client = test_client_new(); 77 78 pkgconf_tuple_add_global(client, "PKG_TEST_KEY", "test_value"); 79 80 const char *found = pkgconf_tuple_find_global(client, "PKG_TEST_KEY"); 81 TEST_ASSERT_NONNULL(found); 82 TEST_ASSERT_STRCMP_EQ(found, "test_value"); 83 84 pkgconf_tuple_free_global(client); 85 pkgconf_client_free(client); 86 } 87 88 static void 89 test_tuple_global_find_absent(void) 90 { 91 pkgconf_client_t *client = test_client_new(); 92 93 const char *found = pkgconf_tuple_find_global(client, "DOES_NOT_EXIST"); 94 TEST_ASSERT_EMPTY_STRING(found); 95 96 pkgconf_client_free(client); 97 } 98 99 static void 100 test_tuple_define_global_kv_form(void) 101 { 102 pkgconf_client_t *client = test_client_new(); 103 104 /* This is the exact path --define-variable=KEY=VALUE takes 105 * pkgconf_tuple_define_global parses the "key=value" form and inserts it as a global tuple. */ 106 pkgconf_tuple_define_global(client, "myvar=myvalue"); 107 108 const char *found = pkgconf_tuple_find_global(client, "myvar"); 109 TEST_ASSERT_NONNULL(found); 110 TEST_ASSERT_STRCMP_EQ(found, "myvalue"); 111 112 pkgconf_tuple_free_global(client); 113 pkgconf_client_free(client); 114 } 115 116 static void 117 test_tuple_define_global_with_equals_in_value(void) 118 { 119 pkgconf_client_t *client = test_client_new(); 120 121 // Only the first '=' should split key from value; subsequent '=' characters belong to the value. 122 pkgconf_tuple_define_global(client, "CFLAGS=-DFOO=bar"); 123 124 const char *found = pkgconf_tuple_find_global(client, "CFLAGS"); 125 TEST_ASSERT_NONNULL(found); 126 TEST_ASSERT_STRCMP_EQ(found, "-DFOO=bar"); 127 128 pkgconf_tuple_free_global(client); 129 pkgconf_client_free(client); 130 } 131 132 static void 133 test_tuple_global_multiple(void) 134 { 135 pkgconf_client_t *client = test_client_new(); 136 137 pkgconf_tuple_define_global(client, "a=1"); 138 pkgconf_tuple_define_global(client, "b=2"); 139 pkgconf_tuple_define_global(client, "c=3"); 140 141 TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find_global(client, "a"), "1"); 142 TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find_global(client, "b"), "2"); 143 TEST_ASSERT_STRCMP_EQ(pkgconf_tuple_find_global(client, "c"), "3"); 144 145 pkgconf_tuple_free_global(client); 146 pkgconf_client_free(client); 147 } 148 149 static void 150 test_tuple_global_free_resets(void) 151 { 152 pkgconf_client_t *client = test_client_new(); 153 154 pkgconf_tuple_define_global(client, "temp=value"); 155 TEST_ASSERT_NONNULL(pkgconf_tuple_find_global(client, "temp")); 156 157 pkgconf_tuple_free_global(client); 158 TEST_ASSERT_EMPTY_STRING(pkgconf_tuple_find_global(client, "temp")); 159 160 pkgconf_client_free(client); 161 } 162 163 static void 164 test_tuple_free_empty(void) 165 { 166 pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER; 167 168 // Freeing an empty list should be a no-op. Mostly an ASan/leak check smoke test. 169 pkgconf_tuple_free(&tuples); 170 } 171 172 static void 173 test_tuple_define_variable_end_to_end(void) 174 { 175 pkgconf_client_t *client = test_client_new(); 176 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 177 bool saw_sysroot = false; 178 179 pkgconf_tuple_define_global(client, "myprefix=/opt/custom"); 180 181 char *out = pkgconf_bytecode_eval_str(client, &vars, "-I${myprefix}/include", &saw_sysroot); 182 183 TEST_ASSERT_NONNULL(out); 184 TEST_ASSERT_STRCMP_EQ(out, "-I/opt/custom/include"); 185 186 free(out); 187 pkgconf_variable_list_free(&vars); 188 pkgconf_tuple_free_global(client); 189 pkgconf_client_free(client); 190 } 191 192 static void 193 test_tuple_define_variable_overrides_local(void) 194 { 195 pkgconf_client_t *client = test_client_new(); 196 pkgconf_list_t vars = PKGCONF_LIST_INITIALIZER; 197 bool saw_sysroot = false; 198 199 pkgconf_variable_t *v = pkgconf_variable_get_or_create(&vars, "prefix"); 200 TEST_ASSERT_NONNULL(v); 201 pkgconf_buffer_reset(&v->bcbuf); 202 pkgconf_bytecode_compile(&v->bcbuf, "/usr"); 203 pkgconf_bytecode_from_buffer(&v->bc, &v->bcbuf); 204 205 // Simulate user passing --define-variable=prefix=/custom. 206 pkgconf_tuple_define_global(client, "prefix=/custom"); 207 208 char *out = pkgconf_bytecode_eval_str(client, &vars, "${prefix}/lib", &saw_sysroot); 209 210 TEST_ASSERT_NONNULL(out); 211 TEST_ASSERT_STRCMP_EQ(out, "/custom/lib"); 212 213 free(out); 214 pkgconf_variable_list_free(&vars); 215 pkgconf_tuple_free_global(client); 216 pkgconf_client_free(client); 217 } 218 219 static void 220 test_tuple_escaped_quote(void) 221 { 222 pkgconf_client_t *client = test_client_new(); 223 pkgconf_list_t tuples = PKGCONF_LIST_INITIALIZER; 224 225 // A double-quoted value containing an escaped double-quote 226 pkgconf_tuple_t *t = pkgconf_tuple_add(client, &tuples, "key", "\"a\\\"b\"", true, 0); 227 TEST_ASSERT_NONNULL(t); 228 229 const char *found = pkgconf_tuple_find(client, &tuples, "key"); 230 TEST_ASSERT_NONNULL(found); 231 TEST_ASSERT_STRCMP_EQ(found, "a\"b"); 232 233 pkgconf_tuple_free(&tuples); 234 pkgconf_client_free(client); 235 } 236 237 int 238 main(int argc, char *argv[]) 239 { 240 (void) argc; 241 const char *basename = pkgconf_path_find_basename(argv[0]); 242 243 TEST_RUN(basename, test_tuple_add_and_find); 244 TEST_RUN(basename, test_tuple_find_absent); 245 TEST_RUN(basename, test_tuple_add_multiple); 246 TEST_RUN(basename, test_tuple_free_empty); 247 TEST_RUN(basename, test_tuple_global_add_and_find); 248 TEST_RUN(basename, test_tuple_global_find_absent); 249 TEST_RUN(basename, test_tuple_define_global_kv_form); 250 TEST_RUN(basename, test_tuple_define_global_with_equals_in_value); 251 TEST_RUN(basename, test_tuple_global_multiple); 252 TEST_RUN(basename, test_tuple_global_free_resets); 253 TEST_RUN(basename, test_tuple_define_variable_end_to_end); 254 TEST_RUN(basename, test_tuple_define_variable_overrides_local); 255 TEST_RUN(basename, test_tuple_escaped_quote); 256 257 return EXIT_SUCCESS; 258 } 259