• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 26127302554

19 May 2026 07:34PM UTC coverage: 92.635% (-0.4%) from 93.024%
26127302554

push

github

Castaglia
Make a flaky, racy mod_copy regression test more reliable.

47303 of 51064 relevant lines covered (92.63%)

241.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.78
/tests/api/parser.c
1
/*
2
 * ProFTPD - FTP server testsuite
3
 * Copyright (c) 2014-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* Parser API tests. */
25

26
#include "tests.h"
27

28
static pool *p = NULL;
29

30
static const char *config_path = "/tmp/prt-parser.conf";
31
static const char *config_path2 = "/tmp/prt-parser2.conf";
32
static const char *config_path3 = "/tmp/prt-parser3.d";
33
static const char *config_tmp_path = "/tmp/prt-parser.conf~";
34

35
static void set_up(void) {
11✔
36
  (void) unlink(config_path);
11✔
37
  (void) unlink(config_path2);
11✔
38
  (void) rmdir(config_path3);
11✔
39
  (void) unlink(config_tmp_path);
11✔
40

41
  if (p == NULL) {
11✔
42
    p = permanent_pool = make_sub_pool(NULL);
11✔
43
  }
44

45
  init_fs();
11✔
46
  pr_fs_statcache_set_policy(PR_TUNABLE_FS_STATCACHE_SIZE,
11✔
47
    PR_TUNABLE_FS_STATCACHE_MAX_AGE, 0);
48
  init_stash();
11✔
49
  modules_init();
11✔
50

51
  if (getenv("TEST_VERBOSE") != NULL) {
11✔
52
    pr_trace_set_levels("config", 1, 20);
11✔
53
  }
54
}
11✔
55

56
static void tear_down(void) {
11✔
57
  (void) pr_parser_cleanup();
11✔
58

59
  (void) unlink(config_path);
11✔
60
  (void) unlink(config_path2);
11✔
61
  (void) rmdir(config_path3);
11✔
62
  (void) unlink(config_tmp_path);
11✔
63

64
  if (getenv("TEST_VERBOSE") != NULL) {
11✔
65
    pr_trace_set_levels("config", 0, 0);
11✔
66
  }
67

68
  if (p) {
11✔
69
    destroy_pool(p);
11✔
70
    p = permanent_pool = NULL;
11✔
71
  }
72
}
11✔
73

74
/* Tests */
75

76
START_TEST (parser_prepare_test) {
1✔
77
  int res;
78
  xaset_t *parsed_servers = NULL;
1✔
79

80
  res = pr_parser_prepare(NULL, NULL);
1✔
81
  ck_assert_msg(res == 0, "Failed to handle null arguments: %s", strerror(errno));
1✔
82

83
  res = pr_parser_prepare(p, NULL);
1✔
84
  ck_assert_msg(res == 0, "Failed to handle null parsed_servers: %s",
2✔
85
    strerror(errno));
86

87
  res = pr_parser_prepare(NULL, &parsed_servers);
1✔
88
  ck_assert_msg(res == 0, "Failed to handle null pool: %s", strerror(errno));
2✔
89

90
  (void) pr_parser_cleanup();
1✔
91
}
92
END_TEST
1✔
93

94
START_TEST (parser_cleanup_test) {
1✔
95
  int res;
96
  server_rec *ctx;
97

98
  mark_point();
1✔
99
  res = pr_parser_cleanup();
1✔
100
  ck_assert_msg(res == 0, "Failed to handle unprepared parser: %s",
1✔
101
    strerror(errno));
102

103
  pr_parser_prepare(NULL, NULL);
1✔
104

105
  mark_point();
1✔
106
  ctx = pr_parser_server_ctxt_open("127.0.0.1");
1✔
107
  ck_assert_msg(ctx != NULL, "Failed to open server context: %s",
2✔
108
    strerror(errno));
109

110
  mark_point();
1✔
111
  res = pr_parser_cleanup();
1✔
112
  ck_assert_msg(res < 0, "Failed to handle existing contexts");
2✔
113
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
114
    strerror(errno), errno);
115

116
  mark_point();
1✔
117
  (void) pr_parser_server_ctxt_close();
1✔
118
  res = pr_parser_cleanup();
1✔
119
  ck_assert_msg(res == 0, "Failed to cleanup parser: %s", strerror(errno));
2✔
120
}
121
END_TEST
1✔
122

123
START_TEST (parser_server_ctxt_test) {
1✔
124
  server_rec *ctx, *res;
125

126
  ctx = pr_parser_server_ctxt_get();
1✔
127
  ck_assert_msg(ctx == NULL, "Found server context unexpectedly");
1✔
128
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
129
    strerror(errno), errno);
130

131
  pr_parser_prepare(p, NULL);
1✔
132

133
  mark_point();
1✔
134
  res = pr_parser_server_ctxt_close();
1✔
135
  ck_assert_msg(res == NULL, "Closed server context unexpectedly");
2✔
136
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
137
    strerror(errno), errno);
138

139
  mark_point();
1✔
140
  res = pr_parser_server_ctxt_open("127.0.0.1");
1✔
141
  ck_assert_msg(res != NULL, "Failed to open server context: %s",
2✔
142
    strerror(errno));
143

144
  mark_point();
1✔
145
  ctx = pr_parser_server_ctxt_get();
1✔
146
  ck_assert_msg(ctx != NULL, "Failed to get current server context: %s",
2✔
147
    strerror(errno));
148
  ck_assert_msg(ctx == res, "Expected server context %p, got %p", res, ctx);
2✔
149

150
  mark_point();
1✔
151
  (void) pr_parser_server_ctxt_close();
1✔
152
  (void) pr_parser_cleanup();
1✔
153
}
154
END_TEST
1✔
155

156
START_TEST (parser_server_ctxt_push_test) {
1✔
157
  int res;
158
  server_rec *ctx, *ctx2;
159

160
  res = pr_parser_server_ctxt_push(NULL);
1✔
161
  ck_assert_msg(res < 0, "Failed to handle null argument");
1✔
162
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
163
    strerror(errno), errno);
164

165
  ctx = pcalloc(p, sizeof(server_rec));
1✔
166

167
  mark_point();
1✔
168
  res = pr_parser_server_ctxt_push(ctx);
1✔
169
  ck_assert_msg(res < 0, "Failed to handle unprepared parser");
2✔
170
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
171
    strerror(errno), errno);
172

173
  pr_parser_prepare(p, NULL);
1✔
174

175
  mark_point();
1✔
176
  res = pr_parser_server_ctxt_push(ctx);
1✔
177
  ck_assert_msg(res == 0, "Failed to push server rec: %s", strerror(errno));
2✔
178

179
  mark_point();
1✔
180
  ctx2 = pr_parser_server_ctxt_get();
1✔
181
  ck_assert_msg(ctx2 != NULL, "Failed to get current server context: %s",
2✔
182
    strerror(errno));
183
  ck_assert_msg(ctx2 == ctx, "Expected server context %p, got %p", ctx, ctx2);
2✔
184

185
  (void) pr_parser_server_ctxt_close();
1✔
186
  (void) pr_parser_cleanup();
1✔
187
}
188
END_TEST
1✔
189

190
START_TEST (parser_config_ctxt_test) {
1✔
191
  int is_empty = FALSE;
1✔
192
  config_rec *ctx, *res;
193

194
  ctx = pr_parser_config_ctxt_get();
1✔
195
  ck_assert_msg(ctx == NULL, "Found config context unexpectedly");
1✔
196
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
197
    strerror(errno), errno);
198

199
  pr_parser_prepare(p, NULL);
1✔
200
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
201

202
  res = pr_parser_config_ctxt_open(NULL);
1✔
203
  ck_assert_msg(res == NULL, "Failed to handle null arguments");
2✔
204
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
205
    strerror(errno), errno);
206

207
  mark_point();
1✔
208
  res = pr_parser_config_ctxt_open("<TestSuite>");
1✔
209
  ck_assert_msg(res != NULL, "Failed to open config context: %s",
2✔
210
    strerror(errno));
211

212
  mark_point();
1✔
213
  ctx = pr_parser_config_ctxt_get();
1✔
214
  ck_assert_msg(ctx != NULL, "Failed to get current config context: %s",
2✔
215
    strerror(errno));
216
  ck_assert_msg(ctx == res, "Expected config context %p, got %p", res, ctx);
2✔
217

218
  mark_point();
1✔
219
  (void) pr_parser_config_ctxt_close(&is_empty);
1✔
220
  ck_assert_msg(is_empty == TRUE, "Expected config context to be empty");
2✔
221

222
  mark_point();
1✔
223
  res = pr_parser_config_ctxt_open("<Global>");
1✔
224
  ck_assert_msg(res != NULL, "Failed to open config context: %s",
2✔
225
    strerror(errno));
226
  (void) pr_parser_config_ctxt_close(&is_empty);
1✔
227
  ck_assert_msg(is_empty == TRUE, "Expected config context to be empty");
2✔
228

229
  (void) pr_parser_server_ctxt_close();
1✔
230
  (void) pr_parser_cleanup();
1✔
231
}
232
END_TEST
1✔
233

234
START_TEST (parser_config_ctxt_push_test) {
1✔
235
  int res;
236
  config_rec *ctx, *ctx2;
237

238
  res = pr_parser_config_ctxt_push(NULL);
1✔
239
  ck_assert_msg(res < 0, "Failed to handle null argument");
1✔
240
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
241
    strerror(errno), errno);
242

243
  ctx = pcalloc(p, sizeof(config_rec));
1✔
244

245
  mark_point();
1✔
246
  res = pr_parser_config_ctxt_push(ctx);
1✔
247
  ck_assert_msg(res < 0, "Failed to handle unprepared parser");
2✔
248
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
249
    strerror(errno), errno);
250

251
  pr_parser_prepare(p, NULL);
1✔
252

253
  mark_point();
1✔
254
  res = pr_parser_config_ctxt_push(ctx);
1✔
255
  ck_assert_msg(res == 0, "Failed to push config rec: %s", strerror(errno));
2✔
256

257
  mark_point();
1✔
258
  ctx2 = pr_parser_config_ctxt_get();
1✔
259
  ck_assert_msg(ctx2 != NULL, "Failed to get current config context: %s",
2✔
260
    strerror(errno));
261
  ck_assert_msg(ctx2 == ctx, "Expected config context %p, got %p", ctx, ctx2);
2✔
262

263
  (void) pr_parser_config_ctxt_close(NULL);
1✔
264
  (void) pr_parser_cleanup();
1✔
265
}
266
END_TEST
1✔
267

268
START_TEST (parser_get_lineno_test) {
1✔
269
  unsigned int res;
270

271
  res = pr_parser_get_lineno();
1✔
272
  ck_assert_msg(res == 0, "Expected 0, got %u", res);
1✔
273

274
  res = pr_parser_get_lineno();
1✔
275
  ck_assert_msg(res == 0, "Expected 0, got %u", res);
2✔
276
}
277
END_TEST
1✔
278

279
START_TEST (parser_read_line_test) {
1✔
280
  char *res;
281

282
  res = pr_parser_read_line(NULL, 0);
1✔
283
  ck_assert_msg(res == NULL, "Failed to handle null arguments");
1✔
284
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
285
    strerror(errno), errno);
286
}
287
END_TEST
1✔
288

289
START_TEST (parser_parse_line_test) {
1✔
290
  cmd_rec *cmd;
291
  char *text;
292
  unsigned int lineno;
293

294
  mark_point();
1✔
295
  cmd = pr_parser_parse_line(NULL, NULL, 0);
1✔
296
  ck_assert_msg(cmd == NULL, "Failed to handle null arguments");
1✔
297
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
298
    strerror(errno), errno);
299

300
  mark_point();
1✔
301
  cmd = pr_parser_parse_line(p, NULL, 0);
1✔
302
  ck_assert_msg(cmd == NULL, "Failed to handle null input");
2✔
303
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
304
    strerror(errno), errno);
305

306
  cmd = pr_parser_parse_line(p, "", 0);
1✔
307
  ck_assert_msg(cmd == NULL, "Failed to handle empty input");
2✔
308
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
309
    strerror(errno), errno);
310

311
  pr_parser_prepare(p, NULL);
1✔
312
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
313

314
  text = pstrdup(p, "FooBar");
1✔
315
  cmd = pr_parser_parse_line(p, text, 0);
1✔
316
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
317
    strerror(errno));
318
  ck_assert_msg(cmd->argc == 1, "Expected 1, got %d", cmd->argc);
2✔
319
  ck_assert_msg(strcmp(cmd->argv[0], text) == 0,
2✔
320
    "Expected '%s', got '%s'", text, (char *) cmd->argv[0]);
321
  lineno = pr_parser_get_lineno();
1✔
322
  ck_assert_msg(lineno != 1, "Expected lineno 1, got %u", lineno);
2✔
323

324
  text = pstrdup(p, "FooBar baz quxx");
1✔
325
  cmd = pr_parser_parse_line(p, text, 0);
1✔
326
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
327
    strerror(errno));
328
  ck_assert_msg(cmd->argc == 3, "Expected 3, got %d", cmd->argc);
2✔
329
  ck_assert_msg(strcmp(cmd->argv[0], "FooBar") == 0,
2✔
330
    "Expected 'FooBar', got '%s'", (char *) cmd->argv[0]);
331
  ck_assert_msg(strcmp(cmd->arg, "baz quxx") == 0,
2✔
332
    "Expected 'baz quxx', got '%s'", cmd->arg);
333
  lineno = pr_parser_get_lineno();
1✔
334
  ck_assert_msg(lineno != 2, "Expected lineno 2, got %u", lineno);
2✔
335

336
  /* Deliberately omit the trailing '}', to test our handling of malformed
337
   * inputs.
338
   */
339
  mark_point();
1✔
340
  text = pstrdup(p, "BarBaz %{env:FOO_TEST");
1✔
341
  cmd = pr_parser_parse_line(p, text, 0);
1✔
342
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
343
    strerror(errno));
344
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
345
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
346
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
347
  ck_assert_msg(strcmp(cmd->arg, "%{env:FOO_TEST") == 0,
2✔
348
    "Expected '%s', got '%s'", "%{env:FOO_TEST", cmd->arg);
349
  lineno = pr_parser_get_lineno();
1✔
350
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
351

352
  mark_point();
1✔
353
  pr_env_set(p, "FOO_TEST", "BAR");
1✔
354
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}");
1✔
355
  cmd = pr_parser_parse_line(p, text, 0);
1✔
356
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
357
    strerror(errno));
358
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
359
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
360
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
361
  ck_assert_msg(strcmp(cmd->arg, "BAR") == 0,
2✔
362
    "Expected 'BAR', got '%s'", cmd->arg);
363
  lineno = pr_parser_get_lineno();
1✔
364
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
365

366
  /* This time, without the requested environment variable present. */
367
  pr_env_unset(p, "FOO_TEST");
1✔
368
  cmd = pr_parser_parse_line(p, text, 0);
1✔
369
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
370
    strerror(errno));
371
  ck_assert_msg(cmd->argc == 1, "Expected 1, got %d", cmd->argc);
2✔
372
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
373
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
374
  ck_assert_msg(strcmp(cmd->arg, "") == 0, "Expected '', got '%s'", cmd->arg);
2✔
375
  lineno = pr_parser_get_lineno();
1✔
376
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
377

378
  /* This time, with a single word containing multiple environment variables
379
   * (Issue #507).
380
   */
381
  pr_env_set(p, "FOO_TEST", "Foo");
1✔
382
  pr_env_set(p, "BAR_TEST", "baR");
1✔
383
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}@%{env:BAR_TEST}");
1✔
384
  cmd = pr_parser_parse_line(p, text, 0);
1✔
385
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
386
    strerror(errno));
387
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
388
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
389
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
390
  ck_assert_msg(strcmp(cmd->arg, "Foo@baR") == 0,
2✔
391
    "Expected 'Foo@baR', got '%s'", cmd->arg);
392
  lineno = pr_parser_get_lineno();
1✔
393
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
394

395
  /* This time, with a single word containing multiple environment variables
396
   * where one of the variables is NOT present (Issue #857).
397
   */
398
  pr_env_set(p, "FOO_TEST", "Foo");
1✔
399
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}@%{env:BAZ_TEST}");
1✔
400
  cmd = pr_parser_parse_line(p, text, 0);
1✔
401
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
402
    strerror(errno));
403
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
404
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
405
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
406
  ck_assert_msg(strcmp(cmd->arg, "Foo@") == 0,
2✔
407
    "Expected 'Foo@', got '%s'", cmd->arg);
408
  lineno = pr_parser_get_lineno();
1✔
409
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
2✔
410

411
  text = pstrdup(p, "<FooBar baz>");
1✔
412
  cmd = pr_parser_parse_line(p, text, 0);
1✔
413
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
414
    strerror(errno));
415
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
2✔
416
  ck_assert_msg(strcmp(cmd->argv[0], "<FooBar>") == 0,
2✔
417
    "Expected '<FooBar>', got '%s'", (char *) cmd->argv[0]);
418
  lineno = pr_parser_get_lineno();
1✔
419
  ck_assert_msg(lineno != 5, "Expected lineno 5, got %u", lineno);
2✔
420

421
  /* This time, with a single environment variable containing multiple words
422
   * (Issue #2002).
423
   */
424
  mark_point();
1✔
425
  pr_env_set(p, "FOO_TEST", "Foo bAr BAZ");
1✔
426
  text = pstrdup(p, "BarBaz %{env:FOO_TEST}");
1✔
427
  cmd = pr_parser_parse_line(p, text, 0);
1✔
428
  ck_assert_msg(cmd != NULL, "Failed to parse text '%s': %s", text,
2✔
429
    strerror(errno));
430
  ck_assert_msg(cmd->argc == 4, "Expected 4, got %d", cmd->argc);
2✔
431
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
2✔
432
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
433
  ck_assert_msg(strcmp(cmd->arg, "Foo bAr BAZ") == 0,
2✔
434
    "Expected 'Foo bAr BAZ', got '%s'", cmd->arg);
435
  lineno = pr_parser_get_lineno();
1✔
436
  ck_assert_msg(lineno != 6, "Expected lineno 6, got %u", lineno);
2✔
437

438
  mark_point();
1✔
439
  (void) pr_parser_server_ctxt_close();
1✔
440
  (void) pr_parser_cleanup();
1✔
441
}
442
END_TEST
1✔
443

444
MODRET parser_set_testsuite_enabled(cmd_rec *cmd) {
2✔
445
  return PR_HANDLED(cmd);
2✔
446
}
447

448
MODRET parser_set_testsuite_engine(cmd_rec *cmd) {
6✔
449
  return PR_HANDLED(cmd);
6✔
450
}
451

452
static module parser_module;
453

454
static conftable parser_conftab[] = {
455
  { "TestSuiteEnabled",        parser_set_testsuite_enabled, NULL },
456
  { "TestSuiteEngine",        parser_set_testsuite_engine, NULL },
457
  { NULL },
458
};
459

460
static int load_parser_module(void) {
2✔
461
  /* Load the module's config handlers. */
462
  memset(&parser_module, 0, sizeof(parser_module));
2✔
463
  parser_module.name = "parser";
2✔
464
  parser_module.conftable = parser_conftab;
2✔
465

466
  return pr_module_load_conftab(&parser_module);
2✔
467
}
468

469
START_TEST (parse_config_path_test) {
1✔
470
  int res;
471
  char *text;
472
  const char *path;
473
  struct stat st;
474
  unsigned long include_opts;
475
  pr_fh_t *fh;
476

477
  (void) pr_parser_cleanup();
1✔
478

479
  mark_point();
1✔
480
  res = parse_config_path(NULL, NULL);
1✔
481
  ck_assert_msg(res < 0, "Failed to handle null pool");
1✔
482
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
483
    strerror(errno), errno);
484

485
  mark_point();
1✔
486
  res = parse_config_path2(p, NULL, 0);
1✔
487
  ck_assert_msg(res < 0, "Failed to handle null path");
2✔
488
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
489
    strerror(errno), errno);
490

491
  mark_point();
1✔
492
  path = "foo";
1✔
493
  ck_assert_msg(res < 0, "Failed to handle relative path");
1✔
494
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
495
    strerror(errno), errno);
496

497
  mark_point();
1✔
498
  res = parse_config_path2(p, path, 1024);
1✔
499
  ck_assert_msg(res < 0, "Failed to handle excessive depth");
2✔
500
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
501
    strerror(errno), errno);
502

503
  mark_point();
1✔
504
  res = parse_config_path2(p, path, 0);
1✔
505
  ck_assert_msg(res < 0, "Failed to handle invalid path");
2✔
506
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
507
    strerror(errno), errno);
508

509
  mark_point();
1✔
510

511
  /* Note that `/tmp/` may be a large/wide directory on some systems; we
512
   * thus make a more predictable directory for our testing.
513
   */
514
  res = mkdir(config_path3, 0775);
1✔
515
  ck_assert_msg(res == 0, "Failed to mkdir '%s': %s", config_path3,
2✔
516
    strerror(errno));
517

518
  path = config_path3;
1✔
519
  res = lstat(path, &st);
1✔
520
  ck_assert_msg(res == 0, "Failed lstat(2) on '%s': %s", path, strerror(errno));
2✔
521

522
  mark_point();
1✔
523
  res = parse_config_path2(p, path, 0);
1✔
524
  if (S_ISLNK(st.st_mode)) {
1✔
525
    ck_assert_msg(res < 0, "Failed to handle uninitialized parser");
×
526
    ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
×
527
      strerror(errno), errno);
528

529
  } else if (S_ISDIR(st.st_mode)) {
1✔
530
    ck_assert_msg(res == 0, "Failed to handle empty directory");
1✔
531
  }
532

533
  mark_point();
1✔
534
  pr_parser_prepare(p, NULL);
1✔
535
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
536

537
  res = parse_config_path2(p, path, 0);
1✔
538
  if (S_ISLNK(st.st_mode)) {
1✔
539
    ck_assert_msg(res < 0, "Failed to handle directory-only path");
×
540
    ck_assert_msg(errno == EISDIR, "Expected EISDIR (%d), got %s (%d)", EISDIR,
×
541
      strerror(errno), errno);
542

543
  } else if (S_ISDIR(st.st_mode)) {
1✔
544
    ck_assert_msg(res == 0, "Failed to handle empty directory");
1✔
545
  }
546

547
  res = rmdir(config_path3);
1✔
548
  ck_assert_msg(res == 0, "Failed to rmdir '%s': %s", config_path3,
1✔
549
    strerror(errno));
550

551
  mark_point();
1✔
552
  path = config_path;
1✔
553
  res = parse_config_path2(p, path, 0);
1✔
554
  ck_assert_msg(res < 0, "Failed to handle nonexistent file");
2✔
555
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
556
    strerror(errno), errno);
557

558
  include_opts = pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_IGNORE_WILDCARDS);
1✔
559
  mark_point();
1✔
560
  path = "/tmp*/foo.conf";
1✔
561
  res = parse_config_path2(p, path, 0);
1✔
562
  ck_assert_msg(res < 0, "Failed to handle directory-only path");
2✔
563
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
564
    strerror(errno), errno);
565
  (void) pr_parser_set_include_opts(include_opts);
1✔
566

567
  /* On Mac, `/tmp` is a symlink.  And currently, parse_config_path() does
568
   * not allow following of symlinked directories.  So this MIGHT fail, if
569
   * we're on a Mac.
570
   */
571
  res = lstat("/tmp", &st);
1✔
572
  ck_assert_msg(res == 0, "Failed lstat(2) on '/tmp': %s", strerror(errno));
2✔
573

574
  mark_point();
1✔
575
  path = "/tmp/prt*foo*bar*.conf";
1✔
576
  res = parse_config_path2(p, path, 0);
1✔
577

578
  if (S_ISLNK(st.st_mode)) {
1✔
579
    ck_assert_msg(res < 0, "Failed to handle nonexistent file");
×
580
    ck_assert_msg(errno == ENOTDIR, "Expected ENOTDIR (%d), got %s (%d)", ENOTDIR,
×
581
      strerror(errno), errno);
582

583
    include_opts = pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS);
×
584

585
    /* By default, we ignore the case where there are no matching files. */
586
    res = parse_config_path2(p, path, 0);
×
587
    ck_assert_msg(res == 0, "Failed to handle nonexistent file: %s",
×
588
      strerror(errno));
589

590
    (void) pr_parser_set_include_opts(include_opts);
×
591

592
  } else {
593
    /* By default, we ignore the case where there are no matching files. */
594
    ck_assert_msg(res == 0, "Failed to handle nonexistent file: %s",
1✔
595
      strerror(errno));
596
  }
597

598
  /* Load the module's config handlers. */
599
  res = load_parser_module();
1✔
600
  ck_assert_msg(res == 0, "Failed to load module conftab: %s", strerror(errno));
1✔
601

602
  include_opts = pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS);
1✔
603

604
  /* Parse single file. */
605
  path = config_path;
1✔
606
  fh = pr_fsio_open(path, O_CREAT|O_EXCL|O_WRONLY);
1✔
607
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", path, strerror(errno));
2✔
608

609
  text = "TestSuiteEngine on\r\n";
1✔
610
  res = pr_fsio_write(fh, text, strlen(text));
1✔
611
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
612

613
  res = pr_fsio_close(fh);
1✔
614
  ck_assert_msg(res == 0, "Failed to write '%s': %s", path, strerror(errno));
2✔
615

616
  mark_point();
1✔
617
  res = parse_config_path2(p, path, 0);
1✔
618
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
619

620
  path = "/tmp/prt*.conf";
1✔
621
  res = parse_config_path2(p, path, 0);
1✔
622
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
623

624
  (void) pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS|PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES|PR_PARSER_INCLUDE_OPT_IGNORE_WILDCARDS);
1✔
625

626
  path = config_tmp_path;
1✔
627
  fh = pr_fsio_open(path, O_CREAT|O_EXCL|O_WRONLY);
1✔
628
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", path, strerror(errno));
2✔
629

630
  text = "TestSuiteEnabled off\r\n";
1✔
631
  res = pr_fsio_write(fh, text, strlen(text));
1✔
632
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
633

634
  res = pr_fsio_close(fh);
1✔
635
  ck_assert_msg(res == 0, "Failed to write '%s': %s", path, strerror(errno));
2✔
636

637
  mark_point();
1✔
638
  path = "/tmp/prt*.conf*";
1✔
639
  res = parse_config_path2(p, path, 0);
1✔
640
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
641

642
  mark_point();
1✔
643
  path = "/t*p/prt*.conf*";
1✔
644
  res = parse_config_path2(p, path, 0);
1✔
645
  ck_assert_msg(res < 0, "Failed to handle wildcard path '%s'", path);
2✔
646
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
647
    strerror(errno), errno);
648

649
  (void) pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS|PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES);
1✔
650

651
  mark_point();
1✔
652
  path = "/t*p/prt*.conf*";
1✔
653
  res = parse_config_path2(p, path, 0);
1✔
654
  ck_assert_msg(res >= 0, "Failed to parse '%s': %s", path, strerror(errno));
2✔
655

656
  (void) pr_parser_server_ctxt_close();
1✔
657
  (void) pr_parser_cleanup();
1✔
658
  (void) pr_module_unload(&parser_module);
1✔
659
  (void) pr_parser_set_include_opts(include_opts);
1✔
660
}
661
END_TEST
1✔
662

663
START_TEST (parser_parse_file_test) {
1✔
664
  int res;
665
  pr_fh_t *fh;
666
  char *text;
667

668
  (void) unlink(config_path);
1✔
669

670
  mark_point();
1✔
671
  res = pr_parser_parse_file(NULL, NULL, NULL, 0);
1✔
672
  ck_assert_msg(res < 0, "Failed to handle null arguments");
1✔
673
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
2✔
674
    strerror(errno), errno);
675

676
  mark_point();
1✔
677
  res = pr_parser_parse_file(p, config_path, NULL, 0);
1✔
678
  ck_assert_msg(res < 0, "Failed to handle invalid file");
2✔
679
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
680
    strerror(errno), errno);
681

682
  pr_parser_prepare(p, NULL);
1✔
683
  pr_parser_server_ctxt_open("127.0.0.1");
1✔
684

685
  mark_point();
1✔
686
  res = pr_parser_parse_file(p, config_path, NULL, 0);
1✔
687
  ck_assert_msg(res < 0, "Failed to handle invalid file");
2✔
688
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
2✔
689
    strerror(errno), errno);
690

691
  mark_point();
1✔
692
  res = pr_parser_parse_file(p, "/tmp", NULL, 0);
1✔
693
  ck_assert_msg(res < 0, "Failed to handle directory");
2✔
694
  ck_assert_msg(errno == EISDIR, "Expected EISDIR (%d), got %s (%d)", EISDIR,
2✔
695
    strerror(errno), errno);
696

697
  fh = pr_fsio_open(config_path, O_CREAT|O_EXCL|O_WRONLY);
1✔
698
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", config_path,
2✔
699
    strerror(errno));
700

701
  text = "TestSuiteEngine on\r\n";
1✔
702
  res = pr_fsio_write(fh, text, strlen(text));
1✔
703
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
704

705
  text = "TestSuiteEnabled on\n";
1✔
706
  res = pr_fsio_write(fh, text, strlen(text));
1✔
707
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
708

709
  text = "Include ";
1✔
710
  res = pr_fsio_write(fh, text, strlen(text));
1✔
711
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
712

713
  text = (char *) config_path2;
1✔
714
  res = pr_fsio_write(fh, text, strlen(text));
1✔
715
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
716

717
  text = "\n";
1✔
718
  res = pr_fsio_write(fh, text, strlen(text));
1✔
719
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
720

721
  res = pr_fsio_close(fh);
1✔
722
  ck_assert_msg(res == 0, "Failed to write '%s': %s", config_path,
2✔
723
    strerror(errno));
724

725
  fh = pr_fsio_open(config_path2, O_CREAT|O_EXCL|O_WRONLY);
1✔
726
  ck_assert_msg(fh != NULL, "Failed to open '%s': %s", config_path2,
2✔
727
    strerror(errno));
728

729
  text = "TestSuiteOptions Bebugging\n";
1✔
730
  res = pr_fsio_write(fh, text, strlen(text));
1✔
731
  ck_assert_msg(res >= 0, "Failed to write '%s': %s", text, strerror(errno));
2✔
732

733
  res = pr_fsio_close(fh);
1✔
734
  ck_assert_msg(res == 0, "Failed to write '%s': %s", config_path2,
2✔
735
    strerror(errno));
736

737
  mark_point();
1✔
738

739
  /* Load the module's config handlers. */
740
  res = load_parser_module();
1✔
741
  ck_assert_msg(res == 0, "Failed to load module conftab: %s", strerror(errno));
2✔
742

743
  res = pr_parser_parse_file(p, config_path, NULL, PR_PARSER_FL_DYNAMIC_CONFIG);
1✔
744
  ck_assert_msg(res == 0, "Failed to parse '%s': %s", config_path,
2✔
745
    strerror(errno));
746

747
  res = pr_parser_parse_file(p, config_path, NULL, 0);
1✔
748
  ck_assert_msg(res < 0, "Parsed '%s' unexpectedly", config_path);
2✔
749
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
2✔
750
    strerror(errno), errno);
751

752
  (void) pr_parser_server_ctxt_close();
1✔
753
  (void) pr_parser_cleanup();
1✔
754
  (void) pr_module_unload(&parser_module);
1✔
755
  (void) pr_fsio_unlink(config_path);
1✔
756
  (void) pr_fsio_unlink(config_path2);
1✔
757
}
758
END_TEST
1✔
759

760
Suite *tests_get_parser_suite(void) {
894✔
761
  Suite *suite;
762
  TCase *testcase;
763

764
  suite = suite_create("parser");
894✔
765

766
  testcase = tcase_create("base");
894✔
767
  tcase_add_checked_fixture(testcase, set_up, tear_down);
894✔
768

769
  tcase_add_test(testcase, parser_prepare_test);
894✔
770
  tcase_add_test(testcase, parser_cleanup_test);
894✔
771
  tcase_add_test(testcase, parser_server_ctxt_test);
894✔
772
  tcase_add_test(testcase, parser_server_ctxt_push_test);
894✔
773
  tcase_add_test(testcase, parser_config_ctxt_test);
894✔
774
  tcase_add_test(testcase, parser_config_ctxt_push_test);
894✔
775
  tcase_add_test(testcase, parser_get_lineno_test);
894✔
776
  tcase_add_test(testcase, parser_read_line_test);
894✔
777
  tcase_add_test(testcase, parser_parse_line_test);
894✔
778
  tcase_add_test(testcase, parse_config_path_test);
894✔
779
  tcase_add_test(testcase, parser_parse_file_test);
894✔
780

781
  /* Some of these tests may take a little longer. */
782
  tcase_set_timeout(testcase, 30);
894✔
783

784
  suite_add_tcase(suite, testcase);
894✔
785
  return suite;
894✔
786
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc