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

aremmell / libsir / 266

29 Aug 2023 12:36PM UTC coverage: 94.955% (-0.06%) from 95.017%
266

Pull #241

gitlab-ci

web-flow
Merge branch 'master' into maybe-enums
Pull Request #241: Change sir_level and sir_option to defines, rather than enum

86 of 86 new or added lines in 9 files covered. (100.0%)

3030 of 3191 relevant lines covered (94.95%)

603446.22 hits per line

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

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

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

63
static bool leave_logs = false;
64

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

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

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

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

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

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

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

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

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

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

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

186
        sir_tests[n].pass = sir_tests[n].fn();
692✔
187
        if (sir_tests[n].pass)
692✔
188
            passed++;
648✔
189

190
        ran++;
602✔
191

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

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

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

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

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

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

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

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

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

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

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

239
bool sirtest_failnooutputdest(void) {
23✔
240
    INIT(si, 0, 0, 0, 0);
23✔
241
    bool pass = si_init;
20✔
242

243
    static const char* logfilename = MAKE_LOG_NAME("nodestination.log");
244

245
    pass &= !sir_notice("this goes nowhere!");
23✔
246

247
    if (pass) {
23✔
248
        print_expected_error();
23✔
249

250
        pass &= sir_stdoutlevels(SIRL_INFO);
23✔
251
        pass &= sir_info("this goes to stdout");
23✔
252
        pass &= sir_stdoutlevels(SIRL_NONE);
23✔
253

254
        sirfileid fid = sir_addfile(logfilename, SIRL_INFO, SIRO_DEFAULT);
23✔
255
        pass &= 0u != fid;
23✔
256
        pass &= sir_info("this goes to %s", logfilename);
23✔
257
        pass &= sir_filelevels(fid, SIRL_NONE);
23✔
258
        pass &= !sir_notice("this goes nowhere!");
23✔
259

260
        if (0u != fid)
23✔
261
            pass &= sir_remfile(fid);
20✔
262

263
        rmfile(logfilename);
23✔
264
    }
265

266
    sir_cleanup();
23✔
267
    return print_result_and_return(pass);
23✔
268
}
269

270
bool sirtest_failnulls(void) {
23✔
271
    INIT_BASE(si, SIRL_ALL, 0, 0, 0, "", false);
23✔
272
    bool pass = true;
20✔
273

274
    pass &= !sir_init(NULL);
23✔
275

276
    if (pass)
23✔
277
        print_expected_error();
23✔
278

279
    pass &= sir_init(&si);
23✔
280
    pass &= !sir_info(NULL); //-V575 //-V618
23✔
281

282
    if (pass)
23✔
283
        print_expected_error();
23✔
284

285
    pass &= 0u == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY);
23✔
286

287
    if (pass)
23✔
288
        print_expected_error();
23✔
289

290
    pass &= !sir_remfile(0u);
23✔
291

292
    if (pass)
23✔
293
        print_expected_error();
23✔
294

295
    sir_cleanup();
23✔
296
    return print_result_and_return(pass);
23✔
297
}
298

299
bool sirtest_failemptymessage(void) {
23✔
300
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
301
    bool pass = si_init;
20✔
302

303
    pass &= !sir_debug("%s", "");
23✔
304

305
    sir_cleanup();
23✔
306
    return print_result_and_return(pass);
23✔
307
}
308

309
bool sirtest_filecachesanity(void) {
23✔
310
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
311
    bool pass = si_init;
20✔
312

313
    size_t numfiles             = SIR_MAXFILES + 1;
20✔
314
    sirfileid ids[SIR_MAXFILES] = {0};
23✔
315

316
    sir_options even = SIRO_MSGONLY;
20✔
317
    sir_options odd  = SIRO_ALL;
20✔
318

319
    for (size_t n = 0; n < numfiles - 1; n++) {
391✔
320
        char path[SIR_MAXPATH] = {0};
368✔
321
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), n);
320✔
322
        rmfile(path);
368✔
323
        ids[n] = sir_addfile(path, SIRL_ALL, (n % 2) ? odd : even);
392✔
324
        pass &= 0u != ids[n] && sir_info("test %zu", n);
368✔
325
    }
326

327
    pass &= sir_info("test test test");
23✔
328

329
    /* this one should fail; max files already added. */
330
    pass &= 0u == sir_addfile(MAKE_LOG_NAME("should-fail.log"), SIRL_ALL, SIRO_MSGONLY);
23✔
331

332
    if (pass)
23✔
333
        print_expected_error();
19✔
334

335
    sir_info("test test test");
23✔
336

337
    /* now remove previously added files in a different order. */
338
    size_t removeorder[SIR_MAXFILES];
339
    memset(removeorder, -1, sizeof(removeorder));
20✔
340

341
    long processed = 0l;
20✔
342
    printf("\tcreating random file ID order...\n");
20✔
343

344
    do {
1,087✔
345
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,269✔
346
        bool skip  = false;
1,107✔
347

348
        for (size_t n = 0ul; n < SIR_MAXFILES; n++)
12,193✔
349
            if (removeorder[n] == rnd) {
11,825✔
350
                skip = true;
787✔
351
                break;
787✔
352
            }
353

354
        if (skip)
1,269✔
355
            continue;
901✔
356

357
        removeorder[processed++] = rnd;
368✔
358

359
        if (processed == SIR_MAXFILES)
368✔
360
            break;
20✔
361
    } while (true);
362

363
    printf("\tremove order: {");
20✔
364
    for (size_t n = 0; n < SIR_MAXFILES; n++)
391✔
365
        printf(" %zu%s", removeorder[n], (n < SIR_MAXFILES - 1) ? "," : "");
371✔
366
    printf(" }...\n");
20✔
367

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

371
        char path[SIR_MAXPATH] = {0};
368✔
372
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), removeorder[n]);
368✔
373
        rmfile(path);
368✔
374
    }
375

376
    pass &= sir_info("test test test");
23✔
377

378
    sir_cleanup();
23✔
379
    return print_result_and_return(pass);
23✔
380
}
381

382
bool sirtest_failinvalidfilename(void) {
23✔
383
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
384
    bool pass = si_init;
20✔
385

386
    pass &= 0u == sir_addfile("bad file!/name", SIRL_ALL, SIRO_MSGONLY);
23✔
387

388
    if (pass)
23✔
389
        print_expected_error();
23✔
390

391
    sir_cleanup();
23✔
392
    return print_result_and_return(pass);
23✔
393
}
394

395
bool sirtest_failfilebadpermission(void) {
23✔
396
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
397
    bool pass = si_init;
20✔
398

399
#if !defined(__WIN__)
400
    static const char* path = "/noperms";
401
#else /* __WIN__ */
402
# if defined(__CYGWIN__)
403
    static const char* path = "/cygdrive/c/Windows/System32/noperms";
404
# else
405
    static const char* path;
406
    if (sirtest_get_wineversion()) {
407
        path = "Z:\\noperms";
408
    } else {
409
        path = "C:\\Windows\\System32\\noperms";
410
    }
411
# endif
412
#endif
413

414
    pass &= 0u == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY);
23✔
415

416
    if (pass)
23✔
417
        print_expected_error();
23✔
418

419
    sir_cleanup();
23✔
420
    return print_result_and_return(pass);
23✔
421
}
422

423
bool sirtest_faildupefile(void) {
23✔
424
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
425
    bool pass = si_init;
20✔
426

427
#if !defined(__WIN__)
428
    static const char* filename1 = "./logs/faildupefile.log";
429
    static const char* filename2 = "logs/faildupefile.log";
430
#else
431
    static const char* filename1 = "logs\\faildupefile.log";
432
    static const char* filename2 = "logs/faildupefile.log";
433
#endif
434

435
    static const char* filename3 = "logs/not-a-dupe.log";
436
    static const char* filename4 = "logs/also-not-a-dupe.log";
437

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

440
    /* should be fine; no other files added yet. */
441
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
23✔
442
    pass &= 0u != fid;
23✔
443

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

446
    /* should fail. this is the same file we already added. */
447
    pass &= 0u == sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
23✔
448

449
    if (pass)
23✔
450
        print_expected_error();
20✔
451

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

454
    /* should also fail. this is the same file we already added, even
455
     * if the path strings don't match. */
456
    pass &= 0u == sir_addfile(filename2, SIRL_ALL, SIRO_DEFAULT);
23✔
457

458
    if (pass)
23✔
459
        print_expected_error();
20✔
460

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

463
    /* should pass. this is a different file. */
464
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
23✔
465
    pass &= 0u != fid2;
23✔
466

467
    /* should also pass. */
468
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
23✔
469
    pass &= 0u != fid3;
23✔
470

471
    pass &= sir_info("hello three valid files");
23✔
472

473
    /* should now fail since we added it earlier. */
474
    pass &= 0u == sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
23✔
475

476
    if (pass)
23✔
477
        print_expected_error();
19✔
478

479
    /* don't remove all of the log files in order to also test
480
     * cache tear-down. */
481
    pass &= sir_remfile(fid);
23✔
482

483
    rmfile(filename1);
23✔
484
    rmfile(filename2);
23✔
485
    rmfile(filename3);
23✔
486
    rmfile(filename4);
23✔
487

488
    pass &= sir_cleanup();
23✔
489
    return print_result_and_return(pass);
23✔
490
}
491

492
bool sirtest_failremovebadfile(void) {
23✔
493
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
494
    bool pass = si_init;
20✔
495

496
    sirfileid invalidid = 9999999;
20✔
497
    pass &= !sir_remfile(invalidid);
23✔
498

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

502
    sir_cleanup();
23✔
503
    return print_result_and_return(pass);
23✔
504
}
505

506
bool sirtest_rollandarchivefile(void) {
24✔
507
    /* roll size minus 1KiB so we can write until it maxes. */
508
    static const long deltasize    = 1024L;
509
    const long fillsize            = SIR_FROLLSIZE - deltasize;
21✔
510
    static const char* logbasename = "rollandarchive";
511
    static const char* logext      = ".log";
512
    static const char* line        = "hello, i am some data. nice to meet you.";
513

514
    char logfilename[SIR_MAXPATH] = {0};
24✔
515
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
24✔
516

517
    unsigned delcount = 0u;
24✔
518
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
24✔
519
        handle_os_error(false, "failed to enumerate log files with base name: %s!",
2✔
520
            logbasename);
521
        return false;
2✔
522
    }
523

524
    if (delcount > 0u)
22✔
525
        printf("\tfound and removed %u log file(s)\n", delcount);
×
526

527
    FILE* f = NULL;
22✔
528
    _sir_fopen(&f, logfilename, "w");
22✔
529

530
    if (!f)
22✔
531
        return print_os_error();
×
532

533
    if (0 != fseek(f, fillsize, SEEK_SET)) {
22✔
534
        handle_os_error(true, "fseek in file %s failed!", logfilename);
1✔
535
        fclose(f);
1✔
536
        return false;
1✔
537
    }
538

539
    if (EOF == fputc('\0', f)) {
21✔
540
        handle_os_error(true, "fputc in file %s failed!", logfilename);
1✔
541
        fclose(f);
1✔
542
        return false;
1✔
543
    }
544

545
    fclose(f);
20✔
546

547
    INIT(si, 0, 0, 0, 0);
20✔
548
    bool pass = si_init;
17✔
549

550
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
20✔
551
    pass &= 0u != fileid;
20✔
552

553
    if (pass) {
20✔
554
        /* write an (approximately) known quantity until we should have rolled */
555
        size_t written  = 0;
15✔
556
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
18✔
557

558
        do {
559
            pass &= sir_debug("%zu %s", written, line);
1,368✔
560
            written += linesize;
1,368✔
561
        } while (pass && (written < deltasize + (linesize * 50ul)));
1,368✔
562

563
        /* look for files matching the original name. */
564
        unsigned foundlogs = 0u;
18✔
565
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
18✔
566
            handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
567
            pass = false;
×
568
        }
569

570
        /* if two (or more) are present, the test is a pass. */
571
        printf("\tfound %u log files with base name: %s\n", foundlogs, logbasename);
18✔
572
        pass &= foundlogs >= 2u;
18✔
573
    }
574

575
    pass &= sir_remfile(fileid);
20✔
576

577
    delcount = 0u;
20✔
578
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
20✔
579
        handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
580
        return false;
×
581
    }
582

583
    if (delcount > 0u)
20✔
584
        printf("\tfound and removed %u log file(s)\n", delcount);
17✔
585

586
    sir_cleanup();
20✔
587
    return print_result_and_return(pass);
20✔
588
}
589

590
bool sirtest_failwithoutinit(void) {
23✔
591
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
23✔
592

593
    if (pass)
23✔
594
        print_expected_error();
23✔
595

596
    return print_result_and_return(pass);
23✔
597
}
598

599
bool sirtest_failinittwice(void) {
23✔
600
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
601
    bool pass = si_init;
20✔
602

603
    INIT(si2, SIRL_ALL, 0, 0, 0);
23✔
604
    pass &= !si2_init;
23✔
605

606
    if (pass)
23✔
607
        print_expected_error();
23✔
608

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

613
bool sirtest_failinvalidinitdata(void) {
23✔
614
    sirinit si;
615

616
    /* fill with bad data. */
617
    memset(&si, 0xab, sizeof(sirinit));
20✔
618

619
    printf("\tcalling sir_init with invalid data...\n");
20✔
620
    bool pass = !sir_init(&si);
23✔
621

622
    if (pass)
23✔
623
        print_expected_error();
23✔
624

625
    sir_cleanup();
23✔
626
    return print_result_and_return(pass);
23✔
627
}
628

629
bool sirtest_initcleanupinit(void) {
23✔
630
    INIT(si1, SIRL_ALL, 0, 0, 0);
23✔
631
    bool pass = si1_init;
20✔
632

633
    pass &= sir_info("init called once; testing output...");
23✔
634
    sir_cleanup();
23✔
635

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

639
    pass &= sir_info("init called again after re-init; testing output...");
23✔
640
    sir_cleanup();
23✔
641

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

645
bool sirtest_initmakeinit(void) {
23✔
646
    bool pass = true;
20✔
647

648
    sirinit si;
649
    pass &= sir_makeinit(&si);
23✔
650
    pass &= sir_init(&si);
23✔
651
    pass &= sir_info("initialized with sir_makeinit");
23✔
652
    pass &= sir_cleanup();
23✔
653

654
    return print_result_and_return(pass);
23✔
655
}
656

657
bool sirtest_failaftercleanup(void) {
23✔
658
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
659
    bool pass = si_init;
20✔
660

661
    sir_cleanup();
23✔
662
    pass &= !sir_info("already cleaned up; this needs to fail");
23✔
663

664
    if (pass)
23✔
665
        print_expected_error();
23✔
666

667
    return print_result_and_return(pass);
23✔
668
}
669

670
bool sirtest_errorsanity(void) {
23✔
671
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
672
    bool pass = si_init;
20✔
673

674
    struct {
675
        uint16_t code;
676
        const char* name;
677
    } errors[] = {
23✔
678
        {SIR_E_NOERROR,   "SIR_E_NOERROR"},   /**< The operation completed successfully (0) */
679
        {SIR_E_NOTREADY,  "SIR_E_NOTREADY"},  /**< libsir has not been initialized (1) */
680
        {SIR_E_ALREADY,   "SIR_E_ALREADY"},   /**< libsir is already initialized (2) */
681
        {SIR_E_DUPITEM,   "SIR_E_DUPITEM"},   /**< Item already managed by libsir (3) */
682
        {SIR_E_NOITEM,    "SIR_E_NOITEM"},    /**< Item not managed by libsir (4) */
683
        {SIR_E_NOROOM,    "SIR_E_NOROOM"},    /**< Maximum number of items already stored (5) */
684
        {SIR_E_OPTIONS,   "SIR_E_OPTIONS"},   /**< Option flags are invalid (6) */
685
        {SIR_E_LEVELS,    "SIR_E_LEVELS"},    /**< Level flags are invalid (7) */
686
        {SIR_E_TEXTSTYLE, "SIR_E_TEXTSTYLE"}, /**< Text style is invalid (8) */
687
        {SIR_E_STRING,    "SIR_E_STRING"},    /**< Invalid string argument (9) */
688
        {SIR_E_NULLPTR,   "SIR_E_NULLPTR"},   /**< NULL pointer argument (10) */
689
        {SIR_E_INVALID,   "SIR_E_INVALID"},   /**< Invalid argument (11) */
690
        {SIR_E_NODEST,    "SIR_E_NODEST"},    /**< No destinations registered for level (12) */
691
        {SIR_E_UNAVAIL,   "SIR_E_UNAVAIL"},   /**< Feature is disabled or unavailable (13) */
692
        {SIR_E_INTERNAL,  "SIR_E_INTERNAL"},  /**< An internal error has occurred (14) */
693
        {SIR_E_COLORMODE, "SIR_E_COLORMODE"}, /**< Invalid color mode (15) */
694
        {SIR_E_TEXTATTR,  "SIR_E_TEXTATTR"},  /**< Invalid text attributes (16) */
695
        {SIR_E_TEXTCOLOR, "SIR_E_TEXTCOLOR"}, /**< Invalid text color (17) */
696
        {SIR_E_PLUGINBAD, "SIR_E_PLUGINBAD"}, /**< Plugin module is malformed (18) */
697
        {SIR_E_PLUGINDAT, "SIR_E_PLUGINDAT"}, /**< Data produced by plugin is invalid (19) */
698
        {SIR_E_PLUGINVER, "SIR_E_PLUGINVER"}, /**< Plugin interface version unsupported (20) */
699
        {SIR_E_PLUGINERR, "SIR_E_PLUGINERR"}, /**< Plugin reported failure (21) */
700
        {SIR_E_PLATFORM,  "SIR_E_PLATFORM"},  /**< Platform error code %d: %s (22) */
701
        {SIR_E_UNKNOWN,   "SIR_E_UNKNOWN"},   /**< Unknown error (4095) */
702
    };
703

704
    char message[SIR_MAXERROR] = {0};
23✔
705
    for (size_t n = 0; n < _sir_countof(errors); n++) {
575✔
706
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
552✔
707
        memset(message, 0, SIR_MAXERROR);
480✔
708
        uint16_t err = sir_geterror(message);
552✔
709
        pass &= errors[n].code == err && *message != '\0';
552✔
710
        printf("\t%s = %s\n", errors[n].name, message);
552✔
711
    }
712

713
    sir_cleanup();
23✔
714
    return print_result_and_return(pass);
23✔
715
}
716

717
bool sirtest_textstylesanity(void) {
23✔
718
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
719
    bool pass = si_init;
20✔
720

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

725
    pass &= !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK);
23✔
726
    pass &= sir_info("oops, did it again...");
23✔
727

728
    pass &= !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff);
23✔
729
    pass &= sir_info("and again.");
23✔
730
    PRINT_PASS(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
23✔
731

732
    printf("\t" WHITEB("--- unusual but valid ---") "\n");
20✔
733
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT);
23✔
734
    pass &= sir_info("system default fg and bg");
23✔
735
    PRINT_PASS(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
23✔
736

737
    printf("\t" WHITEB("--- override defaults ---") "\n");
20✔
738
    pass &= sir_resettextstyles();
23✔
739

740
    pass &= sir_debug("default style");
23✔
741
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY);
23✔
742
    pass &= sir_debug("override style");
23✔
743

744
    pass &= sir_info("default style");
23✔
745
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA);
23✔
746
    pass &= sir_info("override style");
23✔
747

748
    pass &= sir_notice("default style");
23✔
749
    pass &= sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW);
23✔
750
    pass &= sir_notice("override style");
23✔
751

752
    pass &= sir_warn("default style");
23✔
753
    pass &= sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE);
23✔
754
    pass &= sir_warn("override style");
23✔
755

756
    pass &= sir_error("default style");
23✔
757
    pass &= sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE);
23✔
758
    pass &= sir_error("override style");
23✔
759

760
    pass &= sir_crit("default style");
23✔
761
    pass &= sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN);
23✔
762
    pass &= sir_crit("override style");
23✔
763

764
    pass &= sir_alert("default style");
23✔
765
    pass &= sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT);
23✔
766
    pass &= sir_alert("override style");
23✔
767

768
    pass &= sir_emerg("default style");
23✔
769
    pass &= sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT);
23✔
770
    pass &= sir_emerg("override style");
23✔
771
    PRINT_PASS(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
23✔
772

773
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
20✔
774
    pass &= sir_resettextstyles();
23✔
775

776
    pass &= sir_debug("default style (debug)");
23✔
777
    pass &= sir_info("default style (info)");
23✔
778
    pass &= sir_notice("default style (notice)");
23✔
779
    pass &= sir_warn("default style (warning)");
23✔
780
    pass &= sir_error("default style (error)");
23✔
781
    pass &= sir_crit("default style (crit)");
23✔
782
    pass &= sir_alert("default style (alert)");
23✔
783
    pass &= sir_emerg("default style (emergency)");
23✔
784
    PRINT_PASS(pass, "\t--- reset to defaults: %s ---\n\n", PRN_PASS(pass));
23✔
785

786
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
20✔
787
    pass &= sir_setcolormode(SIRCM_256);
23✔
788

789
    for (sir_textcolor fg = 0, bg = 255; (fg < 256 && bg > 0); fg++, bg--) {
5,888✔
790
        if (fg != bg) {
5,865✔
791
            pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
5,865✔
792
            pass &= sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
5,865✔
793
                fg, bg);
794
        }
795
    }
796

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

799
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
20✔
800
    pass &= sir_setcolormode(SIRCM_RGB);
23✔
801

802
    for (size_t n = 0; n < 256; n++) {
5,911✔
803
        sir_textcolor fg = sir_makergb(getrand(255u), getrand(255u), getrand(255u));
5,888✔
804
        sir_textcolor bg = sir_makergb(getrand(255u), getrand(255u), getrand(255u));
5,888✔
805
        pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
5,888✔
806
        pass &= sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
5,888✔
807
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
5,888✔
808
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
5,888✔
809
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg));
5,888✔
810
    }
811
    PRINT_PASS(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
23✔
812

813
    printf("\t" WHITEB("--- change mode: invalid mode ---") "\n");
20✔
814
    pass &= !sir_setcolormode(SIRCM_INVALID);
23✔
815
    sir_textcolor fg = sir_makergb(255, 0, 0);
23✔
816
    sir_textcolor bg = sir_makergb(0, 0, 0);
23✔
817
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
23✔
818
    pass &= sir_debug("this is still RGB color mode");
23✔
819
    PRINT_PASS(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
23✔
820

821
    printf("\t" WHITEB("--- change mode: 16-color ---") "\n");
20✔
822
    pass &= sir_setcolormode(SIRCM_16);
23✔
823
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT);
23✔
824
    pass &= sir_debug("this is 16-color mode (fg: %"PRId32", bg: default)",
23✔
825
        SIRTC_BMAGENTA);
826
    PRINT_PASS(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
23✔
827

828
    sir_cleanup();
23✔
829

830
    return print_result_and_return(pass);
23✔
831
}
832

833
#if defined(__clang__) /* only Clang has implicit-conversion; GCC BZ#87454 */
834
SANITIZE_SUPPRESS("implicit-conversion")
835
#endif
836
bool sirtest_optionssanity(void) {
23✔
837
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
838
    bool pass = si_init;
20✔
839

840
    static const size_t iterations = 10;
841

842
    /* these should all be valid. */
843
    printf("\t" WHITEB("--- individual valid options ---") "\n");
20✔
844
    pass &= _sir_validopts(SIRO_ALL);
23✔
845
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
20✔
846
    pass &= _sir_validopts(SIRO_NOTIME);
23✔
847
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
20✔
848
    pass &= _sir_validopts(SIRO_NOHOST);
23✔
849
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
20✔
850
    pass &= _sir_validopts(SIRO_NOLEVEL);
23✔
851
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
20✔
852
    pass &= _sir_validopts(SIRO_NONAME);
23✔
853
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
20✔
854
    pass &= _sir_validopts(SIRO_NOPID);
23✔
855
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
20✔
856
    pass &= _sir_validopts(SIRO_NOTID);
23✔
857
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
20✔
858
    pass &= _sir_validopts(SIRO_NOHDR);
23✔
859
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
20✔
860
    pass &= _sir_validopts(SIRO_MSGONLY);
23✔
861
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
20✔
862
    PRINT_PASS(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
23✔
863

864
    /* any combination these bitwise OR'd together
865
       to form a bitmask should also be valid. */
866
    static const sir_option option_arr[SIR_NUMOPTIONS] = {
867
        SIRO_NOTIME,
868
        SIRO_NOHOST,
869
        SIRO_NOLEVEL,
870
        SIRO_NONAME,
871
        SIRO_NOMSEC,
872
        SIRO_NOPID,
873
        SIRO_NOTID,
874
        SIRO_NOHDR
875
    };
876

877
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
20✔
878
    uint32_t last_count = SIR_NUMOPTIONS;
20✔
879
    for (size_t n = 0; n < iterations; n++) {
253✔
880
        sir_options opts    = 0;
200✔
881
        uint32_t rand_count = 0;
200✔
882
        size_t last_idx     = 0;
200✔
883

884
        do {
885
            rand_count = getrand(SIR_NUMOPTIONS);
333✔
886
        } while (rand_count == last_count || rand_count <= 1);
333✔
887

888
        last_count = rand_count;
200✔
889

890
        for (size_t i = 0; i < rand_count; i++) {
1,214✔
891
            size_t rand_idx = 0;
855✔
892
            size_t tries    = 0;
855✔
893

894
            do {
895
                if (++tries > SIR_NUMOPTIONS - 2)
1,639✔
896
                    break;
×
897
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
1,639✔
898

899
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,639✔
900

901
            last_idx = rand_idx;
855✔
902
            opts |= option_arr[rand_idx];
984✔
903
        }
904

905
        pass &= _sir_validopts(opts);
230✔
906
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
230✔
907
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
908
    }
909
    PRINT_PASS(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
23✔
910

911
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
912

913
    /* the lowest byte is not valid. */
914
    sir_options invalid = 0x000000ff;
20✔
915
    pass &= !_sir_validopts(invalid);
23✔
916
    printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
20✔
917

918
    /* gaps inbetween valid options. */
919
    invalid = 0x0001ff00u & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
20✔
920
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
921
    pass &= !_sir_validopts(invalid);
23✔
922
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00u: %08"PRIx32) "\n", invalid);
20✔
923

924
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
925
    for (sir_option o = 0x00008f00u; o < SIRO_NOHDR; o += 0x1000u) {
207✔
926
        pass &= !_sir_validopts(o);
184✔
927
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
160✔
928
    }
929

930
    /* greater than SIRO_NOHDR. */
931
    invalid = (0xFFFF0000 & ~SIRO_NOHDR); /* implicit-conversion */
20✔
932
    pass &= !_sir_validopts(invalid);
23✔
933
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
20✔
934

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

937
    sir_cleanup();
23✔
938
    return print_result_and_return(pass);
23✔
939
}
940

941
bool sirtest_levelssanity(void) {
23✔
942
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
943
    bool pass = si_init;
20✔
944

945
    static const size_t iterations = 10;
946

947
    /* these should all be valid. */
948
    printf("\t" WHITEB("--- individual valid levels ---") "\n");
20✔
949
    pass &= _sir_validlevel(SIRL_INFO) && _sir_validlevels(SIRL_INFO);
23✔
950
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_INFO);
20✔
951
    pass &= _sir_validlevel(SIRL_DEBUG) && _sir_validlevels(SIRL_DEBUG);
23✔
952
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_DEBUG);
20✔
953
    pass &= _sir_validlevel(SIRL_NOTICE) && _sir_validlevels(SIRL_NOTICE);
23✔
954
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_NOTICE);
20✔
955
    pass &= _sir_validlevel(SIRL_WARN) && _sir_validlevels(SIRL_WARN);
23✔
956
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_WARN);
20✔
957
    pass &= _sir_validlevel(SIRL_ERROR) && _sir_validlevels(SIRL_ERROR);
23✔
958
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ERROR);
20✔
959
    pass &= _sir_validlevel(SIRL_CRIT) && _sir_validlevels(SIRL_CRIT);
23✔
960
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_CRIT);
20✔
961
    pass &= _sir_validlevel(SIRL_ALERT) && _sir_validlevels(SIRL_ALERT);
23✔
962
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ALERT);
20✔
963
    pass &= _sir_validlevel(SIRL_EMERG) && _sir_validlevels(SIRL_EMERG);
23✔
964
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_EMERG);
20✔
965
    pass &= _sir_validlevels(SIRL_ALL);
23✔
966
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_ALL);
20✔
967
    pass &= _sir_validlevels(SIRL_NONE);
23✔
968
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_NONE);
20✔
969
    PRINT_PASS(pass, "\t--- individual valid levels: %s ---\n\n", PRN_PASS(pass));
23✔
970

971
    /* any combination these bitwise OR'd together
972
       to form a bitmask should also be valid. */
973
    static const sir_levels levels_arr[SIR_NUMLEVELS] = {
974
       SIRL_EMERG,
975
       SIRL_ALERT,
976
       SIRL_CRIT,
977
       SIRL_ERROR,
978
       SIRL_WARN,
979
       SIRL_NOTICE,
980
       SIRL_INFO,
981
       SIRL_DEBUG
982
    };
983

984
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
20✔
985
    uint32_t last_count = SIR_NUMLEVELS;
20✔
986
    for (size_t n = 0; n < iterations; n++) {
253✔
987
        sir_levels levels   = 0u;
200✔
988
        uint32_t rand_count = 0u;
200✔
989
        size_t last_idx     = 0ul;
200✔
990

991
        do {
992
            rand_count = getrand(SIR_NUMLEVELS);
444✔
993
        } while (rand_count == last_count || rand_count <= 1u);
444✔
994

995
        last_count = rand_count;
200✔
996

997
        for (size_t i = 0; i < rand_count; i++) {
1,359✔
998
            size_t rand_idx = 0;
982✔
999
            size_t tries    = 0;
982✔
1000

1001
            do {
1002
                if (++tries > SIR_NUMLEVELS - 2)
1,864✔
1003
                    break;
31✔
1004
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,827✔
1005
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,827✔
1006

1007
            last_idx = rand_idx;
982✔
1008
            levels |= levels_arr[rand_idx];
1,129✔
1009
        }
1010

1011
        pass &= _sir_validlevels(levels);
230✔
1012
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
230✔
1013
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
1014
    }
1015
    PRINT_PASS(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
23✔
1016

1017
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
1018

1019
    /* greater than SIRL_ALL. */
1020
    sir_levels invalid = (0xffffu & ~SIRL_ALL);
20✔
1021
    pass &= !_sir_validlevels(invalid);
23✔
1022
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
20✔
1023

1024
    /* individual invalid level. */
1025
    sir_level invalid2 = 0x1337u;
20✔
1026
    pass &= !_sir_validlevel(invalid2);
23✔
1027
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx16) "\n", invalid2);
20✔
1028

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

1031
    sir_cleanup();
23✔
1032
    return print_result_and_return(pass);
23✔
1033
}
1034

1035
bool sirtest_mutexsanity(void) {
23✔
1036
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1037
    bool pass = si_init;
20✔
1038

1039
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
20✔
1040
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
20✔
1041

1042
    sir_mutex m1 = SIR_MUTEX_INIT;
23✔
1043
    pass &= _sir_mutexcreate(&m1);
23✔
1044

1045
    print_test_error(pass, pass);
23✔
1046

1047
    if (pass) {
23✔
1048
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
20✔
1049
        pass &= _sir_mutexlock(&m1);
23✔
1050

1051
        print_test_error(pass, pass);
23✔
1052

1053
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
20✔
1054
        pass &= _sir_mutexunlock(&m1);
23✔
1055

1056
        print_test_error(pass, pass);
23✔
1057

1058
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
20✔
1059
        pass &= _sir_mutextrylock(&m1);
23✔
1060

1061
        print_test_error(pass, pass);
23✔
1062

1063
        printf(INDENT_ITEM WHITE("unlocking...") "\n");
20✔
1064
        pass &= _sir_mutexunlock(&m1);
23✔
1065

1066
        print_test_error(pass, pass);
23✔
1067

1068
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
20✔
1069
        pass &= _sir_mutexdestroy(&m1);
23✔
1070

1071
        print_test_error(pass, pass);
23✔
1072

1073
    }
1074
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
23✔
1075

1076
    printf("\t" WHITEB("invalid arguments") "\n");
20✔
1077
    printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
20✔
1078
    pass &= !_sir_mutexcreate(NULL);
23✔
1079
    printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
20✔
1080
    pass &= !_sir_mutexlock(NULL);
23✔
1081
    printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
20✔
1082
    pass &= !_sir_mutextrylock(NULL);
23✔
1083
    printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
20✔
1084
    pass &= !_sir_mutexunlock(NULL);
23✔
1085
    printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
20✔
1086
    pass &= !_sir_mutexdestroy(NULL);
23✔
1087
    PRINT_PASS(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
23✔
1088

1089
    pass &= sir_cleanup();
23✔
1090
    return print_result_and_return(pass);
23✔
1091
}
1092

1093
bool sirtest_perf(void) {
1✔
1094
    static const char* logbasename = "libsir-perf";
1095
    static const char* logext      = "";
1096

1097
#if !defined(__WIN__) && !defined(DUMA)
1098
    static const size_t perflines = 1000000;
1099
#else /* __WIN__ */
1100
    static const size_t perflines = 100000;
1101
#endif
1102

1103
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
1✔
1104
    bool pass = si_init;
1✔
1105

1106
    if (pass) {
1✔
1107
        float printfelapsed = 0.0f;
1✔
1108
        float stdioelapsed  = 0.0f;
1✔
1109
        float fileelapsed   = 0.0f;
1✔
1110

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

1113
        sir_timer printftimer = {0};
1✔
1114
        sirtimerstart(&printftimer);
1✔
1115

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

1120
        printfelapsed = sirtimerelapsed(&printftimer);
1✔
1121

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

1124
        sir_timer stdiotimer = {0};
1✔
1125
        sirtimerstart(&stdiotimer);
1✔
1126

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

1131
        stdioelapsed = sirtimerelapsed(&stdiotimer);
1✔
1132

1133
        sir_cleanup();
1✔
1134

1135
        INIT(si2, 0, 0, 0, 0);
1✔
1136
        pass &= si2_init;
1✔
1137

1138
        char logfilename[SIR_MAXPATH] = {0};
1✔
1139
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
1✔
1140

1141
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
1✔
1142
        pass &= 0 != logid;
1✔
1143

1144
        if (pass) {
1✔
1145
            printf("\t" BLUE("%zu lines libsir(log file)...") "\n", perflines);
1✔
1146

1147
            sir_timer filetimer = {0};
1✔
1148
            sirtimerstart(&filetimer);
1✔
1149

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

1153
            fileelapsed = sirtimerelapsed(&filetimer);
1✔
1154

1155
            pass &= sir_remfile(logid);
1✔
1156
        }
1157

1158
        if (pass) {
1✔
1159
            printf("\t" WHITEB("printf: ") CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1✔
1160
                perflines, (double)printfelapsed / (double)1e3,
1✔
1161
                (double)perflines / (double)((double)printfelapsed / (double)1e3));
1✔
1162
            printf("\t" WHITEB("libsir(stdout): ")
1✔
1163
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1164
                perflines, (double)stdioelapsed / (double)1e3,
1✔
1165
                (double)perflines / (double)((double)stdioelapsed / (double)1e3));
1✔
1166
            printf("\t" WHITEB("libsir(log file): ")
1✔
1167
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1168
                perflines, (double)fileelapsed / (double)1e3,
1✔
1169
                (double)perflines / (double)((double)fileelapsed / (double)1e3));
1✔
1170
            printf("\t" WHITEB("timer resolution: ") CYAN("~%ldnsec") "\n", sirtimergetres());
1✔
1171
        }
1172
    }
1173

1174
    unsigned deleted = 0u;
1✔
1175
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
1✔
1176

1177
    if (deleted > 0u)
1✔
1178
        printf("\t" DGRAY("deleted %u log file(s)") "\n", deleted);
1✔
1179

1180
    sir_cleanup();
1✔
1181
    return print_result_and_return(pass);
1✔
1182
}
1183

1184
bool sirtest_updatesanity(void) {
23✔
1185
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
23✔
1186
    bool pass = si_init;
20✔
1187

1188
#define UPDATE_SANITY_ARRSIZE 10
1189

1190
    static const char* logfile = MAKE_LOG_NAME("update-sanity.log");
1191
    static const sir_options opts_array[UPDATE_SANITY_ARRSIZE] = {
1192
        SIRO_NOHOST | SIRO_NOTIME | SIRO_NOLEVEL,
1193
        SIRO_MSGONLY, SIRO_NONAME | SIRO_NOTID,
1194
        SIRO_NOPID | SIRO_NOTIME,
1195
        SIRO_NOTIME | SIRO_NOLEVEL | SIRO_NONAME,
1196
        SIRO_NOTIME, SIRO_NOMSEC | SIRO_NOHOST,
1197
        SIRO_NOPID | SIRO_NOTID,
1198
        SIRO_NOHOST | SIRO_NOTID, SIRO_ALL
1199
    };
1200

1201
    static const sir_levels levels_array[UPDATE_SANITY_ARRSIZE] = {
1202
        SIRL_NONE, SIRL_ALL, SIRL_EMERG, SIRL_ALERT,
1203
        SIRL_CRIT, SIRL_ERROR, SIRL_WARN, SIRL_NOTICE,
1204
        SIRL_INFO, SIRL_DEBUG
1205
    };
1206

1207
    rmfile(logfile);
23✔
1208
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
23✔
1209
    pass &= 0 != id1;
23✔
1210

1211
    for (int i = 0; i < 10; i++) {
214✔
1212
        if (!pass)
195✔
1213
            break;
4✔
1214

1215
        /* reset to defaults*/
1216
        pass &= sir_stdoutlevels(SIRL_DEFAULT);
191✔
1217
        pass &= sir_stderrlevels(SIRL_DEFAULT);
191✔
1218
        pass &= sir_stdoutopts(SIRO_DEFAULT);
191✔
1219
        pass &= sir_stderropts(SIRO_DEFAULT);
191✔
1220

1221
        pass &= sir_debug("default config (debug)");
191✔
1222
        pass &= sir_info("default config (info)");
191✔
1223
        pass &= sir_notice("default config (notice)");
191✔
1224
        pass &= sir_warn("default config (warning)");
191✔
1225
        pass &= sir_error("default config (error)");
191✔
1226
        pass &= sir_crit("default config (critical)");
191✔
1227
        pass &= sir_alert("default config (alert)");
191✔
1228
        pass &= sir_emerg("default config (emergency)");
191✔
1229

1230
        /* pick random options to set/unset */
1231
        uint32_t rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1232
        pass &= sir_stdoutlevels(levels_array[rnd]);
191✔
1233
        pass &= sir_stdoutopts(opts_array[rnd]);
191✔
1234
        printf("\t" WHITE("set random config #%"PRIu32" for stdout") "\n", rnd);
161✔
1235

1236
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1237
        pass &= sir_stderrlevels(levels_array[rnd]);
191✔
1238
        pass &= sir_stderropts(opts_array[rnd]);
191✔
1239
        printf("\t" WHITE("set random config #%"PRIu32" for stderr") "\n", rnd);
161✔
1240

1241
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1242
        pass &= sir_filelevels(id1, levels_array[rnd]);
191✔
1243
        pass &= sir_fileopts(id1, opts_array[rnd]);
191✔
1244
        printf("\t" WHITE("set random config #%"PRIu32" for %s") "\n", rnd, logfile);
191✔
1245

1246
        pass &= filter_error(sir_debug("modified config #%"PRIu32" (debug)", rnd), SIR_E_NODEST);
191✔
1247
        pass &= filter_error(sir_info("modified config #%"PRIu32" (info)", rnd), SIR_E_NODEST);
191✔
1248
        pass &= filter_error(sir_notice("modified config #%"PRIu32" (notice)", rnd), SIR_E_NODEST);
191✔
1249
        pass &= filter_error(sir_warn("modified config #%"PRIu32" (warning)", rnd), SIR_E_NODEST);
191✔
1250
        pass &= filter_error(sir_error("modified config #%"PRIu32" (error)", rnd), SIR_E_NODEST);
191✔
1251
        pass &= filter_error(sir_crit("modified config #%"PRIu32" (critical)", rnd), SIR_E_NODEST);
191✔
1252
        pass &= filter_error(sir_alert("modified config #%"PRIu32" (alert)", rnd), SIR_E_NODEST);
191✔
1253
        pass &= filter_error(sir_emerg("modified config #%"PRIu32" (emergency)", rnd), SIR_E_NODEST);
191✔
1254
    }
1255

1256
    if (pass) {
23✔
1257
        /* restore to default config and run again */
1258
        sir_stdoutlevels(SIRL_DEFAULT);
19✔
1259
        sir_stderrlevels(SIRL_DEFAULT);
19✔
1260
        sir_stdoutopts(SIRO_DEFAULT);
19✔
1261
        sir_stderropts(SIRO_DEFAULT);
19✔
1262

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

1273
    pass &= sir_remfile(id1);
23✔
1274
    rmfile(logfile);
23✔
1275
    sir_cleanup();
23✔
1276

1277
    return print_result_and_return(pass);
23✔
1278
}
1279

1280
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1281
static
1282
bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
17✔
1283
    static const int runs = 5;
1284

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

1289
#if !defined(__WIN__)
1290
    uint32_t rnd = (uint32_t)(_sir_getpid() + _sir_gettid());
17✔
1291
#else
1292
    uint32_t rnd = (uint32_t)GetTickCount();
1293
#endif
1294

1295
    bool pass = true;
14✔
1296
    for (int i = 1; i <= runs; i++) {
102✔
1297
        /* randomly skip setting process name, identity/category to thoroughly
1298
         * test fallback routines; randomly update the config mid-run. */
1299
        bool set_procname = getrand_bool(rnd ^ 0x5a5a5a5au);
85✔
1300
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9u);
85✔
1301
        bool set_category = getrand_bool(rnd ^ 0x32323232u);
85✔
1302
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7u);
85✔
1303

1304
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
85✔
1305
            set_procname, set_identity, set_category, do_update);
1306

1307
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
94✔
1308
        si.d_syslog.opts   = SIRO_DEFAULT;
85✔
1309
        si.d_syslog.levels = SIRL_DEFAULT;
85✔
1310

1311
        if (set_identity)
85✔
1312
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
48✔
1313

1314
        if (set_category)
85✔
1315
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
49✔
1316

1317
        si_init = sir_init(&si); //-V519
85✔
1318
        pass &= si_init;
70✔
1319

1320
        if (do_update)
85✔
1321
            pass &= sir_sysloglevels(SIRL_ALL);
34✔
1322

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

1326
        pass &= sir_notice("%d/%d: this notice message sent to stdout and %s.", i, runs, sl_name);
85✔
1327
        pass &= sir_warn("%d/%d: this warning message sent to stdout and %s.", i, runs, sl_name);
85✔
1328
        pass &= sir_error("%d/%d: this error message sent to stdout and %s.", i, runs, sl_name);
85✔
1329

1330
        if (set_identity) {
85✔
1331
            pass &= sir_syslogid("my test ID");
48✔
1332
            pass &= sir_syslogid("my test ID"); /* test deduping. */
48✔
1333
        }
1334

1335
        if (set_category) {
85✔
1336
            pass &= sir_syslogcat("my test category");
49✔
1337
            pass &= sir_syslogcat("my test category"); /* test deduping. */
49✔
1338
        }
1339

1340
        if (do_update)
85✔
1341
            pass &= sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID));
34✔
1342

1343
        pass &= sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name);
85✔
1344
        pass &= sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name);
85✔
1345
        pass &= sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name);
85✔
1346

1347
# if defined(SIR_OS_LOG_ENABLED)
1348
#  if defined(__MACOS__) && !defined(__INTEL_COMPILER)
1349
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1350
            printf("\ttesting os_log activity feature...\n");
1351

1352
            /* also test activity grouping in Console. there's only one way to validate
1353
             * this and that's by manually viewing the log. */
1354
             os_activity_t parent = os_activity_create("flying to the moon",
1355
                OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DETACHED);
1356

1357
            /* execution now passes to os_log_parent_activity(), where some logging
1358
            * will occur, then a sub-activity will be created, and more logging. */
1359
            os_activity_apply_f(parent, (void*)parent, os_log_parent_activity);
1360
        }
1361
#  endif
1362
# endif
1363

1364
        sir_cleanup();
85✔
1365

1366
        if (!pass)
85✔
1367
            break;
×
1368
    }
1369

1370
    return print_result_and_return(pass);
17✔
1371
}
1372
#endif
1373

1374
#if defined(SIR_NO_SYSTEM_LOGGERS)
1375
static bool generic_disabled_syslog_test(const char* sl_name, const char* identity,
6✔
1376
    const char* category) {
1377
    INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, "sir_disabled_sltest");
6✔
1378
    si.d_syslog.opts   = SIRO_DEFAULT;
6✔
1379
    si.d_syslog.levels = SIRL_DEFAULT;
6✔
1380
    bool pass = true;
6✔
1381

1382
    SIR_UNUSED(sl_name);
1383

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

1386
    /* init should just ignore the syslog settings. */
1387
    pass &= sir_init(&si);
6✔
1388

1389
    /* these calls should all fail. */
1390
    printf("\tsetting levels...\n");
6✔
1391
    pass &= !sir_sysloglevels(SIRL_ALL);
6✔
1392

1393
    if (pass)
6✔
1394
        print_expected_error();
6✔
1395

1396
    printf("\tsetting options...\n");
6✔
1397
    pass &= !sir_syslogopts(SIRO_DEFAULT);
6✔
1398

1399
    if (pass)
6✔
1400
        print_expected_error();
6✔
1401

1402
    printf("\tsetting identity...\n");
6✔
1403
    pass &= !sir_syslogid(identity);
6✔
1404

1405
    if (pass)
6✔
1406
        print_expected_error();
6✔
1407

1408
    printf("\tsetting category...\n");
6✔
1409
    pass &= !sir_syslogcat(category);
6✔
1410

1411
    if (pass)
6✔
1412
        print_expected_error();
6✔
1413

1414
    pass &= sir_cleanup();
6✔
1415
    return print_result_and_return(pass);
6✔
1416
}
1417
#endif
1418

1419
bool sirtest_syslog(void) {
23✔
1420
#if !defined(SIR_SYSLOG_ENABLED)
1421
# if defined(SIR_NO_SYSTEM_LOGGERS)
1422
    bool pass = generic_disabled_syslog_test("syslog", "sirtests", "tests");
6✔
1423
    return print_result_and_return(pass);
6✔
1424
# else
1425
    printf("\t" DGRAY("SIR_SYSLOG_ENABLED is not defined; skipping") "\n");
1426
    return true;
1427
# endif
1428
#else
1429
    bool pass = generic_syslog_test("syslog", "sirtests", "tests");
17✔
1430
    return print_result_and_return(pass);
17✔
1431
#endif
1432
}
1433

1434
bool sirtest_os_log(void) {
23✔
1435
#if !defined(SIR_OS_LOG_ENABLED)
1436
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
20✔
1437
    return true;
23✔
1438
#else
1439
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1440
    return print_result_and_return(pass);
1441
#endif
1442
}
1443

1444
char *sirtest_get_wineversion(void) {
46✔
1445
#if !defined(__WIN__)
1446
    return NULL;
46✔
1447
#else /* __WIN__ */
1448
    typedef char* (__stdcall *get_wine_ver_proc)(void);
1449
    static get_wine_ver_proc _p_wine_get_version = NULL;
1450

1451
    HMODULE _h_ntdll = GetModuleHandle("ntdll.dll");
1452
    if (_h_ntdll != NULL) {
1453
        _p_wine_get_version = (get_wine_ver_proc)GetProcAddress(_h_ntdll, "wine_get_version");
1454
        if (_p_wine_get_version) {
1455
            char *wine_version = _p_wine_get_version();
1456
            if (wine_version)
1457
                return wine_version;
1458
        }
1459
    }
1460
    return NULL;
1461
#endif
1462
}
1463

1464
bool sirtest_filesystem(void) {
23✔
1465
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1466
    bool pass = si_init;
20✔
1467

1468
    /* Wine version */
1469
    printf("\tRunning under Wine: %s\n",
23✔
1470
            sirtest_get_wineversion() ? sirtest_get_wineversion() : "no"); //-V547
23✔
1471

1472
    /* current working directory. */
1473
    char* cwd = _sir_getcwd();
23✔
1474
    pass &= NULL != cwd;
23✔
1475
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
23✔
1476

1477
    if (NULL != cwd) {
23✔
1478
        /* path to this binary file. */
1479
        char* filename = _sir_getappfilename();
23✔
1480
        pass &= NULL != filename;
23✔
1481
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
23✔
1482

1483
        if (NULL != filename) {
23✔
1484
            /* _sir_get[base|dir]name() can potentially modify filename,
1485
             * so make a copy for each call. */
1486
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1487
            pass &= NULL != filename2;
20✔
1488

1489
            if (NULL != filename2) {
20✔
1490
                /* filename, stripped of directory component(s). */
1491
                char* _basename = _sir_getbasename(filename2);
19✔
1492
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
19✔
1493

1494
                if (!_basename) {
19✔
1495
                    pass = false;
×
1496
                } else {
1497
                    /* the last strlen(_basename) chars of filename should match. */
1498
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
19✔
1499
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
19✔
1500
                    size_t n      = 0;
16✔
1501

1502
                    while (n < len) {
171✔
1503
                        if (filename[offset++] != _basename[n++]) {
152✔
1504
                            pass = false;
×
1505
                            break;
×
1506
                        }
1507
                    }
1508
                }
1509
            }
1510

1511
            /* directory this binary file resides in. */
1512
            char* appdir = _sir_getappdir();
20✔
1513
            pass &= NULL != appdir;
20✔
1514
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
20✔
1515

1516
            /* _sir_get[base|dir]name can potentially modify filename,
1517
             * so make a copy for each call. */
1518
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1519
            pass &= NULL != filename3;
20✔
1520

1521
            if (NULL != appdir && NULL != filename3) {
20✔
1522
                /* should yield the same result as _sir_getappdir(). */
1523
                char* _dirname = _sir_getdirname(filename3);
19✔
1524
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
19✔
1525

1526
                pass &= 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH));
19✔
1527
                pass &= NULL != _dirname &&
38✔
1528
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH));
19✔
1529
            }
1530

1531
            _sir_safefree(&appdir);
20✔
1532
            _sir_safefree(&filename);
20✔
1533
            _sir_safefree(&filename2);
20✔
1534
            _sir_safefree(&filename3);
20✔
1535
        }
1536

1537
        _sir_safefree(&cwd);
23✔
1538
    }
1539

1540
    /* this next section doesn't really yield any useful boolean pass/fail
1541
     * information, but could be helpful to review manually. */
1542
    char* dubious_dirnames[] = {
23✔
1543
#if !defined(__WIN__)
1544
        "/foo",
1545
        "/foo/",
1546
        "/foo/bar",
1547
        "/foo/bar/bad:filename",
1548
        "/",
1549
        ""
1550
#else /* __WIN__ */
1551
        "C:\\foo",
1552
        "C:\\foo\\",
1553
        "C:\\foo\\bar",
1554
        "C:\\foo\\bar\\bad:>filename",
1555
        "C:\\",
1556
        "//network-share/myfolder",
1557
        ""
1558
#endif
1559
    };
1560

1561
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
161✔
1562
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
138✔
1563
        if (NULL != tmp) {
138✔
1564
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
132✔
1565
                tmp, _sir_getdirname(tmp));
1566
            _sir_safefree(&tmp);
132✔
1567
        }
1568
    }
1569

1570
    char* dubious_filenames[] = {
23✔
1571
#if !defined(__WIN__)
1572
        "foo/bar/file-or-directory",
1573
        "/foo/bar/file-or-directory",
1574
        "/foo/bar/illegal:filename",
1575
        "/",
1576
        ""
1577
#else /* __WIN__ */
1578
        "foo\\bar\\file.with.many.full.stops",
1579
        "C:\\foo\\bar\\poorly-renamed.txt.pdf",
1580
        "C:\\foo\\bar\\illegal>filename.txt",
1581
        "C:\\",
1582
        "\\Program Files\\foo.bar",
1583
        ""
1584
#endif
1585
    };
1586

1587
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
138✔
1588
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
115✔
1589
        if (NULL != tmp) {
115✔
1590
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
110✔
1591
                tmp, _sir_getbasename(tmp));
1592
            _sir_safefree(&tmp);
110✔
1593
        }
1594
    }
1595

1596
    /* absolute/relative paths. */
1597
    static const struct {
1598
        const char* const path;
1599
        bool abs;
1600
    } abs_or_rel_paths[] = {
1601
        {"this/is/relative", false},
1602
        {"relative", false},
1603
        {"./relative", false},
1604
        {"../../relative", false},
1605
#if !defined(__WIN__)
1606
        {"/usr/local/bin", true},
1607
        {"/", true},
1608
        {"/home/foo/.config", true},
1609
        {"~/.config", true}
1610
#else /* __WIN__ */
1611
        {"D:\\absolute", true},
1612
        {"C:\\Program Files\\FooBar", true},
1613
        {"C:\\", true},
1614
        {"\\absolute", true},
1615
#endif
1616
    };
1617

1618
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
207✔
1619
        bool relative = false;
184✔
1620
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
184✔
1621

1622
        if (relative == abs_or_rel_paths[n].abs) {
184✔
1623
            pass = false;
×
1624
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1625
                relative ? "true" : "false");
×
1626
        } else {
1627
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
184✔
1628
                relative ? "true" : "false");
160✔
1629
        }
1630

1631
        pass &= ret;
160✔
1632
        if (!ret) {
184✔
1633
            bool unused = print_test_error(false, false);
×
1634
            SIR_UNUSED(unused);
1635
        }
1636
    }
1637

1638
    /* file existence. */
1639
    static const struct {
1640
        const char* const path;
1641
        bool exists;
1642
    } real_or_not[] = {
1643
        {"../foobarbaz", false},
1644
        {"foobarbaz", false},
1645
#if !defined(__WIN__)
1646
        {"/", true},
1647
# if !defined(__HAIKU__)
1648
        {"/usr/bin", true},
1649
# else
1650
        {"/bin", true},
1651
# endif
1652
        {"/dev", true},
1653
#else /* __WIN__ */
1654
        {"C:\\Windows", true},
1655
        {"C:\\Program Files", true},
1656
        {"\\", true},
1657
        {".\\", true},
1658
        {"..\\", true},
1659
#endif
1660
        {"../../LICENSES/MIT.txt", true},
1661
        {"../../msvs/libsir.sln", true},
1662
        {"./", true},
1663
        {"../", true},
1664
        {"file.exists", true}
1665
    };
1666

1667
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
253✔
1668
        bool exists = false;
230✔
1669
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
230✔
1670

1671
        if (exists != real_or_not[n].exists) {
230✔
1672
            pass = false;
23✔
1673
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
23✔
1674
                exists ? "true" : "false");
23✔
1675
        } else {
1676
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
207✔
1677
                exists ? "true" : "false");
177✔
1678
        }
1679

1680
        pass &= ret;
200✔
1681
        if (!ret) {
230✔
1682
            bool unused = print_test_error(false, false);
28✔
1683
            SIR_UNUSED(unused);
1684
        }
1685
    }
1686

1687
    /* checking file descriptors. */
1688
    static int bad_fds[] = {
1689
        0,
1690
        1,
1691
        2,
1692
        1234
1693
    };
1694
    if (sirtest_get_wineversion()) { //-V547
23✔
1695
        bad_fds[3] = 0;
×
1696
    }
1697

1698
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
115✔
1699
        if (_sir_validfd(bad_fds[n])) {
92✔
1700
            pass = false;
×
1701
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1702
        } else {
1703
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
92✔
1704
        }
1705
    }
1706

1707
    FILE* f = NULL;
23✔
1708
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
23✔
1709
    if (!ret) {
23✔
1710
        pass = false;
4✔
1711
        handle_os_error(true, "fopen(%s) failed!", "file.exists");
4✔
1712
    } else {
1713
        int fd = fileno(f);
19✔
1714
        if (!_sir_validfd(fd)) {
19✔
1715
            pass = false;
×
1716
            printf("\t" RED("_sir_validfd(%d) = false") "\n", fd);
×
1717
        } else {
1718
            printf("\t" GREEN("_sir_validfd(%d) = true") "\n", fd);
16✔
1719
        }
1720
    }
1721

1722
    _sir_safefclose(&f);
23✔
1723

1724
    sir_cleanup();
23✔
1725
    return print_result_and_return(pass);
23✔
1726
}
1727

1728
bool sirtest_squelchspam(void) {
23✔
1729
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1730
    bool pass = si_init;
20✔
1731

1732
    static const size_t alternate   = 50ul;
1733
    static const size_t sequence[3] = {
1734
        1000ul, /* non-repeating messages. */
1735
        1000ul, /* repeating messages. */
1736
        1000ul  /* alternating repeating and non-repeating messages. */
1737
    };
1738

1739
    sir_timer timer;
1740
    sirtimerstart(&timer);
23✔
1741

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

1744
    for (size_t n = 0ul, ascii_idx = 33ul; n < sequence[0]; n++, ascii_idx++) {
23,023✔
1745
        pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
43,000✔
1746
            (char)ascii_idx + 1);
23,000✔
1747

1748
        if (ascii_idx == 125ul)
23,000✔
1749
            ascii_idx = 33ul;
200✔
1750
    }
1751

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

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

1757
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
23,000✔
1758
            pass &= !ret;
22,908✔
1759
        else
1760
            pass &= ret;
80✔
1761
    }
1762

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

1766
    bool repeating   = false;
20✔
1767
    size_t counter   = 0ul;
20✔
1768
    size_t repeat_id = 0ul;
20✔
1769
    for (size_t n = 0ul, ascii_idx = 33ul; n < sequence[2]; n++, counter++, ascii_idx++) {
23,023✔
1770
        if (!repeating) {
23,000✔
1771
            pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
11,523✔
1772
                (char)ascii_idx + 1);
11,523✔
1773
        } else {
1774
            bool ret = sir_debug("%zu a repeating message", repeat_id);
11,477✔
1775

1776
            if (counter - 1ul >= SIR_SQUELCH_THRESHOLD - 1ul)
11,477✔
1777
                pass &= !ret;
10,557✔
1778
            else
1779
                pass &= ret;
800✔
1780
        }
1781

1782
        if (counter == alternate) {
23,000✔
1783
            repeating = !repeating;
437✔
1784
            counter = 0ul;
380✔
1785
            repeat_id++;
437✔
1786
        }
1787

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

1792
    sir_cleanup();
23✔
1793
    return print_result_and_return(pass);
23✔
1794
}
1795

1796
bool sirtest_pluginloader(void) {
23✔
1797
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1798
    bool pass = si_init;
20✔
1799

1800
#if !defined(__WIN__)
1801
# define PLUGIN_EXT "so"
1802
#else
1803
# define PLUGIN_EXT "dll"
1804
#endif
1805

1806
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1807
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1808
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1809
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1810
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1811
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1812
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1813
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1814

1815
#if defined(SIR_NO_PLUGINS)
1816
    SIR_UNUSED(plugin2);
1817
    SIR_UNUSED(plugin3);
1818
    SIR_UNUSED(plugin4);
1819
    SIR_UNUSED(plugin5);
1820
    SIR_UNUSED(plugin6);
1821
    SIR_UNUSED(plugin7);
1822
    SIR_UNUSED(plugin8);
1823

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

1826
    printf("\tloading good plugin: '%s'...\n", plugin1);
1✔
1827
    /* load a valid, well-behaved plugin. */
1828
    sirpluginid id = sir_loadplugin(plugin1);
1✔
1829
    pass &= 0 == id;
1✔
1830

1831
    if (pass)
1✔
1832
        print_expected_error();
1✔
1833

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

1838
    if (pass)
1✔
1839
        print_expected_error();
1✔
1840
#else
1841
    /* load a valid, well-behaved plugin. */
1842
    printf("\tloading good plugin: '%s'...\n", plugin1);
22✔
1843
    sirpluginid id = sir_loadplugin(plugin1);
22✔
1844
    pass &= 0 != id;
22✔
1845

1846
    print_test_error(pass, pass);
22✔
1847

1848
    pass &= sir_info("this message will be dispatched to the plugin.");
22✔
1849
    pass &= sir_warn("this message will *not* be dispatched to the plugin.");
22✔
1850

1851
    /* re-loading the same plugin should fail. */
1852
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
22✔
1853
    sirpluginid badid = sir_loadplugin(plugin1);
22✔
1854
    pass &= 0 == badid;
22✔
1855

1856
    print_test_error(pass, pass);
22✔
1857

1858
    /* the following are all invalid or misbehaved, and should all fail. */
1859
    printf("\tloading bad plugin: '%s'...\n", plugin2);
22✔
1860
    badid = sir_loadplugin(plugin2);
22✔
1861
    pass &= 0 == badid;
22✔
1862

1863
    print_test_error(pass, pass);
22✔
1864

1865
    printf("\tloading bad plugin: '%s'...\n", plugin3);
22✔
1866
    badid = sir_loadplugin(plugin3);
22✔
1867
    pass &= 0 == badid;
22✔
1868

1869
    print_test_error(pass, pass);
22✔
1870

1871
    printf("\tloading bad plugin: '%s'...\n", plugin4);
22✔
1872
    badid = sir_loadplugin(plugin4);
22✔
1873
    pass &= 0 == badid;
22✔
1874

1875
    print_test_error(pass, pass);
22✔
1876

1877
    printf("\tloading bad plugin: '%s'...\n", plugin5);
22✔
1878
    badid = sir_loadplugin(plugin5);
22✔
1879
    pass &= 0 == badid;
22✔
1880

1881
    print_test_error(pass, pass);
22✔
1882

1883
    printf("\tloading bad plugin: '%s'...\n", plugin6);
22✔
1884
    badid = sir_loadplugin(plugin6);
22✔
1885
    pass &= 0 == badid;
22✔
1886

1887
    print_test_error(pass, pass);
22✔
1888

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

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

1896
    print_test_error(pass, pass);
22✔
1897

1898
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
22✔
1899
    badid = sir_loadplugin(plugin8);
22✔
1900
    pass &= 0 == badid;
22✔
1901

1902
    print_test_error(pass, pass);
22✔
1903

1904
    /* unload the good plugin manually. */
1905
    printf("\tunloading good plugin: '%s'...\n", plugin1);
22✔
1906
    pass &= sir_unloadplugin(id);
22✔
1907

1908
    print_test_error(pass, pass);
22✔
1909

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

1914
    print_test_error(pass, pass);
22✔
1915

1916
    /* test bad paths. */
1917
    printf("\ntrying to load plugin with NULL path...\n");
19✔
1918
    badid = sir_loadplugin(NULL);
22✔
1919
    pass &= 0 == badid;
22✔
1920

1921
    print_test_error(pass, pass);
22✔
1922
#endif
1923
    pass &= sir_cleanup();
23✔
1924
    return print_result_and_return(pass);
23✔
1925
}
1926

1927
bool sirtest_getversioninfo(void) {
23✔
1928
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1929
    bool pass = si_init;
20✔
1930

1931
    printf("\tchecking version retrieval functions...\n");
20✔
1932

1933
    const char* str = sir_getversionstring();
23✔
1934
    pass &= _sir_validstrnofail(str);
23✔
1935

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

1938
    uint32_t hex = sir_getversionhex();
23✔
1939
    pass &= 0 != hex;
23✔
1940

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

1943
    bool prerel = sir_isprerelease();
23✔
1944
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
23✔
1945

1946
    pass &= sir_cleanup();
23✔
1947
    return print_result_and_return(pass);
23✔
1948
}
1949

1950
enum {
1951
    NUM_THREADS = 4
1952
};
1953

1954
static bool threadpool_pseudojob(void* arg) {
164✔
1955
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
164✔
1956
#if !defined(__WIN__)
1957
    sleep(1);
164✔
1958
#else
1959
    Sleep(1000);
1960
#endif
1961
    return true;
159✔
1962
}
1963

1964
bool sirtest_threadpool(void) {
23✔
1965
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
23✔
1966
    bool pass = si_init;
20✔
1967

1968
    static const size_t num_jobs = 30;
1969
    sir_threadpool* pool         = NULL;
23✔
1970

1971
    pass &= _sir_threadpool_create(&pool, NUM_THREADS);
23✔
1972
    if (pass) {
23✔
1973
        /* dispatch a whole bunch of jobs. */
1974
        for (size_t n = 0; n < num_jobs; n++) {
651✔
1975
            sir_threadpool_job* job = calloc(1, sizeof(sir_threadpool_job));
630✔
1976
            pass &= NULL != job;
630✔
1977
            if (job) {
630✔
1978
                job->fn = &threadpool_pseudojob;
630✔
1979
                job->data = (void*)(n + 1);
630✔
1980
                pass &= _sir_threadpool_add_job(pool, job);
630✔
1981
                pass &= sir_info("dispatched job (fn: %"PRIxPTR", data: %p)",
630✔
1982
                    (uintptr_t)job->fn, job->data);
630✔
1983
            }
1984
        }
1985

1986
#if !defined(__WIN__)
1987
        sleep(1);
21✔
1988
#else
1989
        Sleep(1000);
1990
#endif
1991

1992
        pass &= sir_info("destroying thread pool...");
21✔
1993
        pass &= _sir_threadpool_destroy(&pool);
21✔
1994
    }
1995

1996
    pass &= sir_cleanup();
23✔
1997
    return print_result_and_return(pass);
23✔
1998
}
1999

2000
#if !defined(__WIN__)
2001
static void* threadrace_thread(void* arg);
2002
#else /* __WIN__ */
2003
static unsigned __stdcall threadrace_thread(void* arg);
2004
#endif
2005

2006
bool sirtest_threadrace(void) {
23✔
2007
#if !defined(__WIN__)
2008
    pthread_t thrds[NUM_THREADS] = {0};
23✔
2009
#else /* __WIN__ */
2010
    uintptr_t thrds[NUM_THREADS] = {0};
2011
#endif
2012

2013
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
23✔
2014
    bool pass           = si_init;
20✔
2015
    bool any_created    = false;
20✔
2016
    size_t last_created = 0;
20✔
2017

2018
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
23✔
2019
    pass &= NULL != heap_args;
23✔
2020
    if (!heap_args) {
23✔
2021
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2022
        return false;
1✔
2023
    }
2024

2025
    for (size_t n = 0; n < NUM_THREADS; n++) {
106✔
2026
        if (!pass)
85✔
2027
            break;
×
2028

2029
        heap_args[n].pass = true;
85✔
2030
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
85✔
2031
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2032

2033
#if !defined(__WIN__)
2034
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
85✔
2035
        if (0 != create) {
85✔
2036
            errno = create;
1✔
2037
            handle_os_error(true, "pthread_create() for thread #%zu failed!", n + 1);
1✔
2038
#else /* __WIN__ */
2039
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2040
        if (0 == thrds[n]) {
2041
            handle_os_error(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2042
#endif
2043
            pass = false;
1✔
2044
            break;
1✔
2045
        }
2046

2047
        last_created = n;
72✔
2048
        any_created  = true;
72✔
2049
    }
2050

2051
    if (any_created) {
22✔
2052
        for (size_t j = 0; j < last_created + 1; j++) {
105✔
2053
            bool joined = true;
72✔
2054
            printf("\twaiting for thread %zu/%zu...\n", j + 1, last_created + 1);
84✔
2055
#if !defined(__WIN__)
2056
            int join = pthread_join(thrds[j], NULL);
84✔
2057
            if (0 != join) {
84✔
2058
                joined = false;
4✔
2059
                errno  = join;
4✔
2060
                handle_os_error(true, "pthread_join() for thread #%zu failed!", j + 1);
4✔
2061
            }
2062
#else /* __WIN__ */
2063
            DWORD wait = WaitForSingleObject((HANDLE)thrds[j], INFINITE);
2064
            if (WAIT_OBJECT_0 != wait) {
2065
                joined = false;
2066
                handle_os_error(false, "WaitForSingleObject() for thread #%zu (%p) failed!", j + 1,
2067
                    (HANDLE)thrds[j]);
2068
            }
2069
#endif
2070
            pass &= joined;
72✔
2071
            if (joined) {
72✔
2072
                printf("\tthread %zu/%zu joined\n", j + 1, last_created + 1);
68✔
2073

2074
                pass &= heap_args[j].pass;
80✔
2075
                if (heap_args[j].pass)
80✔
2076
                    printf("\t" GREEN("thread #%zu returned pass = true") "\n", j + 1);
68✔
2077
                else
2078
                    printf("\t" RED("thread #%zu returned pass = false!") "\n", j + 1);
×
2079
            }
2080
        }
2081
    }
2082

2083
    _sir_safefree(&heap_args);
22✔
2084

2085
    sir_cleanup();
22✔
2086
    return print_result_and_return(pass);
22✔
2087
}
2088

2089
#if !defined(__WIN__)
2090
static void* threadrace_thread(void* arg) {
84✔
2091
#else /* __WIN__ */
2092
unsigned __stdcall threadrace_thread(void* arg) {
2093
#endif
2094
    pid_t threadid       = _sir_gettid();
84✔
2095
    thread_args* my_args = (thread_args*)arg;
72✔
2096

2097
    rmfile(my_args->log_file);
84✔
2098
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
84✔
2099

2100
    if (0 == id) {
84✔
2101
        bool unused = print_test_error(false, false);
8✔
2102
        SIR_UNUSED(unused);
2103
#if !defined(__WIN__)
2104
        return NULL;
8✔
2105
#else /* __WIN__ */
2106
        return 0;
2107
#endif
2108
    }
2109

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

2113
#if !defined(DUMA)
2114
# define NUM_ITERATIONS 1000
2115
#else
2116
# define NUM_ITERATIONS 100
2117
#endif
2118

2119
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
75,076✔
2120
        /* choose a random level, and colors. */
2121
        sir_textcolor fg = SIRTC_INVALID;
63,001✔
2122
        sir_textcolor bg = SIRTC_INVALID;
63,001✔
2123

2124
        if (n % 2 == 0) {
75,001✔
2125
            fg = SIRTC_CYAN;
31,501✔
2126
            bg = SIRTC_BLACK;
31,501✔
2127
            sir_debug("log message #%zu", n);
37,501✔
2128
        } else {
2129
            fg = SIRTC_BLACK;
31,500✔
2130
            bg = SIRTC_CYAN;
31,500✔
2131
            sir_info("log message #%zu", n);
37,500✔
2132
        }
2133

2134
        /* sometimes remove and re-add the log file, and set some options/styles.
2135
         * other times, just set different options/styles. */
2136
        uint32_t rnd = (uint32_t)(n + threadid);
75,001✔
2137
        if (getrand_bool((uint32_t)(rnd > 1 ? rnd : 1))) {
75,001✔
2138
            my_args->pass = print_test_error(sir_remfile(id), false);
37,772✔
2139
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
37,772✔
2140
                SIRL_ALL, SIRO_MSGONLY), false);
2141

2142
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
75,544✔
2143
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
37,772✔
2144
            my_args->pass = print_test_error(test, false);
37,772✔
2145

2146
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
37,772✔
2147
            my_args->pass = print_test_error(test, false);
37,772✔
2148
        } else {
2149
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
74,458✔
2150
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
37,229✔
2151
            my_args->pass = print_test_error(test, false);
37,229✔
2152

2153
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
37,229✔
2154
            my_args->pass = print_test_error(test, false);
37,227✔
2155

2156
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
37,226✔
2157
            my_args->pass = print_test_error(test, false);
37,229✔
2158
        }
2159

2160
        if (!my_args->pass)
75,001✔
2161
            break;
1✔
2162
    }
2163

2164
    my_args->pass = print_test_error(sir_remfile(id), false);
76✔
2165

2166
    rmfile(my_args->log_file);
76✔
2167

2168
#if !defined(__WIN__)
2169
    return NULL;
76✔
2170
#else /* __WIN__ */
2171
    return 0u;
2172
#endif
2173
}
2174

2175
/*
2176
bool sirtest_XXX(void) {
2177
    INIT(si, SIRL_ALL, 0, 0, 0);
2178
    bool pass = si_init;
2179

2180
    pass &= sir_cleanup();
2181
    return print_result_and_return(pass);
2182
}
2183
*/
2184

2185
/* ========================== end tests ========================== */
2186

2187
bool print_test_error(bool result, bool expected) {
264,326✔
2188
    char message[SIR_MAXERROR] = {0};
264,326✔
2189
    uint16_t code              = sir_geterror(message);
264,326✔
2190

2191
    if (!expected && !result && SIR_E_NOERROR != code)
264,305✔
2192
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
1,580✔
2193
    else if (expected && SIR_E_NOERROR != code)
262,725✔
2194
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
510✔
2195

2196
    return result;
264,300✔
2197
}
2198

2199
bool print_os_error(void) {
64✔
2200
    char message[SIR_MAXERROR] = {0};
64✔
2201
    uint16_t code              = sir_geterror(message);
64✔
2202
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
64✔
2203
    return false;
64✔
2204
}
2205

2206
bool filter_error(bool pass, uint16_t err) {
1,528✔
2207
    if (!pass) {
1,528✔
2208
        char message[SIR_MAXERROR] = {0};
938✔
2209
        uint16_t code              = sir_geterror(message);
938✔
2210
        if (code != err)
938✔
2211
            return false;
2✔
2212
    }
2213
    return true;
1,286✔
2214
}
2215

2216
uint32_t getrand(uint32_t upper_bound) {
116,754✔
2217
#if !defined(__WIN__) || defined(__EMBARCADEROC__)
2218
# if defined(__MACOS__) || defined(__BSD__)
2219
    if (upper_bound < 2u)
2220
        upper_bound = 2u;
2221
    return arc4random_uniform(upper_bound);
2222
# else
2223
#  if defined(__EMBARCADEROC__)
2224
    return (uint32_t)(random(upper_bound));
2225
#  else
2226
    return (uint32_t)(random() % upper_bound);
116,754✔
2227
#  endif
2228
# endif
2229
#else /* __WIN__ */
2230
    uint32_t ctx = 0;
2231
    if (0 != rand_s(&ctx))
2232
        ctx = (uint32_t)rand();
2233
    return ctx % upper_bound;
2234
#endif
2235
}
2236

2237
bool rmfile(const char* filename) {
1,113✔
2238
    bool removed = false;
966✔
2239

2240
    /* return true if leave_logs is true. */
2241
    if (leave_logs) {
1,113✔
2242
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
49✔
2243
            filename, _cl_arg_list[3].flag);
49✔
2244
        return true;
49✔
2245
    }
2246

2247
    /* return true if the file doesn't exist. */
2248
    struct stat st;
2249
    if (0 != stat(filename, &st)) {
1,064✔
2250
        if (ENOENT == errno)
566✔
2251
            return true;
455✔
2252

2253
        handle_os_error(true, "failed to stat %s!", filename);
45✔
2254
        return false;
45✔
2255
    }
2256

2257
    if (!_sir_deletefile(filename)) {
497✔
2258
        handle_os_error(false, "failed to delete %s!", filename);
×
2259
    } else {
2260
        printf("\t" DGRAY("deleted %s (%ld bytes)...") "\n", filename,
417✔
2261
            (long)st.st_size);
498✔
2262
    }
2263

2264
    return removed;
417✔
2265
}
2266

2267
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
240✔
2268
    if (strstr(filename, search)) {
240✔
2269
        char filepath[SIR_MAXPATH];
2270
        (void)snprintf(filepath, SIR_MAXPATH, "%s%s", path, filename);
50✔
2271

2272
        rmfile(filepath);
56✔
2273
        (*data)++;
56✔
2274
    }
2275
}
240✔
2276

2277
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
120✔
2278
    SIR_UNUSED(path);
2279
    if (strstr(filename, search))
120✔
2280
        (*data)++;
35✔
2281
}
120✔
2282

2283
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
63✔
2284
#if !defined(__WIN__)
2285
    DIR* d = opendir(path);
63✔
2286
    if (!d)
63✔
2287
        return print_os_error();
2✔
2288

2289
    rewinddir(d);
61✔
2290
    struct dirent* di = readdir(d);
61✔
2291
    if (!di) {
61✔
2292
        closedir(d);
×
2293
        return print_os_error();
×
2294
    }
2295

2296
    while (NULL != di) {
421✔
2297
        cb(search, path, di->d_name, data);
360✔
2298
        di = readdir(d);
360✔
2299
    }
2300

2301
    closedir(d);
61✔
2302
    d = NULL;
52✔
2303
#else /* __WIN__ */
2304
    WIN32_FIND_DATA finddata = {0};
2305
    char buf[SIR_MAXPATH]    = {0};
2306

2307
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2308

2309
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2310

2311
    if (INVALID_HANDLE_VALUE == enumerator)
2312
        return false;
2313

2314
    do {
2315
        cb(search, path, finddata.cFileName, data);
2316
    } while (FindNextFile(enumerator, &finddata) > 0);
2317

2318
    FindClose(enumerator);
2319
    enumerator = NULL;
2320
#endif
2321

2322
    return true;
61✔
2323
}
2324

2325
bool sirtimerstart(sir_timer* timer) {
51✔
2326
#if !defined(__WIN__) || defined(__ORANGEC__)
2327
    int gettime = clock_gettime(SIRTEST_CLOCK, &timer->ts);
51✔
2328
    if (0 != gettime) {
51✔
2329
        handle_os_error(true, "clock_gettime(%d) failed!", CLOCK_CAST SIRTEST_CLOCK);
2✔
2330
    }
2331

2332
    return 0 == gettime;
51✔
2333
#else /* __WIN__ */
2334
    GetSystemTimePreciseAsFileTime(&timer->ft);
2335
    return true;
2336
#endif
2337
}
2338

2339
float sirtimerelapsed(const sir_timer* timer) {
2,000,028✔
2340
#if !defined(__WIN__) || defined(__ORANGEC__)
2341
    struct timespec now;
2342
    if (0 == clock_gettime(SIRTEST_CLOCK, &now)) {
2,000,028✔
2343
        return (float)(((double)now.tv_sec * (double)1e3)
2,000,027✔
2344
            + ((double)now.tv_nsec / (double)1e6)
2,000,027✔
2345
            - ((double)timer->ts.tv_sec * (double)1e3)
2,000,027✔
2346
            + ((double)timer->ts.tv_nsec / (double)1e6));
2,000,027✔
2347
    } else {
2348
        handle_os_error(true, "clock_gettime(%d) failed!", CLOCK_CAST SIRTEST_CLOCK);
1✔
2349
    }
2350
    return 0.0f;
1✔
2351
#else /* __WIN__ */
2352
    FILETIME now;
2353
    GetSystemTimePreciseAsFileTime(&now);
2354
    ULARGE_INTEGER start   = {0};
2355
    start.LowPart          = timer->ft.dwLowDateTime;
2356
    start.HighPart         = timer->ft.dwHighDateTime;
2357
    ULARGE_INTEGER n100sec = {0};
2358
    n100sec.LowPart        = now.dwLowDateTime;
2359
    n100sec.HighPart       = now.dwHighDateTime;
2360
    return (float)((n100sec.QuadPart - start.QuadPart) / 1e4);
2361
#endif
2362
}
2363

2364
long sirtimergetres(void) {
1✔
2365
    long retval = 0;
1✔
2366
#if !defined(__WIN__)
2367
    struct timespec res;
2368
    if (0 == clock_getres(SIRTEST_CLOCK, &res)) {
1✔
2369
        retval = res.tv_nsec;
1✔
2370
    } else {
2371
        handle_os_error(true, "clock_getres(%d) failed!", CLOCK_CAST SIRTEST_CLOCK); // GCOVR_EXCL_LINE
2372
    }
2373
#else /* __WIN__ */
2374
    retval = 100;
2375
#endif
2376
    return retval;
1✔
2377
}
2378

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

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

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

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

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

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

2411
bool mark_test_to_run(const char* name) {
3✔
2412
    bool found = false;
3✔
2413
    for (size_t t = 0; t < _sir_countof(sir_tests); t++) {
46✔
2414
        if (_sir_strsame(name, sir_tests[t].name,
45✔
2415
            strnlen(sir_tests[t].name, SIR_MAXTESTNAME))) {
2416
            found = sir_tests[t].run = true;
2✔
2417
            break;
2✔
2418
        }
2419
    }
2420

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

2424
    return found;
3✔
2425
}
2426

2427
void print_usage_info(void) {
4✔
2428
    size_t longest = 0;
4✔
2429
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2430
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2431
        if (len > longest)
28✔
2432
            longest = len;
8✔
2433
    }
2434

2435
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2436

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

2440
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2441
        if (len < longest)
28✔
2442
            for (size_t n = len; n < longest; n++)
156✔
2443
                fprintf(stderr, " ");
132✔
2444

2445
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2446
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2447
            _cl_arg_list[i].desc);
28✔
2448
    }
2449

2450
    fprintf(stderr, "\n");
4✔
2451
}
4✔
2452

2453
void print_test_list(void) {
1✔
2454
    size_t longest = 0;
1✔
2455
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
32✔
2456
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2457
        if (len > longest)
31✔
2458
            longest = len;
3✔
2459
    }
2460

2461
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2462

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

2466
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2467
        if (len < longest)
31✔
2468
            for (size_t n = len; n < longest; n++)
270✔
2469
                printf(" ");
240✔
2470

2471
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
31✔
2472
            printf("\n");
16✔
2473
    }
2474

2475
    printf("\n");
1✔
2476
}
1✔
2477

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