xref: /freebsd/contrib/pkgconf/tests/test-runner.c (revision 592efe252472a3385acf36b1f49ecf710a7f3d9c)
1*592efe25SPierre Pronchery /*
2*592efe25SPierre Pronchery  * test-runner.c
3*592efe25SPierre Pronchery  * test harness
4*592efe25SPierre Pronchery  *
5*592efe25SPierre Pronchery  * SPDX-License-Identifier: pkgconf
6*592efe25SPierre Pronchery  *
7*592efe25SPierre Pronchery  * Copyright (c) 2025 pkgconf authors (see AUTHORS).
8*592efe25SPierre Pronchery  *
9*592efe25SPierre Pronchery  * Permission to use, copy, modify, and/or distribute this software for any
10*592efe25SPierre Pronchery  * purpose with or without fee is hereby granted, provided that the above
11*592efe25SPierre Pronchery  * copyright notice and this permission notice appear in all copies.
12*592efe25SPierre Pronchery  *
13*592efe25SPierre Pronchery  * This software is provided 'as is' and without any warranty, express or
14*592efe25SPierre Pronchery  * implied.  In no event shall the authors be liable for any damages arising
15*592efe25SPierre Pronchery  * from the use of this software.
16*592efe25SPierre Pronchery  */
17*592efe25SPierre Pronchery 
18*592efe25SPierre Pronchery #include <libpkgconf/config.h>
19*592efe25SPierre Pronchery #include <libpkgconf/libpkgconf.h>
20*592efe25SPierre Pronchery #include <libpkgconf/stdinc.h>
21*592efe25SPierre Pronchery #include <tests/win-shim.h>
22*592efe25SPierre Pronchery #include <sys/types.h>
23*592efe25SPierre Pronchery #ifndef _WIN32
24*592efe25SPierre Pronchery #	include <sys/wait.h>
25*592efe25SPierre Pronchery #endif
26*592efe25SPierre Pronchery #include <cli/core.h>
27*592efe25SPierre Pronchery #include <cli/getopt_long.h>
28*592efe25SPierre Pronchery #include <limits.h>
29*592efe25SPierre Pronchery #include <assert.h>
30*592efe25SPierre Pronchery 
31*592efe25SPierre Pronchery #ifndef PKGCONF_LITE
32*592efe25SPierre Pronchery #	if !defined(_WIN32) && !defined(__HAIKU__)
33*592efe25SPierre Pronchery #		define PKGCONF_TEST_PLATFORM "unix"
34*592efe25SPierre Pronchery #	elif !defined(_WIN32)
35*592efe25SPierre Pronchery #		define PKGCONF_TEST_PLATFORM "haiku"
36*592efe25SPierre Pronchery #	else
37*592efe25SPierre Pronchery #		define PKGCONF_TEST_PLATFORM "windows"
38*592efe25SPierre Pronchery #	endif
39*592efe25SPierre Pronchery #else // PKGCONF_LITE
40*592efe25SPierre Pronchery #	define PKGCONF_TEST_PLATFORM "lite"
41*592efe25SPierre Pronchery #endif // PKGCONF_LITE
42*592efe25SPierre Pronchery 
43*592efe25SPierre Pronchery static void test_parser_warn(void *p, const char *fmt, ...) PRINTFLIKE(2, 3);
44*592efe25SPierre Pronchery static void handle_substs(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const char *pwd);
45*592efe25SPierre Pronchery 
46*592efe25SPierre Pronchery static pkgconf_buffer_t test_fixtures_dir = PKGCONF_BUFFER_INITIALIZER;
47*592efe25SPierre Pronchery static pkgconf_buffer_t test_tool_dir = PKGCONF_BUFFER_INITIALIZER;
48*592efe25SPierre Pronchery static bool debug = false;
49*592efe25SPierre Pronchery 
50*592efe25SPierre Pronchery typedef enum test_match_strategy_
51*592efe25SPierre Pronchery {
52*592efe25SPierre Pronchery 	MATCH_EXACT = 0,
53*592efe25SPierre Pronchery 	MATCH_PARTIAL,
54*592efe25SPierre Pronchery 	MATCH_EMPTY,
55*592efe25SPierre Pronchery } pkgconf_test_match_strategy_t;
56*592efe25SPierre Pronchery 
57*592efe25SPierre Pronchery typedef struct test_case_
58*592efe25SPierre Pronchery {
59*592efe25SPierre Pronchery 	char *name;
60*592efe25SPierre Pronchery 	char *testfile_dir;
61*592efe25SPierre Pronchery 
62*592efe25SPierre Pronchery 	pkgconf_list_t search_path;
63*592efe25SPierre Pronchery 	pkgconf_buffer_t query;
64*592efe25SPierre Pronchery 
65*592efe25SPierre Pronchery 	pkgconf_list_t expected_stdout;
66*592efe25SPierre Pronchery 	pkgconf_test_match_strategy_t match_stdout;
67*592efe25SPierre Pronchery 
68*592efe25SPierre Pronchery 	pkgconf_list_t expected_stderr;
69*592efe25SPierre Pronchery 	pkgconf_test_match_strategy_t match_stderr;
70*592efe25SPierre Pronchery 
71*592efe25SPierre Pronchery 	pkgconf_buffer_t expected_stdout_file;
72*592efe25SPierre Pronchery 
73*592efe25SPierre Pronchery 	int exitcode;
74*592efe25SPierre Pronchery 	uint64_t wanted_flags;
75*592efe25SPierre Pronchery 
76*592efe25SPierre Pronchery 	pkgconf_list_t env_vars;
77*592efe25SPierre Pronchery 
78*592efe25SPierre Pronchery 	pkgconf_buffer_t want_env_prefix;
79*592efe25SPierre Pronchery 	pkgconf_buffer_t want_variable;
80*592efe25SPierre Pronchery 	pkgconf_buffer_t fragment_filter;
81*592efe25SPierre Pronchery 
82*592efe25SPierre Pronchery 	pkgconf_buffer_t skip_platforms;
83*592efe25SPierre Pronchery 	bool require_utf8_locale;
84*592efe25SPierre Pronchery 
85*592efe25SPierre Pronchery 	pkgconf_list_t define_variables;
86*592efe25SPierre Pronchery 
87*592efe25SPierre Pronchery 	int verbosity;
88*592efe25SPierre Pronchery 
89*592efe25SPierre Pronchery 	pkgconf_buffer_t atleast_version;
90*592efe25SPierre Pronchery 	pkgconf_buffer_t exact_version;
91*592efe25SPierre Pronchery 	pkgconf_buffer_t max_version;
92*592efe25SPierre Pronchery 
93*592efe25SPierre Pronchery 	pkgconf_buffer_t tool;
94*592efe25SPierre Pronchery 	pkgconf_buffer_t tool_args; // TODO: tool-specific flags
95*592efe25SPierre Pronchery 
96*592efe25SPierre Pronchery 	pkgconf_list_t mkdirs;
97*592efe25SPierre Pronchery #ifndef _WIN32
98*592efe25SPierre Pronchery 	pkgconf_list_t symlinks;
99*592efe25SPierre Pronchery #endif
100*592efe25SPierre Pronchery 	pkgconf_list_t copies;
101*592efe25SPierre Pronchery 
102*592efe25SPierre Pronchery #ifndef PKGCONF_LITE
103*592efe25SPierre Pronchery 	pkgconf_buffer_t want_personality;
104*592efe25SPierre Pronchery #endif
105*592efe25SPierre Pronchery } pkgconf_test_case_t;
106*592efe25SPierre Pronchery 
107*592efe25SPierre Pronchery typedef struct test_state_
108*592efe25SPierre Pronchery {
109*592efe25SPierre Pronchery 	pkgconf_cli_state_t cli_state;
110*592efe25SPierre Pronchery 	const pkgconf_test_case_t *testcase;
111*592efe25SPierre Pronchery } pkgconf_test_state_t;
112*592efe25SPierre Pronchery 
113*592efe25SPierre Pronchery typedef struct test_environ_
114*592efe25SPierre Pronchery {
115*592efe25SPierre Pronchery 	pkgconf_node_t node;
116*592efe25SPierre Pronchery 	char *key;
117*592efe25SPierre Pronchery 	char *value;
118*592efe25SPierre Pronchery } pkgconf_test_environ_t;
119*592efe25SPierre Pronchery 
120*592efe25SPierre Pronchery typedef struct test_output_
121*592efe25SPierre Pronchery {
122*592efe25SPierre Pronchery 	pkgconf_output_t output;
123*592efe25SPierre Pronchery 
124*592efe25SPierre Pronchery 	pkgconf_buffer_t o_stdout;
125*592efe25SPierre Pronchery 	pkgconf_buffer_t o_stderr;
126*592efe25SPierre Pronchery } pkgconf_test_output_t;
127*592efe25SPierre Pronchery 
128*592efe25SPierre Pronchery typedef struct test_flag_pair_
129*592efe25SPierre Pronchery {
130*592efe25SPierre Pronchery 	const char *name;
131*592efe25SPierre Pronchery 	uint64_t flag;
132*592efe25SPierre Pronchery } pkgconf_test_flag_pair_t;
133*592efe25SPierre Pronchery 
134*592efe25SPierre Pronchery typedef void (*test_keyword_func_t)(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value);
135*592efe25SPierre Pronchery 
136*592efe25SPierre Pronchery typedef struct test_keyword_pair_
137*592efe25SPierre Pronchery {
138*592efe25SPierre Pronchery 	const char *keyword;
139*592efe25SPierre Pronchery 	const test_keyword_func_t func;
140*592efe25SPierre Pronchery 	const ptrdiff_t offset;
141*592efe25SPierre Pronchery } pkgconf_test_keyword_pair_t;
142*592efe25SPierre Pronchery 
143*592efe25SPierre Pronchery static void
test_environment_push(pkgconf_test_case_t * testcase,const char * key,const char * value)144*592efe25SPierre Pronchery test_environment_push(pkgconf_test_case_t *testcase, const char *key, const char *value)
145*592efe25SPierre Pronchery {
146*592efe25SPierre Pronchery 	pkgconf_test_environ_t *env = calloc(1, sizeof(*env));
147*592efe25SPierre Pronchery 	if (env == NULL)
148*592efe25SPierre Pronchery 		return;
149*592efe25SPierre Pronchery 
150*592efe25SPierre Pronchery 	env->key = strdup(key);
151*592efe25SPierre Pronchery 	env->value = strdup(value);
152*592efe25SPierre Pronchery 	pkgconf_node_insert_tail(&env->node, env, &testcase->env_vars);
153*592efe25SPierre Pronchery }
154*592efe25SPierre Pronchery 
155*592efe25SPierre Pronchery static void
test_environment_free(pkgconf_list_t * env_list)156*592efe25SPierre Pronchery test_environment_free(pkgconf_list_t *env_list)
157*592efe25SPierre Pronchery {
158*592efe25SPierre Pronchery 	pkgconf_node_t *iter, *iter_next;
159*592efe25SPierre Pronchery 
160*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(env_list->head, iter_next, iter)
161*592efe25SPierre Pronchery 	{
162*592efe25SPierre Pronchery 		pkgconf_test_environ_t *env = iter->data;
163*592efe25SPierre Pronchery 
164*592efe25SPierre Pronchery 		pkgconf_node_delete(&env->node, env_list);
165*592efe25SPierre Pronchery 
166*592efe25SPierre Pronchery 		free(env->key);
167*592efe25SPierre Pronchery 		free(env->value);
168*592efe25SPierre Pronchery 		free(env);
169*592efe25SPierre Pronchery 	}
170*592efe25SPierre Pronchery }
171*592efe25SPierre Pronchery 
172*592efe25SPierre Pronchery static const char *
environ_lookup_handler(const pkgconf_client_t * client,const char * key)173*592efe25SPierre Pronchery environ_lookup_handler(const pkgconf_client_t *client, const char *key)
174*592efe25SPierre Pronchery {
175*592efe25SPierre Pronchery 	pkgconf_test_state_t *state = client->client_data;
176*592efe25SPierre Pronchery 	pkgconf_node_t *iter;
177*592efe25SPierre Pronchery 
178*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(state->testcase->env_vars.head, iter)
179*592efe25SPierre Pronchery 	{
180*592efe25SPierre Pronchery 		pkgconf_test_environ_t *env = iter->data;
181*592efe25SPierre Pronchery 
182*592efe25SPierre Pronchery 		if (!strcmp(key, env->key))
183*592efe25SPierre Pronchery 		{
184*592efe25SPierre Pronchery 			char cwd[PATH_MAX] = {0};
185*592efe25SPierre Pronchery 			const char *pwd = getcwd(cwd, sizeof(cwd));
186*592efe25SPierre Pronchery 
187*592efe25SPierre Pronchery 			pkgconf_buffer_t expanded = PKGCONF_BUFFER_INITIALIZER;
188*592efe25SPierre Pronchery 			handle_substs(&expanded, PKGCONF_BUFFER_FROM_STR(env->value), pwd);
189*592efe25SPierre Pronchery 
190*592efe25SPierre Pronchery 			free(env->value);
191*592efe25SPierre Pronchery 			env->value = strdup(pkgconf_buffer_str_or_empty(&expanded));
192*592efe25SPierre Pronchery 			pkgconf_buffer_finalize(&expanded);
193*592efe25SPierre Pronchery 
194*592efe25SPierre Pronchery 			return env->value;
195*592efe25SPierre Pronchery 		}
196*592efe25SPierre Pronchery 	}
197*592efe25SPierre Pronchery 
198*592efe25SPierre Pronchery 	return NULL;
199*592efe25SPierre Pronchery }
200*592efe25SPierre Pronchery 
201*592efe25SPierre Pronchery #ifndef PKGCONF_LITE
202*592efe25SPierre Pronchery static bool
debug_handler(const char * msg,const pkgconf_client_t * client,void * data)203*592efe25SPierre Pronchery debug_handler(const char *msg, const pkgconf_client_t *client, void *data)
204*592efe25SPierre Pronchery {
205*592efe25SPierre Pronchery 	(void) client;
206*592efe25SPierre Pronchery 	(void) data;
207*592efe25SPierre Pronchery 	fprintf(stderr, "%s", msg);
208*592efe25SPierre Pronchery 	return true;
209*592efe25SPierre Pronchery }
210*592efe25SPierre Pronchery #endif // PKGCONF_LITE
211*592efe25SPierre Pronchery 
212*592efe25SPierre Pronchery static bool
error_handler(const char * msg,const pkgconf_client_t * client,void * data)213*592efe25SPierre Pronchery error_handler(const char *msg, const pkgconf_client_t *client, void *data)
214*592efe25SPierre Pronchery {
215*592efe25SPierre Pronchery 	(void) data;
216*592efe25SPierre Pronchery 	pkgconf_test_state_t *state = client->client_data;
217*592efe25SPierre Pronchery 	pkgconf_output_fmt(client->output, state->testcase->wanted_flags & PKG_ERRORS_ON_STDOUT ? PKGCONF_OUTPUT_STDOUT : PKGCONF_OUTPUT_STDERR, "%s", msg);
218*592efe25SPierre Pronchery 	return true;
219*592efe25SPierre Pronchery }
220*592efe25SPierre Pronchery 
221*592efe25SPierre Pronchery static bool
write_handler(pkgconf_output_t * output,pkgconf_output_stream_t stream,const pkgconf_buffer_t * buffer)222*592efe25SPierre Pronchery write_handler(pkgconf_output_t *output, pkgconf_output_stream_t stream, const pkgconf_buffer_t *buffer)
223*592efe25SPierre Pronchery {
224*592efe25SPierre Pronchery 	pkgconf_test_output_t *out = (pkgconf_test_output_t *) output;
225*592efe25SPierre Pronchery 	pkgconf_buffer_t *dest = stream == PKGCONF_OUTPUT_STDERR ? &out->o_stderr : &out->o_stdout;
226*592efe25SPierre Pronchery 
227*592efe25SPierre Pronchery 	pkgconf_buffer_append(dest, pkgconf_buffer_str(buffer));
228*592efe25SPierre Pronchery 	return true;
229*592efe25SPierre Pronchery }
230*592efe25SPierre Pronchery 
231*592efe25SPierre Pronchery static pkgconf_output_t *
test_output(void)232*592efe25SPierre Pronchery test_output(void)
233*592efe25SPierre Pronchery {
234*592efe25SPierre Pronchery 	static pkgconf_test_output_t output =
235*592efe25SPierre Pronchery 	{
236*592efe25SPierre Pronchery 		.output.write = write_handler,
237*592efe25SPierre Pronchery 	};
238*592efe25SPierre Pronchery 
239*592efe25SPierre Pronchery 	return &output.output;
240*592efe25SPierre Pronchery }
241*592efe25SPierre Pronchery 
242*592efe25SPierre Pronchery static void
test_output_reset(pkgconf_test_output_t * out)243*592efe25SPierre Pronchery test_output_reset(pkgconf_test_output_t *out)
244*592efe25SPierre Pronchery {
245*592efe25SPierre Pronchery 	pkgconf_buffer_reset(&out->o_stdout);
246*592efe25SPierre Pronchery 	pkgconf_buffer_reset(&out->o_stderr);
247*592efe25SPierre Pronchery }
248*592efe25SPierre Pronchery 
249*592efe25SPierre Pronchery /*
250*592efe25SPierre Pronchery  * handle_substs: expand %TEST_FIXTURES_DIR%, %DIR_SEP%, and %PWD%
251*592efe25SPierre Pronchery  * in src into dest. pwd may be NULL, in which case %PWD% is left as-is
252*592efe25SPierre Pronchery  * (it should only appear in fields that are re-expanded after tmp_dir creation).
253*592efe25SPierre Pronchery  */
254*592efe25SPierre Pronchery static void
handle_substs(pkgconf_buffer_t * dest,const pkgconf_buffer_t * src,const char * pwd)255*592efe25SPierre Pronchery handle_substs(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const char *pwd)
256*592efe25SPierre Pronchery {
257*592efe25SPierre Pronchery 	struct subst_pair
258*592efe25SPierre Pronchery 	{
259*592efe25SPierre Pronchery 		const char *key;
260*592efe25SPierre Pronchery 		const char *value;
261*592efe25SPierre Pronchery 	} subst_pairs[] =
262*592efe25SPierre Pronchery 	{
263*592efe25SPierre Pronchery 		{"%TEST_FIXTURES_DIR%",	pkgconf_buffer_str(&test_fixtures_dir)},
264*592efe25SPierre Pronchery 		{"%DIR_SEP%",		PKG_CONFIG_PATH_SEP_S},
265*592efe25SPierre Pronchery 		{"%PWD%",		pwd != NULL ? pwd : "%PWD%"},
266*592efe25SPierre Pronchery 	};
267*592efe25SPierre Pronchery 
268*592efe25SPierre Pronchery 	pkgconf_buffer_t workbuf_src = PKGCONF_BUFFER_INITIALIZER;
269*592efe25SPierre Pronchery 	pkgconf_buffer_t workbuf_dest = PKGCONF_BUFFER_INITIALIZER;
270*592efe25SPierre Pronchery 
271*592efe25SPierre Pronchery 	if (!pkgconf_buffer_len(src))
272*592efe25SPierre Pronchery 		return;
273*592efe25SPierre Pronchery 
274*592efe25SPierre Pronchery 	pkgconf_buffer_append(&workbuf_dest, pkgconf_buffer_str(src));
275*592efe25SPierre Pronchery 
276*592efe25SPierre Pronchery 	for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(subst_pairs); i++)
277*592efe25SPierre Pronchery 	{
278*592efe25SPierre Pronchery 		pkgconf_buffer_reset(&workbuf_src);
279*592efe25SPierre Pronchery 		pkgconf_buffer_append(&workbuf_src, pkgconf_buffer_str(&workbuf_dest));
280*592efe25SPierre Pronchery 
281*592efe25SPierre Pronchery 		pkgconf_buffer_reset(&workbuf_dest);
282*592efe25SPierre Pronchery 		pkgconf_buffer_subst(&workbuf_dest, &workbuf_src, subst_pairs[i].key, subst_pairs[i].value);
283*592efe25SPierre Pronchery 	}
284*592efe25SPierre Pronchery 
285*592efe25SPierre Pronchery 	pkgconf_buffer_append(dest, pkgconf_buffer_str(&workbuf_dest));
286*592efe25SPierre Pronchery 
287*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&workbuf_src);
288*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&workbuf_dest);
289*592efe25SPierre Pronchery }
290*592efe25SPierre Pronchery 
291*592efe25SPierre Pronchery static int
test_keyword_pair_cmp(const void * key,const void * ptr)292*592efe25SPierre Pronchery test_keyword_pair_cmp(const void *key, const void *ptr)
293*592efe25SPierre Pronchery {
294*592efe25SPierre Pronchery 	const pkgconf_test_keyword_pair_t *pair = ptr;
295*592efe25SPierre Pronchery 	return strcasecmp(key, pair->keyword);
296*592efe25SPierre Pronchery }
297*592efe25SPierre Pronchery 
298*592efe25SPierre Pronchery static void
test_keyword_set_int(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)299*592efe25SPierre Pronchery test_keyword_set_int(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
300*592efe25SPierre Pronchery {
301*592efe25SPierre Pronchery 	(void) keyword;
302*592efe25SPierre Pronchery 	(void) warnprefix;
303*592efe25SPierre Pronchery 
304*592efe25SPierre Pronchery 	int *dest = (int *)((char *) testcase + offset);
305*592efe25SPierre Pronchery 	*dest = atoi(value);
306*592efe25SPierre Pronchery }
307*592efe25SPierre Pronchery 
308*592efe25SPierre Pronchery static void
test_keyword_set_bool(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)309*592efe25SPierre Pronchery test_keyword_set_bool(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
310*592efe25SPierre Pronchery {
311*592efe25SPierre Pronchery 	(void) keyword;
312*592efe25SPierre Pronchery 	(void) warnprefix;
313*592efe25SPierre Pronchery 
314*592efe25SPierre Pronchery 	bool *dest = (bool *)((char *) testcase + offset);
315*592efe25SPierre Pronchery 	*dest = !strcasecmp(value, "true");
316*592efe25SPierre Pronchery }
317*592efe25SPierre Pronchery 
318*592efe25SPierre Pronchery static void
test_keyword_set_buffer(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)319*592efe25SPierre Pronchery test_keyword_set_buffer(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
320*592efe25SPierre Pronchery {
321*592efe25SPierre Pronchery 	(void) keyword;
322*592efe25SPierre Pronchery 	(void) warnprefix;
323*592efe25SPierre Pronchery 
324*592efe25SPierre Pronchery 	pkgconf_buffer_t *dest = (pkgconf_buffer_t *)((char *) testcase + offset);
325*592efe25SPierre Pronchery 	handle_substs(dest, PKGCONF_BUFFER_FROM_STR((char *) value), NULL);
326*592efe25SPierre Pronchery }
327*592efe25SPierre Pronchery 
328*592efe25SPierre Pronchery static void
test_keyword_extend_bufferset(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)329*592efe25SPierre Pronchery test_keyword_extend_bufferset(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
330*592efe25SPierre Pronchery {
331*592efe25SPierre Pronchery 	(void) keyword;
332*592efe25SPierre Pronchery 	(void) warnprefix;
333*592efe25SPierre Pronchery 
334*592efe25SPierre Pronchery 	pkgconf_list_t *dest = (pkgconf_list_t *)((char *) testcase + offset);
335*592efe25SPierre Pronchery 	pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
336*592efe25SPierre Pronchery 
337*592efe25SPierre Pronchery 	handle_substs(&buf, PKGCONF_BUFFER_FROM_STR((char *) value), NULL);
338*592efe25SPierre Pronchery 	pkgconf_bufferset_extend(dest, &buf);
339*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&buf);
340*592efe25SPierre Pronchery }
341*592efe25SPierre Pronchery 
342*592efe25SPierre Pronchery static int
test_flag_pair_cmp(const void * key,const void * ptr)343*592efe25SPierre Pronchery test_flag_pair_cmp(const void *key, const void *ptr)
344*592efe25SPierre Pronchery {
345*592efe25SPierre Pronchery 	const pkgconf_test_flag_pair_t *pair = ptr;
346*592efe25SPierre Pronchery 	return strcasecmp(key, pair->name);
347*592efe25SPierre Pronchery }
348*592efe25SPierre Pronchery 
349*592efe25SPierre Pronchery static const pkgconf_test_flag_pair_t test_flag_pairs[] =
350*592efe25SPierre Pronchery {
351*592efe25SPierre Pronchery 	{"cflags",			PKG_CFLAGS},
352*592efe25SPierre Pronchery 	{"cflags-only-i",		PKG_CFLAGS_ONLY_I},
353*592efe25SPierre Pronchery 	{"cflags-only-other",		PKG_CFLAGS_ONLY_OTHER},
354*592efe25SPierre Pronchery 	{"debug",			PKG_DEBUG},
355*592efe25SPierre Pronchery 	{"define-prefix",		PKG_DEFINE_PREFIX},
356*592efe25SPierre Pronchery 	{"digraph",			PKG_DIGRAPH},
357*592efe25SPierre Pronchery 	{"dont-define-prefix",		PKG_DONT_DEFINE_PREFIX},
358*592efe25SPierre Pronchery 	{"dont-relocate-paths",		PKG_DONT_RELOCATE_PATHS},
359*592efe25SPierre Pronchery 	{"dump-license",		PKG_DUMP_LICENSE},
360*592efe25SPierre Pronchery 	{"dump-license-file",		PKG_DUMP_LICENSE_FILE},
361*592efe25SPierre Pronchery 	{"dump-personality",		PKG_DUMP_PERSONALITY},
362*592efe25SPierre Pronchery 	{"dump-source",			PKG_DUMP_SOURCE},
363*592efe25SPierre Pronchery 	{"env-only",			PKG_ENV_ONLY},
364*592efe25SPierre Pronchery 	{"errors-on-stdout",		PKG_ERRORS_ON_STDOUT},
365*592efe25SPierre Pronchery 	{"exists",			PKG_EXISTS},
366*592efe25SPierre Pronchery 	{"exists-cflags",		PKG_EXISTS_CFLAGS},
367*592efe25SPierre Pronchery 	{"fragment-tree",		PKG_FRAGMENT_TREE},
368*592efe25SPierre Pronchery 	{"ignore-conflicts",		PKG_IGNORE_CONFLICTS},
369*592efe25SPierre Pronchery 	{"internal-cflags",		PKG_INTERNAL_CFLAGS},
370*592efe25SPierre Pronchery 	{"keep-system-cflags",		PKG_KEEP_SYSTEM_CFLAGS},
371*592efe25SPierre Pronchery 	{"keep-system-libs",		PKG_KEEP_SYSTEM_LIBS},
372*592efe25SPierre Pronchery 	{"libs",			PKG_LIBS},
373*592efe25SPierre Pronchery 	{"libs-only-ldpath",		PKG_LIBS_ONLY_LDPATH},
374*592efe25SPierre Pronchery 	{"libs-only-libname",		PKG_LIBS_ONLY_LIBNAME},
375*592efe25SPierre Pronchery 	{"libs-only-other",		PKG_LIBS_ONLY_OTHER},
376*592efe25SPierre Pronchery 	{"link-abi",			PKG_LINK_ABI},
377*592efe25SPierre Pronchery 	{"list",			PKG_LIST},
378*592efe25SPierre Pronchery 	{"list-package-names",		PKG_LIST_PACKAGE_NAMES},
379*592efe25SPierre Pronchery 	{"modversion",			PKG_MODVERSION},
380*592efe25SPierre Pronchery 	{"msvc-syntax",			PKG_MSVC_SYNTAX},
381*592efe25SPierre Pronchery 	{"newlines",			PKG_NEWLINES},
382*592efe25SPierre Pronchery 	{"no-cache",			PKG_NO_CACHE},
383*592efe25SPierre Pronchery 	{"no-provides",			PKG_NO_PROVIDES},
384*592efe25SPierre Pronchery 	{"no-uninstalled",		PKG_NO_UNINSTALLED},
385*592efe25SPierre Pronchery 	{"path",			PKG_PATH},
386*592efe25SPierre Pronchery 	{"print-digraph-query-nodes",	PKG_PRINT_DIGRAPH_QUERY_NODES},
387*592efe25SPierre Pronchery 	{"print-errors",		PKG_PRINT_ERRORS},
388*592efe25SPierre Pronchery 	{"print-provides",		PKG_PROVIDES},
389*592efe25SPierre Pronchery 	{"print-requires",		PKG_REQUIRES},
390*592efe25SPierre Pronchery 	{"print-requires-private",	PKG_REQUIRES_PRIVATE},
391*592efe25SPierre Pronchery 	{"print-variables",		PKG_VARIABLES},
392*592efe25SPierre Pronchery 	{"pure",			PKG_PURE},
393*592efe25SPierre Pronchery 	{"shared",			PKG_SHARED},
394*592efe25SPierre Pronchery 	{"short-errors",		PKG_SHORT_ERRORS},
395*592efe25SPierre Pronchery 	{"silence-errors",		PKG_SILENCE_ERRORS},
396*592efe25SPierre Pronchery 	{"simulate",			PKG_SIMULATE},
397*592efe25SPierre Pronchery 	{"solution",			PKG_SOLUTION},
398*592efe25SPierre Pronchery 	{"static",			PKG_STATIC},
399*592efe25SPierre Pronchery 	{"uninstalled",			PKG_UNINSTALLED},
400*592efe25SPierre Pronchery 	{"validate",			PKG_VALIDATE},
401*592efe25SPierre Pronchery };
402*592efe25SPierre Pronchery 
403*592efe25SPierre Pronchery static void
test_keyword_set_wanted_flags(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)404*592efe25SPierre Pronchery test_keyword_set_wanted_flags(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
405*592efe25SPierre Pronchery {
406*592efe25SPierre Pronchery 	int i;
407*592efe25SPierre Pronchery 	int flagcount;
408*592efe25SPierre Pronchery 	char **flags = NULL;
409*592efe25SPierre Pronchery 
410*592efe25SPierre Pronchery 	(void) keyword;
411*592efe25SPierre Pronchery 	(void) warnprefix;
412*592efe25SPierre Pronchery 	(void) offset;
413*592efe25SPierre Pronchery 
414*592efe25SPierre Pronchery 	pkgconf_argv_split(value, &flagcount, &flags);
415*592efe25SPierre Pronchery 
416*592efe25SPierre Pronchery 	for (i = 0; i < flagcount; i++)
417*592efe25SPierre Pronchery 	{
418*592efe25SPierre Pronchery 		const char *flag = flags[i];
419*592efe25SPierre Pronchery 		const pkgconf_test_flag_pair_t *pair = bsearch(flag,
420*592efe25SPierre Pronchery 			test_flag_pairs, PKGCONF_ARRAY_SIZE(test_flag_pairs),
421*592efe25SPierre Pronchery 			sizeof(*pair), test_flag_pair_cmp);
422*592efe25SPierre Pronchery 
423*592efe25SPierre Pronchery 		if (pair == NULL)
424*592efe25SPierre Pronchery 			continue;
425*592efe25SPierre Pronchery 
426*592efe25SPierre Pronchery 		testcase->wanted_flags |= pair->flag;
427*592efe25SPierre Pronchery 	}
428*592efe25SPierre Pronchery 
429*592efe25SPierre Pronchery 	pkgconf_argv_free(flags);
430*592efe25SPierre Pronchery }
431*592efe25SPierre Pronchery 
432*592efe25SPierre Pronchery static size_t
prefixed_path_split(const char * text,pkgconf_list_t * dirlist,const char * prefix)433*592efe25SPierre Pronchery prefixed_path_split(const char *text, pkgconf_list_t *dirlist, const char *prefix)
434*592efe25SPierre Pronchery {
435*592efe25SPierre Pronchery 	size_t count = 0;
436*592efe25SPierre Pronchery 	char *workbuf, *p, *iter;
437*592efe25SPierre Pronchery 
438*592efe25SPierre Pronchery 	if (text == NULL)
439*592efe25SPierre Pronchery 		return 0;
440*592efe25SPierre Pronchery 
441*592efe25SPierre Pronchery 	iter = workbuf = strdup(text);
442*592efe25SPierre Pronchery 	while ((p = strtok(iter, PKG_CONFIG_PATH_SEP_S)) != NULL)
443*592efe25SPierre Pronchery 	{
444*592efe25SPierre Pronchery 		pkgconf_buffer_t pathbuf = PKGCONF_BUFFER_INITIALIZER;
445*592efe25SPierre Pronchery 
446*592efe25SPierre Pronchery 		pkgconf_buffer_append(&pathbuf, prefix);
447*592efe25SPierre Pronchery 		pkgconf_buffer_push_byte(&pathbuf, '/');
448*592efe25SPierre Pronchery 		pkgconf_buffer_append(&pathbuf, p);
449*592efe25SPierre Pronchery 		pkgconf_path_add(pkgconf_buffer_str(&pathbuf), dirlist, false);
450*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&pathbuf);
451*592efe25SPierre Pronchery 
452*592efe25SPierre Pronchery 		count++, iter = NULL;
453*592efe25SPierre Pronchery 	}
454*592efe25SPierre Pronchery 	free(workbuf);
455*592efe25SPierre Pronchery 
456*592efe25SPierre Pronchery 	return count;
457*592efe25SPierre Pronchery }
458*592efe25SPierre Pronchery 
459*592efe25SPierre Pronchery static void
test_keyword_set_path_list(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)460*592efe25SPierre Pronchery test_keyword_set_path_list(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
461*592efe25SPierre Pronchery {
462*592efe25SPierre Pronchery 	(void) keyword;
463*592efe25SPierre Pronchery 	(void) warnprefix;
464*592efe25SPierre Pronchery 
465*592efe25SPierre Pronchery 	pkgconf_list_t *dest = (pkgconf_list_t *)((char *) testcase + offset);
466*592efe25SPierre Pronchery 	prefixed_path_split(value, dest, pkgconf_buffer_str(&test_fixtures_dir));
467*592efe25SPierre Pronchery }
468*592efe25SPierre Pronchery 
469*592efe25SPierre Pronchery static void
test_keyword_set_match_strategy(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)470*592efe25SPierre Pronchery test_keyword_set_match_strategy(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
471*592efe25SPierre Pronchery {
472*592efe25SPierre Pronchery 	(void) keyword;
473*592efe25SPierre Pronchery 	(void) warnprefix;
474*592efe25SPierre Pronchery 
475*592efe25SPierre Pronchery 	pkgconf_test_match_strategy_t *dest = (pkgconf_test_match_strategy_t *)((char *) testcase + offset);
476*592efe25SPierre Pronchery 
477*592efe25SPierre Pronchery 	if (!strcasecmp(value, "partial"))
478*592efe25SPierre Pronchery 		*dest = MATCH_PARTIAL;
479*592efe25SPierre Pronchery 
480*592efe25SPierre Pronchery 	if (!strcasecmp(value, "empty"))
481*592efe25SPierre Pronchery 		*dest = MATCH_EMPTY;
482*592efe25SPierre Pronchery }
483*592efe25SPierre Pronchery 
484*592efe25SPierre Pronchery static void
test_keyword_set_environment(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)485*592efe25SPierre Pronchery test_keyword_set_environment(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
486*592efe25SPierre Pronchery {
487*592efe25SPierre Pronchery 	(void) keyword;
488*592efe25SPierre Pronchery 	(void) offset;
489*592efe25SPierre Pronchery 
490*592efe25SPierre Pronchery 	char *eq = strchr(value, '=');
491*592efe25SPierre Pronchery 	if (eq == NULL)
492*592efe25SPierre Pronchery 	{
493*592efe25SPierre Pronchery 		fprintf(stderr, "%s: malformed Environment entry: %s\n", warnprefix, value);
494*592efe25SPierre Pronchery 		return;
495*592efe25SPierre Pronchery 	}
496*592efe25SPierre Pronchery 
497*592efe25SPierre Pronchery 	*eq++ = '\0';
498*592efe25SPierre Pronchery 
499*592efe25SPierre Pronchery 	// store raw, vars are expanded at run time
500*592efe25SPierre Pronchery 	test_environment_push(testcase, value, eq);
501*592efe25SPierre Pronchery }
502*592efe25SPierre Pronchery 
503*592efe25SPierre Pronchery #ifdef _WIN32
504*592efe25SPierre Pronchery static void
test_keyword_disabled(pkgconf_test_case_t * testcase,const char * keyword,const char * warnprefix,const ptrdiff_t offset,const char * value)505*592efe25SPierre Pronchery test_keyword_disabled(pkgconf_test_case_t *testcase, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
506*592efe25SPierre Pronchery {
507*592efe25SPierre Pronchery 	(void) testcase;
508*592efe25SPierre Pronchery 	(void) keyword;
509*592efe25SPierre Pronchery 	(void) warnprefix;
510*592efe25SPierre Pronchery 	(void) offset;
511*592efe25SPierre Pronchery 	(void) value;
512*592efe25SPierre Pronchery }
513*592efe25SPierre Pronchery #endif
514*592efe25SPierre Pronchery 
515*592efe25SPierre Pronchery static const pkgconf_test_keyword_pair_t test_keyword_pairs[] =
516*592efe25SPierre Pronchery {
517*592efe25SPierre Pronchery 	{"AtLeastVersion",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, atleast_version)},
518*592efe25SPierre Pronchery 	{"DefineVariable",	test_keyword_extend_bufferset,		offsetof(pkgconf_test_case_t, define_variables)},
519*592efe25SPierre Pronchery 	{"Environment",		test_keyword_set_environment,		offsetof(pkgconf_test_case_t, env_vars)},
520*592efe25SPierre Pronchery 	{"ExactVersion",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, exact_version)},
521*592efe25SPierre Pronchery 	{"ExpectedExitCode",	test_keyword_set_int,			offsetof(pkgconf_test_case_t, exitcode)},
522*592efe25SPierre Pronchery 	{"ExpectedStderr",	test_keyword_extend_bufferset,		offsetof(pkgconf_test_case_t, expected_stderr)},
523*592efe25SPierre Pronchery 	{"ExpectedStdout",	test_keyword_extend_bufferset,		offsetof(pkgconf_test_case_t, expected_stdout)},
524*592efe25SPierre Pronchery 	{"ExpectedStdoutFile",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, expected_stdout_file)},
525*592efe25SPierre Pronchery 	{"FragmentFilter",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, fragment_filter)},
526*592efe25SPierre Pronchery 	{"MatchStderr",		test_keyword_set_match_strategy,	offsetof(pkgconf_test_case_t, match_stderr)},
527*592efe25SPierre Pronchery 	{"MatchStdout",		test_keyword_set_match_strategy,	offsetof(pkgconf_test_case_t, match_stdout)},
528*592efe25SPierre Pronchery 	{"MaxVersion",		test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, max_version)},
529*592efe25SPierre Pronchery 	{"PackageSearchPath",	test_keyword_set_path_list,		offsetof(pkgconf_test_case_t, search_path)},
530*592efe25SPierre Pronchery 	{"Query",		test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, query)},
531*592efe25SPierre Pronchery 	{"RequireUtf8Locale",	test_keyword_set_bool,			offsetof(pkgconf_test_case_t, require_utf8_locale)},
532*592efe25SPierre Pronchery 	{"SetupCopy",		test_keyword_extend_bufferset,		offsetof(pkgconf_test_case_t, copies)},
533*592efe25SPierre Pronchery 	{"SetupMkdir",		test_keyword_extend_bufferset,		offsetof(pkgconf_test_case_t, mkdirs)},
534*592efe25SPierre Pronchery #ifdef _WIN32
535*592efe25SPierre Pronchery 	{"SetupSymlink",	test_keyword_disabled,			0},
536*592efe25SPierre Pronchery #else
537*592efe25SPierre Pronchery 	{"SetupSymlink",	test_keyword_extend_bufferset,		offsetof(pkgconf_test_case_t, symlinks)},
538*592efe25SPierre Pronchery #endif
539*592efe25SPierre Pronchery 	{"SkipPlatforms",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, skip_platforms)},
540*592efe25SPierre Pronchery 	{"Tool",		test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, tool)},
541*592efe25SPierre Pronchery 	{"ToolArgs",		test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, tool_args)},
542*592efe25SPierre Pronchery 	{"VerbosityLevel",	test_keyword_set_int,			offsetof(pkgconf_test_case_t, verbosity)},
543*592efe25SPierre Pronchery 	{"WantedFlags",		test_keyword_set_wanted_flags,		offsetof(pkgconf_test_case_t, wanted_flags)},
544*592efe25SPierre Pronchery 	{"WantEnvPrefix",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, want_env_prefix)},
545*592efe25SPierre Pronchery #ifndef PKGCONF_LITE
546*592efe25SPierre Pronchery 	{"WantPersonality",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, want_personality)},
547*592efe25SPierre Pronchery #endif
548*592efe25SPierre Pronchery 	{"WantVariable",	test_keyword_set_buffer,		offsetof(pkgconf_test_case_t, want_variable)},
549*592efe25SPierre Pronchery };
550*592efe25SPierre Pronchery 
551*592efe25SPierre Pronchery static void
test_keyword_set(void * data,const char * warnprefix,const char * keyword,const char * value)552*592efe25SPierre Pronchery test_keyword_set(void *data, const char *warnprefix, const char *keyword, const char *value)
553*592efe25SPierre Pronchery {
554*592efe25SPierre Pronchery 	pkgconf_test_case_t *testcase = data;
555*592efe25SPierre Pronchery 	const pkgconf_test_keyword_pair_t *pair = bsearch(keyword,
556*592efe25SPierre Pronchery 		test_keyword_pairs, PKGCONF_ARRAY_SIZE(test_keyword_pairs),
557*592efe25SPierre Pronchery 		sizeof(*pair), test_keyword_pair_cmp);
558*592efe25SPierre Pronchery 
559*592efe25SPierre Pronchery 	if (pair == NULL || pair->func == NULL)
560*592efe25SPierre Pronchery 		return;
561*592efe25SPierre Pronchery 
562*592efe25SPierre Pronchery 	pair->func(testcase, warnprefix, keyword, pair->offset, value);
563*592efe25SPierre Pronchery }
564*592efe25SPierre Pronchery 
565*592efe25SPierre Pronchery static const pkgconf_parser_operand_func_t test_parser_ops[256] =
566*592efe25SPierre Pronchery {
567*592efe25SPierre Pronchery 	[':'] = (pkgconf_parser_operand_func_t) test_keyword_set,
568*592efe25SPierre Pronchery };
569*592efe25SPierre Pronchery 
570*592efe25SPierre Pronchery static void
test_parser_warn(void * p,const char * fmt,...)571*592efe25SPierre Pronchery test_parser_warn(void *p, const char *fmt, ...)
572*592efe25SPierre Pronchery {
573*592efe25SPierre Pronchery 	va_list va;
574*592efe25SPierre Pronchery 
575*592efe25SPierre Pronchery 	(void) p;
576*592efe25SPierre Pronchery 
577*592efe25SPierre Pronchery 	va_start(va, fmt);
578*592efe25SPierre Pronchery 	vfprintf(stderr, fmt, va);
579*592efe25SPierre Pronchery 	va_end(va);
580*592efe25SPierre Pronchery }
581*592efe25SPierre Pronchery 
582*592efe25SPierre Pronchery static pkgconf_test_case_t *
load_test_case(char * testfile)583*592efe25SPierre Pronchery load_test_case(char *testfile)
584*592efe25SPierre Pronchery {
585*592efe25SPierre Pronchery 	FILE *testf = fopen(testfile, "r");
586*592efe25SPierre Pronchery 	if (testf == NULL)
587*592efe25SPierre Pronchery 		return NULL;
588*592efe25SPierre Pronchery 
589*592efe25SPierre Pronchery 	pkgconf_test_case_t *out = calloc(1, sizeof(*out));
590*592efe25SPierre Pronchery 	if (out == NULL)
591*592efe25SPierre Pronchery 		goto cleanup;
592*592efe25SPierre Pronchery 
593*592efe25SPierre Pronchery 	char *nameptr;
594*592efe25SPierre Pronchery 	if ((nameptr = strrchr(testfile, '/')) != NULL)
595*592efe25SPierre Pronchery 		nameptr++;
596*592efe25SPierre Pronchery 	else
597*592efe25SPierre Pronchery 		nameptr = testfile;
598*592efe25SPierre Pronchery 
599*592efe25SPierre Pronchery 	out->name = strdup(nameptr);
600*592efe25SPierre Pronchery 
601*592efe25SPierre Pronchery 	// store directory containing the test file for ExpectedStdoutFile resolution
602*592efe25SPierre Pronchery 	{
603*592efe25SPierre Pronchery 		char *dirend = strrchr(testfile, '/');
604*592efe25SPierre Pronchery 		if (dirend != NULL)
605*592efe25SPierre Pronchery 		{
606*592efe25SPierre Pronchery 			size_t dirlen = (size_t)(dirend - testfile);
607*592efe25SPierre Pronchery 			out->testfile_dir = calloc(1, dirlen + 1);
608*592efe25SPierre Pronchery 			if (out->testfile_dir != NULL)
609*592efe25SPierre Pronchery 				memcpy(out->testfile_dir, testfile, dirlen);
610*592efe25SPierre Pronchery 		}
611*592efe25SPierre Pronchery 		else
612*592efe25SPierre Pronchery 			out->testfile_dir = strdup(".");
613*592efe25SPierre Pronchery 	}
614*592efe25SPierre Pronchery 
615*592efe25SPierre Pronchery 	pkgconf_parser_parse(testf, out, test_parser_ops, test_parser_warn, testfile);
616*592efe25SPierre Pronchery 
617*592efe25SPierre Pronchery cleanup:
618*592efe25SPierre Pronchery 	fclose(testf);
619*592efe25SPierre Pronchery 	return out;
620*592efe25SPierre Pronchery }
621*592efe25SPierre Pronchery 
622*592efe25SPierre Pronchery // we use a custom personality to ensure the tests are fully hermetic
623*592efe25SPierre Pronchery static pkgconf_cross_personality_t *
personality_for_test(const pkgconf_test_case_t * testcase)624*592efe25SPierre Pronchery personality_for_test(const pkgconf_test_case_t *testcase)
625*592efe25SPierre Pronchery {
626*592efe25SPierre Pronchery #ifndef PKGCONF_LITE
627*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&testcase->want_personality))
628*592efe25SPierre Pronchery 		return pkgconf_cross_personality_find(pkgconf_buffer_str(&testcase->want_personality));
629*592efe25SPierre Pronchery #endif
630*592efe25SPierre Pronchery 
631*592efe25SPierre Pronchery 	pkgconf_cross_personality_t *pers = calloc(1, sizeof(*pers));
632*592efe25SPierre Pronchery 	if (pers == NULL)
633*592efe25SPierre Pronchery 		return NULL;
634*592efe25SPierre Pronchery 
635*592efe25SPierre Pronchery 	pers->name = strdup("test");
636*592efe25SPierre Pronchery 	pkgconf_path_copy_list(&pers->dir_list, &testcase->search_path);
637*592efe25SPierre Pronchery 	pkgconf_path_add("/test/sysroot/include", &pers->filter_includedirs, false);
638*592efe25SPierre Pronchery 	pkgconf_path_add("/test/sysroot/lib", &pers->filter_libdirs, false);
639*592efe25SPierre Pronchery 
640*592efe25SPierre Pronchery 	return pers;
641*592efe25SPierre Pronchery }
642*592efe25SPierre Pronchery 
643*592efe25SPierre Pronchery static bool
report_failure(pkgconf_test_match_strategy_t match,const pkgconf_buffer_t * expected,const pkgconf_buffer_t * actual,const char * buffername)644*592efe25SPierre Pronchery report_failure(pkgconf_test_match_strategy_t match, const pkgconf_buffer_t *expected, const pkgconf_buffer_t *actual, const char *buffername)
645*592efe25SPierre Pronchery {
646*592efe25SPierre Pronchery 	fprintf(stderr,
647*592efe25SPierre Pronchery 		"================================================================================\n"
648*592efe25SPierre Pronchery 		"%s did not%s match:\n"
649*592efe25SPierre Pronchery 		"  expected: [%s]\n"
650*592efe25SPierre Pronchery 		"  actual: [%s]\n"
651*592efe25SPierre Pronchery 		"================================================================================\n",
652*592efe25SPierre Pronchery 		buffername, match == MATCH_PARTIAL ? " partially" : "",
653*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(expected),
654*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(actual));
655*592efe25SPierre Pronchery 
656*592efe25SPierre Pronchery 	return false;
657*592efe25SPierre Pronchery }
658*592efe25SPierre Pronchery 
659*592efe25SPierre Pronchery static bool
test_match_buffer(pkgconf_test_match_strategy_t match,const pkgconf_buffer_t * expected,const pkgconf_buffer_t * actual,const char * buffername)660*592efe25SPierre Pronchery test_match_buffer(pkgconf_test_match_strategy_t match, const pkgconf_buffer_t *expected, const pkgconf_buffer_t *actual, const char *buffername)
661*592efe25SPierre Pronchery {
662*592efe25SPierre Pronchery 	if (!pkgconf_buffer_len(expected) && match != MATCH_EMPTY)
663*592efe25SPierre Pronchery 		return true;
664*592efe25SPierre Pronchery 
665*592efe25SPierre Pronchery 	if (!pkgconf_buffer_len(actual))
666*592efe25SPierre Pronchery 	{
667*592efe25SPierre Pronchery 		if (match == MATCH_EMPTY)
668*592efe25SPierre Pronchery 			return true;
669*592efe25SPierre Pronchery 
670*592efe25SPierre Pronchery 		return report_failure(match, expected, actual, buffername);
671*592efe25SPierre Pronchery 	}
672*592efe25SPierre Pronchery 
673*592efe25SPierre Pronchery 	if (match == MATCH_PARTIAL)
674*592efe25SPierre Pronchery 		return pkgconf_buffer_contains(actual, expected) ? true : report_failure(match, expected, actual, buffername);
675*592efe25SPierre Pronchery 
676*592efe25SPierre Pronchery 	return pkgconf_buffer_match(actual, expected) ? true : report_failure(match, expected, actual, buffername);
677*592efe25SPierre Pronchery }
678*592efe25SPierre Pronchery 
679*592efe25SPierre Pronchery static bool
read_file_into_buffer(FILE * f,pkgconf_buffer_t * buf)680*592efe25SPierre Pronchery read_file_into_buffer(FILE *f, pkgconf_buffer_t *buf)
681*592efe25SPierre Pronchery {
682*592efe25SPierre Pronchery 	char tmp[4096] = {0};
683*592efe25SPierre Pronchery 	size_t n;
684*592efe25SPierre Pronchery 	while ((n = fread(tmp, 1, sizeof(tmp), f)) > 0)
685*592efe25SPierre Pronchery 		pkgconf_buffer_append_slice(buf, tmp, n);
686*592efe25SPierre Pronchery 
687*592efe25SPierre Pronchery 	if (ferror(f))
688*592efe25SPierre Pronchery 		return false;
689*592efe25SPierre Pronchery 
690*592efe25SPierre Pronchery 	return true;
691*592efe25SPierre Pronchery }
692*592efe25SPierre Pronchery 
693*592efe25SPierre Pronchery static bool
open_file_into_buffer(const char * path,pkgconf_buffer_t * buf)694*592efe25SPierre Pronchery open_file_into_buffer(const char *path, pkgconf_buffer_t *buf)
695*592efe25SPierre Pronchery {
696*592efe25SPierre Pronchery 	FILE *f = fopen(path, "r");
697*592efe25SPierre Pronchery 	if (f == NULL)
698*592efe25SPierre Pronchery 		return false;
699*592efe25SPierre Pronchery 
700*592efe25SPierre Pronchery 	bool ok = read_file_into_buffer(f, buf);
701*592efe25SPierre Pronchery 	fclose(f);
702*592efe25SPierre Pronchery 	return ok;
703*592efe25SPierre Pronchery }
704*592efe25SPierre Pronchery 
705*592efe25SPierre Pronchery static bool
copy_file(const char * dst,const char * src)706*592efe25SPierre Pronchery copy_file(const char *dst, const char *src)
707*592efe25SPierre Pronchery {
708*592efe25SPierre Pronchery 	FILE *fsrc = fopen(src, "rb");
709*592efe25SPierre Pronchery 	if (!fsrc)
710*592efe25SPierre Pronchery 		return false;
711*592efe25SPierre Pronchery 
712*592efe25SPierre Pronchery 	FILE *fdst = fopen(dst, "wb");
713*592efe25SPierre Pronchery 	if (!fdst)
714*592efe25SPierre Pronchery 	{
715*592efe25SPierre Pronchery 		int errno_save = errno;
716*592efe25SPierre Pronchery 		fclose(fsrc);
717*592efe25SPierre Pronchery 		errno = errno_save;
718*592efe25SPierre Pronchery 		return false;
719*592efe25SPierre Pronchery 	}
720*592efe25SPierre Pronchery 
721*592efe25SPierre Pronchery 	bool ok = true;
722*592efe25SPierre Pronchery 	char buf[4096] = {0};
723*592efe25SPierre Pronchery 	size_t nr;
724*592efe25SPierre Pronchery 	while ((nr = fread(buf, 1, sizeof(buf), fsrc)) > 0)
725*592efe25SPierre Pronchery 	{
726*592efe25SPierre Pronchery 		if (fwrite(buf, 1, nr, fdst) != nr)
727*592efe25SPierre Pronchery 		{
728*592efe25SPierre Pronchery 			ok = false;
729*592efe25SPierre Pronchery 			break;
730*592efe25SPierre Pronchery 		}
731*592efe25SPierre Pronchery 	}
732*592efe25SPierre Pronchery 
733*592efe25SPierre Pronchery 	if (ferror(fsrc) || ferror(fdst))
734*592efe25SPierre Pronchery 		ok = false;
735*592efe25SPierre Pronchery 
736*592efe25SPierre Pronchery 	int errno_save = errno;
737*592efe25SPierre Pronchery 	fclose(fsrc);
738*592efe25SPierre Pronchery 	fclose(fdst);
739*592efe25SPierre Pronchery 	errno = errno_save;
740*592efe25SPierre Pronchery 
741*592efe25SPierre Pronchery 	return ok;
742*592efe25SPierre Pronchery }
743*592efe25SPierre Pronchery 
744*592efe25SPierre Pronchery /*
745*592efe25SPierre Pronchery  * Recursively remove a directory tree.
746*592efe25SPierre Pronchery  */
747*592efe25SPierre Pronchery static void
rmdir_recursive(const char * path)748*592efe25SPierre Pronchery rmdir_recursive(const char *path)
749*592efe25SPierre Pronchery {
750*592efe25SPierre Pronchery 	DIR *dir = opendir(path);
751*592efe25SPierre Pronchery 	if (dir == NULL)
752*592efe25SPierre Pronchery 		return;
753*592efe25SPierre Pronchery 
754*592efe25SPierre Pronchery 	struct dirent *ent;
755*592efe25SPierre Pronchery 	while ((ent = readdir(dir)) != NULL)
756*592efe25SPierre Pronchery 	{
757*592efe25SPierre Pronchery 		if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
758*592efe25SPierre Pronchery 			continue;
759*592efe25SPierre Pronchery 
760*592efe25SPierre Pronchery 		pkgconf_buffer_t child = PKGCONF_BUFFER_INITIALIZER;
761*592efe25SPierre Pronchery 		pkgconf_buffer_append(&child, path);
762*592efe25SPierre Pronchery 		pkgconf_buffer_push_byte(&child, '/');
763*592efe25SPierre Pronchery 		pkgconf_buffer_append(&child, ent->d_name);
764*592efe25SPierre Pronchery 
765*592efe25SPierre Pronchery #ifdef _WIN32
766*592efe25SPierre Pronchery 		if (_access(pkgconf_buffer_str(&child), 0) == 0)
767*592efe25SPierre Pronchery 		{
768*592efe25SPierre Pronchery 			// Get required buffer size
769*592efe25SPierre Pronchery 			int size = MultiByteToWideChar(CP_ACP, 0, pkgconf_buffer_str(&child), -1, NULL, 0);
770*592efe25SPierre Pronchery 
771*592efe25SPierre Pronchery 			// Allocate and convert
772*592efe25SPierre Pronchery 			wchar_t* wide_path = calloc(size, sizeof(wchar_t));
773*592efe25SPierre Pronchery 			assert(wide_path != NULL);
774*592efe25SPierre Pronchery 			MultiByteToWideChar(CP_ACP, 0, pkgconf_buffer_str(&child), -1, wide_path, size);
775*592efe25SPierre Pronchery 
776*592efe25SPierre Pronchery 			DWORD attrs = GetFileAttributesW(wide_path);
777*592efe25SPierre Pronchery 
778*592efe25SPierre Pronchery 			free(wide_path);
779*592efe25SPierre Pronchery 
780*592efe25SPierre Pronchery 			if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
781*592efe25SPierre Pronchery 				rmdir_recursive(pkgconf_buffer_str(&child));
782*592efe25SPierre Pronchery 			else
783*592efe25SPierre Pronchery 				unlink(pkgconf_buffer_str(&child));
784*592efe25SPierre Pronchery 		}
785*592efe25SPierre Pronchery #else
786*592efe25SPierre Pronchery 		struct stat st;
787*592efe25SPierre Pronchery 		if (lstat(pkgconf_buffer_str(&child), &st) == 0)
788*592efe25SPierre Pronchery 		{
789*592efe25SPierre Pronchery 			if (S_ISDIR(st.st_mode))
790*592efe25SPierre Pronchery 				rmdir_recursive(pkgconf_buffer_str(&child));
791*592efe25SPierre Pronchery 			else
792*592efe25SPierre Pronchery 				unlink(pkgconf_buffer_str(&child));
793*592efe25SPierre Pronchery 		}
794*592efe25SPierre Pronchery #endif
795*592efe25SPierre Pronchery 
796*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&child);
797*592efe25SPierre Pronchery 	}
798*592efe25SPierre Pronchery 
799*592efe25SPierre Pronchery 	closedir(dir);
800*592efe25SPierre Pronchery 	rmdir(path);
801*592efe25SPierre Pronchery }
802*592efe25SPierre Pronchery 
803*592efe25SPierre Pronchery /*
804*592efe25SPierre Pronchery  * Recursively make a directory tree.
805*592efe25SPierre Pronchery  */
806*592efe25SPierre Pronchery static bool
mkdir_recursive(const char * path)807*592efe25SPierre Pronchery mkdir_recursive(const char *path)
808*592efe25SPierre Pronchery {
809*592efe25SPierre Pronchery 	if (!path)
810*592efe25SPierre Pronchery 		return false;
811*592efe25SPierre Pronchery 
812*592efe25SPierre Pronchery 	const char *tmpstr = NULL;
813*592efe25SPierre Pronchery 	pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
814*592efe25SPierre Pronchery 	size_t i = 0;
815*592efe25SPierre Pronchery 
816*592efe25SPierre Pronchery 	while (path[i])
817*592efe25SPierre Pronchery 	{
818*592efe25SPierre Pronchery 		// Append characters up to the next separator
819*592efe25SPierre Pronchery 		size_t start = i;
820*592efe25SPierre Pronchery 		while (path[i] && path[i] != '/')
821*592efe25SPierre Pronchery 			i++;
822*592efe25SPierre Pronchery 
823*592efe25SPierre Pronchery 		pkgconf_buffer_append_slice(&buf, path + start, i - start);
824*592efe25SPierre Pronchery 
825*592efe25SPierre Pronchery 		// If we hit a separator, try to create this component
826*592efe25SPierre Pronchery 		if (path[i] == '/')
827*592efe25SPierre Pronchery 		{
828*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(&buf, '/');
829*592efe25SPierre Pronchery 			tmpstr = pkgconf_buffer_str(&buf);
830*592efe25SPierre Pronchery 			if (tmpstr && mkdir(tmpstr, 0755) != 0 && errno != EEXIST)
831*592efe25SPierre Pronchery 			{
832*592efe25SPierre Pronchery 				pkgconf_buffer_finalize(&buf);
833*592efe25SPierre Pronchery 				return false;
834*592efe25SPierre Pronchery 			}
835*592efe25SPierre Pronchery 			i++; // skip the separator
836*592efe25SPierre Pronchery 		}
837*592efe25SPierre Pronchery 	}
838*592efe25SPierre Pronchery 
839*592efe25SPierre Pronchery 	// Make the final directory (handles paths without trailing separator)
840*592efe25SPierre Pronchery 	tmpstr = pkgconf_buffer_str(&buf);
841*592efe25SPierre Pronchery 	bool ok = true;
842*592efe25SPierre Pronchery 	if (tmpstr && strlen(tmpstr) > 0)
843*592efe25SPierre Pronchery 	{
844*592efe25SPierre Pronchery 		if (mkdir(tmpstr, 0755) != 0 && errno != EEXIST)
845*592efe25SPierre Pronchery 			ok = false;
846*592efe25SPierre Pronchery 	}
847*592efe25SPierre Pronchery 
848*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&buf);
849*592efe25SPierre Pronchery 	return ok;
850*592efe25SPierre Pronchery }
851*592efe25SPierre Pronchery 
852*592efe25SPierre Pronchery // Returns true if we need tmp_dir
853*592efe25SPierre Pronchery static bool
needs_tmp_dir(const pkgconf_test_case_t * testcase)854*592efe25SPierre Pronchery needs_tmp_dir(const pkgconf_test_case_t *testcase)
855*592efe25SPierre Pronchery {
856*592efe25SPierre Pronchery #ifdef _WIN32
857*592efe25SPierre Pronchery 	return testcase->mkdirs.head != NULL || testcase->copies.head != NULL;
858*592efe25SPierre Pronchery #else // _WIN32
859*592efe25SPierre Pronchery 	return testcase->mkdirs.head != NULL || testcase->copies.head != NULL || testcase->symlinks.head != NULL;
860*592efe25SPierre Pronchery #endif // _WIN32
861*592efe25SPierre Pronchery }
862*592efe25SPierre Pronchery 
863*592efe25SPierre Pronchery static int
run_tool(const pkgconf_test_case_t * testcase,pkgconf_buffer_t * o_stdout,pkgconf_buffer_t * o_stderr)864*592efe25SPierre Pronchery run_tool(const pkgconf_test_case_t *testcase, pkgconf_buffer_t *o_stdout, pkgconf_buffer_t *o_stderr)
865*592efe25SPierre Pronchery {
866*592efe25SPierre Pronchery 	(void) o_stderr; // TODO: external tool stderr goes to real stderr for now
867*592efe25SPierre Pronchery 
868*592efe25SPierre Pronchery 	pkgconf_buffer_t cmdbuf = PKGCONF_BUFFER_INITIALIZER;
869*592efe25SPierre Pronchery 
870*592efe25SPierre Pronchery 	// build: <tool-dir>/<tool> <tool_args>
871*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&test_tool_dir))
872*592efe25SPierre Pronchery 	{
873*592efe25SPierre Pronchery 		pkgconf_buffer_append(&cmdbuf, pkgconf_buffer_str(&test_tool_dir));
874*592efe25SPierre Pronchery 		pkgconf_buffer_push_byte(&cmdbuf, '/');
875*592efe25SPierre Pronchery 	}
876*592efe25SPierre Pronchery 
877*592efe25SPierre Pronchery 	pkgconf_buffer_append(&cmdbuf, pkgconf_buffer_str(&testcase->tool));
878*592efe25SPierre Pronchery 
879*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&testcase->tool_args))
880*592efe25SPierre Pronchery 	{
881*592efe25SPierre Pronchery 		pkgconf_buffer_append(&cmdbuf, " ");
882*592efe25SPierre Pronchery 		pkgconf_buffer_append(&cmdbuf, pkgconf_buffer_str(&testcase->tool_args));
883*592efe25SPierre Pronchery 	}
884*592efe25SPierre Pronchery 
885*592efe25SPierre Pronchery 	// Inject Environment vars for the child process
886*592efe25SPierre Pronchery 	char tool_cwd[PATH_MAX] = {0};
887*592efe25SPierre Pronchery 	const char *pwd = getcwd(tool_cwd, sizeof(tool_cwd));
888*592efe25SPierre Pronchery 
889*592efe25SPierre Pronchery 	pkgconf_node_t *iter;
890*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->env_vars.head, iter)
891*592efe25SPierre Pronchery 	{
892*592efe25SPierre Pronchery 		pkgconf_test_environ_t *env = iter->data;
893*592efe25SPierre Pronchery 		pkgconf_buffer_t expanded = PKGCONF_BUFFER_INITIALIZER;
894*592efe25SPierre Pronchery 		handle_substs(&expanded, PKGCONF_BUFFER_FROM_STR(env->value), pwd);
895*592efe25SPierre Pronchery 		setenv(env->key, pkgconf_buffer_str_or_empty(&expanded), 1);
896*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&expanded);
897*592efe25SPierre Pronchery 	}
898*592efe25SPierre Pronchery 
899*592efe25SPierre Pronchery 	FILE *pipe = popen(pkgconf_buffer_str(&cmdbuf), "r");
900*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&cmdbuf);
901*592efe25SPierre Pronchery 
902*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->env_vars.head, iter)
903*592efe25SPierre Pronchery 	{
904*592efe25SPierre Pronchery 		pkgconf_test_environ_t *env = iter->data;
905*592efe25SPierre Pronchery 		unsetenv(env->key);
906*592efe25SPierre Pronchery 	}
907*592efe25SPierre Pronchery 
908*592efe25SPierre Pronchery 	if (pipe == NULL)
909*592efe25SPierre Pronchery 	{
910*592efe25SPierre Pronchery 		fprintf(stderr, "popen failed for tool '%s': %s\n",
911*592efe25SPierre Pronchery 			pkgconf_buffer_str(&testcase->tool), strerror(errno));
912*592efe25SPierre Pronchery 		return -1;
913*592efe25SPierre Pronchery 	}
914*592efe25SPierre Pronchery 
915*592efe25SPierre Pronchery 	bool ok = read_file_into_buffer(pipe, o_stdout);
916*592efe25SPierre Pronchery 	int saved_errno = errno; // pclose() will clobber errno, save it
917*592efe25SPierre Pronchery 	int status = pclose(pipe);
918*592efe25SPierre Pronchery 	if (!ok)
919*592efe25SPierre Pronchery 	{
920*592efe25SPierre Pronchery 		fprintf(stderr, "read failed into buffer for command '%s': %s",
921*592efe25SPierre Pronchery 			pkgconf_buffer_str(&testcase->tool), strerror(saved_errno));
922*592efe25SPierre Pronchery 		return -1;
923*592efe25SPierre Pronchery 	}
924*592efe25SPierre Pronchery 
925*592efe25SPierre Pronchery 	if (status == -1)
926*592efe25SPierre Pronchery 	{
927*592efe25SPierre Pronchery 		fprintf(stderr, "pclose failed for command '%s': %s\n",
928*592efe25SPierre Pronchery 			pkgconf_buffer_str(&testcase->tool), strerror(errno));
929*592efe25SPierre Pronchery 		return -1;
930*592efe25SPierre Pronchery 	}
931*592efe25SPierre Pronchery 
932*592efe25SPierre Pronchery #if defined(WIFEXITED) && defined(WEXITSTATUS)
933*592efe25SPierre Pronchery 	if (WIFEXITED(status))
934*592efe25SPierre Pronchery 		return WEXITSTATUS(status);
935*592efe25SPierre Pronchery 
936*592efe25SPierre Pronchery 	fprintf(stderr, "command '%s' did not exit normally\n",
937*592efe25SPierre Pronchery 		pkgconf_buffer_str(&testcase->tool));
938*592efe25SPierre Pronchery 	return -1;
939*592efe25SPierre Pronchery #else
940*592efe25SPierre Pronchery 	return status;
941*592efe25SPierre Pronchery #endif
942*592efe25SPierre Pronchery }
943*592efe25SPierre Pronchery 
944*592efe25SPierre Pronchery /*
945*592efe25SPierre Pronchery  * Split a bufferset entry on the first space into left and right halves.
946*592efe25SPierre Pronchery  * Caller must free *left_out and *right_out.
947*592efe25SPierre Pronchery  */
948*592efe25SPierre Pronchery static bool
split_pair(const char * entry,char ** left_out,char ** right_out)949*592efe25SPierre Pronchery split_pair(const char *entry, char **left_out, char **right_out)
950*592efe25SPierre Pronchery {
951*592efe25SPierre Pronchery 	if (entry == NULL)
952*592efe25SPierre Pronchery 		return false;
953*592efe25SPierre Pronchery 
954*592efe25SPierre Pronchery 	const char *sp = strchr(entry, ' ');
955*592efe25SPierre Pronchery 	if (sp == NULL)
956*592efe25SPierre Pronchery 		return false;
957*592efe25SPierre Pronchery 
958*592efe25SPierre Pronchery 	*left_out = pkgconf_strndup(entry, (size_t)(sp - entry));
959*592efe25SPierre Pronchery 	*right_out = strdup(sp + 1);
960*592efe25SPierre Pronchery 	return true;
961*592efe25SPierre Pronchery }
962*592efe25SPierre Pronchery 
963*592efe25SPierre Pronchery /*
964*592efe25SPierre Pronchery  * run_setup: execute mkdirs, copies, and symlinks in order.
965*592efe25SPierre Pronchery  * Must be called after chdir() into the tmp_dir.
966*592efe25SPierre Pronchery  */
967*592efe25SPierre Pronchery static bool
run_setup(const pkgconf_test_case_t * testcase,const char * pwd)968*592efe25SPierre Pronchery run_setup(const pkgconf_test_case_t *testcase, const char *pwd)
969*592efe25SPierre Pronchery {
970*592efe25SPierre Pronchery 	pkgconf_node_t *iter;
971*592efe25SPierre Pronchery 
972*592efe25SPierre Pronchery 	// mkdirs: each entry is a single path, relative to tmp_dir
973*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->mkdirs.head, iter)
974*592efe25SPierre Pronchery 	{
975*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
976*592efe25SPierre Pronchery 
977*592efe25SPierre Pronchery 		pkgconf_buffer_t path = PKGCONF_BUFFER_INITIALIZER;
978*592efe25SPierre Pronchery 		handle_substs(&path, &set->buffer, pwd);
979*592efe25SPierre Pronchery 
980*592efe25SPierre Pronchery 		bool ok = mkdir_recursive(pkgconf_buffer_str(&path));
981*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&path);
982*592efe25SPierre Pronchery 
983*592efe25SPierre Pronchery 		if (!ok && errno != EEXIST)
984*592efe25SPierre Pronchery 		{
985*592efe25SPierre Pronchery 			fprintf(stderr, "SetupMkdir: mkdir '%s' failed: %s\n",
986*592efe25SPierre Pronchery 				pkgconf_buffer_str_or_empty(&set->buffer), strerror(errno));
987*592efe25SPierre Pronchery 			return false;
988*592efe25SPierre Pronchery 		}
989*592efe25SPierre Pronchery 	}
990*592efe25SPierre Pronchery 
991*592efe25SPierre Pronchery 	// copies: "src dst", src relative to TEST_FIXTURES_DIR, dst relative to tmp_dir
992*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->copies.head, iter)
993*592efe25SPierre Pronchery 	{
994*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
995*592efe25SPierre Pronchery 
996*592efe25SPierre Pronchery 		pkgconf_buffer_t expanded = PKGCONF_BUFFER_INITIALIZER;
997*592efe25SPierre Pronchery 		handle_substs(&expanded, &set->buffer, pwd);
998*592efe25SPierre Pronchery 
999*592efe25SPierre Pronchery 		char *left = NULL, *right = NULL;
1000*592efe25SPierre Pronchery 		if (!split_pair(pkgconf_buffer_str(&expanded), &left, &right))
1001*592efe25SPierre Pronchery 		{
1002*592efe25SPierre Pronchery 			fprintf(stderr, "SetupCopy: malformed entry (expected 'src dst'): %s\n",
1003*592efe25SPierre Pronchery 				pkgconf_buffer_str_or_empty(&set->buffer));
1004*592efe25SPierre Pronchery 			pkgconf_buffer_finalize(&expanded);
1005*592efe25SPierre Pronchery 			return false;
1006*592efe25SPierre Pronchery 		}
1007*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&expanded);
1008*592efe25SPierre Pronchery 
1009*592efe25SPierre Pronchery 		pkgconf_buffer_t srcpath = PKGCONF_BUFFER_INITIALIZER;
1010*592efe25SPierre Pronchery 		pkgconf_buffer_append(&srcpath, pkgconf_buffer_str(&test_fixtures_dir));
1011*592efe25SPierre Pronchery 		pkgconf_buffer_push_byte(&srcpath, '/');
1012*592efe25SPierre Pronchery 		pkgconf_buffer_append(&srcpath, left);
1013*592efe25SPierre Pronchery 
1014*592efe25SPierre Pronchery 		bool ok = copy_file(right, pkgconf_buffer_str(&srcpath));
1015*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&srcpath);
1016*592efe25SPierre Pronchery 		if (!ok)
1017*592efe25SPierre Pronchery 		{
1018*592efe25SPierre Pronchery 			fprintf(stderr, "SetupCopy: failed to copy file '%s' to '%s': %s\n", left, right, strerror(errno));
1019*592efe25SPierre Pronchery 			free(left);
1020*592efe25SPierre Pronchery 			free(right);
1021*592efe25SPierre Pronchery 			return false;
1022*592efe25SPierre Pronchery 		}
1023*592efe25SPierre Pronchery 
1024*592efe25SPierre Pronchery 		free(left);
1025*592efe25SPierre Pronchery 		free(right);
1026*592efe25SPierre Pronchery 	}
1027*592efe25SPierre Pronchery 
1028*592efe25SPierre Pronchery #ifndef _WIN32
1029*592efe25SPierre Pronchery 	// symlinks: "target linkpath" — both may be relative to tmp_dir or absolute after %PWD% expansion
1030*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->symlinks.head, iter)
1031*592efe25SPierre Pronchery 	{
1032*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
1033*592efe25SPierre Pronchery 
1034*592efe25SPierre Pronchery 		pkgconf_buffer_t expanded = PKGCONF_BUFFER_INITIALIZER;
1035*592efe25SPierre Pronchery 		handle_substs(&expanded, &set->buffer, pwd);
1036*592efe25SPierre Pronchery 
1037*592efe25SPierre Pronchery 		char *target = NULL, *linkpath = NULL;
1038*592efe25SPierre Pronchery 		if (!split_pair(pkgconf_buffer_str(&expanded), &target, &linkpath))
1039*592efe25SPierre Pronchery 		{
1040*592efe25SPierre Pronchery 			fprintf(stderr, "SetupSymlink: malformed entry (expected 'target linkpath'): %s\n",
1041*592efe25SPierre Pronchery 				pkgconf_buffer_str_or_empty(&set->buffer));
1042*592efe25SPierre Pronchery 			pkgconf_buffer_finalize(&expanded);
1043*592efe25SPierre Pronchery 			return false;
1044*592efe25SPierre Pronchery 		}
1045*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&expanded);
1046*592efe25SPierre Pronchery 
1047*592efe25SPierre Pronchery 		unlink(linkpath);
1048*592efe25SPierre Pronchery 
1049*592efe25SPierre Pronchery 		if (symlink(target, linkpath) != 0)
1050*592efe25SPierre Pronchery 		{
1051*592efe25SPierre Pronchery 			fprintf(stderr, "SetupSymlink: symlink('%s', '%s') failed: %s\n",
1052*592efe25SPierre Pronchery 				target, linkpath, strerror(errno));
1053*592efe25SPierre Pronchery 			free(target);
1054*592efe25SPierre Pronchery 			free(linkpath);
1055*592efe25SPierre Pronchery 			return false;
1056*592efe25SPierre Pronchery 		}
1057*592efe25SPierre Pronchery 
1058*592efe25SPierre Pronchery 		free(target);
1059*592efe25SPierre Pronchery 		free(linkpath);
1060*592efe25SPierre Pronchery 	}
1061*592efe25SPierre Pronchery #endif // _WIN32
1062*592efe25SPierre Pronchery 
1063*592efe25SPierre Pronchery 	return true;
1064*592efe25SPierre Pronchery }
1065*592efe25SPierre Pronchery 
1066*592efe25SPierre Pronchery static void
annotate_result(const pkgconf_test_case_t * testcase,int ret,const pkgconf_test_output_t * out)1067*592efe25SPierre Pronchery annotate_result(const pkgconf_test_case_t *testcase, int ret, const pkgconf_test_output_t *out)
1068*592efe25SPierre Pronchery {
1069*592efe25SPierre Pronchery 	pkgconf_buffer_t search_path_buf = PKGCONF_BUFFER_INITIALIZER;
1070*592efe25SPierre Pronchery 	const pkgconf_node_t *iter;
1071*592efe25SPierre Pronchery 
1072*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->search_path.head, iter)
1073*592efe25SPierre Pronchery 	{
1074*592efe25SPierre Pronchery 		const pkgconf_path_t *path = iter->data;
1075*592efe25SPierre Pronchery 
1076*592efe25SPierre Pronchery 		if (pkgconf_buffer_len(&search_path_buf))
1077*592efe25SPierre Pronchery 			pkgconf_buffer_push_byte(&search_path_buf, ' ');
1078*592efe25SPierre Pronchery 
1079*592efe25SPierre Pronchery 		pkgconf_buffer_append(&search_path_buf, path->path);
1080*592efe25SPierre Pronchery 	}
1081*592efe25SPierre Pronchery 
1082*592efe25SPierre Pronchery 	pkgconf_buffer_t wanted_flags_buf = PKGCONF_BUFFER_INITIALIZER;
1083*592efe25SPierre Pronchery 
1084*592efe25SPierre Pronchery 	for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(test_flag_pairs); i++)
1085*592efe25SPierre Pronchery 	{
1086*592efe25SPierre Pronchery 		const pkgconf_test_flag_pair_t *pair = &test_flag_pairs[i];
1087*592efe25SPierre Pronchery 
1088*592efe25SPierre Pronchery 		if ((testcase->wanted_flags & pair->flag) == pair->flag)
1089*592efe25SPierre Pronchery 		{
1090*592efe25SPierre Pronchery 			if (pkgconf_buffer_len(&wanted_flags_buf))
1091*592efe25SPierre Pronchery 				pkgconf_buffer_push_byte(&wanted_flags_buf, ' ');
1092*592efe25SPierre Pronchery 
1093*592efe25SPierre Pronchery 			pkgconf_buffer_append(&wanted_flags_buf, pair->name);
1094*592efe25SPierre Pronchery 		}
1095*592efe25SPierre Pronchery 	}
1096*592efe25SPierre Pronchery 
1097*592efe25SPierre Pronchery 	pkgconf_buffer_t env_buf = PKGCONF_BUFFER_INITIALIZER;
1098*592efe25SPierre Pronchery 
1099*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->env_vars.head, iter)
1100*592efe25SPierre Pronchery 	{
1101*592efe25SPierre Pronchery 		const pkgconf_test_environ_t *env = iter->data;
1102*592efe25SPierre Pronchery 
1103*592efe25SPierre Pronchery 		if (pkgconf_buffer_len(&env_buf))
1104*592efe25SPierre Pronchery 			pkgconf_buffer_append(&env_buf, "\n  ");
1105*592efe25SPierre Pronchery 
1106*592efe25SPierre Pronchery 		pkgconf_buffer_append_fmt(&env_buf, "%s: %s", env->key, env->value);
1107*592efe25SPierre Pronchery 	}
1108*592efe25SPierre Pronchery 
1109*592efe25SPierre Pronchery 	fprintf(stderr,
1110*592efe25SPierre Pronchery 		"--------------------------------------------------------------------------------\n"
1111*592efe25SPierre Pronchery 		"search-path: <%s>\n"
1112*592efe25SPierre Pronchery 		"wanted-flags: <%s>\n"
1113*592efe25SPierre Pronchery 		"environment:\n"
1114*592efe25SPierre Pronchery 		"  %s\n"
1115*592efe25SPierre Pronchery 		"query: [%s]\n"
1116*592efe25SPierre Pronchery 		"exit-code: %d\n"
1117*592efe25SPierre Pronchery 		"verbosity: %d\n",
1118*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&search_path_buf),
1119*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&wanted_flags_buf),
1120*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&env_buf),
1121*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&testcase->query),
1122*592efe25SPierre Pronchery 		ret,
1123*592efe25SPierre Pronchery 		testcase->verbosity);
1124*592efe25SPierre Pronchery 
1125*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&testcase->tool))
1126*592efe25SPierre Pronchery 		fprintf(stderr, "tool: [%s] tool-args: [%s]\n",
1127*592efe25SPierre Pronchery 			pkgconf_buffer_str_or_empty(&testcase->tool),
1128*592efe25SPierre Pronchery 			pkgconf_buffer_str_or_empty(&testcase->tool_args));
1129*592efe25SPierre Pronchery 
1130*592efe25SPierre Pronchery 	fprintf(stderr, "stdout: [%s]\n",
1131*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&out->o_stdout));
1132*592efe25SPierre Pronchery 
1133*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->expected_stdout.head, iter)
1134*592efe25SPierre Pronchery 	{
1135*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
1136*592efe25SPierre Pronchery 
1137*592efe25SPierre Pronchery 		fprintf(stderr,
1138*592efe25SPierre Pronchery 			"expected-stdout: [%s] (%s)\n",
1139*592efe25SPierre Pronchery 			pkgconf_buffer_str_or_empty(&set->buffer),
1140*592efe25SPierre Pronchery 			testcase->match_stdout == MATCH_PARTIAL ? "partial" : "exact");
1141*592efe25SPierre Pronchery 	}
1142*592efe25SPierre Pronchery 
1143*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&testcase->expected_stdout_file))
1144*592efe25SPierre Pronchery 		fprintf(stderr, "expected-stdout-file: [%s]\n",
1145*592efe25SPierre Pronchery 			pkgconf_buffer_str_or_empty(&testcase->expected_stdout_file));
1146*592efe25SPierre Pronchery 
1147*592efe25SPierre Pronchery 	fprintf(stderr, "stderr: [%s]\n",
1148*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&out->o_stderr));
1149*592efe25SPierre Pronchery 
1150*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->expected_stderr.head, iter)
1151*592efe25SPierre Pronchery 	{
1152*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
1153*592efe25SPierre Pronchery 
1154*592efe25SPierre Pronchery 		fprintf(stderr,
1155*592efe25SPierre Pronchery 			"expected-stderr: [%s] (%s)\n",
1156*592efe25SPierre Pronchery 			pkgconf_buffer_str_or_empty(&set->buffer),
1157*592efe25SPierre Pronchery 			testcase->match_stderr == MATCH_PARTIAL ? "partial" : "exact");
1158*592efe25SPierre Pronchery 	}
1159*592efe25SPierre Pronchery 
1160*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->define_variables.head, iter)
1161*592efe25SPierre Pronchery 	{
1162*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
1163*592efe25SPierre Pronchery 		fprintf(stderr, "define-variable: [%s]\n", pkgconf_buffer_str_or_empty(&set->buffer));
1164*592efe25SPierre Pronchery 	}
1165*592efe25SPierre Pronchery 
1166*592efe25SPierre Pronchery 	fprintf(stderr,
1167*592efe25SPierre Pronchery 		"want-env-prefix: [%s]\n"
1168*592efe25SPierre Pronchery 		"fragment-filter: [%s]\n"
1169*592efe25SPierre Pronchery 		"--------------------------------------------------------------------------------\n",
1170*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&testcase->want_env_prefix),
1171*592efe25SPierre Pronchery 		pkgconf_buffer_str_or_empty(&testcase->fragment_filter));
1172*592efe25SPierre Pronchery 
1173*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&search_path_buf);
1174*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&wanted_flags_buf);
1175*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&env_buf);
1176*592efe25SPierre Pronchery }
1177*592efe25SPierre Pronchery 
1178*592efe25SPierre Pronchery static bool
run_test_case(const pkgconf_test_case_t * testcase)1179*592efe25SPierre Pronchery run_test_case(const pkgconf_test_case_t *testcase)
1180*592efe25SPierre Pronchery {
1181*592efe25SPierre Pronchery 	bool passed = true;
1182*592efe25SPierre Pronchery 
1183*592efe25SPierre Pronchery 	const pkgconf_buffer_t *our_platform = PKGCONF_BUFFER_FROM_STR(PKGCONF_TEST_PLATFORM);
1184*592efe25SPierre Pronchery 	if (pkgconf_buffer_contains(&testcase->skip_platforms, our_platform))
1185*592efe25SPierre Pronchery 	{
1186*592efe25SPierre Pronchery 		printf("# test skipped on %s\nSKIP: %s\n",
1187*592efe25SPierre Pronchery 			pkgconf_buffer_str(our_platform), testcase->name);
1188*592efe25SPierre Pronchery 		return true;
1189*592efe25SPierre Pronchery 	}
1190*592efe25SPierre Pronchery 
1191*592efe25SPierre Pronchery 	if (testcase->require_utf8_locale && !pkgconf_is_locale_utf8())
1192*592efe25SPierre Pronchery 	{
1193*592efe25SPierre Pronchery 		printf("# test skipped: requires a UTF-8 locale\nSKIP: %s\n", testcase->name);
1194*592efe25SPierre Pronchery 		return true;
1195*592efe25SPierre Pronchery 	}
1196*592efe25SPierre Pronchery 
1197*592efe25SPierre Pronchery 	// If the test has setup steps, create a new tmp_dir and chdir into it.
1198*592efe25SPierre Pronchery 	char original_cwd[PATH_MAX] = {0};
1199*592efe25SPierre Pronchery 	char *tmp_dir = NULL;
1200*592efe25SPierre Pronchery 
1201*592efe25SPierre Pronchery 	if (getcwd(original_cwd, sizeof(original_cwd)) == NULL)
1202*592efe25SPierre Pronchery 	{
1203*592efe25SPierre Pronchery 		fprintf(stderr, "FAIL: getcwd failed: %s\n", strerror(errno));
1204*592efe25SPierre Pronchery 		return false;
1205*592efe25SPierre Pronchery 	}
1206*592efe25SPierre Pronchery 
1207*592efe25SPierre Pronchery 	if (needs_tmp_dir(testcase))
1208*592efe25SPierre Pronchery 	{
1209*592efe25SPierre Pronchery 		pkgconf_buffer_t tmp_buf = PKGCONF_BUFFER_INITIALIZER;
1210*592efe25SPierre Pronchery 		pkgconf_buffer_append_fmt(&tmp_buf, "%s/pkgconf-test-XXXXXX", original_cwd);
1211*592efe25SPierre Pronchery 		tmp_dir = pkgconf_buffer_freeze(&tmp_buf);
1212*592efe25SPierre Pronchery 
1213*592efe25SPierre Pronchery 		if (mkdtemp(tmp_dir) == NULL)
1214*592efe25SPierre Pronchery 		{
1215*592efe25SPierre Pronchery 			fprintf(stderr, "FAIL: mkdtemp failed: %s\n", strerror(errno));
1216*592efe25SPierre Pronchery 			free(tmp_dir);
1217*592efe25SPierre Pronchery 			return false;
1218*592efe25SPierre Pronchery 		}
1219*592efe25SPierre Pronchery 
1220*592efe25SPierre Pronchery 		if (chdir(tmp_dir) != 0)
1221*592efe25SPierre Pronchery 		{
1222*592efe25SPierre Pronchery 			fprintf(stderr, "FAIL: chdir('%s') failed: %s\n", tmp_dir, strerror(errno));
1223*592efe25SPierre Pronchery 			rmdir(tmp_dir);
1224*592efe25SPierre Pronchery 			free(tmp_dir);
1225*592efe25SPierre Pronchery 			return false;
1226*592efe25SPierre Pronchery 		}
1227*592efe25SPierre Pronchery 
1228*592efe25SPierre Pronchery 		if (!run_setup(testcase, tmp_dir))
1229*592efe25SPierre Pronchery 		{
1230*592efe25SPierre Pronchery 			fprintf(stderr, "FAIL: %s (setup failed)\n", testcase->name);
1231*592efe25SPierre Pronchery 			chdir(original_cwd);
1232*592efe25SPierre Pronchery 			rmdir_recursive(tmp_dir);
1233*592efe25SPierre Pronchery 			free(tmp_dir);
1234*592efe25SPierre Pronchery 			return false;
1235*592efe25SPierre Pronchery 		}
1236*592efe25SPierre Pronchery 	}
1237*592efe25SPierre Pronchery 
1238*592efe25SPierre Pronchery 	pkgconf_test_output_t *out = (pkgconf_test_output_t *) test_output();
1239*592efe25SPierre Pronchery 	int ret;
1240*592efe25SPierre Pronchery 
1241*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&testcase->tool))
1242*592efe25SPierre Pronchery 	{
1243*592efe25SPierre Pronchery 		ret = run_tool(testcase, &out->o_stdout, &out->o_stderr);
1244*592efe25SPierre Pronchery 	}
1245*592efe25SPierre Pronchery 	else
1246*592efe25SPierre Pronchery 	{
1247*592efe25SPierre Pronchery 		pkgconf_cross_personality_t *personality = personality_for_test(testcase);
1248*592efe25SPierre Pronchery 		pkgconf_test_state_t state =
1249*592efe25SPierre Pronchery 		{
1250*592efe25SPierre Pronchery 			.cli_state.want_flags = testcase->wanted_flags,
1251*592efe25SPierre Pronchery 			.cli_state.want_env_prefix = pkgconf_buffer_str(&testcase->want_env_prefix),
1252*592efe25SPierre Pronchery 			.cli_state.want_variable = pkgconf_buffer_str(&testcase->want_variable),
1253*592efe25SPierre Pronchery 			.cli_state.want_fragment_filter = pkgconf_buffer_str(&testcase->fragment_filter),
1254*592efe25SPierre Pronchery 			.cli_state.required_module_version = pkgconf_buffer_str(&testcase->atleast_version),
1255*592efe25SPierre Pronchery 			.cli_state.required_exact_module_version = pkgconf_buffer_str(&testcase->exact_version),
1256*592efe25SPierre Pronchery 			.cli_state.required_max_module_version = pkgconf_buffer_str(&testcase->max_version),
1257*592efe25SPierre Pronchery 			.cli_state.verbosity = testcase->verbosity,
1258*592efe25SPierre Pronchery 			.testcase = testcase,
1259*592efe25SPierre Pronchery 		};
1260*592efe25SPierre Pronchery 
1261*592efe25SPierre Pronchery 		pkgconf_client_init(&state.cli_state.pkg_client, error_handler, NULL, personality, &state, environ_lookup_handler);
1262*592efe25SPierre Pronchery 		pkgconf_client_set_output(&state.cli_state.pkg_client, &out->output);
1263*592efe25SPierre Pronchery 
1264*592efe25SPierre Pronchery 		pkgconf_node_t *iter;
1265*592efe25SPierre Pronchery 		PKGCONF_FOREACH_LIST_ENTRY(testcase->define_variables.head, iter)
1266*592efe25SPierre Pronchery 		{
1267*592efe25SPierre Pronchery 			pkgconf_bufferset_t *set = iter->data;
1268*592efe25SPierre Pronchery 			pkgconf_tuple_define_global(&state.cli_state.pkg_client, pkgconf_buffer_str_or_empty(&set->buffer));
1269*592efe25SPierre Pronchery 		}
1270*592efe25SPierre Pronchery 
1271*592efe25SPierre Pronchery 		/*
1272*592efe25SPierre Pronchery 		 * Re-expand Query now that %PWD% is known (if we have a tmp_dir).
1273*592efe25SPierre Pronchery 		 * For tests without a tmp_dir this is a no-op since %PWD% won't appear.
1274*592efe25SPierre Pronchery 		 */
1275*592efe25SPierre Pronchery 		char query_cwd[PATH_MAX] = {0};
1276*592efe25SPierre Pronchery 		const char *query_pwd = getcwd(query_cwd, sizeof(query_cwd));
1277*592efe25SPierre Pronchery 
1278*592efe25SPierre Pronchery 		pkgconf_buffer_t query_expanded = PKGCONF_BUFFER_INITIALIZER;
1279*592efe25SPierre Pronchery 		handle_substs(&query_expanded, &testcase->query, query_pwd);
1280*592efe25SPierre Pronchery 
1281*592efe25SPierre Pronchery 		pkgconf_buffer_t arg_buf = PKGCONF_BUFFER_INITIALIZER;
1282*592efe25SPierre Pronchery 		int test_argc = 0;
1283*592efe25SPierre Pronchery 		char **test_argv = NULL;
1284*592efe25SPierre Pronchery 
1285*592efe25SPierre Pronchery 		if (pkgconf_buffer_len(&query_expanded))
1286*592efe25SPierre Pronchery 			pkgconf_buffer_append_fmt(&arg_buf, "pkgconf %s", pkgconf_buffer_str(&query_expanded));
1287*592efe25SPierre Pronchery 		else
1288*592efe25SPierre Pronchery 			pkgconf_buffer_append(&arg_buf, "pkgconf");
1289*592efe25SPierre Pronchery 
1290*592efe25SPierre Pronchery 		pkgconf_argv_split(pkgconf_buffer_str(&arg_buf), &test_argc, &test_argv);
1291*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&arg_buf);
1292*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&query_expanded);
1293*592efe25SPierre Pronchery 
1294*592efe25SPierre Pronchery 		pkgconf_client_set_warn_handler(&state.cli_state.pkg_client, error_handler, NULL);
1295*592efe25SPierre Pronchery 
1296*592efe25SPierre Pronchery #ifndef PKGCONF_LITE
1297*592efe25SPierre Pronchery 		if (debug)
1298*592efe25SPierre Pronchery 			pkgconf_client_set_trace_handler(&state.cli_state.pkg_client, debug_handler, NULL);
1299*592efe25SPierre Pronchery #endif // PKGCONF_LITE
1300*592efe25SPierre Pronchery 
1301*592efe25SPierre Pronchery 		ret = pkgconf_cli_run(&state.cli_state, test_argc, test_argv, 1);
1302*592efe25SPierre Pronchery 		pkgconf_argv_free(test_argv);
1303*592efe25SPierre Pronchery 	}
1304*592efe25SPierre Pronchery 
1305*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&out->o_stdout))
1306*592efe25SPierre Pronchery 		pkgconf_buffer_trim_byte(&out->o_stdout);
1307*592efe25SPierre Pronchery 
1308*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&out->o_stderr))
1309*592efe25SPierre Pronchery 		pkgconf_buffer_trim_byte(&out->o_stderr);
1310*592efe25SPierre Pronchery 
1311*592efe25SPierre Pronchery 	pkgconf_node_t *iter;
1312*592efe25SPierre Pronchery 
1313*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->expected_stdout.head, iter)
1314*592efe25SPierre Pronchery 	{
1315*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
1316*592efe25SPierre Pronchery 
1317*592efe25SPierre Pronchery 		char expected_cwd[PATH_MAX] = {0};
1318*592efe25SPierre Pronchery 		const char *expected_pwd = getcwd(expected_cwd, sizeof(expected_cwd));
1319*592efe25SPierre Pronchery 
1320*592efe25SPierre Pronchery 		pkgconf_buffer_t expected_expanded = PKGCONF_BUFFER_INITIALIZER;
1321*592efe25SPierre Pronchery 		handle_substs(&expected_expanded, &set->buffer, expected_pwd);
1322*592efe25SPierre Pronchery 
1323*592efe25SPierre Pronchery 		if (!test_match_buffer(testcase->match_stdout, &expected_expanded, &out->o_stdout, "stdout"))
1324*592efe25SPierre Pronchery 			passed = false;
1325*592efe25SPierre Pronchery 
1326*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&expected_expanded);
1327*592efe25SPierre Pronchery 	}
1328*592efe25SPierre Pronchery 
1329*592efe25SPierre Pronchery 	// ExpectedStdoutFile: load file relative to the .test file's directory
1330*592efe25SPierre Pronchery 	if (pkgconf_buffer_len(&testcase->expected_stdout_file))
1331*592efe25SPierre Pronchery 	{
1332*592efe25SPierre Pronchery 		pkgconf_buffer_t filepath = PKGCONF_BUFFER_INITIALIZER;
1333*592efe25SPierre Pronchery 		pkgconf_buffer_append(&filepath, testcase->testfile_dir);
1334*592efe25SPierre Pronchery 		pkgconf_buffer_push_byte(&filepath, '/');
1335*592efe25SPierre Pronchery 		pkgconf_buffer_append(&filepath, pkgconf_buffer_str(&testcase->expected_stdout_file));
1336*592efe25SPierre Pronchery 
1337*592efe25SPierre Pronchery 		pkgconf_buffer_t file_contents = PKGCONF_BUFFER_INITIALIZER;
1338*592efe25SPierre Pronchery 		if (!open_file_into_buffer(pkgconf_buffer_str(&filepath), &file_contents))
1339*592efe25SPierre Pronchery 		{
1340*592efe25SPierre Pronchery 			fprintf(stderr, "ExpectedStdoutFile: failed to open '%s': %s\n", pkgconf_buffer_str(&filepath), strerror(errno));
1341*592efe25SPierre Pronchery 			passed = false;
1342*592efe25SPierre Pronchery 		}
1343*592efe25SPierre Pronchery 		else
1344*592efe25SPierre Pronchery 		{
1345*592efe25SPierre Pronchery 			if (pkgconf_buffer_len(&file_contents))
1346*592efe25SPierre Pronchery 				pkgconf_buffer_trim_byte(&file_contents);
1347*592efe25SPierre Pronchery 
1348*592efe25SPierre Pronchery 			if (!test_match_buffer(testcase->match_stdout, &file_contents, &out->o_stdout, "stdout (file)"))
1349*592efe25SPierre Pronchery 				passed = false;
1350*592efe25SPierre Pronchery 		}
1351*592efe25SPierre Pronchery 
1352*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&file_contents);
1353*592efe25SPierre Pronchery 		pkgconf_buffer_finalize(&filepath);
1354*592efe25SPierre Pronchery 	}
1355*592efe25SPierre Pronchery 
1356*592efe25SPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(testcase->expected_stderr.head, iter)
1357*592efe25SPierre Pronchery 	{
1358*592efe25SPierre Pronchery 		pkgconf_bufferset_t *set = iter->data;
1359*592efe25SPierre Pronchery 
1360*592efe25SPierre Pronchery 		if (!test_match_buffer(testcase->match_stderr, &set->buffer, &out->o_stderr, "stderr"))
1361*592efe25SPierre Pronchery 			passed = false;
1362*592efe25SPierre Pronchery 	}
1363*592efe25SPierre Pronchery 
1364*592efe25SPierre Pronchery 	if (ret != testcase->exitcode)
1365*592efe25SPierre Pronchery 	{
1366*592efe25SPierre Pronchery 		fprintf(stderr, "exitcode %d does not match expected %d\n", ret, testcase->exitcode);
1367*592efe25SPierre Pronchery 		passed = false;
1368*592efe25SPierre Pronchery 	}
1369*592efe25SPierre Pronchery 
1370*592efe25SPierre Pronchery 	printf("%s: %s\n", passed ? "PASS" : "FAIL", testcase->name);
1371*592efe25SPierre Pronchery 
1372*592efe25SPierre Pronchery 	if (!passed)
1373*592efe25SPierre Pronchery 		annotate_result(testcase, ret, out);
1374*592efe25SPierre Pronchery 
1375*592efe25SPierre Pronchery 	test_output_reset(out);
1376*592efe25SPierre Pronchery 
1377*592efe25SPierre Pronchery 	// Restore cwd and clean up tmp_dir if we created one
1378*592efe25SPierre Pronchery 	if (tmp_dir && strcmp(tmp_dir, original_cwd) != 0)
1379*592efe25SPierre Pronchery 	{
1380*592efe25SPierre Pronchery 		chdir(original_cwd);
1381*592efe25SPierre Pronchery 		rmdir_recursive(tmp_dir);
1382*592efe25SPierre Pronchery 	}
1383*592efe25SPierre Pronchery 
1384*592efe25SPierre Pronchery 	free(tmp_dir);
1385*592efe25SPierre Pronchery 	return passed;
1386*592efe25SPierre Pronchery }
1387*592efe25SPierre Pronchery 
1388*592efe25SPierre Pronchery static void
free_test_case(pkgconf_test_case_t * testcase)1389*592efe25SPierre Pronchery free_test_case(pkgconf_test_case_t *testcase)
1390*592efe25SPierre Pronchery {
1391*592efe25SPierre Pronchery 	pkgconf_bufferset_free(&testcase->define_variables);
1392*592efe25SPierre Pronchery 	pkgconf_bufferset_free(&testcase->expected_stderr);
1393*592efe25SPierre Pronchery 	pkgconf_bufferset_free(&testcase->expected_stdout);
1394*592efe25SPierre Pronchery 	pkgconf_bufferset_free(&testcase->mkdirs);
1395*592efe25SPierre Pronchery #ifndef _WIN32
1396*592efe25SPierre Pronchery 	pkgconf_bufferset_free(&testcase->symlinks);
1397*592efe25SPierre Pronchery #endif // _WIN32
1398*592efe25SPierre Pronchery 	pkgconf_bufferset_free(&testcase->copies);
1399*592efe25SPierre Pronchery 
1400*592efe25SPierre Pronchery 	test_environment_free(&testcase->env_vars);
1401*592efe25SPierre Pronchery 	pkgconf_path_free(&testcase->search_path);
1402*592efe25SPierre Pronchery 
1403*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->query);
1404*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->want_env_prefix);
1405*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->want_variable);
1406*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->fragment_filter);
1407*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->skip_platforms);
1408*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->atleast_version);
1409*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->exact_version);
1410*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->max_version);
1411*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->expected_stdout_file);
1412*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->tool);
1413*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->tool_args);
1414*592efe25SPierre Pronchery 
1415*592efe25SPierre Pronchery #ifndef PKGCONF_LITE
1416*592efe25SPierre Pronchery 	pkgconf_buffer_finalize(&testcase->want_personality);
1417*592efe25SPierre Pronchery #endif
1418*592efe25SPierre Pronchery 
1419*592efe25SPierre Pronchery 	free(testcase->name);
1420*592efe25SPierre Pronchery 	free(testcase->testfile_dir);
1421*592efe25SPierre Pronchery 	free(testcase);
1422*592efe25SPierre Pronchery }
1423*592efe25SPierre Pronchery 
1424*592efe25SPierre Pronchery static bool
process_test_case(char * testcase_file)1425*592efe25SPierre Pronchery process_test_case(char *testcase_file)
1426*592efe25SPierre Pronchery {
1427*592efe25SPierre Pronchery 	pkgconf_test_case_t *testcase = load_test_case(testcase_file);
1428*592efe25SPierre Pronchery 	bool ret;
1429*592efe25SPierre Pronchery 
1430*592efe25SPierre Pronchery 	if (testcase == NULL)
1431*592efe25SPierre Pronchery 	{
1432*592efe25SPierre Pronchery 		fprintf(stderr, "test %s failed to load\n", testcase_file);
1433*592efe25SPierre Pronchery 		return false;
1434*592efe25SPierre Pronchery 	}
1435*592efe25SPierre Pronchery 
1436*592efe25SPierre Pronchery 	ret = run_test_case(testcase);
1437*592efe25SPierre Pronchery 	free_test_case(testcase);
1438*592efe25SPierre Pronchery 
1439*592efe25SPierre Pronchery 	return ret;
1440*592efe25SPierre Pronchery }
1441*592efe25SPierre Pronchery 
1442*592efe25SPierre Pronchery static inline bool
str_has_suffix(const char * str,const char * suffix)1443*592efe25SPierre Pronchery str_has_suffix(const char *str, const char *suffix)
1444*592efe25SPierre Pronchery {
1445*592efe25SPierre Pronchery 	size_t str_len = strlen(str);
1446*592efe25SPierre Pronchery 	size_t suf_len = strlen(suffix);
1447*592efe25SPierre Pronchery 
1448*592efe25SPierre Pronchery 	if (str_len < suf_len)
1449*592efe25SPierre Pronchery 		return false;
1450*592efe25SPierre Pronchery 
1451*592efe25SPierre Pronchery 	return !strncasecmp(str + str_len - suf_len, suffix, suf_len);
1452*592efe25SPierre Pronchery }
1453*592efe25SPierre Pronchery 
1454*592efe25SPierre Pronchery static int
path_sort_cmp(const void * a,const void * b)1455*592efe25SPierre Pronchery path_sort_cmp(const void *a, const void *b)
1456*592efe25SPierre Pronchery {
1457*592efe25SPierre Pronchery 	return strcmp(*(const char **) a, *(const char **) b);
1458*592efe25SPierre Pronchery }
1459*592efe25SPierre Pronchery 
1460*592efe25SPierre Pronchery static bool
process_test_directory(char * dirpath)1461*592efe25SPierre Pronchery process_test_directory(char *dirpath)
1462*592efe25SPierre Pronchery {
1463*592efe25SPierre Pronchery 	bool ret = true;
1464*592efe25SPierre Pronchery 	DIR *dir = opendir(dirpath);
1465*592efe25SPierre Pronchery 	if (dir == NULL)
1466*592efe25SPierre Pronchery 	{
1467*592efe25SPierre Pronchery 		fprintf(stderr, "failed to open test directory %s\n", dirpath);
1468*592efe25SPierre Pronchery 		return false;
1469*592efe25SPierre Pronchery 	}
1470*592efe25SPierre Pronchery 	char **paths = NULL;
1471*592efe25SPierre Pronchery 	size_t numpaths = 0;
1472*592efe25SPierre Pronchery 
1473*592efe25SPierre Pronchery 	struct dirent *dirent;
1474*592efe25SPierre Pronchery 	for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir))
1475*592efe25SPierre Pronchery 	{
1476*592efe25SPierre Pronchery 		pkgconf_buffer_t pathbuf = PKGCONF_BUFFER_INITIALIZER;
1477*592efe25SPierre Pronchery 
1478*592efe25SPierre Pronchery 		pkgconf_buffer_append(&pathbuf, dirpath);
1479*592efe25SPierre Pronchery 		pkgconf_buffer_push_byte(&pathbuf, '/');
1480*592efe25SPierre Pronchery 		pkgconf_buffer_append(&pathbuf, dirent->d_name);
1481*592efe25SPierre Pronchery 
1482*592efe25SPierre Pronchery 		char *pathstr = pkgconf_buffer_freeze(&pathbuf);
1483*592efe25SPierre Pronchery 		if (pathstr == NULL)
1484*592efe25SPierre Pronchery 			continue;
1485*592efe25SPierre Pronchery 
1486*592efe25SPierre Pronchery 		if (!str_has_suffix(pathstr, ".test"))
1487*592efe25SPierre Pronchery 		{
1488*592efe25SPierre Pronchery 			free(pathstr);
1489*592efe25SPierre Pronchery 			continue;
1490*592efe25SPierre Pronchery 		}
1491*592efe25SPierre Pronchery 
1492*592efe25SPierre Pronchery 		paths = pkgconf_reallocarray(paths, ++numpaths, sizeof(void *));
1493*592efe25SPierre Pronchery 		paths[numpaths - 1] = pathstr;
1494*592efe25SPierre Pronchery 	}
1495*592efe25SPierre Pronchery 
1496*592efe25SPierre Pronchery 	qsort(paths, numpaths, sizeof(void *), path_sort_cmp);
1497*592efe25SPierre Pronchery 
1498*592efe25SPierre Pronchery 	for (size_t i = 0; i < numpaths; i++)
1499*592efe25SPierre Pronchery 	{
1500*592efe25SPierre Pronchery 		char *pathstr = paths[i];
1501*592efe25SPierre Pronchery 
1502*592efe25SPierre Pronchery 		ret = process_test_case(pathstr);
1503*592efe25SPierre Pronchery 		if (!ret)
1504*592efe25SPierre Pronchery 			break;
1505*592efe25SPierre Pronchery 	}
1506*592efe25SPierre Pronchery 
1507*592efe25SPierre Pronchery 	for (size_t i = 0; i < numpaths; i++)
1508*592efe25SPierre Pronchery 		free(paths[i]);
1509*592efe25SPierre Pronchery 
1510*592efe25SPierre Pronchery 	free(paths);
1511*592efe25SPierre Pronchery 	closedir(dir);
1512*592efe25SPierre Pronchery 	return ret;
1513*592efe25SPierre Pronchery }
1514*592efe25SPierre Pronchery 
1515*592efe25SPierre Pronchery static void
usage(void)1516*592efe25SPierre Pronchery usage(void)
1517*592efe25SPierre Pronchery {
1518*592efe25SPierre Pronchery 	fprintf(stderr, "usage: test-runner --test-fixtures <path-to-fixtures> [--tool-dir <path>] <path-to-tests>\n");
1519*592efe25SPierre Pronchery 	exit(EXIT_FAILURE);
1520*592efe25SPierre Pronchery }
1521*592efe25SPierre Pronchery 
1522*592efe25SPierre Pronchery int
main(int argc,char * argv[])1523*592efe25SPierre Pronchery main(int argc, char *argv[])
1524*592efe25SPierre Pronchery {
1525*592efe25SPierre Pronchery 	int ret;
1526*592efe25SPierre Pronchery 	char *test_fixtures_dir_arg = NULL;
1527*592efe25SPierre Pronchery 	char *test_tool_dir_arg = NULL;
1528*592efe25SPierre Pronchery 
1529*592efe25SPierre Pronchery 	struct pkg_option options[] =
1530*592efe25SPierre Pronchery 	{
1531*592efe25SPierre Pronchery 		{"test-fixtures",	required_argument,	NULL,	1},
1532*592efe25SPierre Pronchery 		{"debug",		no_argument,		NULL,	2},
1533*592efe25SPierre Pronchery 		{"test-case",		required_argument,	NULL,	3},
1534*592efe25SPierre Pronchery 		{"tool-dir",		required_argument,	NULL,	4},
1535*592efe25SPierre Pronchery 		{NULL,			0,			NULL,	0},
1536*592efe25SPierre Pronchery 	};
1537*592efe25SPierre Pronchery 	char *testcase = NULL;
1538*592efe25SPierre Pronchery 
1539*592efe25SPierre Pronchery 	while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1)
1540*592efe25SPierre Pronchery 	{
1541*592efe25SPierre Pronchery 		switch (ret)
1542*592efe25SPierre Pronchery 		{
1543*592efe25SPierre Pronchery 		case 1:
1544*592efe25SPierre Pronchery 			test_fixtures_dir_arg = pkg_optarg;
1545*592efe25SPierre Pronchery 			break;
1546*592efe25SPierre Pronchery 		case 2:
1547*592efe25SPierre Pronchery 			debug = true;
1548*592efe25SPierre Pronchery 			break;
1549*592efe25SPierre Pronchery 		case 3:
1550*592efe25SPierre Pronchery 			testcase = pkg_optarg;
1551*592efe25SPierre Pronchery 			break;
1552*592efe25SPierre Pronchery 		case 4:
1553*592efe25SPierre Pronchery 			test_tool_dir_arg = pkg_optarg;
1554*592efe25SPierre Pronchery 			break;
1555*592efe25SPierre Pronchery 		}
1556*592efe25SPierre Pronchery 	}
1557*592efe25SPierre Pronchery 
1558*592efe25SPierre Pronchery 	if (test_fixtures_dir_arg == NULL)
1559*592efe25SPierre Pronchery 		usage();
1560*592efe25SPierre Pronchery 
1561*592efe25SPierre Pronchery 	{
1562*592efe25SPierre Pronchery 		char test_fixtures_dir_abs[PATH_MAX] = {0};
1563*592efe25SPierre Pronchery 		if (!realpath(test_fixtures_dir_arg, test_fixtures_dir_abs))
1564*592efe25SPierre Pronchery 		{
1565*592efe25SPierre Pronchery 			fprintf(stderr, "realpath failed: %s\n", strerror(errno));
1566*592efe25SPierre Pronchery 			return EXIT_FAILURE;
1567*592efe25SPierre Pronchery 		}
1568*592efe25SPierre Pronchery 		const pkgconf_buffer_t *test_fixtures_dir_arg_buf = PKGCONF_BUFFER_FROM_STR_NONNULL(test_fixtures_dir_abs);
1569*592efe25SPierre Pronchery 		pkgconf_buffer_subst(&test_fixtures_dir, test_fixtures_dir_arg_buf, "\\", "/");
1570*592efe25SPierre Pronchery 	}
1571*592efe25SPierre Pronchery 
1572*592efe25SPierre Pronchery 	if (test_tool_dir_arg != NULL)
1573*592efe25SPierre Pronchery 	{
1574*592efe25SPierre Pronchery 		const pkgconf_buffer_t *test_tool_dir_arg_buf = PKGCONF_BUFFER_FROM_STR(test_tool_dir_arg);
1575*592efe25SPierre Pronchery 		pkgconf_buffer_subst(&test_tool_dir, test_tool_dir_arg_buf, "\\", "/");
1576*592efe25SPierre Pronchery 	}
1577*592efe25SPierre Pronchery 
1578*592efe25SPierre Pronchery 	if (testcase != NULL)
1579*592efe25SPierre Pronchery 		return process_test_case(testcase) ? EXIT_SUCCESS : EXIT_FAILURE;
1580*592efe25SPierre Pronchery 
1581*592efe25SPierre Pronchery 	if (argv[pkg_optind] == NULL)
1582*592efe25SPierre Pronchery 		usage();
1583*592efe25SPierre Pronchery 
1584*592efe25SPierre Pronchery 	return process_test_directory(argv[pkg_optind]) ? EXIT_SUCCESS : EXIT_FAILURE;
1585*592efe25SPierre Pronchery }
1586