1 /*
2 * test-license.c
3 * Tests for the public libpkgconf license API.
4 *
5 * SPDX-License-Identifier: pkgconf
6 *
7 * Copyright (c) 2026 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 "test-api.h"
19
20 static size_t
license_count(const pkgconf_list_t * list)21 license_count(const pkgconf_list_t *list)
22 {
23 size_t n = 0;
24 const pkgconf_node_t *iter;
25
26 PKGCONF_FOREACH_LIST_ENTRY(list->head, iter)
27 {
28 n++;
29 }
30
31 return n;
32 }
33
34 static char *
render_to_string(pkgconf_client_t * client,const pkgconf_list_t * list)35 render_to_string(pkgconf_client_t *client, const pkgconf_list_t *list)
36 {
37 pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
38 pkgconf_license_render(client, list, &buf);
39
40 char *out = strdup(pkgconf_buffer_str_or_empty(&buf));
41 pkgconf_buffer_finalize(&buf);
42 return out;
43 }
44
45 static void
test_license_insert_and_free(void)46 test_license_insert_and_free(void)
47 {
48 pkgconf_client_t *client = test_client_new();
49 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
50
51 pkgconf_license_insert(client, &licenses, PKGCONF_LICENSE_EXPRESSION, "BSD-3-Clause");
52 TEST_ASSERT_EQ(license_count(&licenses), 1);
53
54 const pkgconf_license_t *l = licenses.head->data;
55 TEST_ASSERT_NONNULL(l);
56 TEST_ASSERT_EQ(l->type, PKGCONF_LICENSE_EXPRESSION);
57 TEST_ASSERT_STRCMP_EQ(l->data, "BSD-3-Clause");
58
59 pkgconf_license_free(&licenses);
60 pkgconf_client_free(client);
61 }
62
63 static void
test_license_free_empty(void)64 test_license_free_empty(void)
65 {
66 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
67
68 // Freeing an empty list is a no-op. Smoke test.
69 pkgconf_license_free(&licenses);
70
71 // Smoke test
72 pkgconf_license_free(NULL);
73 }
74
75 static void
test_license_evaluate_single(void)76 test_license_evaluate_single(void)
77 {
78 pkgconf_client_t *client = test_client_new();
79 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
80
81 pkgconf_license_evaluate_str(client, &licenses, "BSD-3-Clause", 0);
82
83 TEST_ASSERT_EQ(license_count(&licenses), 1);
84 const pkgconf_license_t *l = licenses.head->data;
85 TEST_ASSERT_EQ(l->type, PKGCONF_LICENSE_EXPRESSION);
86 TEST_ASSERT_STRCMP_EQ(l->data, "BSD-3-Clause");
87
88 pkgconf_license_free(&licenses);
89 pkgconf_client_free(client);
90 }
91
92 static void
test_license_evaluate_or(void)93 test_license_evaluate_or(void)
94 {
95 pkgconf_client_t *client = test_client_new();
96 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
97
98 pkgconf_license_evaluate_str(client, &licenses, "MIT OR ISC", 0);
99
100 TEST_ASSERT_EQ(license_count(&licenses), 3);
101
102 char *rendered = render_to_string(client, &licenses);
103 TEST_ASSERT_STRCMP_EQ(rendered, "MIT OR ISC");
104 free(rendered);
105
106 pkgconf_license_free(&licenses);
107 pkgconf_client_free(client);
108 }
109
110 static void
test_license_evaluate_and(void)111 test_license_evaluate_and(void)
112 {
113 pkgconf_client_t *client = test_client_new();
114 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
115
116 pkgconf_license_evaluate_str(client, &licenses, "LGPL-2.1-only AND MIT", 0);
117
118 char *rendered = render_to_string(client, &licenses);
119 TEST_ASSERT_STRCMP_EQ(rendered, "LGPL-2.1-only AND MIT");
120 free(rendered);
121
122 pkgconf_license_free(&licenses);
123 pkgconf_client_free(client);
124 }
125
126 static void
test_license_evaluate_multiple_keys_implicit_and(void)127 test_license_evaluate_multiple_keys_implicit_and(void)
128 {
129 pkgconf_client_t *client = test_client_new();
130 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
131
132 pkgconf_license_evaluate_str(client, &licenses, "BSD-3-Clause", 0);
133 pkgconf_license_evaluate_str(client, &licenses, "BSD-2-Clause", 0);
134
135 char *rendered = render_to_string(client, &licenses);
136 TEST_ASSERT_STRCMP_EQ(rendered, "BSD-3-Clause AND BSD-2-Clause");
137 free(rendered);
138
139 pkgconf_license_free(&licenses);
140 pkgconf_client_free(client);
141 }
142
143 static void
test_license_evaluate_brackets(void)144 test_license_evaluate_brackets(void)
145 {
146 pkgconf_client_t *client = test_client_new();
147 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
148
149 pkgconf_license_evaluate_str(client, &licenses, "ISC AND (BSD-3-Clause AND BSD-2-Clause)", 0);
150
151 char *rendered = render_to_string(client, &licenses);
152 TEST_ASSERT_STRCMP_EQ(rendered, "ISC AND (BSD-3-Clause AND BSD-2-Clause)");
153 free(rendered);
154
155 pkgconf_license_free(&licenses);
156 pkgconf_client_free(client);
157 }
158
159 static void
test_license_evaluate_empty(void)160 test_license_evaluate_empty(void)
161 {
162 pkgconf_client_t *client = test_client_new();
163 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
164
165 pkgconf_license_evaluate_str(client, &licenses, "", 0);
166 TEST_ASSERT_EQ(license_count(&licenses), 0);
167
168 pkgconf_license_free(&licenses);
169 pkgconf_client_free(client);
170 }
171
172 static void
test_license_evaluate_sanitizes(void)173 test_license_evaluate_sanitizes(void)
174 {
175 pkgconf_client_t *client = test_client_new();
176 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
177
178 /* The sanitiser strips characters outside the allowed set
179 * (alnum, '-', '+', '(', ')', '.', ':'). A token of pure
180 * junk should sanitise to empty and be skipped. */
181 pkgconf_license_evaluate_str(client, &licenses, "BSD-3-Clause", 0);
182
183 const pkgconf_license_t *l = licenses.head->data;
184 TEST_ASSERT_STRCMP_EQ(l->data, "BSD-3-Clause");
185
186 pkgconf_license_free(&licenses);
187 pkgconf_client_free(client);
188 }
189
190 static void
test_license_render_empty(void)191 test_license_render_empty(void)
192 {
193 pkgconf_client_t *client = test_client_new();
194 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
195
196 char *rendered = render_to_string(client, &licenses);
197 TEST_ASSERT_EMPTY_STRING(rendered);
198 free(rendered);
199
200 pkgconf_license_free(&licenses);
201 pkgconf_client_free(client);
202 }
203
204 static void
test_license_render_single(void)205 test_license_render_single(void)
206 {
207 pkgconf_client_t *client = test_client_new();
208 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
209
210 pkgconf_license_insert(client, &licenses, PKGCONF_LICENSE_EXPRESSION, "MIT");
211
212 char *rendered = render_to_string(client, &licenses);
213 TEST_ASSERT_STRCMP_EQ(rendered, "MIT");
214 free(rendered);
215
216 pkgconf_license_free(&licenses);
217 pkgconf_client_free(client);
218 }
219
220 static void
test_license_copy_list(void)221 test_license_copy_list(void)
222 {
223 pkgconf_client_t *client = test_client_new();
224 pkgconf_list_t src = PKGCONF_LIST_INITIALIZER;
225 pkgconf_list_t dst = PKGCONF_LIST_INITIALIZER;
226
227 pkgconf_license_evaluate_str(client, &src, "MIT OR ISC", 0);
228 size_t src_count = license_count(&src);
229
230 pkgconf_license_copy_list(client, &dst, &src);
231 TEST_ASSERT_EQ(license_count(&dst), src_count);
232
233 /* The copy is independent: freeing the source must not affect
234 * the destination's rendered output. */
235 pkgconf_license_free(&src);
236
237 char *rendered = render_to_string(client, &dst);
238 TEST_ASSERT_STRCMP_EQ(rendered, "MIT OR ISC");
239 free(rendered);
240
241 pkgconf_license_free(&dst);
242 pkgconf_client_free(client);
243 }
244
245 static void
test_license_evaluate_long_sanitized_token(void)246 test_license_evaluate_long_sanitized_token(void)
247 {
248 pkgconf_client_t *client = test_client_new();
249 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
250
251 char token[512];
252 token[0] = 'A';
253 memset(token + 1, '_', 400);
254 token[401] = '\0';
255
256 pkgconf_license_evaluate_str(client, &licenses, token, 0);
257
258 TEST_ASSERT_EQ(license_count(&licenses), 1);
259 const pkgconf_license_t *l = licenses.head->data;
260 TEST_ASSERT_EQ(l->type, PKGCONF_LICENSE_EXPRESSION);
261 TEST_ASSERT_STRCMP_EQ(l->data, "A");
262
263 pkgconf_license_free(&licenses);
264 pkgconf_client_free(client);
265 }
266
267 static void
test_license_evaluate_unterminated_quote(void)268 test_license_evaluate_unterminated_quote(void)
269 {
270 pkgconf_client_t *client = test_client_new();
271 pkgconf_list_t licenses = PKGCONF_LIST_INITIALIZER;
272
273 pkgconf_license_evaluate_str(client, &licenses, "\"", 0);
274 TEST_ASSERT_EQ(license_count(&licenses), 0);
275
276 pkgconf_license_evaluate_str(client, &licenses, "MIT \"unterminated", 0);
277 TEST_ASSERT_EQ(license_count(&licenses), 0);
278
279 pkgconf_license_evaluate_str(client, &licenses, "\\", 0);
280 TEST_ASSERT_EQ(license_count(&licenses), 0);
281
282 pkgconf_license_free(&licenses);
283 pkgconf_client_free(client);
284 }
285
286 int
main(int argc,char * argv[])287 main(int argc, char *argv[])
288 {
289 (void) argc;
290 const char *basename = pkgconf_path_find_basename(argv[0]);
291
292 TEST_RUN(basename, test_license_insert_and_free);
293 TEST_RUN(basename, test_license_free_empty);
294
295 TEST_RUN(basename, test_license_evaluate_single);
296 TEST_RUN(basename, test_license_evaluate_or);
297 TEST_RUN(basename, test_license_evaluate_and);
298 TEST_RUN(basename, test_license_evaluate_multiple_keys_implicit_and);
299 TEST_RUN(basename, test_license_evaluate_brackets);
300 TEST_RUN(basename, test_license_evaluate_empty);
301 TEST_RUN(basename, test_license_evaluate_sanitizes);
302 TEST_RUN(basename, test_license_evaluate_long_sanitized_token);
303 TEST_RUN(basename, test_license_evaluate_unterminated_quote);
304
305 TEST_RUN(basename, test_license_render_empty);
306 TEST_RUN(basename, test_license_render_single);
307
308 TEST_RUN(basename, test_license_copy_list);
309
310 return EXIT_SUCCESS;
311 }
312