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

aremmell / libsir / 433

05 Sep 2023 12:02AM UTC coverage: 94.671% (-0.2%) from 94.865%
433

Pull #257

gitlab-ci

aremmell
flip ifdef logic for static init
Pull Request #257: General performance enhancements & portability improvements

236 of 236 new or added lines in 13 files covered. (100.0%)

3020 of 3190 relevant lines covered (94.67%)

632992.37 hits per line

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

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

29
static sir_test sir_tests[] = {
30
    {"performance",             sirtest_perf, false, true},
31
    {"thread-race",             sirtest_threadrace, false, true},
32
    {"thread-pool",             sirtest_threadpool, false, true},
33
    {"exceed-max-buffer-size",  sirtest_exceedmaxsize, false, true},
34
    {"no-output-destination",   sirtest_failnooutputdest, false, true},
35
    {"null-pointers",           sirtest_failnulls, false, true},
36
    {"empty-message",           sirtest_failemptymessage, false, true},
37
    {"file-cache-sanity",       sirtest_filecachesanity, false, true},
38
    {"file-invalid-name",       sirtest_failinvalidfilename, false, true},
39
    {"file-bad-permissions",    sirtest_failfilebadpermission, false, true},
40
    {"file-duplicate-name",     sirtest_faildupefile, false, true},
41
    {"file-remove-nonexistent", sirtest_failremovebadfile, false, true},
42
    {"file-archive-large",      sirtest_rollandarchivefile, false, true},
43
    {"init-output-before",      sirtest_failwithoutinit, false, true},
44
    {"init-superfluous",        sirtest_failinittwice, false, true},
45
    {"init-bad-data",           sirtest_failinvalidinitdata, false, true},
46
    {"init-cleanup-init",       sirtest_initcleanupinit, false, true},
47
    {"init-with-makeinit",      sirtest_initmakeinit, false, true},
48
    {"cleanup-output-after",    sirtest_failaftercleanup, false, true},
49
    {"sanity-errors",           sirtest_errorsanity, false, true},
50
    {"sanity-text-styles",      sirtest_textstylesanity, false, true},
51
    {"sanity-options",          sirtest_optionssanity, false, true},
52
    {"sanity-levels",           sirtest_levelssanity, false, true},
53
    {"sanity-mutexes",          sirtest_mutexsanity, false, true},
54
    {"sanity-update-config",    sirtest_updatesanity, false, true},
55
    {"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) {
33✔
66
#if defined(__HAIKU__) && !defined(DEBUG)
67
    disable_debugger(1);
68
#endif
69

70
#include "tests_malloc.h"
71

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

87
    bool wait     = false;
29✔
88
    bool only     = false;
29✔
89
    size_t to_run = 0;
29✔
90

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

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

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

152
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
808✔
153
        if (only && !sir_tests[n].run) {
782✔
154
            _sir_selflog("skipping '%s'; not marked to run", sir_tests[n].name);
60✔
155
            continue;
60✔
156
        }
157

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

160
        sir_tests[n].pass = sir_tests[n].fn();
722✔
161
        if (sir_tests[n].pass)
722✔
162
            passed++;
677✔
163

164
        ran++;
632✔
165

166
        printf(WHITEB("\n(%zu/%zu) '%s' finished: ") "%s\n", ran, tgt_tests, sir_tests[n].name,
722✔
167
            PRN_PASS(sir_tests[n].pass));
632✔
168
    }
169

170
    double elapsed = sir_timer_elapsed(&timer);
26✔
171

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

181
        printf(REDB("Failed %s:") "\n\n", TEST_S(tgt_tests - passed));
16✔
182

183
        for (size_t t = 0; t < _sir_countof(sir_tests); t++)
512✔
184
            if (!sir_tests[t].pass)
496✔
185
                printf(RED(INDENT_ITEM "%s\n"), sir_tests[t].name);
45✔
186
        printf("\n");
16✔
187
    }
188

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

196
    return passed == tgt_tests ? EXIT_SUCCESS : EXIT_FAILURE;
26✔
197
}
198

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

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

207
    pass &= sir_info("%s", toobig);
24✔
208

209
    sir_cleanup();
24✔
210
    return print_result_and_return(pass);
24✔
211
}
212

213
bool sirtest_failnooutputdest(void) {
24✔
214
    INIT(si, 0, 0, 0, 0);
24✔
215
    bool pass = si_init;
21✔
216

217
    static const char* logfilename = MAKE_LOG_NAME("nodestination.log");
218

219
    pass &= !sir_notice("this goes nowhere!");
24✔
220

221
    if (pass) {
24✔
222
        print_expected_error();
24✔
223

224
        pass &= sir_stdoutlevels(SIRL_INFO);
24✔
225
        pass &= sir_info("this goes to stdout");
24✔
226
        pass &= sir_stdoutlevels(SIRL_NONE);
24✔
227

228
        sirfileid fid = sir_addfile(logfilename, SIRL_INFO, SIRO_DEFAULT);
24✔
229
        pass &= 0U != fid;
24✔
230
        pass &= sir_info("this goes to %s", logfilename);
24✔
231
        pass &= sir_filelevels(fid, SIRL_NONE);
24✔
232
        pass &= !sir_notice("this goes nowhere!");
24✔
233

234
        if (0U != fid)
24✔
235
            pass &= sir_remfile(fid);
21✔
236

237
        rmfile(logfilename);
24✔
238
    }
239

240
    sir_cleanup();
24✔
241
    return print_result_and_return(pass);
24✔
242
}
243

244
bool sirtest_failnulls(void) {
24✔
245
    INIT_BASE(si, SIRL_ALL, 0, 0, 0, "", false);
24✔
246
    bool pass = true;
21✔
247

248
    pass &= !sir_init(NULL);
24✔
249

250
    if (pass)
24✔
251
        print_expected_error();
24✔
252

253
    pass &= sir_init(&si);
24✔
254
    pass &= !sir_info(NULL); //-V575 //-V618
24✔
255

256
    if (pass)
24✔
257
        print_expected_error();
24✔
258

259
    pass &= 0U == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY);
24✔
260

261
    if (pass)
24✔
262
        print_expected_error();
24✔
263

264
    pass &= !sir_remfile(0U);
24✔
265

266
    if (pass)
24✔
267
        print_expected_error();
24✔
268

269
    sir_cleanup();
24✔
270
    return print_result_and_return(pass);
24✔
271
}
272

273
bool sirtest_failemptymessage(void) {
24✔
274
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
275
    bool pass = si_init;
21✔
276

277
    pass &= !sir_debug("%s", "");
24✔
278

279
    sir_cleanup();
24✔
280
    return print_result_and_return(pass);
24✔
281
}
282

283
bool sirtest_filecachesanity(void) {
24✔
284
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
285
    bool pass = si_init;
21✔
286

287
    size_t numfiles             = SIR_MAXFILES + 1;
21✔
288
    sirfileid ids[SIR_MAXFILES] = {0};
24✔
289

290
    sir_options even = SIRO_MSGONLY;
21✔
291
    sir_options odd  = SIRO_ALL;
21✔
292

293
    for (size_t n = 0; n < numfiles - 1; n++) {
408✔
294
        char path[SIR_MAXPATH] = {0};
384✔
295
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), n);
336✔
296
        rmfile(path);
384✔
297
        ids[n] = sir_addfile(path, SIRL_ALL, (n % 2) ? odd : even);
408✔
298
        pass &= 0U != ids[n] && sir_info("test %zu", n);
384✔
299
    }
300

301
    pass &= sir_info("test test test");
24✔
302

303
    /* this one should fail; max files already added. */
304
    pass &= 0U == sir_addfile(MAKE_LOG_NAME("should-fail.log"), SIRL_ALL, SIRO_MSGONLY);
24✔
305

306
    if (pass)
24✔
307
        print_expected_error();
20✔
308

309
    sir_info("test test test");
24✔
310

311
    /* now remove previously added files in a different order. */
312
    size_t removeorder[SIR_MAXFILES];
313
    memset(removeorder, -1, sizeof(removeorder));
21✔
314

315
    long processed = 0L;
21✔
316
    printf("\tcreating random file ID order...\n");
21✔
317

318
    do {
1,167✔
319
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,350✔
320
        bool skip  = false;
1,188✔
321

322
        for (size_t n = 0; n < SIR_MAXFILES; n++)
12,911✔
323
            if (removeorder[n] == rnd) {
12,527✔
324
                skip = true;
852✔
325
                break;
852✔
326
            }
327

328
        if (skip)
1,350✔
329
            continue;
966✔
330

331
        removeorder[processed++] = rnd;
384✔
332

333
        if (processed == SIR_MAXFILES)
384✔
334
            break;
21✔
335
    } while (true);
336

337
    printf("\tremove order: {");
21✔
338
    for (size_t n = 0; n < SIR_MAXFILES; n++)
408✔
339
        printf(" %zu%s", removeorder[n], (n < SIR_MAXFILES - 1) ? "," : "");
387✔
340
    printf(" }...\n");
21✔
341

342
    for (size_t n = 0; n < SIR_MAXFILES; n++) {
408✔
343
        pass &= sir_remfile(ids[removeorder[n]]);
384✔
344

345
        char path[SIR_MAXPATH] = {0};
384✔
346
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), removeorder[n]);
384✔
347
        rmfile(path);
384✔
348
    }
349

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

352
    sir_cleanup();
24✔
353
    return print_result_and_return(pass);
24✔
354
}
355

356
bool sirtest_failinvalidfilename(void) {
24✔
357
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
358
    bool pass = si_init;
21✔
359

360
    pass &= 0U == sir_addfile("bad file!/name", SIRL_ALL, SIRO_MSGONLY);
24✔
361

362
    if (pass)
24✔
363
        print_expected_error();
24✔
364

365
    sir_cleanup();
24✔
366
    return print_result_and_return(pass);
24✔
367
}
368

369
bool sirtest_failfilebadpermission(void) {
24✔
370
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
371
    bool pass = si_init;
21✔
372

373
#if !defined(__WIN__)
374
    static const char* path = "/noperms";
375
#else /* __WIN__ */
376
# if defined(__CYGWIN__)
377
    static const char* path = "/cygdrive/c/Windows/System32/noperms";
378
# else
379
    static const char* path;
380
    if (sirtest_get_wineversion()) {
381
        path = "Z:\\noperms";
382
    } else {
383
        path = "C:\\Windows\\System32\\noperms";
384
    }
385
# endif
386
#endif
387

388
    pass &= 0U == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY);
24✔
389

390
    if (pass)
24✔
391
        print_expected_error();
24✔
392

393
    sir_cleanup();
24✔
394
    return print_result_and_return(pass);
24✔
395
}
396

397
bool sirtest_faildupefile(void) {
24✔
398
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
399
    bool pass = si_init;
21✔
400

401
#if !defined(__WIN__)
402
    static const char* filename1 = "./logs/faildupefile.log";
403
    static const char* filename2 = "logs/faildupefile.log";
404
#else
405
    static const char* filename1 = "logs\\faildupefile.log";
406
    static const char* filename2 = "logs/faildupefile.log";
407
#endif
408

409
    static const char* filename3 = "logs/not-a-dupe.log";
410
    static const char* filename4 = "logs/also-not-a-dupe.log";
411

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

414
    /* should be fine; no other files added yet. */
415
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
24✔
416
    pass &= 0U != fid;
24✔
417

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

420
    /* should fail. this is the same file we already added. */
421
    pass &= 0U == sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
24✔
422

423
    if (pass)
24✔
424
        print_expected_error();
21✔
425

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

428
    /* should also fail. this is the same file we already added, even
429
     * if the path strings don't match. */
430
    pass &= 0U == sir_addfile(filename2, SIRL_ALL, SIRO_DEFAULT);
24✔
431

432
    if (pass)
24✔
433
        print_expected_error();
21✔
434

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

437
    /* should pass. this is a different file. */
438
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
24✔
439
    pass &= 0U != fid2;
24✔
440

441
    /* should also pass. */
442
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
24✔
443
    pass &= 0U != fid3;
24✔
444

445
    pass &= sir_info("hello three valid files");
24✔
446

447
    /* should now fail since we added it earlier. */
448
    pass &= 0U == sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
24✔
449

450
    if (pass)
24✔
451
        print_expected_error();
20✔
452

453
    /* don't remove all of the log files in order to also test
454
     * cache tear-down. */
455
    pass &= sir_remfile(fid);
24✔
456

457
    rmfile(filename1);
24✔
458
    rmfile(filename2);
24✔
459
    rmfile(filename3);
24✔
460
    rmfile(filename4);
24✔
461

462
    pass &= sir_cleanup();
24✔
463
    return print_result_and_return(pass);
24✔
464
}
465

466
bool sirtest_failremovebadfile(void) {
24✔
467
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
468
    bool pass = si_init;
21✔
469

470
    sirfileid invalidid = 9999999;
21✔
471
    pass &= !sir_remfile(invalidid);
24✔
472

473
    if (pass)
24✔
474
        print_expected_error();
24✔
475

476
    sir_cleanup();
24✔
477
    return print_result_and_return(pass);
24✔
478
}
479

480
bool sirtest_rollandarchivefile(void) {
25✔
481
    /* roll size minus 1KiB so we can write until it maxes. */
482
    static const long deltasize    = 1024L;
483
    const long fillsize            = SIR_FROLLSIZE - deltasize;
22✔
484
    static const char* logbasename = "rollandarchive";
485
    static const char* logext      = ".log";
486
    static const char* line        = "hello, i am some data. nice to meet you.";
487

488
    char logfilename[SIR_MAXPATH] = {0};
25✔
489
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
25✔
490

491
    unsigned delcount = 0U;
25✔
492
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
25✔
493
        handle_os_error(false, "failed to enumerate log files with base name: %s!",
2✔
494
            logbasename);
495
        return false;
2✔
496
    }
497

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

501
    FILE* f = NULL;
23✔
502
    _sir_fopen(&f, logfilename, "w");
23✔
503

504
    if (!f)
23✔
505
        return print_os_error();
×
506

507
    if (0 != fseek(f, fillsize, SEEK_SET)) {
23✔
508
        handle_os_error(true, "fseek in file %s failed!", logfilename);
1✔
509
        fclose(f);
1✔
510
        return false;
1✔
511
    }
512

513
    if (EOF == fputc('\0', f)) {
22✔
514
        handle_os_error(true, "fputc in file %s failed!", logfilename);
1✔
515
        fclose(f);
1✔
516
        return false;
1✔
517
    }
518

519
    fclose(f);
21✔
520

521
    INIT(si, 0, 0, 0, 0);
21✔
522
    bool pass = si_init;
18✔
523

524
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
21✔
525
    pass &= 0U != fileid;
21✔
526

527
    if (pass) {
21✔
528
        /* write an (approximately) known quantity until we should have rolled */
529
        size_t written  = 0;
16✔
530
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
19✔
531

532
        do {
533
            pass &= sir_debug("%zu %s", written, line);
1,444✔
534
            written += linesize;
1,444✔
535
        } while (pass && (written < deltasize + (linesize * 50)));
1,444✔
536

537
        /* look for files matching the original name. */
538
        unsigned foundlogs = 0U;
19✔
539
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
19✔
540
            handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
541
            pass = false;
×
542
        }
543

544
        /* if two (or more) are present, the test is a pass. */
545
        printf("\tfound %u log files with base name: %s\n", foundlogs, logbasename);
19✔
546
        pass &= foundlogs >= 2U;
19✔
547
    }
548

549
    pass &= sir_remfile(fileid);
21✔
550

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

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

560
    sir_cleanup();
21✔
561
    return print_result_and_return(pass);
21✔
562
}
563

564
bool sirtest_failwithoutinit(void) {
24✔
565
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
24✔
566

567
    if (pass)
24✔
568
        print_expected_error();
24✔
569

570
    return print_result_and_return(pass);
24✔
571
}
572

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

577
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
578
    pass &= !si2_init;
24✔
579

580
    if (pass)
24✔
581
        print_expected_error();
24✔
582

583
    sir_cleanup();
24✔
584
    return print_result_and_return(pass);
24✔
585
}
586

587
bool sirtest_failinvalidinitdata(void) {
24✔
588
    sirinit si;
589

590
    /* fill with bad data. */
591
    memset(&si, 0xab, sizeof(sirinit));
21✔
592

593
    printf("\tcalling sir_init with invalid data...\n");
21✔
594
    bool pass = !sir_init(&si);
24✔
595

596
    if (pass)
24✔
597
        print_expected_error();
24✔
598

599
    sir_cleanup();
24✔
600
    return print_result_and_return(pass);
24✔
601
}
602

603
bool sirtest_initcleanupinit(void) {
24✔
604
    INIT(si1, SIRL_ALL, 0, 0, 0);
24✔
605
    bool pass = si1_init;
21✔
606

607
    pass &= sir_info("init called once; testing output...");
24✔
608
    sir_cleanup();
24✔
609

610
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
611
    pass &= si2_init;
21✔
612

613
    pass &= sir_info("init called again after re-init; testing output...");
24✔
614
    sir_cleanup();
24✔
615

616
    return print_result_and_return(pass);
24✔
617
}
618

619
bool sirtest_initmakeinit(void) {
24✔
620
    bool pass = true;
21✔
621

622
    sirinit si;
623
    pass &= sir_makeinit(&si);
24✔
624
    pass &= sir_init(&si);
24✔
625
    pass &= sir_info("initialized with sir_makeinit");
24✔
626
    pass &= sir_cleanup();
24✔
627

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

631
bool sirtest_failaftercleanup(void) {
24✔
632
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
633
    bool pass = si_init;
21✔
634

635
    sir_cleanup();
24✔
636
    pass &= !sir_info("already cleaned up; this needs to fail");
24✔
637

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

641
    return print_result_and_return(pass);
24✔
642
}
643

644
bool sirtest_errorsanity(void) {
24✔
645
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
646
    bool pass = si_init;
21✔
647

648
    struct {
649
        uint16_t code;
650
        const char* name;
651
    } errors[] = {
24✔
652
        {SIR_E_NOERROR,   "SIR_E_NOERROR"},   /**< The operation completed successfully (0) */
653
        {SIR_E_NOTREADY,  "SIR_E_NOTREADY"},  /**< libsir has not been initialized (1) */
654
        {SIR_E_ALREADY,   "SIR_E_ALREADY"},   /**< libsir is already initialized (2) */
655
        {SIR_E_DUPITEM,   "SIR_E_DUPITEM"},   /**< Item already managed by libsir (3) */
656
        {SIR_E_NOITEM,    "SIR_E_NOITEM"},    /**< Item not managed by libsir (4) */
657
        {SIR_E_NOROOM,    "SIR_E_NOROOM"},    /**< Maximum number of items already stored (5) */
658
        {SIR_E_OPTIONS,   "SIR_E_OPTIONS"},   /**< Option flags are invalid (6) */
659
        {SIR_E_LEVELS,    "SIR_E_LEVELS"},    /**< Level flags are invalid (7) */
660
        {SIR_E_TEXTSTYLE, "SIR_E_TEXTSTYLE"}, /**< Text style is invalid (8) */
661
        {SIR_E_STRING,    "SIR_E_STRING"},    /**< Invalid string argument (9) */
662
        {SIR_E_NULLPTR,   "SIR_E_NULLPTR"},   /**< NULL pointer argument (10) */
663
        {SIR_E_INVALID,   "SIR_E_INVALID"},   /**< Invalid argument (11) */
664
        {SIR_E_NODEST,    "SIR_E_NODEST"},    /**< No destinations registered for level (12) */
665
        {SIR_E_UNAVAIL,   "SIR_E_UNAVAIL"},   /**< Feature is disabled or unavailable (13) */
666
        {SIR_E_INTERNAL,  "SIR_E_INTERNAL"},  /**< An internal error has occurred (14) */
667
        {SIR_E_COLORMODE, "SIR_E_COLORMODE"}, /**< Invalid color mode (15) */
668
        {SIR_E_TEXTATTR,  "SIR_E_TEXTATTR"},  /**< Invalid text attributes (16) */
669
        {SIR_E_TEXTCOLOR, "SIR_E_TEXTCOLOR"}, /**< Invalid text color (17) */
670
        {SIR_E_PLUGINBAD, "SIR_E_PLUGINBAD"}, /**< Plugin module is malformed (18) */
671
        {SIR_E_PLUGINDAT, "SIR_E_PLUGINDAT"}, /**< Data produced by plugin is invalid (19) */
672
        {SIR_E_PLUGINVER, "SIR_E_PLUGINVER"}, /**< Plugin interface version unsupported (20) */
673
        {SIR_E_PLUGINERR, "SIR_E_PLUGINERR"}, /**< Plugin reported failure (21) */
674
        {SIR_E_PLATFORM,  "SIR_E_PLATFORM"},  /**< Platform error code %d: %s (22) */
675
        {SIR_E_UNKNOWN,   "SIR_E_UNKNOWN"},   /**< Unknown error (4095) */
676
    };
677

678
    char message[SIR_MAXERROR] = {0};
24✔
679
    for (size_t n = 0; n < _sir_countof(errors); n++) {
600✔
680
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
576✔
681
        memset(message, 0, SIR_MAXERROR);
504✔
682
        uint16_t err = sir_geterror(message);
576✔
683
        pass &= errors[n].code == err && *message != '\0';
576✔
684
        printf("\t%s = %s\n", errors[n].name, message);
576✔
685
    }
686

687
    sir_cleanup();
24✔
688
    return print_result_and_return(pass);
24✔
689
}
690

691
bool sirtest_textstylesanity(void) {
24✔
692
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
693
    bool pass = si_init;
21✔
694

695
    printf("\t" WHITEB("--- explicitly invalid ---") "\n");
21✔
696
    pass &= !sir_settextstyle(SIRL_INFO, (sir_textattr)0xbbb, 800, 920);
24✔
697
    pass &= sir_info("I have set an invalid text style.");
24✔
698

699
    pass &= !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK);
24✔
700
    pass &= sir_info("oops, did it again...");
24✔
701

702
    pass &= !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff);
24✔
703
    pass &= sir_info("and again.");
24✔
704
    PRINT_PASS(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
24✔
705

706
    printf("\t" WHITEB("--- unusual but valid ---") "\n");
21✔
707
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT);
24✔
708
    pass &= sir_info("system default fg and bg");
24✔
709
    PRINT_PASS(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
24✔
710

711
    printf("\t" WHITEB("--- override defaults ---") "\n");
21✔
712
    pass &= sir_resettextstyles();
24✔
713

714
    pass &= sir_debug("default style");
24✔
715
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY);
24✔
716
    pass &= sir_debug("override style");
24✔
717

718
    pass &= sir_info("default style");
24✔
719
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA);
24✔
720
    pass &= sir_info("override style");
24✔
721

722
    pass &= sir_notice("default style");
24✔
723
    pass &= sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW);
24✔
724
    pass &= sir_notice("override style");
24✔
725

726
    pass &= sir_warn("default style");
24✔
727
    pass &= sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE);
24✔
728
    pass &= sir_warn("override style");
24✔
729

730
    pass &= sir_error("default style");
24✔
731
    pass &= sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE);
24✔
732
    pass &= sir_error("override style");
24✔
733

734
    pass &= sir_crit("default style");
24✔
735
    pass &= sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN);
24✔
736
    pass &= sir_crit("override style");
24✔
737

738
    pass &= sir_alert("default style");
24✔
739
    pass &= sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT);
24✔
740
    pass &= sir_alert("override style");
24✔
741

742
    pass &= sir_emerg("default style");
24✔
743
    pass &= sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT);
24✔
744
    pass &= sir_emerg("override style");
24✔
745
    PRINT_PASS(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
24✔
746

747
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
21✔
748
    pass &= sir_resettextstyles();
24✔
749

750
    pass &= sir_debug("default style (debug)");
24✔
751
    pass &= sir_info("default style (info)");
24✔
752
    pass &= sir_notice("default style (notice)");
24✔
753
    pass &= sir_warn("default style (warning)");
24✔
754
    pass &= sir_error("default style (error)");
24✔
755
    pass &= sir_crit("default style (crit)");
24✔
756
    pass &= sir_alert("default style (alert)");
24✔
757
    pass &= sir_emerg("default style (emergency)");
24✔
758
    PRINT_PASS(pass, "\t--- reset to defaults: %s ---\n\n", PRN_PASS(pass));
24✔
759

760
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
21✔
761
    pass &= sir_setcolormode(SIRCM_256);
24✔
762

763
    for (sir_textcolor fg = 0, bg = 255; (fg < 256 && bg > 0); fg++, bg--) {
6,144✔
764
        if (fg != bg) {
6,120✔
765
            pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
6,120✔
766
            pass &= sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
6,120✔
767
                fg, bg);
768
        }
769
    }
770

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

773
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
21✔
774
    pass &= sir_setcolormode(SIRCM_RGB);
24✔
775

776
    for (size_t n = 0; n < 256; n++) {
6,168✔
777
        sir_textcolor fg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
778
        sir_textcolor bg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
779
        pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
6,144✔
780
        pass &= sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
6,144✔
781
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
6,144✔
782
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
6,144✔
783
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg));
6,144✔
784
    }
785
    PRINT_PASS(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
24✔
786

787
    printf("\t" WHITEB("--- change mode: invalid mode ---") "\n");
21✔
788
    pass &= !sir_setcolormode(SIRCM_INVALID);
24✔
789
    sir_textcolor fg = sir_makergb(255, 0, 0);
24✔
790
    sir_textcolor bg = sir_makergb(0, 0, 0);
24✔
791
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
24✔
792
    pass &= sir_debug("this is still RGB color mode");
24✔
793
    PRINT_PASS(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
24✔
794

795
    printf("\t" WHITEB("--- change mode: 16-color ---") "\n");
21✔
796
    pass &= sir_setcolormode(SIRCM_16);
24✔
797
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT);
24✔
798
    pass &= sir_debug("this is 16-color mode (fg: %"PRId32", bg: default)",
24✔
799
        SIRTC_BMAGENTA);
800
    PRINT_PASS(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
24✔
801

802
    sir_cleanup();
24✔
803

804
    return print_result_and_return(pass);
24✔
805
}
806

807
#if defined(__clang__) && !defined(__EMBARCADEROC__)
808
/* only Clang has implicit-conversion; GCC BZ#87454 */
809
SANITIZE_SUPPRESS("implicit-conversion")
810
#endif
811
bool sirtest_optionssanity(void) {
24✔
812
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
813
    bool pass = si_init;
21✔
814

815
    static const size_t iterations = 10;
816

817
    /* these should all be valid. */
818
    printf("\t" WHITEB("--- individual valid options ---") "\n");
21✔
819
    pass &= _sir_validopts(SIRO_ALL);
24✔
820
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
21✔
821
    pass &= _sir_validopts(SIRO_NOTIME);
24✔
822
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
21✔
823
    pass &= _sir_validopts(SIRO_NOHOST);
24✔
824
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
21✔
825
    pass &= _sir_validopts(SIRO_NOLEVEL);
24✔
826
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
21✔
827
    pass &= _sir_validopts(SIRO_NONAME);
24✔
828
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
21✔
829
    pass &= _sir_validopts(SIRO_NOPID);
24✔
830
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
21✔
831
    pass &= _sir_validopts(SIRO_NOTID);
24✔
832
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
21✔
833
    pass &= _sir_validopts(SIRO_NOHDR);
24✔
834
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
21✔
835
    pass &= _sir_validopts(SIRO_MSGONLY);
24✔
836
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
21✔
837
    PRINT_PASS(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
24✔
838

839
    /* any combination these bitwise OR'd together
840
       to form a bitmask should also be valid. */
841
    static const sir_option option_arr[SIR_NUMOPTIONS] = {
842
        SIRO_NOTIME,
843
        SIRO_NOHOST,
844
        SIRO_NOLEVEL,
845
        SIRO_NONAME,
846
        SIRO_NOMSEC,
847
        SIRO_NOPID,
848
        SIRO_NOTID,
849
        SIRO_NOHDR
850
    };
851

852
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
21✔
853
    uint32_t last_count = SIR_NUMOPTIONS;
21✔
854
    for (size_t n = 0; n < iterations; n++) {
264✔
855
        sir_options opts    = 0;
210✔
856
        uint32_t rand_count = 0;
210✔
857
        size_t last_idx     = 0;
210✔
858

859
        do {
860
            rand_count = getrand(SIR_NUMOPTIONS);
346✔
861
        } while (rand_count == last_count || rand_count <= 1);
346✔
862

863
        last_count = rand_count;
210✔
864

865
        for (size_t i = 0; i < rand_count; i++) {
1,262✔
866
            size_t rand_idx = 0;
893✔
867
            size_t tries    = 0;
893✔
868

869
            do {
870
                if (++tries > SIR_NUMOPTIONS - 2)
1,716✔
871
                    break;
×
872
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
1,716✔
873
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,716✔
874

875
            last_idx = rand_idx;
893✔
876
            opts |= option_arr[rand_idx];
1,022✔
877
        }
878

879
        pass &= _sir_validopts(opts);
240✔
880
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
240✔
881
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
882
    }
883
    PRINT_PASS(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
24✔
884

885
    printf("\t" WHITEB("--- invalid values ---") "\n");
21✔
886

887
    /* the lowest byte is not valid. */
888
    sir_options invalid = 0x000000ff;
21✔
889
    pass &= !_sir_validopts(invalid);
24✔
890
    printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
21✔
891

892
    /* gaps inbetween valid options. */
893
    invalid = 0x0001ff00U & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
21✔
894
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
895
    pass &= !_sir_validopts(invalid);
24✔
896
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00U: %08"PRIx32) "\n", invalid);
21✔
897

898
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
899
    for (sir_option o = 0x00008f00U; o < SIRO_NOHDR; o += 0x1000U) {
216✔
900
        pass &= !_sir_validopts(o);
192✔
901
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
168✔
902
    }
903

904
    /* greater than SIRO_NOHDR. */
905
    invalid = (0xFFFF0000 & ~SIRO_NOHDR); /* implicit-conversion */
21✔
906
    pass &= !_sir_validopts(invalid);
24✔
907
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
21✔
908

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

911
    sir_cleanup();
24✔
912
    return print_result_and_return(pass);
24✔
913
}
914

915
bool sirtest_levelssanity(void) {
24✔
916
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
917
    bool pass = si_init;
21✔
918

919
    static const size_t iterations = 10;
920

921
    /* these should all be valid. */
922
    printf("\t" WHITEB("--- individual valid levels ---") "\n");
21✔
923
    pass &= _sir_validlevel(SIRL_INFO) && _sir_validlevels(SIRL_INFO);
24✔
924
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_INFO);
21✔
925
    pass &= _sir_validlevel(SIRL_DEBUG) && _sir_validlevels(SIRL_DEBUG);
24✔
926
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_DEBUG);
21✔
927
    pass &= _sir_validlevel(SIRL_NOTICE) && _sir_validlevels(SIRL_NOTICE);
24✔
928
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_NOTICE);
21✔
929
    pass &= _sir_validlevel(SIRL_WARN) && _sir_validlevels(SIRL_WARN);
24✔
930
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_WARN);
21✔
931
    pass &= _sir_validlevel(SIRL_ERROR) && _sir_validlevels(SIRL_ERROR);
24✔
932
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ERROR);
21✔
933
    pass &= _sir_validlevel(SIRL_CRIT) && _sir_validlevels(SIRL_CRIT);
24✔
934
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_CRIT);
21✔
935
    pass &= _sir_validlevel(SIRL_ALERT) && _sir_validlevels(SIRL_ALERT);
24✔
936
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ALERT);
21✔
937
    pass &= _sir_validlevel(SIRL_EMERG) && _sir_validlevels(SIRL_EMERG);
24✔
938
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_EMERG);
21✔
939
    pass &= _sir_validlevels(SIRL_ALL);
24✔
940
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_ALL);
21✔
941
    pass &= _sir_validlevels(SIRL_NONE);
24✔
942
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_NONE);
21✔
943
    PRINT_PASS(pass, "\t--- individual valid levels: %s ---\n\n", PRN_PASS(pass));
24✔
944

945
    /* any combination these bitwise OR'd together
946
       to form a bitmask should also be valid. */
947
    static const sir_levels levels_arr[SIR_NUMLEVELS] = {
948
        SIRL_EMERG,
949
        SIRL_ALERT,
950
        SIRL_CRIT,
951
        SIRL_ERROR,
952
        SIRL_WARN,
953
        SIRL_NOTICE,
954
        SIRL_INFO,
955
        SIRL_DEBUG
956
    };
957

958
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
21✔
959
    uint32_t last_count = SIR_NUMLEVELS;
21✔
960
    for (size_t n = 0; n < iterations; n++) {
264✔
961
        sir_levels levels   = 0U;
210✔
962
        uint32_t rand_count = 0U;
210✔
963
        size_t last_idx     = 0UL;
210✔
964

965
        do {
966
            rand_count = getrand(SIR_NUMLEVELS);
464✔
967
        } while (rand_count == last_count || rand_count <= 1U);
464✔
968

969
        last_count = rand_count;
210✔
970

971
        for (size_t i = 0; i < rand_count; i++) {
1,416✔
972
            size_t rand_idx = 0;
1,029✔
973
            size_t tries    = 0;
1,029✔
974

975
            do {
976
                if (++tries > SIR_NUMLEVELS - 2)
1,918✔
977
                    break;
32✔
978
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,880✔
979
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,880✔
980

981
            last_idx = rand_idx;
1,029✔
982
            levels |= levels_arr[rand_idx];
1,176✔
983
        }
984

985
        pass &= _sir_validlevels(levels);
240✔
986
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
240✔
987
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
988
    }
989
    PRINT_PASS(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
24✔
990

991
    printf("\t" WHITEB("--- invalid values ---") "\n");
21✔
992

993
    /* greater than SIRL_ALL. */
994
    sir_levels invalid = (0xffffu & ~SIRL_ALL);
21✔
995
    pass &= !_sir_validlevels(invalid);
24✔
996
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
21✔
997

998
    /* individual invalid level. */
999
    sir_level invalid2 = 0x1337U;
21✔
1000
    pass &= !_sir_validlevel(invalid2);
24✔
1001
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx16) "\n", invalid2);
21✔
1002

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

1005
    sir_cleanup();
24✔
1006
    return print_result_and_return(pass);
24✔
1007
}
1008

1009
bool sirtest_mutexsanity(void) {
24✔
1010
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1011
    bool pass = si_init;
21✔
1012

1013
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
21✔
1014
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
21✔
1015

1016
    sir_mutex m1 = SIR_MUTEX_INIT;
24✔
1017
    pass &= _sir_mutexcreate(&m1);
24✔
1018

1019
    print_test_error(pass, pass);
24✔
1020

1021
    if (pass) {
24✔
1022
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
21✔
1023
        pass &= _sir_mutexlock(&m1);
24✔
1024

1025
        print_test_error(pass, pass);
24✔
1026

1027
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
21✔
1028
        pass &= _sir_mutexunlock(&m1);
24✔
1029

1030
        print_test_error(pass, pass);
24✔
1031

1032
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
21✔
1033
        pass &= _sir_mutextrylock(&m1);
24✔
1034

1035
        print_test_error(pass, pass);
24✔
1036

1037
        printf(INDENT_ITEM WHITE("unlocking...") "\n");
21✔
1038
        pass &= _sir_mutexunlock(&m1);
24✔
1039

1040
        print_test_error(pass, pass);
24✔
1041

1042
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
21✔
1043
        pass &= _sir_mutexdestroy(&m1);
24✔
1044

1045
        print_test_error(pass, pass);
24✔
1046

1047
    }
1048
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
24✔
1049

1050
    printf("\t" WHITEB("invalid arguments") "\n");
21✔
1051
    printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
21✔
1052
    pass &= !_sir_mutexcreate(NULL);
24✔
1053
    printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
21✔
1054
    pass &= !_sir_mutexlock(NULL);
24✔
1055
    printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
21✔
1056
    pass &= !_sir_mutextrylock(NULL);
24✔
1057
    printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
21✔
1058
    pass &= !_sir_mutexunlock(NULL);
24✔
1059
    printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
21✔
1060
    pass &= !_sir_mutexdestroy(NULL);
24✔
1061
    PRINT_PASS(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
24✔
1062

1063
    pass &= sir_cleanup();
24✔
1064
    return print_result_and_return(pass);
24✔
1065
}
1066

1067
bool sirtest_perf(void) {
1✔
1068
#if !defined(SIR_PERF_PROFILE)
1069
    static const char* logbasename = "libsir-perf";
1070
    static const char* logext      = "";
1071
#endif
1072

1073
#if !defined(DUMA)
1074
# if !defined(SIR_PERF_PROFILE)
1075
#  if !defined(__WIN__)
1076
    static const size_t perflines = 1000000;
1077
#  else
1078
    static const size_t perflines = 100000;
1079
#  endif
1080
# else
1081
    static const size_t perflines = 4000000;
1082
# endif
1083
#else /* DUMA */
1084
    static const size_t perflines = 100000;
1085
#endif
1086

1087
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
1✔
1088
    bool pass = si_init;
1✔
1089

1090
    if (pass) {
1✔
1091
        double stdioelapsed  = 0.0;
1✔
1092
#if !defined(SIR_PERF_PROFILE)
1093
        double printfelapsed = 0.0;
1✔
1094
        double fileelapsed   = 0.0;
1✔
1095

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

1098
        sir_time printftimer = {0};
1✔
1099
        sir_timer_start(&printftimer);
1✔
1100

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

1105
        printfelapsed = sir_timer_elapsed(&printftimer);
1✔
1106
#endif
1107

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

1110
        sir_time stdiotimer = {0};
1✔
1111
        sir_timer_start(&stdiotimer);
1✔
1112

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

1117
        stdioelapsed = sir_timer_elapsed(&stdiotimer);
1✔
1118

1119
        sir_cleanup();
1✔
1120

1121
        INIT(si2, 0, 0, 0, 0);
1✔
1122
        pass &= si2_init;
1✔
1123

1124
#if !defined(SIR_PERF_PROFILE)
1125
        char logfilename[SIR_MAXPATH] = {0};
1✔
1126
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
1✔
1127

1128
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
1✔
1129
        pass &= 0 != logid;
1✔
1130

1131
        if (pass) {
1✔
1132
            printf("\t" BLUE("%zu lines libsir(log file)...") "\n", perflines);
1✔
1133

1134
            sir_time filetimer = {0};
1✔
1135
            sir_timer_start(&filetimer);
1✔
1136

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

1140
            fileelapsed = sir_timer_elapsed(&filetimer);
1✔
1141

1142
            pass &= sir_remfile(logid);
1✔
1143
        }
1144
#endif
1145

1146
        if (pass) {
1✔
1147
#if !defined(SIR_PERF_PROFILE)
1148
            printf("\t" WHITEB("printf: ") CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1✔
1149
                perflines, printfelapsed / 1e3, (double)perflines / (printfelapsed / 1e3));
1✔
1150
#endif
1151
            printf("\t" WHITEB("libsir(stdout): ")
1✔
1152
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n", perflines,
1153
                    stdioelapsed / 1e3, (double)perflines / (stdioelapsed / 1e3));
1✔
1154
#if !defined(SIR_PERF_PROFILE)
1155
            printf("\t" WHITEB("libsir(log file): ")
1✔
1156
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n", perflines,
1157
                    fileelapsed / 1e3, (double)perflines / (fileelapsed / 1e3));
1✔
1158
#endif
1159
            printf("\t" WHITEB("timer resolution: ") CYAN("~%ldnsec") "\n", sir_timer_getres());
1✔
1160
        }
1161
    }
1162

1163
#if !defined(SIR_PERF_PROFILE)
1164
    unsigned deleted = 0U;
1✔
1165
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
1✔
1166

1167
    if (deleted > 0U)
1✔
1168
        printf("\t" DGRAY("deleted %u log file(s)") "\n", deleted);
1✔
1169
#endif
1170

1171
    sir_cleanup();
1✔
1172
    return print_result_and_return(pass);
1✔
1173
}
1174

1175
bool sirtest_updatesanity(void) {
24✔
1176
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
24✔
1177
    bool pass = si_init;
21✔
1178

1179
#define UPDATE_SANITY_ARRSIZE 10
1180

1181
    static const char* logfile = MAKE_LOG_NAME("update-sanity.log");
1182
    static const sir_options opts_array[UPDATE_SANITY_ARRSIZE] = {
1183
        SIRO_NOHOST | SIRO_NOTIME | SIRO_NOLEVEL,
1184
        SIRO_MSGONLY, SIRO_NONAME | SIRO_NOTID,
1185
        SIRO_NOPID | SIRO_NOTIME,
1186
        SIRO_NOTIME | SIRO_NOLEVEL | SIRO_NONAME,
1187
        SIRO_NOTIME, SIRO_NOMSEC | SIRO_NOHOST,
1188
        SIRO_NOPID | SIRO_NOTID,
1189
        SIRO_NOHOST | SIRO_NOTID, SIRO_ALL
1190
    };
1191

1192
    static const sir_levels levels_array[UPDATE_SANITY_ARRSIZE] = {
1193
        SIRL_NONE, SIRL_ALL, SIRL_EMERG, SIRL_ALERT,
1194
        SIRL_CRIT, SIRL_ERROR, SIRL_WARN, SIRL_NOTICE,
1195
        SIRL_INFO, SIRL_DEBUG
1196
    };
1197

1198
    rmfile(logfile);
24✔
1199
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
24✔
1200
    pass &= 0 != id1;
24✔
1201

1202
    for (int i = 0; i < 10; i++) {
225✔
1203
        if (!pass)
205✔
1204
            break;
4✔
1205

1206
        /* reset to defaults*/
1207
        pass &= sir_stdoutlevels(SIRL_DEFAULT);
201✔
1208
        pass &= sir_stderrlevels(SIRL_DEFAULT);
201✔
1209
        pass &= sir_stdoutopts(SIRO_DEFAULT);
201✔
1210
        pass &= sir_stderropts(SIRO_DEFAULT);
201✔
1211

1212
        pass &= sir_debug("default config (debug)");
201✔
1213
        pass &= sir_info("default config (info)");
201✔
1214
        pass &= sir_notice("default config (notice)");
201✔
1215
        pass &= sir_warn("default config (warning)");
201✔
1216
        pass &= sir_error("default config (error)");
201✔
1217
        pass &= sir_crit("default config (critical)");
201✔
1218
        pass &= sir_alert("default config (alert)");
201✔
1219
        pass &= sir_emerg("default config (emergency)");
201✔
1220

1221
        /* pick random options to set/unset */
1222
        uint32_t rnd = getrand(UPDATE_SANITY_ARRSIZE);
201✔
1223
        pass &= sir_stdoutlevels(levels_array[rnd]);
201✔
1224
        pass &= sir_stdoutopts(opts_array[rnd]);
201✔
1225
        printf("\t" WHITE("set random config #%"PRIu32" for stdout") "\n", rnd);
171✔
1226

1227
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
201✔
1228
        pass &= sir_stderrlevels(levels_array[rnd]);
201✔
1229
        pass &= sir_stderropts(opts_array[rnd]);
201✔
1230
        printf("\t" WHITE("set random config #%"PRIu32" for stderr") "\n", rnd);
171✔
1231

1232
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
201✔
1233
        pass &= sir_filelevels(id1, levels_array[rnd]);
201✔
1234
        pass &= sir_fileopts(id1, opts_array[rnd]);
201✔
1235
        printf("\t" WHITE("set random config #%"PRIu32" for %s") "\n", rnd, logfile);
201✔
1236

1237
        pass &= filter_error(sir_debug("modified config #%"PRIu32" (debug)", rnd), SIR_E_NODEST);
201✔
1238
        pass &= filter_error(sir_info("modified config #%"PRIu32" (info)", rnd), SIR_E_NODEST);
201✔
1239
        pass &= filter_error(sir_notice("modified config #%"PRIu32" (notice)", rnd), SIR_E_NODEST);
201✔
1240
        pass &= filter_error(sir_warn("modified config #%"PRIu32" (warning)", rnd), SIR_E_NODEST);
201✔
1241
        pass &= filter_error(sir_error("modified config #%"PRIu32" (error)", rnd), SIR_E_NODEST);
201✔
1242
        pass &= filter_error(sir_crit("modified config #%"PRIu32" (critical)", rnd), SIR_E_NODEST);
201✔
1243
        pass &= filter_error(sir_alert("modified config #%"PRIu32" (alert)", rnd), SIR_E_NODEST);
201✔
1244
        pass &= filter_error(sir_emerg("modified config #%"PRIu32" (emergency)", rnd), SIR_E_NODEST);
201✔
1245
    }
1246

1247
    if (pass) {
24✔
1248
        /* restore to default config and run again */
1249
        sir_stdoutlevels(SIRL_DEFAULT);
20✔
1250
        sir_stderrlevels(SIRL_DEFAULT);
20✔
1251
        sir_stdoutopts(SIRO_DEFAULT);
20✔
1252
        sir_stderropts(SIRO_DEFAULT);
20✔
1253

1254
        pass &= sir_debug("default config (debug)");
20✔
1255
        pass &= sir_info("default config (info)");
20✔
1256
        pass &= sir_notice("default config (notice)");
20✔
1257
        pass &= sir_warn("default config (warning)");
20✔
1258
        pass &= sir_error("default config (error)");
20✔
1259
        pass &= sir_crit("default config (critical)");
20✔
1260
        pass &= sir_alert("default config (alert)");
20✔
1261
        pass &= sir_emerg("default config (emergency)");
20✔
1262
    }
1263

1264
    pass &= sir_remfile(id1);
24✔
1265
    rmfile(logfile);
24✔
1266
    sir_cleanup();
24✔
1267

1268
    return print_result_and_return(pass);
24✔
1269
}
1270

1271
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1272
static
1273
bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
18✔
1274
    static const int runs = 5;
1275

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

1280
# if !defined(__WIN__)
1281
    uint32_t rnd = (uint32_t)(_sir_getpid() + _sir_gettid());
18✔
1282
# else
1283
    uint32_t rnd = (uint32_t)GetTickCount();
1284
# endif
1285

1286
    bool pass = true;
15✔
1287
    for (int i = 1; i <= runs; i++) {
108✔
1288
        /* randomly skip setting process name, identity/category to thoroughly
1289
         * test fallback routines; randomly update the config mid-run. */
1290
        bool set_procname = getrand_bool(rnd ^ 0x5a5a5a5aU);
90✔
1291
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9U);
90✔
1292
        bool set_category = getrand_bool(rnd ^ 0x32323232U);
90✔
1293
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7U);
90✔
1294

1295
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
90✔
1296
            set_procname, set_identity, set_category, do_update);
1297

1298
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
99✔
1299
        si.d_syslog.opts   = SIRO_DEFAULT;
90✔
1300
        si.d_syslog.levels = SIRL_DEFAULT;
90✔
1301

1302
        if (set_identity)
90✔
1303
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
52✔
1304

1305
        if (set_category)
90✔
1306
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
52✔
1307

1308
        si_init = sir_init(&si); //-V519
90✔
1309
        pass &= si_init;
75✔
1310

1311
        if (do_update)
90✔
1312
            pass &= sir_sysloglevels(SIRL_ALL);
36✔
1313

1314
        pass &= sir_debug("%d/%d: this debug message sent to stdout and %s.", i, runs, sl_name);
90✔
1315
        pass &= sir_info("%d/%d: this info message sent to stdout and %s.", i, runs, sl_name);
90✔
1316

1317
        pass &= sir_notice("%d/%d: this notice message sent to stdout and %s.", i, runs, sl_name);
90✔
1318
        pass &= sir_warn("%d/%d: this warning message sent to stdout and %s.", i, runs, sl_name);
90✔
1319
        pass &= sir_error("%d/%d: this error message sent to stdout and %s.", i, runs, sl_name);
90✔
1320

1321
        if (set_identity) {
90✔
1322
            pass &= sir_syslogid("my test ID");
52✔
1323
            pass &= sir_syslogid("my test ID"); /* test deduping. */
52✔
1324
        }
1325

1326
        if (set_category) {
90✔
1327
            pass &= sir_syslogcat("my test category");
52✔
1328
            pass &= sir_syslogcat("my test category"); /* test deduping. */
52✔
1329
        }
1330

1331
        if (do_update)
90✔
1332
            pass &= sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID));
36✔
1333

1334
        pass &= sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name);
90✔
1335
        pass &= sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name);
90✔
1336
        pass &= sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name);
90✔
1337

1338
# if defined(SIR_OS_LOG_ENABLED)
1339
#  if defined(__MACOS__) && !defined(__INTEL_COMPILER)
1340
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1341
            printf("\ttesting os_log activity feature...\n");
1342

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

1348
            /* execution now passes to os_log_parent_activity(), where some logging
1349
            * will occur, then a sub-activity will be created, and more logging. */
1350
            os_activity_apply_f(parent, (void*)parent, os_log_parent_activity);
1351
        }
1352
#  endif
1353
# endif
1354

1355
        sir_cleanup();
90✔
1356

1357
        if (!pass)
90✔
1358
            break;
×
1359
    }
1360

1361
    return print_result_and_return(pass);
18✔
1362
}
1363
#endif
1364

1365
#if defined(SIR_NO_SYSTEM_LOGGERS)
1366
static bool generic_disabled_syslog_test(const char* sl_name, const char* identity,
6✔
1367
    const char* category) {
1368
    INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0U, 0U, "sir_disabled_sltest");
6✔
1369
    si.d_syslog.opts   = SIRO_DEFAULT;
6✔
1370
    si.d_syslog.levels = SIRL_DEFAULT;
6✔
1371
    bool pass = true;
6✔
1372

1373
    SIR_UNUSED(sl_name);
1374

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

1377
    /* init should just ignore the syslog settings. */
1378
    pass &= sir_init(&si);
6✔
1379

1380
    /* these calls should all fail. */
1381
    printf("\tsetting levels...\n");
6✔
1382
    pass &= !sir_sysloglevels(SIRL_ALL);
6✔
1383

1384
    if (pass)
6✔
1385
        print_expected_error();
6✔
1386

1387
    printf("\tsetting options...\n");
6✔
1388
    pass &= !sir_syslogopts(SIRO_DEFAULT);
6✔
1389

1390
    if (pass)
6✔
1391
        print_expected_error();
6✔
1392

1393
    printf("\tsetting identity...\n");
6✔
1394
    pass &= !sir_syslogid(identity);
6✔
1395

1396
    if (pass)
6✔
1397
        print_expected_error();
6✔
1398

1399
    printf("\tsetting category...\n");
6✔
1400
    pass &= !sir_syslogcat(category);
6✔
1401

1402
    if (pass)
6✔
1403
        print_expected_error();
6✔
1404

1405
    pass &= sir_cleanup();
6✔
1406
    return print_result_and_return(pass);
6✔
1407
}
1408
#endif
1409

1410
bool sirtest_syslog(void) {
24✔
1411
#if !defined(SIR_SYSLOG_ENABLED)
1412
# if defined(SIR_NO_SYSTEM_LOGGERS)
1413
    bool pass = generic_disabled_syslog_test("syslog", "sirtests", "tests");
6✔
1414
    return print_result_and_return(pass);
6✔
1415
# else
1416
    printf("\t" DGRAY("SIR_SYSLOG_ENABLED is not defined; skipping") "\n");
1417
    return true;
1418
# endif
1419
#else
1420
    bool pass = generic_syslog_test("syslog", "sirtests", "tests");
18✔
1421
    return print_result_and_return(pass);
18✔
1422
#endif
1423
}
1424

1425
bool sirtest_os_log(void) {
24✔
1426
#if !defined(SIR_OS_LOG_ENABLED)
1427
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
21✔
1428
    return true;
24✔
1429
#else
1430
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1431
    return print_result_and_return(pass);
1432
#endif
1433
}
1434

1435
char *sirtest_get_wineversion(void) {
48✔
1436
#if !defined(__WIN__)
1437
    return NULL;
48✔
1438
#else /* __WIN__ */
1439
    typedef char* (__stdcall *get_wine_ver_proc)(void);
1440
    static get_wine_ver_proc _p_wine_get_version = NULL;
1441

1442
    HMODULE _h_ntdll = GetModuleHandle("ntdll.dll");
1443
    if (_h_ntdll != NULL) {
1444
        _p_wine_get_version = (get_wine_ver_proc)GetProcAddress(_h_ntdll, "wine_get_version");
1445
        if (_p_wine_get_version) {
1446
            char *wine_version = _p_wine_get_version();
1447
            if (wine_version)
1448
                return wine_version;
1449
        }
1450
    }
1451
    return NULL;
1452
#endif
1453
}
1454

1455
bool sirtest_filesystem(void) {
24✔
1456
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1457
    bool pass = si_init;
21✔
1458

1459
    /* Wine version */
1460
    printf("\tRunning under Wine: %s\n",
24✔
1461
            sirtest_get_wineversion() ? sirtest_get_wineversion() : "no"); //-V547
24✔
1462

1463
    /* current working directory. */
1464
    char* cwd = _sir_getcwd();
24✔
1465
    pass &= NULL != cwd;
24✔
1466
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
24✔
1467

1468
    if (NULL != cwd) {
24✔
1469
        /* path to this binary file. */
1470
        char* filename = _sir_getappfilename();
24✔
1471
        pass &= NULL != filename;
24✔
1472
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
24✔
1473

1474
        if (NULL != filename) {
24✔
1475
            /* _sir_get[base|dir]name() can potentially modify filename,
1476
             * so make a copy for each call. */
1477
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
21✔
1478
            pass &= NULL != filename2;
21✔
1479

1480
            if (NULL != filename2) {
21✔
1481
                /* filename, stripped of directory component(s). */
1482
                char* _basename = _sir_getbasename(filename2);
20✔
1483
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
20✔
1484

1485
                if (!_basename) {
20✔
1486
                    pass = false;
×
1487
                } else {
1488
                    /* the last strlen(_basename) chars of filename should match. */
1489
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
20✔
1490
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
20✔
1491
                    size_t n      = 0;
17✔
1492

1493
                    while (n < len) {
180✔
1494
                        if (filename[offset++] != _basename[n++]) {
160✔
1495
                            pass = false;
×
1496
                            break;
×
1497
                        }
1498
                    }
1499
                }
1500
            }
1501

1502
            /* directory this binary file resides in. */
1503
            char* appdir = _sir_getappdir();
21✔
1504
            pass &= NULL != appdir;
21✔
1505
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
21✔
1506

1507
            /* _sir_get[base|dir]name can potentially modify filename,
1508
             * so make a copy for each call. */
1509
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
21✔
1510
            pass &= NULL != filename3;
21✔
1511

1512
            if (NULL != appdir && NULL != filename3) {
21✔
1513
                /* should yield the same result as _sir_getappdir(). */
1514
                char* _dirname = _sir_getdirname(filename3);
20✔
1515
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
20✔
1516

1517
                pass &= 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH));
20✔
1518
                pass &= NULL != _dirname &&
40✔
1519
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH));
20✔
1520
            }
1521

1522
            _sir_safefree(&appdir);
21✔
1523
            _sir_safefree(&filename);
21✔
1524
            _sir_safefree(&filename2);
21✔
1525
            _sir_safefree(&filename3);
21✔
1526
        }
1527

1528
        _sir_safefree(&cwd);
24✔
1529
    }
1530

1531
    /* this next section doesn't really yield any useful boolean pass/fail
1532
     * information, but could be helpful to review manually. */
1533
    char* dubious_dirnames[] = {
24✔
1534
#if !defined(__WIN__)
1535
        "/foo",
1536
        "/foo/",
1537
        "/foo/bar",
1538
        "/foo/bar/bad:filename",
1539
        "/",
1540
        ""
1541
#else /* __WIN__ */
1542
        "C:\\foo",
1543
        "C:\\foo\\",
1544
        "C:\\foo\\bar",
1545
        "C:\\foo\\bar\\bad:>filename",
1546
        "C:\\",
1547
        "//network-share/myfolder",
1548
        ""
1549
#endif
1550
    };
1551

1552
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
168✔
1553
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
144✔
1554
        if (NULL != tmp) {
144✔
1555
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
138✔
1556
                tmp, _sir_getdirname(tmp));
1557
            _sir_safefree(&tmp);
138✔
1558
        }
1559
    }
1560

1561
    char* dubious_filenames[] = {
24✔
1562
#if !defined(__WIN__)
1563
        "foo/bar/file-or-directory",
1564
        "/foo/bar/file-or-directory",
1565
        "/foo/bar/illegal:filename",
1566
        "/",
1567
        ""
1568
#else /* __WIN__ */
1569
        "foo\\bar\\file.with.many.full.stops",
1570
        "C:\\foo\\bar\\poorly-renamed.txt.pdf",
1571
        "C:\\foo\\bar\\illegal>filename.txt",
1572
        "C:\\",
1573
        "\\Program Files\\foo.bar",
1574
        ""
1575
#endif
1576
    };
1577

1578
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
144✔
1579
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
120✔
1580
        if (NULL != tmp) {
120✔
1581
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
115✔
1582
                tmp, _sir_getbasename(tmp));
1583
            _sir_safefree(&tmp);
115✔
1584
        }
1585
    }
1586

1587
    /* absolute/relative paths. */
1588
    static const struct {
1589
        const char* const path;
1590
        bool abs;
1591
    } abs_or_rel_paths[] = {
1592
        {"this/is/relative", false},
1593
        {"relative", false},
1594
        {"./relative", false},
1595
        {"../../relative", false},
1596
#if !defined(__WIN__)
1597
        {"/usr/local/bin", true},
1598
        {"/", true},
1599
        {"/home/foo/.config", true},
1600
        {"~/.config", true}
1601
#else /* __WIN__ */
1602
        {"D:\\absolute", true},
1603
        {"C:\\Program Files\\FooBar", true},
1604
        {"C:\\", true},
1605
        {"\\absolute", true},
1606
#endif
1607
    };
1608

1609
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
216✔
1610
        bool relative = false;
192✔
1611
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
192✔
1612

1613
        if (relative == abs_or_rel_paths[n].abs) {
192✔
1614
            pass = false;
×
1615
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1616
                relative ? "true" : "false");
×
1617
        } else {
1618
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
192✔
1619
                relative ? "true" : "false");
168✔
1620
        }
1621

1622
        pass &= ret;
168✔
1623
        if (!ret) {
192✔
1624
            bool unused = print_test_error(false, false);
×
1625
            SIR_UNUSED(unused);
1626
        }
1627
    }
1628

1629
    /* file existence. */
1630
    static const struct {
1631
        const char* const path;
1632
        bool exists;
1633
    } real_or_not[] = {
1634
        {"../foobarbaz", false},
1635
        {"foobarbaz", false},
1636
#if !defined(__WIN__)
1637
        {"/", true},
1638
# if !defined(__HAIKU__)
1639
        {"/usr/bin", true},
1640
# else
1641
        {"/bin", true},
1642
# endif
1643
        {"/dev", true},
1644
#else /* __WIN__ */
1645
        {"C:\\Windows", true},
1646
        {"C:\\Program Files", true},
1647
        {"\\", true},
1648
        {".\\", true},
1649
        {"..\\", true},
1650
#endif
1651
        {"../../LICENSES/MIT.txt", true},
1652
        {"../../msvs/libsir.sln", true},
1653
        {"./", true},
1654
        {"../", true},
1655
        {"file.exists", true}
1656
    };
1657

1658
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
264✔
1659
        bool exists = false;
240✔
1660
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
240✔
1661

1662
        if (exists != real_or_not[n].exists) {
240✔
1663
            pass = false;
23✔
1664
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
23✔
1665
                exists ? "true" : "false");
23✔
1666
        } else {
1667
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
217✔
1668
                exists ? "true" : "false");
187✔
1669
        }
1670

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

1678
    /* checking file descriptors. */
1679
    static int bad_fds[] = {
1680
        0,
1681
        1,
1682
        2,
1683
        1234
1684
    };
1685
    if (sirtest_get_wineversion()) { //-V547
24✔
1686
        bad_fds[3] = 0;
×
1687
    }
1688

1689
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
120✔
1690
        if (_sir_validfd(bad_fds[n])) {
96✔
1691
            pass = false;
×
1692
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1693
        } else {
1694
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
96✔
1695
        }
1696
    }
1697

1698
    FILE* f = NULL;
24✔
1699
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
24✔
1700
    if (!ret) {
24✔
1701
        pass = false;
4✔
1702
        handle_os_error(true, "fopen(%s) failed!", "file.exists");
4✔
1703
    } else {
1704
        int fd = fileno(f);
20✔
1705
        if (!_sir_validfd(fd)) {
20✔
1706
            pass = false;
×
1707
            printf("\t" RED("_sir_validfd(%d) = false") "\n", fd);
×
1708
        } else {
1709
            printf("\t" GREEN("_sir_validfd(%d) = true") "\n", fd);
17✔
1710
        }
1711
    }
1712

1713
    _sir_safefclose(&f);
24✔
1714

1715
    sir_cleanup();
24✔
1716
    return print_result_and_return(pass);
24✔
1717
}
1718

1719
bool sirtest_squelchspam(void) {
24✔
1720
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1721
    bool pass = si_init;
21✔
1722

1723
    static const size_t alternate   = 50;
1724
    static const size_t sequence[3] = {
1725
        1000, /* non-repeating messages. */
1726
        1000, /* repeating messages. */
1727
        1000  /* alternating repeating and non-repeating messages. */
1728
    };
1729

1730
    sir_time timer;
1731
    sir_timer_start(&timer);
24✔
1732

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

1735
    for (size_t n = 0, ascii_idx = 33; n < sequence[0]; n++, ascii_idx++) {
24,024✔
1736
        pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
45,000✔
1737
            (char)ascii_idx + 1);
24,000✔
1738

1739
        if (ascii_idx == 125)
24,000✔
1740
            ascii_idx = 33;
210✔
1741
    }
1742

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

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

1748
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
24,000✔
1749
            pass &= !ret;
23,904✔
1750
        else
1751
            pass &= ret;
84✔
1752
    }
1753

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

1757
    bool repeating   = false;
21✔
1758
    size_t counter   = 0;
21✔
1759
    size_t repeat_id = 0;
21✔
1760
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
24,024✔
1761
        if (!repeating) {
24,000✔
1762
            pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
12,024✔
1763
                (char)ascii_idx + 1);
12,024✔
1764
        } else {
1765
            bool ret = sir_debug("%zu a repeating message", repeat_id);
11,976✔
1766

1767
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
11,976✔
1768
                pass &= !ret;
11,016✔
1769
            else
1770
                pass &= ret;
840✔
1771
        }
1772

1773
        if (counter == alternate) {
24,000✔
1774
            repeating = !repeating;
456✔
1775
            counter = 0;
399✔
1776
            repeat_id++;
456✔
1777
        }
1778

1779
        if (ascii_idx == 125)
24,000✔
1780
            ascii_idx = 33;
210✔
1781
    }
1782

1783
    sir_cleanup();
24✔
1784
    return print_result_and_return(pass);
24✔
1785
}
1786

1787
bool sirtest_pluginloader(void) {
24✔
1788
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1789
    bool pass = si_init;
21✔
1790

1791
#if !defined(__WIN__)
1792
# define PLUGIN_EXT "so"
1793
#else
1794
# define PLUGIN_EXT "dll"
1795
#endif
1796

1797
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1798
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1799
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1800
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1801
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1802
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1803
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1804
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1805

1806
#if defined(SIR_NO_PLUGINS)
1807
    SIR_UNUSED(plugin2);
1808
    SIR_UNUSED(plugin3);
1809
    SIR_UNUSED(plugin4);
1810
    SIR_UNUSED(plugin5);
1811
    SIR_UNUSED(plugin6);
1812
    SIR_UNUSED(plugin7);
1813
    SIR_UNUSED(plugin8);
1814

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

1817
    printf("\tloading good plugin: '%s'...\n", plugin1);
1✔
1818
    /* load a valid, well-behaved plugin. */
1819
    sirpluginid id = sir_loadplugin(plugin1);
1✔
1820
    pass &= 0 == id;
1✔
1821

1822
    if (pass)
1✔
1823
        print_expected_error();
1✔
1824

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

1829
    if (pass)
1✔
1830
        print_expected_error();
1✔
1831
#else
1832
    /* load a valid, well-behaved plugin. */
1833
    printf("\tloading good plugin: '%s'...\n", plugin1);
23✔
1834
    sirpluginid id = sir_loadplugin(plugin1);
23✔
1835
    pass &= 0 != id;
23✔
1836

1837
    print_test_error(pass, pass);
23✔
1838

1839
    pass &= sir_info("this message will be dispatched to the plugin.");
23✔
1840
    pass &= sir_warn("this message will *not* be dispatched to the plugin.");
23✔
1841

1842
    /* re-loading the same plugin should fail. */
1843
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
23✔
1844
    sirpluginid badid = sir_loadplugin(plugin1);
23✔
1845
    pass &= 0 == badid;
23✔
1846

1847
    print_test_error(pass, pass);
23✔
1848

1849
    /* the following are all invalid or misbehaved, and should all fail. */
1850
    printf("\tloading bad plugin: '%s'...\n", plugin2);
23✔
1851
    badid = sir_loadplugin(plugin2);
23✔
1852
    pass &= 0 == badid;
23✔
1853

1854
    print_test_error(pass, pass);
23✔
1855

1856
    printf("\tloading bad plugin: '%s'...\n", plugin3);
23✔
1857
    badid = sir_loadplugin(plugin3);
23✔
1858
    pass &= 0 == badid;
23✔
1859

1860
    print_test_error(pass, pass);
23✔
1861

1862
    printf("\tloading bad plugin: '%s'...\n", plugin4);
23✔
1863
    badid = sir_loadplugin(plugin4);
23✔
1864
    pass &= 0 == badid;
23✔
1865

1866
    print_test_error(pass, pass);
23✔
1867

1868
    printf("\tloading bad plugin: '%s'...\n", plugin5);
23✔
1869
    badid = sir_loadplugin(plugin5);
23✔
1870
    pass &= 0 == badid;
23✔
1871

1872
    print_test_error(pass, pass);
23✔
1873

1874
    printf("\tloading bad plugin: '%s'...\n", plugin6);
23✔
1875
    badid = sir_loadplugin(plugin6);
23✔
1876
    pass &= 0 == badid;
23✔
1877

1878
    print_test_error(pass, pass);
23✔
1879

1880
    printf("\tloading bad plugin: '%s'...\n", plugin7);
23✔
1881
    badid = sir_loadplugin(plugin7);
23✔
1882
    pass &= 0 != badid; /* this one should load, just return false from write */
23✔
1883

1884
    pass &= !sir_info("this should fail, because one plugin failed to process"
23✔
1885
                      " the message.");
1886

1887
    print_test_error(pass, pass);
23✔
1888

1889
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
23✔
1890
    badid = sir_loadplugin(plugin8);
23✔
1891
    pass &= 0 == badid;
23✔
1892

1893
    print_test_error(pass, pass);
23✔
1894

1895
    /* unload the good plugin manually. */
1896
    printf("\tunloading good plugin: '%s'...\n", plugin1);
23✔
1897
    pass &= sir_unloadplugin(id);
23✔
1898

1899
    print_test_error(pass, pass);
23✔
1900

1901
    /* try to unload the plugin again. */
1902
    printf("\nunloading already unloaded plugin '%s'...\n", plugin1);
23✔
1903
    pass &= !sir_unloadplugin(id);
23✔
1904

1905
    print_test_error(pass, pass);
23✔
1906

1907
    /* test bad paths. */
1908
    printf("\ntrying to load plugin with NULL path...\n");
20✔
1909
    badid = sir_loadplugin(NULL);
23✔
1910
    pass &= 0 == badid;
23✔
1911

1912
    print_test_error(pass, pass);
23✔
1913
#endif
1914
    pass &= sir_cleanup();
24✔
1915
    return print_result_and_return(pass);
24✔
1916
}
1917

1918
bool sirtest_getversioninfo(void) {
24✔
1919
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1920
    bool pass = si_init;
21✔
1921

1922
    printf("\tchecking version retrieval functions...\n");
21✔
1923

1924
    const char* str = sir_getversionstring();
24✔
1925
    pass &= _sir_validstrnofail(str);
24✔
1926

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

1929
    uint32_t hex = sir_getversionhex();
24✔
1930
    pass &= 0 != hex;
24✔
1931

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

1934
    bool prerel = sir_isprerelease();
24✔
1935
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
24✔
1936

1937
    pass &= sir_cleanup();
24✔
1938
    return print_result_and_return(pass);
24✔
1939
}
1940

1941
enum {
1942
    NUM_THREADS = 4
1943
};
1944

1945
static bool threadpool_pseudojob(void* arg) {
173✔
1946
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
173✔
1947
#if !defined(__WIN__)
1948
    sleep(1);
173✔
1949
#else
1950
    Sleep(1000);
1951
#endif
1952
    return true;
165✔
1953
}
1954

1955
bool sirtest_threadpool(void) {
24✔
1956
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
24✔
1957
    bool pass = si_init;
21✔
1958

1959
    static const size_t num_jobs = 30;
1960
    sir_threadpool* pool         = NULL;
24✔
1961

1962
    pass &= _sir_threadpool_create(&pool, NUM_THREADS);
24✔
1963
    if (pass) {
24✔
1964
        /* dispatch a whole bunch of jobs. */
1965
        for (size_t n = 0; n < num_jobs; n++) {
682✔
1966
            sir_threadpool_job* job = calloc(1, sizeof(sir_threadpool_job));
660✔
1967
            pass &= NULL != job;
660✔
1968
            if (job) {
660✔
1969
                job->fn = &threadpool_pseudojob;
660✔
1970
                job->data = (void*)(n + 1);
660✔
1971
                pass &= _sir_threadpool_add_job(pool, job);
660✔
1972
                pass &= sir_info("dispatched job (fn: %"PRIxPTR", data: %p)",
660✔
1973
                    (uintptr_t)job->fn, job->data);
660✔
1974
            }
1975
        }
1976

1977
#if !defined(__WIN__)
1978
        sleep(1);
22✔
1979
#else
1980
        Sleep(1000);
1981
#endif
1982

1983
        pass &= sir_info("destroying thread pool...");
22✔
1984
        pass &= _sir_threadpool_destroy(&pool);
22✔
1985
    }
1986

1987
    pass &= sir_cleanup();
24✔
1988
    return print_result_and_return(pass);
24✔
1989
}
1990

1991
#if !defined(__WIN__)
1992
static void* threadrace_thread(void* arg);
1993
#else /* __WIN__ */
1994
static unsigned __stdcall threadrace_thread(void* arg);
1995
#endif
1996

1997
bool sirtest_threadrace(void) {
24✔
1998
#if !defined(__WIN__)
1999
    pthread_t thrds[NUM_THREADS] = {0};
24✔
2000
#else /* __WIN__ */
2001
    uintptr_t thrds[NUM_THREADS] = {0};
2002
#endif
2003

2004
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
24✔
2005
    bool pass           = si_init;
21✔
2006
    bool any_created    = false;
21✔
2007
    size_t last_created = 0;
21✔
2008

2009
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
24✔
2010
    pass &= NULL != heap_args;
24✔
2011
    if (!heap_args) {
24✔
2012
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2013
        return false;
1✔
2014
    }
2015

2016
    for (size_t n = 0; n < NUM_THREADS; n++) {
111✔
2017
        if (!pass)
89✔
2018
            break;
×
2019

2020
        heap_args[n].pass = true;
89✔
2021
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
89✔
2022
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2023

2024
#if !defined(__WIN__)
2025
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
89✔
2026
        if (0 != create) {
89✔
2027
            errno = create;
1✔
2028
            handle_os_error(true, "pthread_create() for thread #%zu failed!", n + 1);
1✔
2029
#else /* __WIN__ */
2030
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2031
        if (0 == thrds[n]) {
2032
            handle_os_error(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2033
#endif
2034
            pass = false;
1✔
2035
            break;
1✔
2036
        }
2037

2038
        last_created = n;
76✔
2039
        any_created  = true;
76✔
2040
    }
2041

2042
    if (any_created) {
23✔
2043
        for (size_t j = 0; j < last_created + 1; j++) {
110✔
2044
            bool joined = true;
76✔
2045
            printf("\twaiting for thread %zu/%zu...\n", j + 1, last_created + 1);
88✔
2046
#if !defined(__WIN__)
2047
            int join = pthread_join(thrds[j], NULL);
88✔
2048
            if (0 != join) {
88✔
2049
                joined = false;
4✔
2050
                errno  = join;
4✔
2051
                handle_os_error(true, "pthread_join() for thread #%zu failed!", j + 1);
4✔
2052
            }
2053
#else /* __WIN__ */
2054
            DWORD wait = WaitForSingleObject((HANDLE)thrds[j], INFINITE);
2055
            if (WAIT_OBJECT_0 != wait) {
2056
                joined = false;
2057
                handle_os_error(false, "WaitForSingleObject() for thread #%zu (%p) failed!", j + 1,
2058
                    (HANDLE)thrds[j]);
2059
            }
2060
#endif
2061
            pass &= joined;
76✔
2062
            if (joined) {
76✔
2063
                printf("\tthread %zu/%zu joined\n", j + 1, last_created + 1);
72✔
2064

2065
                pass &= heap_args[j].pass;
84✔
2066
                if (heap_args[j].pass)
84✔
2067
                    printf("\t" GREEN("thread #%zu returned pass = true") "\n", j + 1);
72✔
2068
                else
2069
                    printf("\t" RED("thread #%zu returned pass = false!") "\n", j + 1);
×
2070
            }
2071
        }
2072
    }
2073

2074
    _sir_safefree(&heap_args);
23✔
2075

2076
    sir_cleanup();
23✔
2077
    return print_result_and_return(pass);
23✔
2078
}
2079

2080
#if !defined(__WIN__)
2081
static void* threadrace_thread(void* arg) {
88✔
2082
#else /* __WIN__ */
2083
unsigned __stdcall threadrace_thread(void* arg) {
2084
#endif
2085
    pid_t threadid       = _sir_gettid();
88✔
2086
    thread_args* my_args = (thread_args*)arg;
76✔
2087

2088
    rmfile(my_args->log_file);
88✔
2089
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
88✔
2090

2091
    if (0 == id) {
88✔
2092
        bool unused = print_test_error(false, false);
11✔
2093
        SIR_UNUSED(unused);
2094
#if !defined(__WIN__)
2095
        return NULL;
11✔
2096
#else /* __WIN__ */
2097
        return 0;
2098
#endif
2099
    }
2100

2101
    printf("\thi, i'm thread (id: %d), logging to: '%s'...\n",
65✔
2102
            PID_CAST threadid, my_args->log_file);
65✔
2103

2104
#if !defined(DUMA)
2105
# define NUM_ITERATIONS 1000
2106
#else
2107
# define NUM_ITERATIONS 100
2108
#endif
2109

2110
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
76,076✔
2111
        /* choose a random level, and colors. */
2112
        sir_textcolor fg = SIRTC_DEFAULT;
64,001✔
2113
        sir_textcolor bg = SIRTC_DEFAULT;
64,001✔
2114

2115
        if (n % 2 == 0) {
76,000✔
2116
            fg = SIRTC_CYAN;
32,001✔
2117
            bg = SIRTC_BLACK;
32,001✔
2118
            sir_debug("log message #%zu", n);
38,001✔
2119
        } else {
2120
            fg = SIRTC_BLACK;
32,000✔
2121
            bg = SIRTC_CYAN;
32,000✔
2122
            sir_info("log message #%zu", n);
37,999✔
2123
        }
2124

2125
        /* sometimes remove and re-add the log file, and set some options/styles.
2126
         * other times, just set different options/styles. */
2127
        uint32_t rnd = (uint32_t)(n + threadid);
76,001✔
2128
        if (getrand_bool((uint32_t)(rnd > 1 ? rnd : 1))) {
76,001✔
2129
            my_args->pass = print_test_error(sir_remfile(id), false);
38,252✔
2130
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
38,252✔
2131
                SIRL_ALL, SIRO_MSGONLY), false);
2132

2133
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
76,503✔
2134
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
38,252✔
2135
            my_args->pass = print_test_error(test, false);
38,252✔
2136

2137
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
38,252✔
2138
            my_args->pass = print_test_error(test, false);
38,252✔
2139
        } else {
2140
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
75,498✔
2141
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
37,749✔
2142
            my_args->pass = print_test_error(test, false);
37,749✔
2143

2144
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
37,749✔
2145
            my_args->pass = print_test_error(test, false);
37,746✔
2146

2147
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
37,747✔
2148
            my_args->pass = print_test_error(test, false);
37,749✔
2149
        }
2150

2151
        if (!my_args->pass)
76,000✔
2152
            break;
1✔
2153
    }
2154

2155
    my_args->pass = print_test_error(sir_remfile(id), false);
77✔
2156

2157
    rmfile(my_args->log_file);
77✔
2158

2159
#if !defined(__WIN__)
2160
    return NULL;
77✔
2161
#else /* __WIN__ */
2162
    return 0U;
2163
#endif
2164
}
2165

2166
/*
2167
bool sirtest_XXX(void) {
2168
    INIT(si, SIRL_ALL, 0U, 0U, 0U);
2169
    bool pass = si_init;
2170

2171
    pass &= sir_cleanup();
2172
    return print_result_and_return(pass);
2173
}
2174
*/
2175

2176
/* ========================== end tests ========================== */
2177

2178
bool print_test_error(bool result, bool expected) {
267,867✔
2179
    char message[SIR_MAXERROR] = {0};
267,867✔
2180
    uint16_t code              = sir_geterror(message);
267,867✔
2181

2182
    if (!expected && !result && SIR_E_NOERROR != code)
267,853✔
2183
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
65✔
2184
    else if (expected && SIR_E_NOERROR != code)
267,788✔
2185
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
534✔
2186

2187
    return result;
267,855✔
2188
}
2189

2190
bool print_os_error(void) {
62✔
2191
    char message[SIR_MAXERROR] = {0};
62✔
2192
    uint16_t code              = sir_geterror(message);
62✔
2193
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
62✔
2194
    return false;
62✔
2195
}
2196

2197
bool filter_error(bool pass, uint16_t err) {
1,608✔
2198
    if (!pass) {
1,608✔
2199
        char message[SIR_MAXERROR] = {0};
987✔
2200
        uint16_t code              = sir_geterror(message);
987✔
2201
        if (code != err)
987✔
2202
            return false;
2✔
2203
    }
2204
    return true;
1,366✔
2205
}
2206

2207
uint32_t getrand(uint32_t upper_bound) {
119,584✔
2208
#if !defined(__WIN__) || defined(__EMBARCADEROC__)
2209
# if defined(__MACOS__) || defined(__BSD__)
2210
    if (upper_bound < 2U)
2211
        upper_bound = 2U;
2212
    return arc4random_uniform(upper_bound);
2213
# else
2214
#  if defined(__EMBARCADEROC__)
2215
    return (uint32_t)(random(upper_bound));
2216
#  else
2217
    return (uint32_t)(random() % upper_bound);
119,584✔
2218
#  endif
2219
# endif
2220
#else /* __WIN__ */
2221
    uint32_t ctx = 0;
2222
    if (0 != rand_s(&ctx))
2223
        ctx = (uint32_t)rand();
2224
    return ctx % upper_bound;
2225
#endif
2226
}
2227

2228
bool rmfile(const char* filename) {
1,160✔
2229
    bool removed = false;
1,013✔
2230

2231
    /* return true if leave_logs is true. */
2232
    if (leave_logs) {
1,160✔
2233
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
49✔
2234
            filename, _cl_arg_list[3].flag);
49✔
2235
        return true;
49✔
2236
    }
2237

2238
    /* return true if the file doesn't exist. */
2239
    struct stat st;
2240
    if (0 != stat(filename, &st)) {
1,111✔
2241
        if (ENOENT == errno)
589✔
2242
            return true;
477✔
2243

2244
        handle_os_error(true, "failed to stat %s!", filename);
46✔
2245
        return false;
46✔
2246
    }
2247

2248
    if (!_sir_deletefile(filename)) {
522✔
2249
        handle_os_error(false, "failed to delete %s!", filename);
×
2250
    } else {
2251
        printf("\t" DGRAY("deleted %s (%ld bytes)...") "\n", filename,
441✔
2252
            (long)st.st_size);
522✔
2253
    }
2254

2255
    return removed;
441✔
2256
}
2257

2258
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
247✔
2259
    if (strstr(filename, search)) {
247✔
2260
        char filepath[SIR_MAXPATH];
2261
        _sir_snprintf_trunc(filepath, SIR_MAXPATH, "%s%s", path, filename);
59✔
2262

2263
        rmfile(filepath);
59✔
2264
        (*data)++;
59✔
2265
    }
2266
}
247✔
2267

2268
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
123✔
2269
    SIR_UNUSED(path);
2270
    if (strstr(filename, search))
123✔
2271
        (*data)++;
36✔
2272
}
123✔
2273

2274
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
66✔
2275
#if !defined(__WIN__)
2276
    DIR* d = opendir(path);
66✔
2277
    if (!d)
66✔
2278
        return print_os_error();
2✔
2279

2280
    rewinddir(d);
64✔
2281
    struct dirent* di = readdir(d);
64✔
2282
    if (!di) {
64✔
2283
        closedir(d);
×
2284
        return print_os_error();
×
2285
    }
2286

2287
    while (NULL != di) {
434✔
2288
        cb(search, path, di->d_name, data);
370✔
2289
        di = readdir(d);
370✔
2290
    }
2291

2292
    closedir(d);
64✔
2293
    d = NULL;
55✔
2294
#else /* __WIN__ */
2295
    WIN32_FIND_DATA finddata = {0};
2296
    char buf[SIR_MAXPATH]    = {0};
2297

2298
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2299

2300
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2301

2302
    if (INVALID_HANDLE_VALUE == enumerator)
2303
        return false;
2304

2305
    do {
2306
        cb(search, path, finddata.cFileName, data);
2307
    } while (FindNextFile(enumerator, &finddata) > 0);
2308

2309
    FindClose(enumerator);
2310
    enumerator = NULL;
2311
#endif
2312

2313
    return true;
64✔
2314
}
2315

2316
double sir_timer_elapsed(const sir_time* timer) {
2,000,029✔
2317
    sir_time now;
2318
    return _sir_msec_since(timer, &now);
2,000,029✔
2319
}
2320

2321
long sir_timer_getres(void) {
1✔
2322
    long retval = 0L;
1✔
2323
#if !defined(__WIN__)
2324
    struct timespec res;
2325
    if (0 == clock_getres(SIR_INTERVALCLOCK, &res)) {
1✔
2326
        retval = res.tv_nsec;
1✔
2327
    } else {
2328
        handle_os_error(true, "clock_getres(%d) failed!", CLOCK_CAST SIR_INTERVALCLOCK); // GCOVR_EXCL_LINE
2329
    }
2330
#else /* __WIN__ */
2331
    LARGE_INTEGER cntr_freq;
2332
    (void)QueryPerformanceFrequency(&cntr_freq);
2333
    if (cntr_freq.QuadPart <= 0)
2334
        retval = 0L;
2335
    else
2336
        retval = (long)(ceil(((double)cntr_freq.QuadPart) / 1e9));
2337
#endif
2338
    return retval;
1✔
2339
}
2340

2341
void sir_sleep_msec(uint32_t msec) {
×
2342
    if (0U == msec)
×
2343
        return;
×
2344

2345
#if !defined(__WIN__)
2346
    struct timespec ts = { msec / 1000, (msec % 1000) * 1000000 };
×
2347
    (void)nanosleep(&ts, NULL);
×
2348
#else /* __WIN__ */
2349
    (void)SleepEx((DWORD)msec, TRUE);
2350
#endif
2351
}
2352

2353
#if defined(SIR_OS_LOG_ENABLED)
2354
void os_log_parent_activity(void* ctx) {
2355
    sir_debug("confirming with ground control that we are a go...");
2356
    sir_info("all systems go; initiating launch sequence");
2357
    sir_warn("getting some excessive vibration here");
2358
    sir_info("safely reached escape velocity. catch you on the flip side");
2359
    sir_info("(3 days later) we have landed on the lunar surface");
2360
    sir_notice("beginning rock counting...");
2361

2362
    os_activity_t parent = (os_activity_t)ctx;
2363
    os_activity_t child = os_activity_create("counting moon rocks", parent, // -V530
2364
        OS_ACTIVITY_FLAG_DEFAULT);
2365

2366
    float rock_count = 0.0f;
2367
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2368
    sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2369
        (double)rock_count);
2370
}
2371

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

2375
    for (size_t n = 0; n < 10; n++) {
2376
        sir_info("counting rocks in sector %zu...", n);
2377
    }
2378

2379
    float* rock_count = (float*)ctx;
2380
    *rock_count = 1e12f;
2381
    sir_info("all sectors counted; heading back to the lunar lander");
2382
}
2383
#endif
2384

2385
bool mark_test_to_run(const char* name) {
3✔
2386
    bool found = false;
3✔
2387
    for (size_t t = 0; t < _sir_countof(sir_tests); t++) {
46✔
2388
        if (_sir_strsame(name, sir_tests[t].name,
45✔
2389
            strnlen(sir_tests[t].name, SIR_MAXTESTNAME))) {
2390
            found = sir_tests[t].run = true;
2✔
2391
            break;
2✔
2392
        }
2393
    }
2394

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

2398
    return found;
3✔
2399
}
2400

2401
void print_usage_info(void) {
4✔
2402
    size_t longest = 0;
4✔
2403
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2404
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2405
        if (len > longest)
28✔
2406
            longest = len;
8✔
2407
    }
2408

2409
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2410

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

2414
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2415
        if (len < longest)
28✔
2416
            for (size_t n = len; n < longest; n++)
156✔
2417
                fprintf(stderr, " ");
132✔
2418

2419
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2420
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2421
            _cl_arg_list[i].desc);
28✔
2422
    }
2423

2424
    fprintf(stderr, "\n");
4✔
2425
}
4✔
2426

2427
void print_test_list(void) {
1✔
2428
    size_t longest = 0;
1✔
2429
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
32✔
2430
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2431
        if (len > longest)
31✔
2432
            longest = len;
3✔
2433
    }
2434

2435
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2436

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

2440
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2441
        if (len < longest)
31✔
2442
            for (size_t n = len; n < longest; n++)
270✔
2443
                printf(" ");
240✔
2444

2445
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
31✔
2446
            printf("\n");
16✔
2447
    }
2448

2449
    printf("\n");
1✔
2450
}
1✔
2451

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