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

aremmell / libsir / 473

06 Sep 2023 02:24AM UTC coverage: 94.751% (+0.1%) from 94.608%
473

Pull #258

gitlab-ci

johnsonjh
Merge branch 'set-thread-names' of github.com:/aremmell/libsir into set-thread-names
Pull Request #258: Bug fix, new tests, portable set thread name functionality

473 of 473 new or added lines in 10 files covered. (100.0%)

3105 of 3277 relevant lines covered (94.75%)

614386.54 hits per line

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

97.75
/tests/tests.c
1
/*
2
 * tests.c
3
 *
4
 * Author:    Ryan M. Lederman <lederman@gmail.com>
5
 * Copyright: Copyright (c) 2018-2023
6
 * Version:   2.2.3
7
 * License:   The MIT License (MIT)
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
10
 * this software and associated documentation files (the "Software"), to deal in
11
 * the Software without restriction, including without limitation the rights to
12
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
13
 * the Software, and to permit persons to whom the Software is furnished to do so,
14
 * subject to the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be included in all
17
 * copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
21
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
22
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
23
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 */
26
//-V:_sir_logv:575
27
#include "tests.h"
28

29
static sir_test sir_tests[] = {
30
    {"performance",             sirtest_perf, false, true},
31
    {"thread-race",             sirtest_threadrace, false, true},
32
    {"thread-pool",             sirtest_threadpool, false, true},
33
    {"exceed-max-buffer-size",  sirtest_exceedmaxsize, false, true},
34
    {"no-output-destination",   sirtest_failnooutputdest, false, true},
35
    {"null-pointers",           sirtest_failnulls, false, true},
36
    {"empty-message",           sirtest_failemptymessage, false, true},
37
    {"file-cache-sanity",       sirtest_filecachesanity, false, true},
38
    {"file-invalid-name",       sirtest_failinvalidfilename, false, true},
39
    {"file-bad-permissions",    sirtest_failfilebadpermission, false, true},
40
    {"file-duplicate-name",     sirtest_faildupefile, false, true},
41
    {"file-remove-nonexistent", sirtest_failremovebadfile, false, true},
42
    {"file-archive-large",      sirtest_rollandarchivefile, false, true},
43
    {"init-output-before",      sirtest_failwithoutinit, false, true},
44
    {"init-superfluous",        sirtest_failinittwice, false, true},
45
    {"init-bad-data",           sirtest_failinvalidinitdata, false, true},
46
    {"init-cleanup-init",       sirtest_initcleanupinit, false, true},
47
    {"init-with-makeinit",      sirtest_initmakeinit, false, true},
48
    {"cleanup-output-after",    sirtest_failaftercleanup, false, true},
49
    {"sanity-errors",           sirtest_errorsanity, false, true},
50
    {"sanity-text-styles",      sirtest_textstylesanity, false, true},
51
    {"sanity-options",          sirtest_optionssanity, false, true},
52
    {"sanity-levels",           sirtest_levelssanity, false, true},
53
    {"sanity-mutexes",          sirtest_mutexsanity, false, true},
54
    {"sanity-update-config",    sirtest_updatesanity, false, true},
55
    {"sanity-thread-ids",       sirtest_threadidsanity, false, true},
56
    {"sanity-file-write",       sirtest_logwritesanity, false, true},
57
    {"syslog",                  sirtest_syslog, false, true},
58
    {"os_log",                  sirtest_os_log, false, true},
59
    {"filesystem",              sirtest_filesystem, false, true},
60
    {"squelch-spam",            sirtest_squelchspam, false, true},
61
    {"plugin-loader",           sirtest_pluginloader, false, true},
62
    {"get-version-info",        sirtest_getversioninfo, false, true}
63
};
64

65
static bool leave_logs = false;
66

67
int main(int argc, char** argv) {
33✔
68
#if defined(__HAIKU__) && !defined(DEBUG)
69
    disable_debugger(1);
70
#endif
71

72
#include "tests_malloc.h"
73

74
#if !defined(__WIN__) && !defined(__HAIKU__)
75
    /* Disallow execution by root / sudo; some of the tests rely on lack of permissions. */
76
    if (geteuid() == 0) {
33✔
77
        fprintf(stderr, "Sorry, but this program may not be executed by root.\n");
1✔
78
        return EXIT_FAILURE;
1✔
79
    }
80
#else /* __WIN__ */
81
# if defined(_DEBUG) && defined(SIR_ASSERT_ENABLED)
82
    /* Prevents assert() from calling abort() before the user is able to:
83
     * a.) break into the code and debug (Retry button)
84
     * b.) ignore the assert() and continue. */
85
    _set_error_mode(_OUT_TO_MSGBOX);
86
# endif
87
#endif
88

89
    bool wait     = false;
29✔
90
    bool only     = false;
29✔
91
    size_t to_run = 0;
29✔
92

93
    for (int n = 1; n < argc; n++) {
36✔
94
        if (_sir_strsame(argv[n], _cl_arg_list[0].flag,
10✔
95
            strnlen(_cl_arg_list[0].flag, SIR_MAXCLIFLAG))) { /* --perf */
9✔
96
            only = mark_test_to_run("performance");
1✔
97
            if (only)
1✔
98
                to_run = 1;
1✔
99
        } else if (_sir_strsame(argv[n], _cl_arg_list[1].flag,
9✔
100
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --only */
8✔
101
            while (++n < argc) {
4✔
102
                if (_sir_validstrnofail(argv[n])) {
2✔
103
                    if (*argv[n] == '-' || !mark_test_to_run(argv[n])) {
2✔
104
                        fprintf(stderr, RED("invalid argument: '%s'") "\n", argv[n]);
1✔
105
                        print_usage_info();
1✔
106
                        return EXIT_FAILURE;
1✔
107
                    }
108
                    to_run++;
1✔
109
                }
110
            }
111
            if (0 == to_run) {
2✔
112
                fprintf(stderr, RED("value expected for '%s'") "\n",
1✔
113
                    _cl_arg_list[1].flag);
1✔
114
                print_usage_info();
1✔
115
                return EXIT_FAILURE;
1✔
116
            }
117
            only = true;
1✔
118
        } else if (_sir_strsame(argv[n], _cl_arg_list[2].flag,
6✔
119
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --list */
5✔
120
            print_test_list();
1✔
121
            return EXIT_SUCCESS;
1✔
122
        } else if (_sir_strsame(argv[n], _cl_arg_list[3].flag,
5✔
123
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --leave-logs */
4✔
124
            leave_logs = true;
1✔
125
        } else if (_sir_strsame(argv[n], _cl_arg_list[4].flag,
4✔
126
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --wait */
3✔
127
            wait = true;
×
128
        }  else if (_sir_strsame(argv[n], _cl_arg_list[5].flag,
3✔
129
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --version */
3✔
130
            print_libsir_version();
1✔
131
            return EXIT_SUCCESS;
1✔
132
        }else if (_sir_strsame(argv[n], _cl_arg_list[6].flag,
2✔
133
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --help */
2✔
134
            print_usage_info();
1✔
135
            return EXIT_SUCCESS;
1✔
136
        } else {
137
            fprintf(stderr, "unknown argument: '%s'", argv[n]);
1✔
138
            print_usage_info();
1✔
139
            return EXIT_FAILURE;
1✔
140
        }
141
    }
142

143
    size_t first     = (only ? 0 : 1);
26✔
144
    size_t tgt_tests = (only ? to_run : _sir_countof(sir_tests) - first);
26✔
145
    size_t passed    = 0;
23✔
146
    size_t ran       = 0;
23✔
147
    sir_time timer  = {0};
26✔
148

149
    printf(WHITEB("\n" ULINE("libsir") " %s (%s) running %zu %s...") "\n",
52✔
150
        sir_getversionstring(), (sir_isprerelease() ? "prerelease" : "release"),
26✔
151
        tgt_tests, TEST_S(tgt_tests));
152
    sir_timer_start(&timer);
26✔
153

154
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
860✔
155
        if (only && !sir_tests[n].run) {
834✔
156
            _sir_selflog("skipping '%s'; not marked to run", sir_tests[n].name);
64✔
157
            continue;
64✔
158
        }
159

160
        printf(WHITEB("\n(%zu/%zu) '%s'...") "\n\n", ran + 1, tgt_tests, sir_tests[n].name);
770✔
161

162
        sir_tests[n].pass = sir_tests[n].fn();
770✔
163
        if (sir_tests[n].pass)
770✔
164
            passed++;
626✔
165

166
        ran++;
674✔
167

168
        printf(WHITEB("\n(%zu/%zu) '%s' finished: ") "%s\n", ran, tgt_tests, sir_tests[n].name,
770✔
169
            PRN_PASS(sir_tests[n].pass));
674✔
170
    }
171

172
    double elapsed = sir_timer_elapsed(&timer);
26✔
173

174
    if (passed == tgt_tests) {
26✔
175
        printf("\n" WHITEB("done: ")
9✔
176
                   GREENB("%s%zu " ULINE("libsir") " %s passed in %.03fsec!") "\n\n",
177
            tgt_tests > 1 ? "all " : "", tgt_tests, TEST_S(tgt_tests), elapsed / 1e3);
178
    } else {
179
        printf("\n" WHITEB("done: ")
17✔
180
                   REDB("%zu of %zu " ULINE("libsir") " %s failed in %.03fsec") "\n\n",
181
            tgt_tests - passed, tgt_tests, TEST_S(tgt_tests), elapsed / 1e3);
182

183
        printf(REDB("Failed %s:") "\n\n", TEST_S(tgt_tests - passed));
17✔
184

185
        for (size_t t = 0; t < _sir_countof(sir_tests); t++)
578✔
186
            if (!sir_tests[t].pass)
561✔
187
                printf(RED(INDENT_ITEM "%s\n"), sir_tests[t].name);
144✔
188
        printf("\n");
17✔
189
    }
190

191
    if (wait) {
26✔
192
        printf(WHITEB(EMPH("press any key to exit...")) "\n");
×
193
        char ch = '\0';
1✔
194
        (void)_sir_getchar(&ch);
1✔
195
        SIR_UNUSED(ch);
196
    }
197

198
    return passed == tgt_tests ? EXIT_SUCCESS : EXIT_FAILURE;
26✔
199
}
200

201
bool sirtest_exceedmaxsize(void) {
24✔
202
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
203
    bool pass = si_init;
21✔
204

205
    char toobig[SIR_MAXMESSAGE + 100] = {0};
21✔
206
    memset(toobig, 'a', SIR_MAXMESSAGE + 100);
21✔
207
    toobig[SIR_MAXMESSAGE + 99] = '\0';
24✔
208

209
    _sir_andeql(pass, sir_info("%s", toobig));
24✔
210

211
    _sir_andeql(pass, sir_cleanup());
24✔
212
    return print_result_and_return(pass);
24✔
213
}
214

215
bool sirtest_logwritesanity(void) {
24✔
216
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
217
    bool pass = si_init;
21✔
218

219
    static const char* logfilename = MAKE_LOG_NAME("write-validate.log");
220
    static const char* message     = "Lorem ipsum dolor sit amet, sea ei dicit"
221
                                     " regione laboramus, eos cu minim putent."
222
                                     " Sale omnium conceptam est in, cu nam possim"
223
                                     " prompta eleifend. Duo purto nostrud eu."
224
                                     " Alia accumsan has cu, mentitum invenire"
225
                                     " mel an, dicta noster legendos et pro."
226
                                     " Solum nobis laboramus et quo, nam putant"
227
                                     " dolores consequuntur ex. Sit veniam eruditi"
228
                                     " contentiones at. Cu ponderum oporteat"
229
                                     " oportere mel, has et saperet accusata"
230
                                     " complectitur.";
231

232
    printf("\tadding log file '%s' to libsir...\n", logfilename);
24✔
233
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
24✔
234
    _sir_andeql(pass, 0U != id);
24✔
235

236
    print_test_error(pass, false);
24✔
237

238
    printf("\twriting message to stdout and %s...\n", logfilename);
24✔
239

240
    _sir_andeql(pass, sir_debug("%s", message));
24✔
241

242
    print_test_error(pass, false);
24✔
243

244
    printf("\tremoving %s from libsir...\n", logfilename);
24✔
245
    _sir_andeql(pass, sir_remfile(id));
24✔
246

247
    print_test_error(pass, false);
24✔
248

249
    printf("\topening %s for reading...\n", logfilename);
24✔
250

251
    FILE* f = fopen(logfilename, "r");
24✔
252
    if (!f) {
24✔
253
        pass = false;
3✔
254
    } else {
255
        char buf[512] = {0};
21✔
256
        _sir_andeql(pass, 0 != sir_readline(f, buf, 512));
21✔
257

258
        bool found = NULL != strstr(buf, message);
21✔
259
        _sir_andeql(pass, found);
21✔
260

261
        if (found)
21✔
262
            printf("\t" GREEN("found '%s'") "\n", message);
16✔
263
        else
264
            printf("\t" RED("did not find '%s'") "\n", message);
2✔
265

266
        _sir_safefclose(&f);
21✔
267
        printf("\tdeleting %s...\n", logfilename);
21✔
268
        (void)rmfile(logfilename);
21✔
269
    }
270

271
    _sir_andeql(pass, sir_cleanup());
24✔
272
    return print_result_and_return(pass);
24✔
273
}
274

275
bool sirtest_threadidsanity(void)
24✔
276
{
277
#if defined(SIR_NO_THREAD_NAMES)
278
    printf("\t" DGRAY("test skipped for this system configuration") "\n");
279
    return true;
280
#endif
281
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
282
    bool pass = si_init;
21✔
283

284
    static const char* thread_name = "mythread";
285
    static const char* logfilename = MAKE_LOG_NAME("thread-id-name.log");
286

287
    printf("\tadding log file '%s' to libsir...\n", logfilename);
24✔
288
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
24✔
289
    _sir_andeql(pass, 0U != id);
24✔
290

291
    print_test_error(pass, false);
24✔
292

293
    printf("\tlogging a message normally...\n");
21✔
294
    _sir_andeql(pass, sir_debug("this is a test of the libsir system"));
24✔
295

296
    printf("\tsetting the thread name to '%s' and logging again...\n", thread_name);
24✔
297

298
    _sir_andeql(pass, _sir_setthreadname(thread_name));
24✔
299
    sir_sleep_msec((uint32_t)SIR_THRD_CHK_INTERVAL + 200U);
24✔
300

301
    print_test_error(pass, false);
24✔
302

303
    _sir_andeql(pass, sir_debug("this is a test of the libsir system after setting thread name"));
24✔
304

305
    printf("\tsetting the thread name to '' and logging again...\n");
21✔
306

307
    _sir_andeql(pass, _sir_setthreadname(""));
24✔
308
    sir_sleep_msec((uint32_t)SIR_THRD_CHK_INTERVAL + 200U);
24✔
309

310
    print_test_error(pass, false);
24✔
311

312
    _sir_andeql(pass, sir_debug("this is a test of the libsir system after clearing thread name"));
24✔
313

314
     /* remove the log file from libsir, then open it and read it line by line. */
315
    printf("\tremoving %s from libsir...\n", logfilename);
24✔
316
    _sir_andeql(pass, sir_remfile(id));
24✔
317

318
    printf("\topening %s for reading...\n", logfilename);
24✔
319

320
    FILE* f = fopen(logfilename, "r");
24✔
321
    if (!f) {
24✔
322
        pass = false;
3✔
323
    } else {
324
        /* look for, in order, TID, thread name, TID. */
325
        for (size_t n = 0; n < 3; n++) {
84✔
326
            char buf[256] = {0};
63✔
327
            _sir_andeql(pass, 0 != sir_readline(f, buf, 256));
63✔
328
            printf("\tread line %zu: '%s'\n", n, buf);
54✔
329

330
            char search[SIR_MAXPID] = {0};
63✔
331
            switch (n) {
63✔
332
                case 0:
42✔
333
                case 2:
334
                    (void)snprintf(search, SIR_MAXPID, SIR_PIDFORMAT, _sir_gettid());
42✔
335
                break;
36✔
336
                case 1:
21✔
337
                    (void)_sir_strncpy(search, SIR_MAXPID, thread_name, strlen(thread_name));
21✔
338
                break;
21✔
339
                default: break; // GCOVR_EXCL_LINE
340
            }
341

342
            bool found = NULL != strstr(buf, search);
63✔
343
            _sir_andeql(pass, found);
63✔
344

345
            if (found)
63✔
346
                printf("\t" GREEN("line %zu: found '%s'") "\n", n, search);
49✔
347
            else
348
                printf("\t" RED("line %zu: did not find '%s'") "\n", n, search);
5✔
349
        }
350

351
        _sir_safefclose(&f);
21✔
352
        printf("\tdeleting %s...\n", logfilename);
21✔
353
        (void)rmfile(logfilename);
21✔
354
    }
355

356
    _sir_andeql(pass, sir_cleanup());
24✔
357
    return print_result_and_return(pass);
24✔
358
}
359

360
bool sirtest_failnooutputdest(void) {
24✔
361
    INIT(si, 0, 0, 0, 0);
24✔
362
    bool pass = si_init;
21✔
363

364
    static const char* logfilename = MAKE_LOG_NAME("nodestination.log");
365

366
    _sir_andeql(pass, !sir_notice("this goes nowhere!"));
24✔
367

368
    if (pass) {
21✔
369
        print_expected_error();
20✔
370

371
        _sir_andeql(pass, sir_stdoutlevels(SIRL_INFO));
20✔
372
        _sir_andeql(pass, sir_info("this goes to stdout"));
20✔
373
        _sir_andeql(pass, sir_stdoutlevels(SIRL_NONE));
20✔
374

375
        sirfileid fid = sir_addfile(logfilename, SIRL_INFO, SIRO_DEFAULT);
20✔
376
        _sir_andeql(pass, 0U != fid);
20✔
377
        _sir_andeql(pass, sir_info("this goes to %s", logfilename));
20✔
378
        _sir_andeql(pass, sir_filelevels(fid, SIRL_NONE));
20✔
379
        _sir_andeql(pass, !sir_notice("this goes nowhere!"));
20✔
380

381
        if (0U != fid)
20✔
382
            _sir_andeql(pass, sir_remfile(fid));
18✔
383

384
        (void)rmfile(logfilename);
20✔
385
    }
386

387
    _sir_andeql(pass, sir_cleanup());
24✔
388
    return print_result_and_return(pass);
24✔
389
}
390

391
bool sirtest_failnulls(void) {
24✔
392
    INIT_BASE(si, SIRL_ALL, 0, 0, 0, "", false);
24✔
393
    bool pass = true;
21✔
394

395
    _sir_andeql(pass, !sir_init(NULL));
24✔
396

397
    if (pass)
21✔
398
        print_expected_error();
24✔
399

400
    _sir_andeql(pass, sir_init(&si));
24✔
401
    _sir_andeql(pass, !sir_info(NULL)); //-V575 //-V618
24✔
402

403
    if (pass)
21✔
404
        print_expected_error();
18✔
405

406
    _sir_andeql(pass, 0U == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY));
24✔
407

408
    if (pass)
21✔
409
        print_expected_error();
18✔
410

411
    _sir_andeql(pass, !sir_remfile(0U));
24✔
412

413
    if (pass)
21✔
414
        print_expected_error();
18✔
415

416
    _sir_andeql(pass, sir_cleanup());
24✔
417
    return print_result_and_return(pass);
24✔
418
}
419

420
bool sirtest_failemptymessage(void) {
24✔
421
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
422
    bool pass = si_init;
21✔
423

424
    _sir_andeql(pass, !sir_debug("%s", ""));
24✔
425

426
    _sir_andeql(pass, sir_cleanup());
24✔
427
    return print_result_and_return(pass);
24✔
428
}
429

430
bool sirtest_filecachesanity(void) {
24✔
431
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
432
    bool pass = si_init;
21✔
433

434
    size_t numfiles             = SIR_MAXFILES + 1;
21✔
435
    sirfileid ids[SIR_MAXFILES] = {0};
24✔
436

437
    sir_options even = SIRO_MSGONLY;
21✔
438
    sir_options odd  = SIRO_ALL;
21✔
439

440
    for (size_t n = 0; n < numfiles - 1; n++) {
408✔
441
        char path[SIR_MAXPATH] = {0};
384✔
442
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), n);
336✔
443
        (void)rmfile(path);
384✔
444
        ids[n] = sir_addfile(path, SIRL_ALL, (n % 2) ? odd : even);
408✔
445
        _sir_andeql(pass, 0U != ids[n] && sir_info("test %zu", n));
384✔
446
    }
447

448
    _sir_andeql(pass, sir_info("test test test"));
24✔
449

450
    /* this one should fail; max files already added. */
451
    _sir_andeql(pass, 0U == sir_addfile(MAKE_LOG_NAME("should-fail.log"),
24✔
452
        SIRL_ALL, SIRO_MSGONLY));
453

454
    if (pass)
21✔
455
        print_expected_error();
18✔
456

457
    sir_info("test test test");
24✔
458

459
    /* now remove previously added files in a different order. */
460
    size_t removeorder[SIR_MAXFILES];
461
    memset(removeorder, -1, sizeof(removeorder));
21✔
462

463
    long processed = 0L;
21✔
464
    printf("\tcreating random file ID order...\n");
21✔
465

466
    do {
1,153✔
467
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,336✔
468
        bool skip  = false;
1,174✔
469

470
        for (size_t n = 0; n < SIR_MAXFILES; n++)
12,792✔
471
            if (removeorder[n] == rnd) {
12,408✔
472
                skip = true;
838✔
473
                break;
838✔
474
            }
475

476
        if (skip)
1,336✔
477
            continue;
952✔
478

479
        removeorder[processed++] = rnd;
384✔
480

481
        if (processed == SIR_MAXFILES)
384✔
482
            break;
21✔
483
    } while (true);
484

485
    printf("\tremove order: {");
21✔
486
    for (size_t n = 0; n < SIR_MAXFILES; n++)
408✔
487
        printf(" %zu%s", removeorder[n], (n < SIR_MAXFILES - 1) ? "," : "");
387✔
488
    printf(" }...\n");
21✔
489

490
    for (size_t n = 0; n < SIR_MAXFILES; n++) {
408✔
491
        _sir_andeql(pass, sir_remfile(ids[removeorder[n]]));
384✔
492

493
        char path[SIR_MAXPATH] = {0};
384✔
494
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), removeorder[n]);
384✔
495
        (void)rmfile(path);
384✔
496
    }
497

498
    _sir_andeql(pass, sir_info("test test test"));
24✔
499

500
    _sir_andeql(pass, sir_cleanup());
24✔
501
    return print_result_and_return(pass);
24✔
502
}
503

504
bool sirtest_failinvalidfilename(void) {
24✔
505
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
506
    bool pass = si_init;
21✔
507

508
    _sir_andeql(pass, 0U == sir_addfile("bad file!/name", SIRL_ALL, SIRO_MSGONLY));
24✔
509

510
    if (pass)
21✔
511
        print_expected_error();
18✔
512

513
    _sir_andeql(pass, sir_cleanup());
24✔
514
    return print_result_and_return(pass);
24✔
515
}
516

517
bool sirtest_failfilebadpermission(void) {
24✔
518
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
519
    bool pass = si_init;
21✔
520

521
#if !defined(__WIN__)
522
    static const char* path = "/noperms";
523
#else /* __WIN__ */
524
# if defined(__CYGWIN__)
525
    static const char* path = "/cygdrive/c/Windows/System32/noperms";
526
# else
527
    static const char* path;
528
    if (sirtest_get_wineversion()) {
529
        path = "Z:\\noperms";
530
    } else {
531
        path = "C:\\Windows\\System32\\noperms";
532
    }
533
# endif
534
#endif
535

536
    _sir_andeql(pass, 0U == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY));
24✔
537

538
    if (pass)
21✔
539
        print_expected_error();
18✔
540

541
    _sir_andeql(pass, sir_cleanup());
24✔
542
    return print_result_and_return(pass);
24✔
543
}
544

545
bool sirtest_faildupefile(void) {
24✔
546
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
547
    bool pass = si_init;
21✔
548

549
#if !defined(__WIN__)
550
    static const char* filename1 = "./logs/faildupefile.log";
551
    static const char* filename2 = "logs/faildupefile.log";
552
#else
553
    static const char* filename1 = "logs\\faildupefile.log";
554
    static const char* filename2 = "logs/faildupefile.log";
555
#endif
556

557
    static const char* filename3 = "logs/not-a-dupe.log";
558
    static const char* filename4 = "logs/also-not-a-dupe.log";
559

560
    printf("\tadding log file '%s'...\n", filename1);
24✔
561

562
    /* should be fine; no other files added yet. */
563
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
24✔
564
    _sir_andeql(pass, 0U != fid);
24✔
565

566
    printf("\ttrying again to add log file '%s'...\n", filename1);
24✔
567

568
    /* should fail. this is the same file we already added. */
569
    _sir_andeql(pass, 0U == sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT));
24✔
570

571
    if (pass)
21✔
572
        print_expected_error();
18✔
573

574
    printf("\tadding log file '%s'...\n", filename2);
24✔
575

576
    /* should also fail. this is the same file we already added, even
577
     * if the path strings don't match. */
578
    _sir_andeql(pass, 0U == sir_addfile(filename2, SIRL_ALL, SIRO_DEFAULT));
24✔
579

580
    if (pass)
21✔
581
        print_expected_error();
18✔
582

583
    printf("\tadding log file '%s'...\n", filename3);
24✔
584

585
    /* should pass. this is a different file. */
586
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
24✔
587
    _sir_andeql(pass, 0U != fid2);
24✔
588

589
    /* should also pass. */
590
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
24✔
591
    _sir_andeql(pass, 0U != fid3);
24✔
592

593
    _sir_andeql(pass, sir_info("hello three valid files"));
24✔
594

595
    /* should now fail since we added it earlier. */
596
    _sir_andeql(pass, 0U == sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT));
24✔
597

598
    if (pass)
21✔
599
        print_expected_error();
18✔
600

601
    /* don't remove all of the log files in order to also test
602
     * cache tear-down. */
603
    _sir_andeql(pass, sir_remfile(fid));
24✔
604
    _sir_andeql(pass, sir_cleanup());
24✔
605

606
    (void)rmfile(filename1);
24✔
607
    (void)rmfile(filename2);
24✔
608
    (void)rmfile(filename3);
24✔
609
    (void)rmfile(filename4);
24✔
610

611
    return print_result_and_return(pass);
24✔
612
}
613

614
bool sirtest_failremovebadfile(void) {
24✔
615
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
616
    bool pass = si_init;
21✔
617

618
    sirfileid invalidid = 9999999;
21✔
619
    _sir_andeql(pass, !sir_remfile(invalidid));
24✔
620

621
    if (pass)
21✔
622
        print_expected_error();
18✔
623

624
    _sir_andeql(pass, sir_cleanup());
24✔
625
    return print_result_and_return(pass);
24✔
626
}
627

628
bool sirtest_rollandarchivefile(void) {
25✔
629
    /* roll size minus 1KiB so we can write until it maxes. */
630
    static const long deltasize    = 1024L;
631
    const long fillsize            = SIR_FROLLSIZE - deltasize;
22✔
632
    static const char* logbasename = "rollandarchive";
633
    static const char* logext      = ".log";
634
    static const char* line        = "hello, i am some data. nice to meet you.";
635

636
    char logfilename[SIR_MAXPATH] = {0};
25✔
637
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
25✔
638

639
    unsigned delcount = 0U;
25✔
640
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
25✔
641
        handle_os_error(false, "failed to enumerate log files with base name: %s!",
2✔
642
            logbasename);
643
        return false;
2✔
644
    }
645

646
    if (delcount > 0U)
23✔
647
        printf("\tfound and removed %u log file(s)\n", delcount);
×
648

649
    FILE* f = NULL;
23✔
650
    _sir_fopen(&f, logfilename, "w");
23✔
651

652
    if (!f)
23✔
653
        return print_os_error();
×
654

655
    if (0 != fseek(f, fillsize, SEEK_SET)) {
23✔
656
        handle_os_error(true, "fseek in file %s failed!", logfilename);
1✔
657
        _sir_safefclose(&f);
1✔
658
        return false;
1✔
659
    }
660

661
    if (EOF == fputc('\0', f)) {
22✔
662
        handle_os_error(true, "fputc in file %s failed!", logfilename);
1✔
663
        _sir_safefclose(&f);
1✔
664
        return false;
1✔
665
    }
666

667
    _sir_safefclose(&f);
21✔
668

669
    INIT(si, 0, 0, 0, 0);
21✔
670
    bool pass = si_init;
18✔
671

672
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
21✔
673
    _sir_andeql(pass, 0U != fileid);
21✔
674

675
    if (pass) {
21✔
676
        /* write an (approximately) known quantity until we should have rolled */
677
        size_t written  = 0;
13✔
678
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
16✔
679

680
        do {
681
            _sir_andeql(pass, sir_debug("%zu %s", written, line));
1,216✔
682
            written += linesize;
1,216✔
683
        } while (pass && (written < deltasize + (linesize * 50)));
1,216✔
684

685
        /* look for files matching the original name. */
686
        unsigned foundlogs = 0U;
16✔
687
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
16✔
688
            handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
689
            pass = false;
×
690
        }
691

692
        /* if two (or more) are present, the test is a pass. */
693
        printf("\tfound %u log files with base name: %s\n", foundlogs, logbasename);
16✔
694
        _sir_andeql(pass, foundlogs >= 2U);
16✔
695
    }
696

697
    _sir_andeql(pass, sir_remfile(fileid));
21✔
698

699
    delcount = 0U;
21✔
700
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
21✔
701
        handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
702
        return false;
×
703
    }
704

705
    if (delcount > 0U)
21✔
706
        printf("\tfound and removed %u log file(s)\n", delcount);
18✔
707

708
    _sir_andeql(pass, sir_cleanup());
21✔
709
    return print_result_and_return(pass);
21✔
710
}
711

712
bool sirtest_failwithoutinit(void) {
24✔
713
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
24✔
714

715
    if (pass)
24✔
716
        print_expected_error();
21✔
717

718
    return print_result_and_return(pass);
24✔
719
}
720

721
bool sirtest_failinittwice(void) {
24✔
722
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
723
    bool pass = si_init;
21✔
724

725
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
726
    _sir_andeql(pass, !si2_init);
24✔
727

728
    if (pass)
24✔
729
        print_expected_error();
16✔
730

731
    _sir_andeql(pass, sir_cleanup());
24✔
732
    return print_result_and_return(pass);
24✔
733
}
734

735
bool sirtest_failinvalidinitdata(void) {
24✔
736
    sirinit si;
737

738
    /* fill with bad data. */
739
    memset(&si, 0xab, sizeof(sirinit));
21✔
740

741
    printf("\tcalling sir_init with invalid data...\n");
21✔
742
    bool pass = !sir_init(&si);
24✔
743

744
    if (pass)
24✔
745
        print_expected_error();
24✔
746

747
    (void)sir_cleanup();
24✔
748
    return print_result_and_return(pass);
24✔
749
}
750

751
bool sirtest_initcleanupinit(void) {
24✔
752
    INIT(si1, SIRL_ALL, 0, 0, 0);
24✔
753
    bool pass = si1_init;
21✔
754

755
    _sir_andeql(pass, sir_info("init called once; testing output..."));
24✔
756
    _sir_andeql(pass, sir_cleanup());
24✔
757

758
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
759
    _sir_andeql(pass, si2_init);
24✔
760

761
    _sir_andeql(pass, sir_info("init called again after re-init; testing output..."));
24✔
762
    _sir_andeql(pass, sir_cleanup());
24✔
763

764
    return print_result_and_return(pass);
24✔
765
}
766

767
bool sirtest_initmakeinit(void) {
24✔
768
    bool pass = true;
21✔
769

770
    sirinit si;
771
    _sir_andeql(pass, sir_makeinit(&si));
24✔
772
    _sir_andeql(pass, sir_init(&si));
24✔
773
    _sir_andeql(pass, sir_info("initialized with sir_makeinit"));
24✔
774
    _sir_andeql(pass, sir_cleanup());
24✔
775

776
    return print_result_and_return(pass);
24✔
777
}
778

779
bool sirtest_failaftercleanup(void) {
24✔
780
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
781
    bool pass = si_init;
21✔
782

783
    _sir_andeql(pass, sir_cleanup());
24✔
784
    _sir_andeql(pass, !sir_info("already cleaned up; this needs to fail"));
24✔
785

786
    if (pass)
21✔
787
        print_expected_error();
23✔
788

789
    return print_result_and_return(pass);
24✔
790
}
791

792
bool sirtest_errorsanity(void) {
24✔
793
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
794
    bool pass = si_init;
21✔
795

796
    struct {
797
        uint16_t code;
798
        const char* name;
799
    } errors[] = {
24✔
800
        {SIR_E_NOERROR,   "SIR_E_NOERROR"},   /**< The operation completed successfully (0) */
801
        {SIR_E_NOTREADY,  "SIR_E_NOTREADY"},  /**< libsir has not been initialized (1) */
802
        {SIR_E_ALREADY,   "SIR_E_ALREADY"},   /**< libsir is already initialized (2) */
803
        {SIR_E_DUPITEM,   "SIR_E_DUPITEM"},   /**< Item already managed by libsir (3) */
804
        {SIR_E_NOITEM,    "SIR_E_NOITEM"},    /**< Item not managed by libsir (4) */
805
        {SIR_E_NOROOM,    "SIR_E_NOROOM"},    /**< Maximum number of items already stored (5) */
806
        {SIR_E_OPTIONS,   "SIR_E_OPTIONS"},   /**< Option flags are invalid (6) */
807
        {SIR_E_LEVELS,    "SIR_E_LEVELS"},    /**< Level flags are invalid (7) */
808
        {SIR_E_TEXTSTYLE, "SIR_E_TEXTSTYLE"}, /**< Text style is invalid (8) */
809
        {SIR_E_STRING,    "SIR_E_STRING"},    /**< Invalid string argument (9) */
810
        {SIR_E_NULLPTR,   "SIR_E_NULLPTR"},   /**< NULL pointer argument (10) */
811
        {SIR_E_INVALID,   "SIR_E_INVALID"},   /**< Invalid argument (11) */
812
        {SIR_E_NODEST,    "SIR_E_NODEST"},    /**< No destinations registered for level (12) */
813
        {SIR_E_UNAVAIL,   "SIR_E_UNAVAIL"},   /**< Feature is disabled or unavailable (13) */
814
        {SIR_E_INTERNAL,  "SIR_E_INTERNAL"},  /**< An internal error has occurred (14) */
815
        {SIR_E_COLORMODE, "SIR_E_COLORMODE"}, /**< Invalid color mode (15) */
816
        {SIR_E_TEXTATTR,  "SIR_E_TEXTATTR"},  /**< Invalid text attributes (16) */
817
        {SIR_E_TEXTCOLOR, "SIR_E_TEXTCOLOR"}, /**< Invalid text color (17) */
818
        {SIR_E_PLUGINBAD, "SIR_E_PLUGINBAD"}, /**< Plugin module is malformed (18) */
819
        {SIR_E_PLUGINDAT, "SIR_E_PLUGINDAT"}, /**< Data produced by plugin is invalid (19) */
820
        {SIR_E_PLUGINVER, "SIR_E_PLUGINVER"}, /**< Plugin interface version unsupported (20) */
821
        {SIR_E_PLUGINERR, "SIR_E_PLUGINERR"}, /**< Plugin reported failure (21) */
822
        {SIR_E_PLATFORM,  "SIR_E_PLATFORM"},  /**< Platform error code %d: %s (22) */
823
        {SIR_E_UNKNOWN,   "SIR_E_UNKNOWN"},   /**< Unknown error (4095) */
824
    };
825

826
    char message[SIR_MAXERROR] = {0};
24✔
827
    for (size_t n = 0; n < _sir_countof(errors); n++) {
600✔
828
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
576✔
829
        memset(message, 0, SIR_MAXERROR);
504✔
830
        uint16_t err = sir_geterror(message);
576✔
831
        _sir_andeql(pass, errors[n].code == err && *message != '\0');
576✔
832
        printf("\t%s = %s\n", errors[n].name, message);
576✔
833
    }
834

835
    _sir_andeql(pass, sir_cleanup());
24✔
836
    return print_result_and_return(pass);
24✔
837
}
838

839
bool sirtest_textstylesanity(void) {
24✔
840
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
841
    bool pass = si_init;
21✔
842

843
    printf("\t" WHITEB("--- explicitly invalid ---") "\n");
21✔
844
    _sir_andeql(pass, !sir_settextstyle(SIRL_INFO, (sir_textattr)0xbbb, 800, 920));
24✔
845
    _sir_andeql(pass, sir_info("I have set an invalid text style."));
24✔
846

847
    _sir_andeql(pass, !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK));
24✔
848
    _sir_andeql(pass, sir_info("oops, did it again..."));
24✔
849

850
    _sir_andeql(pass, !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff));
24✔
851
    _sir_andeql(pass, sir_info("and again."));
24✔
852
    PRINT_PASS(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
21✔
853

854
    printf("\t" WHITEB("--- unusual but valid ---") "\n");
21✔
855
    _sir_andeql(pass, sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT));
24✔
856
    _sir_andeql(pass, sir_info("system default fg and bg"));
24✔
857
    PRINT_PASS(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
21✔
858

859
    printf("\t" WHITEB("--- override defaults ---") "\n");
21✔
860
    _sir_andeql(pass, sir_resettextstyles());
24✔
861

862
    _sir_andeql(pass, sir_debug("default style"));
24✔
863
    _sir_andeql(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY));
24✔
864
    _sir_andeql(pass, sir_debug("override style"));
24✔
865

866
    _sir_andeql(pass, sir_info("default style"));
24✔
867
    _sir_andeql(pass, sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA));
24✔
868
    _sir_andeql(pass, sir_info("override style"));
24✔
869

870
    _sir_andeql(pass, sir_notice("default style"));
24✔
871
    _sir_andeql(pass, sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW));
24✔
872
    _sir_andeql(pass, sir_notice("override style"));
24✔
873

874
    _sir_andeql(pass, sir_warn("default style"));
24✔
875
    _sir_andeql(pass, sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE));
24✔
876
    _sir_andeql(pass, sir_warn("override style"));
24✔
877

878
    _sir_andeql(pass, sir_error("default style"));
24✔
879
    _sir_andeql(pass, sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE));
24✔
880
    _sir_andeql(pass, sir_error("override style"));
24✔
881

882
    _sir_andeql(pass, sir_crit("default style"));
24✔
883
    _sir_andeql(pass, sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN));
24✔
884
    _sir_andeql(pass, sir_crit("override style"));
24✔
885

886
    _sir_andeql(pass, sir_alert("default style"));
24✔
887
    _sir_andeql(pass, sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT));
24✔
888
    _sir_andeql(pass, sir_alert("override style"));
24✔
889

890
    _sir_andeql(pass, sir_emerg("default style"));
24✔
891
    _sir_andeql(pass, sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT));
24✔
892
    _sir_andeql(pass, sir_emerg("override style"));
24✔
893
    PRINT_PASS(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
21✔
894

895
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
21✔
896
    _sir_andeql(pass, sir_resettextstyles());
24✔
897

898
    _sir_andeql(pass, sir_debug("default style (debug)"));
24✔
899
    _sir_andeql(pass, sir_info("default style (info)"));
24✔
900
    _sir_andeql(pass, sir_notice("default style (notice)"));
24✔
901
    _sir_andeql(pass, sir_warn("default style (warning)"));
24✔
902
    _sir_andeql(pass, sir_error("default style (error)"));
24✔
903
    _sir_andeql(pass, sir_crit("default style (crit)"));
24✔
904
    _sir_andeql(pass, sir_alert("default style (alert)"));
24✔
905
    _sir_andeql(pass, sir_emerg("default style (emergency)"));
24✔
906
    PRINT_PASS(pass, "\t--- reset to defaults: %s ---\n\n", PRN_PASS(pass));
21✔
907

908
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
21✔
909
    _sir_andeql(pass, sir_setcolormode(SIRCM_256));
24✔
910

911
    for (sir_textcolor fg = 0, bg = 255; (fg < 256 && bg > 0); fg++, bg--) {
6,144✔
912
        if (fg != bg) {
6,120✔
913
            _sir_andeql(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
6,120✔
914
            _sir_andeql(pass, sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
6,120✔
915
                fg, bg));
916
        }
917
    }
918

919
    PRINT_PASS(pass, "\t--- change mode: 256-color: %s ---\n\n", PRN_PASS(pass));
24✔
920

921
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
21✔
922
    _sir_andeql(pass, sir_setcolormode(SIRCM_RGB));
24✔
923

924
    for (size_t n = 0; n < 256; n++) {
6,168✔
925
        sir_textcolor fg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
926
        sir_textcolor bg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
927
        _sir_andeql(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
6,144✔
928
        _sir_andeql(pass, sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
6,144✔
929
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
930
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
931
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg)));
932
    }
933
    PRINT_PASS(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
24✔
934

935
    printf("\t" WHITEB("--- change mode: invalid mode ---") "\n");
21✔
936
    _sir_andeql(pass, !sir_setcolormode(SIRCM_INVALID));
24✔
937
    sir_textcolor fg = sir_makergb(255, 0, 0);
24✔
938
    sir_textcolor bg = sir_makergb(0, 0, 0);
24✔
939
    _sir_andeql(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
24✔
940
    _sir_andeql(pass, sir_debug("this is still RGB color mode"));
24✔
941
    PRINT_PASS(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
21✔
942

943
    printf("\t" WHITEB("--- change mode: 16-color ---") "\n");
21✔
944
    _sir_andeql(pass, sir_setcolormode(SIRCM_16));
24✔
945
    _sir_andeql(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT));
24✔
946
    _sir_andeql(pass, sir_debug("this is 16-color mode (fg: %"PRId32", bg: default)",
24✔
947
        SIRTC_BMAGENTA));
948
    PRINT_PASS(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
21✔
949

950
    _sir_andeql(pass, sir_cleanup());
24✔
951

952
    return print_result_and_return(pass);
24✔
953
}
954

955
#if defined(__clang__) && !defined(__EMBARCADEROC__)
956
/* only Clang has implicit-conversion; GCC BZ#87454 */
957
SANITIZE_SUPPRESS("implicit-conversion")
958
#endif
959
bool sirtest_optionssanity(void) {
24✔
960
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
961
    bool pass = si_init;
21✔
962

963
    static const size_t iterations = 10;
964

965
    /* these should all be valid. */
966
    printf("\t" WHITEB("--- individual valid options ---") "\n");
21✔
967
    _sir_andeql(pass, _sir_validopts(SIRO_ALL));
24✔
968
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
21✔
969
    _sir_andeql(pass, _sir_validopts(SIRO_NOTIME));
24✔
970
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
21✔
971
    _sir_andeql(pass, _sir_validopts(SIRO_NOHOST));
24✔
972
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
21✔
973
    _sir_andeql(pass, _sir_validopts(SIRO_NOLEVEL));
24✔
974
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
21✔
975
    _sir_andeql(pass, _sir_validopts(SIRO_NONAME));
24✔
976
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
21✔
977
    _sir_andeql(pass, _sir_validopts(SIRO_NOPID));
24✔
978
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
21✔
979
    _sir_andeql(pass, _sir_validopts(SIRO_NOTID));
24✔
980
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
21✔
981
    _sir_andeql(pass, _sir_validopts(SIRO_NOHDR));
24✔
982
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
21✔
983
    _sir_andeql(pass, _sir_validopts(SIRO_MSGONLY));
24✔
984
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
21✔
985
    PRINT_PASS(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
24✔
986

987
    /* any combination these bitwise OR'd together
988
       to form a bitmask should also be valid. */
989
    static const sir_option option_arr[SIR_NUMOPTIONS] = {
990
        SIRO_NOTIME,
991
        SIRO_NOHOST,
992
        SIRO_NOLEVEL,
993
        SIRO_NONAME,
994
        SIRO_NOMSEC,
995
        SIRO_NOPID,
996
        SIRO_NOTID,
997
        SIRO_NOHDR
998
    };
999

1000
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
21✔
1001
    uint32_t last_count = SIR_NUMOPTIONS;
21✔
1002
    for (size_t n = 0; n < iterations; n++) {
264✔
1003
        sir_options opts    = 0;
210✔
1004
        uint32_t rand_count = 0;
210✔
1005
        size_t last_idx     = 0;
210✔
1006

1007
        do {
1008
            rand_count = getrand(SIR_NUMOPTIONS);
343✔
1009
        } while (rand_count == last_count || rand_count <= 1);
343✔
1010

1011
        last_count = rand_count;
210✔
1012

1013
        for (size_t i = 0; i < rand_count; i++) {
1,268✔
1014
            size_t rand_idx = 0;
899✔
1015
            size_t tries    = 0;
899✔
1016

1017
            do {
1018
                if (++tries > SIR_NUMOPTIONS - 2)
1,728✔
1019
                    break;
×
1020
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
1,728✔
1021
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,728✔
1022

1023
            last_idx = rand_idx;
899✔
1024
            opts |= option_arr[rand_idx];
1,028✔
1025
        }
1026

1027
        _sir_andeql(pass, _sir_validopts(opts));
240✔
1028
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
240✔
1029
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
1030
    }
1031
    PRINT_PASS(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
24✔
1032

1033
    printf("\t" WHITEB("--- invalid values ---") "\n");
21✔
1034

1035
    /* the lowest byte is not valid. */
1036
    sir_options invalid = 0x000000ff;
21✔
1037
    _sir_andeql(pass, !_sir_validopts(invalid));
24✔
1038
    printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
21✔
1039

1040
    /* gaps inbetween valid options. */
1041
    invalid = 0x0001ff00U & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
21✔
1042
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
1043
    _sir_andeql(pass, !_sir_validopts(invalid));
24✔
1044
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00U: %08"PRIx32) "\n", invalid);
21✔
1045

1046
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
1047
    for (sir_option o = 0x00008f00U; o < SIRO_NOHDR; o += 0x1000U) {
216✔
1048
        _sir_andeql(pass, !_sir_validopts(o));
192✔
1049
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
168✔
1050
    }
1051

1052
    /* greater than SIRO_NOHDR. */
1053
    invalid = (0xFFFF0000 & ~SIRO_NOHDR); /* implicit-conversion */
21✔
1054
    _sir_andeql(pass, !_sir_validopts(invalid));
24✔
1055
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
21✔
1056

1057
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
24✔
1058

1059
    _sir_andeql(pass, sir_cleanup());
24✔
1060
    return print_result_and_return(pass);
24✔
1061
}
1062

1063
bool sirtest_levelssanity(void) {
24✔
1064
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1065
    bool pass = si_init;
21✔
1066

1067
    static const size_t iterations = 10;
1068

1069
    /* these should all be valid. */
1070
    printf("\t" WHITEB("--- individual valid levels ---") "\n");
21✔
1071
    _sir_andeql(pass, _sir_validlevel(SIRL_INFO) && _sir_validlevels(SIRL_INFO));
24✔
1072
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_INFO);
21✔
1073
    _sir_andeql(pass, _sir_validlevel(SIRL_DEBUG) && _sir_validlevels(SIRL_DEBUG));
24✔
1074
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_DEBUG);
21✔
1075
    _sir_andeql(pass, _sir_validlevel(SIRL_NOTICE) && _sir_validlevels(SIRL_NOTICE));
24✔
1076
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_NOTICE);
21✔
1077
    _sir_andeql(pass, _sir_validlevel(SIRL_WARN) && _sir_validlevels(SIRL_WARN));
24✔
1078
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_WARN);
21✔
1079
    _sir_andeql(pass, _sir_validlevel(SIRL_ERROR) && _sir_validlevels(SIRL_ERROR));
24✔
1080
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ERROR);
21✔
1081
    _sir_andeql(pass, _sir_validlevel(SIRL_CRIT) && _sir_validlevels(SIRL_CRIT));
24✔
1082
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_CRIT);
21✔
1083
    _sir_andeql(pass, _sir_validlevel(SIRL_ALERT) && _sir_validlevels(SIRL_ALERT));
24✔
1084
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ALERT);
21✔
1085
    _sir_andeql(pass, _sir_validlevel(SIRL_EMERG) && _sir_validlevels(SIRL_EMERG));
24✔
1086
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_EMERG);
21✔
1087
    _sir_andeql(pass, _sir_validlevels(SIRL_ALL));
24✔
1088
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_ALL);
21✔
1089
    _sir_andeql(pass, _sir_validlevels(SIRL_NONE));
24✔
1090
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_NONE);
21✔
1091
    PRINT_PASS(pass, "\t--- individual valid levels: %s ---\n\n", PRN_PASS(pass));
24✔
1092

1093
    /* any combination these bitwise OR'd together
1094
       to form a bitmask should also be valid. */
1095
    static const sir_levels levels_arr[SIR_NUMLEVELS] = {
1096
        SIRL_EMERG,
1097
        SIRL_ALERT,
1098
        SIRL_CRIT,
1099
        SIRL_ERROR,
1100
        SIRL_WARN,
1101
        SIRL_NOTICE,
1102
        SIRL_INFO,
1103
        SIRL_DEBUG
1104
    };
1105

1106
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
21✔
1107
    uint32_t last_count = SIR_NUMLEVELS;
21✔
1108
    for (size_t n = 0; n < iterations; n++) {
264✔
1109
        sir_levels levels   = 0U;
210✔
1110
        uint32_t rand_count = 0U;
210✔
1111
        size_t last_idx     = 0UL;
210✔
1112

1113
        do {
1114
            rand_count = getrand(SIR_NUMLEVELS);
466✔
1115
        } while (rand_count == last_count || rand_count <= 1U);
466✔
1116

1117
        last_count = rand_count;
210✔
1118

1119
        for (size_t i = 0; i < rand_count; i++) {
1,411✔
1120
            size_t rand_idx = 0;
1,024✔
1121
            size_t tries    = 0;
1,024✔
1122

1123
            do {
1124
                if (++tries > SIR_NUMLEVELS - 2)
1,911✔
1125
                    break;
33✔
1126
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,872✔
1127
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,872✔
1128

1129
            last_idx = rand_idx;
1,024✔
1130
            levels |= levels_arr[rand_idx];
1,171✔
1131
        }
1132

1133
        _sir_andeql(pass, _sir_validlevels(levels));
240✔
1134
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
240✔
1135
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
1136
    }
1137
    PRINT_PASS(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
24✔
1138

1139
    printf("\t" WHITEB("--- invalid values ---") "\n");
21✔
1140

1141
    /* greater than SIRL_ALL. */
1142
    sir_levels invalid = (0xffffu & ~SIRL_ALL);
21✔
1143
    _sir_andeql(pass, !_sir_validlevels(invalid));
24✔
1144
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
21✔
1145

1146
    /* individual invalid level. */
1147
    sir_level invalid2 = 0x1337U;
21✔
1148
    _sir_andeql(pass, !_sir_validlevel(invalid2));
24✔
1149
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx16) "\n", invalid2);
21✔
1150

1151
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
24✔
1152

1153
    _sir_andeql(pass, sir_cleanup());
24✔
1154
    return print_result_and_return(pass);
24✔
1155
}
1156

1157
bool sirtest_mutexsanity(void) {
24✔
1158
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1159
    bool pass = si_init;
21✔
1160

1161
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
21✔
1162
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
21✔
1163

1164
    sir_mutex m1 = SIR_MUTEX_INIT;
24✔
1165
    _sir_andeql(pass, _sir_mutexcreate(&m1));
24✔
1166

1167
    print_test_error(pass, pass);
24✔
1168

1169
    if (pass) {
24✔
1170
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
20✔
1171
        _sir_andeql(pass, _sir_mutexlock(&m1));
23✔
1172

1173
        print_test_error(pass, pass);
23✔
1174

1175
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
20✔
1176
        _sir_andeql(pass, _sir_mutexunlock(&m1));
23✔
1177

1178
        print_test_error(pass, pass);
23✔
1179

1180
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
20✔
1181
        _sir_andeql(pass, _sir_mutextrylock(&m1));
23✔
1182

1183
        print_test_error(pass, pass);
23✔
1184

1185
        printf(INDENT_ITEM WHITE("unlocking...") "\n");
20✔
1186
        _sir_andeql(pass, _sir_mutexunlock(&m1));
23✔
1187

1188
        print_test_error(pass, pass);
23✔
1189

1190
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
20✔
1191
        _sir_andeql(pass, _sir_mutexdestroy(&m1));
23✔
1192

1193
        print_test_error(pass, pass);
23✔
1194

1195
    }
1196
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
24✔
1197

1198
    printf("\t" WHITEB("invalid arguments") "\n");
21✔
1199
    printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
21✔
1200
    _sir_andeql(pass, !_sir_mutexcreate(NULL));
24✔
1201
    printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
21✔
1202
    _sir_andeql(pass, !_sir_mutexlock(NULL));
24✔
1203
    printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
21✔
1204
    _sir_andeql(pass, !_sir_mutextrylock(NULL));
24✔
1205
    printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
21✔
1206
    _sir_andeql(pass, !_sir_mutexunlock(NULL));
24✔
1207
    printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
21✔
1208
    _sir_andeql(pass, !_sir_mutexdestroy(NULL));
24✔
1209
    PRINT_PASS(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
21✔
1210

1211
    _sir_andeql(pass, sir_cleanup());
24✔
1212
    return print_result_and_return(pass); // -V1020
24✔
1213
}
1214

1215
bool sirtest_perf(void) {
1✔
1216
    static const char* logbasename = "libsir-perf";
1217
    static const char* logext      = "";
1218

1219
#if !defined(DUMA)
1220
# if !defined(SIR_PERF_PROFILE)
1221
#  if !defined(__WIN__)
1222
    static const size_t perflines = 1000000;
1223
#  else
1224
    static const size_t perflines = 100000;
1225
#  endif
1226
# else
1227
    static const size_t perflines = 4000000;
1228
# endif
1229
#else /* DUMA */
1230
    static const size_t perflines = 100000;
1231
#endif
1232

1233
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
1✔
1234
    bool pass = si_init;
1✔
1235

1236
    if (pass) {
1✔
1237
        double stdioelapsed  = 0.0;
1✔
1238
        double fileelapsed   = 0.0;
1✔
1239
#if !defined(SIR_PERF_PROFILE)
1240
        double printfelapsed = 0.0;
1✔
1241

1242
        printf("\t" BLUE("%zu lines printf...") "\n", perflines);
1✔
1243

1244
        sir_time printftimer = {0};
1✔
1245
        sir_timer_start(&printftimer);
1✔
1246

1247
        for (size_t n = 0; n < perflines; n++)
1,000,001✔
1248
            printf(WHITE("%.2f: lorem ipsum foo bar %s: %zu") "\n",
1,000,000✔
1249
                sir_timer_elapsed(&printftimer), "baz", 1234 + n);
1250

1251
        printfelapsed = sir_timer_elapsed(&printftimer);
1✔
1252
#endif
1253

1254
        printf("\t" BLUE("%zu lines libsir (stdout)...") "\n", perflines);
1✔
1255

1256
        sir_time stdiotimer = {0};
1✔
1257
        sir_timer_start(&stdiotimer);
1✔
1258

1259
        for (size_t n = 0; n < perflines; n++)
1,000,001✔
1260
            sir_debug("%.2f: lorem ipsum foo bar %s: %zu",
1,000,000✔
1261
                sir_timer_elapsed(&stdiotimer), "baz", 1234 + n);
1262

1263
        stdioelapsed = sir_timer_elapsed(&stdiotimer);
1✔
1264

1265
        _sir_andeql(pass, sir_cleanup());
1✔
1266

1267
        INIT(si2, 0, 0, 0, 0);
1✔
1268
        _sir_andeql(pass, si2_init);
1✔
1269

1270
        char logfilename[SIR_MAXPATH] = {0};
1✔
1271
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
1✔
1272

1273
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
1✔
1274
        _sir_andeql(pass, 0 != logid);
1✔
1275

1276
        if (pass) {
1✔
1277
            printf("\t" BLUE("%zu lines libsir (file)...") "\n", perflines);
1✔
1278

1279
            sir_time filetimer = {0};
1✔
1280
            sir_timer_start(&filetimer);
1✔
1281

1282
            for (size_t n = 0; n < perflines; n++)
1,000,001✔
1283
                sir_debug("lorem ipsum foo bar %s: %zu", "baz", 1234 + n);
1,000,000✔
1284

1285
            fileelapsed = sir_timer_elapsed(&filetimer);
1✔
1286

1287
            _sir_andeql(pass, sir_remfile(logid));
1✔
1288
        }
1289

1290
        if (pass) {
1✔
1291
#if !defined(SIR_PERF_PROFILE)
1292
            printf("\t" WHITEB("printf: ") CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1✔
1293
                perflines, printfelapsed / 1e3, (double)perflines / (printfelapsed / 1e3));
1✔
1294
#endif
1295
            printf("\t" WHITEB("libsir (stdout): ")
1✔
1296
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n", perflines,
1297
                    stdioelapsed / 1e3, (double)perflines / (stdioelapsed / 1e3));
1✔
1298

1299
            printf("\t" WHITEB("libsir (file): ")
1✔
1300
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n", perflines,
1301
                    fileelapsed / 1e3, (double)perflines / (fileelapsed / 1e3));
1✔
1302

1303
            printf("\t" WHITEB("timer resolution: ") CYAN("~%ldnsec") "\n", sir_timer_getres());
1✔
1304
        }
1305
    }
1306

1307
    unsigned deleted = 0U;
1✔
1308
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
1✔
1309

1310
    if (deleted > 0U)
1✔
1311
        printf("\t" DGRAY("deleted %u log file(s)") "\n", deleted);
1✔
1312

1313
    _sir_andeql(pass, sir_cleanup());
1✔
1314
    return print_result_and_return(pass);
1✔
1315
}
1316

1317
bool sirtest_updatesanity(void) {
24✔
1318
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
24✔
1319
    bool pass = si_init;
21✔
1320

1321
#define UPDATE_SANITY_ARRSIZE 10
1322

1323
    static const char* logfile = MAKE_LOG_NAME("update-sanity.log");
1324
    static const sir_options opts_array[UPDATE_SANITY_ARRSIZE] = {
1325
        SIRO_NOHOST | SIRO_NOTIME | SIRO_NOLEVEL,
1326
        SIRO_MSGONLY, SIRO_NONAME | SIRO_NOTID,
1327
        SIRO_NOPID | SIRO_NOTIME,
1328
        SIRO_NOTIME | SIRO_NOLEVEL | SIRO_NONAME,
1329
        SIRO_NOTIME, SIRO_NOMSEC | SIRO_NOHOST,
1330
        SIRO_NOPID | SIRO_NOTID,
1331
        SIRO_NOHOST | SIRO_NOTID, SIRO_ALL
1332
    };
1333

1334
    static const sir_levels levels_array[UPDATE_SANITY_ARRSIZE] = {
1335
        SIRL_NONE, SIRL_ALL, SIRL_EMERG, SIRL_ALERT,
1336
        SIRL_CRIT, SIRL_ERROR, SIRL_WARN, SIRL_NOTICE,
1337
        SIRL_INFO, SIRL_DEBUG
1338
    };
1339

1340
    (void)rmfile(logfile);
24✔
1341
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
24✔
1342
    _sir_andeql(pass, 0 != id1);
24✔
1343

1344
    for (int i = 0; i < 10; i++) {
224✔
1345
        if (!pass)
204✔
1346
            break;
4✔
1347

1348
        /* reset to defaults*/
1349
        _sir_andeql(pass, sir_stdoutlevels(SIRL_DEFAULT));
200✔
1350
        _sir_andeql(pass, sir_stderrlevels(SIRL_DEFAULT));
200✔
1351
        _sir_andeql(pass, sir_stdoutopts(SIRO_DEFAULT));
200✔
1352
        _sir_andeql(pass, sir_stderropts(SIRO_DEFAULT));
200✔
1353

1354
        _sir_andeql(pass, sir_debug("default config (debug)"));
200✔
1355
        _sir_andeql(pass, sir_info("default config (info)"));
200✔
1356
        _sir_andeql(pass, sir_notice("default config (notice)"));
200✔
1357
        _sir_andeql(pass, sir_warn("default config (warning)"));
200✔
1358
        _sir_andeql(pass, sir_error("default config (error)"));
200✔
1359
        _sir_andeql(pass, sir_crit("default config (critical)"));
200✔
1360
        _sir_andeql(pass, sir_alert("default config (alert)"));
200✔
1361
        _sir_andeql(pass, sir_emerg("default config (emergency)"));
200✔
1362

1363
        /* pick random options to set/unset */
1364
        uint32_t rnd = getrand(UPDATE_SANITY_ARRSIZE);
200✔
1365
        _sir_andeql(pass, sir_stdoutlevels(levels_array[rnd]));
200✔
1366
        _sir_andeql(pass, sir_stdoutopts(opts_array[rnd]));
200✔
1367
        printf("\t" WHITE("set random config #%"PRIu32" for stdout") "\n", rnd);
170✔
1368

1369
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
200✔
1370
        _sir_andeql(pass, sir_stderrlevels(levels_array[rnd]));
200✔
1371
        _sir_andeql(pass, sir_stderropts(opts_array[rnd]));
200✔
1372
        printf("\t" WHITE("set random config #%"PRIu32" for stderr") "\n", rnd);
170✔
1373

1374
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
200✔
1375
        _sir_andeql(pass, sir_filelevels(id1, levels_array[rnd]));
200✔
1376
        _sir_andeql(pass, sir_fileopts(id1, opts_array[rnd]));
200✔
1377
        printf("\t" WHITE("set random config #%"PRIu32" for %s") "\n", rnd, logfile);
200✔
1378

1379
        _sir_andeql(pass, filter_error(sir_debug("modified config #%"PRIu32" (debug)", rnd), SIR_E_NODEST));
200✔
1380
        _sir_andeql(pass, filter_error(sir_info("modified config #%"PRIu32" (info)", rnd), SIR_E_NODEST));
200✔
1381
        _sir_andeql(pass, filter_error(sir_notice("modified config #%"PRIu32" (notice)", rnd), SIR_E_NODEST));
200✔
1382
        _sir_andeql(pass, filter_error(sir_warn("modified config #%"PRIu32" (warning)", rnd), SIR_E_NODEST));
200✔
1383
        _sir_andeql(pass, filter_error(sir_error("modified config #%"PRIu32" (error)", rnd), SIR_E_NODEST));
200✔
1384
        _sir_andeql(pass, filter_error(sir_crit("modified config #%"PRIu32" (critical)", rnd), SIR_E_NODEST));
200✔
1385
        _sir_andeql(pass, filter_error(sir_alert("modified config #%"PRIu32" (alert)", rnd), SIR_E_NODEST));
200✔
1386
        _sir_andeql(pass, filter_error(sir_emerg("modified config #%"PRIu32" (emergency)", rnd), SIR_E_NODEST));
200✔
1387
    }
1388

1389
    if (pass) {
24✔
1390
        /* restore to default config and run again */
1391
        sir_stdoutlevels(SIRL_DEFAULT);
20✔
1392
        sir_stderrlevels(SIRL_DEFAULT);
20✔
1393
        sir_stdoutopts(SIRO_DEFAULT);
20✔
1394
        sir_stderropts(SIRO_DEFAULT);
20✔
1395

1396
        _sir_andeql(pass, sir_debug("default config (debug)"));
20✔
1397
        _sir_andeql(pass, sir_info("default config (info)"));
20✔
1398
        _sir_andeql(pass, sir_notice("default config (notice)"));
20✔
1399
        _sir_andeql(pass, sir_warn("default config (warning)"));
20✔
1400
        _sir_andeql(pass, sir_error("default config (error)"));
20✔
1401
        _sir_andeql(pass, sir_crit("default config (critical)"));
20✔
1402
        _sir_andeql(pass, sir_alert("default config (alert)"));
20✔
1403
        _sir_andeql(pass, sir_emerg("default config (emergency)"));
20✔
1404
    }
1405

1406
    _sir_andeql(pass, sir_remfile(id1));
24✔
1407
    (void)rmfile(logfile);
24✔
1408

1409
    _sir_andeql(pass, sir_cleanup());
24✔
1410
    return print_result_and_return(pass);
24✔
1411
}
1412

1413
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1414
static
1415
bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
18✔
1416
    static const int runs = 5;
1417

1418
    /* repeat initializing, opening, logging, closing, cleaning up n times. */
1419
    printf("\trunning %d passes of random configs (system logger: '%s', "
15✔
1420
           "identity: '%s', category: '%s')...\n", runs, sl_name, identity, category);
1421

1422
# if !defined(__WIN__)
1423
    uint32_t rnd = (uint32_t)(_sir_getpid() + _sir_gettid());
18✔
1424
# else
1425
    uint32_t rnd = (uint32_t)GetTickCount();
1426
# endif
1427

1428
    bool pass = true;
15✔
1429
    for (int i = 1; i <= runs; i++) {
98✔
1430
        /* randomly skip setting process name, identity/category to thoroughly
1431
         * test fallback routines; randomly update the config mid-run. */
1432
        bool set_procname = getrand_bool(rnd ^ 0x5a5a5a5aU);
82✔
1433
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9U);
82✔
1434
        bool set_category = getrand_bool(rnd ^ 0x32323232U);
82✔
1435
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7U);
82✔
1436

1437
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
82✔
1438
            set_procname, set_identity, set_category, do_update);
1439

1440
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
91✔
1441
        si.d_syslog.opts   = SIRO_DEFAULT;
82✔
1442
        si.d_syslog.levels = SIRL_DEFAULT;
82✔
1443

1444
        if (set_identity)
82✔
1445
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
48✔
1446

1447
        if (set_category)
82✔
1448
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
48✔
1449

1450
        si_init = sir_init(&si); //-V519
82✔
1451
        _sir_andeql(pass, si_init);
82✔
1452

1453
        if (do_update)
82✔
1454
            _sir_andeql(pass, sir_sysloglevels(SIRL_ALL));
34✔
1455

1456
        _sir_andeql(pass, sir_debug("%d/%d: this debug message sent to stdout and %s.", i, runs, sl_name));
82✔
1457
        _sir_andeql(pass, sir_info("%d/%d: this info message sent to stdout and %s.", i, runs, sl_name));
82✔
1458

1459
        _sir_andeql(pass, sir_notice("%d/%d: this notice message sent to stdout and %s.", i, runs, sl_name));
82✔
1460
        _sir_andeql(pass, sir_warn("%d/%d: this warning message sent to stdout and %s.", i, runs, sl_name));
82✔
1461
        _sir_andeql(pass, sir_error("%d/%d: this error message sent to stdout and %s.", i, runs, sl_name));
82✔
1462

1463
        if (set_identity) {
82✔
1464
            _sir_andeql(pass, sir_syslogid("my test ID"));
48✔
1465
            _sir_andeql(pass, sir_syslogid("my test ID")); /* test deduping. */
48✔
1466
        }
1467

1468
        if (set_category) {
82✔
1469
            _sir_andeql(pass, sir_syslogcat("my test category"));
48✔
1470
            _sir_andeql(pass, sir_syslogcat("my test category")); /* test deduping. */
48✔
1471
        }
1472

1473
        if (do_update)
82✔
1474
            _sir_andeql(pass, sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID)));
34✔
1475

1476
        _sir_andeql(pass, sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name));
82✔
1477
        _sir_andeql(pass, sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name));
82✔
1478
        _sir_andeql(pass, sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name));
82✔
1479

1480
# if defined(SIR_OS_LOG_ENABLED)
1481
#  if defined(__MACOS__) && !defined(__INTEL_COMPILER)
1482
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1483
            printf("\ttesting os_log activity feature...\n");
1484

1485
            /* also test activity grouping in Console. there's only one way to validate
1486
             * this and that's by manually viewing the log. */
1487
             os_activity_t parent = os_activity_create("flying to the moon", // -V530
1488
                OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DETACHED);
1489

1490
            /* execution now passes to os_log_parent_activity(), where some logging
1491
            * will occur, then a sub-activity will be created, and more logging. */
1492
            os_activity_apply_f(parent, (void*)parent, os_log_parent_activity);
1493
        }
1494
#  endif
1495
# endif
1496

1497
        _sir_andeql(pass, sir_cleanup());
82✔
1498

1499
        if (!pass)
82✔
1500
            break;
2✔
1501
    }
1502

1503
    return print_result_and_return(pass);
18✔
1504
}
1505
#endif
1506

1507
#if defined(SIR_NO_SYSTEM_LOGGERS)
1508
static bool generic_disabled_syslog_test(const char* sl_name, const char* identity,
6✔
1509
    const char* category) {
1510
    INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0U, 0U, "sir_disabled_sltest");
6✔
1511
    si.d_syslog.opts   = SIRO_DEFAULT;
6✔
1512
    si.d_syslog.levels = SIRL_DEFAULT;
6✔
1513
    bool pass = true;
6✔
1514

1515
    SIR_UNUSED(sl_name);
1516

1517
    printf("\tSIR_NO_SYSTEM_LOGGERS is defined; expecting calls to fail...\n");
6✔
1518

1519
    /* init should just ignore the syslog settings. */
1520
    _sir_andeql(pass, sir_init(&si));
6✔
1521

1522
    /* these calls should all fail. */
1523
    printf("\tsetting levels...\n");
6✔
1524
    _sir_andeql(pass, !sir_sysloglevels(SIRL_ALL));
6✔
1525

1526
    if (pass)
6✔
1527
        print_expected_error();
3✔
1528

1529
    printf("\tsetting options...\n");
6✔
1530
    _sir_andeql(pass, !sir_syslogopts(SIRO_DEFAULT));
6✔
1531

1532
    if (pass)
6✔
1533
        print_expected_error();
3✔
1534

1535
    printf("\tsetting identity...\n");
6✔
1536
    _sir_andeql(pass, !sir_syslogid(identity));
6✔
1537

1538
    if (pass)
6✔
1539
        print_expected_error();
3✔
1540

1541
    printf("\tsetting category...\n");
6✔
1542
    _sir_andeql(pass, !sir_syslogcat(category));
6✔
1543

1544
    if (pass)
6✔
1545
        print_expected_error();
3✔
1546

1547
    _sir_andeql(pass, sir_cleanup());
6✔
1548
    return print_result_and_return(pass);
6✔
1549
}
1550
#endif
1551

1552
bool sirtest_syslog(void) {
24✔
1553
#if !defined(SIR_SYSLOG_ENABLED)
1554
# if defined(SIR_NO_SYSTEM_LOGGERS)
1555
    bool pass = generic_disabled_syslog_test("syslog", "sirtests", "tests");
6✔
1556
    return print_result_and_return(pass);
6✔
1557
# else
1558
    printf("\t" DGRAY("SIR_SYSLOG_ENABLED is not defined; skipping") "\n");
1559
    return true;
1560
# endif
1561
#else
1562
    bool pass = generic_syslog_test("syslog", "sirtests", "tests");
18✔
1563
    return print_result_and_return(pass);
18✔
1564
#endif
1565
}
1566

1567
bool sirtest_os_log(void) {
24✔
1568
#if !defined(SIR_OS_LOG_ENABLED)
1569
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
21✔
1570
    return true;
24✔
1571
#else
1572
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1573
    return print_result_and_return(pass);
1574
#endif
1575
}
1576

1577
char *sirtest_get_wineversion(void) {
48✔
1578
#if !defined(__WIN__)
1579
    return NULL;
48✔
1580
#else /* __WIN__ */
1581
    typedef char* (__stdcall *get_wine_ver_proc)(void);
1582
    static get_wine_ver_proc _p_wine_get_version = NULL;
1583

1584
    HMODULE _h_ntdll = GetModuleHandle("ntdll.dll");
1585
    if (_h_ntdll != NULL) {
1586
        _p_wine_get_version = (get_wine_ver_proc)GetProcAddress(_h_ntdll, "wine_get_version");
1587
        if (_p_wine_get_version) {
1588
            char *wine_version = _p_wine_get_version();
1589
            if (wine_version)
1590
                return wine_version;
1591
        }
1592
    }
1593
    return NULL;
1594
#endif
1595
}
1596

1597
bool sirtest_filesystem(void) {
24✔
1598
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1599
    bool pass = si_init;
21✔
1600

1601
    /* Wine version */
1602
    printf("\tRunning under Wine: %s\n",
24✔
1603
            sirtest_get_wineversion() ? sirtest_get_wineversion() : "no"); //-V547
24✔
1604

1605
    /* current working directory. */
1606
    char* cwd = _sir_getcwd();
24✔
1607
    _sir_andeql(pass, NULL != cwd);
24✔
1608
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
24✔
1609

1610
    if (NULL != cwd) {
24✔
1611
        /* path to this binary file. */
1612
        char* filename = _sir_getappfilename();
24✔
1613
        _sir_andeql(pass, NULL != filename);
24✔
1614
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
24✔
1615

1616
        if (NULL != filename) {
24✔
1617
            /* _sir_get[base|dir]name() can potentially modify filename,
1618
             * so make a copy for each call. */
1619
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
21✔
1620
            _sir_andeql(pass, NULL != filename2);
21✔
1621

1622
            if (NULL != filename2) {
21✔
1623
                /* filename, stripped of directory component(s). */
1624
                char* _basename = _sir_getbasename(filename2);
20✔
1625
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
20✔
1626

1627
                if (!_basename) {
20✔
1628
                    pass = false;
×
1629
                } else {
1630
                    /* the last strlen(_basename) chars of filename should match. */
1631
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
20✔
1632
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
20✔
1633
                    size_t n      = 0;
17✔
1634

1635
                    while (n < len) {
180✔
1636
                        if (filename[offset++] != _basename[n++]) {
160✔
1637
                            pass = false;
×
1638
                            break;
×
1639
                        }
1640
                    }
1641
                }
1642
            }
1643

1644
            /* directory this binary file resides in. */
1645
            char* appdir = _sir_getappdir();
21✔
1646
            _sir_andeql(pass, NULL != appdir);
21✔
1647
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
21✔
1648

1649
            /* _sir_get[base|dir]name can potentially modify filename,
1650
             * so make a copy for each call. */
1651
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
21✔
1652
            _sir_andeql(pass, NULL != filename3);
21✔
1653

1654
            if (NULL != appdir && NULL != filename3) {
21✔
1655
                /* should yield the same result as _sir_getappdir(). */
1656
                char* _dirname = _sir_getdirname(filename3);
20✔
1657
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
20✔
1658

1659
                _sir_andeql(pass, 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH)));
20✔
1660
                _sir_andeql(pass, NULL != _dirname &&
20✔
1661
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH)));
1662
            }
1663

1664
            _sir_safefree(&appdir);
21✔
1665
            _sir_safefree(&filename);
21✔
1666
            _sir_safefree(&filename2);
21✔
1667
            _sir_safefree(&filename3);
21✔
1668
        }
1669

1670
        _sir_safefree(&cwd);
24✔
1671
    }
1672

1673
    /* this next section doesn't really yield any useful boolean pass/fail
1674
     * information, but could be helpful to review manually. */
1675
    char* dubious_dirnames[] = {
24✔
1676
#if !defined(__WIN__)
1677
        "/foo",
1678
        "/foo/",
1679
        "/foo/bar",
1680
        "/foo/bar/bad:filename",
1681
        "/",
1682
        ""
1683
#else /* __WIN__ */
1684
        "C:\\foo",
1685
        "C:\\foo\\",
1686
        "C:\\foo\\bar",
1687
        "C:\\foo\\bar\\bad:>filename",
1688
        "C:\\",
1689
        "//network-share/myfolder",
1690
        ""
1691
#endif
1692
    };
1693

1694
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
168✔
1695
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
144✔
1696
        if (NULL != tmp) {
144✔
1697
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
138✔
1698
                tmp, _sir_getdirname(tmp));
1699
            _sir_safefree(&tmp);
138✔
1700
        }
1701
    }
1702

1703
    char* dubious_filenames[] = {
24✔
1704
#if !defined(__WIN__)
1705
        "foo/bar/file-or-directory",
1706
        "/foo/bar/file-or-directory",
1707
        "/foo/bar/illegal:filename",
1708
        "/",
1709
        ""
1710
#else /* __WIN__ */
1711
        "foo\\bar\\file.with.many.full.stops",
1712
        "C:\\foo\\bar\\poorly-renamed.txt.pdf",
1713
        "C:\\foo\\bar\\illegal>filename.txt",
1714
        "C:\\",
1715
        "\\Program Files\\foo.bar",
1716
        ""
1717
#endif
1718
    };
1719

1720
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
144✔
1721
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
120✔
1722
        if (NULL != tmp) {
120✔
1723
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
115✔
1724
                tmp, _sir_getbasename(tmp));
1725
            _sir_safefree(&tmp);
115✔
1726
        }
1727
    }
1728

1729
    /* absolute/relative paths. */
1730
    static const struct {
1731
        const char* const path;
1732
        bool abs;
1733
    } abs_or_rel_paths[] = {
1734
        {"this/is/relative", false},
1735
        {"relative", false},
1736
        {"./relative", false},
1737
        {"../../relative", false},
1738
#if !defined(__WIN__)
1739
        {"/usr/local/bin", true},
1740
        {"/", true},
1741
        {"/home/foo/.config", true},
1742
        {"~/.config", true}
1743
#else /* __WIN__ */
1744
        {"D:\\absolute", true},
1745
        {"C:\\Program Files\\FooBar", true},
1746
        {"C:\\", true},
1747
        {"\\absolute", true},
1748
#endif
1749
    };
1750

1751
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
216✔
1752
        bool relative = false;
192✔
1753
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
192✔
1754

1755
        if (relative == abs_or_rel_paths[n].abs) {
192✔
1756
            pass = false;
×
1757
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1758
                relative ? "true" : "false");
×
1759
        } else {
1760
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
192✔
1761
                relative ? "true" : "false");
168✔
1762
        }
1763

1764
        _sir_andeql(pass, ret);
192✔
1765
        if (!ret) {
192✔
1766
            bool unused = print_test_error(false, false);
×
1767
            SIR_UNUSED(unused);
1768
        }
1769
    }
1770

1771
    /* file existence. */
1772
    static const struct {
1773
        const char* const path;
1774
        bool exists;
1775
    } real_or_not[] = {
1776
        {"../foobarbaz", false},
1777
        {"foobarbaz", false},
1778
#if !defined(__WIN__)
1779
        {"/", true},
1780
# if !defined(__HAIKU__)
1781
        {"/usr/bin", true},
1782
# else
1783
        {"/bin", true},
1784
# endif
1785
        {"/dev", true},
1786
#else /* __WIN__ */
1787
        {"C:\\Windows", true},
1788
        {"C:\\Program Files", true},
1789
        {"\\", true},
1790
        {".\\", true},
1791
        {"..\\", true},
1792
#endif
1793
        {"../../LICENSES/MIT.txt", true},
1794
        {"../../msvs/libsir.sln", true},
1795
        {"./", true},
1796
        {"../", true},
1797
        {"file.exists", true}
1798
    };
1799

1800
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
264✔
1801
        bool exists = false;
240✔
1802
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
240✔
1803

1804
        if (exists != real_or_not[n].exists) {
240✔
1805
            pass = false;
23✔
1806
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
23✔
1807
                exists ? "true" : "false");
23✔
1808
        } else {
1809
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
217✔
1810
                exists ? "true" : "false");
187✔
1811
        }
1812

1813
        _sir_andeql(pass, ret);
240✔
1814
        if (!ret) {
240✔
1815
            bool unused = print_test_error(false, false);
28✔
1816
            SIR_UNUSED(unused);
1817
        }
1818
    }
1819

1820
    /* checking file descriptors. */
1821
    static int bad_fds[] = {
1822
        0,
1823
        1,
1824
        2,
1825
        1234
1826
    };
1827
    if (sirtest_get_wineversion()) { //-V547
24✔
1828
        bad_fds[3] = 0;
×
1829
    }
1830

1831
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
120✔
1832
        if (_sir_validfd(bad_fds[n])) {
96✔
1833
            pass = false;
×
1834
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1835
        } else {
1836
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
96✔
1837
        }
1838
    }
1839

1840
    FILE* f = NULL;
24✔
1841
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
24✔
1842
    if (!ret) {
24✔
1843
        pass = false;
4✔
1844
        handle_os_error(true, "fopen(%s) failed!", "file.exists");
4✔
1845
    } else {
1846
        int fd = fileno(f);
20✔
1847
        if (!_sir_validfd(fd)) {
20✔
1848
            pass = false;
×
1849
            printf("\t" RED("_sir_validfd(%d) = false") "\n", fd);
×
1850
        } else {
1851
            printf("\t" GREEN("_sir_validfd(%d) = true") "\n", fd);
17✔
1852
        }
1853
    }
1854

1855
    _sir_safefclose(&f);
24✔
1856

1857
    _sir_andeql(pass, sir_cleanup());
24✔
1858
    return print_result_and_return(pass);
24✔
1859
}
1860

1861
bool sirtest_squelchspam(void) {
24✔
1862
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1863
    bool pass = si_init;
21✔
1864

1865
    static const size_t alternate   = 50;
1866
    static const size_t sequence[3] = {
1867
        1000, /* non-repeating messages. */
1868
        1000, /* repeating messages. */
1869
        1000  /* alternating repeating and non-repeating messages. */
1870
    };
1871

1872
    sir_time timer;
1873
    sir_timer_start(&timer);
24✔
1874

1875
    printf("\t" BLUE("%zu non-repeating messages...") "\n", sequence[0]);
21✔
1876

1877
    for (size_t n = 0, ascii_idx = 33; n < sequence[0]; n++, ascii_idx++) {
24,024✔
1878
        _sir_andeql(pass, sir_debug("%c%c a non-repeating message", (char)ascii_idx,
24,000✔
1879
            (char)ascii_idx + 1));
1880

1881
        if (ascii_idx == 125)
24,000✔
1882
            ascii_idx = 33;
210✔
1883
    }
1884

1885
    printf("\t" BLUE("%zu repeating messages...") "\n", sequence[1]);
21✔
1886

1887
    for (size_t n = 0; n < sequence[1]; n++) {
24,024✔
1888
        bool ret = sir_debug("a repeating message");
24,000✔
1889

1890
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
24,000✔
1891
            _sir_andeql(pass, !ret);
23,904✔
1892
        else
1893
            _sir_andeql(pass, ret);
96✔
1894
    }
1895

1896
    printf("\t" BLUE("%zu alternating repeating and non-repeating messages...")
21✔
1897
           "\n", sequence[2]);
21✔
1898

1899
    bool repeating   = false;
21✔
1900
    size_t counter   = 0;
21✔
1901
    size_t repeat_id = 0;
21✔
1902
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
24,024✔
1903
        if (!repeating) {
24,000✔
1904
            _sir_andeql(pass, sir_debug("%c%c a non-repeating message", (char)ascii_idx,
12,024✔
1905
                (char)ascii_idx + 1));
1906
        } else {
1907
            bool ret = sir_debug("%zu a repeating message", repeat_id);
11,976✔
1908

1909
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
11,976✔
1910
                _sir_andeql(pass, !ret);
11,016✔
1911
            else
1912
                _sir_andeql(pass, ret);
960✔
1913
        }
1914

1915
        if (counter == alternate) {
24,000✔
1916
            repeating = !repeating;
456✔
1917
            counter = 0;
399✔
1918
            repeat_id++;
456✔
1919
        }
1920

1921
        if (ascii_idx == 125)
24,000✔
1922
            ascii_idx = 33;
210✔
1923
    }
1924

1925
    _sir_andeql(pass, sir_cleanup());
24✔
1926
    return print_result_and_return(pass);
24✔
1927
}
1928

1929
bool sirtest_pluginloader(void) {
24✔
1930
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1931
    bool pass = si_init;
21✔
1932

1933
#if !defined(__WIN__)
1934
# define PLUGIN_EXT "so"
1935
#else
1936
# define PLUGIN_EXT "dll"
1937
#endif
1938

1939
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1940
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1941
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1942
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1943
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1944
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1945
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1946
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1947

1948
#if defined(SIR_NO_PLUGINS)
1949
    SIR_UNUSED(plugin2);
1950
    SIR_UNUSED(plugin3);
1951
    SIR_UNUSED(plugin4);
1952
    SIR_UNUSED(plugin5);
1953
    SIR_UNUSED(plugin6);
1954
    SIR_UNUSED(plugin7);
1955
    SIR_UNUSED(plugin8);
1956

1957
    printf("\tSIR_NO_PLUGINS is defined; expecting calls to fail\n");
1✔
1958

1959
    printf("\tloading good plugin: '%s'...\n", plugin1);
1✔
1960
    /* load a valid, well-behaved plugin. */
1961
    sirpluginid id = sir_loadplugin(plugin1);
1✔
1962
    _sir_andeql(pass, 0 == id);
1✔
1963

1964
    if (pass)
1✔
1965
        print_expected_error();
1✔
1966

1967
    printf("\tunloading good plugin: '%s'...\n", plugin1);
1✔
1968
    /* also try the unload function. */
1969
    _sir_andeql(pass, !sir_unloadplugin(id));
1✔
1970

1971
    if (pass)
1✔
1972
        print_expected_error();
1✔
1973
#else
1974
    /* load a valid, well-behaved plugin. */
1975
    printf("\tloading good plugin: '%s'...\n", plugin1);
23✔
1976
    sirpluginid id = sir_loadplugin(plugin1);
23✔
1977
    _sir_andeql(pass, 0 != id);
23✔
1978

1979
    print_test_error(pass, pass);
23✔
1980

1981
    _sir_andeql(pass, sir_info("this message will be dispatched to the plugin."));
23✔
1982
    _sir_andeql(pass, sir_warn("this message will *not* be dispatched to the plugin."));
23✔
1983

1984
    /* re-loading the same plugin should fail. */
1985
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
23✔
1986
    sirpluginid badid = sir_loadplugin(plugin1);
23✔
1987
    _sir_andeql(pass, 0 == badid);
23✔
1988

1989
    print_test_error(pass, pass);
23✔
1990

1991
    /* the following are all invalid or misbehaved, and should all fail. */
1992
    printf("\tloading bad plugin: '%s'...\n", plugin2);
23✔
1993
    badid = sir_loadplugin(plugin2);
23✔
1994
    _sir_andeql(pass, 0 == badid);
23✔
1995

1996
    print_test_error(pass, pass);
23✔
1997

1998
    printf("\tloading bad plugin: '%s'...\n", plugin3);
23✔
1999
    badid = sir_loadplugin(plugin3);
23✔
2000
    _sir_andeql(pass, 0 == badid);
23✔
2001

2002
    print_test_error(pass, pass);
23✔
2003

2004
    printf("\tloading bad plugin: '%s'...\n", plugin4);
23✔
2005
    badid = sir_loadplugin(plugin4);
23✔
2006
    _sir_andeql(pass, 0 == badid);
23✔
2007

2008
    print_test_error(pass, pass);
23✔
2009

2010
    printf("\tloading bad plugin: '%s'...\n", plugin5);
23✔
2011
    badid = sir_loadplugin(plugin5);
23✔
2012
    _sir_andeql(pass, 0 == badid);
23✔
2013

2014
    print_test_error(pass, pass);
23✔
2015

2016
    printf("\tloading bad plugin: '%s'...\n", plugin6);
23✔
2017
    badid = sir_loadplugin(plugin6);
23✔
2018
    _sir_andeql(pass, 0 == badid);
23✔
2019

2020
    print_test_error(pass, pass);
23✔
2021

2022
    printf("\tloading bad plugin: '%s'...\n", plugin7);
23✔
2023
    badid = sir_loadplugin(plugin7);
23✔
2024
    _sir_andeql(pass, 0 != badid); /* this one should load, just return false from write */
23✔
2025

2026
    _sir_andeql(pass, !sir_info("should fail; a plugin failed to process the message."));
23✔
2027

2028
    print_test_error(pass, pass);
23✔
2029

2030
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
23✔
2031
    badid = sir_loadplugin(plugin8);
23✔
2032
    _sir_andeql(pass, 0 == badid);
23✔
2033

2034
    print_test_error(pass, pass);
23✔
2035

2036
    /* unload the good plugin manually. */
2037
    printf("\tunloading good plugin: '%s'...\n", plugin1);
23✔
2038
    _sir_andeql(pass, sir_unloadplugin(id));
23✔
2039

2040
    print_test_error(pass, pass);
23✔
2041

2042
    /* try to unload the plugin again. */
2043
    printf("\nunloading already unloaded plugin '%s'...\n", plugin1);
23✔
2044
    _sir_andeql(pass, !sir_unloadplugin(id));
23✔
2045

2046
    print_test_error(pass, pass);
23✔
2047

2048
    /* test bad paths. */
2049
    printf("\ntrying to load plugin with NULL path...\n");
20✔
2050
    badid = sir_loadplugin(NULL);
23✔
2051
    _sir_andeql(pass, 0 == badid);
23✔
2052

2053
    print_test_error(pass, pass);
23✔
2054
#endif
2055
    _sir_andeql(pass, sir_cleanup());
24✔
2056
    return print_result_and_return(pass);
24✔
2057
}
2058

2059
bool sirtest_getversioninfo(void) {
24✔
2060
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
2061
    bool pass = si_init;
21✔
2062

2063
    printf("\tchecking version retrieval functions...\n");
21✔
2064

2065
    const char* str = sir_getversionstring();
24✔
2066
    _sir_andeql(pass, _sir_validstrnofail(str));
24✔
2067

2068
    printf("\tversion as string: '%s'\n", _SIR_PRNSTR(str));
24✔
2069

2070
    uint32_t hex = sir_getversionhex();
24✔
2071
    _sir_andeql(pass, 0 != hex);
24✔
2072

2073
    printf("\tversion as hex: 0x%08"PRIx32"\n", hex);
21✔
2074

2075
    bool prerel = sir_isprerelease();
24✔
2076
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
24✔
2077

2078
    _sir_andeql(pass, sir_cleanup());
24✔
2079
    return print_result_and_return(pass);
24✔
2080
}
2081

2082
enum {
2083
    NUM_THREADS = 4
2084
};
2085

2086
static bool threadpool_pseudojob(void* arg) {
149✔
2087
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
149✔
2088
#if !defined(__WIN__)
2089
    sleep(1);
149✔
2090
#else
2091
    Sleep(1000);
2092
#endif
2093
    return true;
149✔
2094
}
2095

2096
bool sirtest_threadpool(void) {
24✔
2097
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
24✔
2098
    bool pass = si_init;
21✔
2099

2100
    static const size_t num_jobs = 30;
2101
    sir_threadpool* pool         = NULL;
24✔
2102

2103
    _sir_andeql(pass, _sir_threadpool_create(&pool, NUM_THREADS));
24✔
2104
    if (pass) {
21✔
2105
        /* dispatch a whole bunch of jobs. */
2106
        for (size_t n = 0; n < num_jobs; n++) {
651✔
2107
            sir_threadpool_job* job = calloc(1, sizeof(sir_threadpool_job));
630✔
2108
            _sir_andeql(pass, NULL != job);
630✔
2109
            if (job) {
630✔
2110
                job->fn = &threadpool_pseudojob;
630✔
2111
                job->data = (void*)(n + 1);
630✔
2112
                _sir_andeql(pass, _sir_threadpool_add_job(pool, job));
630✔
2113
                _sir_andeql(pass, sir_info("dispatched job (fn: %"PRIxPTR", data: %p)",
630✔
2114
                    (uintptr_t)job->fn, job->data));
2115
            }
2116
        }
2117

2118
#if !defined(__WIN__)
2119
        sleep(1);
21✔
2120
#else
2121
        Sleep(1000);
2122
#endif
2123

2124
        _sir_andeql(pass, sir_info("destroying thread pool..."));
21✔
2125
        _sir_andeql(pass, _sir_threadpool_destroy(&pool));
21✔
2126
    }
2127

2128
    _sir_andeql(pass, sir_cleanup());
24✔
2129
    return print_result_and_return(pass);
24✔
2130
}
2131

2132
#if !defined(__WIN__)
2133
static void* threadrace_thread(void* arg);
2134
#else /* __WIN__ */
2135
static unsigned __stdcall threadrace_thread(void* arg);
2136
#endif
2137

2138
bool sirtest_threadrace(void) {
24✔
2139
#if !defined(__WIN__)
2140
    pthread_t thrds[NUM_THREADS] = {0};
24✔
2141
#else /* __WIN__ */
2142
    uintptr_t thrds[NUM_THREADS] = {0};
2143
#endif
2144

2145
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
24✔
2146
    bool pass           = si_init;
21✔
2147
    bool any_created    = false;
21✔
2148
    size_t last_created = 0;
21✔
2149

2150
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
24✔
2151
    _sir_andeql(pass, NULL != heap_args);
24✔
2152
    if (!heap_args) {
24✔
2153
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2154
        return false;
1✔
2155
    }
2156

2157
    for (size_t n = 0; n < NUM_THREADS; n++) {
111✔
2158
        if (!pass)
89✔
2159
            break;
×
2160

2161
        heap_args[n].pass = true;
89✔
2162
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
89✔
2163
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2164

2165
#if !defined(__WIN__)
2166
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
89✔
2167
        if (0 != create) {
89✔
2168
            errno = create;
1✔
2169
            handle_os_error(true, "pthread_create() for thread #%zu failed!", n + 1);
1✔
2170
#else /* __WIN__ */
2171
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2172
        if (0 == thrds[n]) {
2173
            handle_os_error(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2174
#endif
2175
            pass = false;
1✔
2176
            break;
1✔
2177
        }
2178

2179
        last_created = n;
76✔
2180
        any_created  = true;
76✔
2181
    }
2182

2183
    if (any_created) {
23✔
2184
        for (size_t j = 0; j < last_created + 1; j++) {
110✔
2185
            bool joined = true;
76✔
2186
            printf("\twaiting for thread %zu/%zu...\n", j + 1, last_created + 1);
88✔
2187
#if !defined(__WIN__)
2188
            int join = pthread_join(thrds[j], NULL);
88✔
2189
            if (0 != join) {
88✔
2190
                joined = false;
4✔
2191
                errno  = join;
4✔
2192
                handle_os_error(true, "pthread_join() for thread #%zu failed!", j + 1);
4✔
2193
            }
2194
#else /* __WIN__ */
2195
            DWORD wait = WaitForSingleObject((HANDLE)thrds[j], INFINITE);
2196
            if (WAIT_OBJECT_0 != wait) {
2197
                joined = false;
2198
                handle_os_error(false, "WaitForSingleObject() for thread #%zu (%p) failed!", j + 1,
2199
                    (HANDLE)thrds[j]);
2200
            }
2201
#endif
2202
            _sir_andeql(pass, joined);
76✔
2203
            if (joined) {
76✔
2204
                printf("\tthread %zu/%zu joined\n", j + 1, last_created + 1);
72✔
2205

2206
                _sir_andeql(pass, heap_args[j].pass);
84✔
2207
                if (heap_args[j].pass)
84✔
2208
                    printf("\t" GREEN("thread #%zu returned pass = true") "\n", j + 1);
72✔
2209
                else
2210
                    printf("\t" RED("thread #%zu returned pass = false!") "\n", j + 1);
×
2211
            }
2212
        }
2213
    }
2214

2215
    _sir_safefree(&heap_args);
23✔
2216

2217
    _sir_andeql(pass, sir_cleanup());
23✔
2218
    return print_result_and_return(pass);
23✔
2219
}
2220

2221
#if !defined(__WIN__)
2222
static void* threadrace_thread(void* arg) {
88✔
2223
#else /* __WIN__ */
2224
unsigned __stdcall threadrace_thread(void* arg) {
2225
#endif
2226
    pid_t threadid       = _sir_gettid();
88✔
2227
    thread_args* my_args = (thread_args*)arg;
76✔
2228

2229
    (void)rmfile(my_args->log_file);
88✔
2230
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
88✔
2231

2232
    if (0U == id) {
88✔
2233
        bool unused = print_test_error(false, false);
10✔
2234
        SIR_UNUSED(unused);
2235
#if !defined(__WIN__)
2236
        return NULL;
10✔
2237
#else /* __WIN__ */
2238
        return 0;
2239
#endif
2240
    }
2241

2242
    printf("\thi, i'm thread (id: %d), logging to: '%s'...\n",
66✔
2243
            PID_CAST threadid, my_args->log_file);
66✔
2244

2245
#if !defined(DUMA)
2246
# define NUM_ITERATIONS 1000
2247
#else
2248
# define NUM_ITERATIONS 100
2249
#endif
2250

2251
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
76,339✔
2252
        /* choose a random level, and colors. */
2253
        sir_textcolor fg = SIRTC_DEFAULT;
64,263✔
2254
        sir_textcolor bg = SIRTC_DEFAULT;
64,263✔
2255

2256
        if (n % 2 == 0) {
76,263✔
2257
            fg = SIRTC_CYAN;
32,132✔
2258
            bg = SIRTC_BLACK;
32,132✔
2259
            sir_debug("log message #%zu", n);
38,132✔
2260
        } else {
2261
            fg = SIRTC_BLACK;
32,131✔
2262
            bg = SIRTC_CYAN;
32,131✔
2263
            sir_info("log message #%zu", n);
38,131✔
2264
        }
2265

2266
        /* sometimes remove and re-add the log file, and set some options/styles.
2267
         * other times, just set different options/styles. */
2268
        uint32_t rnd = (uint32_t)(n + threadid);
76,263✔
2269
        if (getrand_bool(rnd > 1U ? rnd : 1U)) {
76,263✔
2270
            my_args->pass = print_test_error(sir_remfile(id), false);
38,622✔
2271
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
38,622✔
2272
                SIRL_ALL, SIRO_MSGONLY), false);
2273

2274
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
77,242✔
2275
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
38,620✔
2276
            my_args->pass = print_test_error(test, false);
38,622✔
2277

2278
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
38,622✔
2279
            my_args->pass = print_test_error(test, false);
38,622✔
2280
        } else {
2281
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
75,282✔
2282
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
37,640✔
2283
            my_args->pass = print_test_error(test, false);
37,641✔
2284

2285
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
37,641✔
2286
            my_args->pass = print_test_error(test, false);
37,640✔
2287

2288
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
37,638✔
2289
            my_args->pass = print_test_error(test, false);
37,641✔
2290
        }
2291

2292
        if (!my_args->pass)
76,263✔
2293
            break;
2✔
2294
    }
2295

2296
    my_args->pass = print_test_error(sir_remfile(id), false);
78✔
2297

2298
    (void)rmfile(my_args->log_file);
78✔
2299

2300
#if !defined(__WIN__)
2301
    return NULL;
78✔
2302
#else /* __WIN__ */
2303
    return 0U;
2304
#endif
2305
}
2306

2307
/*
2308
bool sirtest_XXX(void) {
2309
    INIT(si, SIRL_ALL, 0, 0, 0);
2310
    bool pass = si_init;
2311

2312
    _sir_andeql(pass, sir_cleanup());
2313
    return print_result_and_return(pass);
2314
}
2315
*/
2316

2317
/* ========================== end tests ========================== */
2318

2319
bool print_test_error(bool result, bool expected) {
269,146✔
2320
    char message[SIR_MAXERROR] = {0};
269,146✔
2321
    uint16_t code              = sir_geterror(message);
269,146✔
2322

2323
    if (!expected && !result && SIR_E_NOERROR != code)
269,138✔
2324
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
637✔
2325
    else if (expected && SIR_E_NOERROR != code)
268,501✔
2326
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
434✔
2327

2328
    return result;
269,139✔
2329
}
2330

2331
bool print_os_error(void) {
64✔
2332
    char message[SIR_MAXERROR] = {0};
64✔
2333
    uint16_t code              = sir_geterror(message);
64✔
2334
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
64✔
2335
    return false;
64✔
2336
}
2337

2338
bool filter_error(bool pass, uint16_t err) {
1,600✔
2339
    if (!pass) {
1,600✔
2340
        char message[SIR_MAXERROR] = {0};
987✔
2341
        uint16_t code              = sir_geterror(message);
987✔
2342
        if (code != err)
987✔
2343
            return false;
×
2344
    }
2345
    return true;
1,360✔
2346
}
2347

2348
uint32_t getrand(uint32_t upper_bound) {
119,800✔
2349
#if !defined(__WIN__) || defined(__EMBARCADEROC__)
2350
# if defined(__MACOS__) || defined(__BSD__)
2351
    if (upper_bound < 2U)
2352
        upper_bound = 2U;
2353
    return arc4random_uniform(upper_bound);
2354
# else
2355
#  if defined(__EMBARCADEROC__)
2356
    return (uint32_t)(random(upper_bound));
2357
#  else
2358
    return (uint32_t)(random() % upper_bound);
119,800✔
2359
#  endif
2360
# endif
2361
#else /* __WIN__ */
2362
    uint32_t ctx = 0;
2363
    if (0 != rand_s(&ctx))
2364
        ctx = (uint32_t)rand();
2365
    return ctx % upper_bound;
2366
#endif
2367
}
2368

2369
bool rmfile(const char* filename) {
1,196✔
2370
    bool removed = false;
1,043✔
2371

2372
    /* return true if leave_logs is true. */
2373
    if (leave_logs) {
1,196✔
2374
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
51✔
2375
            filename, _cl_arg_list[3].flag);
51✔
2376
        return true;
51✔
2377
    }
2378

2379
    /* return true if the file doesn't exist. */
2380
    struct stat st;
2381
    if (0 != stat(filename, &st)) {
1,145✔
2382
        if (ENOENT == errno)
600✔
2383
            return true;
486✔
2384

2385
        handle_os_error(true, "failed to stat %s!", filename);
48✔
2386
        return false;
48✔
2387
    }
2388

2389
    if (!_sir_deletefile(filename)) {
545✔
2390
        handle_os_error(false, "failed to delete %s!", filename);
×
2391
    } else {
2392
        printf("\t" DGRAY("deleted %s (%ld bytes)") "\n", filename,
458✔
2393
            (long)st.st_size);
545✔
2394
    }
2395

2396
    return removed;
458✔
2397
}
2398

2399
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
244✔
2400
    if (strstr(filename, search)) {
244✔
2401
        char filepath[SIR_MAXPATH];
2402
        _sir_snprintf_trunc(filepath, SIR_MAXPATH, "%s%s", path, filename);
56✔
2403

2404
        (void)rmfile(filepath);
56✔
2405
        (*data)++;
56✔
2406
    }
2407
}
244✔
2408

2409
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
110✔
2410
    SIR_UNUSED(path);
2411
    if (strstr(filename, search))
110✔
2412
        (*data)++;
30✔
2413
}
110✔
2414

2415
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
63✔
2416
#if !defined(__WIN__)
2417
    DIR* d = opendir(path);
63✔
2418
    if (!d)
63✔
2419
        return print_os_error();
2✔
2420

2421
    rewinddir(d);
61✔
2422
    const struct dirent* di = readdir(d);
61✔
2423
    if (!di) {
61✔
2424
        closedir(d);
×
2425
        return print_os_error();
×
2426
    }
2427

2428
    while (NULL != di) {
415✔
2429
        cb(search, path, di->d_name, data);
354✔
2430
        di = readdir(d);
354✔
2431
    }
2432

2433
    closedir(d);
61✔
2434
    d = NULL;
52✔
2435
#else /* __WIN__ */
2436
    WIN32_FIND_DATA finddata = {0};
2437
    char buf[SIR_MAXPATH]    = {0};
2438

2439
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2440

2441
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2442

2443
    if (INVALID_HANDLE_VALUE == enumerator)
2444
        return false;
2445

2446
    do {
2447
        cb(search, path, finddata.cFileName, data);
2448
    } while (FindNextFile(enumerator, &finddata) > 0);
2449

2450
    FindClose(enumerator);
2451
    enumerator = NULL;
2452
#endif
2453

2454
    return true;
61✔
2455
}
2456

2457
double sir_timer_elapsed(const sir_time* timer) {
2,000,029✔
2458
    sir_time now;
2459
    return _sir_msec_since(timer, &now);
2,000,029✔
2460
}
2461

2462
long sir_timer_getres(void) {
1✔
2463
    long retval = 0L;
1✔
2464
#if !defined(__WIN__)
2465
    struct timespec res;
2466
    if (0 == clock_getres(SIR_INTERVALCLOCK, &res)) {
1✔
2467
        retval = res.tv_nsec;
1✔
2468
    } else {
2469
        handle_os_error(true, "clock_getres(%d) failed!", CLOCK_CAST SIR_INTERVALCLOCK); // GCOVR_EXCL_LINE
2470
    }
2471
#else /* __WIN__ */
2472
    LARGE_INTEGER cntr_freq;
2473
    (void)QueryPerformanceFrequency(&cntr_freq);
2474
    if (cntr_freq.QuadPart <= 0)
2475
        retval = 0L;
2476
    else
2477
        retval = (long)(ceil(((double)cntr_freq.QuadPart) / 1e9));
2478
#endif
2479
    return retval;
1✔
2480
}
2481

2482
void sir_sleep_msec(uint32_t msec) {
48✔
2483
    if (0U == msec)
48✔
2484
        return;
×
2485

2486
#if !defined(__WIN__)
2487
    struct timespec ts = { msec / 1000, (msec % 1000) * 1000000 };
48✔
2488
    (void)nanosleep(&ts, NULL);
48✔
2489
#else /* __WIN__ */
2490
    (void)SleepEx((DWORD)msec, TRUE);
2491
#endif
2492
}
2493

2494
size_t sir_readline(FILE* f, char* buf, size_t size) {
78✔
2495
    if (!f || !buf || 0 == size)
78✔
2496
        return 0;
×
2497

2498
    int ch     = 0;
66✔
2499
    size_t idx = 0;
66✔
2500

2501
    while (idx < size) {
13,433✔
2502
        ch = getc(f);
13,433✔
2503
        if (EOF == ch || '\n' == ch)
13,433✔
2504
            break;
2505
        buf[idx++] = (char)ch;
13,355✔
2506
    }
2507

2508
    return (0 == ferror(f)) ? idx : 0;
78✔
2509
}
2510

2511
#if defined(SIR_OS_LOG_ENABLED)
2512
void os_log_parent_activity(void* ctx) {
2513
    sir_debug("confirming with ground control that we are a go...");
2514
    sir_info("all systems go; initiating launch sequence");
2515
    sir_warn("getting some excessive vibration here");
2516
    sir_info("safely reached escape velocity. catch you on the flip side");
2517
    sir_info("(3 days later) we have landed on the lunar surface");
2518
    sir_notice("beginning rock counting...");
2519

2520
    os_activity_t parent = (os_activity_t)ctx;
2521
    os_activity_t child = os_activity_create("counting moon rocks", parent, // -V530
2522
        OS_ACTIVITY_FLAG_DEFAULT);
2523

2524
    float rock_count = 0.0f;
2525
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2526
    sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2527
        (double)rock_count);
2528
}
2529

2530
void os_log_child_activity(void* ctx) {
2531
    sir_info("there are a lot of rocks here; we're going to be here a while");
2532

2533
    for (size_t n = 0; n < 10; n++) {
2534
        sir_info("counting rocks in sector %zu...", n);
2535
    }
2536

2537
    float* rock_count = (float*)ctx;
2538
    *rock_count = 1e12f;
2539
    sir_info("all sectors counted; heading back to the lunar lander");
2540
}
2541
#endif
2542

2543
bool mark_test_to_run(const char* name) {
3✔
2544
    bool found = false;
3✔
2545
    for (size_t t = 0; t < _sir_countof(sir_tests); t++) {
48✔
2546
        if (_sir_strsame(name, sir_tests[t].name,
47✔
2547
            strnlen(sir_tests[t].name, SIR_MAXTESTNAME))) {
2548
            found = sir_tests[t].run = true;
2✔
2549
            break;
2✔
2550
        }
2551
    }
2552

2553
    if (!found)
3✔
2554
        _sir_selflog("warning: unable to locate '%s' in test array", name); // GCOVR_EXCL_LINE
2555

2556
    return found;
3✔
2557
}
2558

2559
void print_usage_info(void) {
4✔
2560
    size_t longest = 0;
4✔
2561
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2562
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2563
        if (len > longest)
28✔
2564
            longest = len;
8✔
2565
    }
2566

2567
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2568

2569
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2570
        fprintf(stderr, "\t%s ", _cl_arg_list[i].flag);
28✔
2571

2572
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2573
        if (len < longest)
28✔
2574
            for (size_t n = len; n < longest; n++)
156✔
2575
                fprintf(stderr, " ");
132✔
2576

2577
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2578
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2579
            _cl_arg_list[i].desc);
28✔
2580
    }
2581

2582
    fprintf(stderr, "\n");
4✔
2583
}
4✔
2584

2585
void print_test_list(void) {
1✔
2586
    size_t longest = 0;
1✔
2587
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
34✔
2588
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
33✔
2589
        if (len > longest)
33✔
2590
            longest = len;
3✔
2591
    }
2592

2593
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2594

2595
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
34✔
2596
        printf("\t%s\t", sir_tests[i].name);
33✔
2597

2598
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
33✔
2599
        if (len < longest)
33✔
2600
            for (size_t n = len; n < longest; n++)
284✔
2601
                printf(" ");
252✔
2602

2603
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
33✔
2604
            printf("\n");
17✔
2605
    }
2606

2607
    printf("\n");
1✔
2608
}
1✔
2609

2610
void print_libsir_version(void) {
1✔
2611
    printf("\n"ULINE("libsir") " %s (%s)\n\n", sir_getversionstring(),
1✔
2612
        sir_isprerelease() ? "prerelease" : "release");
1✔
2613
}
1✔
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

© 2025 Coveralls, Inc