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

aremmell / libsir / 376

03 Sep 2023 10:18AM UTC coverage: 94.796% (-0.09%) from 94.888%
376

push

gitlab-ci

aremmell
update config.h to use %Y instead of %y (gcc warnings)

3060 of 3228 relevant lines covered (94.8%)

621037.12 hits per line

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

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

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

64
static bool leave_logs = false;
65

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

71
#if defined(MTMALLOC)
72
# include <mtmalloc.h>
73
# if !defined(DEBUG)
74
mallocctl(MTDOUBLEFREE, 0);
75
# else
76
mallocctl(MTDOUBLEFREE, 1);
77
mallocctl(MTINITBUFFER, 1);
78
mallocctl(MTDEBUGPATTERN, 1);
79
# endif
80
#endif
81

82
#if defined(__OpenBSD__) && defined(DEBUG)
83
extern char *malloc_options;
84
malloc_options = "CFGRSU";
85
#endif
86

87
#if defined(DUMA)
88
# if defined(DUMA_EXPLICIT_INIT)
89
duma_init();
90
# endif
91
# if defined(DUMA_MIN_ALIGNMENT)
92
#  if DUMA_MIN_ALIGNMENT > 0
93
DUMA_SET_ALIGNMENT(DUMA_MIN_ALIGNMENT);
94
#  endif
95
# endif
96
DUMA_SET_FILL(0x2E);
97
#endif
98

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

114
    bool wait     = false;
28✔
115
    bool only     = false;
28✔
116
    size_t to_run = 0;
28✔
117

118
    for (int n = 1; n < argc; n++) {
35✔
119
        if (_sir_strsame(argv[n], _cl_arg_list[0].flag,
10✔
120
            strnlen(_cl_arg_list[0].flag, SIR_MAXCLIFLAG))) { /* --perf */
9✔
121
            only = mark_test_to_run("performance");
1✔
122
            if (only)
1✔
123
                to_run = 1;
1✔
124
        } else if (_sir_strsame(argv[n], _cl_arg_list[1].flag,
9✔
125
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --only */
8✔
126
            while (++n < argc) {
4✔
127
                if (_sir_validstrnofail(argv[n])) {
2✔
128
                    if (*argv[n] == '-' || !mark_test_to_run(argv[n])) {
2✔
129
                        fprintf(stderr, RED("invalid argument: '%s'") "\n", argv[n]);
1✔
130
                        print_usage_info();
1✔
131
                        return EXIT_FAILURE;
1✔
132
                    }
133
                    to_run++;
1✔
134
                }
135
            }
136
            if (0 == to_run) {
2✔
137
                fprintf(stderr, RED("value expected for '%s'") "\n",
1✔
138
                    _cl_arg_list[1].flag);
1✔
139
                print_usage_info();
1✔
140
                return EXIT_FAILURE;
1✔
141
            }
142
            only = true;
1✔
143
        } else if (_sir_strsame(argv[n], _cl_arg_list[2].flag,
6✔
144
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --list */
5✔
145
            print_test_list();
1✔
146
            return EXIT_SUCCESS;
1✔
147
        } else if (_sir_strsame(argv[n], _cl_arg_list[3].flag,
5✔
148
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --leave-logs */
4✔
149
            leave_logs = true;
1✔
150
        } else if (_sir_strsame(argv[n], _cl_arg_list[4].flag,
4✔
151
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --wait */
3✔
152
            wait = true;
×
153
        }  else if (_sir_strsame(argv[n], _cl_arg_list[5].flag,
3✔
154
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --version */
3✔
155
            print_libsir_version();
1✔
156
            return EXIT_SUCCESS;
1✔
157
        }else if (_sir_strsame(argv[n], _cl_arg_list[6].flag,
2✔
158
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --help */
2✔
159
            print_usage_info();
1✔
160
            return EXIT_SUCCESS;
1✔
161
        } else {
162
            fprintf(stderr, "unknown argument: '%s'", argv[n]);
1✔
163
            print_usage_info();
1✔
164
            return EXIT_FAILURE;
1✔
165
        }
166
    }
167

168
    size_t first     = (only ? 0 : 1);
25✔
169
    size_t tgt_tests = (only ? to_run : _sir_countof(sir_tests) - first);
25✔
170
    size_t passed    = 0;
22✔
171
    size_t ran       = 0;
22✔
172
    sir_timer timer  = {0};
25✔
173

174
    printf(WHITEB("\n" ULINE("libsir") " %s (%s) running %zu %s...") "\n",
50✔
175
        sir_getversionstring(), (sir_isprerelease() ? "prerelease" : "release"),
25✔
176
        tgt_tests, TEST_S(tgt_tests));
177
    sirtimerstart(&timer);
25✔
178

179
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
802✔
180
        if (only && !sir_tests[n].run) {
777✔
181
            _sir_selflog("skipping '%s'; not marked to run", sir_tests[n].name);
62✔
182
            continue;
62✔
183
        }
184

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

187
        sir_tests[n].pass = sir_tests[n].fn();
715✔
188
        if (sir_tests[n].pass)
715✔
189
            passed++;
670✔
190

191
        ran++;
622✔
192

193
        printf(WHITEB("\n(%zu/%zu) '%s' finished: ") "%s\n", ran, tgt_tests, sir_tests[n].name,
715✔
194
            PRN_PASS(sir_tests[n].pass));
622✔
195
    }
196

197
    float elapsed = sirtimerelapsed(&timer);
25✔
198

199
    if (passed == tgt_tests) {
25✔
200
        printf("\n" WHITEB("done: ")
10✔
201
                   GREENB("%s%zu " ULINE("libsir") " %s passed in %.03fsec!") "\n\n",
202
            tgt_tests > 1 ? "all " : "", tgt_tests, TEST_S(tgt_tests), (double)elapsed / (double)1e3);
10✔
203
    } else {
204
        printf("\n" WHITEB("done: ")
15✔
205
                   REDB("%zu of %zu " ULINE("libsir") " %s failed in %.03fsec") "\n\n",
206
            tgt_tests - passed, tgt_tests, TEST_S(tgt_tests), (double)elapsed / (double)1e3);
15✔
207

208
        printf(REDB("Failed %s:") "\n\n", TEST_S(tgt_tests - passed));
15✔
209

210
        for (size_t t = 0; t < _sir_countof(sir_tests); t++)
495✔
211
            if (!sir_tests[t].pass)
480✔
212
                printf(RED(INDENT_ITEM "%s\n"), sir_tests[t].name);
45✔
213
        printf("\n");
15✔
214
    }
215

216
    if (wait) {
25✔
217
        printf(WHITEB(EMPH("press any key to exit...")) "\n");
×
218
        char ch = '\0';
1✔
219
        (void)_sir_getchar(&ch);
1✔
220
        SIR_UNUSED(ch);
221
    }
222

223
    return passed == tgt_tests ? EXIT_SUCCESS : EXIT_FAILURE;
25✔
224
}
225

226
bool sirtest_exceedmaxsize(void) {
23✔
227
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
228
    bool pass = si_init;
20✔
229

230
    char toobig[SIR_MAXMESSAGE + 100] = {0};
20✔
231
    memset(toobig, 'a', SIR_MAXMESSAGE + 100);
20✔
232
    toobig[SIR_MAXMESSAGE + 99] = '\0';
23✔
233

234
    pass &= sir_info("%s", toobig);
23✔
235

236
    sir_cleanup();
23✔
237
    return print_result_and_return(pass);
23✔
238
}
239

240
bool sirtest_threadidsanity(void)
23✔
241
{
242
    INIT(si, SIRL_ALL, SIRO_NOHOST, 0, 0);
23✔
243
    bool pass = si_init;
20✔
244

245
    static const char* thread_name = "mythread";
246

247
    /* this is a particularly difficult test to write: the behavior of libsir
248
     * is set at compile time, so for certain configurations, we cannot perform
249
     * the necessary testing. */
250
    printf("\tlogging a message normally...\n");
20✔
251
    pass &= sir_debug("this is a test of the libsir system");
23✔
252

253
    printf("\tsetting the thread name to '%s' and logging again...\n", thread_name);
23✔
254

255
    _sir_setthreadname(thread_name);
23✔
256
    sir_sleep_msec(SIR_MISC_CHK_INTERVAL * 3);
23✔
257

258
    pass &= sir_debug("this is a test of the libsir system after setting thread name");
23✔
259

260
    printf("\tsetting the thread name to '' and logging again...\n");
20✔
261

262
    _sir_setthreadname("");
23✔
263
    sir_sleep_msec(SIR_MISC_CHK_INTERVAL * 3);
23✔
264

265
    pass &= sir_debug("this is a test of the libsir system after clearing thread name");
23✔
266

267
    pass &= sir_cleanup();
23✔
268
    return pass;
23✔
269
}
270

271
bool sirtest_failnooutputdest(void) {
23✔
272
    INIT(si, 0, 0, 0, 0);
23✔
273
    bool pass = si_init;
20✔
274

275
    static const char* logfilename = MAKE_LOG_NAME("nodestination.log");
276

277
    pass &= !sir_notice("this goes nowhere!");
23✔
278

279
    if (pass) {
23✔
280
        print_expected_error();
23✔
281

282
        pass &= sir_stdoutlevels(SIRL_INFO);
23✔
283
        pass &= sir_info("this goes to stdout");
23✔
284
        pass &= sir_stdoutlevels(SIRL_NONE);
23✔
285

286
        sirfileid fid = sir_addfile(logfilename, SIRL_INFO, SIRO_DEFAULT);
23✔
287
        pass &= 0U != fid;
23✔
288
        pass &= sir_info("this goes to %s", logfilename);
23✔
289
        pass &= sir_filelevels(fid, SIRL_NONE);
23✔
290
        pass &= !sir_notice("this goes nowhere!");
23✔
291

292
        if (0U != fid)
23✔
293
            pass &= sir_remfile(fid);
20✔
294

295
        rmfile(logfilename);
23✔
296
    }
297

298
    sir_cleanup();
23✔
299
    return print_result_and_return(pass);
23✔
300
}
301

302
bool sirtest_failnulls(void) {
23✔
303
    INIT_BASE(si, SIRL_ALL, 0, 0, 0, "", false);
23✔
304
    bool pass = true;
20✔
305

306
    pass &= !sir_init(NULL);
23✔
307

308
    if (pass)
23✔
309
        print_expected_error();
23✔
310

311
    pass &= sir_init(&si);
23✔
312
    pass &= !sir_info(NULL); //-V575 //-V618
23✔
313

314
    if (pass)
23✔
315
        print_expected_error();
23✔
316

317
    pass &= 0U == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY);
23✔
318

319
    if (pass)
23✔
320
        print_expected_error();
23✔
321

322
    pass &= !sir_remfile(0U);
23✔
323

324
    if (pass)
23✔
325
        print_expected_error();
23✔
326

327
    sir_cleanup();
23✔
328
    return print_result_and_return(pass);
23✔
329
}
330

331
bool sirtest_failemptymessage(void) {
23✔
332
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
333
    bool pass = si_init;
20✔
334

335
    pass &= !sir_debug("%s", "");
23✔
336

337
    sir_cleanup();
23✔
338
    return print_result_and_return(pass);
23✔
339
}
340

341
bool sirtest_filecachesanity(void) {
23✔
342
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
343
    bool pass = si_init;
20✔
344

345
    size_t numfiles             = SIR_MAXFILES + 1;
20✔
346
    sirfileid ids[SIR_MAXFILES] = {0};
23✔
347

348
    sir_options even = SIRO_MSGONLY;
20✔
349
    sir_options odd  = SIRO_ALL;
20✔
350

351
    for (size_t n = 0; n < numfiles - 1; n++) {
391✔
352
        char path[SIR_MAXPATH] = {0};
368✔
353
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), n);
320✔
354
        rmfile(path);
368✔
355
        ids[n] = sir_addfile(path, SIRL_ALL, (n % 2) ? odd : even);
392✔
356
        pass &= 0U != ids[n] && sir_info("test %zu", n);
368✔
357
    }
358

359
    pass &= sir_info("test test test");
23✔
360

361
    /* this one should fail; max files already added. */
362
    pass &= 0U == sir_addfile(MAKE_LOG_NAME("should-fail.log"), SIRL_ALL, SIRO_MSGONLY);
23✔
363

364
    if (pass)
23✔
365
        print_expected_error();
19✔
366

367
    sir_info("test test test");
23✔
368

369
    /* now remove previously added files in a different order. */
370
    size_t removeorder[SIR_MAXFILES];
371
    memset(removeorder, -1, sizeof(removeorder));
20✔
372

373
    long processed = 0L;
20✔
374
    printf("\tcreating random file ID order...\n");
20✔
375

376
    do {
1,104✔
377
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,286✔
378
        bool skip  = false;
1,124✔
379

380
        for (size_t n = 0; n < SIR_MAXFILES; n++)
12,280✔
381
            if (removeorder[n] == rnd) {
11,912✔
382
                skip = true;
804✔
383
                break;
804✔
384
            }
385

386
        if (skip)
1,286✔
387
            continue;
918✔
388

389
        removeorder[processed++] = rnd;
368✔
390

391
        if (processed == SIR_MAXFILES)
368✔
392
            break;
20✔
393
    } while (true);
394

395
    printf("\tremove order: {");
20✔
396
    for (size_t n = 0; n < SIR_MAXFILES; n++)
391✔
397
        printf(" %zu%s", removeorder[n], (n < SIR_MAXFILES - 1) ? "," : "");
371✔
398
    printf(" }...\n");
20✔
399

400
    for (size_t n = 0; n < SIR_MAXFILES; n++) {
391✔
401
        pass &= sir_remfile(ids[removeorder[n]]);
368✔
402

403
        char path[SIR_MAXPATH] = {0};
368✔
404
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), removeorder[n]);
368✔
405
        rmfile(path);
368✔
406
    }
407

408
    pass &= sir_info("test test test");
23✔
409

410
    sir_cleanup();
23✔
411
    return print_result_and_return(pass);
23✔
412
}
413

414
bool sirtest_failinvalidfilename(void) {
23✔
415
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
416
    bool pass = si_init;
20✔
417

418
    pass &= 0U == sir_addfile("bad file!/name", SIRL_ALL, SIRO_MSGONLY);
23✔
419

420
    if (pass)
23✔
421
        print_expected_error();
23✔
422

423
    sir_cleanup();
23✔
424
    return print_result_and_return(pass);
23✔
425
}
426

427
bool sirtest_failfilebadpermission(void) {
23✔
428
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
429
    bool pass = si_init;
20✔
430

431
#if !defined(__WIN__)
432
    static const char* path = "/noperms";
433
#else /* __WIN__ */
434
# if defined(__CYGWIN__)
435
    static const char* path = "/cygdrive/c/Windows/System32/noperms";
436
# else
437
    static const char* path;
438
    if (sirtest_get_wineversion()) {
439
        path = "Z:\\noperms";
440
    } else {
441
        path = "C:\\Windows\\System32\\noperms";
442
    }
443
# endif
444
#endif
445

446
    pass &= 0U == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY);
23✔
447

448
    if (pass)
23✔
449
        print_expected_error();
23✔
450

451
    sir_cleanup();
23✔
452
    return print_result_and_return(pass);
23✔
453
}
454

455
bool sirtest_faildupefile(void) {
23✔
456
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
457
    bool pass = si_init;
20✔
458

459
#if !defined(__WIN__)
460
    static const char* filename1 = "./logs/faildupefile.log";
461
    static const char* filename2 = "logs/faildupefile.log";
462
#else
463
    static const char* filename1 = "logs\\faildupefile.log";
464
    static const char* filename2 = "logs/faildupefile.log";
465
#endif
466

467
    static const char* filename3 = "logs/not-a-dupe.log";
468
    static const char* filename4 = "logs/also-not-a-dupe.log";
469

470
    printf("\tadding log file '%s'...\n", filename1);
23✔
471

472
    /* should be fine; no other files added yet. */
473
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
23✔
474
    pass &= 0U != fid;
23✔
475

476
    printf("\ttrying again to add log file '%s'...\n", filename1);
23✔
477

478
    /* should fail. this is the same file we already added. */
479
    pass &= 0U == sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
23✔
480

481
    if (pass)
23✔
482
        print_expected_error();
20✔
483

484
    printf("\tadding log file '%s'...\n", filename2);
23✔
485

486
    /* should also fail. this is the same file we already added, even
487
     * if the path strings don't match. */
488
    pass &= 0U == sir_addfile(filename2, SIRL_ALL, SIRO_DEFAULT);
23✔
489

490
    if (pass)
23✔
491
        print_expected_error();
20✔
492

493
    printf("\tadding log file '%s'...\n", filename3);
23✔
494

495
    /* should pass. this is a different file. */
496
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
23✔
497
    pass &= 0U != fid2;
23✔
498

499
    /* should also pass. */
500
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
23✔
501
    pass &= 0U != fid3;
23✔
502

503
    pass &= sir_info("hello three valid files");
23✔
504

505
    /* should now fail since we added it earlier. */
506
    pass &= 0U == sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
23✔
507

508
    if (pass)
23✔
509
        print_expected_error();
19✔
510

511
    /* don't remove all of the log files in order to also test
512
     * cache tear-down. */
513
    pass &= sir_remfile(fid);
23✔
514

515
    rmfile(filename1);
23✔
516
    rmfile(filename2);
23✔
517
    rmfile(filename3);
23✔
518
    rmfile(filename4);
23✔
519

520
    pass &= sir_cleanup();
23✔
521
    return print_result_and_return(pass);
23✔
522
}
523

524
bool sirtest_failremovebadfile(void) {
23✔
525
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
526
    bool pass = si_init;
20✔
527

528
    sirfileid invalidid = 9999999;
20✔
529
    pass &= !sir_remfile(invalidid);
23✔
530

531
    if (pass)
23✔
532
        print_expected_error();
23✔
533

534
    sir_cleanup();
23✔
535
    return print_result_and_return(pass);
23✔
536
}
537

538
bool sirtest_rollandarchivefile(void) {
24✔
539
    /* roll size minus 1KiB so we can write until it maxes. */
540
    static const long deltasize    = 1024L;
541
    const long fillsize            = SIR_FROLLSIZE - deltasize;
21✔
542
    static const char* logbasename = "rollandarchive";
543
    static const char* logext      = ".log";
544
    static const char* line        = "hello, i am some data. nice to meet you.";
545

546
    char logfilename[SIR_MAXPATH] = {0};
24✔
547
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
24✔
548

549
    unsigned delcount = 0U;
24✔
550
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
24✔
551
        handle_os_error(false, "failed to enumerate log files with base name: %s!",
2✔
552
            logbasename);
553
        return false;
2✔
554
    }
555

556
    if (delcount > 0U)
22✔
557
        printf("\tfound and removed %u log file(s)\n", delcount);
×
558

559
    FILE* f = NULL;
22✔
560
    _sir_fopen(&f, logfilename, "w");
22✔
561

562
    if (!f)
22✔
563
        return print_os_error();
×
564

565
    if (0 != fseek(f, fillsize, SEEK_SET)) {
22✔
566
        handle_os_error(true, "fseek in file %s failed!", logfilename);
1✔
567
        fclose(f);
1✔
568
        return false;
1✔
569
    }
570

571
    if (EOF == fputc('\0', f)) {
21✔
572
        handle_os_error(true, "fputc in file %s failed!", logfilename);
1✔
573
        fclose(f);
1✔
574
        return false;
1✔
575
    }
576

577
    fclose(f);
20✔
578

579
    INIT(si, 0, 0, 0, 0);
20✔
580
    bool pass = si_init;
17✔
581

582
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
20✔
583
    pass &= 0U != fileid;
20✔
584

585
    if (pass) {
20✔
586
        /* write an (approximately) known quantity until we should have rolled */
587
        size_t written  = 0;
15✔
588
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
18✔
589

590
        do {
591
            pass &= sir_debug("%zu %s", written, line);
1,368✔
592
            written += linesize;
1,368✔
593
        } while (pass && (written < deltasize + (linesize * 50)));
1,368✔
594

595
        /* look for files matching the original name. */
596
        unsigned foundlogs = 0U;
18✔
597
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
18✔
598
            handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
599
            pass = false;
×
600
        }
601

602
        /* if two (or more) are present, the test is a pass. */
603
        printf("\tfound %u log files with base name: %s\n", foundlogs, logbasename);
18✔
604
        pass &= foundlogs >= 2U;
18✔
605
    }
606

607
    pass &= sir_remfile(fileid);
20✔
608

609
    delcount = 0U;
20✔
610
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
20✔
611
        handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
612
        return false;
×
613
    }
614

615
    if (delcount > 0U)
20✔
616
        printf("\tfound and removed %u log file(s)\n", delcount);
17✔
617

618
    sir_cleanup();
20✔
619
    return print_result_and_return(pass);
20✔
620
}
621

622
bool sirtest_failwithoutinit(void) {
23✔
623
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
23✔
624

625
    if (pass)
23✔
626
        print_expected_error();
23✔
627

628
    return print_result_and_return(pass);
23✔
629
}
630

631
bool sirtest_failinittwice(void) {
23✔
632
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
633
    bool pass = si_init;
20✔
634

635
    INIT(si2, SIRL_ALL, 0, 0, 0);
23✔
636
    pass &= !si2_init;
23✔
637

638
    if (pass)
23✔
639
        print_expected_error();
23✔
640

641
    sir_cleanup();
23✔
642
    return print_result_and_return(pass);
23✔
643
}
644

645
bool sirtest_failinvalidinitdata(void) {
23✔
646
    sirinit si;
647

648
    /* fill with bad data. */
649
    memset(&si, 0xab, sizeof(sirinit));
20✔
650

651
    printf("\tcalling sir_init with invalid data...\n");
20✔
652
    bool pass = !sir_init(&si);
23✔
653

654
    if (pass)
23✔
655
        print_expected_error();
23✔
656

657
    sir_cleanup();
23✔
658
    return print_result_and_return(pass);
23✔
659
}
660

661
bool sirtest_initcleanupinit(void) {
23✔
662
    INIT(si1, SIRL_ALL, 0, 0, 0);
23✔
663
    bool pass = si1_init;
20✔
664

665
    pass &= sir_info("init called once; testing output...");
23✔
666
    sir_cleanup();
23✔
667

668
    INIT(si2, SIRL_ALL, 0, 0, 0);
23✔
669
    pass &= si2_init;
20✔
670

671
    pass &= sir_info("init called again after re-init; testing output...");
23✔
672
    sir_cleanup();
23✔
673

674
    return print_result_and_return(pass);
23✔
675
}
676

677
bool sirtest_initmakeinit(void) {
23✔
678
    bool pass = true;
20✔
679

680
    sirinit si;
681
    pass &= sir_makeinit(&si);
23✔
682
    pass &= sir_init(&si);
23✔
683
    pass &= sir_info("initialized with sir_makeinit");
23✔
684
    pass &= sir_cleanup();
23✔
685

686
    return print_result_and_return(pass);
23✔
687
}
688

689
bool sirtest_failaftercleanup(void) {
23✔
690
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
691
    bool pass = si_init;
20✔
692

693
    sir_cleanup();
23✔
694
    pass &= !sir_info("already cleaned up; this needs to fail");
23✔
695

696
    if (pass)
23✔
697
        print_expected_error();
23✔
698

699
    return print_result_and_return(pass);
23✔
700
}
701

702
bool sirtest_errorsanity(void) {
23✔
703
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
704
    bool pass = si_init;
20✔
705

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

736
    char message[SIR_MAXERROR] = {0};
23✔
737
    for (size_t n = 0; n < _sir_countof(errors); n++) {
575✔
738
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
552✔
739
        memset(message, 0, SIR_MAXERROR);
480✔
740
        uint16_t err = sir_geterror(message);
552✔
741
        pass &= errors[n].code == err && *message != '\0';
552✔
742
        printf("\t%s = %s\n", errors[n].name, message);
552✔
743
    }
744

745
    sir_cleanup();
23✔
746
    return print_result_and_return(pass);
23✔
747
}
748

749
bool sirtest_textstylesanity(void) {
23✔
750
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
751
    bool pass = si_init;
20✔
752

753
    printf("\t" WHITEB("--- explicitly invalid ---") "\n");
20✔
754
    pass &= !sir_settextstyle(SIRL_INFO, (sir_textattr)0xbbb, 800, 920);
23✔
755
    pass &= sir_info("I have set an invalid text style.");
23✔
756

757
    pass &= !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK);
23✔
758
    pass &= sir_info("oops, did it again...");
23✔
759

760
    pass &= !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff);
23✔
761
    pass &= sir_info("and again.");
23✔
762
    PRINT_PASS(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
23✔
763

764
    printf("\t" WHITEB("--- unusual but valid ---") "\n");
20✔
765
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT);
23✔
766
    pass &= sir_info("system default fg and bg");
23✔
767
    PRINT_PASS(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
23✔
768

769
    printf("\t" WHITEB("--- override defaults ---") "\n");
20✔
770
    pass &= sir_resettextstyles();
23✔
771

772
    pass &= sir_debug("default style");
23✔
773
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY);
23✔
774
    pass &= sir_debug("override style");
23✔
775

776
    pass &= sir_info("default style");
23✔
777
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA);
23✔
778
    pass &= sir_info("override style");
23✔
779

780
    pass &= sir_notice("default style");
23✔
781
    pass &= sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW);
23✔
782
    pass &= sir_notice("override style");
23✔
783

784
    pass &= sir_warn("default style");
23✔
785
    pass &= sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE);
23✔
786
    pass &= sir_warn("override style");
23✔
787

788
    pass &= sir_error("default style");
23✔
789
    pass &= sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE);
23✔
790
    pass &= sir_error("override style");
23✔
791

792
    pass &= sir_crit("default style");
23✔
793
    pass &= sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN);
23✔
794
    pass &= sir_crit("override style");
23✔
795

796
    pass &= sir_alert("default style");
23✔
797
    pass &= sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT);
23✔
798
    pass &= sir_alert("override style");
23✔
799

800
    pass &= sir_emerg("default style");
23✔
801
    pass &= sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT);
23✔
802
    pass &= sir_emerg("override style");
23✔
803
    PRINT_PASS(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
23✔
804

805
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
20✔
806
    pass &= sir_resettextstyles();
23✔
807

808
    pass &= sir_debug("default style (debug)");
23✔
809
    pass &= sir_info("default style (info)");
23✔
810
    pass &= sir_notice("default style (notice)");
23✔
811
    pass &= sir_warn("default style (warning)");
23✔
812
    pass &= sir_error("default style (error)");
23✔
813
    pass &= sir_crit("default style (crit)");
23✔
814
    pass &= sir_alert("default style (alert)");
23✔
815
    pass &= sir_emerg("default style (emergency)");
23✔
816
    PRINT_PASS(pass, "\t--- reset to defaults: %s ---\n\n", PRN_PASS(pass));
23✔
817

818
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
20✔
819
    pass &= sir_setcolormode(SIRCM_256);
23✔
820

821
    for (sir_textcolor fg = 0, bg = 255; (fg < 256 && bg > 0); fg++, bg--) {
5,888✔
822
        if (fg != bg) {
5,865✔
823
            pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
5,865✔
824
            pass &= sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
5,865✔
825
                fg, bg);
826
        }
827
    }
828

829
    PRINT_PASS(pass, "\t--- change mode: 256-color: %s ---\n\n", PRN_PASS(pass));
23✔
830

831
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
20✔
832
    pass &= sir_setcolormode(SIRCM_RGB);
23✔
833

834
    for (size_t n = 0; n < 256; n++) {
5,911✔
835
        sir_textcolor fg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
5,888✔
836
        sir_textcolor bg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
5,888✔
837
        pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
5,888✔
838
        pass &= sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
5,888✔
839
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
5,888✔
840
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
5,888✔
841
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg));
5,888✔
842
    }
843
    PRINT_PASS(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
23✔
844

845
    printf("\t" WHITEB("--- change mode: invalid mode ---") "\n");
20✔
846
    pass &= !sir_setcolormode(SIRCM_INVALID);
23✔
847
    sir_textcolor fg = sir_makergb(255, 0, 0);
23✔
848
    sir_textcolor bg = sir_makergb(0, 0, 0);
23✔
849
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
23✔
850
    pass &= sir_debug("this is still RGB color mode");
23✔
851
    PRINT_PASS(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
23✔
852

853
    printf("\t" WHITEB("--- change mode: 16-color ---") "\n");
20✔
854
    pass &= sir_setcolormode(SIRCM_16);
23✔
855
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT);
23✔
856
    pass &= sir_debug("this is 16-color mode (fg: %"PRId32", bg: default)",
23✔
857
        SIRTC_BMAGENTA);
858
    PRINT_PASS(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
23✔
859

860
    sir_cleanup();
23✔
861

862
    return print_result_and_return(pass);
23✔
863
}
864

865
#if defined(__clang__) /* only Clang has implicit-conversion; GCC BZ#87454 */
866
SANITIZE_SUPPRESS("implicit-conversion")
867
#endif
868
bool sirtest_optionssanity(void) {
23✔
869
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
870
    bool pass = si_init;
20✔
871

872
    static const size_t iterations = 10;
873

874
    /* these should all be valid. */
875
    printf("\t" WHITEB("--- individual valid options ---") "\n");
20✔
876
    pass &= _sir_validopts(SIRO_ALL);
23✔
877
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
20✔
878
    pass &= _sir_validopts(SIRO_NOTIME);
23✔
879
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
20✔
880
    pass &= _sir_validopts(SIRO_NOHOST);
23✔
881
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
20✔
882
    pass &= _sir_validopts(SIRO_NOLEVEL);
23✔
883
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
20✔
884
    pass &= _sir_validopts(SIRO_NONAME);
23✔
885
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
20✔
886
    pass &= _sir_validopts(SIRO_NOPID);
23✔
887
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
20✔
888
    pass &= _sir_validopts(SIRO_NOTID);
23✔
889
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
20✔
890
    pass &= _sir_validopts(SIRO_NOHDR);
23✔
891
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
20✔
892
    pass &= _sir_validopts(SIRO_MSGONLY);
23✔
893
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
20✔
894
    PRINT_PASS(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
23✔
895

896
    /* any combination these bitwise OR'd together
897
       to form a bitmask should also be valid. */
898
    static const sir_option option_arr[SIR_NUMOPTIONS] = {
899
        SIRO_NOTIME,
900
        SIRO_NOHOST,
901
        SIRO_NOLEVEL,
902
        SIRO_NONAME,
903
        SIRO_NOMSEC,
904
        SIRO_NOPID,
905
        SIRO_NOTID,
906
        SIRO_NOHDR
907
    };
908

909
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
20✔
910
    uint32_t last_count = SIR_NUMOPTIONS;
20✔
911
    for (size_t n = 0; n < iterations; n++) {
253✔
912
        sir_options opts    = 0;
200✔
913
        uint32_t rand_count = 0;
200✔
914
        size_t last_idx     = 0;
200✔
915

916
        do {
917
            rand_count = getrand(SIR_NUMOPTIONS);
330✔
918
        } while (rand_count == last_count || rand_count <= 1);
330✔
919

920
        last_count = rand_count;
200✔
921

922
        for (size_t i = 0; i < rand_count; i++) {
1,211✔
923
            size_t rand_idx = 0;
852✔
924
            size_t tries    = 0;
852✔
925

926
            do {
927
                if (++tries > SIR_NUMOPTIONS - 2)
1,654✔
928
                    break;
×
929
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
1,654✔
930
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,654✔
931

932
            last_idx = rand_idx;
852✔
933
            opts |= option_arr[rand_idx];
981✔
934
        }
935

936
        pass &= _sir_validopts(opts);
230✔
937
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
230✔
938
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
939
    }
940
    PRINT_PASS(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
23✔
941

942
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
943

944
    /* the lowest byte is not valid. */
945
    sir_options invalid = 0x000000ff;
20✔
946
    pass &= !_sir_validopts(invalid);
23✔
947
    printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
20✔
948

949
    /* gaps inbetween valid options. */
950
    invalid = 0x0001ff00U & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
20✔
951
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
952
    pass &= !_sir_validopts(invalid);
23✔
953
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00U: %08"PRIx32) "\n", invalid);
20✔
954

955
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
956
    for (sir_option o = 0x00008f00U; o < SIRO_NOHDR; o += 0x1000U) {
207✔
957
        pass &= !_sir_validopts(o);
184✔
958
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
160✔
959
    }
960

961
    /* greater than SIRO_NOHDR. */
962
    invalid = (0xFFFF0000 & ~SIRO_NOHDR); /* implicit-conversion */
20✔
963
    pass &= !_sir_validopts(invalid);
23✔
964
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
20✔
965

966
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
23✔
967

968
    sir_cleanup();
23✔
969
    return print_result_and_return(pass);
23✔
970
}
971

972
bool sirtest_levelssanity(void) {
23✔
973
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
974
    bool pass = si_init;
20✔
975

976
    static const size_t iterations = 10;
977

978
    /* these should all be valid. */
979
    printf("\t" WHITEB("--- individual valid levels ---") "\n");
20✔
980
    pass &= _sir_validlevel(SIRL_INFO) && _sir_validlevels(SIRL_INFO);
23✔
981
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_INFO);
20✔
982
    pass &= _sir_validlevel(SIRL_DEBUG) && _sir_validlevels(SIRL_DEBUG);
23✔
983
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_DEBUG);
20✔
984
    pass &= _sir_validlevel(SIRL_NOTICE) && _sir_validlevels(SIRL_NOTICE);
23✔
985
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_NOTICE);
20✔
986
    pass &= _sir_validlevel(SIRL_WARN) && _sir_validlevels(SIRL_WARN);
23✔
987
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_WARN);
20✔
988
    pass &= _sir_validlevel(SIRL_ERROR) && _sir_validlevels(SIRL_ERROR);
23✔
989
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ERROR);
20✔
990
    pass &= _sir_validlevel(SIRL_CRIT) && _sir_validlevels(SIRL_CRIT);
23✔
991
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_CRIT);
20✔
992
    pass &= _sir_validlevel(SIRL_ALERT) && _sir_validlevels(SIRL_ALERT);
23✔
993
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ALERT);
20✔
994
    pass &= _sir_validlevel(SIRL_EMERG) && _sir_validlevels(SIRL_EMERG);
23✔
995
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_EMERG);
20✔
996
    pass &= _sir_validlevels(SIRL_ALL);
23✔
997
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_ALL);
20✔
998
    pass &= _sir_validlevels(SIRL_NONE);
23✔
999
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_NONE);
20✔
1000
    PRINT_PASS(pass, "\t--- individual valid levels: %s ---\n\n", PRN_PASS(pass));
23✔
1001

1002
    /* any combination these bitwise OR'd together
1003
       to form a bitmask should also be valid. */
1004
    static const sir_levels levels_arr[SIR_NUMLEVELS] = {
1005
        SIRL_EMERG,
1006
        SIRL_ALERT,
1007
        SIRL_CRIT,
1008
        SIRL_ERROR,
1009
        SIRL_WARN,
1010
        SIRL_NOTICE,
1011
        SIRL_INFO,
1012
        SIRL_DEBUG
1013
    };
1014

1015
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
20✔
1016
    uint32_t last_count = SIR_NUMLEVELS;
20✔
1017
    for (size_t n = 0; n < iterations; n++) {
253✔
1018
        sir_levels levels   = 0U;
200✔
1019
        uint32_t rand_count = 0U;
200✔
1020
        size_t last_idx     = 0UL;
200✔
1021

1022
        do {
1023
            rand_count = getrand(SIR_NUMLEVELS);
451✔
1024
        } while (rand_count == last_count || rand_count <= 1U);
451✔
1025

1026
        last_count = rand_count;
200✔
1027

1028
        for (size_t i = 0; i < rand_count; i++) {
1,357✔
1029
            size_t rand_idx = 0;
980✔
1030
            size_t tries    = 0;
980✔
1031

1032
            do {
1033
                if (++tries > SIR_NUMLEVELS - 2)
1,846✔
1034
                    break;
32✔
1035
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,808✔
1036
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,808✔
1037

1038
            last_idx = rand_idx;
980✔
1039
            levels |= levels_arr[rand_idx];
1,127✔
1040
        }
1041

1042
        pass &= _sir_validlevels(levels);
230✔
1043
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
230✔
1044
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
1045
    }
1046
    PRINT_PASS(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
23✔
1047

1048
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
1049

1050
    /* greater than SIRL_ALL. */
1051
    sir_levels invalid = (0xffffu & ~SIRL_ALL);
20✔
1052
    pass &= !_sir_validlevels(invalid);
23✔
1053
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
20✔
1054

1055
    /* individual invalid level. */
1056
    sir_level invalid2 = 0x1337U;
20✔
1057
    pass &= !_sir_validlevel(invalid2);
23✔
1058
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx16) "\n", invalid2);
20✔
1059

1060
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
23✔
1061

1062
    sir_cleanup();
23✔
1063
    return print_result_and_return(pass);
23✔
1064
}
1065

1066
bool sirtest_mutexsanity(void) {
23✔
1067
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1068
    bool pass = si_init;
20✔
1069

1070
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
20✔
1071
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
20✔
1072

1073
    sir_mutex m1 = SIR_MUTEX_INIT;
23✔
1074
    pass &= _sir_mutexcreate(&m1);
23✔
1075

1076
    print_test_error(pass, pass);
23✔
1077

1078
    if (pass) {
23✔
1079
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
20✔
1080
        pass &= _sir_mutexlock(&m1);
23✔
1081

1082
        print_test_error(pass, pass);
23✔
1083

1084
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
20✔
1085
        pass &= _sir_mutexunlock(&m1);
23✔
1086

1087
        print_test_error(pass, pass);
23✔
1088

1089
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
20✔
1090
        pass &= _sir_mutextrylock(&m1);
23✔
1091

1092
        print_test_error(pass, pass);
23✔
1093

1094
        printf(INDENT_ITEM WHITE("unlocking...") "\n");
20✔
1095
        pass &= _sir_mutexunlock(&m1);
23✔
1096

1097
        print_test_error(pass, pass);
23✔
1098

1099
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
20✔
1100
        pass &= _sir_mutexdestroy(&m1);
23✔
1101

1102
        print_test_error(pass, pass);
23✔
1103

1104
    }
1105
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
23✔
1106

1107
    printf("\t" WHITEB("invalid arguments") "\n");
20✔
1108
    printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
20✔
1109
    pass &= !_sir_mutexcreate(NULL);
23✔
1110
    printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
20✔
1111
    pass &= !_sir_mutexlock(NULL);
23✔
1112
    printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
20✔
1113
    pass &= !_sir_mutextrylock(NULL);
23✔
1114
    printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
20✔
1115
    pass &= !_sir_mutexunlock(NULL);
23✔
1116
    printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
20✔
1117
    pass &= !_sir_mutexdestroy(NULL);
23✔
1118
    PRINT_PASS(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
23✔
1119

1120
    pass &= sir_cleanup();
23✔
1121
    return print_result_and_return(pass);
23✔
1122
}
1123

1124
bool sirtest_perf(void) {
1✔
1125
#if !defined(SIR_PERF_PROFILE)
1126
    static const char* logbasename = "libsir-perf";
1127
    static const char* logext      = "";
1128
#endif
1129

1130
#if !defined(DUMA)
1131
# if !defined(SIR_PERF_PROFILE)
1132
    static const size_t perflines = 1000000;
1133
# else
1134
    static const size_t perflines = 4000000;
1135
# endif
1136
#else /* DUMA */
1137
    static const size_t perflines = 100000;
1138
#endif
1139

1140
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
1✔
1141
    bool pass = si_init;
1✔
1142

1143
    if (pass) {
1✔
1144
        float stdioelapsed  = 0.0f;
1✔
1145
#if !defined(SIR_PERF_PROFILE)
1146
        float printfelapsed = 0.0f;
1✔
1147
        float fileelapsed   = 0.0f;
1✔
1148

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

1151
        sir_timer printftimer = {0};
1✔
1152
        sirtimerstart(&printftimer);
1✔
1153

1154
        for (size_t n = 0; n < perflines; n++)
1,000,001✔
1155
            printf(WHITE("%.2f: lorem ipsum foo bar %s: %zu") "\n",
1,000,000✔
1156
                (double)sirtimerelapsed(&printftimer), "baz", 1234 + n);
1,000,000✔
1157

1158
        printfelapsed = sirtimerelapsed(&printftimer);
1✔
1159
#endif
1160

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

1163
        sir_timer stdiotimer = {0};
1✔
1164
        sirtimerstart(&stdiotimer);
1✔
1165

1166
        for (size_t n = 0; n < perflines; n++)
1,000,001✔
1167
            sir_debug("%.2f: lorem ipsum foo bar %s: %zu",
1,000,000✔
1168
                (double)sirtimerelapsed(&stdiotimer), "baz", 1234 + n);
1,000,000✔
1169

1170
        stdioelapsed = sirtimerelapsed(&stdiotimer);
1✔
1171

1172
        sir_cleanup();
1✔
1173

1174
        INIT(si2, 0, 0, 0, 0);
1✔
1175
        pass &= si2_init;
1✔
1176

1177
#if !defined(SIR_PERF_PROFILE)
1178
        char logfilename[SIR_MAXPATH] = {0};
1✔
1179
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
1✔
1180

1181
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
1✔
1182
        pass &= 0 != logid;
1✔
1183

1184
        if (pass) {
1✔
1185
            printf("\t" BLUE("%zu lines libsir(log file)...") "\n", perflines);
1✔
1186

1187
            sir_timer filetimer = {0};
1✔
1188
            sirtimerstart(&filetimer);
1✔
1189

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

1193
            fileelapsed = sirtimerelapsed(&filetimer);
1✔
1194

1195
            pass &= sir_remfile(logid);
1✔
1196
        }
1197
#endif
1198

1199
        if (pass) {
1✔
1200
#if !defined(SIR_PERF_PROFILE)
1201
            printf("\t" WHITEB("printf: ") CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1✔
1202
                perflines, (double)printfelapsed / (double)1e3,
1✔
1203
                (double)perflines / (double)((double)printfelapsed / (double)1e3));
1✔
1204
#endif
1205
            printf("\t" WHITEB("libsir(stdout): ")
1✔
1206
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1207
                perflines, (double)stdioelapsed / (double)1e3,
1✔
1208
                (double)perflines / (double)((double)stdioelapsed / (double)1e3));
1✔
1209
#if !defined(SIR_PERF_PROFILE)
1210
            printf("\t" WHITEB("libsir(log file): ")
1✔
1211
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1212
                perflines, (double)fileelapsed / (double)1e3,
1✔
1213
                (double)perflines / (double)((double)fileelapsed / (double)1e3));
1✔
1214
#endif
1215
            printf("\t" WHITEB("timer resolution: ") CYAN("~%ldnsec") "\n", sirtimergetres());
1✔
1216
        }
1217
    }
1218

1219
#if !defined(SIR_PERF_PROFILE)
1220
    unsigned deleted = 0U;
1✔
1221
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
1✔
1222

1223
    if (deleted > 0U)
1✔
1224
        printf("\t" DGRAY("deleted %u log file(s)") "\n", deleted);
1✔
1225
#endif
1226

1227
    sir_cleanup();
1✔
1228
    return print_result_and_return(pass);
1✔
1229
}
1230

1231
bool sirtest_updatesanity(void) {
23✔
1232
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
23✔
1233
    bool pass = si_init;
20✔
1234

1235
#define UPDATE_SANITY_ARRSIZE 10
1236

1237
    static const char* logfile = MAKE_LOG_NAME("update-sanity.log");
1238
    static const sir_options opts_array[UPDATE_SANITY_ARRSIZE] = {
1239
        SIRO_NOHOST | SIRO_NOTIME | SIRO_NOLEVEL,
1240
        SIRO_MSGONLY, SIRO_NONAME | SIRO_NOTID,
1241
        SIRO_NOPID | SIRO_NOTIME,
1242
        SIRO_NOTIME | SIRO_NOLEVEL | SIRO_NONAME,
1243
        SIRO_NOTIME, SIRO_NOMSEC | SIRO_NOHOST,
1244
        SIRO_NOPID | SIRO_NOTID,
1245
        SIRO_NOHOST | SIRO_NOTID, SIRO_ALL
1246
    };
1247

1248
    static const sir_levels levels_array[UPDATE_SANITY_ARRSIZE] = {
1249
        SIRL_NONE, SIRL_ALL, SIRL_EMERG, SIRL_ALERT,
1250
        SIRL_CRIT, SIRL_ERROR, SIRL_WARN, SIRL_NOTICE,
1251
        SIRL_INFO, SIRL_DEBUG
1252
    };
1253

1254
    rmfile(logfile);
23✔
1255
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
23✔
1256
    pass &= 0 != id1;
23✔
1257

1258
    for (int i = 0; i < 10; i++) {
214✔
1259
        if (!pass)
195✔
1260
            break;
4✔
1261

1262
        /* reset to defaults*/
1263
        pass &= sir_stdoutlevels(SIRL_DEFAULT);
191✔
1264
        pass &= sir_stderrlevels(SIRL_DEFAULT);
191✔
1265
        pass &= sir_stdoutopts(SIRO_DEFAULT);
191✔
1266
        pass &= sir_stderropts(SIRO_DEFAULT);
191✔
1267

1268
        pass &= sir_debug("default config (debug)");
191✔
1269
        pass &= sir_info("default config (info)");
191✔
1270
        pass &= sir_notice("default config (notice)");
191✔
1271
        pass &= sir_warn("default config (warning)");
191✔
1272
        pass &= sir_error("default config (error)");
191✔
1273
        pass &= sir_crit("default config (critical)");
191✔
1274
        pass &= sir_alert("default config (alert)");
191✔
1275
        pass &= sir_emerg("default config (emergency)");
191✔
1276

1277
        /* pick random options to set/unset */
1278
        uint32_t rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1279
        pass &= sir_stdoutlevels(levels_array[rnd]);
191✔
1280
        pass &= sir_stdoutopts(opts_array[rnd]);
191✔
1281
        printf("\t" WHITE("set random config #%"PRIu32" for stdout") "\n", rnd);
161✔
1282

1283
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1284
        pass &= sir_stderrlevels(levels_array[rnd]);
191✔
1285
        pass &= sir_stderropts(opts_array[rnd]);
191✔
1286
        printf("\t" WHITE("set random config #%"PRIu32" for stderr") "\n", rnd);
161✔
1287

1288
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1289
        pass &= sir_filelevels(id1, levels_array[rnd]);
191✔
1290
        pass &= sir_fileopts(id1, opts_array[rnd]);
191✔
1291
        printf("\t" WHITE("set random config #%"PRIu32" for %s") "\n", rnd, logfile);
191✔
1292

1293
        pass &= filter_error(sir_debug("modified config #%"PRIu32" (debug)", rnd), SIR_E_NODEST);
191✔
1294
        pass &= filter_error(sir_info("modified config #%"PRIu32" (info)", rnd), SIR_E_NODEST);
191✔
1295
        pass &= filter_error(sir_notice("modified config #%"PRIu32" (notice)", rnd), SIR_E_NODEST);
191✔
1296
        pass &= filter_error(sir_warn("modified config #%"PRIu32" (warning)", rnd), SIR_E_NODEST);
191✔
1297
        pass &= filter_error(sir_error("modified config #%"PRIu32" (error)", rnd), SIR_E_NODEST);
191✔
1298
        pass &= filter_error(sir_crit("modified config #%"PRIu32" (critical)", rnd), SIR_E_NODEST);
191✔
1299
        pass &= filter_error(sir_alert("modified config #%"PRIu32" (alert)", rnd), SIR_E_NODEST);
191✔
1300
        pass &= filter_error(sir_emerg("modified config #%"PRIu32" (emergency)", rnd), SIR_E_NODEST);
191✔
1301
    }
1302

1303
    if (pass) {
23✔
1304
        /* restore to default config and run again */
1305
        sir_stdoutlevels(SIRL_DEFAULT);
19✔
1306
        sir_stderrlevels(SIRL_DEFAULT);
19✔
1307
        sir_stdoutopts(SIRO_DEFAULT);
19✔
1308
        sir_stderropts(SIRO_DEFAULT);
19✔
1309

1310
        pass &= sir_debug("default config (debug)");
19✔
1311
        pass &= sir_info("default config (info)");
19✔
1312
        pass &= sir_notice("default config (notice)");
19✔
1313
        pass &= sir_warn("default config (warning)");
19✔
1314
        pass &= sir_error("default config (error)");
19✔
1315
        pass &= sir_crit("default config (critical)");
19✔
1316
        pass &= sir_alert("default config (alert)");
19✔
1317
        pass &= sir_emerg("default config (emergency)");
19✔
1318
    }
1319

1320
    pass &= sir_remfile(id1);
23✔
1321
    rmfile(logfile);
23✔
1322
    sir_cleanup();
23✔
1323

1324
    return print_result_and_return(pass);
23✔
1325
}
1326

1327
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1328
static
1329
bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
17✔
1330
    static const int runs = 5;
1331

1332
    /* repeat initializing, opening, logging, closing, cleaning up n times. */
1333
    printf("\trunning %d passes of random configs (system logger: '%s', "
14✔
1334
           "identity: '%s', category: '%s')...\n", runs, sl_name, identity, category);
1335

1336
# if !defined(__WIN__)
1337
    uint32_t rnd = (uint32_t)(_sir_getpid() + _sir_gettid());
17✔
1338
# else
1339
    uint32_t rnd = (uint32_t)GetTickCount();
1340
# endif
1341

1342
    bool pass = true;
14✔
1343
    for (int i = 1; i <= runs; i++) {
102✔
1344
        /* randomly skip setting process name, identity/category to thoroughly
1345
         * test fallback routines; randomly update the config mid-run. */
1346
        bool set_procname = getrand_bool(rnd ^ 0x5a5a5a5aU);
85✔
1347
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9U);
85✔
1348
        bool set_category = getrand_bool(rnd ^ 0x32323232U);
85✔
1349
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7U);
85✔
1350

1351
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
85✔
1352
            set_procname, set_identity, set_category, do_update);
1353

1354
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
94✔
1355
        si.d_syslog.opts   = SIRO_DEFAULT;
85✔
1356
        si.d_syslog.levels = SIRL_DEFAULT;
85✔
1357

1358
        if (set_identity)
85✔
1359
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
49✔
1360

1361
        if (set_category)
85✔
1362
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
50✔
1363

1364
        si_init = sir_init(&si); //-V519
85✔
1365
        pass &= si_init;
70✔
1366

1367
        if (do_update)
85✔
1368
            pass &= sir_sysloglevels(SIRL_ALL);
33✔
1369

1370
        pass &= sir_debug("%d/%d: this debug message sent to stdout and %s.", i, runs, sl_name);
85✔
1371
        pass &= sir_info("%d/%d: this info message sent to stdout and %s.", i, runs, sl_name);
85✔
1372

1373
        pass &= sir_notice("%d/%d: this notice message sent to stdout and %s.", i, runs, sl_name);
85✔
1374
        pass &= sir_warn("%d/%d: this warning message sent to stdout and %s.", i, runs, sl_name);
85✔
1375
        pass &= sir_error("%d/%d: this error message sent to stdout and %s.", i, runs, sl_name);
85✔
1376

1377
        if (set_identity) {
85✔
1378
            pass &= sir_syslogid("my test ID");
49✔
1379
            pass &= sir_syslogid("my test ID"); /* test deduping. */
49✔
1380
        }
1381

1382
        if (set_category) {
85✔
1383
            pass &= sir_syslogcat("my test category");
50✔
1384
            pass &= sir_syslogcat("my test category"); /* test deduping. */
50✔
1385
        }
1386

1387
        if (do_update)
85✔
1388
            pass &= sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID));
33✔
1389

1390
        pass &= sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name);
85✔
1391
        pass &= sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name);
85✔
1392
        pass &= sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name);
85✔
1393

1394
# if defined(SIR_OS_LOG_ENABLED)
1395
#  if defined(__MACOS__) && !defined(__INTEL_COMPILER)
1396
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1397
            printf("\ttesting os_log activity feature...\n");
1398

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

1404
            /* execution now passes to os_log_parent_activity(), where some logging
1405
            * will occur, then a sub-activity will be created, and more logging. */
1406
            os_activity_apply_f(parent, (void*)parent, os_log_parent_activity);
1407
        }
1408
#  endif
1409
# endif
1410

1411
        sir_cleanup();
85✔
1412

1413
        if (!pass)
85✔
1414
            break;
×
1415
    }
1416

1417
    return print_result_and_return(pass);
17✔
1418
}
1419
#endif
1420

1421
#if defined(SIR_NO_SYSTEM_LOGGERS)
1422
static bool generic_disabled_syslog_test(const char* sl_name, const char* identity,
6✔
1423
    const char* category) {
1424
    INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0U, 0U, "sir_disabled_sltest");
6✔
1425
    si.d_syslog.opts   = SIRO_DEFAULT;
6✔
1426
    si.d_syslog.levels = SIRL_DEFAULT;
6✔
1427
    bool pass = true;
6✔
1428

1429
    SIR_UNUSED(sl_name);
1430

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

1433
    /* init should just ignore the syslog settings. */
1434
    pass &= sir_init(&si);
6✔
1435

1436
    /* these calls should all fail. */
1437
    printf("\tsetting levels...\n");
6✔
1438
    pass &= !sir_sysloglevels(SIRL_ALL);
6✔
1439

1440
    if (pass)
6✔
1441
        print_expected_error();
6✔
1442

1443
    printf("\tsetting options...\n");
6✔
1444
    pass &= !sir_syslogopts(SIRO_DEFAULT);
6✔
1445

1446
    if (pass)
6✔
1447
        print_expected_error();
6✔
1448

1449
    printf("\tsetting identity...\n");
6✔
1450
    pass &= !sir_syslogid(identity);
6✔
1451

1452
    if (pass)
6✔
1453
        print_expected_error();
6✔
1454

1455
    printf("\tsetting category...\n");
6✔
1456
    pass &= !sir_syslogcat(category);
6✔
1457

1458
    if (pass)
6✔
1459
        print_expected_error();
6✔
1460

1461
    pass &= sir_cleanup();
6✔
1462
    return print_result_and_return(pass);
6✔
1463
}
1464
#endif
1465

1466
bool sirtest_syslog(void) {
23✔
1467
#if !defined(SIR_SYSLOG_ENABLED)
1468
# if defined(SIR_NO_SYSTEM_LOGGERS)
1469
    bool pass = generic_disabled_syslog_test("syslog", "sirtests", "tests");
6✔
1470
    return print_result_and_return(pass);
6✔
1471
# else
1472
    printf("\t" DGRAY("SIR_SYSLOG_ENABLED is not defined; skipping") "\n");
1473
    return true;
1474
# endif
1475
#else
1476
    bool pass = generic_syslog_test("syslog", "sirtests", "tests");
17✔
1477
    return print_result_and_return(pass);
17✔
1478
#endif
1479
}
1480

1481
bool sirtest_os_log(void) {
23✔
1482
#if !defined(SIR_OS_LOG_ENABLED)
1483
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
20✔
1484
    return true;
23✔
1485
#else
1486
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1487
    return print_result_and_return(pass);
1488
#endif
1489
}
1490

1491
char *sirtest_get_wineversion(void) {
46✔
1492
#if !defined(__WIN__)
1493
    return NULL;
46✔
1494
#else /* __WIN__ */
1495
    typedef char* (__stdcall *get_wine_ver_proc)(void);
1496
    static get_wine_ver_proc _p_wine_get_version = NULL;
1497

1498
    HMODULE _h_ntdll = GetModuleHandle("ntdll.dll");
1499
    if (_h_ntdll != NULL) {
1500
        _p_wine_get_version = (get_wine_ver_proc)GetProcAddress(_h_ntdll, "wine_get_version");
1501
        if (_p_wine_get_version) {
1502
            char *wine_version = _p_wine_get_version();
1503
            if (wine_version)
1504
                return wine_version;
1505
        }
1506
    }
1507
    return NULL;
1508
#endif
1509
}
1510

1511
bool sirtest_filesystem(void) {
23✔
1512
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1513
    bool pass = si_init;
20✔
1514

1515
    /* Wine version */
1516
    printf("\tRunning under Wine: %s\n",
23✔
1517
            sirtest_get_wineversion() ? sirtest_get_wineversion() : "no"); //-V547
23✔
1518

1519
    /* current working directory. */
1520
    char* cwd = _sir_getcwd();
23✔
1521
    pass &= NULL != cwd;
23✔
1522
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
23✔
1523

1524
    if (NULL != cwd) {
23✔
1525
        /* path to this binary file. */
1526
        char* filename = _sir_getappfilename();
23✔
1527
        pass &= NULL != filename;
23✔
1528
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
23✔
1529

1530
        if (NULL != filename) {
23✔
1531
            /* _sir_get[base|dir]name() can potentially modify filename,
1532
             * so make a copy for each call. */
1533
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1534
            pass &= NULL != filename2;
20✔
1535

1536
            if (NULL != filename2) {
20✔
1537
                /* filename, stripped of directory component(s). */
1538
                char* _basename = _sir_getbasename(filename2);
19✔
1539
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
19✔
1540

1541
                if (!_basename) {
19✔
1542
                    pass = false;
×
1543
                } else {
1544
                    /* the last strlen(_basename) chars of filename should match. */
1545
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
19✔
1546
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
19✔
1547
                    size_t n      = 0;
16✔
1548

1549
                    while (n < len) {
171✔
1550
                        if (filename[offset++] != _basename[n++]) {
152✔
1551
                            pass = false;
×
1552
                            break;
×
1553
                        }
1554
                    }
1555
                }
1556
            }
1557

1558
            /* directory this binary file resides in. */
1559
            char* appdir = _sir_getappdir();
20✔
1560
            pass &= NULL != appdir;
20✔
1561
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
20✔
1562

1563
            /* _sir_get[base|dir]name can potentially modify filename,
1564
             * so make a copy for each call. */
1565
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1566
            pass &= NULL != filename3;
20✔
1567

1568
            if (NULL != appdir && NULL != filename3) {
20✔
1569
                /* should yield the same result as _sir_getappdir(). */
1570
                char* _dirname = _sir_getdirname(filename3);
19✔
1571
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
19✔
1572

1573
                pass &= 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH));
19✔
1574
                pass &= NULL != _dirname &&
38✔
1575
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH));
19✔
1576
            }
1577

1578
            _sir_safefree(&appdir);
20✔
1579
            _sir_safefree(&filename);
20✔
1580
            _sir_safefree(&filename2);
20✔
1581
            _sir_safefree(&filename3);
20✔
1582
        }
1583

1584
        _sir_safefree(&cwd);
23✔
1585
    }
1586

1587
    /* this next section doesn't really yield any useful boolean pass/fail
1588
     * information, but could be helpful to review manually. */
1589
    char* dubious_dirnames[] = {
23✔
1590
#if !defined(__WIN__)
1591
        "/foo",
1592
        "/foo/",
1593
        "/foo/bar",
1594
        "/foo/bar/bad:filename",
1595
        "/",
1596
        ""
1597
#else /* __WIN__ */
1598
        "C:\\foo",
1599
        "C:\\foo\\",
1600
        "C:\\foo\\bar",
1601
        "C:\\foo\\bar\\bad:>filename",
1602
        "C:\\",
1603
        "//network-share/myfolder",
1604
        ""
1605
#endif
1606
    };
1607

1608
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
161✔
1609
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
138✔
1610
        if (NULL != tmp) {
138✔
1611
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
132✔
1612
                tmp, _sir_getdirname(tmp));
1613
            _sir_safefree(&tmp);
132✔
1614
        }
1615
    }
1616

1617
    char* dubious_filenames[] = {
23✔
1618
#if !defined(__WIN__)
1619
        "foo/bar/file-or-directory",
1620
        "/foo/bar/file-or-directory",
1621
        "/foo/bar/illegal:filename",
1622
        "/",
1623
        ""
1624
#else /* __WIN__ */
1625
        "foo\\bar\\file.with.many.full.stops",
1626
        "C:\\foo\\bar\\poorly-renamed.txt.pdf",
1627
        "C:\\foo\\bar\\illegal>filename.txt",
1628
        "C:\\",
1629
        "\\Program Files\\foo.bar",
1630
        ""
1631
#endif
1632
    };
1633

1634
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
138✔
1635
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
115✔
1636
        if (NULL != tmp) {
115✔
1637
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
110✔
1638
                tmp, _sir_getbasename(tmp));
1639
            _sir_safefree(&tmp);
110✔
1640
        }
1641
    }
1642

1643
    /* absolute/relative paths. */
1644
    static const struct {
1645
        const char* const path;
1646
        bool abs;
1647
    } abs_or_rel_paths[] = {
1648
        {"this/is/relative", false},
1649
        {"relative", false},
1650
        {"./relative", false},
1651
        {"../../relative", false},
1652
#if !defined(__WIN__)
1653
        {"/usr/local/bin", true},
1654
        {"/", true},
1655
        {"/home/foo/.config", true},
1656
        {"~/.config", true}
1657
#else /* __WIN__ */
1658
        {"D:\\absolute", true},
1659
        {"C:\\Program Files\\FooBar", true},
1660
        {"C:\\", true},
1661
        {"\\absolute", true},
1662
#endif
1663
    };
1664

1665
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
207✔
1666
        bool relative = false;
184✔
1667
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
184✔
1668

1669
        if (relative == abs_or_rel_paths[n].abs) {
184✔
1670
            pass = false;
×
1671
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1672
                relative ? "true" : "false");
×
1673
        } else {
1674
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
184✔
1675
                relative ? "true" : "false");
160✔
1676
        }
1677

1678
        pass &= ret;
160✔
1679
        if (!ret) {
184✔
1680
            bool unused = print_test_error(false, false);
×
1681
            SIR_UNUSED(unused);
1682
        }
1683
    }
1684

1685
    /* file existence. */
1686
    static const struct {
1687
        const char* const path;
1688
        bool exists;
1689
    } real_or_not[] = {
1690
        {"../foobarbaz", false},
1691
        {"foobarbaz", false},
1692
#if !defined(__WIN__)
1693
        {"/", true},
1694
# if !defined(__HAIKU__)
1695
        {"/usr/bin", true},
1696
# else
1697
        {"/bin", true},
1698
# endif
1699
        {"/dev", true},
1700
#else /* __WIN__ */
1701
        {"C:\\Windows", true},
1702
        {"C:\\Program Files", true},
1703
        {"\\", true},
1704
        {".\\", true},
1705
        {"..\\", true},
1706
#endif
1707
        {"../../LICENSES/MIT.txt", true},
1708
        {"../../msvs/libsir.sln", true},
1709
        {"./", true},
1710
        {"../", true},
1711
        {"file.exists", true}
1712
    };
1713

1714
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
253✔
1715
        bool exists = false;
230✔
1716
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
230✔
1717

1718
        if (exists != real_or_not[n].exists) {
230✔
1719
            pass = false;
23✔
1720
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
23✔
1721
                exists ? "true" : "false");
23✔
1722
        } else {
1723
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
207✔
1724
                exists ? "true" : "false");
177✔
1725
        }
1726

1727
        pass &= ret;
200✔
1728
        if (!ret) {
230✔
1729
            bool unused = print_test_error(false, false);
28✔
1730
            SIR_UNUSED(unused);
1731
        }
1732
    }
1733

1734
    /* checking file descriptors. */
1735
    static int bad_fds[] = {
1736
        0,
1737
        1,
1738
        2,
1739
        1234
1740
    };
1741
    if (sirtest_get_wineversion()) { //-V547
23✔
1742
        bad_fds[3] = 0;
×
1743
    }
1744

1745
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
115✔
1746
        if (_sir_validfd(bad_fds[n])) {
92✔
1747
            pass = false;
×
1748
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1749
        } else {
1750
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
92✔
1751
        }
1752
    }
1753

1754
    FILE* f = NULL;
23✔
1755
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
23✔
1756
    if (!ret) {
23✔
1757
        pass = false;
4✔
1758
        handle_os_error(true, "fopen(%s) failed!", "file.exists");
4✔
1759
    } else {
1760
        int fd = fileno(f);
19✔
1761
        if (!_sir_validfd(fd)) {
19✔
1762
            pass = false;
×
1763
            printf("\t" RED("_sir_validfd(%d) = false") "\n", fd);
×
1764
        } else {
1765
            printf("\t" GREEN("_sir_validfd(%d) = true") "\n", fd);
16✔
1766
        }
1767
    }
1768

1769
    _sir_safefclose(&f);
23✔
1770

1771
    sir_cleanup();
23✔
1772
    return print_result_and_return(pass);
23✔
1773
}
1774

1775
bool sirtest_squelchspam(void) {
23✔
1776
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1777
    bool pass = si_init;
20✔
1778

1779
    static const size_t alternate   = 50;
1780
    static const size_t sequence[3] = {
1781
        1000, /* non-repeating messages. */
1782
        1000, /* repeating messages. */
1783
        1000  /* alternating repeating and non-repeating messages. */
1784
    };
1785

1786
    sir_timer timer;
1787
    sirtimerstart(&timer);
23✔
1788

1789
    printf("\t" BLUE("%zu non-repeating messages...") "\n", sequence[0]);
20✔
1790

1791
    for (size_t n = 0, ascii_idx = 33; n < sequence[0]; n++, ascii_idx++) {
23,023✔
1792
        pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
43,000✔
1793
            (char)ascii_idx + 1);
23,000✔
1794

1795
        if (ascii_idx == 125)
23,000✔
1796
            ascii_idx = 33;
200✔
1797
    }
1798

1799
    printf("\t" BLUE("%zu repeating messages...") "\n", sequence[1]);
20✔
1800

1801
    for (size_t n = 0; n < sequence[1]; n++) {
23,023✔
1802
        bool ret = sir_debug("a repeating message");
23,000✔
1803

1804
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
23,000✔
1805
            pass &= !ret;
22,908✔
1806
        else
1807
            pass &= ret;
80✔
1808
    }
1809

1810
    printf("\t" BLUE("%zu alternating repeating and non-repeating messages...")
20✔
1811
           "\n", sequence[2]);
20✔
1812

1813
    bool repeating   = false;
20✔
1814
    size_t counter   = 0;
20✔
1815
    size_t repeat_id = 0;
20✔
1816
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
23,023✔
1817
        if (!repeating) {
23,000✔
1818
            pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
11,523✔
1819
                (char)ascii_idx + 1);
11,523✔
1820
        } else {
1821
            bool ret = sir_debug("%zu a repeating message", repeat_id);
11,477✔
1822

1823
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
11,477✔
1824
                pass &= !ret;
10,557✔
1825
            else
1826
                pass &= ret;
800✔
1827
        }
1828

1829
        if (counter == alternate) {
23,000✔
1830
            repeating = !repeating;
437✔
1831
            counter = 0;
380✔
1832
            repeat_id++;
437✔
1833
        }
1834

1835
        if (ascii_idx == 125)
23,000✔
1836
            ascii_idx = 33;
200✔
1837
    }
1838

1839
    sir_cleanup();
23✔
1840
    return print_result_and_return(pass);
23✔
1841
}
1842

1843
bool sirtest_pluginloader(void) {
23✔
1844
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1845
    bool pass = si_init;
20✔
1846

1847
#if !defined(__WIN__)
1848
# define PLUGIN_EXT "so"
1849
#else
1850
# define PLUGIN_EXT "dll"
1851
#endif
1852

1853
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1854
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1855
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1856
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1857
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1858
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1859
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1860
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1861

1862
#if defined(SIR_NO_PLUGINS)
1863
    SIR_UNUSED(plugin2);
1864
    SIR_UNUSED(plugin3);
1865
    SIR_UNUSED(plugin4);
1866
    SIR_UNUSED(plugin5);
1867
    SIR_UNUSED(plugin6);
1868
    SIR_UNUSED(plugin7);
1869
    SIR_UNUSED(plugin8);
1870

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

1873
    printf("\tloading good plugin: '%s'...\n", plugin1);
1✔
1874
    /* load a valid, well-behaved plugin. */
1875
    sirpluginid id = sir_loadplugin(plugin1);
1✔
1876
    pass &= 0 == id;
1✔
1877

1878
    if (pass)
1✔
1879
        print_expected_error();
1✔
1880

1881
    printf("\tunloading good plugin: '%s'...\n", plugin1);
1✔
1882
    /* also try the unload function. */
1883
    pass &= !sir_unloadplugin(id);
1✔
1884

1885
    if (pass)
1✔
1886
        print_expected_error();
1✔
1887
#else
1888
    /* load a valid, well-behaved plugin. */
1889
    printf("\tloading good plugin: '%s'...\n", plugin1);
22✔
1890
    sirpluginid id = sir_loadplugin(plugin1);
22✔
1891
    pass &= 0 != id;
22✔
1892

1893
    print_test_error(pass, pass);
22✔
1894

1895
    pass &= sir_info("this message will be dispatched to the plugin.");
22✔
1896
    pass &= sir_warn("this message will *not* be dispatched to the plugin.");
22✔
1897

1898
    /* re-loading the same plugin should fail. */
1899
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
22✔
1900
    sirpluginid badid = sir_loadplugin(plugin1);
22✔
1901
    pass &= 0 == badid;
22✔
1902

1903
    print_test_error(pass, pass);
22✔
1904

1905
    /* the following are all invalid or misbehaved, and should all fail. */
1906
    printf("\tloading bad plugin: '%s'...\n", plugin2);
22✔
1907
    badid = sir_loadplugin(plugin2);
22✔
1908
    pass &= 0 == badid;
22✔
1909

1910
    print_test_error(pass, pass);
22✔
1911

1912
    printf("\tloading bad plugin: '%s'...\n", plugin3);
22✔
1913
    badid = sir_loadplugin(plugin3);
22✔
1914
    pass &= 0 == badid;
22✔
1915

1916
    print_test_error(pass, pass);
22✔
1917

1918
    printf("\tloading bad plugin: '%s'...\n", plugin4);
22✔
1919
    badid = sir_loadplugin(plugin4);
22✔
1920
    pass &= 0 == badid;
22✔
1921

1922
    print_test_error(pass, pass);
22✔
1923

1924
    printf("\tloading bad plugin: '%s'...\n", plugin5);
22✔
1925
    badid = sir_loadplugin(plugin5);
22✔
1926
    pass &= 0 == badid;
22✔
1927

1928
    print_test_error(pass, pass);
22✔
1929

1930
    printf("\tloading bad plugin: '%s'...\n", plugin6);
22✔
1931
    badid = sir_loadplugin(plugin6);
22✔
1932
    pass &= 0 == badid;
22✔
1933

1934
    print_test_error(pass, pass);
22✔
1935

1936
    printf("\tloading bad plugin: '%s'...\n", plugin7);
22✔
1937
    badid = sir_loadplugin(plugin7);
22✔
1938
    pass &= 0 != badid; /* this one should load, just return false from write */
22✔
1939

1940
    pass &= !sir_info("this should fail, because one plugin failed to process"
22✔
1941
                      " the message.");
1942

1943
    print_test_error(pass, pass);
22✔
1944

1945
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
22✔
1946
    badid = sir_loadplugin(plugin8);
22✔
1947
    pass &= 0 == badid;
22✔
1948

1949
    print_test_error(pass, pass);
22✔
1950

1951
    /* unload the good plugin manually. */
1952
    printf("\tunloading good plugin: '%s'...\n", plugin1);
22✔
1953
    pass &= sir_unloadplugin(id);
22✔
1954

1955
    print_test_error(pass, pass);
22✔
1956

1957
    /* try to unload the plugin again. */
1958
    printf("\nunloading already unloaded plugin '%s'...\n", plugin1);
22✔
1959
    pass &= !sir_unloadplugin(id);
22✔
1960

1961
    print_test_error(pass, pass);
22✔
1962

1963
    /* test bad paths. */
1964
    printf("\ntrying to load plugin with NULL path...\n");
19✔
1965
    badid = sir_loadplugin(NULL);
22✔
1966
    pass &= 0 == badid;
22✔
1967

1968
    print_test_error(pass, pass);
22✔
1969
#endif
1970
    pass &= sir_cleanup();
23✔
1971
    return print_result_and_return(pass);
23✔
1972
}
1973

1974
bool sirtest_getversioninfo(void) {
23✔
1975
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1976
    bool pass = si_init;
20✔
1977

1978
    printf("\tchecking version retrieval functions...\n");
20✔
1979

1980
    const char* str = sir_getversionstring();
23✔
1981
    pass &= _sir_validstrnofail(str);
23✔
1982

1983
    printf("\tversion as string: '%s'\n", _SIR_PRNSTR(str));
23✔
1984

1985
    uint32_t hex = sir_getversionhex();
23✔
1986
    pass &= 0 != hex;
23✔
1987

1988
    printf("\tversion as hex: 0x%08"PRIx32"\n", hex);
20✔
1989

1990
    bool prerel = sir_isprerelease();
23✔
1991
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
23✔
1992

1993
    pass &= sir_cleanup();
23✔
1994
    return print_result_and_return(pass);
23✔
1995
}
1996

1997
enum {
1998
    NUM_THREADS = 4
1999
};
2000

2001
static bool threadpool_pseudojob(void* arg) {
165✔
2002
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
165✔
2003
#if !defined(__WIN__)
2004
    sleep(1);
165✔
2005
#else
2006
    Sleep(1000);
2007
#endif
2008
    return true;
159✔
2009
}
2010

2011
bool sirtest_threadpool(void) {
23✔
2012
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
23✔
2013
    bool pass = si_init;
20✔
2014

2015
    static const size_t num_jobs = 30;
2016
    sir_threadpool* pool         = NULL;
23✔
2017

2018
    pass &= _sir_threadpool_create(&pool, NUM_THREADS);
23✔
2019
    if (pass) {
23✔
2020
        /* dispatch a whole bunch of jobs. */
2021
        for (size_t n = 0; n < num_jobs; n++) {
651✔
2022
            sir_threadpool_job* job = calloc(1, sizeof(sir_threadpool_job));
630✔
2023
            pass &= NULL != job;
630✔
2024
            if (job) {
630✔
2025
                job->fn = &threadpool_pseudojob;
630✔
2026
                job->data = (void*)(n + 1);
630✔
2027
                pass &= _sir_threadpool_add_job(pool, job);
630✔
2028
                pass &= sir_info("dispatched job (fn: %"PRIxPTR", data: %p)",
630✔
2029
                    (uintptr_t)job->fn, job->data);
630✔
2030
            }
2031
        }
2032

2033
#if !defined(__WIN__)
2034
        sleep(1);
21✔
2035
#else
2036
        Sleep(1000);
2037
#endif
2038

2039
        pass &= sir_info("destroying thread pool...");
21✔
2040
        pass &= _sir_threadpool_destroy(&pool);
21✔
2041
    }
2042

2043
    pass &= sir_cleanup();
23✔
2044
    return print_result_and_return(pass);
23✔
2045
}
2046

2047
#if !defined(__WIN__)
2048
static void* threadrace_thread(void* arg);
2049
#else /* __WIN__ */
2050
static unsigned __stdcall threadrace_thread(void* arg);
2051
#endif
2052

2053
bool sirtest_threadrace(void) {
23✔
2054
#if !defined(__WIN__)
2055
    pthread_t thrds[NUM_THREADS] = {0};
23✔
2056
#else /* __WIN__ */
2057
    uintptr_t thrds[NUM_THREADS] = {0};
2058
#endif
2059

2060
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
23✔
2061
    bool pass           = si_init;
20✔
2062
    bool any_created    = false;
20✔
2063
    size_t last_created = 0;
20✔
2064

2065
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
23✔
2066
    pass &= NULL != heap_args;
23✔
2067
    if (!heap_args) {
23✔
2068
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2069
        return false;
1✔
2070
    }
2071

2072
    for (size_t n = 0; n < NUM_THREADS; n++) {
106✔
2073
        if (!pass)
85✔
2074
            break;
×
2075

2076
        heap_args[n].pass = true;
85✔
2077
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
85✔
2078
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2079

2080
#if !defined(__WIN__)
2081
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
85✔
2082
        if (0 != create) {
85✔
2083
            errno = create;
1✔
2084
            handle_os_error(true, "pthread_create() for thread #%zu failed!", n + 1);
1✔
2085
#else /* __WIN__ */
2086
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2087
        if (0 == thrds[n]) {
2088
            handle_os_error(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2089
#endif
2090
            pass = false;
1✔
2091
            break;
1✔
2092
        }
2093

2094
        last_created = n;
72✔
2095
        any_created  = true;
72✔
2096
    }
2097

2098
    if (any_created) {
22✔
2099
        for (size_t j = 0; j < last_created + 1; j++) {
105✔
2100
            bool joined = true;
72✔
2101
            printf("\twaiting for thread %zu/%zu...\n", j + 1, last_created + 1);
84✔
2102
#if !defined(__WIN__)
2103
            int join = pthread_join(thrds[j], NULL);
84✔
2104
            if (0 != join) {
84✔
2105
                joined = false;
4✔
2106
                errno  = join;
4✔
2107
                handle_os_error(true, "pthread_join() for thread #%zu failed!", j + 1);
4✔
2108
            }
2109
#else /* __WIN__ */
2110
            DWORD wait = WaitForSingleObject((HANDLE)thrds[j], INFINITE);
2111
            if (WAIT_OBJECT_0 != wait) {
2112
                joined = false;
2113
                handle_os_error(false, "WaitForSingleObject() for thread #%zu (%p) failed!", j + 1,
2114
                    (HANDLE)thrds[j]);
2115
            }
2116
#endif
2117
            pass &= joined;
72✔
2118
            if (joined) {
72✔
2119
                printf("\tthread %zu/%zu joined\n", j + 1, last_created + 1);
68✔
2120

2121
                pass &= heap_args[j].pass;
80✔
2122
                if (heap_args[j].pass)
80✔
2123
                    printf("\t" GREEN("thread #%zu returned pass = true") "\n", j + 1);
68✔
2124
                else
2125
                    printf("\t" RED("thread #%zu returned pass = false!") "\n", j + 1);
×
2126
            }
2127
        }
2128
    }
2129

2130
    _sir_safefree(&heap_args);
22✔
2131

2132
    sir_cleanup();
22✔
2133
    return print_result_and_return(pass);
22✔
2134
}
2135

2136
#if !defined(__WIN__)
2137
static void* threadrace_thread(void* arg) {
84✔
2138
#else /* __WIN__ */
2139
unsigned __stdcall threadrace_thread(void* arg) {
2140
#endif
2141
    pid_t threadid       = _sir_gettid();
84✔
2142
    thread_args* my_args = (thread_args*)arg;
72✔
2143

2144
    rmfile(my_args->log_file);
84✔
2145
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
84✔
2146

2147
    if (0 == id) {
84✔
2148
        bool unused = print_test_error(false, false);
8✔
2149
        SIR_UNUSED(unused);
2150
#if !defined(__WIN__)
2151
        return NULL;
8✔
2152
#else /* __WIN__ */
2153
        return 0;
2154
#endif
2155
    }
2156

2157
    printf("\thi, i'm thread (id: %d), logging to: '%s'...\n",
64✔
2158
            PID_CAST threadid, my_args->log_file);
64✔
2159

2160
#if !defined(DUMA)
2161
# define NUM_ITERATIONS 1000
2162
#else
2163
# define NUM_ITERATIONS 100
2164
#endif
2165

2166
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
76,075✔
2167
        /* choose a random level, and colors. */
2168
        sir_textcolor fg = SIRTC_DEFAULT;
64,000✔
2169
        sir_textcolor bg = SIRTC_DEFAULT;
64,000✔
2170

2171
        if (n % 2 == 0) {
75,999✔
2172
            fg = SIRTC_CYAN;
32,000✔
2173
            bg = SIRTC_BLACK;
32,000✔
2174
            sir_debug("log message #%zu", n);
37,999✔
2175
        } else {
2176
            fg = SIRTC_BLACK;
32,000✔
2177
            bg = SIRTC_CYAN;
32,000✔
2178
            sir_info("log message #%zu", n);
38,000✔
2179
        }
2180

2181
        /* sometimes remove and re-add the log file, and set some options/styles.
2182
         * other times, just set different options/styles. */
2183
        uint32_t rnd = (uint32_t)(n + threadid);
76,000✔
2184
        if (getrand_bool((uint32_t)(rnd > 1 ? rnd : 1))) {
76,000✔
2185
            my_args->pass = print_test_error(sir_remfile(id), false);
38,290✔
2186
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
38,290✔
2187
                SIRL_ALL, SIRO_MSGONLY), false);
2188

2189
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
76,580✔
2190
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
38,290✔
2191
            my_args->pass = print_test_error(test, false);
38,290✔
2192

2193
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
38,290✔
2194
            my_args->pass = print_test_error(test, false);
38,290✔
2195
        } else {
2196
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
75,420✔
2197
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
37,710✔
2198
            my_args->pass = print_test_error(test, false);
37,710✔
2199

2200
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
37,710✔
2201
            my_args->pass = print_test_error(test, false);
37,710✔
2202

2203
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
37,710✔
2204
            my_args->pass = print_test_error(test, false);
37,709✔
2205
        }
2206

2207
        if (!my_args->pass)
75,999✔
2208
            break;
×
2209
    }
2210

2211
    my_args->pass = print_test_error(sir_remfile(id), false);
76✔
2212

2213
    rmfile(my_args->log_file);
76✔
2214

2215
#if !defined(__WIN__)
2216
    return NULL;
76✔
2217
#else /* __WIN__ */
2218
    return 0U;
2219
#endif
2220
}
2221

2222
/*
2223
bool sirtest_XXX(void) {
2224
    INIT(si, SIRL_ALL, 0U, 0U, 0U);
2225
    bool pass = si_init;
2226

2227
    pass &= sir_cleanup();
2228
    return print_result_and_return(pass);
2229
}
2230
*/
2231

2232
/* ========================== end tests ========================== */
2233

2234
bool print_test_error(bool result, bool expected) {
267,827✔
2235
    char message[SIR_MAXERROR] = {0};
267,827✔
2236
    uint16_t code              = sir_geterror(message);
267,827✔
2237

2238
    if (!expected && !result && SIR_E_NOERROR != code)
267,799✔
2239
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
1,592✔
2240
    else if (expected && SIR_E_NOERROR != code)
266,207✔
2241
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
510✔
2242

2243
    return result;
267,799✔
2244
}
2245

2246
bool print_os_error(void) {
64✔
2247
    char message[SIR_MAXERROR] = {0};
64✔
2248
    uint16_t code              = sir_geterror(message);
64✔
2249
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
64✔
2250
    return false;
64✔
2251
}
2252

2253
bool filter_error(bool pass, uint16_t err) {
1,528✔
2254
    if (!pass) {
1,528✔
2255
        char message[SIR_MAXERROR] = {0};
956✔
2256
        uint16_t code              = sir_geterror(message);
956✔
2257
        if (code != err)
956✔
2258
            return false;
2✔
2259
    }
2260
    return true;
1,286✔
2261
}
2262

2263
uint32_t getrand(uint32_t upper_bound) {
117,770✔
2264
#if !defined(__WIN__) || defined(__EMBARCADEROC__)
2265
# if defined(__MACOS__) || defined(__BSD__)
2266
    if (upper_bound < 2U)
2267
        upper_bound = 2U;
2268
    return arc4random_uniform(upper_bound);
2269
# else
2270
#  if defined(__EMBARCADEROC__)
2271
    return (uint32_t)(random(upper_bound));
2272
#  else
2273
    return (uint32_t)(random() % upper_bound);
117,770✔
2274
#  endif
2275
# endif
2276
#else /* __WIN__ */
2277
    uint32_t ctx = 0;
2278
    if (0 != rand_s(&ctx))
2279
        ctx = (uint32_t)rand();
2280
    return ctx % upper_bound;
2281
#endif
2282
}
2283

2284
bool rmfile(const char* filename) {
1,114✔
2285
    bool removed = false;
967✔
2286

2287
    /* return true if leave_logs is true. */
2288
    if (leave_logs) {
1,114✔
2289
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
49✔
2290
            filename, _cl_arg_list[3].flag);
49✔
2291
        return true;
49✔
2292
    }
2293

2294
    /* return true if the file doesn't exist. */
2295
    struct stat st;
2296
    if (0 != stat(filename, &st)) {
1,065✔
2297
        if (ENOENT == errno)
566✔
2298
            return true;
455✔
2299

2300
        handle_os_error(true, "failed to stat %s!", filename);
45✔
2301
        return false;
45✔
2302
    }
2303

2304
    if (!_sir_deletefile(filename)) {
500✔
2305
        handle_os_error(false, "failed to delete %s!", filename);
×
2306
    } else {
2307
        printf("\t" DGRAY("deleted %s (%ld bytes)...") "\n", filename,
419✔
2308
            (long)st.st_size);
500✔
2309
    }
2310

2311
    return removed;
419✔
2312
}
2313

2314
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
242✔
2315
    if (strstr(filename, search)) {
242✔
2316
        char filepath[SIR_MAXPATH];
2317
        _sir_snprintf_trunc(filepath, SIR_MAXPATH, "%s%s", path, filename);
58✔
2318

2319
        rmfile(filepath);
58✔
2320
        (*data)++;
58✔
2321
    }
2322
}
242✔
2323

2324
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
120✔
2325
    SIR_UNUSED(path);
2326
    if (strstr(filename, search))
120✔
2327
        (*data)++;
35✔
2328
}
120✔
2329

2330
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
63✔
2331
#if !defined(__WIN__)
2332
    DIR* d = opendir(path);
63✔
2333
    if (!d)
63✔
2334
        return print_os_error();
2✔
2335

2336
    rewinddir(d);
61✔
2337
    struct dirent* di = readdir(d);
61✔
2338
    if (!di) {
61✔
2339
        closedir(d);
×
2340
        return print_os_error();
×
2341
    }
2342

2343
    while (NULL != di) {
423✔
2344
        cb(search, path, di->d_name, data);
362✔
2345
        di = readdir(d);
362✔
2346
    }
2347

2348
    closedir(d);
61✔
2349
    d = NULL;
52✔
2350
#else /* __WIN__ */
2351
    WIN32_FIND_DATA finddata = {0};
2352
    char buf[SIR_MAXPATH]    = {0};
2353

2354
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2355

2356
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2357

2358
    if (INVALID_HANDLE_VALUE == enumerator)
2359
        return false;
2360

2361
    do {
2362
        cb(search, path, finddata.cFileName, data);
2363
    } while (FindNextFile(enumerator, &finddata) > 0);
2364

2365
    FindClose(enumerator);
2366
    enumerator = NULL;
2367
#endif
2368

2369
    return true;
61✔
2370
}
2371

2372
bool sirtimerstart(sir_timer* timer) {
51✔
2373
#if !defined(__WIN__) || defined(__ORANGEC__)
2374
    int gettime = clock_gettime(SIRTEST_CLOCK, &timer->ts);
51✔
2375
    if (0 != gettime) {
51✔
2376
        handle_os_error(true, "clock_gettime(%d) failed!", CLOCK_CAST SIRTEST_CLOCK);
2✔
2377
    }
2378

2379
    return 0 == gettime;
51✔
2380
#else /* __WIN__ */
2381
    GetSystemTimePreciseAsFileTime(&timer->ft);
2382
    return true;
2383
#endif
2384
}
2385

2386
float sirtimerelapsed(const sir_timer* timer) {
2,000,028✔
2387
#if !defined(__WIN__) || defined(__ORANGEC__)
2388
    struct timespec now;
2389
    if (0 == clock_gettime(SIRTEST_CLOCK, &now)) {
2,000,028✔
2390
        return (float)(((((double)now.tv_sec * 1e9) + ((double)now.tv_nsec))
2,000,027✔
2391
               - (((double)timer->ts.tv_sec * 1e9) + ((double)timer->ts.tv_nsec))) / 1e6);
2,000,027✔
2392
    } else {
2393
        handle_os_error(true, "clock_gettime(%d) failed!", CLOCK_CAST SIRTEST_CLOCK);
1✔
2394
    }
2395
    return 0.0f;
1✔
2396
#else /* __WIN__ */
2397
    FILETIME now;
2398
    GetSystemTimePreciseAsFileTime(&now);
2399
    ULARGE_INTEGER start   = {0};
2400
    start.LowPart          = timer->ft.dwLowDateTime;
2401
    start.HighPart         = timer->ft.dwHighDateTime;
2402
    ULARGE_INTEGER n100sec = {0};
2403
    n100sec.LowPart        = now.dwLowDateTime;
2404
    n100sec.HighPart       = now.dwHighDateTime;
2405
    return (float)((n100sec.QuadPart - start.QuadPart) / 1e4);
2406
#endif
2407
}
2408

2409
long sirtimergetres(void) {
1✔
2410
    long retval = 0;
1✔
2411
#if !defined(__WIN__)
2412
    struct timespec res;
2413
    if (0 == clock_getres(SIRTEST_CLOCK, &res)) {
1✔
2414
        retval = res.tv_nsec;
1✔
2415
    } else {
2416
        handle_os_error(true, "clock_getres(%d) failed!", CLOCK_CAST SIRTEST_CLOCK); // GCOVR_EXCL_LINE
2417
    }
2418
#else /* __WIN__ */
2419
    retval = 100;
2420
#endif
2421
    return retval;
1✔
2422
}
2423

2424
void sir_sleep_msec(uint32_t msec) {
46✔
2425
    if (0U == msec)
46✔
2426
        return;
×
2427

2428
#if !defined(__WIN__)
2429
    struct timespec ts = { msec / 1000, (msec % 1000) * 1000000 };
46✔
2430
    (void)nanosleep(&ts, NULL);
46✔
2431
#else /* __WIN__ */
2432
    (void)SleepEx((DWORD)msec, TRUE);
2433
#endif
2434
}
2435

2436
#if defined(SIR_OS_LOG_ENABLED)
2437
void os_log_parent_activity(void* ctx) {
2438
    sir_debug("confirming with ground control that we are a go...");
2439
    sir_info("all systems go; initiating launch sequence");
2440
    sir_warn("getting some excessive vibration here");
2441
    sir_info("safely reached escape velocity. catch you on the flip side");
2442
    sir_info("(3 days later) we have landed on the lunar surface");
2443
    sir_notice("beginning rock counting...");
2444

2445
    os_activity_t parent = (os_activity_t)ctx;
2446
    os_activity_t child = os_activity_create("counting moon rocks", parent, // -V530
2447
        OS_ACTIVITY_FLAG_DEFAULT);
2448

2449
    float rock_count = 0.0f;
2450
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2451
    sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2452
        (double)rock_count);
2453
}
2454

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

2458
    for (size_t n = 0; n < 10; n++) {
2459
        sir_info("counting rocks in sector %zu...", n);
2460
    }
2461

2462
    float* rock_count = (float*)ctx;
2463
    *rock_count = 1e12f;
2464
    sir_info("all sectors counted; heading back to the lunar lander");
2465
}
2466
#endif
2467

2468
bool mark_test_to_run(const char* name) {
3✔
2469
    bool found = false;
3✔
2470
    for (size_t t = 0; t < _sir_countof(sir_tests); t++) {
47✔
2471
        if (_sir_strsame(name, sir_tests[t].name,
46✔
2472
            strnlen(sir_tests[t].name, SIR_MAXTESTNAME))) {
2473
            found = sir_tests[t].run = true;
2✔
2474
            break;
2✔
2475
        }
2476
    }
2477

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

2481
    return found;
3✔
2482
}
2483

2484
void print_usage_info(void) {
4✔
2485
    size_t longest = 0;
4✔
2486
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2487
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2488
        if (len > longest)
28✔
2489
            longest = len;
8✔
2490
    }
2491

2492
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2493

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

2497
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2498
        if (len < longest)
28✔
2499
            for (size_t n = len; n < longest; n++)
156✔
2500
                fprintf(stderr, " ");
132✔
2501

2502
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2503
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2504
            _cl_arg_list[i].desc);
28✔
2505
    }
2506

2507
    fprintf(stderr, "\n");
4✔
2508
}
4✔
2509

2510
void print_test_list(void) {
1✔
2511
    size_t longest = 0;
1✔
2512
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
33✔
2513
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
32✔
2514
        if (len > longest)
32✔
2515
            longest = len;
3✔
2516
    }
2517

2518
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2519

2520
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
33✔
2521
        printf("\t%s\t", sir_tests[i].name);
32✔
2522

2523
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
32✔
2524
        if (len < longest)
32✔
2525
            for (size_t n = len; n < longest; n++)
277✔
2526
                printf(" ");
246✔
2527

2528
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
32✔
2529
            printf("\n");
16✔
2530
    }
2531

2532
    printf("\n");
1✔
2533
}
1✔
2534

2535
void print_libsir_version(void) {
1✔
2536
    printf("\n"ULINE("libsir") " %s (%s)\n\n", sir_getversionstring(),
1✔
2537
        sir_isprerelease() ? "prerelease" : "release");
1✔
2538
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc