xref: /freebsd/contrib/pkgconf/tests/api/test-license.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
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