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

aremmell / libsir / 1427

26 Dec 2023 05:14AM UTC coverage: 95.505% (+0.03%) from 95.477%
1427

Pull #376

gitlab-ci

aremmell
pvs 547
Pull Request #376: Tweaks based on cppcheck linter warnings

61 of 62 new or added lines in 5 files covered. (98.39%)

14 existing lines in 1 file now uncovered.

3867 of 4049 relevant lines covered (95.51%)

473122.93 hits per line

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

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

33
//-V:_sir_logv:575
34

35
#include "tests.h"
36

37
static sir_test sir_tests[] = {
38
    {SIR_CL_PERFNAME,           sirtest_perf, false, true},
39
    {"thread-race",             sirtest_threadrace, false, true},
40
    {"thread-pool",             sirtest_threadpool, false, true},
41
    {"exceed-max-buffer-size",  sirtest_exceedmaxsize, false, true},
42
    {"no-output-destination",   sirtest_failnooutputdest, false, true},
43
    {"null-pointers",           sirtest_failnulls, false, true},
44
    {"empty-message",           sirtest_failemptymessage, false, true},
45
    {"file-cache-sanity",       sirtest_filecachesanity, false, true},
46
    {"file-invalid-name",       sirtest_failinvalidfilename, false, true},
47
    {"file-bad-permissions",    sirtest_failfilebadpermission, false, true},
48
    {"file-duplicate-name",     sirtest_faildupefile, false, true},
49
    {"file-remove-nonexistent", sirtest_failremovebadfile, false, true},
50
    {"file-archive-large",      sirtest_rollandarchivefile, false, true},
51
    {"init-output-before",      sirtest_failwithoutinit, false, true},
52
    {"init-check-state",        sirtest_isinitialized, false, true},
53
    {"init-superfluous",        sirtest_failinittwice, false, true},
54
    {"init-bad-data",           sirtest_failinvalidinitdata, false, true},
55
    {"init-cleanup-init",       sirtest_initcleanupinit, false, true},
56
    {"init-with-makeinit",      sirtest_initmakeinit, false, true},
57
    {"cleanup-output-after",    sirtest_failaftercleanup, false, true},
58
    {"sanity-errors",           sirtest_errorsanity, false, true},
59
    {"sanity-text-styles",      sirtest_textstylesanity, false, true},
60
    {"sanity-options",          sirtest_optionssanity, false, true},
61
    {"sanity-levels",           sirtest_levelssanity, false, true},
62
    {"sanity-mutexes",          sirtest_mutexsanity, false, true},
63
    {"sanity-update-config",    sirtest_updatesanity, false, true},
64
    {"sanity-thread-ids",       sirtest_threadidsanity, false, true},
65
    {"sanity-file-write",       sirtest_logwritesanity, false, true},
66
    {"syslog",                  sirtest_syslog, false, true},
67
    {"os_log",                  sirtest_os_log, false, true},
68
    {"wineventlog",             sirtest_win_eventlog, false, true},
69
    {"filesystem",              sirtest_filesystem, false, true},
70
    {"squelch-spam",            sirtest_squelchspam, false, true},
71
    {"plugin-loader",           sirtest_pluginloader, false, true},
72
    {"string-utils",            sirtest_stringutils, false, true},
73
    {"get-cpu-count",           sirtest_getcpucount, false, true},
74
    {"get-version-info",        sirtest_getversioninfo, false, true}
75
};
76

77
/** List of available command line arguments. */
78
static const sir_cl_arg cl_args[] = {
79
    {SIR_CL_PERFFLAG,      "", SIR_CL_PERFDESC},
80
    {SIR_CL_ONLYFLAG,      ""  SIR_CL_ONLYUSAGE, SIR_CL_ONLYDESC},
81
    {SIR_CL_LISTFLAG,      "", SIR_CL_LISTDESC},
82
    {SIR_CL_LEAVELOGSFLAG, "", SIR_CL_LEAVELOGSDESC},
83
    {SIR_CL_WAITFLAG,      "", SIR_CL_WAITDESC},
84
    {SIR_CL_VERSIONFLAG,   "", SIR_CL_VERSIONDESC},
85
    {SIR_CL_HELPFLAG,      "", SIR_CL_HELPDESC}
86
};
87

88
static sir_cl_config cl_cfg = {0};
89

90
int main(int argc, char** argv) {
33✔
91
#if defined(__HAIKU__) && !defined(DEBUG)
92
    disable_debugger(1);
93
#endif
94

95
#include "tests_malloc.h"
96

97
#if !defined(__WIN__) && !defined(__HAIKU__) && !defined(__EMSCRIPTEN__)
98
    /* Disallow execution by root / sudo; some of the tests rely on lack of permissions. */
99
    if (geteuid() == 0) {
33✔
100
        (void)fprintf(stderr, "Sorry, but this program may not be executed by root.\n");
1✔
101
        return EXIT_FAILURE;
1✔
102
    }
103
#else /* __WIN__ */
104
# if defined(_DEBUG) && defined(SIR_ASSERT_ENABLED)
105
    /* Prevents assert() from calling abort() before the user is able to:
106
     * a.) break into the code and debug (Retry button)
107
     * b.) ignore the assert() and continue. */
108
    _set_error_mode(_OUT_TO_MSGBOX);
109
# endif
110
#endif
111

112
    bool parsed = parse_cmd_line(argc, argv, cl_args, _sir_countof(cl_args),
32✔
113
        sir_tests, _sir_countof(sir_tests), &cl_cfg);
114
    if (!parsed)
32✔
115
        return EXIT_FAILURE;
6✔
116

117
    size_t first     = (cl_cfg.only ? 0 : 1);
26✔
118
    size_t tgt_tests = (cl_cfg.only ? cl_cfg.to_run : _sir_countof(sir_tests) - first);
26✔
119
    size_t passed    =  0;
23✔
120
    size_t ran       =  0;
23✔
121
    sir_time timer   = {0};
26✔
122

123
    print_intro(tgt_tests);
26✔
124
    sir_timer_start(&timer);
26✔
125

126
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
964✔
127
        if (cl_cfg.only && !sir_tests[n].run) {
938✔
128
            _sir_selflog("skipping '%s'; not marked to run", sir_tests[n].name);
72✔
129
            continue;
72✔
130
        }
131

132
        print_test_intro(ran + 1, tgt_tests, sir_tests[n].name);
866✔
133

134
        sir_tests[n].pass = sir_tests[n].fn();
866✔
135
        if (sir_tests[n].pass)
866✔
136
            passed++;
810✔
137

138
        ran++;
758✔
139

140
        print_test_outro(ran, tgt_tests, sir_tests[n].name, sir_tests[n].pass);
866✔
141
    }
142

143
    print_test_summary(tgt_tests, passed, sir_timer_elapsed(&timer));
26✔
144

145
    if (passed != tgt_tests) {
26✔
146
        print_failed_test_intro(tgt_tests, passed);
17✔
147

148
        for (size_t t = 0; t < _sir_countof(sir_tests); t++)
646✔
149
            if (!sir_tests[t].pass)
629✔
150
                print_failed_test(sir_tests[t].name);
56✔
151
        (void)printf("\n");
17✔
152
    }
153

154
    if (cl_cfg.wait)
26✔
155
        wait_for_keypress();
1✔
156

157
    return passed == tgt_tests ? EXIT_SUCCESS : EXIT_FAILURE;
26✔
158
}
159

160
bool sirtest_exceedmaxsize(void) {
24✔
161
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
162
    bool pass = si_init;
21✔
163

164
    char toobig[SIR_MAXMESSAGE + 100] = {0};
21✔
165
    (void)memset(toobig, 'a', SIR_MAXMESSAGE + 100);
21✔
166
    toobig[SIR_MAXMESSAGE + 99] = '\0';
24✔
167

168
    _sir_eqland(pass, sir_info("%s", toobig));
24✔
169

170
    _sir_eqland(pass, sir_cleanup());
24✔
171
    return PRINT_RESULT_RETURN(pass);
24✔
172
}
173

174
bool sirtest_logwritesanity(void) {
24✔
175
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
176
    bool pass = si_init;
21✔
177

178
    static const char* logfilename = MAKE_LOG_NAME("write-validate.log");
179
    static const char* message     = "Lorem ipsum dolor sit amet, sea ei dicit"
180
                                     " regione laboramus, eos cu minim putent."
181
                                     " Sale omnium conceptam est in, cu nam possim"
182
                                     " prompta eleifend. Duo purto nostrud eu."
183
                                     " Alia accumsan has cu, mentitum invenire"
184
                                     " mel an, dicta noster legendos et pro."
185
                                     " Solum nobis laboramus et quo, nam putant"
186
                                     " dolores consequuntur ex. Sit veniam eruditi"
187
                                     " contentiones at. Cu ponderum oporteat"
188
                                     " oportere mel, has et saperet accusata"
189
                                     " complectitur.";
190

191
    TEST_MSG("adding log file '%s' to libsir...", logfilename);
24✔
192
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
24✔
193
    _sir_eqland(pass, 0U != id);
24✔
194

195
    (void)print_test_error(pass, false);
24✔
196

197
    TEST_MSG("writing message to stdout and %s...", logfilename);
24✔
198

199
    _sir_eqland(pass, sir_debug("%s", message));
24✔
200

201
    (void)print_test_error(pass, false);
24✔
202

203
    TEST_MSG("removing %s from libsir...", logfilename);
24✔
204
    _sir_eqland(pass, sir_remfile(id));
24✔
205

206
    (void)print_test_error(pass, false);
24✔
207

208
    TEST_MSG("opening %s for reading...", logfilename);
24✔
209

210
    FILE* f = fopen(logfilename, "r");
24✔
211
    if (!f) {
24✔
212
        pass = false;
3✔
213
    } else {
214
        char buf[512] = {0};
21✔
215
        _sir_eqland(pass, 0 != sir_readline(f, buf, 512));
21✔
216

217
        bool found = NULL != strstr(buf, message);
21✔
218
        _sir_eqland(pass, found);
21✔
219

220
        if (found)
21✔
221
            TEST_MSG(GREEN("found '%s'"), message);
18✔
222
        else
223
            TEST_MSG(RED("did not find '%s'"), message);
×
224

225
        _sir_safefclose(&f);
21✔
226
        TEST_MSG("deleting %s...", logfilename);
21✔
227
        rmfile(logfilename, cl_cfg.leave_logs);
21✔
228
    }
229

230
    _sir_eqland(pass, sir_cleanup());
24✔
231
    return PRINT_RESULT_RETURN(pass);
24✔
232
}
233

234
bool sirtest_threadidsanity(void)
24✔
235
{
236
#if defined(SIR_NO_THREAD_NAMES)
237
    TEST_MSG_0(DGRAY("test skipped for this system configuration"));
238
    return true;
239
#endif
240
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
241
    bool pass = si_init;
21✔
242

243
    static const char* thread_name = "mythread";
244
    static const char* logfilename = MAKE_LOG_NAME("thread-id-name.log");
245

246
    TEST_MSG("adding log file '%s' to libsir...", logfilename);
24✔
247
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
24✔
248
    _sir_eqland(pass, 0U != id);
24✔
249

250
    (void)print_test_error(pass, false);
24✔
251

252
    TEST_MSG_0("logging a message normally...");
21✔
253
    _sir_eqland(pass, sir_debug("this is a test of the libsir system"));
24✔
254

255
    TEST_MSG("setting the thread name to '%s' and logging again...", thread_name);
24✔
256

257
    _sir_eqland(pass, _sir_setthreadname(thread_name));
24✔
258
    sir_sleep_msec((uint32_t)SIR_THRD_CHK_INTERVAL + 200U);
24✔
259

260
    (void)print_test_error(pass, false);
24✔
261

262
    _sir_eqland(pass, sir_debug("this is a test of the libsir system after setting thread name"));
24✔
263

264
    TEST_MSG_0("setting the thread name to '' and logging again...");
21✔
265

266
    _sir_eqland(pass, _sir_setthreadname(""));
24✔
267
    sir_sleep_msec((uint32_t)SIR_THRD_CHK_INTERVAL + 200U);
24✔
268

269
    (void)print_test_error(pass, false);
24✔
270

271
    _sir_eqland(pass, sir_debug("this is a test of the libsir system after clearing thread name"));
24✔
272

273
     /* remove the log file from libsir, then open it and read it line by line. */
274
    TEST_MSG("removing %s from libsir...", logfilename);
24✔
275
    _sir_eqland(pass, sir_remfile(id));
24✔
276

277
    TEST_MSG("opening %s for reading...", logfilename);
24✔
278

279
    FILE* f = fopen(logfilename, "r");
24✔
280
    if (!f) {
24✔
281
        pass = false;
3✔
282
    } else {
283
        /* look for, in order, TID, thread name, TID. */
284
        for (size_t n = 0; n < 3; n++) {
84✔
285
            char buf[256] = {0};
63✔
286
            _sir_eqland(pass, 0 != sir_readline(f, buf, 256));
63✔
287
            TEST_MSG("read line %zu: '%s'", n, buf);
54✔
288

289
            char search[SIR_MAXPID] = {0};
63✔
290
            switch (n) {
63✔
291
                case 0:
42✔
292
                case 2:
293
                    (void)snprintf(search, SIR_MAXPID, SIR_TIDFORMAT, _sir_gettid());
42✔
294
                break;
36✔
295
                case 1:
21✔
296
                    (void)_sir_strncpy(search, SIR_MAXPID, thread_name, strlen(thread_name));
21✔
297
                break;
21✔
298
                default: break; // GCOVR_EXCL_LINE
299
            }
300

301
            bool found = NULL != strstr(buf, search);
63✔
302
            _sir_eqland(pass, found);
63✔
303

304
            if (found)
63✔
305
                TEST_MSG(GREEN("line %zu: found '%s'"), n, search);
53✔
306
            else
307
                TEST_MSG(RED("line %zu: did not find '%s'"), n, search);
1✔
308
        }
309

310
        _sir_safefclose(&f);
21✔
311
        TEST_MSG("deleting %s...", logfilename);
21✔
312
        rmfile(logfilename, cl_cfg.leave_logs);
21✔
313
    }
314

315
    _sir_eqland(pass, sir_cleanup());
24✔
316
    return PRINT_RESULT_RETURN(pass);
24✔
317
}
318

319
bool sirtest_failnooutputdest(void) {
24✔
320
    INIT(si, 0, 0, 0, 0);
24✔
321
    bool pass = si_init;
21✔
322

323
    _sir_eqland(pass, !sir_notice("this goes nowhere!"));
24✔
324

325
    if (pass) {
21✔
326
        static const char* logfilename = MAKE_LOG_NAME("nodestination.log");
327
        PRINT_EXPECTED_ERROR();
24✔
328

329
        _sir_eqland(pass, sir_stdoutlevels(SIRL_INFO));
24✔
330
        _sir_eqland(pass, sir_info("this goes to stdout"));
24✔
331
        _sir_eqland(pass, sir_stdoutlevels(SIRL_NONE));
24✔
332

333
        sirfileid fid = sir_addfile(logfilename, SIRL_INFO, SIRO_DEFAULT);
24✔
334
        _sir_eqland(pass, 0U != fid);
24✔
335
        _sir_eqland(pass, sir_info("this goes to %s", logfilename));
24✔
336
        _sir_eqland(pass, sir_filelevels(fid, SIRL_NONE));
24✔
337
        _sir_eqland(pass, !sir_notice("this goes nowhere!"));
24✔
338

339
        if (0U != fid)
24✔
340
            _sir_eqland(pass, sir_remfile(fid));
21✔
341

342
        rmfile(logfilename, cl_cfg.leave_logs);
24✔
343
    }
344

345
    _sir_eqland(pass, sir_cleanup());
24✔
346
    return PRINT_RESULT_RETURN(pass);
24✔
347
}
348

349
bool sirtest_failnulls(void) {
24✔
350
    INIT_BASE(si, SIRL_ALL, 0, 0, 0, "", false);
24✔
351
    bool pass = true;
21✔
352

353
    _sir_eqland(pass, !sir_init(NULL));
24✔
354

355
    if (pass)
21✔
356
        PRINT_EXPECTED_ERROR();
24✔
357

358
    _sir_eqland(pass, sir_init(&si));
24✔
359
    _sir_eqland(pass, !sir_info(NULL)); //-V618
24✔
360

361
    if (pass)
21✔
362
        PRINT_EXPECTED_ERROR();
24✔
363

364
    _sir_eqland(pass, 0U == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY));
24✔
365

366
    if (pass)
21✔
367
        PRINT_EXPECTED_ERROR();
24✔
368

369
    _sir_eqland(pass, !sir_remfile(0U));
24✔
370

371
    if (pass)
21✔
372
        PRINT_EXPECTED_ERROR();
24✔
373

374
    _sir_eqland(pass, sir_cleanup());
24✔
375
    return PRINT_RESULT_RETURN(pass);
24✔
376
}
377

378
bool sirtest_failemptymessage(void) {
24✔
379
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
380
    bool pass = si_init;
21✔
381

382
    _sir_eqland(pass, !sir_debug("%s", ""));
24✔
383

384
    _sir_eqland(pass, sir_cleanup());
24✔
385
    return PRINT_RESULT_RETURN(pass);
24✔
386
}
387

388
bool sirtest_filecachesanity(void) {
24✔
389
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
390
    bool pass = si_init;
21✔
391

392
    size_t numfiles             = SIR_MAXFILES + 1;
21✔
393
    sirfileid ids[SIR_MAXFILES] = {0};
24✔
394

395
    sir_options even = SIRO_MSGONLY;
21✔
396
    sir_options odd  = SIRO_ALL;
21✔
397

398
    for (size_t n = 0; n < numfiles - 1; n++) {
408✔
399
        char path[SIR_MAXPATH] = {0};
384✔
400
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), n);
336✔
401
        rmfile(path, cl_cfg.leave_logs);
384✔
402
        ids[n] = sir_addfile(path, SIRL_ALL, (n % 2) ? odd : even);
408✔
403
        _sir_eqland(pass, 0U != ids[n] && sir_info("test %zu", n));
384✔
404
    }
405

406
    _sir_eqland(pass, sir_info("test test test"));
24✔
407

408
    /* this one should fail; max files already added. */
409
    _sir_eqland(pass, 0U == sir_addfile(MAKE_LOG_NAME("should-fail.log"),
24✔
410
        SIRL_ALL, SIRO_MSGONLY));
411

412
    if (pass)
21✔
413
        PRINT_EXPECTED_ERROR();
20✔
414

415
    _sir_eqland(pass, sir_info("test test test"));
24✔
416

417
    /* now remove previously added files in a different order. */
418
    size_t removeorder[SIR_MAXFILES];
419
    (void)memset(removeorder, -1, sizeof(removeorder));
21✔
420

421
    long processed = 0L;
21✔
422
    TEST_MSG_0("creating random file ID order...");
21✔
423

424
    do {
1,064✔
425
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,258✔
426
        bool skip  = false;
1,085✔
427

428
        for (size_t n = 0; n < SIR_MAXFILES; n++)
12,620✔
429
            if (removeorder[n] == rnd) {
12,236✔
430
                skip = true;
749✔
431
                break;
749✔
432
            }
433

434
        if (skip)
1,258✔
435
            continue;
874✔
436

437
        removeorder[processed++] = rnd;
384✔
438

439
        if (processed == SIR_MAXFILES)
384✔
440
            break;
21✔
441
    } while (true);
442

443
    (void)printf("\tremove order: {");
21✔
444
    for (size_t n = 0; n < SIR_MAXFILES; n++)
408✔
445
        (void)printf(" %zu%s", removeorder[n], (n < SIR_MAXFILES - 1) ? "," : "");
387✔
446
    (void)printf(" }...\n");
21✔
447

448
    for (size_t n = 0; n < SIR_MAXFILES; n++) {
408✔
449
        _sir_eqland(pass, sir_remfile(ids[removeorder[n]]));
384✔
450

451
        char path[SIR_MAXPATH] = {0};
384✔
452
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), removeorder[n]);
384✔
453
        rmfile(path, cl_cfg.leave_logs);
384✔
454
    }
455

456
    _sir_eqland(pass, sir_info("test test test"));
24✔
457

458
    _sir_eqland(pass, sir_cleanup());
24✔
459
    return PRINT_RESULT_RETURN(pass);
24✔
460
}
461

462
bool sirtest_failinvalidfilename(void) {
24✔
463
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
464
    bool pass = si_init;
21✔
465

466
    _sir_eqland(pass, 0U == sir_addfile("bad file!/name", SIRL_ALL, SIRO_MSGONLY));
24✔
467

468
    if (pass)
21✔
469
        PRINT_EXPECTED_ERROR();
24✔
470

471
    _sir_eqland(pass, sir_cleanup());
24✔
472
    return PRINT_RESULT_RETURN(pass);
24✔
473
}
474

475
bool sirtest_failfilebadpermission(void) {
24✔
476
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
477
    bool pass = si_init;
21✔
478

479
#if !defined(__WIN__)
480
    static const char* path = "/noperms";
481
#else /* __WIN__ */
482
# if defined(__CYGWIN__)
483
    static const char* path = "/cygdrive/c/Windows/System32/noperms";
484
# else
485
    static const char* path;
486
    if (get_wineversion()) {
487
        path = "Z:\\noperms";
488
    } else {
489
        path = "C:\\Windows\\System32\\noperms";
490
    }
491
# endif
492
#endif
493

494
    _sir_eqland(pass, 0U == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY));
24✔
495

496
    if (pass)
21✔
497
        PRINT_EXPECTED_ERROR();
24✔
498

499
    _sir_eqland(pass, sir_cleanup());
24✔
500
    return PRINT_RESULT_RETURN(pass);
24✔
501
}
502

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

507
#if !defined(__WIN__)
508
    static const char* filename1 = "./logs/faildupefile.log";
509
    static const char* filename2 = "logs/faildupefile.log";
510
#else
511
    static const char* filename1 = "logs\\faildupefile.log";
512
    static const char* filename2 = "logs/faildupefile.log";
513
#endif
514

515
    static const char* filename3 = "logs/not-a-dupe.log";
516
    static const char* filename4 = "logs/also-not-a-dupe.log";
517

518
    TEST_MSG("adding log file '%s'...", filename1);
24✔
519

520
    /* should be fine; no other files added yet. */
521
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
24✔
522
    _sir_eqland(pass, 0U != fid);
24✔
523

524
    TEST_MSG("trying again to add log file '%s'...", filename1);
24✔
525

526
    /* should fail. this is the same file we already added. */
527
    _sir_eqland(pass, 0U == sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT));
24✔
528

529
    if (pass)
21✔
530
        PRINT_EXPECTED_ERROR();
21✔
531

532
    TEST_MSG("adding log file '%s'...", filename2);
24✔
533

534
    /* should also fail. this is the same file we already added, even
535
     * if the path strings don't match. */
536
    _sir_eqland(pass, 0U == sir_addfile(filename2, SIRL_ALL, SIRO_DEFAULT));
24✔
537

538
    if (pass)
21✔
539
        PRINT_EXPECTED_ERROR();
21✔
540

541
    TEST_MSG("adding log file '%s'...", filename3);
24✔
542

543
    /* should pass. this is a different file. */
544
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
24✔
545
    _sir_eqland(pass, 0U != fid2);
24✔
546

547
    /* should also pass. */
548
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
24✔
549
    _sir_eqland(pass, 0U != fid3);
24✔
550

551
    _sir_eqland(pass, sir_info("hello three valid files"));
24✔
552

553
    /* should now fail since we added it earlier. */
554
    _sir_eqland(pass, 0U == sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT));
24✔
555

556
    if (pass)
21✔
557
        PRINT_EXPECTED_ERROR();
20✔
558

559
    /* don't remove all of the log files in order to also test
560
     * cache tear-down. */
561
    _sir_eqland(pass, sir_remfile(fid));
24✔
562
    _sir_eqland(pass, sir_cleanup());
24✔
563

564
    rmfile(filename1, cl_cfg.leave_logs);
24✔
565
    rmfile(filename2, cl_cfg.leave_logs);
24✔
566
    rmfile(filename3, cl_cfg.leave_logs);
24✔
567
    rmfile(filename4, cl_cfg.leave_logs);
24✔
568

569
    return PRINT_RESULT_RETURN(pass);
24✔
570
}
571

572
bool sirtest_failremovebadfile(void) {
24✔
573
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
574
    bool pass = si_init;
21✔
575

576
    sirfileid invalidid = 9999999;
21✔
577
    _sir_eqland(pass, !sir_remfile(invalidid));
24✔
578

579
    if (pass)
21✔
580
        PRINT_EXPECTED_ERROR();
24✔
581

582
    _sir_eqland(pass, sir_cleanup());
24✔
583
    return PRINT_RESULT_RETURN(pass);
24✔
584
}
585

586
bool sirtest_rollandarchivefile(void) {
25✔
587
    static const char* filename = "rollandarchive";
588
    static const char* ext1     = ".log";
589
    static const char* ext2     = "";
590

591
    bool pass = true;
22✔
592

593
    _sir_eqland(pass, roll_and_archive(filename, ext1));
25✔
594
    _sir_eqland(pass, roll_and_archive(filename, ext2));
25✔
595

596
    return pass;
25✔
597
}
598

599
bool sirtest_failwithoutinit(void) {
24✔
600
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
24✔
601

602
    if (pass)
24✔
603
        PRINT_EXPECTED_ERROR();
24✔
604

605
    return PRINT_RESULT_RETURN(pass);
24✔
606
}
607

608
bool sirtest_isinitialized(void) {
24✔
609

610
    bool pass = true;
21✔
611

612
    TEST_MSG_0("checking sir_isinitialized before initialization...");
21✔
613
    _sir_eqland(pass, !sir_isinitialized());
24✔
614

615
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
616
    _sir_eqland(pass, si_init);
24✔
617

618
    TEST_MSG_0("checking sir_isinitialized after initialization...");
21✔
619
    _sir_eqland(pass, sir_isinitialized());
24✔
620

621
    _sir_eqland(pass, sir_cleanup());
24✔
622

623
    TEST_MSG_0("checking sir_isinitialized after cleanup...");
21✔
624
    _sir_eqland(pass, !sir_isinitialized());
24✔
625

626
    return PRINT_RESULT_RETURN(pass);
24✔
627
}
628

629
bool sirtest_failinittwice(void) {
24✔
630
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
631
    bool pass = si_init;
21✔
632

633
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
634
    _sir_eqland(pass, !si2_init);
24✔
635

636
    if (pass)
24✔
637
        PRINT_EXPECTED_ERROR();
24✔
638

639
    _sir_eqland(pass, sir_cleanup());
24✔
640
    return PRINT_RESULT_RETURN(pass);
24✔
641
}
642

643
bool sirtest_failinvalidinitdata(void) {
24✔
644
    sirinit si;
645

646
    /* fill with bad data. */
647
    (void)memset(&si, 0xab, sizeof(sirinit));
21✔
648

649
    TEST_MSG_0("calling sir_init with invalid data...");
21✔
650
    bool pass = !sir_init(&si);
24✔
651

652
    if (pass)
24✔
653
        PRINT_EXPECTED_ERROR();
24✔
654

655
    (void)sir_cleanup();
24✔
656
    return PRINT_RESULT_RETURN(pass);
24✔
657
}
658

659
bool sirtest_initcleanupinit(void) {
24✔
660
    INIT(si1, SIRL_ALL, 0, 0, 0);
24✔
661
    bool pass = si1_init;
21✔
662

663
    _sir_eqland(pass, sir_info("init called once; testing output..."));
24✔
664
    _sir_eqland(pass, sir_cleanup());
24✔
665

666
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
667
    _sir_eqland(pass, si2_init);
24✔
668

669
    _sir_eqland(pass, sir_info("init called again after re-init; testing output..."));
24✔
670
    _sir_eqland(pass, sir_cleanup());
24✔
671

672
    return PRINT_RESULT_RETURN(pass);
24✔
673
}
674

675
bool sirtest_initmakeinit(void) {
24✔
676
    bool pass = true;
21✔
677

678
    sirinit si;
679
    _sir_eqland(pass, sir_makeinit(&si));
24✔
680
    _sir_eqland(pass, sir_init(&si));
24✔
681
    _sir_eqland(pass, sir_info("initialized with sir_makeinit"));
24✔
682
    _sir_eqland(pass, sir_cleanup());
24✔
683

684
    return PRINT_RESULT_RETURN(pass);
24✔
685
}
686

687
bool sirtest_failaftercleanup(void) {
24✔
688
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
689
    bool pass = si_init;
21✔
690

691
    _sir_eqland(pass, sir_cleanup());
24✔
692
    _sir_eqland(pass, !sir_info("already cleaned up; this needs to fail"));
24✔
693

694
    if (pass)
21✔
695
        PRINT_EXPECTED_ERROR();
24✔
696

697
    return PRINT_RESULT_RETURN(pass);
24✔
698
}
699

700
bool sirtest_errorsanity(void) {
24✔
701
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
702
    bool pass = si_init;
21✔
703

704
    struct {
705
        uint16_t code;
706
        const char* name;
707
    } errors[] = {
24✔
708
        {SIR_E_NOERROR,   "SIR_E_NOERROR"},   /**< The operation completed successfully (1) */
709
        {SIR_E_NOTREADY,  "SIR_E_NOTREADY"},  /**< libsir has not been initialized (2) */
710
        {SIR_E_ALREADY,   "SIR_E_ALREADY"},   /**< libsir is already initialized (3) */
711
        {SIR_E_DUPITEM,   "SIR_E_DUPITEM"},   /**< Item already managed by libsir (4) */
712
        {SIR_E_NOITEM,    "SIR_E_NOITEM"},    /**< Item not managed by libsir (5) */
713
        {SIR_E_NOROOM,    "SIR_E_NOROOM"},    /**< Maximum number of items already stored (6) */
714
        {SIR_E_OPTIONS,   "SIR_E_OPTIONS"},   /**< Option flags are invalid (7) */
715
        {SIR_E_LEVELS,    "SIR_E_LEVELS"},    /**< Level flags are invalid (8) */
716
        {SIR_E_TEXTSTYLE, "SIR_E_TEXTSTYLE"}, /**< Text style is invalid (9) */
717
        {SIR_E_STRING,    "SIR_E_STRING"},    /**< Invalid string argument (10) */
718
        {SIR_E_NULLPTR,   "SIR_E_NULLPTR"},   /**< NULL pointer argument (11) */
719
        {SIR_E_INVALID,   "SIR_E_INVALID"},   /**< Invalid argument (12) */
720
        {SIR_E_NODEST,    "SIR_E_NODEST"},    /**< No destinations registered for level (13) */
721
        {SIR_E_UNAVAIL,   "SIR_E_UNAVAIL"},   /**< Feature is disabled or unavailable (14) */
722
        {SIR_E_INTERNAL,  "SIR_E_INTERNAL"},  /**< An internal error has occurred (15) */
723
        {SIR_E_COLORMODE, "SIR_E_COLORMODE"}, /**< Invalid color mode (16) */
724
        {SIR_E_TEXTATTR,  "SIR_E_TEXTATTR"},  /**< Invalid text attributes (17) */
725
        {SIR_E_TEXTCOLOR, "SIR_E_TEXTCOLOR"}, /**< Invalid text color (18) */
726
        {SIR_E_PLUGINBAD, "SIR_E_PLUGINBAD"}, /**< Plugin module is malformed (19) */
727
        {SIR_E_PLUGINDAT, "SIR_E_PLUGINDAT"}, /**< Data produced by plugin is invalid (20) */
728
        {SIR_E_PLUGINVER, "SIR_E_PLUGINVER"}, /**< Plugin interface version unsupported (21) */
729
        {SIR_E_PLUGINERR, "SIR_E_PLUGINERR"}, /**< Plugin reported failure (22) */
730
        {SIR_E_PLATFORM,  "SIR_E_PLATFORM"},  /**< Platform error code %d: %s (23) */
731
        {SIR_E_UNKNOWN,   "SIR_E_UNKNOWN"},   /**< Unknown error (4095) */
732
    };
733

734
    char message[SIR_MAXERROR] = {0};
24✔
735
    for (size_t n = 0; n < _sir_countof(errors); n++) {
600✔
736
        if (SIR_E_PLATFORM == errors[n].code) {
576✔
737
            /* cause an actual platform error. */
738
            (void)sir_addfile("invalid/file!name", SIRL_ALL, SIRO_DEFAULT);
24✔
739
        } else {
740
            (void)_sir_seterror(_sir_mkerror(errors[n].code));
552✔
741
        }
742

743
        (void)memset(message, 0, SIR_MAXERROR);
504✔
744
        uint16_t err = sir_geterror(message);
576✔
745
        _sir_eqland(pass, errors[n].code == err && *message != '\0');
576✔
746
        TEST_MSG("%s = %s", errors[n].name, message);
576✔
747

748
        /* ensure that sir_geterrorinfo agrees with sir_geterror, and
749
         * that it returns sane data. */
750
        sir_errorinfo errinfo = {0};
576✔
751
        sir_geterrorinfo(&errinfo);
576✔
752

753
        TEST_MSG("errinfo = {'%s', '%s', %"PRIu32", %"PRIu16", '%s', %d, '%s'}",
576✔
754
            errinfo.func, errinfo.file, errinfo.line, errinfo.code, errinfo.msg,
755
            errinfo.os_code, errinfo.os_msg);
756

757
        _sir_eqland(pass, errinfo.code == err);
576✔
758
        _sir_eqland(pass, errinfo.line != 0U);
576✔
759
        _sir_eqland(pass, _sir_validstrnofail(errinfo.msg));
576✔
760

761
        if (errinfo.code == SIR_E_PLATFORM) {
576✔
762
            _sir_eqland(pass, errinfo.os_code != 0);
22✔
763
            _sir_eqland(pass, _sir_validstrnofail(errinfo.os_msg));
22✔
764
            _sir_eqland(pass, _sir_validstrnofail(errinfo.func));
22✔
765
            _sir_eqland(pass, _sir_validstrnofail(errinfo.file));
22✔
766
        } else {
767
            _sir_eqland(pass, _sir_strsame(__func__, errinfo.func, strlen(__func__)));
554✔
768
            _sir_eqland(pass, _sir_strsame(__file__, errinfo.file, SIR_MAXPATH));
554✔
769
        }
770
    }
771

772
    _sir_eqland(pass, sir_cleanup());
24✔
773
    return PRINT_RESULT_RETURN(pass);
24✔
774
}
775

776
bool sirtest_textstylesanity(void) {
24✔
777
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
778
    bool pass = si_init;
21✔
779

780
    TEST_MSG_0(WHITEB("--- explicitly invalid ---"));
21✔
781
    _sir_eqland(pass, !sir_settextstyle(SIRL_INFO, (sir_textattr)0xbbb, 800, 920));
24✔
782
    _sir_eqland(pass, sir_info("I have set an invalid text style."));
24✔
783

784
    _sir_eqland(pass, !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK));
24✔
785
    _sir_eqland(pass, sir_info("oops, did it again..."));
24✔
786

787
    _sir_eqland(pass, !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff));
24✔
788
    _sir_eqland(pass, sir_info("and again."));
24✔
789
    PASSFAIL_MSG(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
21✔
790

791
    TEST_MSG_0(WHITEB("--- unusual but valid ---"));
21✔
792
    _sir_eqland(pass, sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT));
24✔
793
    _sir_eqland(pass, sir_info("system default fg and bg"));
24✔
794
    PASSFAIL_MSG(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
21✔
795

796
    TEST_MSG_0(WHITEB("--- override defaults ---"));
21✔
797
    _sir_eqland(pass, sir_resettextstyles());
24✔
798

799
    _sir_eqland(pass, sir_debug("default style"));
24✔
800
    _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY));
24✔
801
    _sir_eqland(pass, sir_debug("override style"));
24✔
802

803
    _sir_eqland(pass, sir_info("default style"));
24✔
804
    _sir_eqland(pass, sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA));
24✔
805
    _sir_eqland(pass, sir_info("override style"));
24✔
806

807
    _sir_eqland(pass, sir_notice("default style"));
24✔
808
    _sir_eqland(pass, sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW));
24✔
809
    _sir_eqland(pass, sir_notice("override style"));
24✔
810

811
    _sir_eqland(pass, sir_warn("default style"));
24✔
812
    _sir_eqland(pass, sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE));
24✔
813
    _sir_eqland(pass, sir_warn("override style"));
24✔
814

815
    _sir_eqland(pass, sir_error("default style"));
24✔
816
    _sir_eqland(pass, sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE));
24✔
817
    _sir_eqland(pass, sir_error("override style"));
24✔
818

819
    _sir_eqland(pass, sir_crit("default style"));
24✔
820
    _sir_eqland(pass, sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN));
24✔
821
    _sir_eqland(pass, sir_crit("override style"));
24✔
822

823
    _sir_eqland(pass, sir_alert("default style"));
24✔
824
    _sir_eqland(pass, sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT));
24✔
825
    _sir_eqland(pass, sir_alert("override style"));
24✔
826

827
    _sir_eqland(pass, sir_emerg("default style"));
24✔
828
    _sir_eqland(pass, sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT));
24✔
829
    _sir_eqland(pass, sir_emerg("override style"));
24✔
830
    PASSFAIL_MSG(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
21✔
831

832
    TEST_MSG_0(WHITEB("--- reset to defaults ---"));
21✔
833
    _sir_eqland(pass, sir_resettextstyles());
24✔
834

835
    _sir_eqland(pass, sir_debug("default style (debug)"));
24✔
836
    _sir_eqland(pass, sir_info("default style (info)"));
24✔
837
    _sir_eqland(pass, sir_notice("default style (notice)"));
24✔
838
    _sir_eqland(pass, sir_warn("default style (warning)"));
24✔
839
    _sir_eqland(pass, sir_error("default style (error)"));
24✔
840
    _sir_eqland(pass, sir_crit("default style (crit)"));
24✔
841
    _sir_eqland(pass, sir_alert("default style (alert)"));
24✔
842
    _sir_eqland(pass, sir_emerg("default style (emergency)"));
24✔
843
    PASSFAIL_MSG(pass, "\t--- reset to defaults: %s ---\n\n", PRN_PASS(pass));
21✔
844

845
    TEST_MSG_0(WHITEB("--- change mode: 256-color ---"));
21✔
846
    _sir_eqland(pass, sir_setcolormode(SIRCM_256));
24✔
847

848
    for (sir_textcolor fg = 0, bg = 255; (fg < 256 && bg > 0); fg++, bg--) {
6,144✔
849
        if (fg != bg) {
6,120✔
850
            _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
6,120✔
851
            _sir_eqland(pass, sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
6,120✔
852
                fg, bg));
853
        }
854
    }
855

856
    PASSFAIL_MSG(pass, "\t--- change mode: 256-color: %s ---\n\n", PRN_PASS(pass));
24✔
857

858
    TEST_MSG_0(WHITEB("--- change mode: RGB-color ---"));
21✔
859
    _sir_eqland(pass, sir_setcolormode(SIRCM_RGB));
24✔
860

861
    for (size_t n = 0; n < 256; n++) {
6,168✔
862
        sir_textcolor fg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
863
        sir_textcolor bg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
864
        _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
6,144✔
865
        _sir_eqland(pass, sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
6,144✔
866
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
867
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
868
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg)));
869
    }
870
    PASSFAIL_MSG(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
24✔
871

872
    TEST_MSG_0(WHITEB("--- change mode: invalid mode ---"));
21✔
873
    _sir_eqland(pass, !sir_setcolormode(SIRCM_INVALID));
24✔
874
    sir_textcolor fg = sir_makergb(255, 0, 0);
24✔
875
    sir_textcolor bg = sir_makergb(0, 0, 0);
24✔
876
    _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
24✔
877
    _sir_eqland(pass, sir_debug("this is still RGB color mode"));
24✔
878
    PASSFAIL_MSG(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
21✔
879

880
    TEST_MSG_0(WHITEB("--- change mode: 16-color ---"));
21✔
881
    _sir_eqland(pass, sir_setcolormode(SIRCM_16));
24✔
882
    _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT));
24✔
883
    _sir_eqland(pass, sir_debug("this is 16-color mode (fg: %"PRId32", bg: default)",
24✔
884
        SIRTC_BMAGENTA));
885
    PASSFAIL_MSG(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
21✔
886

887
    _sir_eqland(pass, sir_cleanup());
24✔
888

889
    return PRINT_RESULT_RETURN(pass);
24✔
890
}
891

892
bool sirtest_optionssanity(void) {
24✔
893
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
894
    bool pass = si_init;
21✔
895

896
    static const size_t iterations = 10;
897

898
    /*
899
     * TODO(aremmell): Update printf -> TEST_MSG,
900
     * rename INDENT_ITEM to BULLETED_ITEM.
901
     */
902

903
    /* these should all be valid. */
904
    TEST_MSG_0(WHITEB("--- individual valid options ---"));
21✔
905
    _sir_eqland(pass, _sir_validopts(SIRO_ALL));
24✔
906
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
21✔
907
    _sir_eqland(pass, _sir_validopts(SIRO_NOTIME));
24✔
908
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
21✔
909
    _sir_eqland(pass, _sir_validopts(SIRO_NOHOST));
24✔
910
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
21✔
911
    _sir_eqland(pass, _sir_validopts(SIRO_NOLEVEL));
24✔
912
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
21✔
913
    _sir_eqland(pass, _sir_validopts(SIRO_NONAME));
24✔
914
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
21✔
915
    _sir_eqland(pass, _sir_validopts(SIRO_NOPID));
24✔
916
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
21✔
917
    _sir_eqland(pass, _sir_validopts(SIRO_NOTID));
24✔
918
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
21✔
919
    _sir_eqland(pass, _sir_validopts(SIRO_NOHDR));
24✔
920
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
21✔
921
    _sir_eqland(pass, _sir_validopts(SIRO_MSGONLY));
24✔
922
    (void)printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
21✔
923
    PASSFAIL_MSG(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
24✔
924

925
    /* any combination these bitwise OR'd together
926
     * to form a bitmask should also be valid. */
927
    static const sir_option option_arr[SIR_NUMOPTIONS] = {
928
        SIRO_NOTIME,
929
        SIRO_NOHOST,
930
        SIRO_NOLEVEL,
931
        SIRO_NONAME,
932
        SIRO_NOMSEC,
933
        SIRO_NOPID,
934
        SIRO_NOTID,
935
        SIRO_NOHDR
936
    };
937

938
    TEST_MSG_0(WHITEB("--- random bitmask of valid options ---"));
21✔
939
    uint32_t last_count = SIR_NUMOPTIONS;
21✔
940
    for (size_t n = 0; n < iterations; n++) {
264✔
941
        sir_options opts    = 0;
210✔
942
        uint32_t rand_count = 0;
210✔
943
        size_t last_idx     = 0;
210✔
944

945
        do {
946
            rand_count = getrand(SIR_NUMOPTIONS);
401✔
947
        } while (rand_count == last_count || rand_count <= 1);
401✔
948

949
        last_count = rand_count;
210✔
950

951
        for (size_t i = 0; i < rand_count; i++) {
1,322✔
952
            size_t rand_idx = 0;
942✔
953
            size_t tries    = 0;
942✔
954

955
            do {
956
                if (++tries > SIR_NUMOPTIONS - 2)
1,653✔
957
                    break;
13✔
958
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
1,638✔
959
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,638✔
960

961
            last_idx = rand_idx;
942✔
962
            opts |= option_arr[rand_idx];
1,082✔
963
        }
964

965
        _sir_eqland(pass, _sir_validopts(opts));
240✔
966
        (void)printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
240✔
967
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
968
    }
969
    PASSFAIL_MSG(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
24✔
970

971
    TEST_MSG_0(WHITEB("--- invalid values ---"));
21✔
972

973
    /* the lowest byte is not valid. */
974
    sir_options invalid = 0x000000ff;
21✔
975
    _sir_eqland(pass, !_sir_validopts(invalid));
24✔
976
    (void)printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
21✔
977

978
    /* gaps inbetween valid options. */
979
    invalid = 0x0001ff00U & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
21✔
980
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
981
    _sir_eqland(pass, !_sir_validopts(invalid));
24✔
982
    (void)printf(INDENT_ITEM WHITE("gaps in 0x001ff00U: %08"PRIx32) "\n", invalid);
21✔
983

984
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
985
    for (sir_option o = 0x00008f00U; o < SIRO_NOHDR; o += 0x1000U) {
216✔
986
        _sir_eqland(pass, !_sir_validopts(o));
192✔
987
        (void)printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
168✔
988
    }
989

990
    /* greater than SIRO_NOHDR. */
991
    invalid = (0xFFFF0000 & ~SIRO_NOHDR);
21✔
992
    _sir_eqland(pass, !_sir_validopts(invalid));
24✔
993
    (void)printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
21✔
994

995
    PASSFAIL_MSG(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
24✔
996

997
    _sir_eqland(pass, sir_cleanup());
24✔
998
    return PRINT_RESULT_RETURN(pass);
24✔
999
}
1000

1001
bool sirtest_levelssanity(void) {
24✔
1002
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1003
    bool pass = si_init;
21✔
1004

1005
    static const size_t iterations = 10;
1006

1007
    /* these should all be valid. */
1008
    TEST_MSG_0(WHITEB("--- individual valid levels ---"));
21✔
1009
    _sir_eqland(pass, _sir_validlevel(SIRL_INFO) && _sir_validlevels(SIRL_INFO));
24✔
1010
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_INFO);
21✔
1011
    _sir_eqland(pass, _sir_validlevel(SIRL_DEBUG) && _sir_validlevels(SIRL_DEBUG));
24✔
1012
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_DEBUG);
21✔
1013
    _sir_eqland(pass, _sir_validlevel(SIRL_NOTICE) && _sir_validlevels(SIRL_NOTICE));
24✔
1014
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_NOTICE);
21✔
1015
    _sir_eqland(pass, _sir_validlevel(SIRL_WARN) && _sir_validlevels(SIRL_WARN));
24✔
1016
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_WARN);
21✔
1017
    _sir_eqland(pass, _sir_validlevel(SIRL_ERROR) && _sir_validlevels(SIRL_ERROR));
24✔
1018
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ERROR);
21✔
1019
    _sir_eqland(pass, _sir_validlevel(SIRL_CRIT) && _sir_validlevels(SIRL_CRIT));
24✔
1020
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_CRIT);
21✔
1021
    _sir_eqland(pass, _sir_validlevel(SIRL_ALERT) && _sir_validlevels(SIRL_ALERT));
24✔
1022
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ALERT);
21✔
1023
    _sir_eqland(pass, _sir_validlevel(SIRL_EMERG) && _sir_validlevels(SIRL_EMERG));
24✔
1024
    (void)printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_EMERG);
21✔
1025
    _sir_eqland(pass, _sir_validlevels(SIRL_ALL));
24✔
1026
    (void)printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_ALL);
21✔
1027
    _sir_eqland(pass, _sir_validlevels(SIRL_NONE));
24✔
1028
    (void)printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_NONE);
21✔
1029
    PASSFAIL_MSG(pass, "\t--- individual valid levels: %s ---\n\n", PRN_PASS(pass));
24✔
1030

1031
    /* any combination these bitwise OR'd together
1032
     * to form a bitmask should also be valid. */
1033
    static const sir_levels levels_arr[SIR_NUMLEVELS] = {
1034
        SIRL_EMERG,
1035
        SIRL_ALERT,
1036
        SIRL_CRIT,
1037
        SIRL_ERROR,
1038
        SIRL_WARN,
1039
        SIRL_NOTICE,
1040
        SIRL_INFO,
1041
        SIRL_DEBUG
1042
    };
1043

1044
    TEST_MSG_0(WHITEB("--- random bitmask of valid levels ---"));
21✔
1045
    uint32_t last_count = SIR_NUMLEVELS;
21✔
1046
    for (size_t n = 0; n < iterations; n++) {
264✔
1047
        sir_levels levels   = 0U;
210✔
1048
        uint32_t rand_count = 0U;
210✔
1049
        size_t last_idx     = 0UL;
210✔
1050

1051
        do {
1052
            rand_count = getrand(SIR_NUMLEVELS);
367✔
1053
        } while (rand_count == last_count || rand_count <= 1U);
367✔
1054

1055
        last_count = rand_count;
210✔
1056

1057
        for (size_t i = 0; i < rand_count; i++) {
1,354✔
1058
            size_t rand_idx = 0;
977✔
1059
            size_t tries    = 0;
977✔
1060

1061
            do {
1062
                if (++tries > SIR_NUMLEVELS - 2)
1,729✔
1063
                    break;
13✔
1064
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,713✔
1065
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,713✔
1066

1067
            last_idx = rand_idx;
977✔
1068
            levels |= levels_arr[rand_idx];
1,114✔
1069
        }
1070

1071
        _sir_eqland(pass, _sir_validlevels(levels));
240✔
1072
        (void)printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
240✔
1073
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
1074
    }
1075
    PASSFAIL_MSG(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
24✔
1076

1077
    TEST_MSG_0(WHITEB("--- invalid values ---"));
21✔
1078

1079
    /* greater than SIRL_ALL. */
1080
    sir_levels invalid = (0xffffu & ~SIRL_ALL);
21✔
1081
    _sir_eqland(pass, !_sir_validlevels(invalid));
24✔
1082
    (void)printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
21✔
1083

1084
    /* individual invalid level. */
1085
    sir_level invalid2 = 0x1337U;
21✔
1086
    _sir_eqland(pass, !_sir_validlevel(invalid2));
24✔
1087
    (void)printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx16) "\n", invalid2);
21✔
1088

1089
    PASSFAIL_MSG(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
24✔
1090

1091
    _sir_eqland(pass, sir_cleanup());
24✔
1092
    return PRINT_RESULT_RETURN(pass);
24✔
1093
}
1094

1095
bool sirtest_mutexsanity(void) {
24✔
1096
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1097
    bool pass = si_init;
21✔
1098

1099
    TEST_MSG_0(WHITEB("create, lock, unlock, destroy"));
21✔
1100
    (void)printf(INDENT_ITEM WHITE("creating mutex...") "\n");
21✔
1101

1102
    sir_mutex m1 = SIR_MUTEX_INIT;
24✔
1103
    _sir_eqland(pass, _sir_mutexcreate(&m1));
24✔
1104

1105
    (void)print_test_error(pass, pass);
24✔
1106

1107
    if (pass) {
24✔
1108
        (void)printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
21✔
1109
        _sir_eqland(pass, _sir_mutexlock(&m1));
24✔
1110

1111
        (void)print_test_error(pass, pass);
24✔
1112

1113
        (void)printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
21✔
1114
        _sir_eqland(pass, _sir_mutexunlock(&m1));
24✔
1115

1116
        (void)print_test_error(pass, pass);
24✔
1117

1118
        (void)printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
21✔
1119
        _sir_eqland(pass, _sir_mutextrylock(&m1));
24✔
1120

1121
        (void)print_test_error(pass, pass);
24✔
1122

1123
        (void)printf(INDENT_ITEM WHITE("unlocking...") "\n");
21✔
1124
        _sir_eqland(pass, _sir_mutexunlock(&m1));
24✔
1125

1126
        (void)print_test_error(pass, pass);
24✔
1127

1128
        (void)printf(INDENT_ITEM WHITE("destryoing...") "\n");
21✔
1129
        _sir_eqland(pass, _sir_mutexdestroy(&m1));
24✔
1130

1131
        (void)print_test_error(pass, pass);
24✔
1132

1133
    }
1134
    PASSFAIL_MSG(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
24✔
1135

1136
    TEST_MSG_0(WHITEB("invalid arguments"));
21✔
1137
    (void)printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
21✔
1138
    _sir_eqland(pass, !_sir_mutexcreate(NULL));
24✔
1139
    (void)printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
21✔
1140
    _sir_eqland(pass, !_sir_mutexlock(NULL));
24✔
1141
    (void)printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
21✔
1142
    _sir_eqland(pass, !_sir_mutextrylock(NULL));
24✔
1143
    (void)printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
21✔
1144
    _sir_eqland(pass, !_sir_mutexunlock(NULL));
24✔
1145
    (void)printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
21✔
1146
    _sir_eqland(pass, !_sir_mutexdestroy(NULL));
24✔
1147
    PASSFAIL_MSG(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
21✔
1148

1149
    _sir_eqland(pass, sir_cleanup());
24✔
1150
    return PRINT_RESULT_RETURN(pass); //-V1020
24✔
1151
}
1152

1153
bool sirtest_perf(void) {
1✔
1154
    static const char* logbasename = "libsir-perf";
1155
    static const char* logext      = "";
1156

1157
#if !defined(DUMA)
1158
# if !defined(SIR_PERF_PROFILE)
1159
#  if !defined(__WIN__)
1160
    static const size_t perflines = 1000000;
1161
#  else
1162
    static const size_t perflines = 100000;
1163
#  endif
1164
# else
1165
    static const size_t perflines = 4000000;
1166
# endif
1167
#else /* DUMA */
1168
    static const size_t perflines = 100000;
1169
#endif
1170

1171
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
1✔
1172
    bool pass = si_init;
1✔
1173

1174
    if (pass) {
1✔
1175
        double stdioelapsed  = 0.0;
1✔
1176
        double fileelapsed   = 0.0;
1✔
1177
#if !defined(SIR_PERF_PROFILE)
1178
        double printfelapsed = 0.0;
1✔
1179

1180
        TEST_MSG(BLUE("%zu lines printf..."), perflines);
1✔
1181

1182
        sir_time printftimer = {0};
1✔
1183
        sir_timer_start(&printftimer);
1✔
1184

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

1189
        printfelapsed = sir_timer_elapsed(&printftimer);
1✔
1190
#endif
1191

1192
        TEST_MSG(BLUE("%zu lines libsir (stdout)..."), perflines);
1✔
1193

1194
        sir_time stdiotimer = {0};
1✔
1195
        sir_timer_start(&stdiotimer);
1✔
1196

1197
        for (size_t n = 0; n < perflines; n++)
1,000,001✔
1198
            (void)sir_debug("%.2f: lorem ipsum foo bar %s: %zu",
1,000,000✔
1199
                sir_timer_elapsed(&stdiotimer), "baz", 1234 + n);
1200

1201
        stdioelapsed = sir_timer_elapsed(&stdiotimer);
1✔
1202

1203
        _sir_eqland(pass, sir_cleanup());
1✔
1204

1205
        INIT(si2, 0, 0, 0, 0);
1✔
1206
        _sir_eqland(pass, si2_init);
1✔
1207

1208
        char logfilename[SIR_MAXPATH] = {0};
1✔
1209
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
1✔
1210

1211
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
1✔
1212
        _sir_eqland(pass, 0 != logid);
1✔
1213

1214
        if (pass) {
1✔
1215
            TEST_MSG(BLUE("%zu lines libsir (file)..."), perflines);
1✔
1216

1217
            sir_time filetimer = {0};
1✔
1218
            sir_timer_start(&filetimer);
1✔
1219

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

1223
            fileelapsed = sir_timer_elapsed(&filetimer);
1✔
1224

1225
            _sir_eqland(pass, sir_remfile(logid));
1✔
1226
        }
1227

1228
        if (pass) {
1✔
1229
#if !defined(SIR_PERF_PROFILE)
1230
            TEST_MSG(WHITEB("printf: ") CYAN("%zu lines in %.3fsec (%.1f lines/sec)"),
1✔
1231
                perflines, printfelapsed / 1e3, (double)perflines / (printfelapsed / 1e3));
1232
#endif
1233
            TEST_MSG(WHITEB("libsir (stdout): ")
1✔
1234
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)"), perflines,
1235
                    stdioelapsed / 1e3, (double)perflines / (stdioelapsed / 1e3));
1236

1237
            TEST_MSG(WHITEB("libsir (file): ")
1✔
1238
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)"), perflines,
1239
                    fileelapsed / 1e3, (double)perflines / (fileelapsed / 1e3));
1240

1241
            TEST_MSG(WHITEB("timer resolution: ") CYAN("~%ldnsec"), sir_timer_getres());
1✔
1242
        }
1243
    }
1244

1245
    unsigned deleted = 0U;
1✔
1246
    (void)enumfiles(SIR_TESTLOGDIR, logbasename, !cl_cfg.leave_logs, &deleted);
1✔
1247

1248
    if (deleted > 0U)
1✔
1249
        TEST_MSG(DGRAY("deleted %u log file(s)"), deleted);
1✔
1250

1251
    _sir_eqland(pass, sir_cleanup());
1✔
1252
    return PRINT_RESULT_RETURN(pass);
1✔
1253
}
1254

1255
bool sirtest_updatesanity(void) {
24✔
1256
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
24✔
1257
    bool pass = si_init;
21✔
1258

1259
#define UPDATE_SANITY_ARRSIZE 10
1260

1261
    static const char* logfile = MAKE_LOG_NAME("update-sanity.log");
1262
    static const sir_options opts_array[UPDATE_SANITY_ARRSIZE] = {
1263
        SIRO_NOHOST | SIRO_NOTIME | SIRO_NOLEVEL,
1264
        SIRO_MSGONLY,
1265
        SIRO_NONAME | SIRO_NOTID,
1266
        SIRO_NOPID | SIRO_NOTIME,
1267
        SIRO_NOTIME | SIRO_NOLEVEL | SIRO_NONAME,
1268
        SIRO_NOTIME,
1269
        SIRO_NOMSEC | SIRO_NOHOST,
1270
        SIRO_NOPID | SIRO_NOTID,
1271
        SIRO_NOHOST | SIRO_NOTID,
1272
        SIRO_ALL
1273
    };
1274

1275
    static const sir_levels levels_array[UPDATE_SANITY_ARRSIZE] = {
1276
        SIRL_NONE,
1277
        SIRL_ALL,
1278
        SIRL_EMERG,
1279
        SIRL_ALERT,
1280
        SIRL_CRIT,
1281
        SIRL_ERROR,
1282
        SIRL_WARN,
1283
        SIRL_NOTICE,
1284
        SIRL_INFO,
1285
        SIRL_DEBUG
1286
    };
1287

1288
    rmfile(logfile, cl_cfg.leave_logs);
24✔
1289
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
24✔
1290
    _sir_eqland(pass, 0 != id1);
24✔
1291

1292
    for (int i = 0; i < 10; i++) {
225✔
1293
        if (!pass)
205✔
1294
            break;
4✔
1295

1296
        /* reset to defaults*/
1297
        _sir_eqland(pass, sir_stdoutlevels(SIRL_DEFAULT));
201✔
1298
        _sir_eqland(pass, sir_stderrlevels(SIRL_DEFAULT));
201✔
1299
        _sir_eqland(pass, sir_stdoutopts(SIRO_DEFAULT));
201✔
1300
        _sir_eqland(pass, sir_stderropts(SIRO_DEFAULT));
201✔
1301

1302
        _sir_eqland(pass, sir_debug("default config (debug)"));
201✔
1303
        _sir_eqland(pass, sir_info("default config (info)"));
201✔
1304
        _sir_eqland(pass, sir_notice("default config (notice)"));
201✔
1305
        _sir_eqland(pass, sir_warn("default config (warning)"));
201✔
1306
        _sir_eqland(pass, sir_error("default config (error)"));
201✔
1307
        _sir_eqland(pass, sir_crit("default config (critical)"));
201✔
1308
        _sir_eqland(pass, sir_alert("default config (alert)"));
201✔
1309
        _sir_eqland(pass, sir_emerg("default config (emergency)"));
201✔
1310

1311
        /* pick random options to set/unset */
1312
        uint32_t rnd = getrand(UPDATE_SANITY_ARRSIZE);
201✔
1313
        _sir_eqland(pass, sir_stdoutlevels(levels_array[rnd]));
201✔
1314
        _sir_eqland(pass, sir_stdoutopts(opts_array[rnd]));
201✔
1315
        TEST_MSG(WHITE("set random config #%"PRIu32" for stdout"), rnd);
171✔
1316

1317
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
201✔
1318
        _sir_eqland(pass, sir_stderrlevels(levels_array[rnd]));
201✔
1319
        _sir_eqland(pass, sir_stderropts(opts_array[rnd]));
201✔
1320
        TEST_MSG(WHITE("set random config #%"PRIu32" for stderr"), rnd);
171✔
1321

1322
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
201✔
1323
        _sir_eqland(pass, sir_filelevels(id1, levels_array[rnd]));
201✔
1324
        _sir_eqland(pass, sir_fileopts(id1, opts_array[rnd]));
201✔
1325
        TEST_MSG(WHITE("set random config #%"PRIu32" for %s"), rnd, logfile);
201✔
1326

1327
        _sir_eqland(pass, filter_error(sir_debug("modified config #%"PRIu32" (debug)", rnd), SIR_E_NODEST));
201✔
1328
        _sir_eqland(pass, filter_error(sir_info("modified config #%"PRIu32" (info)", rnd), SIR_E_NODEST));
201✔
1329
        _sir_eqland(pass, filter_error(sir_notice("modified config #%"PRIu32" (notice)", rnd), SIR_E_NODEST));
201✔
1330
        _sir_eqland(pass, filter_error(sir_warn("modified config #%"PRIu32" (warning)", rnd), SIR_E_NODEST));
201✔
1331
        _sir_eqland(pass, filter_error(sir_error("modified config #%"PRIu32" (error)", rnd), SIR_E_NODEST));
201✔
1332
        _sir_eqland(pass, filter_error(sir_crit("modified config #%"PRIu32" (critical)", rnd), SIR_E_NODEST));
201✔
1333
        _sir_eqland(pass, filter_error(sir_alert("modified config #%"PRIu32" (alert)", rnd), SIR_E_NODEST));
201✔
1334
        _sir_eqland(pass, filter_error(sir_emerg("modified config #%"PRIu32" (emergency)", rnd), SIR_E_NODEST));
201✔
1335
    }
1336

1337
    if (pass) {
24✔
1338
        /* restore to default config and run again */
1339
        _sir_eqland(pass, sir_stdoutlevels(SIRL_DEFAULT));
20✔
1340
        _sir_eqland(pass, sir_stderrlevels(SIRL_DEFAULT));
20✔
1341
        _sir_eqland(pass, sir_stdoutopts(SIRO_DEFAULT));
20✔
1342
        _sir_eqland(pass, sir_stderropts(SIRO_DEFAULT));
20✔
1343

1344
        _sir_eqland(pass, sir_debug("default config (debug)"));
20✔
1345
        _sir_eqland(pass, sir_info("default config (info)"));
20✔
1346
        _sir_eqland(pass, sir_notice("default config (notice)"));
20✔
1347
        _sir_eqland(pass, sir_warn("default config (warning)"));
20✔
1348
        _sir_eqland(pass, sir_error("default config (error)"));
20✔
1349
        _sir_eqland(pass, sir_crit("default config (critical)"));
20✔
1350
        _sir_eqland(pass, sir_alert("default config (alert)"));
20✔
1351
        _sir_eqland(pass, sir_emerg("default config (emergency)"));
20✔
1352
    }
1353

1354
    _sir_eqland(pass, sir_remfile(id1));
24✔
1355
    rmfile(logfile, cl_cfg.leave_logs);
24✔
1356

1357
    _sir_eqland(pass, sir_cleanup());
24✔
1358
    return PRINT_RESULT_RETURN(pass);
24✔
1359
}
1360

1361
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED) || \
1362
    defined(SIR_EVENTLOG_ENABLED)
1363
static
1364
bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
18✔
1365
    static const int runs = 5;
1366

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

1371
# if !defined(__WIN__)
1372
    uint32_t rnd = (uint32_t)(_sir_getpid() + _sir_gettid());
18✔
1373
# else
1374
    uint32_t rnd = (uint32_t)GetTickCount64();
1375
# endif
1376

1377
    bool pass = true;
15✔
1378
    for (int i = 1; i <= runs; i++) {
108✔
1379
        /* randomly skip setting process name, identity/category to thoroughly
1380
         * test fallback routines; randomly update the config mid-run. */
1381
        bool set_procname = getrand_bool(rnd ^ 0x5a5a5a5aU);
90✔
1382
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9U);
90✔
1383
        bool set_category = getrand_bool(rnd ^ 0x32323232U);
90✔
1384
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7U);
90✔
1385

1386
        TEST_MSG("set_procname: %d, set_identity: %d, set_category: %d, do_update: %d",
90✔
1387
            set_procname, set_identity, set_category, do_update);
1388

1389
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
95✔
1390
        si.d_syslog.opts   = SIRO_DEFAULT;
90✔
1391
        si.d_syslog.levels = SIRL_DEFAULT;
90✔
1392

1393
        if (set_identity)
90✔
1394
            (void)_sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
39✔
1395

1396
        if (set_category)
90✔
1397
            (void)_sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
43✔
1398

1399
        _sir_eqland(pass, sir_init(&si));
90✔
1400

1401
        if (do_update)
90✔
1402
            _sir_eqland(pass, sir_sysloglevels(SIRL_ALL));
56✔
1403

1404
        _sir_eqland(pass, sir_debug("%d/%d: this debug message sent to stdout and %s.", i, runs, sl_name));
90✔
1405
        _sir_eqland(pass, sir_info("%d/%d: this info message sent to stdout and %s.", i, runs, sl_name));
90✔
1406

1407
        _sir_eqland(pass, sir_notice("%d/%d: this notice message sent to stdout and %s.", i, runs, sl_name));
90✔
1408
        _sir_eqland(pass, sir_warn("%d/%d: this warning message sent to stdout and %s.", i, runs, sl_name));
90✔
1409
        _sir_eqland(pass, sir_error("%d/%d: this error message sent to stdout and %s.", i, runs, sl_name));
90✔
1410

1411
        if (set_identity) {
90✔
1412
            _sir_eqland(pass, sir_syslogid("my test ID"));
39✔
1413
            _sir_eqland(pass, sir_syslogid("my test ID")); /* test deduping. */
39✔
1414
        }
1415

1416
        if (set_category) {
90✔
1417
            _sir_eqland(pass, sir_syslogcat("my test category"));
43✔
1418
            _sir_eqland(pass, sir_syslogcat("my test category")); /* test deduping. */
43✔
1419
        }
1420

1421
        if (do_update)
90✔
1422
            _sir_eqland(pass, sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID)));
56✔
1423

1424
        _sir_eqland(pass, sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name));
90✔
1425
        _sir_eqland(pass, sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name));
90✔
1426
        _sir_eqland(pass, sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name));
90✔
1427

1428
# if defined(SIR_OS_LOG_ENABLED)
1429
#  if defined(__MACOS__) && !defined(__INTEL_COMPILER)
1430
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1431
            TEST_MSG_0("testing os_log activity feature...");
1432

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

1438
            /* execution now passes to os_log_parent_activity(), where some logging
1439
             * will occur, then a sub-activity will be created, and more logging. */
1440
            os_activity_apply_f(parent, (void*)parent, os_log_parent_activity);
1441
        }
1442
#  endif
1443
# endif
1444

1445
        _sir_eqland(pass, sir_cleanup());
90✔
1446

1447
        if (!pass)
90✔
1448
            break;
×
1449
    }
1450

1451
    return PRINT_RESULT_RETURN(pass);
18✔
1452
}
1453
#endif
1454

1455
#if defined(SIR_NO_SYSTEM_LOGGERS)
1456
static bool generic_disabled_syslog_test(const char* sl_name, const char* identity,
18✔
1457
    const char* category) {
1458
    INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0U, 0U, "sir_disabled_sltest");
18✔
1459
    si.d_syslog.opts   = SIRO_DEFAULT;
18✔
1460
    si.d_syslog.levels = SIRL_DEFAULT;
18✔
1461
    bool pass = true;
18✔
1462

1463
    SIR_UNUSED(sl_name);
1464

1465
    TEST_MSG_0("SIR_NO_SYSTEM_LOGGERS is defined; expecting calls to fail...");
18✔
1466

1467
    /* init should just ignore the syslog settings. */
1468
    _sir_eqland(pass, sir_init(&si));
18✔
1469

1470
    /* these calls should all fail. */
1471
    TEST_MSG_0("setting levels...");
18✔
1472
    _sir_eqland(pass, !sir_sysloglevels(SIRL_ALL));
18✔
1473

1474
    if (pass)
18✔
1475
        PRINT_EXPECTED_ERROR();
18✔
1476

1477
    TEST_MSG_0("setting options...");
18✔
1478
    _sir_eqland(pass, !sir_syslogopts(SIRO_DEFAULT));
18✔
1479

1480
    if (pass)
18✔
1481
        PRINT_EXPECTED_ERROR();
18✔
1482

1483
    TEST_MSG_0("setting identity...");
18✔
1484
    _sir_eqland(pass, !sir_syslogid(identity));
18✔
1485

1486
    if (pass)
18✔
1487
        PRINT_EXPECTED_ERROR();
18✔
1488

1489
    TEST_MSG_0("setting category...");
18✔
1490
    _sir_eqland(pass, !sir_syslogcat(category));
18✔
1491

1492
    if (pass)
18✔
1493
        PRINT_EXPECTED_ERROR();
18✔
1494

1495
    _sir_eqland(pass, sir_cleanup());
18✔
1496
    return PRINT_RESULT_RETURN(pass);
18✔
1497
}
1498
#endif
1499

1500
bool sirtest_syslog(void) {
24✔
1501
#if !defined(SIR_SYSLOG_ENABLED)
1502
# if defined(SIR_NO_SYSTEM_LOGGERS)
1503
    bool pass = generic_disabled_syslog_test("syslog", "sirtests", "tests");
6✔
1504
    return PRINT_RESULT_RETURN(pass);
6✔
1505
# else
1506
    TEST_MSG_0(DGRAY("SIR_SYSLOG_ENABLED is not defined; skipping"));
1507
    return true;
1508
# endif
1509
#else
1510
    bool pass = generic_syslog_test("syslog", "sirtests", "tests");
18✔
1511
    return PRINT_RESULT_RETURN(pass);
18✔
1512
#endif
1513
}
1514

1515
bool sirtest_os_log(void) {
24✔
1516
#if !defined(SIR_OS_LOG_ENABLED)
1517
# if defined(SIR_NO_SYSTEM_LOGGERS)
1518
    bool pass = generic_disabled_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
6✔
1519
    return PRINT_RESULT_RETURN(pass);
6✔
1520
# else
1521
    TEST_MSG_0(DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping"));
15✔
1522
    return true;
18✔
1523
# endif
1524
#else
1525
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1526
    return PRINT_RESULT_RETURN(pass);
1527
#endif
1528
}
1529

1530
bool sirtest_win_eventlog(void) {
24✔
1531
#if !defined(SIR_EVENTLOG_ENABLED)
1532
# if defined(SIR_NO_SYSTEM_LOGGERS)
1533
    bool pass = generic_disabled_syslog_test("eventlog", "sirtests", "tests");
6✔
1534
    return PRINT_RESULT_RETURN(pass);
6✔
1535
# else
1536
    TEST_MSG_0(DGRAY("SIR_EVENTLOG_ENABLED is not defined; skipping"));
15✔
1537
    return true;
18✔
1538
# endif
1539
#else
1540
    bool pass = generic_syslog_test("eventlog", "sirtests", "tests");
1541
    return PRINT_RESULT_RETURN(pass);
1542
#endif
1543
}
1544

1545
bool sirtest_filesystem(void) {
24✔
1546
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1547
    bool pass = si_init;
21✔
1548

1549
    /* Wine version */
1550
    TEST_MSG("Running under Wine: %s",
24✔
1551
            get_wineversion() ? get_wineversion() : "no");
1552

1553
    /* current working directory. */
1554
    char* cwd = _sir_getcwd();
24✔
1555
    _sir_eqland(pass, NULL != cwd);
24✔
1556
    TEST_MSG("_sir_getcwd: '%s'", PRN_STR(cwd));
24✔
1557

1558
    if (NULL != cwd) {
24✔
1559
        /* path to this binary file. */
1560
        char* filename = _sir_getappfilename();
24✔
1561
        _sir_eqland(pass, NULL != filename);
24✔
1562
        TEST_MSG("_sir_getappfilename: '%s'", PRN_STR(filename));
24✔
1563

1564
        if (NULL != filename) {
24✔
1565
            /* _sir_get[base|dir]name() can potentially modify filename,
1566
             * so make a copy for each call. */
1567
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
21✔
1568
            _sir_eqland(pass, NULL != filename2);
21✔
1569

1570
            if (NULL != filename2) {
21✔
1571
                /* filename, stripped of directory component(s). */
1572
                char* _basename = _sir_getbasename(filename2);
20✔
1573
                TEST_MSG("_sir_getbasename: '%s'", PRN_STR(_basename));
20✔
1574

1575
                if (!_basename) {
20✔
1576
                    pass = false;
×
1577
                } else {
1578
                    /* the last strlen(_basename) chars of filename should match. */
1579
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
20✔
1580
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
20✔
1581
                    size_t n      = 0;
17✔
1582

1583
                    while (n < len) {
180✔
1584
                        if (filename[offset++] != _basename[n++]) {
160✔
1585
                            pass = false;
×
1586
                            break;
×
1587
                        }
1588
                    }
1589
                }
1590
            }
1591

1592
            /* directory this binary file resides in. */
1593
            char* appdir = _sir_getappdir();
21✔
1594
            _sir_eqland(pass, NULL != appdir);
21✔
1595
            TEST_MSG("_sir_getappdir: '%s'", PRN_STR(appdir));
21✔
1596

1597
            /* _sir_get[base|dir]name can potentially modify filename,
1598
             * so make a copy for each call. */
1599
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
21✔
1600
            _sir_eqland(pass, NULL != filename3);
21✔
1601

1602
            if (NULL != appdir && NULL != filename3) {
21✔
1603
                /* should yield the same result as _sir_getappdir(). */
1604
                char* _dirname = _sir_getdirname(filename3);
20✔
1605
                TEST_MSG("_sir_getdirname: '%s'", PRN_STR(_dirname));
20✔
1606

1607
                _sir_eqland(pass, 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH)));
20✔
1608
                _sir_eqland(pass, NULL != _dirname &&
20✔
1609
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH)));
1610
            }
1611

1612
            _sir_safefree(&appdir);
21✔
1613
            _sir_safefree(&filename);
21✔
1614
            _sir_safefree(&filename2);
21✔
1615
            _sir_safefree(&filename3);
21✔
1616
        }
1617

1618
        _sir_safefree(&cwd);
24✔
1619
    }
1620

1621
    /* this next section doesn't really yield any useful boolean pass/fail
1622
     * information, but could be helpful to review manually. */
1623
    char* dubious_dirnames[] = {
24✔
1624
#if !defined(__WIN__)
1625
        "/foo",
1626
        "/foo/",
1627
        "/foo/bar",
1628
        "/foo/bar/bad:filename",
1629
        "/",
1630
        ""
1631
#else /* __WIN__ */
1632
        "C:\\foo",
1633
        "C:\\foo\\",
1634
        "C:\\foo\\bar",
1635
        "C:\\foo\\bar\\bad:>filename",
1636
        "C:\\",
1637
        "//network-share/myfolder",
1638
        ""
1639
#endif
1640
    };
1641

1642
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
168✔
1643
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
144✔
1644
        if (NULL != tmp) {
144✔
1645
            TEST_MSG("_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "",
138✔
1646
                tmp, _sir_getdirname(tmp));
1647
            _sir_safefree(&tmp);
138✔
1648
        }
1649
    }
1650

1651
    char* dubious_filenames[] = {
24✔
1652
#if !defined(__WIN__)
1653
        "foo/bar/file-or-directory",
1654
        "/foo/bar/file-or-directory",
1655
        "/foo/bar/illegal:filename",
1656
        "/",
1657
        ""
1658
#else /* __WIN__ */
1659
        "foo\\bar\\file.with.many.full.stops",
1660
        "C:\\foo\\bar\\poorly-renamed.txt.pdf",
1661
        "C:\\foo\\bar\\illegal>filename.txt",
1662
        "C:\\",
1663
        "\\Program Files\\foo.bar",
1664
        ""
1665
#endif
1666
    };
1667

1668
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
144✔
1669
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
120✔
1670
        if (NULL != tmp) {
120✔
1671
            TEST_MSG("_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "",
115✔
1672
                tmp, _sir_getbasename(tmp));
1673
            _sir_safefree(&tmp);
115✔
1674
        }
1675
    }
1676

1677
    /* absolute/relative paths. */
1678
    static const struct {
1679
        const char* const path;
1680
        bool abs;
1681
    } abs_or_rel_paths[] = {
1682
        {"this/is/relative", false},
1683
        {"relative", false},
1684
        {"./relative", false},
1685
        {"../../relative", false},
1686
#if !defined(__WIN__)
1687
        {"/usr/local/bin", true},
1688
        {"/", true},
1689
        {"/home/foo/.config", true},
1690
        {"~/.config", true}
1691
#else /* __WIN__ */
1692
        {"D:\\absolute", true},
1693
        {"C:\\Program Files\\FooBar", true},
1694
        {"C:\\", true},
1695
        {"\\absolute", true},
1696
#endif
1697
    };
1698

1699
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
216✔
1700
        bool relative = false;
192✔
1701
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
192✔
1702

1703
        if (relative == abs_or_rel_paths[n].abs) {
192✔
1704
            pass = false;
×
1705
            TEST_MSG(RED("_sir_ispathrelative('%s') = %s"), abs_or_rel_paths[n].path,
×
1706
                relative ? "true" : "false");
1707
        } else {
1708
            TEST_MSG(GREEN("_sir_ispathrelative('%s') = %s"), abs_or_rel_paths[n].path,
192✔
1709
                relative ? "true" : "false");
1710
        }
1711

1712
        _sir_eqland(pass, ret);
192✔
1713
        if (!ret) {
192✔
1714
            bool unused = print_test_error(false, false);
×
1715
            SIR_UNUSED(unused);
1716
        }
1717
    }
1718

1719
    /* file existence. */
1720
    static const struct {
1721
        const char* const path;
1722
        bool exists;
1723
    } real_or_not[] = {
1724
        {"../foobarbaz", false},
1725
        {"foobarbaz", false},
1726
#if !defined(__WIN__)
1727
        {"/", true},
1728
# if !defined(__HAIKU__)
1729
        {"/usr/bin", true},
1730
# else
1731
        {"/bin", true},
1732
# endif
1733
        {"/dev", true},
1734
#else /* __WIN__ */
1735
        {"C:\\Windows", true},
1736
        {"C:\\Program Files", true},
1737
        {"\\", true},
1738
        {".\\", true},
1739
        {"..\\", true},
1740
#endif
1741
        {"../../LICENSES/MIT.txt", true},
1742
        {"../../msvs/libsir.sln", true},
1743
        {"./", true},
1744
        {"../", true},
1745
        {"file.exists", true}
1746
    };
1747

1748
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
264✔
1749
        bool exists = false;
240✔
1750
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
240✔
1751

1752
        if (exists != real_or_not[n].exists) {
240✔
1753
            pass = false;
23✔
1754
            TEST_MSG(RED("_sir_pathexists('%s') = %s"), real_or_not[n].path,
23✔
1755
                exists ? "true" : "false");
1756
        } else {
1757
            TEST_MSG(GREEN("_sir_pathexists('%s') = %s"), real_or_not[n].path,
217✔
1758
                exists ? "true" : "false");
1759
        }
1760

1761
        _sir_eqland(pass, ret);
240✔
1762
        if (!ret) {
240✔
1763
            bool unused = print_test_error(false, false);
28✔
1764
            SIR_UNUSED(unused);
1765
        }
1766
    }
1767

1768
    /* checking file descriptors. */
1769
    static int bad_fds[] = {
1770
        0,
1771
        1,
1772
        2,
1773
        1234
1774
    };
1775

1776
#if defined(__WIN__)
1777
    if (get_wineversion()) {
1778
        bad_fds[3] = 0;
1779
    }
1780
#endif
1781

1782
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
120✔
1783
        if (_sir_validfd(bad_fds[n])) {
96✔
UNCOV
1784
            pass = false;
×
UNCOV
1785
            TEST_MSG(RED("_sir_validfd(%d) = true"), bad_fds[n]);
×
1786
        } else {
1787
            TEST_MSG(GREEN("_sir_validfd(%d) = false"), bad_fds[n]);
96✔
1788
        }
1789
    }
1790

1791
    FILE* f = NULL;
24✔
1792
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
24✔
1793
    if (!ret) {
24✔
1794
        pass = false;
4✔
1795
        HANDLE_OS_ERROR(true, "fopen(%s) failed!", "file.exists");
4✔
1796
    } else {
1797
        int fd = fileno(f);
20✔
1798
        if (!_sir_validfd(fd)) {
20✔
UNCOV
1799
            pass = false;
×
UNCOV
1800
            TEST_MSG(RED("_sir_validfd(%d) = false"), fd);
×
1801
        } else {
1802
            TEST_MSG(GREEN("_sir_validfd(%d) = true"), fd);
17✔
1803
        }
1804
    }
1805

1806
    _sir_safefclose(&f);
24✔
1807

1808
    _sir_eqland(pass, sir_cleanup());
24✔
1809
    return PRINT_RESULT_RETURN(pass);
24✔
1810
}
1811

1812
bool sirtest_squelchspam(void) {
24✔
1813
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1814
    bool pass = si_init;
21✔
1815

1816
    static const size_t alternate  = 50;
1817
    static const size_t sequence[] = {
1818
        1000, /* non-repeating messages. */
1819
        1000, /* repeating messages. */
1820
        1000, /* alternating repeating and non-repeating messages. */
1821
        100   /* repeating messages, but on different levels. */
1822
    };
1823

1824
    sir_time timer;
1825
    sir_timer_start(&timer);
24✔
1826

1827
    TEST_MSG(BLUE("%zu non-repeating messages..."), sequence[0]);
21✔
1828

1829
    for (size_t n = 0, ascii_idx = 33; n < sequence[0]; n++, ascii_idx++) {
24,024✔
1830
        _sir_eqland(pass, sir_debug("%c%c a non-repeating message", (char)ascii_idx,
24,000✔
1831
            (char)ascii_idx + 1));
1832

1833
        if (ascii_idx == 125)
24,000✔
1834
            ascii_idx = 33;
210✔
1835
    }
1836

1837
    TEST_MSG(BLUE("%zu repeating messages..."), sequence[1]);
21✔
1838

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

1842
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
24,000✔
1843
            _sir_eqland(pass, !ret);
23,904✔
1844
        else
1845
            _sir_eqland(pass, ret);
96✔
1846
    }
1847

1848
    TEST_MSG(BLUE("%zu alternating repeating and non-repeating messages..."),
21✔
1849
        sequence[2]);
1850

1851
    bool repeating   = false;
21✔
1852
    size_t counter   = 0;
21✔
1853
    size_t repeat_id = 0;
21✔
1854
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
24,024✔
1855
        if (!repeating) {
24,000✔
1856
            _sir_eqland(pass, sir_debug("%c%c a non-repeating message", (char)ascii_idx,
12,024✔
1857
                (char)ascii_idx + 1));
1858
        } else {
1859
            bool ret = sir_debug("%zu a repeating message", repeat_id);
11,976✔
1860

1861
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
11,976✔
1862
                _sir_eqland(pass, !ret);
11,016✔
1863
            else
1864
                _sir_eqland(pass, ret);
960✔
1865
        }
1866

1867
        if (counter == alternate) {
24,000✔
1868
            repeating = !repeating;
456✔
1869
            counter = 0;
399✔
1870
            repeat_id++;
456✔
1871
        }
1872

1873
        if (ascii_idx == 125)
24,000✔
1874
            ascii_idx = 33;
210✔
1875
    }
1876

1877
    TEST_MSG(BLUE("%zu repeating messages, but on different levels..."), sequence[3]);
21✔
1878

1879
    for (size_t n = 0; n < sequence[3]; n++) {
2,424✔
1880
        if (n % 2 == 0)
2,400✔
1881
            _sir_eqland(pass, sir_debug("a repeating message"));
1,200✔
1882
        else
1883
            _sir_eqland(pass, sir_info("a repeating message"));
1,200✔
1884
    }
1885

1886
    _sir_eqland(pass, sir_cleanup());
24✔
1887
    return PRINT_RESULT_RETURN(pass);
24✔
1888
}
1889

1890
bool sirtest_pluginloader(void) {
24✔
1891
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1892
    bool pass = si_init;
21✔
1893

1894
#if !defined(__WIN__)
1895
# define PLUGIN_EXT "so"
1896
#else
1897
# define PLUGIN_EXT "dll"
1898
#endif
1899

1900
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1901
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1902
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1903
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1904
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1905
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1906
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1907
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1908

1909
#if defined(SIR_NO_PLUGINS)
1910
    SIR_UNUSED(plugin2);
1911
    SIR_UNUSED(plugin3);
1912
    SIR_UNUSED(plugin4);
1913
    SIR_UNUSED(plugin5);
1914
    SIR_UNUSED(plugin6);
1915
    SIR_UNUSED(plugin7);
1916
    SIR_UNUSED(plugin8);
1917

1918
    TEST_MSG_0("SIR_NO_PLUGINS is defined; expecting calls to fail");
1✔
1919

1920
    TEST_MSG("loading good plugin: '%s'...", plugin1);
1✔
1921
    /* load a valid, well-behaved plugin. */
1922
    sirpluginid id = sir_loadplugin(plugin1);
1✔
1923
    _sir_eqland(pass, 0 == id);
1✔
1924

1925
    if (pass)
1✔
1926
        PRINT_EXPECTED_ERROR();
1✔
1927

1928
    TEST_MSG("unloading good plugin: '%s'...", plugin1);
1✔
1929
    /* also try the unload function. */
1930
    _sir_eqland(pass, !sir_unloadplugin(id));
1✔
1931

1932
    if (pass)
1✔
1933
        PRINT_EXPECTED_ERROR();
1✔
1934
#else
1935
    /* load a valid, well-behaved plugin. */
1936
    TEST_MSG("loading good plugin: '%s'...", plugin1);
23✔
1937
    sirpluginid id = sir_loadplugin(plugin1);
23✔
1938
    _sir_eqland(pass, 0 != id);
23✔
1939

1940
    (void)print_test_error(pass, pass);
23✔
1941

1942
    _sir_eqland(pass, sir_info("this message will be dispatched to the plugin."));
23✔
1943
    _sir_eqland(pass, sir_warn("this message will *not* be dispatched to the plugin."));
23✔
1944

1945
    /* re-loading the same plugin should fail. */
1946
    TEST_MSG("loading duplicate plugin: '%s'...", plugin1);
23✔
1947
    sirpluginid badid = sir_loadplugin(plugin1);
23✔
1948
    _sir_eqland(pass, 0 == badid);
23✔
1949

1950
    (void)print_test_error(pass, pass);
23✔
1951

1952
    /* the following are all invalid or misbehaved, and should all fail. */
1953
    TEST_MSG("loading bad plugin: '%s'...", plugin2);
23✔
1954
    badid = sir_loadplugin(plugin2);
23✔
1955
    _sir_eqland(pass, 0 == badid);
23✔
1956

1957
    (void)print_test_error(pass, pass);
23✔
1958

1959
    TEST_MSG("loading bad plugin: '%s'...", plugin3);
23✔
1960
    badid = sir_loadplugin(plugin3);
23✔
1961
    _sir_eqland(pass, 0 == badid);
23✔
1962

1963
    (void)print_test_error(pass, pass);
23✔
1964

1965
    TEST_MSG("loading bad plugin: '%s'...", plugin4);
23✔
1966
    badid = sir_loadplugin(plugin4);
23✔
1967
    _sir_eqland(pass, 0 == badid);
23✔
1968

1969
    (void)print_test_error(pass, pass);
23✔
1970

1971
    TEST_MSG("loading bad plugin: '%s'...", plugin5);
23✔
1972
    badid = sir_loadplugin(plugin5);
23✔
1973
    _sir_eqland(pass, 0 == badid);
23✔
1974

1975
    (void)print_test_error(pass, pass);
23✔
1976

1977
    TEST_MSG("loading bad plugin: '%s'...", plugin6);
23✔
1978
    badid = sir_loadplugin(plugin6);
23✔
1979
    _sir_eqland(pass, 0 == badid);
23✔
1980

1981
    (void)print_test_error(pass, pass);
23✔
1982

1983
    TEST_MSG("loading bad plugin: '%s'...", plugin7);
23✔
1984
    badid = sir_loadplugin(plugin7);
23✔
1985
    _sir_eqland(pass, 0 != badid); /* this one should load, just return false from write */
23✔
1986

1987
    _sir_eqland(pass, !sir_info("should fail; a plugin failed to process the message."));
23✔
1988

1989
    (void)print_test_error(pass, pass);
23✔
1990

1991
    TEST_MSG("loading nonexistent plugin: '%s'...", plugin8);
23✔
1992
    badid = sir_loadplugin(plugin8);
23✔
1993
    _sir_eqland(pass, 0 == badid);
23✔
1994

1995
    (void)print_test_error(pass, pass);
23✔
1996

1997
    /* unload the good plugin manually. */
1998
    TEST_MSG("unloading good plugin: '%s'...", plugin1);
23✔
1999
    _sir_eqland(pass, sir_unloadplugin(id));
23✔
2000

2001
    (void)print_test_error(pass, pass);
23✔
2002

2003
    /* try to unload the plugin again. */
2004
    TEST_MSG("unloading already unloaded plugin '%s'...", plugin1);
23✔
2005
    _sir_eqland(pass, !sir_unloadplugin(id));
23✔
2006

2007
    (void)print_test_error(pass, pass);
23✔
2008

2009
    /* test bad paths. */
2010
    TEST_MSG_0("trying to load plugin with NULL path...");
20✔
2011
    badid = sir_loadplugin(NULL);
23✔
2012
    _sir_eqland(pass, 0 == badid);
23✔
2013

2014
    (void)print_test_error(pass, pass);
23✔
2015
#endif
2016
    _sir_eqland(pass, sir_cleanup());
24✔
2017
    return PRINT_RESULT_RETURN(pass);
24✔
2018
}
2019

2020
bool sirtest_stringutils(void) {
24✔
2021
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
2022
    bool pass = si_init;
21✔
2023

2024
    char str[] = "Kneel  \f \n  before  \t \r \v  Zod!?";
24✔
2025

2026
    TEST_MSG_0(WHITEB("--- valid string utility usage ---"));
21✔
2027

2028
    _sir_eqland(pass, _sir_strsqueeze(str) && 0 == strncmp(str, "Kneel before Zod!?", 18));
24✔
2029
    TEST_MSG("_sir_strsqueeze:            '%s'", str);
21✔
2030

2031
    _sir_eqland(pass, _sir_strremove(str, "!") && 0 == strncmp(str, "Kneel before Zod?", 17));
24✔
2032
    TEST_MSG("_sir_strremove(\"!\"):        '%s'", str);
21✔
2033

2034
    _sir_eqland(pass, _sir_strreplace(str, '?', '.') && 0 == strncmp(str, "Kneel before Zod.", 17));
24✔
2035
    TEST_MSG("_sir_strreplace(\"?\", \".\"):  '%s'", str);
21✔
2036

2037
    _sir_eqland(pass, 1 == _sir_strcreplace(str, '.', '!', 1) && 0 == strncmp(str, "Kneel before Zod!", 17));
24✔
2038
    TEST_MSG("_sir_strcreplace(\".\", \"!\"): '%s'", str);
21✔
2039

2040
    _sir_eqland(pass, _sir_strredact(str, "e", '*') && 0 == strncmp(str, "Kn**l b*for* Zod!", 17));
24✔
2041
    TEST_MSG("_sir_strredact(\"e\", \"*\"):   '%s'", str);
21✔
2042

2043
    _sir_eqland(pass, _sir_strredact(str, "X", 'Y') && 0 == strncmp(str, "Kn**l b*for* Zod!", 17));
24✔
2044
    TEST_MSG("_sir_strredact(\"X\", \"Y\"):   '%s'", str);
21✔
2045

2046
    PASSFAIL_MSG(pass, "\t--- valid string utility usage: %s ---\n\n", PRN_PASS(pass));
24✔
2047

2048
    TEST_MSG_0(WHITEB("--- invalid string utility usage - NULL pointer ---"));
21✔
2049

2050
    TEST_MSG_0("_sir_strsqueeze:  NULL pointer");
21✔
2051
    _sir_eqland(pass, !_sir_strsqueeze(NULL));
24✔
2052

2053
    TEST_MSG_0("_sir_strremove:   NULL pointer");
21✔
2054
    _sir_eqland(pass, !_sir_strremove(NULL, "sub"));
24✔
2055

2056
    TEST_MSG_0("_sir_strreplace:  NULL pointer");
21✔
2057
    _sir_eqland(pass, !_sir_strreplace(NULL, 'c', 'n'));
24✔
2058

2059
    TEST_MSG_0("_sir_strcreplace: NULL pointer");
21✔
2060
    _sir_eqland(pass, !_sir_strcreplace(NULL, 'c', 'n', -1));
24✔
2061

2062
    TEST_MSG_0("_sir_strredact:   NULL pointer");
21✔
2063
    _sir_eqland(pass, !_sir_strredact(NULL, "s", '*'));
24✔
2064

2065
    PASSFAIL_MSG(pass, "\t--- invalid string utility usage - NULL pointer: %s ---\n\n", PRN_PASS(pass));
21✔
2066

2067
    TEST_MSG_0(WHITEB("--- invalid string utility usage - bad parameters ---"));
21✔
2068

2069
    TEST_MSG_0("_sir_strremove:   bad parameter \"sub\"");
21✔
2070
    _sir_eqland(pass, _sir_strremove(str, NULL));
24✔
2071

2072
    TEST_MSG_0("_sir_strreplace:  bad parameter 'c'");
21✔
2073
    _sir_eqland(pass, _sir_strreplace(str, 0, 'n'));
24✔
2074

2075
    TEST_MSG_0("_sir_strreplace:  bad parameter 'n'");
21✔
2076
    _sir_eqland(pass, _sir_strreplace(str, 'c', 0));
24✔
2077

2078
    TEST_MSG_0("_sir_strcreplace: bad parameter 'c'");
21✔
2079
    _sir_eqland(pass, !_sir_strcreplace(str, 0, 'n', -1));
24✔
2080

2081
    TEST_MSG_0("_sir_strcreplace: bad parameter 'n'");
21✔
2082
    _sir_eqland(pass, !_sir_strcreplace(str, 'c', 0, -1));
24✔
2083

2084
    TEST_MSG_0("_sir_strcreplace: bad parameter 'max'");
21✔
2085
    _sir_eqland(pass, !_sir_strcreplace(str, 'c', 'n', 0));
24✔
2086

2087
    TEST_MSG_0("_sir_strredact:   bad parameter \"sub\"");
21✔
2088
    _sir_eqland(pass, _sir_strredact(str, NULL, '*'));
24✔
2089

2090
    TEST_MSG_0("_sir_strredact:   bad parameter 'c'");
21✔
2091
    _sir_eqland(pass, _sir_strredact(str, "sub", 0));
24✔
2092

2093
    PASSFAIL_MSG(pass, "\t--- invalid string utility usage - bad parameters: %s ---\n", PRN_PASS(pass));
21✔
2094

2095
    _sir_eqland(pass, sir_cleanup());
24✔
2096
    return PRINT_RESULT_RETURN(pass);
24✔
2097
}
2098

2099
bool sirtest_getcpucount(void) {
24✔
2100
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
2101
    bool pass = si_init;
21✔
2102

2103
    TEST_MSG_0("checking processor counting function...");
21✔
2104

2105
    const long cpus = _sir_nprocs_test();
24✔
2106
    _sir_eqland(pass, 0 < cpus);
24✔
2107

2108
    TEST_MSG("processor(s) detected: %ld", cpus);
21✔
2109

2110
    _sir_eqland(pass, sir_cleanup());
24✔
2111
    return PRINT_RESULT_RETURN(pass);
24✔
2112
}
2113

2114
bool sirtest_getversioninfo(void) {
24✔
2115
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
2116
    bool pass = si_init;
21✔
2117

2118
    TEST_MSG_0("checking version retrieval functions...");
21✔
2119

2120
    const char* str = sir_getversionstring();
24✔
2121
    _sir_eqland(pass, _sir_validstrnofail(str));
24✔
2122

2123
    TEST_MSG("version as string: '%s'", _SIR_PRNSTR(str));
24✔
2124

2125
    uint32_t hex = sir_getversionhex();
24✔
2126
    _sir_eqland(pass, 0 != hex);
24✔
2127

2128
    TEST_MSG("version as hex: 0x%08"PRIx32"", hex);
21✔
2129

2130
    bool prerel = sir_isprerelease();
24✔
2131
    TEST_MSG("prerelease: %s", prerel ? "true" : "false");
24✔
2132

2133
    _sir_eqland(pass, sir_cleanup());
24✔
2134
    return PRINT_RESULT_RETURN(pass);
24✔
2135
}
2136

2137
enum {
2138
    NUM_THREADS = 4
2139
};
2140

2141
static bool threadpool_pseudojob(void* arg) {
176✔
2142
    char thread_name[SIR_MAXPID] = {0};
176✔
2143

2144
    _sir_snprintf_trunc(thread_name, SIR_MAXPID, "pool.%p", arg);
176✔
2145
    (void)_sir_setthreadname(thread_name);
176✔
2146

2147
    (void)sir_debug("start of pseudo job that does nothing (arg: %p)", arg);
176✔
2148
    sir_sleep_msec(1000);
176✔
2149
    (void)sir_debug("end of pseudo job that does nothing (arg: %p)", arg);
176✔
2150

2151
    return true;
176✔
2152
}
2153

2154
bool sirtest_threadpool(void) {
24✔
2155
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
24✔
2156
    bool pass = si_init;
21✔
2157

2158
    static const size_t num_jobs = 30;
2159
    sir_threadpool* pool         = NULL;
24✔
2160

2161
    _sir_eqland(pass, _sir_threadpool_create(&pool, NUM_THREADS));
24✔
2162
    if (pass) {
21✔
2163
        /* dispatch a whole bunch of jobs. */
2164
        for (size_t n = 0; n < num_jobs; n++) {
682✔
2165
            sir_threadpool_job* job = calloc(1, sizeof(sir_threadpool_job));
660✔
2166
            _sir_eqland(pass, NULL != job);
660✔
2167
            if (job) {
660✔
2168
                job->fn = &threadpool_pseudojob;
660✔
2169
                job->data = (void*)(n + 1);
660✔
2170
                _sir_eqland(pass, _sir_threadpool_add_job(pool, job));
660✔
2171
                _sir_eqland(pass, sir_info("dispatched job (fn: %"PRIxPTR", data: %p)",
660✔
2172
                    (uintptr_t)job->fn, job->data));
2173
            }
2174
        }
2175

2176
        sir_sleep_msec(1000);
22✔
2177

2178
        _sir_eqland(pass, sir_info("destroying thread pool..."));
22✔
2179
        _sir_eqland(pass, _sir_threadpool_destroy(&pool));
22✔
2180
    }
2181

2182
    _sir_eqland(pass, sir_cleanup());
24✔
2183
    return PRINT_RESULT_RETURN(pass);
24✔
2184
}
2185

2186
#if !defined(__WIN__)
2187
static void* threadrace_thread(void* arg);
2188
#else /* __WIN__ */
2189
static unsigned __stdcall threadrace_thread(void* arg);
2190
#endif
2191

2192
typedef struct {
2193
    char log_file[SIR_MAXPATH];
2194
    bool pass;
2195
} thread_args;
2196

2197
bool sirtest_threadrace(void) {
24✔
2198
#if !defined(__WIN__)
2199
    pthread_t thrds[NUM_THREADS] = {0};
24✔
2200
#else /* __WIN__ */
2201
    uintptr_t thrds[NUM_THREADS] = {0};
2202
#endif
2203

2204
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
24✔
2205
    bool pass           = si_init;
21✔
2206
    bool any_created    = false;
21✔
2207
    size_t last_created = 0;
21✔
2208

2209
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
24✔
2210
    if (!heap_args) {
24✔
2211
        HANDLE_OS_ERROR(true, "calloc() %zu bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2212
        return false;
1✔
2213
    }
2214

2215
    for (size_t n = 0; n < NUM_THREADS; n++) {
111✔
2216
        if (!pass)
89✔
UNCOV
2217
            break;
×
2218

2219
        heap_args[n].pass = true;
89✔
2220
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
89✔
2221
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2222

2223
#if !defined(__WIN__)
2224
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
89✔
2225
        if (0 != create) {
89✔
2226
            errno = create;
1✔
2227
            HANDLE_OS_ERROR(true, "pthread_create() for thread #%zu failed!", n + 1);
1✔
2228
#else /* __WIN__ */
2229
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2230
        if (0 == thrds[n]) {
2231
            HANDLE_OS_ERROR(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2232
#endif
2233
            pass = false;
1✔
2234
            break;
1✔
2235
        }
2236

2237
        last_created = n;
76✔
2238
        any_created  = true;
76✔
2239
    }
2240

2241
    if (any_created) {
23✔
2242
        for (size_t j = 0; j < last_created + 1; j++) {
110✔
2243
            bool joined = true;
76✔
2244
            TEST_MSG("waiting for thread %zu/%zu...", j + 1, last_created + 1);
88✔
2245
#if !defined(__WIN__)
2246
            int join = pthread_join(thrds[j], NULL);
88✔
2247
            if (0 != join) {
88✔
2248
                joined = false;
×
UNCOV
2249
                errno  = join;
×
UNCOV
2250
                HANDLE_OS_ERROR(true, "pthread_join() for thread #%zu failed!", j + 1);
×
2251
            }
2252
#else /* __WIN__ */
2253
            DWORD wait = WaitForSingleObject((HANDLE)thrds[j], INFINITE);
2254
            if (WAIT_OBJECT_0 != wait) {
2255
                joined = false;
2256
                HANDLE_OS_ERROR(false, "WaitForSingleObject() for thread #%zu (%p) failed!", j + 1,
2257
                    (HANDLE)thrds[j]);
2258
            }
2259
#endif
2260
            _sir_eqland(pass, joined);
76✔
2261
            if (joined) {
76✔
2262
                TEST_MSG("thread %zu/%zu joined", j + 1, last_created + 1);
76✔
2263

2264
                _sir_eqland(pass, heap_args[j].pass);
88✔
2265
                if (heap_args[j].pass)
88✔
2266
                    TEST_MSG(GREEN("thread #%zu returned pass = true"), j + 1);
76✔
2267
                else
UNCOV
2268
                    TEST_MSG(RED("thread #%zu returned pass = false!"), j + 1);
×
2269
            }
2270
        }
2271
    }
2272

2273
    _sir_safefree(&heap_args);
23✔
2274

2275
    _sir_eqland(pass, sir_cleanup());
23✔
2276
    return PRINT_RESULT_RETURN(pass);
23✔
2277
}
2278

2279
#if !defined(__WIN__)
2280
static void* threadrace_thread(void* arg) {
88✔
2281
#else /* __WIN__ */
2282
unsigned __stdcall threadrace_thread(void* arg) {
2283
#endif
2284
    pid_t threadid       = _sir_gettid();
88✔
2285
    thread_args* my_args = (thread_args*)arg;
76✔
2286

2287
    rmfile(my_args->log_file, cl_cfg.leave_logs);
88✔
2288
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
88✔
2289

2290
    if (0U == id) {
88✔
2291
        bool unused = print_test_error(false, false);
8✔
2292
        SIR_UNUSED(unused);
2293
#if !defined(__WIN__)
2294
        return NULL;
8✔
2295
#else /* __WIN__ */
2296
        return 0;
2297
#endif
2298
    }
2299

2300
    TEST_MSG("hi, i'm thread (id: " SIR_TIDFORMAT "), logging to: '%s'...",
68✔
2301
            PID_CAST threadid, my_args->log_file);
2302

2303
#if !defined(DUMA)
2304
# if !defined(__EMSCRIPTEN__)
2305
#  define NUM_ITERATIONS 400
2306
# else
2307
#  define NUM_ITERATIONS 200
2308
# endif
2309
#else
2310
# define NUM_ITERATIONS 100
2311
#endif
2312

2313
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
32,079✔
2314
        /* choose a random level, and colors. */
2315
        sir_textcolor fg = SIRTC_DEFAULT;
27,200✔
2316
        sir_textcolor bg = SIRTC_DEFAULT;
27,200✔
2317

2318
        if (n % 2 == 0) {
31,999✔
2319
            fg = SIRTC_CYAN;
13,600✔
2320
            bg = SIRTC_BLACK;
13,600✔
2321
            (void)sir_debug("log message #%zu", n);
15,999✔
2322
        } else {
2323
            fg = SIRTC_BLACK;
13,600✔
2324
            bg = SIRTC_CYAN;
13,600✔
2325
            (void)sir_info("log message #%zu", n);
16,000✔
2326
        }
2327

2328
        /* sometimes remove and re-add the log file, and set some options/styles.
2329
         * other times, just set different options/styles. */
2330
        uint32_t rnd = (uint32_t)(n + threadid);
32,000✔
2331
        if (getrand_bool(rnd > 1U ? rnd : 1U)) {
32,000✔
2332
            my_args->pass = print_test_error(sir_remfile(id), false);
15,994✔
2333
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
15,995✔
2334
                SIRL_ALL, SIRO_MSGONLY), false);
2335

2336
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
31,990✔
2337
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
15,995✔
2338
            my_args->pass = print_test_error(test, false);
15,995✔
2339

2340
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
15,995✔
2341
            my_args->pass = print_test_error(test, false);
15,995✔
2342
        } else {
2343
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
32,010✔
2344
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
16,005✔
2345
            my_args->pass = print_test_error(test, false);
16,005✔
2346

2347
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
16,005✔
2348
            my_args->pass = print_test_error(test, false);
16,005✔
2349

2350
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
16,005✔
2351
            my_args->pass = print_test_error(test, false);
16,005✔
2352
        }
2353

2354
        if (!my_args->pass)
31,999✔
UNCOV
2355
            break;
×
2356
    }
2357

2358
    my_args->pass = print_test_error(sir_remfile(id), false);
80✔
2359

2360
    rmfile(my_args->log_file, cl_cfg.leave_logs);
80✔
2361

2362
#if !defined(__WIN__)
2363
    return NULL;
80✔
2364
#else /* __WIN__ */
2365
    return 0U;
2366
#endif
2367
}
2368

2369
/*
2370
bool sirtest_XXX(void) {
2371
    INIT(si, SIRL_ALL, 0, 0, 0);
2372
    bool pass = si_init;
2373

2374
    _sir_eqland(pass, sir_cleanup());
2375
    return PRINT_RESULT_RETURN(pass);
2376
}
2377
*/
2378

2379
#if defined(SIR_OS_LOG_ENABLED)
2380
void os_log_parent_activity(void* ctx) {
2381
    (void)sir_debug("confirming with ground control that we are a go...");
2382
    (void)sir_info("all systems go; initiating launch sequence");
2383
    (void)sir_warn("getting some excessive vibration here");
2384
    (void)sir_info("safely reached escape velocity. catch you on the flip side");
2385
    (void)sir_info("(3 days later) we have landed on the lunar surface");
2386
    (void)sir_notice("beginning rock counting...");
2387

2388
    os_activity_t parent = (os_activity_t)ctx;
2389
    os_activity_t child = os_activity_create("counting moon rocks", parent, //-V530
2390
        OS_ACTIVITY_FLAG_DEFAULT);
2391

2392
    float rock_count = 0.0f;
2393
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2394
    (void)sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2395
        (double)rock_count);
2396
}
2397

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

2401
    for (size_t n = 0; n < 10; n++) {
2402
        (void)sir_info("counting rocks in sector %zu...", n);
2403
    }
2404

2405
    float* rock_count = (float*)ctx;
2406
    *rock_count = 1e12f;
2407
    (void)sir_info("all sectors counted; heading back to the lunar lander");
2408
}
2409
#endif
2410

2411
/* ========================== end tests ========================== */
2412

2413
bool filter_error(bool pass, uint16_t err) {
1,608✔
2414
    if (!pass) {
1,608✔
2415
        char msg[SIR_MAXERROR] = {0};
785✔
2416
        if (sir_geterror(msg) != err)
785✔
2417
            return false;
8✔
2418
    }
2419
    return true;
1,360✔
2420
}
2421

2422
char *get_wineversion(void) {
24✔
2423
#if !defined(__WIN__)
2424
    return NULL;
24✔
2425
#else /* __WIN__ */
2426
    typedef char* (__stdcall *get_wine_ver_proc)(void);
2427
    static get_wine_ver_proc _p_wine_get_version = NULL;
2428

2429
    HMODULE _h_ntdll = GetModuleHandle("ntdll.dll");
2430
    if (_h_ntdll != NULL) {
2431
        _p_wine_get_version = (get_wine_ver_proc)GetProcAddress(_h_ntdll, "wine_get_version");
2432
        if (_p_wine_get_version) {
2433
            char *wine_version = _p_wine_get_version();
2434
            if (wine_version)
2435
                return wine_version;
2436
        }
2437
    }
2438
    return NULL;
2439
#endif
2440
}
2441

2442
bool roll_and_archive(const char* filename, const char* extension) {
50✔
2443
    /* roll size minus 1KiB so we can write until it maxes. */
2444
    static const long deltasize = 1024L;
2445
    const long fillsize         = SIR_FROLLSIZE - deltasize;
44✔
2446

2447
    char logfilename[SIR_MAXPATH] = {0};
50✔
2448
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), filename, extension);
44✔
2449

2450
    TEST_MSG_0("deleting any stale logs from a previous run...");
44✔
2451

2452
    unsigned delcount = 0U;
50✔
2453
    if (!enumfiles(SIR_TESTLOGDIR, filename, !cl_cfg.leave_logs, &delcount)) {
50✔
2454
        HANDLE_OS_ERROR(false, "failed to enumerate log files with base name: %s!", filename);
6✔
2455
        return false;
6✔
2456
    }
2457

2458
    if (delcount > 0U)
44✔
2459
        TEST_MSG("found and removed %u log file(s)", delcount);
4✔
2460

2461
    FILE* f = NULL;
44✔
2462
    _sir_fopen(&f, logfilename, "w");
44✔
2463

2464
    if (NULL == f)
44✔
UNCOV
2465
        return print_test_error(false, false);
×
2466

2467
    TEST_MSG("filling %s nearly to SIR_FROLLSIZE...", logfilename);
38✔
2468

2469
    if (0 != fseek(f, fillsize, SEEK_SET)) {
44✔
2470
        HANDLE_OS_ERROR(true, "fseek in file %s failed!", logfilename);
2✔
2471
        _sir_safefclose(&f);
2✔
2472
        return false;
2✔
2473
    }
2474

2475
    if (EOF == fputc('\0', f)) {
42✔
2476
        HANDLE_OS_ERROR(true, "fputc in file %s failed!", logfilename);
2✔
2477
        _sir_safefclose(&f);
2✔
2478
        return false;
2✔
2479
    }
2480

2481
    _sir_safefclose(&f);
40✔
2482

2483
    INIT(si, 0, 0, 0, 0);
40✔
2484
    bool pass = si_init;
34✔
2485

2486
    TEST_MSG("adding %s to libsir...", logfilename);
34✔
2487

2488
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
40✔
2489
    _sir_eqland(pass, 0U != fileid);
40✔
2490

2491
    (void)print_test_error(pass, false);
40✔
2492

2493
    if (pass) {
40✔
2494
        static const char* line = "hello, i am some data. nice to meet you.";
2495
        TEST_MSG("writing to %s until SIR_FROLLSIZE has been exceeded...", logfilename);
30✔
2496

2497
        /* write an (approximately) known quantity until we should have rolled */
2498
        size_t written  = 0;
30✔
2499
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
36✔
2500

2501
        do {
2502
            _sir_eqland(pass, sir_debug("%zu %s", written, line));
2,736✔
2503
            written += linesize;
2,736✔
2504
        } while (pass && (written < deltasize + (linesize * 50)));
2,736✔
2505

2506
        TEST_MSG_0("looking for two log files, since it should have been rolled...");
30✔
2507

2508
        /* look for files matching the original name. */
2509
        unsigned foundlogs = 0U;
36✔
2510
        if (!enumfiles(SIR_TESTLOGDIR, filename, false, &foundlogs)) {
36✔
UNCOV
2511
            HANDLE_OS_ERROR(false, "failed to enumerate log files with base name: %s!", filename);
×
UNCOV
2512
            pass = false;
×
2513
        }
2514

2515
        /* if two (or more) are present, the test is a pass. */
2516
        TEST_MSG("found %u log files with base name: %s", foundlogs, filename);
36✔
2517
        _sir_eqland(pass, foundlogs >= 2U);
36✔
2518
    }
2519

2520
    _sir_eqland(pass, sir_remfile(fileid));
40✔
2521

2522
    delcount = 0U;
40✔
2523
    if (!enumfiles(SIR_TESTLOGDIR, filename, !cl_cfg.leave_logs, &delcount)) {
40✔
UNCOV
2524
        HANDLE_OS_ERROR(false, "failed to enumerate log files with base name: %s!", filename);
×
UNCOV
2525
        pass = false;
×
2526
    }
2527

2528
    if (delcount > 0U)
40✔
2529
        TEST_MSG("found and removed %u log file(s)", delcount);
34✔
2530

2531
    _sir_eqland(pass, sir_cleanup());
40✔
2532
    return PRINT_RESULT_RETURN(pass);
40✔
2533
}
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