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

aremmell / libsir / 98

10 Aug 2023 02:46AM UTC coverage: 94.832% (+0.03%) from 94.804%
98

Pull #213

gitlab-ci

johnsonjh
Adjust formatting

Signed-off-by: Jeffrey H. Johnson <trnsz@pobox.com>
Pull Request #213: Support Embarcadero C++ for Windows

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

3046 of 3212 relevant lines covered (94.83%)

1607987.79 hits per line

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

97.46
/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.2
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
bool leave_logs = false;
64

65
int main(int argc, char** argv) {
31✔
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) {
31✔
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;
27✔
114
    bool only     = false;
27✔
115
    size_t to_run = 0;
27✔
116

117
    for (int n = 1; n < argc; n++) {
34✔
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 (0 == 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);
24✔
168
    size_t tgt_tests = (only ? to_run : _sir_countof(sir_tests) - first);
24✔
169
    size_t passed    = 0;
21✔
170
    size_t ran       = 0;
21✔
171
    sir_timer timer  = {0};
24✔
172

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

178
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
746✔
179
        if (only && !sir_tests[n].run) {
722✔
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);
662✔
185

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

190
        ran++;
572✔
191

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

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

198
    if (passed == tgt_tests) {
24✔
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: ")
14✔
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);
14✔
206

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

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

215
    if (wait) {
24✔
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;
24✔
223
}
224

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

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

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

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

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

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

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

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

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

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

260
        if (0 != fid)
22✔
261
            pass &= sir_remfile(fid);
19✔
262

263
        rmfile(logfilename);
22✔
264
    }
265

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

332
    if (pass)
22✔
333
        print_expected_error();
18✔
334

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

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

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

344
    do {
1,051✔
345
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,232✔
346
        bool skip  = false;
1,070✔
347

348
        for (size_t n = 0; n < SIR_MAXFILES; n++)
11,768✔
349
            if (removeorder[n] == rnd) {
11,416✔
350
                skip = true;
766✔
351
                break;
766✔
352
            }
353

354
        if (skip)
1,232✔
355
            continue;
880✔
356

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

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

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

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

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

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

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

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

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

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

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

395
bool sirtest_failfilebadpermission(void) {
22✔
396
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
397
    bool pass = si_init;
19✔
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 &= 0 == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY);
22✔
415

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

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

423
bool sirtest_faildupefile(void) {
22✔
424
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
425
    bool pass = si_init;
19✔
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);
22✔
439

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

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

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

449
    if (pass)
22✔
450
        print_expected_error();
19✔
451

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

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

458
    if (pass)
22✔
459
        print_expected_error();
19✔
460

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

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

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

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

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

476
    if (pass)
22✔
477
        print_expected_error();
18✔
478

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

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

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

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

496
    sirfileid invalidid = 9999999;
19✔
497
    pass &= !sir_remfile(invalidid);
22✔
498

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

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

506
bool sirtest_rollandarchivefile(void) {
23✔
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;
20✔
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};
23✔
515
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
23✔
516

517
    unsigned delcount = 0;
23✔
518
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
23✔
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 > 0)
21✔
525
        printf("\tfound and removed %u log file(s)\n", delcount);
×
526

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

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

533
    if (0 != fseek(f, fillsize, SEEK_SET)) {
21✔
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)) {
20✔
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);
19✔
546

547
    INIT(si, 0, 0, 0, 0);
19✔
548
    bool pass = si_init;
16✔
549

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

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

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

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

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

576
    pass &= sir_remfile(fileid);
19✔
577

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

584
    if (delcount > 0)
19✔
585
        printf("\tfound and removed %u log file(s)\n", delcount);
16✔
586

587
    sir_cleanup();
19✔
588
    return print_result_and_return(pass);
19✔
589
}
590

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

594
    if (pass)
22✔
595
        print_expected_error();
22✔
596

597
    return print_result_and_return(pass);
22✔
598
}
599

600
bool sirtest_failinittwice(void) {
22✔
601
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
602
    bool pass = si_init;
19✔
603

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

607
    if (pass)
22✔
608
        print_expected_error();
22✔
609

610
    sir_cleanup();
22✔
611
    return print_result_and_return(pass);
22✔
612
}
613

614
bool sirtest_failinvalidinitdata(void) {
22✔
615
    sirinit si;
616

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

620
    printf("\tcalling sir_init with invalid data...\n");
19✔
621
    bool pass = !sir_init(&si);
22✔
622

623
    if (pass)
22✔
624
        print_expected_error();
22✔
625

626
    sir_cleanup();
22✔
627
    return print_result_and_return(pass);
22✔
628
}
629

630
bool sirtest_initcleanupinit(void) {
22✔
631
    INIT(si1, SIRL_ALL, 0, 0, 0);
22✔
632
    bool pass = si1_init;
19✔
633

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

637
    INIT(si2, SIRL_ALL, 0, 0, 0);
22✔
638
    pass &= si2_init;
19✔
639

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

643
    return print_result_and_return(pass);
22✔
644
}
645

646
bool sirtest_initmakeinit(void) {
22✔
647
    bool pass = true;
19✔
648

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

655
    return print_result_and_return(pass);
22✔
656
}
657

658
bool sirtest_failaftercleanup(void) {
22✔
659
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
660
    bool pass = si_init;
19✔
661

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

665
    if (pass)
22✔
666
        print_expected_error();
22✔
667

668
    return print_result_and_return(pass);
22✔
669
}
670

671
bool sirtest_errorsanity(void) {
22✔
672
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
673
    bool pass = si_init;
19✔
674

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

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

714
    sir_cleanup();
22✔
715
    return print_result_and_return(pass);
22✔
716
}
717

718
bool sirtest_textstylesanity(void) {
22✔
719
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
720
    bool pass = si_init;
19✔
721

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

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

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

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

738
    printf("\t" WHITEB("--- override defaults ---") "\n");
19✔
739
    pass &= sir_resettextstyles();
22✔
740

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

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

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

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

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

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

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

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

774
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
19✔
775
    pass &= sir_resettextstyles();
22✔
776

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

787
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
19✔
788
    pass &= sir_setcolormode(SIRCM_256);
22✔
789

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

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

800
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
19✔
801
    pass &= sir_setcolormode(SIRCM_RGB);
22✔
802

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

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

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

829
    sir_cleanup();
22✔
830

831
    return print_result_and_return(pass);
22✔
832
}
833

834
bool sirtest_optionssanity(void) {
22✔
835
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
836
    bool pass = si_init;
19✔
837

838
    static const size_t iterations = 10;
839

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

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

875
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
19✔
876
    uint32_t last_count = SIR_NUMOPTIONS;
19✔
877
    for (size_t n = 0; n < iterations; n++) {
242✔
878
        sir_options opts    = 0;
190✔
879
        uint32_t rand_count = 0;
190✔
880
        size_t last_idx     = 0;
190✔
881

882
        do {
883
            rand_count = getrand(SIR_NUMOPTIONS);
316✔
884
        } while (rand_count == last_count || rand_count <= 1);
316✔
885

886
        last_count = rand_count;
190✔
887

888
        for (size_t i = 0; i < rand_count; i++) {
1,158✔
889
            size_t rand_idx = 0;
809✔
890
            size_t tries    = 0;
809✔
891

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

897
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,580✔
898

899
            last_idx = rand_idx;
809✔
900
            opts |= option_arr[rand_idx];
938✔
901
        }
902

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

909
    printf("\t" WHITEB("--- invalid values ---") "\n");
19✔
910

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

916
    /* gaps inbetween valid options. */
917
    invalid = 0x0001ff00 & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
19✔
918
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
919
    pass &= !_sir_validopts(invalid);
22✔
920
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00: %08"PRIx32) "\n", invalid);
19✔
921

922
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
923
    for (sir_option o = 0x00008f00; o < SIRO_NOHDR; o += 0x1000) {
198✔
924
        pass &= !_sir_validopts(o);
176✔
925
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
152✔
926
    }
927

928
    /* greater than SIRO_NOHDR. */
929
    invalid = (0xFFFF0000 & ~SIRO_NOHDR);
19✔
930
    pass &= !_sir_validopts(invalid);
22✔
931
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
19✔
932

933
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
22✔
934

935
    sir_cleanup();
22✔
936
    return print_result_and_return(pass);
22✔
937
}
938

939
bool sirtest_levelssanity(void) {
22✔
940
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
941
    bool pass = si_init;
19✔
942

943
    static const size_t iterations = 10;
944

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

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

982
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
19✔
983
    uint32_t last_count = SIR_NUMLEVELS;
19✔
984
    for (size_t n = 0; n < iterations; n++) {
242✔
985
        sir_levels levels   = 0;
190✔
986
        uint32_t rand_count = 0;
190✔
987
        size_t last_idx     = 0;
190✔
988

989
        do {
990
            rand_count = getrand(SIR_NUMLEVELS);
430✔
991
        } while (rand_count == last_count || rand_count <= 1);
430✔
992

993
        last_count = rand_count;
190✔
994

995
        for (size_t i = 0; i < rand_count; i++) {
1,298✔
996
            size_t rand_idx = 0;
931✔
997
            size_t tries    = 0;
931✔
998

999
            do {
1000
                if (++tries > SIR_NUMLEVELS - 2)
1,764✔
1001
                    break;
30✔
1002
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,728✔
1003
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,728✔
1004

1005
            last_idx = rand_idx;
931✔
1006
            levels |= levels_arr[rand_idx];
1,078✔
1007
        }
1008

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

1015
    printf("\t" WHITEB("--- invalid values ---") "\n");
19✔
1016

1017
    /* greater than SIRL_ALL. */
1018
    sir_levels invalid = (0xffff & ~SIRL_ALL);
19✔
1019
    pass &= !_sir_validlevels(invalid);
22✔
1020
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
19✔
1021

1022
    /* individual invalid level. */
1023
    sir_level invalid2 = 0x1337;
19✔
1024
    pass &= !_sir_validlevel(invalid2);
22✔
1025
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx32) "\n", invalid2);
19✔
1026

1027
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
22✔
1028

1029
    sir_cleanup();
22✔
1030
    return print_result_and_return(pass);
22✔
1031
}
1032

1033
bool sirtest_mutexsanity(void) {
22✔
1034
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
1035
    bool pass = si_init;
19✔
1036

1037
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
19✔
1038
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
19✔
1039

1040
    sir_mutex m1 = SIR_MUTEX_INIT;
22✔
1041
    pass &= _sir_mutexcreate(&m1);
22✔
1042

1043
    print_test_error(pass, pass);
22✔
1044

1045
    if (pass) {
22✔
1046
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
19✔
1047
        pass &= _sir_mutexlock(&m1);
22✔
1048

1049
        print_test_error(pass, pass);
22✔
1050

1051
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
19✔
1052
        pass &= _sir_mutexunlock(&m1);
22✔
1053

1054
        print_test_error(pass, pass);
22✔
1055

1056
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
19✔
1057
        pass &= _sir_mutextrylock(&m1);
22✔
1058

1059
        print_test_error(pass, pass);
22✔
1060

1061
        printf(INDENT_ITEM WHITE("unlocking...") "\n");
19✔
1062
        pass &= _sir_mutexunlock(&m1);
22✔
1063

1064
        print_test_error(pass, pass);
22✔
1065

1066
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
19✔
1067
        pass &= _sir_mutexdestroy(&m1);
22✔
1068

1069
        print_test_error(pass, pass);
22✔
1070

1071
    }
1072
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
22✔
1073

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

1087
    pass &= sir_cleanup();
22✔
1088
    return print_result_and_return(pass);
22✔
1089
}
1090

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

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

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

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

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

1111
        sir_timer printftimer = {0};
1✔
1112
        sirtimerstart(&printftimer);
1✔
1113

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

1118
        printfelapsed = sirtimerelapsed(&printftimer);
1✔
1119

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

1122
        sir_timer stdiotimer = {0};
1✔
1123
        sirtimerstart(&stdiotimer);
1✔
1124

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

1129
        stdioelapsed = sirtimerelapsed(&stdiotimer);
1✔
1130

1131
        sir_cleanup();
1✔
1132

1133
        INIT(si2, 0, 0, 0, 0);
1✔
1134
        pass &= si2_init;
1✔
1135

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

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

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

1145
            sir_timer filetimer = {0};
1✔
1146
            sirtimerstart(&filetimer);
1✔
1147

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

1151
            fileelapsed = sirtimerelapsed(&filetimer);
1✔
1152

1153
            pass &= sir_remfile(logid);
1✔
1154
        }
1155

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

1172
    unsigned deleted = 0;
1✔
1173
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
1✔
1174

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

1178
    sir_cleanup();
1✔
1179
    return print_result_and_return(pass);
1✔
1180
}
1181

1182
bool sirtest_updatesanity(void) {
22✔
1183
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
22✔
1184
    bool pass = si_init;
19✔
1185

1186
#define UPDATE_SANITY_ARRSIZE 10
1187

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

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

1205
    rmfile(logfile);
22✔
1206
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
22✔
1207
    pass &= 0 != id1;
22✔
1208

1209
    for (int i = 0; i < 10; i++) {
203✔
1210
        if (!pass)
185✔
1211
            break;
4✔
1212

1213
        /* reset to defaults*/
1214
        pass &= sir_stdoutlevels(SIRL_DEFAULT);
181✔
1215
        pass &= sir_stderrlevels(SIRL_DEFAULT);
181✔
1216
        pass &= sir_stdoutopts(SIRO_DEFAULT);
181✔
1217
        pass &= sir_stderropts(SIRO_DEFAULT);
181✔
1218

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

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

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

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

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

1254
    if (pass) {
22✔
1255
        /* restore to default config and run again */
1256
        sir_stdoutlevels(SIRL_DEFAULT);
18✔
1257
        sir_stderrlevels(SIRL_DEFAULT);
18✔
1258
        sir_stdoutopts(SIRO_DEFAULT);
18✔
1259
        sir_stderropts(SIRO_DEFAULT);
18✔
1260

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

1271
    pass &= sir_remfile(id1);
22✔
1272
    rmfile(logfile);
22✔
1273
    sir_cleanup();
22✔
1274

1275
    return print_result_and_return(pass);
22✔
1276
}
1277

1278
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1279
static bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
16✔
1280
    bool pass             = true;
13✔
1281
    static const int runs = 5;
1282

1283
    /* repeat initializing, opening, logging, closing, cleaning up n times. */
1284
    sir_timer timer = {0};
16✔
1285
    pass &= sirtimerstart(&timer);
16✔
1286

1287
    printf("\trunning %d passes of random configs (system logger: '%s', "
13✔
1288
           "identity: '%s', category: '%s')...\n",
1289
        runs, sl_name, identity, category);
1290

1291
    for (int i = 0; i < runs; i++) {
96✔
1292
        /* randomly skip setting process name, identity/category to thoroughly
1293
         * test fallback routines; randomly update the config mid-run. */
1294
        bool set_procname = getrand_bool(1+(uint32_t)sirtimerelapsed(&timer));
80✔
1295
        bool set_identity = getrand_bool(1+(uint32_t)sirtimerelapsed(&timer));
80✔
1296
        bool set_category = getrand_bool(1+(uint32_t)sirtimerelapsed(&timer));
80✔
1297
        bool do_update    = getrand_bool(1+(uint32_t)sirtimerelapsed(&timer));
80✔
1298

1299
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
80✔
1300
            set_procname, set_identity, set_category, do_update);
1301

1302
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
88✔
1303
        si.d_syslog.opts   = SIRO_DEFAULT;
80✔
1304
        si.d_syslog.levels = SIRL_DEFAULT;
80✔
1305

1306
        if (set_identity)
80✔
1307
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
44✔
1308

1309
        if (set_category)
80✔
1310
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
46✔
1311

1312
        si_init = sir_init(&si); //-V519
80✔
1313
        pass &= si_init;
65✔
1314

1315
        if (do_update)
80✔
1316
            pass &= sir_sysloglevels(SIRL_ALL);
36✔
1317

1318
        pass &= sir_debug("%d/%d: this debug message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1319
        pass &= sir_info("%d/%d: this info message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1320

1321
        pass &= sir_notice("%d/%d: this notice message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1322
        pass &= sir_warn("%d/%d: this warning message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1323
        pass &= sir_error("%d/%d: this error message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1324

1325
        if (set_identity) {
80✔
1326
            pass &= sir_syslogid("my test ID");
44✔
1327
            pass &= sir_syslogid("my test ID"); /* test deduping. */
44✔
1328
        }
1329

1330
        if (set_category) {
80✔
1331
            pass &= sir_syslogcat("my test category");
46✔
1332
            pass &= sir_syslogcat("my test category"); /* test deduping. */
46✔
1333
        }
1334

1335
        if (do_update)
80✔
1336
            pass &= sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID));
36✔
1337

1338
        pass &= sir_crit("%d/%d: this critical message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1339
        pass &= sir_alert("%d/%d: this alert message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1340
        pass &= sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i + 1, runs, sl_name);
80✔
1341

1342
# if defined(SIR_OS_LOG_ENABLED)
1343
#  if defined(__APPLE__) && !defined(__INTEL_COMPILER)
1344
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1345
            printf("\ttesting os_log activity feature...\n");
1346

1347
            /* also test activity grouping in Console. there's only one way to validate
1348
             * this and that's by manually viewing the log. */
1349
             os_activity_t parent = os_activity_create("flying to the moon",
1350
                OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DETACHED);
1351

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

1359
        sir_cleanup();
80✔
1360

1361
        if (!pass)
80✔
1362
            break;
×
1363
    }
1364

1365
    return print_result_and_return(pass);
16✔
1366
}
1367
#endif
1368

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

1377
    SIR_UNUSED(sl_name);
1378

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

1381
    /* init should just ignore the syslog settings. */
1382
    pass &= sir_init(&si);
6✔
1383

1384
    /* these calls should all fail. */
1385
    printf("\tsetting levels...\n");
6✔
1386
    pass &= !sir_sysloglevels(SIRL_ALL);
6✔
1387

1388
    if (pass)
6✔
1389
        print_expected_error();
6✔
1390

1391
    printf("\tsetting options...\n");
6✔
1392
    pass &= !sir_syslogopts(SIRO_DEFAULT);
6✔
1393

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

1397
    printf("\tsetting identity...\n");
6✔
1398
    pass &= !sir_syslogid(identity);
6✔
1399

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

1403
    printf("\tsetting category...\n");
6✔
1404
    pass &= !sir_syslogcat(category);
6✔
1405

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

1409
    pass &= sir_cleanup();
6✔
1410
    return print_result_and_return(pass);
6✔
1411
}
1412
#endif
1413

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

1429
bool sirtest_os_log(void) {
22✔
1430
#if !defined(SIR_OS_LOG_ENABLED)
1431
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
19✔
1432
    return true;
22✔
1433
#else
1434
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1435
    return print_result_and_return(pass);
1436
#endif
1437
}
1438

1439
char *sirtest_get_wineversion(void) {
44✔
1440
#if !defined(__WIN__)
1441
    return NULL;
44✔
1442
#else /* __WIN__ */
1443
    typedef char* (__stdcall *get_wine_ver_proc)(void);
1444
    static get_wine_ver_proc _p_wine_get_version = NULL;
1445

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

1459
bool sirtest_filesystem(void) {
22✔
1460
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
1461
    bool pass = si_init;
19✔
1462

1463
    /* Wine version */
1464
    printf("\tRunning under Wine: %s\n",
22✔
1465
            sirtest_get_wineversion() ? sirtest_get_wineversion() : "no"); //-V547
22✔
1466

1467
    /* current working directory. */
1468
    char* cwd = _sir_getcwd();
22✔
1469
    pass &= NULL != cwd;
22✔
1470
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
22✔
1471

1472
    if (NULL != cwd) {
22✔
1473
        /* path to this binary file. */
1474
        char* filename = _sir_getappfilename();
22✔
1475
        pass &= NULL != filename;
22✔
1476
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
22✔
1477

1478
        if (NULL != filename) {
22✔
1479
            /* _sir_get[base|dir]name() can potentially modify filename,
1480
             * so make a copy for each call. */
1481
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
19✔
1482
            pass &= NULL != filename2;
19✔
1483

1484
            if (NULL != filename2) {
19✔
1485
                /* filename, stripped of directory component(s). */
1486
                char* _basename = _sir_getbasename(filename2);
18✔
1487
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
18✔
1488

1489
                if (!_basename) {
18✔
1490
                    pass = false;
×
1491
                } else {
1492
                    /* the last strlen(_basename) chars of filename should match. */
1493
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
18✔
1494
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
18✔
1495
                    size_t n      = 0;
15✔
1496

1497
                    while (n < len) {
162✔
1498
                        if (filename[offset++] != _basename[n++]) {
144✔
1499
                            pass = false;
×
1500
                            break;
×
1501
                        }
1502
                    };
1503
                }
1504
            }
1505

1506
            /* directory this binary file resides in. */
1507
            char* appdir = _sir_getappdir();
19✔
1508
            pass &= NULL != appdir;
19✔
1509
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
19✔
1510

1511
            /* _sir_get[base|dir]name can potentially modify filename,
1512
             * so make a copy for each call. */
1513
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
19✔
1514
            pass &= NULL != filename3;
19✔
1515

1516
            if (NULL != appdir && NULL != filename3) {
19✔
1517
                /* should yield the same result as _sir_getappdir(). */
1518
                char* _dirname = _sir_getdirname(filename3);
18✔
1519
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
18✔
1520

1521
                pass &= 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH));
18✔
1522
                pass &= NULL != _dirname &&
36✔
1523
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH));
18✔
1524
            }
1525

1526
            _sir_safefree(&appdir);
19✔
1527
            _sir_safefree(&filename);
19✔
1528
            _sir_safefree(&filename2);
19✔
1529
            _sir_safefree(&filename3);
19✔
1530
        }
1531

1532
        _sir_safefree(&cwd);
22✔
1533
    }
1534

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

1556
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
154✔
1557
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
132✔
1558
        if (NULL != tmp) {
132✔
1559
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
126✔
1560
                tmp, _sir_getdirname(tmp));
1561
            _sir_safefree(&tmp);
126✔
1562
        }
1563
    }
1564

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

1582
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
132✔
1583
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
110✔
1584
        if (NULL != tmp) {
110✔
1585
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
105✔
1586
                tmp, _sir_getbasename(tmp));
1587
            _sir_safefree(&tmp);
105✔
1588
        }
1589
    }
1590

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

1613
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
198✔
1614
        bool relative = false;
176✔
1615
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
176✔
1616

1617
        if (relative == abs_or_rel_paths[n].abs) {
176✔
1618
            pass = false;
×
1619
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1620
                relative ? "true" : "false");
×
1621
        } else {
1622
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
176✔
1623
                relative ? "true" : "false");
152✔
1624
        }
1625

1626
        pass &= ret;
152✔
1627
        if (!ret) {
176✔
1628
            bool unused = print_test_error(false, false);
×
1629
            SIR_UNUSED(unused);
1630
        }
1631
    }
1632

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

1662
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
242✔
1663
        bool exists = false;
220✔
1664
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
220✔
1665

1666
        if (exists != real_or_not[n].exists) {
220✔
1667
            pass = false;
23✔
1668
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
23✔
1669
                exists ? "true" : "false");
23✔
1670
        } else {
1671
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
197✔
1672
                exists ? "true" : "false");
167✔
1673
        }
1674

1675
        pass &= ret;
190✔
1676
        if (!ret) {
220✔
1677
            bool unused = print_test_error(false, false);
28✔
1678
            SIR_UNUSED(unused);
1679
        }
1680
    }
1681

1682
    /* checking file descriptors. */
1683
    static int bad_fds[] = {
1684
        0,
1685
        1,
1686
        2,
1687
        1234
1688
    };
1689
    if (sirtest_get_wineversion()) { //-V547
22✔
1690
        bad_fds[3] = 0;
×
1691
    }
1692

1693
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
110✔
1694
        if (_sir_validfd(bad_fds[n])) {
88✔
1695
            pass = false;
×
1696
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1697
        } else {
1698
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
88✔
1699
        }
1700
    }
1701

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

1717
    _sir_safefclose(&f);
22✔
1718

1719
    sir_cleanup();
22✔
1720
    return print_result_and_return(pass);
22✔
1721
}
1722

1723
bool sirtest_squelchspam(void) {
22✔
1724
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
1725
    bool pass = si_init;
19✔
1726

1727
    static const size_t alternate   = 50;
1728
    static const size_t sequence[3] = {
1729
        1000, /* non-repeating messages. */
1730
        1000, /* repeating messages. */
1731
        1000  /* alternating repeating and non-repeating messages. */
1732
    };
1733

1734
    sir_timer timer;
1735
    sirtimerstart(&timer);
22✔
1736

1737
    printf("\t" BLUE("%zu non-repeating messages...") "\n", sequence[0]);
19✔
1738

1739
    for (size_t n = 0, ascii_idx = 33; n < sequence[0]; n++, ascii_idx++) {
22,022✔
1740
        pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
41,000✔
1741
            (char)ascii_idx + 1);
22,000✔
1742

1743
        if (ascii_idx == 125)
22,000✔
1744
            ascii_idx = 33;
190✔
1745
    }
1746

1747
    printf("\t" BLUE("%zu repeating messages...") "\n", sequence[1]);
19✔
1748

1749
    for (size_t n = 0; n < sequence[1]; n++) {
22,022✔
1750
        bool ret = sir_debug("a repeating message");
22,000✔
1751

1752
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
22,000✔
1753
            pass &= !ret;
21,912✔
1754
        else
1755
            pass &= ret;
76✔
1756
    }
1757

1758
    printf("\t" BLUE("%zu alternating repeating and non-repeating messages...")
19✔
1759
           "\n", sequence[2]);
19✔
1760

1761
    bool repeating   = false;
19✔
1762
    size_t counter   = 0;
19✔
1763
    size_t repeat_id = 0;
19✔
1764
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
22,022✔
1765
        if (!repeating) {
22,000✔
1766
            pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
11,022✔
1767
                (char)ascii_idx + 1);
11,022✔
1768
        } else {
1769
            bool ret = sir_debug("%zu a repeating message", repeat_id);
10,978✔
1770

1771
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
10,978✔
1772
                pass &= !ret;
10,098✔
1773
            else
1774
                pass &= ret;
760✔
1775
        }
1776

1777
        if (counter == alternate) {
22,000✔
1778
            repeating = !repeating;
418✔
1779
            counter = 0;
361✔
1780
            repeat_id++;
418✔
1781
        }
1782

1783
        if (ascii_idx == 125)
22,000✔
1784
            ascii_idx = 33;
190✔
1785
    }
1786

1787
    sir_cleanup();
22✔
1788
    return print_result_and_return(pass);
22✔
1789
}
1790

1791
bool sirtest_pluginloader(void) {
22✔
1792
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
1793
    bool pass = si_init;
19✔
1794

1795
#if !defined(__WIN__)
1796
# define PLUGIN_EXT "so"
1797
#else
1798
# define PLUGIN_EXT "dll"
1799
#endif
1800

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

1810
#if defined(SIR_NO_PLUGINS)
1811
    SIR_UNUSED(plugin2);
1812
    SIR_UNUSED(plugin3);
1813
    SIR_UNUSED(plugin4);
1814
    SIR_UNUSED(plugin5);
1815
    SIR_UNUSED(plugin6);
1816
    SIR_UNUSED(plugin7);
1817
    SIR_UNUSED(plugin8);
1818

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

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

1826
    if (pass)
1✔
1827
        print_expected_error();
1✔
1828

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

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

1841
    print_test_error(pass, pass);
21✔
1842

1843
    pass &= sir_info("this message will be dispatched to the plugin.");
21✔
1844
    pass &= sir_warn("this message will *not* be dispatched to the plugin.");
21✔
1845

1846
    /* re-loading the same plugin should fail. */
1847
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
21✔
1848
    sirpluginid badid = sir_loadplugin(plugin1);
21✔
1849
    pass &= 0 == badid;
21✔
1850

1851
    print_test_error(pass, pass);
21✔
1852

1853
    /* the following are all invalid or misbehaved, and should all fail. */
1854
    printf("\tloading bad plugin: '%s'...\n", plugin2);
21✔
1855
    badid = sir_loadplugin(plugin2);
21✔
1856
    pass &= 0 == badid;
21✔
1857

1858
    print_test_error(pass, pass);
21✔
1859

1860
    printf("\tloading bad plugin: '%s'...\n", plugin3);
21✔
1861
    badid = sir_loadplugin(plugin3);
21✔
1862
    pass &= 0 == badid;
21✔
1863

1864
    print_test_error(pass, pass);
21✔
1865

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

1870
    print_test_error(pass, pass);
21✔
1871

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

1876
    print_test_error(pass, pass);
21✔
1877

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

1882
    print_test_error(pass, pass);
21✔
1883

1884
    printf("\tloading bad plugin: '%s'...\n", plugin7);
21✔
1885
    badid = sir_loadplugin(plugin7);
21✔
1886
    pass &= 0 != badid; /* this one should load, just return false from write */
21✔
1887

1888
    pass &= !sir_info("this should fail, because one plugin failed to process"
21✔
1889
                      " the message.");
1890

1891
    print_test_error(pass, pass);
21✔
1892

1893
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
21✔
1894
    badid = sir_loadplugin(plugin8);
21✔
1895
    pass &= 0 == badid;
21✔
1896

1897
    print_test_error(pass, pass);
21✔
1898

1899
    /* unload the good plugin manually. */
1900
    printf("\tunloading good plugin: '%s'...\n", plugin1);
21✔
1901
    pass &= sir_unloadplugin(id);
21✔
1902

1903
    print_test_error(pass, pass);
21✔
1904

1905
    /* try to unload the plugin again. */
1906
    printf("\nunloading already unloaded plugin '%s'...\n", plugin1);
21✔
1907
    pass &= !sir_unloadplugin(id);
21✔
1908

1909
    print_test_error(pass, pass);
21✔
1910

1911
    /* test bad paths. */
1912
    printf("\ntrying to load plugin with NULL path...\n");
18✔
1913
    badid = sir_loadplugin(NULL);
21✔
1914
    pass &= 0 == badid;
21✔
1915

1916
    print_test_error(pass, pass);
21✔
1917
#endif
1918
    pass &= sir_cleanup();
22✔
1919
    return print_result_and_return(pass);
22✔
1920
}
1921

1922
bool sirtest_getversioninfo(void) {
22✔
1923
    INIT(si, SIRL_ALL, 0, 0, 0);
22✔
1924
    bool pass = si_init;
19✔
1925

1926
    printf("\tchecking version retrieval functions...\n");
19✔
1927

1928
    const char* str = sir_getversionstring();
22✔
1929
    pass &= _sir_validstrnofail(str);
22✔
1930

1931
    printf("\tversion as string: '%s'\n", _SIR_PRNSTR(str));
22✔
1932

1933
    uint32_t hex = sir_getversionhex();
22✔
1934
    pass &= 0 != hex;
22✔
1935

1936
    printf("\tversion as hex: 0x%08"PRIx32"\n", hex);
19✔
1937

1938
    bool prerel = sir_isprerelease();
22✔
1939
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
22✔
1940

1941
    pass &= sir_cleanup();
22✔
1942
    return print_result_and_return(pass);
22✔
1943
}
1944

1945
enum {
1946
    NUM_THREADS = 4
1947
};
1948

1949
static bool threadpool_pseudojob(void* arg) {
160✔
1950
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
160✔
1951
#if !defined(__WIN__)
1952
    sleep(1);
160✔
1953
#else
1954
    Sleep(1000);
1955
#endif
1956
    return true;
155✔
1957
}
1958

1959
bool sirtest_threadpool(void) {
22✔
1960
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
22✔
1961
    bool pass = si_init;
19✔
1962

1963
    static const size_t num_jobs = 30;
1964
    sir_threadpool* pool         = NULL;
22✔
1965

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

1981
#if !defined(__WIN__)
1982
        sleep(1);
20✔
1983
#else
1984
        Sleep(1000);
1985
#endif
1986

1987
        pass &= sir_info("destroying thread pool...");
20✔
1988
        pass &= _sir_threadpool_destroy(&pool);
20✔
1989
    }
1990

1991
    pass &= sir_cleanup();
22✔
1992
    return print_result_and_return(pass);
22✔
1993
}
1994

1995
#if !defined(__WIN__)
1996
static void* threadrace_thread(void* arg);
1997
#else /* __WIN__ */
1998
static unsigned threadrace_thread(void* arg);
1999
#endif
2000

2001
bool sirtest_threadrace(void) {
22✔
2002
#if !defined(__WIN__)
2003
    pthread_t thrds[NUM_THREADS] = {0};
22✔
2004
#else /* __WIN__ */
2005
    uintptr_t thrds[NUM_THREADS] = {0};
2006
#endif
2007

2008
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
22✔
2009
    bool pass           = si_init;
19✔
2010
    bool any_created    = false;
19✔
2011
    size_t last_created = 0;
19✔
2012

2013
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
22✔
2014
    pass &= NULL != heap_args;
22✔
2015
    if (!heap_args) {
22✔
2016
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2017
        return false;
1✔
2018
    }
2019

2020
    for (size_t n = 0; n < NUM_THREADS; n++) {
101✔
2021
        if (!pass)
81✔
2022
            break;
×
2023

2024
        heap_args[n].pass = true;
81✔
2025
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
81✔
2026
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2027

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

2042
        last_created = n;
68✔
2043
        any_created  = true;
68✔
2044
    }
2045

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

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

2078
    _sir_safefree(&heap_args);
21✔
2079

2080
    sir_cleanup();
21✔
2081
    return print_result_and_return(pass);
21✔
2082
}
2083

2084
#if !defined(__WIN__)
2085
static void* threadrace_thread(void* arg) {
80✔
2086
#else /* __WIN__ */
2087
unsigned threadrace_thread(void* arg) {
2088
#endif
2089
    pid_t threadid       = _sir_gettid();
80✔
2090
    thread_args* my_args = (thread_args*)arg;
68✔
2091

2092
    rmfile(my_args->log_file);
80✔
2093
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
80✔
2094

2095
    if (0 == id) {
80✔
2096
        bool unused = print_test_error(false, false);
8✔
2097
        SIR_UNUSED(unused);
2098
#if !defined(__WIN__)
2099
        return NULL;
8✔
2100
#else /* __WIN__ */
2101
        return 0;
2102
#endif
2103
    }
2104

2105
    printf("\thi, i'm thread (id: %d), logging to: '%s'...\n",
60✔
2106
            PID_CAST threadid, my_args->log_file);
60✔
2107

2108
#if !defined(DUMA)
2109
# define NUM_ITERATIONS 1000
2110
#else
2111
# define NUM_ITERATIONS 100
2112
#endif
2113

2114
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
72,072✔
2115
        /* choose a random level, and colors. */
2116
        sir_textcolor fg = SIRTC_INVALID;
60,000✔
2117
        sir_textcolor bg = SIRTC_INVALID;
60,000✔
2118

2119
        if (n % 2 == 0) {
72,000✔
2120
            fg = SIRTC_CYAN;
30,000✔
2121
            bg = SIRTC_BLACK;
30,000✔
2122
            sir_debug("log message #%zu", n);
36,000✔
2123
        } else {
2124
            fg = SIRTC_BLACK;
30,000✔
2125
            bg = SIRTC_CYAN;
30,000✔
2126
            sir_info("log message #%zu", n);
36,000✔
2127
        }
2128

2129
        /* sometimes remove and re-add the log file, and set some options/styles.
2130
         * other times, just set different options/styles. */
2131
        uint32_t rnd = (uint32_t)(n + threadid);
72,000✔
2132
        if (getrand_bool((uint32_t)(rnd > 1 ? rnd : 1))) {
72,000✔
2133
            my_args->pass = print_test_error(sir_remfile(id), false);
36,269✔
2134
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
36,269✔
2135
                SIRL_ALL, SIRO_MSGONLY), false);
2136

2137
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
72,538✔
2138
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
36,269✔
2139
            my_args->pass = print_test_error(test, false);
36,269✔
2140

2141
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
36,269✔
2142
            my_args->pass = print_test_error(test, false);
36,269✔
2143
        } else {
2144
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
71,462✔
2145
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
35,731✔
2146
            my_args->pass = print_test_error(test, false);
35,731✔
2147

2148
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
35,731✔
2149
            my_args->pass = print_test_error(test, false);
35,728✔
2150

2151
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
35,728✔
2152
            my_args->pass = print_test_error(test, false);
35,731✔
2153
        }
2154

2155
        if (!my_args->pass)
72,000✔
2156
            break;
×
2157
    }
2158

2159
    my_args->pass = print_test_error(sir_remfile(id), false);
72✔
2160

2161
    rmfile(my_args->log_file);
72✔
2162

2163
#if !defined(__WIN__)
2164
    return NULL;
72✔
2165
#else /* __WIN__ */
2166
    return 0;
2167
#endif
2168
}
2169

2170
/*
2171
bool sirtest_XXX(void) {
2172
    INIT(si, SIRL_ALL, 0, 0, 0);
2173
    bool pass = si_init;
2174

2175
    pass &= sir_cleanup();
2176
    return print_result_and_return(pass);
2177
}
2178
*/
2179

2180
/* ========================== end tests ========================== */
2181

2182
bool print_test_error(bool result, bool expected) {
253,770✔
2183
    char message[SIR_MAXERROR] = {0};
253,770✔
2184
    uint16_t code              = sir_geterror(message);
253,770✔
2185

2186
    if (!expected && !result && SIR_E_NOERROR != code)
253,768✔
2187
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
59✔
2188
    else if (expected && SIR_E_NOERROR != code)
253,709✔
2189
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
486✔
2190

2191
    return result;
253,769✔
2192
}
2193

2194
bool print_os_error(void) {
60✔
2195
    char message[SIR_MAXERROR] = {0};
60✔
2196
    uint16_t code              = sir_geterror(message);
60✔
2197
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
60✔
2198
    return false;
60✔
2199
}
2200

2201
bool filter_error(bool pass, uint16_t err) {
1,448✔
2202
    if (!pass) {
1,448✔
2203
        char message[SIR_MAXERROR] = {0};
905✔
2204
        uint16_t code              = sir_geterror(message);
905✔
2205
        if (code != err)
905✔
2206
            return false;
2✔
2207
    }
2208
    return true;
1,206✔
2209
}
2210

2211
uint32_t getrand(uint32_t upper_bound) {
111,941✔
2212
#if !defined(__WIN__) || defined(__EMBARCADEROC__)
2213
# if defined(__MACOS__) || defined(__BSD__)
2214
    if (upper_bound < 1)
2215
        upper_bound = 1;
2216
    return arc4random_uniform(upper_bound);
2217
# else
2218
#  if defined(__EMBARCADEROC__)
2219
    return (uint32_t)(random(upper_bound));
2220
#  else
2221
    return (uint32_t)(random() % upper_bound);
111,941✔
2222
#  endif
2223
# endif
2224
#else /* __WIN__ */
2225
    uint32_t ctx = 0;
2226
    if (0 != rand_s(&ctx))
2227
        ctx = (uint32_t)rand();
2228
    return ctx % upper_bound;
2229
#endif
2230
}
2231

2232
bool rmfile(const char* filename) {
1,063✔
2233
    bool removed = false;
916✔
2234

2235
    /* return true if leave_logs is true. */
2236
    if (leave_logs) {
1,063✔
2237
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
49✔
2238
            filename, _cl_arg_list[3].flag);
49✔
2239
        return true;
49✔
2240
    }
2241

2242
    /* return true if the file doesn't exist. */
2243
    struct stat st;
2244
    if (0 != stat(filename, &st)) {
1,015✔
2245
        if (ENOENT == errno)
544✔
2246
            return true;
433✔
2247

2248
        handle_os_error(true, "failed to stat %s!", filename);
45✔
2249
        return false;
45✔
2250
    }
2251

2252
    if (!_sir_deletefile(filename)) {
471✔
2253
        handle_os_error(false, "failed to delete %s!", filename);
×
2254
    } else {
2255
        printf("\t" DGRAY("deleted %s (%ld bytes)...") "\n", filename,
390✔
2256
            (long)st.st_size);
471✔
2257
    }
2258

2259
    return removed;
390✔
2260
}
2261

2262
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
232✔
2263
    if (strstr(filename, search)) {
232✔
2264
        char filepath[SIR_MAXPATH];
2265
        (void)snprintf(filepath, SIR_MAXPATH, "%s%s", path, filename);
48✔
2266

2267
        rmfile(filepath);
54✔
2268
        (*data)++;
54✔
2269
    }
2270
}
232✔
2271

2272
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
115✔
2273
    SIR_UNUSED(path);
2274
    if (strstr(filename, search))
115✔
2275
        (*data)++;
33✔
2276
}
115✔
2277

2278
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
60✔
2279
#if !defined(__WIN__)
2280
    DIR* d = opendir(path);
60✔
2281
    if (!d)
60✔
2282
        return print_os_error();
2✔
2283

2284
    rewinddir(d);
58✔
2285
    struct dirent* di = readdir(d);
58✔
2286
    if (!di) {
58✔
2287
        closedir(d);
×
2288
        return print_os_error();
×
2289
    }
2290

2291
    while (NULL != di) {
405✔
2292
        cb(search, path, di->d_name, data);
347✔
2293
        di = readdir(d);
347✔
2294
    };
2295

2296
    closedir(d);
58✔
2297
    d = NULL;
49✔
2298
#else /* __WIN__ */
2299
    WIN32_FIND_DATA finddata = {0};
2300
    char buf[SIR_MAXPATH]    = {0};
2301

2302
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2303

2304
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2305

2306
    if (INVALID_HANDLE_VALUE == enumerator)
2307
        return false;
2308

2309
    do {
2310
        cb(search, path, finddata.cFileName, data);
2311
    } while (FindNextFile(enumerator, &finddata) > 0);
2312

2313
    FindClose(enumerator);
2314
    enumerator = NULL;
2315
#endif
2316

2317
    return true;
58✔
2318
}
2319

2320
bool sirtimerstart(sir_timer* timer) {
65✔
2321
#if !defined(__WIN__) || defined(__ORANGEC__)
2322
    int gettime = clock_gettime(SIRTEST_CLOCK, &timer->ts);
65✔
2323
    if (0 != gettime) {
65✔
2324
        handle_os_error(true, "clock_gettime(%d) failed!", CLOCK_CAST SIRTEST_CLOCK);
2✔
2325
    }
2326

2327
    return 0 == gettime;
65✔
2328
#else /* __WIN__ */
2329
    GetSystemTimePreciseAsFileTime(&timer->ft);
2330
    return true;
2331
#endif
2332
}
2333

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

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

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

2383
    os_activity_t parent = (os_activity_t)ctx;
2384
    os_activity_t child = os_activity_create("counting moon rocks", parent,
2385
        OS_ACTIVITY_FLAG_DEFAULT);
2386

2387
    float rock_count = 0.0f;
2388
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2389
    sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2390
        rock_count);
2391
}
2392

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

2396
    for (size_t n = 0; n < 10; n++) {
2397
        sir_info("counting rocks in sector %zu...", n);
2398
    }
2399

2400
    float* rock_count = (float*)ctx;
2401
    *rock_count = 1e12;
2402
    sir_info("all sectors counted; heading back to the lunar lander");
2403
}
2404
#endif
2405

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

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

2419
    return found;
3✔
2420
}
2421

2422
void print_usage_info(void) {
4✔
2423
    size_t longest = 0;
4✔
2424
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2425
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2426
        if (len > longest)
28✔
2427
            longest = len;
8✔
2428
    }
2429

2430
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2431

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

2435
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2436
        if (len < longest)
28✔
2437
            for (size_t n = len; n < longest; n++)
156✔
2438
                fprintf(stderr, " ");
132✔
2439

2440
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2441
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2442
            _cl_arg_list[i].desc);
28✔
2443
    }
2444

2445
    fprintf(stderr, "\n");
4✔
2446
}
4✔
2447

2448
void print_test_list(void) {
1✔
2449
    size_t longest = 0;
1✔
2450
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
32✔
2451
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2452
        if (len > longest)
31✔
2453
            longest = len;
3✔
2454
    }
2455

2456
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2457

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

2461
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2462
        if (len < longest)
31✔
2463
            for (size_t n = len; n < longest; n++)
270✔
2464
                printf(" ");
240✔
2465

2466
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
31✔
2467
            printf("\n");
16✔
2468
    }
2469

2470
    printf("\n");
1✔
2471
}
1✔
2472

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