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