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

proftpd / proftpd / 26182518137

20 May 2026 06:49PM UTC coverage: 93.024% (+0.4%) from 92.635%
26182518137

push

github

51329 of 55178 relevant lines covered (93.02%)

226.63 hits per line

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

97.85
/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) {
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

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

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

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

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

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

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

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

11✔
74
/* Tests */
75

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

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

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

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

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

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

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

103
  pr_parser_prepare(NULL, NULL);
104

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

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

116
  mark_point();
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));
1✔
120
}
1✔
121
END_TEST
1✔
122

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

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

131
  pr_parser_prepare(p, NULL);
132

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

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

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

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

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

1✔
160
  res = pr_parser_server_ctxt_push(NULL);
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,
1✔
163
    strerror(errno), errno);
1✔
164

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

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

173
  pr_parser_prepare(p, NULL);
174

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

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

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

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

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

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

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

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

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

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

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

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

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

1✔
238
  res = pr_parser_config_ctxt_push(NULL);
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,
1✔
241
    strerror(errno), errno);
1✔
242

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

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

251
  pr_parser_prepare(p, NULL);
252

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

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

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

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

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

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

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

1✔
282
  res = pr_parser_read_line(NULL, 0);
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,
1✔
285
    strerror(errno), errno);
1✔
286
}
287
END_TEST
1✔
288

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

1✔
294
  mark_point();
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,
1✔
298
    strerror(errno), errno);
1✔
299

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

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

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

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

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

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

1✔
352
  mark_point();
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,
1✔
357
    strerror(errno));
1✔
358
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
359
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
1✔
360
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
1✔
361
  ck_assert_msg(strcmp(cmd->arg, "BAR") == 0,
362
    "Expected 'BAR', got '%s'", cmd->arg);
1✔
363
  lineno = pr_parser_get_lineno();
364
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
1✔
365

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

1✔
378
  /* This time, with a single word containing multiple environment variables
379
   * (Issue #507).
380
   */
381
  pr_env_set(p, "FOO_TEST", "Foo");
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,
1✔
386
    strerror(errno));
1✔
387
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
388
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
1✔
389
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
1✔
390
  ck_assert_msg(strcmp(cmd->arg, "Foo@baR") == 0,
391
    "Expected 'Foo@baR', got '%s'", cmd->arg);
1✔
392
  lineno = pr_parser_get_lineno();
393
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
1✔
394

1✔
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");
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,
1✔
402
    strerror(errno));
1✔
403
  ck_assert_msg(cmd->argc == 2, "Expected 2, got %d", cmd->argc);
404
  ck_assert_msg(strcmp(cmd->argv[0], "BarBaz") == 0,
1✔
405
    "Expected 'BarBaz', got '%s'", (char *) cmd->argv[0]);
1✔
406
  ck_assert_msg(strcmp(cmd->arg, "Foo@") == 0,
407
    "Expected 'Foo@', got '%s'", cmd->arg);
1✔
408
  lineno = pr_parser_get_lineno();
409
  ck_assert_msg(lineno != 3, "Expected lineno 3, got %u", lineno);
1✔
410

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

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

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

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

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

452
static module parser_module;
453

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

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

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

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

1✔
477
  (void) pr_parser_cleanup();
1✔
478

1✔
479
  mark_point();
480
  res = parse_config_path(NULL, NULL);
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,
1✔
483
    strerror(errno), errno);
1✔
484

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

1✔
491
  mark_point();
492
  path = "foo";
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,
495
    strerror(errno), errno);
496

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

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

1✔
509
  mark_point();
×
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
   */
1✔
514
  res = mkdir(config_path3, 0775);
1✔
515
  ck_assert_msg(res == 0, "Failed to mkdir '%s': %s", config_path3,
516
    strerror(errno));
517

1✔
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));
521

1✔
522
  mark_point();
1✔
523
  res = parse_config_path2(p, path, 0);
×
524
  if (S_ISLNK(st.st_mode)) {
×
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);
1✔
528

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

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

1✔
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");
1✔
540
    ck_assert_msg(errno == EISDIR, "Expected EISDIR (%d), got %s (%d)", EISDIR,
541
      strerror(errno), errno);
542

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

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

551
  mark_point();
552
  path = config_path;
553
  res = parse_config_path2(p, path, 0);
554
  ck_assert_msg(res < 0, "Failed to handle nonexistent file");
555
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
1✔
556
    strerror(errno), errno);
1✔
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);
562
  ck_assert_msg(res < 0, "Failed to handle directory-only path");
1✔
563
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
×
564
    strerror(errno), errno);
×
565
  (void) pr_parser_set_include_opts(include_opts);
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);
×
572
  ck_assert_msg(res == 0, "Failed lstat(2) on '/tmp': %s", strerror(errno));
573

574
  mark_point();
×
575
  path = "/tmp/prt*foo*bar*.conf";
576
  res = parse_config_path2(p, path, 0);
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);
1✔
584

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

1✔
590
    (void) pr_parser_set_include_opts(include_opts);
1✔
591

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

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

1✔
602
  include_opts = pr_parser_set_include_opts(PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS);
1✔
603

604
  /* Parse single file. */
1✔
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));
608

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

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

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

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

1✔
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));
1✔
629

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

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

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

1✔
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);
646
  ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
647
    strerror(errno), errno);
1✔
648

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

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

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

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

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

1✔
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,
674
    strerror(errno), errno);
675

1✔
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");
1✔
679
  ck_assert_msg(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM,
680
    strerror(errno), errno);
681

1✔
682
  pr_parser_prepare(p, NULL);
1✔
683
  pr_parser_server_ctxt_open("127.0.0.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");
1✔
688
  ck_assert_msg(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT,
689
    strerror(errno), errno);
1✔
690

1✔
691
  mark_point();
1✔
692
  res = pr_parser_parse_file(p, "/tmp", NULL, 0);
693
  ck_assert_msg(res < 0, "Failed to handle directory");
1✔
694
  ck_assert_msg(errno == EISDIR, "Expected EISDIR (%d), got %s (%d)", EISDIR,
1✔
695
    strerror(errno), errno);
1✔
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,
1✔
699
    strerror(errno));
1✔
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));
1✔
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));
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));
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));
1✔
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));
720

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

1✔
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,
727
    strerror(errno));
1✔
728

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

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

1✔
737
  mark_point();
1✔
738

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

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

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

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

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

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

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

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

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

784
  suite_add_tcase(suite, testcase);
785
  return suite;
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