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

aremmell / libsir / 428

04 Sep 2023 10:15PM UTC coverage: 94.486% (-0.4%) from 94.865%
428

Pull #257

gitlab-ci

aremmell
don't need check for OrangeC-test is disabled
Pull Request #257: WIP

238 of 238 new or added lines in 12 files covered. (100.0%)

3016 of 3192 relevant lines covered (94.49%)

629655.05 hits per line

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

97.27
/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
    {"syslog",                  sirtest_syslog, false, true},
56
    {"os_log",                  sirtest_os_log, false, true},
57
    {"filesystem",              sirtest_filesystem, false, true},
58
    {"squelch-spam",            sirtest_squelchspam, false, true},
59
    {"plugin-loader",           sirtest_pluginloader, false, true},
60
    {"get-version-info",        sirtest_getversioninfo, false, true}
61
};
62

63
static bool leave_logs = false;
64

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

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

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

86
#if !defined(DEBUG_MALLOC_FILL_BYTE)
87
# define DEBUG_MALLOC_FILL_BYTE 0x2E
88
#endif
89

90
#if defined(DUMA)
91
# if defined(DUMA_EXPLICIT_INIT)
92
    duma_init();
93
# endif
94
# if defined(DUMA_MIN_ALIGNMENT)
95
#  if DUMA_MIN_ALIGNMENT > 0
96
    DUMA_SET_ALIGNMENT(DUMA_MIN_ALIGNMENT);
97
#  endif
98
# endif
99
    DUMA_SET_FILL(DEBUG_MALLOC_FILL_BYTE);
100
#endif
101

102
#if defined(DEBUG)
103
# if defined(__GLIBC__)
104
#  if !defined(_GNU_SOURCE)
105
#   define _GNU_SOURCE 1
106
#  endif
107
#  if defined(__has_include)
108
#   if __has_include(<malloc.h>) && !defined(DUMA)
109
#    include <malloc.h>
110
#   endif
111
#  endif
112
#  if GLIBC_VERSION >= 20400 && defined(M_PERTURB)
113
    mallopt(M_PERTURB, DEBUG_MALLOC_FILL_BYTE);
29✔
114
#  endif
115
#  if defined(M_CHECK_ACTION)
116
    mallopt(M_CHECK_ACTION, 3);
29✔
117
#  endif
118
# endif
119
#endif
120

121
#if !defined(__WIN__) && !defined(__HAIKU__)
122
    /* Disallow execution by root / sudo; some of the tests rely on lack of permissions. */
123
    if (geteuid() == 0) {
32✔
124
        fprintf(stderr, "Sorry, but this program may not be executed by root.\n");
1✔
125
        return EXIT_FAILURE;
1✔
126
    }
127
#else /* __WIN__ */
128
# if defined(_DEBUG) && defined(SIR_ASSERT_ENABLED)
129
    /* Prevents assert() from calling abort() before the user is able to:
130
     * a.) break into the code and debug (Retry button)
131
     * b.) ignore the assert() and continue. */
132
    _set_error_mode(_OUT_TO_MSGBOX);
133
# endif
134
#endif
135

136
    bool wait     = false;
28✔
137
    bool only     = false;
28✔
138
    size_t to_run = 0;
28✔
139

140
    for (int n = 1; n < argc; n++) {
35✔
141
        if (_sir_strsame(argv[n], _cl_arg_list[0].flag,
10✔
142
            strnlen(_cl_arg_list[0].flag, SIR_MAXCLIFLAG))) { /* --perf */
9✔
143
            only = mark_test_to_run("performance");
1✔
144
            if (only)
1✔
145
                to_run = 1;
1✔
146
        } else if (_sir_strsame(argv[n], _cl_arg_list[1].flag,
9✔
147
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --only */
8✔
148
            while (++n < argc) {
4✔
149
                if (_sir_validstrnofail(argv[n])) {
2✔
150
                    if (*argv[n] == '-' || !mark_test_to_run(argv[n])) {
2✔
151
                        fprintf(stderr, RED("invalid argument: '%s'") "\n", argv[n]);
1✔
152
                        print_usage_info();
1✔
153
                        return EXIT_FAILURE;
1✔
154
                    }
155
                    to_run++;
1✔
156
                }
157
            }
158
            if (0 == to_run) {
2✔
159
                fprintf(stderr, RED("value expected for '%s'") "\n",
1✔
160
                    _cl_arg_list[1].flag);
1✔
161
                print_usage_info();
1✔
162
                return EXIT_FAILURE;
1✔
163
            }
164
            only = true;
1✔
165
        } else if (_sir_strsame(argv[n], _cl_arg_list[2].flag,
6✔
166
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --list */
5✔
167
            print_test_list();
1✔
168
            return EXIT_SUCCESS;
1✔
169
        } else if (_sir_strsame(argv[n], _cl_arg_list[3].flag,
5✔
170
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --leave-logs */
4✔
171
            leave_logs = true;
1✔
172
        } else if (_sir_strsame(argv[n], _cl_arg_list[4].flag,
4✔
173
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --wait */
3✔
174
            wait = true;
×
175
        }  else if (_sir_strsame(argv[n], _cl_arg_list[5].flag,
3✔
176
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --version */
3✔
177
            print_libsir_version();
1✔
178
            return EXIT_SUCCESS;
1✔
179
        }else if (_sir_strsame(argv[n], _cl_arg_list[6].flag,
2✔
180
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --help */
2✔
181
            print_usage_info();
1✔
182
            return EXIT_SUCCESS;
1✔
183
        } else {
184
            fprintf(stderr, "unknown argument: '%s'", argv[n]);
1✔
185
            print_usage_info();
1✔
186
            return EXIT_FAILURE;
1✔
187
        }
188
    }
189

190
    size_t first     = (only ? 0 : 1);
25✔
191
    size_t tgt_tests = (only ? to_run : _sir_countof(sir_tests) - first);
25✔
192
    size_t passed    = 0;
22✔
193
    size_t ran       = 0;
22✔
194
    sir_time timer  = {0};
25✔
195

196
    printf(WHITEB("\n" ULINE("libsir") " %s (%s) running %zu %s...") "\n",
50✔
197
        sir_getversionstring(), (sir_isprerelease() ? "prerelease" : "release"),
25✔
198
        tgt_tests, TEST_S(tgt_tests));
199
    sir_timer_start(&timer);
25✔
200

201
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
777✔
202
        if (only && !sir_tests[n].run) {
752✔
203
            _sir_selflog("skipping '%s'; not marked to run", sir_tests[n].name);
60✔
204
            continue;
60✔
205
        }
206

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

209
        sir_tests[n].pass = sir_tests[n].fn();
692✔
210
        if (sir_tests[n].pass)
692✔
211
            passed++;
648✔
212

213
        ran++;
602✔
214

215
        printf(WHITEB("\n(%zu/%zu) '%s' finished: ") "%s\n", ran, tgt_tests, sir_tests[n].name,
692✔
216
            PRN_PASS(sir_tests[n].pass));
602✔
217
    }
218

219
    double elapsed = sir_timer_elapsed(&timer);
25✔
220

221
    if (passed == tgt_tests) {
25✔
222
        printf("\n" WHITEB("done: ")
10✔
223
                   GREENB("%s%zu " ULINE("libsir") " %s passed in %.03fsec!") "\n\n",
224
            tgt_tests > 1 ? "all " : "", tgt_tests, TEST_S(tgt_tests), elapsed / 1e3);
225
    } else {
226
        printf("\n" WHITEB("done: ")
15✔
227
                   REDB("%zu of %zu " ULINE("libsir") " %s failed in %.03fsec") "\n\n",
228
            tgt_tests - passed, tgt_tests, TEST_S(tgt_tests), elapsed / 1e3);
229

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

232
        for (size_t t = 0; t < _sir_countof(sir_tests); t++)
480✔
233
            if (!sir_tests[t].pass)
465✔
234
                printf(RED(INDENT_ITEM "%s\n"), sir_tests[t].name);
44✔
235
        printf("\n");
15✔
236
    }
237

238
    if (wait) {
25✔
239
        printf(WHITEB(EMPH("press any key to exit...")) "\n");
×
240
        char ch = '\0';
1✔
241
        (void)_sir_getchar(&ch);
1✔
242
        SIR_UNUSED(ch);
243
    }
244

245
    return passed == tgt_tests ? EXIT_SUCCESS : EXIT_FAILURE;
25✔
246
}
247

248
bool sirtest_exceedmaxsize(void) {
23✔
249
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
250
    bool pass = si_init;
20✔
251

252
    char toobig[SIR_MAXMESSAGE + 100] = {0};
20✔
253
    memset(toobig, 'a', SIR_MAXMESSAGE + 100);
20✔
254
    toobig[SIR_MAXMESSAGE + 99] = '\0';
23✔
255

256
    pass &= sir_info("%s", toobig);
23✔
257

258
    sir_cleanup();
23✔
259
    return print_result_and_return(pass);
23✔
260
}
261

262
bool sirtest_failnooutputdest(void) {
23✔
263
    INIT(si, 0, 0, 0, 0);
23✔
264
    bool pass = si_init;
20✔
265

266
    static const char* logfilename = MAKE_LOG_NAME("nodestination.log");
267

268
    pass &= !sir_notice("this goes nowhere!");
23✔
269

270
    if (pass) {
23✔
271
        print_expected_error();
23✔
272

273
        pass &= sir_stdoutlevels(SIRL_INFO);
23✔
274
        pass &= sir_info("this goes to stdout");
23✔
275
        pass &= sir_stdoutlevels(SIRL_NONE);
23✔
276

277
        sirfileid fid = sir_addfile(logfilename, SIRL_INFO, SIRO_DEFAULT);
23✔
278
        pass &= 0U != fid;
23✔
279
        pass &= sir_info("this goes to %s", logfilename);
23✔
280
        pass &= sir_filelevels(fid, SIRL_NONE);
23✔
281
        pass &= !sir_notice("this goes nowhere!");
23✔
282

283
        if (0U != fid)
23✔
284
            pass &= sir_remfile(fid);
20✔
285

286
        rmfile(logfilename);
23✔
287
    }
288

289
    sir_cleanup();
23✔
290
    return print_result_and_return(pass);
23✔
291
}
292

293
bool sirtest_failnulls(void) {
23✔
294
    INIT_BASE(si, SIRL_ALL, 0, 0, 0, "", false);
23✔
295
    bool pass = true;
20✔
296

297
    pass &= !sir_init(NULL);
23✔
298

299
    if (pass)
23✔
300
        print_expected_error();
23✔
301

302
    pass &= sir_init(&si);
23✔
303
    pass &= !sir_info(NULL); //-V575 //-V618
23✔
304

305
    if (pass)
23✔
306
        print_expected_error();
23✔
307

308
    pass &= 0U == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY);
23✔
309

310
    if (pass)
23✔
311
        print_expected_error();
23✔
312

313
    pass &= !sir_remfile(0U);
23✔
314

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

318
    sir_cleanup();
23✔
319
    return print_result_and_return(pass);
23✔
320
}
321

322
bool sirtest_failemptymessage(void) {
23✔
323
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
324
    bool pass = si_init;
20✔
325

326
    pass &= !sir_debug("%s", "");
23✔
327

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

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

336
    size_t numfiles             = SIR_MAXFILES + 1;
20✔
337
    sirfileid ids[SIR_MAXFILES] = {0};
23✔
338

339
    sir_options even = SIRO_MSGONLY;
20✔
340
    sir_options odd  = SIRO_ALL;
20✔
341

342
    for (size_t n = 0; n < numfiles - 1; n++) {
391✔
343
        char path[SIR_MAXPATH] = {0};
368✔
344
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), n);
320✔
345
        rmfile(path);
368✔
346
        ids[n] = sir_addfile(path, SIRL_ALL, (n % 2) ? odd : even);
392✔
347
        pass &= 0U != ids[n] && sir_info("test %zu", n);
368✔
348
    }
349

350
    pass &= sir_info("test test test");
23✔
351

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

355
    if (pass)
23✔
356
        print_expected_error();
19✔
357

358
    sir_info("test test test");
23✔
359

360
    /* now remove previously added files in a different order. */
361
    size_t removeorder[SIR_MAXFILES];
362
    memset(removeorder, -1, sizeof(removeorder));
20✔
363

364
    long processed = 0L;
20✔
365
    printf("\tcreating random file ID order...\n");
20✔
366

367
    do {
1,095✔
368
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,277✔
369
        bool skip  = false;
1,115✔
370

371
        for (size_t n = 0; n < SIR_MAXFILES; n++)
12,220✔
372
            if (removeorder[n] == rnd) {
11,852✔
373
                skip = true;
795✔
374
                break;
795✔
375
            }
376

377
        if (skip)
1,277✔
378
            continue;
909✔
379

380
        removeorder[processed++] = rnd;
368✔
381

382
        if (processed == SIR_MAXFILES)
368✔
383
            break;
20✔
384
    } while (true);
385

386
    printf("\tremove order: {");
20✔
387
    for (size_t n = 0; n < SIR_MAXFILES; n++)
391✔
388
        printf(" %zu%s", removeorder[n], (n < SIR_MAXFILES - 1) ? "," : "");
371✔
389
    printf(" }...\n");
20✔
390

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

394
        char path[SIR_MAXPATH] = {0};
368✔
395
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), removeorder[n]);
368✔
396
        rmfile(path);
368✔
397
    }
398

399
    pass &= sir_info("test test test");
23✔
400

401
    sir_cleanup();
23✔
402
    return print_result_and_return(pass);
23✔
403
}
404

405
bool sirtest_failinvalidfilename(void) {
23✔
406
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
407
    bool pass = si_init;
20✔
408

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

411
    if (pass)
23✔
412
        print_expected_error();
23✔
413

414
    sir_cleanup();
23✔
415
    return print_result_and_return(pass);
23✔
416
}
417

418
bool sirtest_failfilebadpermission(void) {
23✔
419
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
420
    bool pass = si_init;
20✔
421

422
#if !defined(__WIN__)
423
    static const char* path = "/noperms";
424
#else /* __WIN__ */
425
# if defined(__CYGWIN__)
426
    static const char* path = "/cygdrive/c/Windows/System32/noperms";
427
# else
428
    static const char* path;
429
    if (sirtest_get_wineversion()) {
430
        path = "Z:\\noperms";
431
    } else {
432
        path = "C:\\Windows\\System32\\noperms";
433
    }
434
# endif
435
#endif
436

437
    pass &= 0U == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY);
23✔
438

439
    if (pass)
23✔
440
        print_expected_error();
23✔
441

442
    sir_cleanup();
23✔
443
    return print_result_and_return(pass);
23✔
444
}
445

446
bool sirtest_faildupefile(void) {
23✔
447
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
448
    bool pass = si_init;
20✔
449

450
#if !defined(__WIN__)
451
    static const char* filename1 = "./logs/faildupefile.log";
452
    static const char* filename2 = "logs/faildupefile.log";
453
#else
454
    static const char* filename1 = "logs\\faildupefile.log";
455
    static const char* filename2 = "logs/faildupefile.log";
456
#endif
457

458
    static const char* filename3 = "logs/not-a-dupe.log";
459
    static const char* filename4 = "logs/also-not-a-dupe.log";
460

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

463
    /* should be fine; no other files added yet. */
464
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
23✔
465
    pass &= 0U != fid;
23✔
466

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

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

472
    if (pass)
23✔
473
        print_expected_error();
20✔
474

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

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

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

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

486
    /* should pass. this is a different file. */
487
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
23✔
488
    pass &= 0U != fid2;
23✔
489

490
    /* should also pass. */
491
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
23✔
492
    pass &= 0U != fid3;
23✔
493

494
    pass &= sir_info("hello three valid files");
23✔
495

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

499
    if (pass)
23✔
500
        print_expected_error();
19✔
501

502
    /* don't remove all of the log files in order to also test
503
     * cache tear-down. */
504
    pass &= sir_remfile(fid);
23✔
505

506
    rmfile(filename1);
23✔
507
    rmfile(filename2);
23✔
508
    rmfile(filename3);
23✔
509
    rmfile(filename4);
23✔
510

511
    pass &= sir_cleanup();
23✔
512
    return print_result_and_return(pass);
23✔
513
}
514

515
bool sirtest_failremovebadfile(void) {
23✔
516
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
517
    bool pass = si_init;
20✔
518

519
    sirfileid invalidid = 9999999;
20✔
520
    pass &= !sir_remfile(invalidid);
23✔
521

522
    if (pass)
23✔
523
        print_expected_error();
23✔
524

525
    sir_cleanup();
23✔
526
    return print_result_and_return(pass);
23✔
527
}
528

529
bool sirtest_rollandarchivefile(void) {
24✔
530
    /* roll size minus 1KiB so we can write until it maxes. */
531
    static const long deltasize    = 1024L;
532
    const long fillsize            = SIR_FROLLSIZE - deltasize;
21✔
533
    static const char* logbasename = "rollandarchive";
534
    static const char* logext      = ".log";
535
    static const char* line        = "hello, i am some data. nice to meet you.";
536

537
    char logfilename[SIR_MAXPATH] = {0};
24✔
538
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
24✔
539

540
    unsigned delcount = 0U;
24✔
541
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
24✔
542
        handle_os_error(false, "failed to enumerate log files with base name: %s!",
2✔
543
            logbasename);
544
        return false;
2✔
545
    }
546

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

550
    FILE* f = NULL;
22✔
551
    _sir_fopen(&f, logfilename, "w");
22✔
552

553
    if (!f)
22✔
554
        return print_os_error();
×
555

556
    if (0 != fseek(f, fillsize, SEEK_SET)) {
22✔
557
        handle_os_error(true, "fseek in file %s failed!", logfilename);
1✔
558
        fclose(f);
1✔
559
        return false;
1✔
560
    }
561

562
    if (EOF == fputc('\0', f)) {
21✔
563
        handle_os_error(true, "fputc in file %s failed!", logfilename);
1✔
564
        fclose(f);
1✔
565
        return false;
1✔
566
    }
567

568
    fclose(f);
20✔
569

570
    INIT(si, 0, 0, 0, 0);
20✔
571
    bool pass = si_init;
17✔
572

573
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
20✔
574
    pass &= 0U != fileid;
20✔
575

576
    if (pass) {
20✔
577
        /* write an (approximately) known quantity until we should have rolled */
578
        size_t written  = 0;
15✔
579
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
18✔
580

581
        do {
582
            pass &= sir_debug("%zu %s", written, line);
1,368✔
583
            written += linesize;
1,368✔
584
        } while (pass && (written < deltasize + (linesize * 50)));
1,368✔
585

586
        /* look for files matching the original name. */
587
        unsigned foundlogs = 0U;
18✔
588
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
18✔
589
            handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
590
            pass = false;
×
591
        }
592

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

598
    pass &= sir_remfile(fileid);
20✔
599

600
    delcount = 0U;
20✔
601
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
20✔
602
        handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
603
        return false;
×
604
    }
605

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

609
    sir_cleanup();
20✔
610
    return print_result_and_return(pass);
20✔
611
}
612

613
bool sirtest_failwithoutinit(void) {
23✔
614
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
23✔
615

616
    if (pass)
23✔
617
        print_expected_error();
23✔
618

619
    return print_result_and_return(pass);
23✔
620
}
621

622
bool sirtest_failinittwice(void) {
23✔
623
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
624
    bool pass = si_init;
20✔
625

626
    INIT(si2, SIRL_ALL, 0, 0, 0);
23✔
627
    pass &= !si2_init;
23✔
628

629
    if (pass)
23✔
630
        print_expected_error();
23✔
631

632
    sir_cleanup();
23✔
633
    return print_result_and_return(pass);
23✔
634
}
635

636
bool sirtest_failinvalidinitdata(void) {
23✔
637
    sirinit si;
638

639
    /* fill with bad data. */
640
    memset(&si, 0xab, sizeof(sirinit));
20✔
641

642
    printf("\tcalling sir_init with invalid data...\n");
20✔
643
    bool pass = !sir_init(&si);
23✔
644

645
    if (pass)
23✔
646
        print_expected_error();
23✔
647

648
    sir_cleanup();
23✔
649
    return print_result_and_return(pass);
23✔
650
}
651

652
bool sirtest_initcleanupinit(void) {
23✔
653
    INIT(si1, SIRL_ALL, 0, 0, 0);
23✔
654
    bool pass = si1_init;
20✔
655

656
    pass &= sir_info("init called once; testing output...");
23✔
657
    sir_cleanup();
23✔
658

659
    INIT(si2, SIRL_ALL, 0, 0, 0);
23✔
660
    pass &= si2_init;
20✔
661

662
    pass &= sir_info("init called again after re-init; testing output...");
23✔
663
    sir_cleanup();
23✔
664

665
    return print_result_and_return(pass);
23✔
666
}
667

668
bool sirtest_initmakeinit(void) {
23✔
669
    bool pass = true;
20✔
670

671
    sirinit si;
672
    pass &= sir_makeinit(&si);
23✔
673
    pass &= sir_init(&si);
23✔
674
    pass &= sir_info("initialized with sir_makeinit");
23✔
675
    pass &= sir_cleanup();
23✔
676

677
    return print_result_and_return(pass);
23✔
678
}
679

680
bool sirtest_failaftercleanup(void) {
23✔
681
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
682
    bool pass = si_init;
20✔
683

684
    sir_cleanup();
23✔
685
    pass &= !sir_info("already cleaned up; this needs to fail");
23✔
686

687
    if (pass)
23✔
688
        print_expected_error();
23✔
689

690
    return print_result_and_return(pass);
23✔
691
}
692

693
bool sirtest_errorsanity(void) {
23✔
694
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
695
    bool pass = si_init;
20✔
696

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

727
    char message[SIR_MAXERROR] = {0};
23✔
728
    for (size_t n = 0; n < _sir_countof(errors); n++) {
575✔
729
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
552✔
730
        memset(message, 0, SIR_MAXERROR);
480✔
731
        uint16_t err = sir_geterror(message);
552✔
732
        pass &= errors[n].code == err && *message != '\0';
552✔
733
        printf("\t%s = %s\n", errors[n].name, message);
552✔
734
    }
735

736
    sir_cleanup();
23✔
737
    return print_result_and_return(pass);
23✔
738
}
739

740
bool sirtest_textstylesanity(void) {
23✔
741
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
742
    bool pass = si_init;
20✔
743

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

748
    pass &= !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK);
23✔
749
    pass &= sir_info("oops, did it again...");
23✔
750

751
    pass &= !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff);
23✔
752
    pass &= sir_info("and again.");
23✔
753
    PRINT_PASS(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
23✔
754

755
    printf("\t" WHITEB("--- unusual but valid ---") "\n");
20✔
756
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT);
23✔
757
    pass &= sir_info("system default fg and bg");
23✔
758
    PRINT_PASS(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
23✔
759

760
    printf("\t" WHITEB("--- override defaults ---") "\n");
20✔
761
    pass &= sir_resettextstyles();
23✔
762

763
    pass &= sir_debug("default style");
23✔
764
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY);
23✔
765
    pass &= sir_debug("override style");
23✔
766

767
    pass &= sir_info("default style");
23✔
768
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA);
23✔
769
    pass &= sir_info("override style");
23✔
770

771
    pass &= sir_notice("default style");
23✔
772
    pass &= sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW);
23✔
773
    pass &= sir_notice("override style");
23✔
774

775
    pass &= sir_warn("default style");
23✔
776
    pass &= sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE);
23✔
777
    pass &= sir_warn("override style");
23✔
778

779
    pass &= sir_error("default style");
23✔
780
    pass &= sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE);
23✔
781
    pass &= sir_error("override style");
23✔
782

783
    pass &= sir_crit("default style");
23✔
784
    pass &= sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN);
23✔
785
    pass &= sir_crit("override style");
23✔
786

787
    pass &= sir_alert("default style");
23✔
788
    pass &= sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT);
23✔
789
    pass &= sir_alert("override style");
23✔
790

791
    pass &= sir_emerg("default style");
23✔
792
    pass &= sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT);
23✔
793
    pass &= sir_emerg("override style");
23✔
794
    PRINT_PASS(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
23✔
795

796
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
20✔
797
    pass &= sir_resettextstyles();
23✔
798

799
    pass &= sir_debug("default style (debug)");
23✔
800
    pass &= sir_info("default style (info)");
23✔
801
    pass &= sir_notice("default style (notice)");
23✔
802
    pass &= sir_warn("default style (warning)");
23✔
803
    pass &= sir_error("default style (error)");
23✔
804
    pass &= sir_crit("default style (crit)");
23✔
805
    pass &= sir_alert("default style (alert)");
23✔
806
    pass &= sir_emerg("default style (emergency)");
23✔
807
    PRINT_PASS(pass, "\t--- reset to defaults: %s ---\n\n", PRN_PASS(pass));
23✔
808

809
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
20✔
810
    pass &= sir_setcolormode(SIRCM_256);
23✔
811

812
    for (sir_textcolor fg = 0, bg = 255; (fg < 256 && bg > 0); fg++, bg--) {
5,888✔
813
        if (fg != bg) {
5,865✔
814
            pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
5,865✔
815
            pass &= sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
5,865✔
816
                fg, bg);
817
        }
818
    }
819

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

822
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
20✔
823
    pass &= sir_setcolormode(SIRCM_RGB);
23✔
824

825
    for (size_t n = 0; n < 256; n++) {
5,911✔
826
        sir_textcolor fg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
5,888✔
827
        sir_textcolor bg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
5,888✔
828
        pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
5,888✔
829
        pass &= sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
5,888✔
830
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
5,888✔
831
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
5,888✔
832
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg));
5,888✔
833
    }
834
    PRINT_PASS(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
23✔
835

836
    printf("\t" WHITEB("--- change mode: invalid mode ---") "\n");
20✔
837
    pass &= !sir_setcolormode(SIRCM_INVALID);
23✔
838
    sir_textcolor fg = sir_makergb(255, 0, 0);
23✔
839
    sir_textcolor bg = sir_makergb(0, 0, 0);
23✔
840
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
23✔
841
    pass &= sir_debug("this is still RGB color mode");
23✔
842
    PRINT_PASS(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
23✔
843

844
    printf("\t" WHITEB("--- change mode: 16-color ---") "\n");
20✔
845
    pass &= sir_setcolormode(SIRCM_16);
23✔
846
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT);
23✔
847
    pass &= sir_debug("this is 16-color mode (fg: %"PRId32", bg: default)",
23✔
848
        SIRTC_BMAGENTA);
849
    PRINT_PASS(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
23✔
850

851
    sir_cleanup();
23✔
852

853
    return print_result_and_return(pass);
23✔
854
}
855

856
#if defined(__clang__) && !defined(__EMBARCADEROC__)
857
/* only Clang has implicit-conversion; GCC BZ#87454 */
858
SANITIZE_SUPPRESS("implicit-conversion")
859
#endif
860
bool sirtest_optionssanity(void) {
23✔
861
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
862
    bool pass = si_init;
20✔
863

864
    static const size_t iterations = 10;
865

866
    /* these should all be valid. */
867
    printf("\t" WHITEB("--- individual valid options ---") "\n");
20✔
868
    pass &= _sir_validopts(SIRO_ALL);
23✔
869
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
20✔
870
    pass &= _sir_validopts(SIRO_NOTIME);
23✔
871
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
20✔
872
    pass &= _sir_validopts(SIRO_NOHOST);
23✔
873
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
20✔
874
    pass &= _sir_validopts(SIRO_NOLEVEL);
23✔
875
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
20✔
876
    pass &= _sir_validopts(SIRO_NONAME);
23✔
877
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
20✔
878
    pass &= _sir_validopts(SIRO_NOPID);
23✔
879
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
20✔
880
    pass &= _sir_validopts(SIRO_NOTID);
23✔
881
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
20✔
882
    pass &= _sir_validopts(SIRO_NOHDR);
23✔
883
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
20✔
884
    pass &= _sir_validopts(SIRO_MSGONLY);
23✔
885
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
20✔
886
    PRINT_PASS(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
23✔
887

888
    /* any combination these bitwise OR'd together
889
       to form a bitmask should also be valid. */
890
    static const sir_option option_arr[SIR_NUMOPTIONS] = {
891
        SIRO_NOTIME,
892
        SIRO_NOHOST,
893
        SIRO_NOLEVEL,
894
        SIRO_NONAME,
895
        SIRO_NOMSEC,
896
        SIRO_NOPID,
897
        SIRO_NOTID,
898
        SIRO_NOHDR
899
    };
900

901
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
20✔
902
    uint32_t last_count = SIR_NUMOPTIONS;
20✔
903
    for (size_t n = 0; n < iterations; n++) {
253✔
904
        sir_options opts    = 0;
200✔
905
        uint32_t rand_count = 0;
200✔
906
        size_t last_idx     = 0;
200✔
907

908
        do {
909
            rand_count = getrand(SIR_NUMOPTIONS);
334✔
910
        } while (rand_count == last_count || rand_count <= 1);
334✔
911

912
        last_count = rand_count;
200✔
913

914
        for (size_t i = 0; i < rand_count; i++) {
1,214✔
915
            size_t rand_idx = 0;
855✔
916
            size_t tries    = 0;
855✔
917

918
            do {
919
                if (++tries > SIR_NUMOPTIONS - 2)
1,647✔
920
                    break;
×
921
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
1,647✔
922
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,647✔
923

924
            last_idx = rand_idx;
855✔
925
            opts |= option_arr[rand_idx];
984✔
926
        }
927

928
        pass &= _sir_validopts(opts);
230✔
929
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
230✔
930
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
931
    }
932
    PRINT_PASS(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
23✔
933

934
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
935

936
    /* the lowest byte is not valid. */
937
    sir_options invalid = 0x000000ff;
20✔
938
    pass &= !_sir_validopts(invalid);
23✔
939
    printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
20✔
940

941
    /* gaps inbetween valid options. */
942
    invalid = 0x0001ff00U & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
20✔
943
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
944
    pass &= !_sir_validopts(invalid);
23✔
945
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00U: %08"PRIx32) "\n", invalid);
20✔
946

947
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
948
    for (sir_option o = 0x00008f00U; o < SIRO_NOHDR; o += 0x1000U) {
207✔
949
        pass &= !_sir_validopts(o);
184✔
950
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
160✔
951
    }
952

953
    /* greater than SIRO_NOHDR. */
954
    invalid = (0xFFFF0000 & ~SIRO_NOHDR); /* implicit-conversion */
20✔
955
    pass &= !_sir_validopts(invalid);
23✔
956
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
20✔
957

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

960
    sir_cleanup();
23✔
961
    return print_result_and_return(pass);
23✔
962
}
963

964
bool sirtest_levelssanity(void) {
23✔
965
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
966
    bool pass = si_init;
20✔
967

968
    static const size_t iterations = 10;
969

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

994
    /* any combination these bitwise OR'd together
995
       to form a bitmask should also be valid. */
996
    static const sir_levels levels_arr[SIR_NUMLEVELS] = {
997
        SIRL_EMERG,
998
        SIRL_ALERT,
999
        SIRL_CRIT,
1000
        SIRL_ERROR,
1001
        SIRL_WARN,
1002
        SIRL_NOTICE,
1003
        SIRL_INFO,
1004
        SIRL_DEBUG
1005
    };
1006

1007
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
20✔
1008
    uint32_t last_count = SIR_NUMLEVELS;
20✔
1009
    for (size_t n = 0; n < iterations; n++) {
253✔
1010
        sir_levels levels   = 0U;
200✔
1011
        uint32_t rand_count = 0U;
200✔
1012
        size_t last_idx     = 0UL;
200✔
1013

1014
        do {
1015
            rand_count = getrand(SIR_NUMLEVELS);
441✔
1016
        } while (rand_count == last_count || rand_count <= 1U);
441✔
1017

1018
        last_count = rand_count;
200✔
1019

1020
        for (size_t i = 0; i < rand_count; i++) {
1,352✔
1021
            size_t rand_idx = 0;
975✔
1022
            size_t tries    = 0;
975✔
1023

1024
            do {
1025
                if (++tries > SIR_NUMLEVELS - 2)
1,824✔
1026
                    break;
30✔
1027
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,788✔
1028
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,788✔
1029

1030
            last_idx = rand_idx;
975✔
1031
            levels |= levels_arr[rand_idx];
1,122✔
1032
        }
1033

1034
        pass &= _sir_validlevels(levels);
230✔
1035
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
230✔
1036
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
1037
    }
1038
    PRINT_PASS(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
23✔
1039

1040
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
1041

1042
    /* greater than SIRL_ALL. */
1043
    sir_levels invalid = (0xffffu & ~SIRL_ALL);
20✔
1044
    pass &= !_sir_validlevels(invalid);
23✔
1045
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
20✔
1046

1047
    /* individual invalid level. */
1048
    sir_level invalid2 = 0x1337U;
20✔
1049
    pass &= !_sir_validlevel(invalid2);
23✔
1050
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx16) "\n", invalid2);
20✔
1051

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

1054
    sir_cleanup();
23✔
1055
    return print_result_and_return(pass);
23✔
1056
}
1057

1058
bool sirtest_mutexsanity(void) {
23✔
1059
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1060
    bool pass = si_init;
20✔
1061

1062
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
20✔
1063
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
20✔
1064

1065
    sir_mutex m1 = SIR_MUTEX_INIT;
23✔
1066
    pass &= _sir_mutexcreate(&m1);
23✔
1067

1068
    print_test_error(pass, pass);
23✔
1069

1070
    if (pass) {
23✔
1071
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
20✔
1072
        pass &= _sir_mutexlock(&m1);
23✔
1073

1074
        print_test_error(pass, pass);
23✔
1075

1076
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
20✔
1077
        pass &= _sir_mutexunlock(&m1);
23✔
1078

1079
        print_test_error(pass, pass);
23✔
1080

1081
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
20✔
1082
        pass &= _sir_mutextrylock(&m1);
23✔
1083

1084
        print_test_error(pass, pass);
23✔
1085

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

1089
        print_test_error(pass, pass);
23✔
1090

1091
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
20✔
1092
        pass &= _sir_mutexdestroy(&m1);
23✔
1093

1094
        print_test_error(pass, pass);
23✔
1095

1096
    }
1097
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
23✔
1098

1099
    printf("\t" WHITEB("invalid arguments") "\n");
20✔
1100
    printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
20✔
1101
    pass &= !_sir_mutexcreate(NULL);
23✔
1102
    printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
20✔
1103
    pass &= !_sir_mutexlock(NULL);
23✔
1104
    printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
20✔
1105
    pass &= !_sir_mutextrylock(NULL);
23✔
1106
    printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
20✔
1107
    pass &= !_sir_mutexunlock(NULL);
23✔
1108
    printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
20✔
1109
    pass &= !_sir_mutexdestroy(NULL);
23✔
1110
    PRINT_PASS(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
23✔
1111

1112
    pass &= sir_cleanup();
23✔
1113
    return print_result_and_return(pass);
23✔
1114
}
1115

1116
bool sirtest_perf(void) {
1✔
1117
#if !defined(SIR_PERF_PROFILE)
1118
    static const char* logbasename = "libsir-perf";
1119
    static const char* logext      = "";
1120
#endif
1121

1122
#if !defined(DUMA)
1123
# if !defined(SIR_PERF_PROFILE)
1124
#  if !defined(__WIN__)
1125
    static const size_t perflines = 1000000;
1126
#  else
1127
    static const size_t perflines = 100000;
1128
#  endif
1129
# else
1130
    static const size_t perflines = 4000000;
1131
# endif
1132
#else /* DUMA */
1133
    static const size_t perflines = 100000;
1134
#endif
1135

1136
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
1✔
1137
    bool pass = si_init;
1✔
1138

1139
    if (pass) {
1✔
1140
        double stdioelapsed  = 0.0;
1✔
1141
#if !defined(SIR_PERF_PROFILE)
1142
        double printfelapsed = 0.0;
1✔
1143
        double fileelapsed   = 0.0;
1✔
1144

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

1147
        sir_time printftimer = {0};
1✔
1148
        sir_timer_start(&printftimer);
1✔
1149

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

1154
        printfelapsed = sir_timer_elapsed(&printftimer);
1✔
1155
#endif
1156

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

1159
        sir_time stdiotimer = {0};
1✔
1160
        sir_timer_start(&stdiotimer);
1✔
1161

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

1166
        stdioelapsed = sir_timer_elapsed(&stdiotimer);
1✔
1167

1168
        sir_cleanup();
1✔
1169

1170
        INIT(si2, 0, 0, 0, 0);
1✔
1171
        pass &= si2_init;
1✔
1172

1173
#if !defined(SIR_PERF_PROFILE)
1174
        char logfilename[SIR_MAXPATH] = {0};
1✔
1175
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
1✔
1176

1177
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
1✔
1178
        pass &= 0 != logid;
1✔
1179

1180
        if (pass) {
1✔
1181
            printf("\t" BLUE("%zu lines libsir(log file)...") "\n", perflines);
1✔
1182

1183
            sir_time filetimer = {0};
1✔
1184
            sir_timer_start(&filetimer);
1✔
1185

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

1189
            fileelapsed = sir_timer_elapsed(&filetimer);
1✔
1190

1191
            pass &= sir_remfile(logid);
1✔
1192
        }
1193
#endif
1194

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

1212
#if !defined(SIR_PERF_PROFILE)
1213
    unsigned deleted = 0U;
1✔
1214
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
1✔
1215

1216
    if (deleted > 0U)
1✔
1217
        printf("\t" DGRAY("deleted %u log file(s)") "\n", deleted);
1✔
1218
#endif
1219

1220
    sir_cleanup();
1✔
1221
    return print_result_and_return(pass);
1✔
1222
}
1223

1224
bool sirtest_updatesanity(void) {
23✔
1225
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
23✔
1226
    bool pass = si_init;
20✔
1227

1228
#define UPDATE_SANITY_ARRSIZE 10
1229

1230
    static const char* logfile = MAKE_LOG_NAME("update-sanity.log");
1231
    static const sir_options opts_array[UPDATE_SANITY_ARRSIZE] = {
1232
        SIRO_NOHOST | SIRO_NOTIME | SIRO_NOLEVEL,
1233
        SIRO_MSGONLY, SIRO_NONAME | SIRO_NOTID,
1234
        SIRO_NOPID | SIRO_NOTIME,
1235
        SIRO_NOTIME | SIRO_NOLEVEL | SIRO_NONAME,
1236
        SIRO_NOTIME, SIRO_NOMSEC | SIRO_NOHOST,
1237
        SIRO_NOPID | SIRO_NOTID,
1238
        SIRO_NOHOST | SIRO_NOTID, SIRO_ALL
1239
    };
1240

1241
    static const sir_levels levels_array[UPDATE_SANITY_ARRSIZE] = {
1242
        SIRL_NONE, SIRL_ALL, SIRL_EMERG, SIRL_ALERT,
1243
        SIRL_CRIT, SIRL_ERROR, SIRL_WARN, SIRL_NOTICE,
1244
        SIRL_INFO, SIRL_DEBUG
1245
    };
1246

1247
    rmfile(logfile);
23✔
1248
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
23✔
1249
    pass &= 0 != id1;
23✔
1250

1251
    for (int i = 0; i < 10; i++) {
214✔
1252
        if (!pass)
195✔
1253
            break;
4✔
1254

1255
        /* reset to defaults*/
1256
        pass &= sir_stdoutlevels(SIRL_DEFAULT);
191✔
1257
        pass &= sir_stderrlevels(SIRL_DEFAULT);
191✔
1258
        pass &= sir_stdoutopts(SIRO_DEFAULT);
191✔
1259
        pass &= sir_stderropts(SIRO_DEFAULT);
191✔
1260

1261
        pass &= sir_debug("default config (debug)");
191✔
1262
        pass &= sir_info("default config (info)");
191✔
1263
        pass &= sir_notice("default config (notice)");
191✔
1264
        pass &= sir_warn("default config (warning)");
191✔
1265
        pass &= sir_error("default config (error)");
191✔
1266
        pass &= sir_crit("default config (critical)");
191✔
1267
        pass &= sir_alert("default config (alert)");
191✔
1268
        pass &= sir_emerg("default config (emergency)");
191✔
1269

1270
        /* pick random options to set/unset */
1271
        uint32_t rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1272
        pass &= sir_stdoutlevels(levels_array[rnd]);
191✔
1273
        pass &= sir_stdoutopts(opts_array[rnd]);
191✔
1274
        printf("\t" WHITE("set random config #%"PRIu32" for stdout") "\n", rnd);
161✔
1275

1276
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1277
        pass &= sir_stderrlevels(levels_array[rnd]);
191✔
1278
        pass &= sir_stderropts(opts_array[rnd]);
191✔
1279
        printf("\t" WHITE("set random config #%"PRIu32" for stderr") "\n", rnd);
161✔
1280

1281
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1282
        pass &= sir_filelevels(id1, levels_array[rnd]);
191✔
1283
        pass &= sir_fileopts(id1, opts_array[rnd]);
191✔
1284
        printf("\t" WHITE("set random config #%"PRIu32" for %s") "\n", rnd, logfile);
191✔
1285

1286
        pass &= filter_error(sir_debug("modified config #%"PRIu32" (debug)", rnd), SIR_E_NODEST);
191✔
1287
        pass &= filter_error(sir_info("modified config #%"PRIu32" (info)", rnd), SIR_E_NODEST);
191✔
1288
        pass &= filter_error(sir_notice("modified config #%"PRIu32" (notice)", rnd), SIR_E_NODEST);
191✔
1289
        pass &= filter_error(sir_warn("modified config #%"PRIu32" (warning)", rnd), SIR_E_NODEST);
191✔
1290
        pass &= filter_error(sir_error("modified config #%"PRIu32" (error)", rnd), SIR_E_NODEST);
191✔
1291
        pass &= filter_error(sir_crit("modified config #%"PRIu32" (critical)", rnd), SIR_E_NODEST);
191✔
1292
        pass &= filter_error(sir_alert("modified config #%"PRIu32" (alert)", rnd), SIR_E_NODEST);
191✔
1293
        pass &= filter_error(sir_emerg("modified config #%"PRIu32" (emergency)", rnd), SIR_E_NODEST);
191✔
1294
    }
1295

1296
    if (pass) {
23✔
1297
        /* restore to default config and run again */
1298
        sir_stdoutlevels(SIRL_DEFAULT);
19✔
1299
        sir_stderrlevels(SIRL_DEFAULT);
19✔
1300
        sir_stdoutopts(SIRO_DEFAULT);
19✔
1301
        sir_stderropts(SIRO_DEFAULT);
19✔
1302

1303
        pass &= sir_debug("default config (debug)");
19✔
1304
        pass &= sir_info("default config (info)");
19✔
1305
        pass &= sir_notice("default config (notice)");
19✔
1306
        pass &= sir_warn("default config (warning)");
19✔
1307
        pass &= sir_error("default config (error)");
19✔
1308
        pass &= sir_crit("default config (critical)");
19✔
1309
        pass &= sir_alert("default config (alert)");
19✔
1310
        pass &= sir_emerg("default config (emergency)");
19✔
1311
    }
1312

1313
    pass &= sir_remfile(id1);
23✔
1314
    rmfile(logfile);
23✔
1315
    sir_cleanup();
23✔
1316

1317
    return print_result_and_return(pass);
23✔
1318
}
1319

1320
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1321
static
1322
bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
17✔
1323
    static const int runs = 5;
1324

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

1329
# if !defined(__WIN__)
1330
    uint32_t rnd = (uint32_t)(_sir_getpid() + _sir_gettid());
17✔
1331
# else
1332
    uint32_t rnd = (uint32_t)GetTickCount();
1333
# endif
1334

1335
    bool pass = true;
14✔
1336
    for (int i = 1; i <= runs; i++) {
102✔
1337
        /* randomly skip setting process name, identity/category to thoroughly
1338
         * test fallback routines; randomly update the config mid-run. */
1339
        bool set_procname = getrand_bool(rnd ^ 0x5a5a5a5aU);
85✔
1340
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9U);
85✔
1341
        bool set_category = getrand_bool(rnd ^ 0x32323232U);
85✔
1342
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7U);
85✔
1343

1344
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
85✔
1345
            set_procname, set_identity, set_category, do_update);
1346

1347
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
94✔
1348
        si.d_syslog.opts   = SIRO_DEFAULT;
85✔
1349
        si.d_syslog.levels = SIRL_DEFAULT;
85✔
1350

1351
        if (set_identity)
85✔
1352
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
47✔
1353

1354
        if (set_category)
85✔
1355
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
48✔
1356

1357
        si_init = sir_init(&si); //-V519
85✔
1358
        pass &= si_init;
70✔
1359

1360
        if (do_update)
85✔
1361
            pass &= sir_sysloglevels(SIRL_ALL);
33✔
1362

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

1366
        pass &= sir_notice("%d/%d: this notice message sent to stdout and %s.", i, runs, sl_name);
85✔
1367
        pass &= sir_warn("%d/%d: this warning message sent to stdout and %s.", i, runs, sl_name);
85✔
1368
        pass &= sir_error("%d/%d: this error message sent to stdout and %s.", i, runs, sl_name);
85✔
1369

1370
        if (set_identity) {
85✔
1371
            pass &= sir_syslogid("my test ID");
47✔
1372
            pass &= sir_syslogid("my test ID"); /* test deduping. */
47✔
1373
        }
1374

1375
        if (set_category) {
85✔
1376
            pass &= sir_syslogcat("my test category");
48✔
1377
            pass &= sir_syslogcat("my test category"); /* test deduping. */
48✔
1378
        }
1379

1380
        if (do_update)
85✔
1381
            pass &= sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID));
33✔
1382

1383
        pass &= sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name);
85✔
1384
        pass &= sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name);
85✔
1385
        pass &= sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name);
85✔
1386

1387
# if defined(SIR_OS_LOG_ENABLED)
1388
#  if defined(__MACOS__) && !defined(__INTEL_COMPILER)
1389
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1390
            printf("\ttesting os_log activity feature...\n");
1391

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

1397
            /* execution now passes to os_log_parent_activity(), where some logging
1398
            * will occur, then a sub-activity will be created, and more logging. */
1399
            os_activity_apply_f(parent, (void*)parent, os_log_parent_activity);
1400
        }
1401
#  endif
1402
# endif
1403

1404
        sir_cleanup();
85✔
1405

1406
        if (!pass)
85✔
1407
            break;
×
1408
    }
1409

1410
    return print_result_and_return(pass);
17✔
1411
}
1412
#endif
1413

1414
#if defined(SIR_NO_SYSTEM_LOGGERS)
1415
static bool generic_disabled_syslog_test(const char* sl_name, const char* identity,
6✔
1416
    const char* category) {
1417
    INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0U, 0U, "sir_disabled_sltest");
6✔
1418
    si.d_syslog.opts   = SIRO_DEFAULT;
6✔
1419
    si.d_syslog.levels = SIRL_DEFAULT;
6✔
1420
    bool pass = true;
6✔
1421

1422
    SIR_UNUSED(sl_name);
1423

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

1426
    /* init should just ignore the syslog settings. */
1427
    pass &= sir_init(&si);
6✔
1428

1429
    /* these calls should all fail. */
1430
    printf("\tsetting levels...\n");
6✔
1431
    pass &= !sir_sysloglevels(SIRL_ALL);
6✔
1432

1433
    if (pass)
6✔
1434
        print_expected_error();
6✔
1435

1436
    printf("\tsetting options...\n");
6✔
1437
    pass &= !sir_syslogopts(SIRO_DEFAULT);
6✔
1438

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

1442
    printf("\tsetting identity...\n");
6✔
1443
    pass &= !sir_syslogid(identity);
6✔
1444

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

1448
    printf("\tsetting category...\n");
6✔
1449
    pass &= !sir_syslogcat(category);
6✔
1450

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

1454
    pass &= sir_cleanup();
6✔
1455
    return print_result_and_return(pass);
6✔
1456
}
1457
#endif
1458

1459
bool sirtest_syslog(void) {
23✔
1460
#if !defined(SIR_SYSLOG_ENABLED)
1461
# if defined(SIR_NO_SYSTEM_LOGGERS)
1462
    bool pass = generic_disabled_syslog_test("syslog", "sirtests", "tests");
6✔
1463
    return print_result_and_return(pass);
6✔
1464
# else
1465
    printf("\t" DGRAY("SIR_SYSLOG_ENABLED is not defined; skipping") "\n");
1466
    return true;
1467
# endif
1468
#else
1469
    bool pass = generic_syslog_test("syslog", "sirtests", "tests");
17✔
1470
    return print_result_and_return(pass);
17✔
1471
#endif
1472
}
1473

1474
bool sirtest_os_log(void) {
23✔
1475
#if !defined(SIR_OS_LOG_ENABLED)
1476
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
20✔
1477
    return true;
23✔
1478
#else
1479
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1480
    return print_result_and_return(pass);
1481
#endif
1482
}
1483

1484
char *sirtest_get_wineversion(void) {
46✔
1485
#if !defined(__WIN__)
1486
    return NULL;
46✔
1487
#else /* __WIN__ */
1488
    typedef char* (__stdcall *get_wine_ver_proc)(void);
1489
    static get_wine_ver_proc _p_wine_get_version = NULL;
1490

1491
    HMODULE _h_ntdll = GetModuleHandle("ntdll.dll");
1492
    if (_h_ntdll != NULL) {
1493
        _p_wine_get_version = (get_wine_ver_proc)GetProcAddress(_h_ntdll, "wine_get_version");
1494
        if (_p_wine_get_version) {
1495
            char *wine_version = _p_wine_get_version();
1496
            if (wine_version)
1497
                return wine_version;
1498
        }
1499
    }
1500
    return NULL;
1501
#endif
1502
}
1503

1504
bool sirtest_filesystem(void) {
23✔
1505
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1506
    bool pass = si_init;
20✔
1507

1508
    /* Wine version */
1509
    printf("\tRunning under Wine: %s\n",
23✔
1510
            sirtest_get_wineversion() ? sirtest_get_wineversion() : "no"); //-V547
23✔
1511

1512
    /* current working directory. */
1513
    char* cwd = _sir_getcwd();
23✔
1514
    pass &= NULL != cwd;
23✔
1515
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
23✔
1516

1517
    if (NULL != cwd) {
23✔
1518
        /* path to this binary file. */
1519
        char* filename = _sir_getappfilename();
23✔
1520
        pass &= NULL != filename;
23✔
1521
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
23✔
1522

1523
        if (NULL != filename) {
23✔
1524
            /* _sir_get[base|dir]name() can potentially modify filename,
1525
             * so make a copy for each call. */
1526
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1527
            pass &= NULL != filename2;
20✔
1528

1529
            if (NULL != filename2) {
20✔
1530
                /* filename, stripped of directory component(s). */
1531
                char* _basename = _sir_getbasename(filename2);
19✔
1532
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
19✔
1533

1534
                if (!_basename) {
19✔
1535
                    pass = false;
×
1536
                } else {
1537
                    /* the last strlen(_basename) chars of filename should match. */
1538
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
19✔
1539
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
19✔
1540
                    size_t n      = 0;
16✔
1541

1542
                    while (n < len) {
171✔
1543
                        if (filename[offset++] != _basename[n++]) {
152✔
1544
                            pass = false;
×
1545
                            break;
×
1546
                        }
1547
                    }
1548
                }
1549
            }
1550

1551
            /* directory this binary file resides in. */
1552
            char* appdir = _sir_getappdir();
20✔
1553
            pass &= NULL != appdir;
20✔
1554
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
20✔
1555

1556
            /* _sir_get[base|dir]name can potentially modify filename,
1557
             * so make a copy for each call. */
1558
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1559
            pass &= NULL != filename3;
20✔
1560

1561
            if (NULL != appdir && NULL != filename3) {
20✔
1562
                /* should yield the same result as _sir_getappdir(). */
1563
                char* _dirname = _sir_getdirname(filename3);
19✔
1564
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
19✔
1565

1566
                pass &= 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH));
19✔
1567
                pass &= NULL != _dirname &&
38✔
1568
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH));
19✔
1569
            }
1570

1571
            _sir_safefree(&appdir);
20✔
1572
            _sir_safefree(&filename);
20✔
1573
            _sir_safefree(&filename2);
20✔
1574
            _sir_safefree(&filename3);
20✔
1575
        }
1576

1577
        _sir_safefree(&cwd);
23✔
1578
    }
1579

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

1601
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
161✔
1602
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
138✔
1603
        if (NULL != tmp) {
138✔
1604
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
132✔
1605
                tmp, _sir_getdirname(tmp));
1606
            _sir_safefree(&tmp);
132✔
1607
        }
1608
    }
1609

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

1627
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
138✔
1628
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
115✔
1629
        if (NULL != tmp) {
115✔
1630
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
110✔
1631
                tmp, _sir_getbasename(tmp));
1632
            _sir_safefree(&tmp);
110✔
1633
        }
1634
    }
1635

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

1658
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
207✔
1659
        bool relative = false;
184✔
1660
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
184✔
1661

1662
        if (relative == abs_or_rel_paths[n].abs) {
184✔
1663
            pass = false;
×
1664
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1665
                relative ? "true" : "false");
×
1666
        } else {
1667
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
184✔
1668
                relative ? "true" : "false");
160✔
1669
        }
1670

1671
        pass &= ret;
160✔
1672
        if (!ret) {
184✔
1673
            bool unused = print_test_error(false, false);
×
1674
            SIR_UNUSED(unused);
1675
        }
1676
    }
1677

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

1707
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
253✔
1708
        bool exists = false;
230✔
1709
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
230✔
1710

1711
        if (exists != real_or_not[n].exists) {
230✔
1712
            pass = false;
23✔
1713
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
23✔
1714
                exists ? "true" : "false");
23✔
1715
        } else {
1716
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
207✔
1717
                exists ? "true" : "false");
177✔
1718
        }
1719

1720
        pass &= ret;
200✔
1721
        if (!ret) {
230✔
1722
            bool unused = print_test_error(false, false);
28✔
1723
            SIR_UNUSED(unused);
1724
        }
1725
    }
1726

1727
    /* checking file descriptors. */
1728
    static int bad_fds[] = {
1729
        0,
1730
        1,
1731
        2,
1732
        1234
1733
    };
1734
    if (sirtest_get_wineversion()) { //-V547
23✔
1735
        bad_fds[3] = 0;
×
1736
    }
1737

1738
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
115✔
1739
        if (_sir_validfd(bad_fds[n])) {
92✔
1740
            pass = false;
×
1741
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1742
        } else {
1743
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
92✔
1744
        }
1745
    }
1746

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

1762
    _sir_safefclose(&f);
23✔
1763

1764
    sir_cleanup();
23✔
1765
    return print_result_and_return(pass);
23✔
1766
}
1767

1768
bool sirtest_squelchspam(void) {
23✔
1769
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1770
    bool pass = si_init;
20✔
1771

1772
    static const size_t alternate   = 50;
1773
    static const size_t sequence[3] = {
1774
        1000, /* non-repeating messages. */
1775
        1000, /* repeating messages. */
1776
        1000  /* alternating repeating and non-repeating messages. */
1777
    };
1778

1779
    sir_time timer;
1780
    sir_timer_start(&timer);
23✔
1781

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

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

1788
        if (ascii_idx == 125)
23,000✔
1789
            ascii_idx = 33;
200✔
1790
    }
1791

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

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

1797
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
23,000✔
1798
            pass &= !ret;
22,908✔
1799
        else
1800
            pass &= ret;
80✔
1801
    }
1802

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

1806
    bool repeating   = false;
20✔
1807
    size_t counter   = 0;
20✔
1808
    size_t repeat_id = 0;
20✔
1809
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
23,023✔
1810
        if (!repeating) {
23,000✔
1811
            pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
11,523✔
1812
                (char)ascii_idx + 1);
11,523✔
1813
        } else {
1814
            bool ret = sir_debug("%zu a repeating message", repeat_id);
11,477✔
1815

1816
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
11,477✔
1817
                pass &= !ret;
10,557✔
1818
            else
1819
                pass &= ret;
800✔
1820
        }
1821

1822
        if (counter == alternate) {
23,000✔
1823
            repeating = !repeating;
437✔
1824
            counter = 0;
380✔
1825
            repeat_id++;
437✔
1826
        }
1827

1828
        if (ascii_idx == 125)
23,000✔
1829
            ascii_idx = 33;
200✔
1830
    }
1831

1832
    sir_cleanup();
23✔
1833
    return print_result_and_return(pass);
23✔
1834
}
1835

1836
bool sirtest_pluginloader(void) {
23✔
1837
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1838
    bool pass = si_init;
20✔
1839

1840
#if !defined(__WIN__)
1841
# define PLUGIN_EXT "so"
1842
#else
1843
# define PLUGIN_EXT "dll"
1844
#endif
1845

1846
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1847
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1848
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1849
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1850
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1851
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1852
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1853
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1854

1855
#if defined(SIR_NO_PLUGINS)
1856
    SIR_UNUSED(plugin2);
1857
    SIR_UNUSED(plugin3);
1858
    SIR_UNUSED(plugin4);
1859
    SIR_UNUSED(plugin5);
1860
    SIR_UNUSED(plugin6);
1861
    SIR_UNUSED(plugin7);
1862
    SIR_UNUSED(plugin8);
1863

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

1866
    printf("\tloading good plugin: '%s'...\n", plugin1);
1✔
1867
    /* load a valid, well-behaved plugin. */
1868
    sirpluginid id = sir_loadplugin(plugin1);
1✔
1869
    pass &= 0 == id;
1✔
1870

1871
    if (pass)
1✔
1872
        print_expected_error();
1✔
1873

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

1878
    if (pass)
1✔
1879
        print_expected_error();
1✔
1880
#else
1881
    /* load a valid, well-behaved plugin. */
1882
    printf("\tloading good plugin: '%s'...\n", plugin1);
22✔
1883
    sirpluginid id = sir_loadplugin(plugin1);
22✔
1884
    pass &= 0 != id;
22✔
1885

1886
    print_test_error(pass, pass);
22✔
1887

1888
    pass &= sir_info("this message will be dispatched to the plugin.");
22✔
1889
    pass &= sir_warn("this message will *not* be dispatched to the plugin.");
22✔
1890

1891
    /* re-loading the same plugin should fail. */
1892
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
22✔
1893
    sirpluginid badid = sir_loadplugin(plugin1);
22✔
1894
    pass &= 0 == badid;
22✔
1895

1896
    print_test_error(pass, pass);
22✔
1897

1898
    /* the following are all invalid or misbehaved, and should all fail. */
1899
    printf("\tloading bad plugin: '%s'...\n", plugin2);
22✔
1900
    badid = sir_loadplugin(plugin2);
22✔
1901
    pass &= 0 == badid;
22✔
1902

1903
    print_test_error(pass, pass);
22✔
1904

1905
    printf("\tloading bad plugin: '%s'...\n", plugin3);
22✔
1906
    badid = sir_loadplugin(plugin3);
22✔
1907
    pass &= 0 == badid;
22✔
1908

1909
    print_test_error(pass, pass);
22✔
1910

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

1915
    print_test_error(pass, pass);
22✔
1916

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

1921
    print_test_error(pass, pass);
22✔
1922

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

1927
    print_test_error(pass, pass);
22✔
1928

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

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

1936
    print_test_error(pass, pass);
22✔
1937

1938
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
22✔
1939
    badid = sir_loadplugin(plugin8);
22✔
1940
    pass &= 0 == badid;
22✔
1941

1942
    print_test_error(pass, pass);
22✔
1943

1944
    /* unload the good plugin manually. */
1945
    printf("\tunloading good plugin: '%s'...\n", plugin1);
22✔
1946
    pass &= sir_unloadplugin(id);
22✔
1947

1948
    print_test_error(pass, pass);
22✔
1949

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

1954
    print_test_error(pass, pass);
22✔
1955

1956
    /* test bad paths. */
1957
    printf("\ntrying to load plugin with NULL path...\n");
19✔
1958
    badid = sir_loadplugin(NULL);
22✔
1959
    pass &= 0 == badid;
22✔
1960

1961
    print_test_error(pass, pass);
22✔
1962
#endif
1963
    pass &= sir_cleanup();
23✔
1964
    return print_result_and_return(pass);
23✔
1965
}
1966

1967
bool sirtest_getversioninfo(void) {
23✔
1968
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1969
    bool pass = si_init;
20✔
1970

1971
    printf("\tchecking version retrieval functions...\n");
20✔
1972

1973
    const char* str = sir_getversionstring();
23✔
1974
    pass &= _sir_validstrnofail(str);
23✔
1975

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

1978
    uint32_t hex = sir_getversionhex();
23✔
1979
    pass &= 0 != hex;
23✔
1980

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

1983
    bool prerel = sir_isprerelease();
23✔
1984
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
23✔
1985

1986
    pass &= sir_cleanup();
23✔
1987
    return print_result_and_return(pass);
23✔
1988
}
1989

1990
enum {
1991
    NUM_THREADS = 4
1992
};
1993

1994
static bool threadpool_pseudojob(void* arg) {
168✔
1995
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
168✔
1996
#if !defined(__WIN__)
1997
    sleep(1);
168✔
1998
#else
1999
    Sleep(1000);
2000
#endif
2001
    return true;
161✔
2002
}
2003

2004
bool sirtest_threadpool(void) {
23✔
2005
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
23✔
2006
    bool pass = si_init;
20✔
2007

2008
    static const size_t num_jobs = 30;
2009
    sir_threadpool* pool         = NULL;
23✔
2010

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

2026
#if !defined(__WIN__)
2027
        sleep(1);
21✔
2028
#else
2029
        Sleep(1000);
2030
#endif
2031

2032
        pass &= sir_info("destroying thread pool...");
21✔
2033
        pass &= _sir_threadpool_destroy(&pool);
21✔
2034
    }
2035

2036
    pass &= sir_cleanup();
23✔
2037
    return print_result_and_return(pass);
23✔
2038
}
2039

2040
#if !defined(__WIN__)
2041
static void* threadrace_thread(void* arg);
2042
#else /* __WIN__ */
2043
static unsigned __stdcall threadrace_thread(void* arg);
2044
#endif
2045

2046
bool sirtest_threadrace(void) {
23✔
2047
#if !defined(__WIN__)
2048
    pthread_t thrds[NUM_THREADS] = {0};
23✔
2049
#else /* __WIN__ */
2050
    uintptr_t thrds[NUM_THREADS] = {0};
2051
#endif
2052

2053
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
23✔
2054
    bool pass           = si_init;
20✔
2055
    bool any_created    = false;
20✔
2056
    size_t last_created = 0;
20✔
2057

2058
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
23✔
2059
    pass &= NULL != heap_args;
23✔
2060
    if (!heap_args) {
23✔
2061
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2062
        return false;
1✔
2063
    }
2064

2065
    for (size_t n = 0; n < NUM_THREADS; n++) {
106✔
2066
        if (!pass)
85✔
2067
            break;
×
2068

2069
        heap_args[n].pass = true;
85✔
2070
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
85✔
2071
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2072

2073
#if !defined(__WIN__)
2074
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
85✔
2075
        if (0 != create) {
85✔
2076
            errno = create;
1✔
2077
            handle_os_error(true, "pthread_create() for thread #%zu failed!", n + 1);
1✔
2078
#else /* __WIN__ */
2079
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2080
        if (0 == thrds[n]) {
2081
            handle_os_error(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2082
#endif
2083
            pass = false;
1✔
2084
            break;
1✔
2085
        }
2086

2087
        last_created = n;
72✔
2088
        any_created  = true;
72✔
2089
    }
2090

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

2114
                pass &= heap_args[j].pass;
80✔
2115
                if (heap_args[j].pass)
80✔
2116
                    printf("\t" GREEN("thread #%zu returned pass = true") "\n", j + 1);
68✔
2117
                else
2118
                    printf("\t" RED("thread #%zu returned pass = false!") "\n", j + 1);
×
2119
            }
2120
        }
2121
    }
2122

2123
    _sir_safefree(&heap_args);
22✔
2124

2125
    sir_cleanup();
22✔
2126
    return print_result_and_return(pass);
22✔
2127
}
2128

2129
#if !defined(__WIN__)
2130
static void* threadrace_thread(void* arg) {
83✔
2131
#else /* __WIN__ */
2132
unsigned __stdcall threadrace_thread(void* arg) {
2133
#endif
2134
    pid_t threadid       = _sir_gettid();
83✔
2135
    thread_args* my_args = (thread_args*)arg;
71✔
2136

2137
    rmfile(my_args->log_file);
83✔
2138
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
84✔
2139

2140
    if (0 == id) {
84✔
2141
        bool unused = print_test_error(false, false);
10✔
2142
        SIR_UNUSED(unused);
2143
#if !defined(__WIN__)
2144
        return NULL;
10✔
2145
#else /* __WIN__ */
2146
        return 0;
2147
#endif
2148
    }
2149

2150
    printf("\thi, i'm thread (id: %d), logging to: '%s'...\n",
62✔
2151
            PID_CAST threadid, my_args->log_file);
62✔
2152

2153
#if !defined(DUMA)
2154
# define NUM_ITERATIONS 1000
2155
#else
2156
# define NUM_ITERATIONS 100
2157
#endif
2158

2159
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
74,073✔
2160
        /* choose a random level, and colors. */
2161
        sir_textcolor fg = SIRTC_DEFAULT;
62,000✔
2162
        sir_textcolor bg = SIRTC_DEFAULT;
62,000✔
2163

2164
        if (n % 2 == 0) {
73,999✔
2165
            fg = SIRTC_CYAN;
31,000✔
2166
            bg = SIRTC_BLACK;
31,000✔
2167
            sir_debug("log message #%zu", n);
37,000✔
2168
        } else {
2169
            fg = SIRTC_BLACK;
31,000✔
2170
            bg = SIRTC_CYAN;
31,000✔
2171
            sir_info("log message #%zu", n);
36,999✔
2172
        }
2173

2174
        /* sometimes remove and re-add the log file, and set some options/styles.
2175
         * other times, just set different options/styles. */
2176
        uint32_t rnd = (uint32_t)(n + threadid);
74,000✔
2177
        if (getrand_bool((uint32_t)(rnd > 1 ? rnd : 1))) {
74,000✔
2178
            my_args->pass = print_test_error(sir_remfile(id), false);
37,380✔
2179
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
37,380✔
2180
                SIRL_ALL, SIRO_MSGONLY), false);
2181

2182
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
74,760✔
2183
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
37,380✔
2184
            my_args->pass = print_test_error(test, false);
37,380✔
2185

2186
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
37,380✔
2187
            my_args->pass = print_test_error(test, false);
37,380✔
2188
        } else {
2189
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
73,240✔
2190
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
36,620✔
2191
            my_args->pass = print_test_error(test, false);
36,620✔
2192

2193
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
36,620✔
2194
            my_args->pass = print_test_error(test, false);
36,607✔
2195

2196
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
36,620✔
2197
            my_args->pass = print_test_error(test, false);
36,619✔
2198
        }
2199

2200
        if (!my_args->pass)
73,999✔
2201
            break;
×
2202
    }
2203

2204
    my_args->pass = print_test_error(sir_remfile(id), false);
74✔
2205

2206
    rmfile(my_args->log_file);
74✔
2207

2208
#if !defined(__WIN__)
2209
    return NULL;
74✔
2210
#else /* __WIN__ */
2211
    return 0U;
2212
#endif
2213
}
2214

2215
/*
2216
bool sirtest_XXX(void) {
2217
    INIT(si, SIRL_ALL, 0U, 0U, 0U);
2218
    bool pass = si_init;
2219

2220
    pass &= sir_cleanup();
2221
    return print_result_and_return(pass);
2222
}
2223
*/
2224

2225
/* ========================== end tests ========================== */
2226

2227
bool print_test_error(bool result, bool expected) {
260,874✔
2228
    char message[SIR_MAXERROR] = {0};
260,874✔
2229
    uint16_t code              = sir_geterror(message);
260,874✔
2230

2231
    if (!expected && !result && SIR_E_NOERROR != code)
260,914✔
2232
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
3,069✔
2233
    else if (expected && SIR_E_NOERROR != code)
257,845✔
2234
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
509✔
2235

2236
    return result;
260,911✔
2237
}
2238

2239
bool print_os_error(void) {
62✔
2240
    char message[SIR_MAXERROR] = {0};
62✔
2241
    uint16_t code              = sir_geterror(message);
62✔
2242
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
62✔
2243
    return false;
62✔
2244
}
2245

2246
bool filter_error(bool pass, uint16_t err) {
1,528✔
2247
    if (!pass) {
1,528✔
2248
        char message[SIR_MAXERROR] = {0};
949✔
2249
        uint16_t code              = sir_geterror(message);
949✔
2250
        if (code != err)
949✔
2251
            return false;
2✔
2252
    }
2253
    return true;
1,286✔
2254
}
2255

2256
uint32_t getrand(uint32_t upper_bound) {
115,728✔
2257
#if !defined(__WIN__) || defined(__EMBARCADEROC__)
2258
# if defined(__MACOS__) || defined(__BSD__)
2259
    if (upper_bound < 2U)
2260
        upper_bound = 2U;
2261
    return arc4random_uniform(upper_bound);
2262
# else
2263
#  if defined(__EMBARCADEROC__)
2264
    return (uint32_t)(random(upper_bound));
2265
#  else
2266
    return (uint32_t)(random() % upper_bound);
115,728✔
2267
#  endif
2268
# endif
2269
#else /* __WIN__ */
2270
    uint32_t ctx = 0;
2271
    if (0 != rand_s(&ctx))
2272
        ctx = (uint32_t)rand();
2273
    return ctx % upper_bound;
2274
#endif
2275
}
2276

2277
bool rmfile(const char* filename) {
1,112✔
2278
    bool removed = false;
965✔
2279

2280
    /* return true if leave_logs is true. */
2281
    if (leave_logs) {
1,112✔
2282
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
49✔
2283
            filename, _cl_arg_list[3].flag);
49✔
2284
        return true;
49✔
2285
    }
2286

2287
    /* return true if the file doesn't exist. */
2288
    struct stat st;
2289
    if (0 != stat(filename, &st)) {
1,063✔
2290
        if (ENOENT == errno)
567✔
2291
            return true;
455✔
2292

2293
        handle_os_error(true, "failed to stat %s!", filename);
46✔
2294
        return false;
46✔
2295
    }
2296

2297
    if (!_sir_deletefile(filename)) {
497✔
2298
        handle_os_error(false, "failed to delete %s!", filename);
×
2299
    } else {
2300
        printf("\t" DGRAY("deleted %s (%ld bytes)...") "\n", filename,
416✔
2301
            (long)st.st_size);
497✔
2302
    }
2303

2304
    return removed;
416✔
2305
}
2306

2307
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
244✔
2308
    if (strstr(filename, search)) {
244✔
2309
        char filepath[SIR_MAXPATH];
2310
        _sir_snprintf_trunc(filepath, SIR_MAXPATH, "%s%s", path, filename);
58✔
2311

2312
        rmfile(filepath);
58✔
2313
        (*data)++;
58✔
2314
    }
2315
}
244✔
2316

2317
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
121✔
2318
    SIR_UNUSED(path);
2319
    if (strstr(filename, search))
121✔
2320
        (*data)++;
35✔
2321
}
121✔
2322

2323
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
63✔
2324
#if !defined(__WIN__)
2325
    DIR* d = opendir(path);
63✔
2326
    if (!d)
63✔
2327
        return print_os_error();
2✔
2328

2329
    rewinddir(d);
61✔
2330
    struct dirent* di = readdir(d);
61✔
2331
    if (!di) {
61✔
2332
        closedir(d);
×
2333
        return print_os_error();
×
2334
    }
2335

2336
    while (NULL != di) {
426✔
2337
        cb(search, path, di->d_name, data);
365✔
2338
        di = readdir(d);
365✔
2339
    }
2340

2341
    closedir(d);
61✔
2342
    d = NULL;
52✔
2343
#else /* __WIN__ */
2344
    WIN32_FIND_DATA finddata = {0};
2345
    char buf[SIR_MAXPATH]    = {0};
2346

2347
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2348

2349
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2350

2351
    if (INVALID_HANDLE_VALUE == enumerator)
2352
        return false;
2353

2354
    do {
2355
        cb(search, path, finddata.cFileName, data);
2356
    } while (FindNextFile(enumerator, &finddata) > 0);
2357

2358
    FindClose(enumerator);
2359
    enumerator = NULL;
2360
#endif
2361

2362
    return true;
61✔
2363
}
2364

2365
double sir_timer_elapsed(const sir_time* timer) {
2,000,028✔
2366
    sir_time now;
2367
    return _sir_msec_since(timer, &now);
2,000,028✔
2368
}
2369

2370
long sir_timer_getres(void) {
1✔
2371
    long retval = 0L;
1✔
2372
#if !defined(__WIN__)
2373
    struct timespec res;
2374
    if (0 == clock_getres(SIR_INTERVALCLOCK, &res)) {
1✔
2375
        retval = res.tv_nsec;
1✔
2376
    } else {
2377
        handle_os_error(true, "clock_getres(%d) failed!", CLOCK_CAST SIR_INTERVALCLOCK); // GCOVR_EXCL_LINE
2378
    }
2379
#else /* __WIN__ */
2380
    LARGE_INTEGER cntr_freq;
2381
    (void)QueryPerformanceFrequency(&cntr_freq);
2382
    if (cntr_freq.QuadPart <= 0)
2383
        retval = 0L;
2384
    else
2385
        retval = (long)(ceil(((double)cntr_freq.QuadPart) / 1e9));
2386
#endif
2387
    return retval;
1✔
2388
}
2389

2390
void sir_sleep_msec(uint32_t msec) {
×
2391
    if (0U == msec)
×
2392
        return;
×
2393

2394
#if !defined(__WIN__)
2395
    struct timespec ts = { msec / 1000, (msec % 1000) * 1000000 };
×
2396
    (void)nanosleep(&ts, NULL);
×
2397
#else /* __WIN__ */
2398
    (void)SleepEx((DWORD)msec, TRUE);
2399
#endif
2400
}
2401

2402
#if defined(SIR_OS_LOG_ENABLED)
2403
void os_log_parent_activity(void* ctx) {
2404
    sir_debug("confirming with ground control that we are a go...");
2405
    sir_info("all systems go; initiating launch sequence");
2406
    sir_warn("getting some excessive vibration here");
2407
    sir_info("safely reached escape velocity. catch you on the flip side");
2408
    sir_info("(3 days later) we have landed on the lunar surface");
2409
    sir_notice("beginning rock counting...");
2410

2411
    os_activity_t parent = (os_activity_t)ctx;
2412
    os_activity_t child = os_activity_create("counting moon rocks", parent, // -V530
2413
        OS_ACTIVITY_FLAG_DEFAULT);
2414

2415
    float rock_count = 0.0f;
2416
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2417
    sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2418
        (double)rock_count);
2419
}
2420

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

2424
    for (size_t n = 0; n < 10; n++) {
2425
        sir_info("counting rocks in sector %zu...", n);
2426
    }
2427

2428
    float* rock_count = (float*)ctx;
2429
    *rock_count = 1e12f;
2430
    sir_info("all sectors counted; heading back to the lunar lander");
2431
}
2432
#endif
2433

2434
bool mark_test_to_run(const char* name) {
3✔
2435
    bool found = false;
3✔
2436
    for (size_t t = 0; t < _sir_countof(sir_tests); t++) {
46✔
2437
        if (_sir_strsame(name, sir_tests[t].name,
45✔
2438
            strnlen(sir_tests[t].name, SIR_MAXTESTNAME))) {
2439
            found = sir_tests[t].run = true;
2✔
2440
            break;
2✔
2441
        }
2442
    }
2443

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

2447
    return found;
3✔
2448
}
2449

2450
void print_usage_info(void) {
4✔
2451
    size_t longest = 0;
4✔
2452
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2453
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2454
        if (len > longest)
28✔
2455
            longest = len;
8✔
2456
    }
2457

2458
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2459

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

2463
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2464
        if (len < longest)
28✔
2465
            for (size_t n = len; n < longest; n++)
156✔
2466
                fprintf(stderr, " ");
132✔
2467

2468
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2469
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2470
            _cl_arg_list[i].desc);
28✔
2471
    }
2472

2473
    fprintf(stderr, "\n");
4✔
2474
}
4✔
2475

2476
void print_test_list(void) {
1✔
2477
    size_t longest = 0;
1✔
2478
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
32✔
2479
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2480
        if (len > longest)
31✔
2481
            longest = len;
3✔
2482
    }
2483

2484
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2485

2486
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
32✔
2487
        printf("\t%s\t", sir_tests[i].name);
31✔
2488

2489
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2490
        if (len < longest)
31✔
2491
            for (size_t n = len; n < longest; n++)
270✔
2492
                printf(" ");
240✔
2493

2494
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
31✔
2495
            printf("\n");
16✔
2496
    }
2497

2498
    printf("\n");
1✔
2499
}
1✔
2500

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