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

aremmell / libsir / 1153

25 Jul 2023 04:30AM UTC coverage: 80.118% (-14.7%) from 94.801%
1153

push

travis-ci

aremmell
fix ternary

1 of 1 new or added line in 1 file covered. (100.0%)

2571 of 3209 relevant lines covered (80.12%)

24530.87 hits per line

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

70.03
/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.1
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) {
10✔
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) {
10✔
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;
6✔
114
    bool only     = false;
6✔
115
    size_t to_run = 0;
6✔
116

117
    for (int n = 1; n < argc; n++) {
10✔
118
        if (_sir_strsame(argv[n], _cl_arg_list[0].flag,
7✔
119
            strnlen(_cl_arg_list[0].flag, SIR_MAXCLIFLAG))) { /* --perf */
6✔
120
            only = mark_test_to_run("performance");
×
121
            if (only)
×
122
                to_run = 1;
×
123
        } else if (_sir_strsame(argv[n], _cl_arg_list[1].flag,
7✔
124
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --only */
6✔
125
            while (++n < argc) {
2✔
126
                if (_sir_validstrnofail(argv[n])) {
1✔
127
                    if (*argv[n] == '-' || !mark_test_to_run(argv[n])) {
1✔
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++;
×
133
                }
134
            };
135
            if (0 == to_run) {
1✔
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;
×
142
        } else if (_sir_strsame(argv[n], _cl_arg_list[2].flag,
5✔
143
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --list */
4✔
144
            print_test_list();
1✔
145
            return EXIT_SUCCESS;
1✔
146
        } else if (_sir_strsame(argv[n], _cl_arg_list[3].flag,
4✔
147
            strnlen(_cl_arg_list[1].flag, SIR_MAXCLIFLAG))) { /* --leave-logs */
3✔
148
            leave_logs = true;
×
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);
3✔
168
    size_t tgt_tests = (only ? to_run : _sir_countof(sir_tests) - first);
3✔
169
    size_t passed    = 0;
×
170
    size_t ran       = 0;
×
171
    sir_timer timer  = {0};
3✔
172

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

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

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

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

190
        ran++;
×
191

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

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

198
    if (passed == tgt_tests) {
3✔
199
        printf("\n" WHITEB("done: ")
3✔
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);
3✔
202
    } else {
203
        printf("\n" WHITEB("done: ")
×
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);
×
206

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

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

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

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

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

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

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

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

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

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

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

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

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

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

263
        rmfile(logfilename);
3✔
264
    }
265

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

316
    sir_options even = SIRO_MSGONLY;
×
317
    sir_options odd  = SIRO_ALL;
×
318

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

327
    pass &= sir_info("test test test");
3✔
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);
3✔
331

332
    if (pass)
3✔
333
        print_expected_error();
3✔
334

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

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

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

344
    do {
×
345
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
162✔
346
        bool skip  = false;
×
347

348
        for (size_t n = 0; n < SIR_MAXFILES; n++)
1,536✔
349
            if (removeorder[n] == rnd) {
1,488✔
350
                skip = true;
×
351
                break;
×
352
            }
353

354
        if (skip)
162✔
355
            continue;
114✔
356

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

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

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

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

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

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

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

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

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

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

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

395
bool sirtest_failfilebadpermission(void) {
3✔
396
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
397
    bool pass = si_init;
×
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 = "C:\\Windows\\System32\\noperms";
406
# endif
407
#endif
408

409
    pass &= 0 == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY);
3✔
410

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

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

418
bool sirtest_faildupefile(void) {
3✔
419
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
420
    bool pass = si_init;
×
421

422
#if !defined(__WIN__)
423
    static const char* filename1 = "./logs/faildupefile.log";
424
    static const char* filename2 = "logs/faildupefile.log";
425
#else
426
    static const char* filename1 = "logs\\faildupefile.log";
427
    static const char* filename2 = "logs/faildupefile.log";
428
#endif
429

430
    static const char* filename3 = "logs/not-a-dupe.log";
431
    static const char* filename4 = "logs/also-not-a-dupe.log";
432

433
    printf("\tadding log file '%s'...\n", filename1);
3✔
434

435
    /* should be fine; no other files added yet. */
436
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
3✔
437
    pass &= 0 != fid;
3✔
438

439
    printf("\ttrying again to add log file '%s'...\n", filename1);
3✔
440

441
    /* should fail. this is the same file we already added. */
442
    pass &= 0 == sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
3✔
443

444
    if (pass)
3✔
445
        print_expected_error();
3✔
446

447
    printf("\tadding log file '%s'...\n", filename2);
3✔
448

449
    /* should also fail. this is the same file we already added, even
450
     * if the path strings don't match. */
451
    pass &= 0 == sir_addfile(filename2, SIRL_ALL, SIRO_DEFAULT);
3✔
452

453
    if (pass)
3✔
454
        print_expected_error();
3✔
455

456
    printf("\tadding log file '%s'...\n", filename3);
3✔
457

458
    /* should pass. this is a different file. */
459
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
3✔
460
    pass &= 0 != fid2;
3✔
461

462
    /* should also pass. */
463
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
3✔
464
    pass &= 0 != fid3;
3✔
465

466
    pass &= sir_info("hello three valid files");
3✔
467

468
    /* should now fail since we added it earlier. */
469
    pass &= 0 == sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
3✔
470

471
    if (pass)
3✔
472
        print_expected_error();
3✔
473

474
    /* don't remove all of the log files in order to also test
475
     * cache tear-down. */
476
    pass &= sir_remfile(fid);
3✔
477

478
    rmfile(filename1);
3✔
479
    rmfile(filename2);
3✔
480
    rmfile(filename3);
3✔
481
    rmfile(filename4);
3✔
482

483
    pass &= sir_cleanup();
3✔
484
    return print_result_and_return(pass);
3✔
485
}
486

487
bool sirtest_failremovebadfile(void) {
3✔
488
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
489
    bool pass = si_init;
×
490

491
    sirfileid invalidid = 9999999;
×
492
    pass &= !sir_remfile(invalidid);
3✔
493

494
    if (pass)
3✔
495
        print_expected_error();
3✔
496

497
    sir_cleanup();
3✔
498
    return print_result_and_return(pass);
3✔
499
}
500

501
bool sirtest_rollandarchivefile(void) {
3✔
502
    /* roll size minus 1KiB so we can write until it maxes. */
503
    static const long deltasize    = 1024L;
504
    const long fillsize            = SIR_FROLLSIZE - deltasize;
×
505
    static const char* logbasename = "rollandarchive";
506
    static const char* logext      = ".log";
507
    static const char* line        = "hello, i am some data. nice to meet you.";
508

509
    char logfilename[SIR_MAXPATH] = {0};
3✔
510
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
3✔
511

512
    unsigned delcount = 0;
3✔
513
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
3✔
514
        handle_os_error(false, "failed to enumerate log files with base name: %s!",
×
515
            logbasename);
516
        return false;
×
517
    }
518

519
    if (delcount > 0)
3✔
520
        printf("\tfound and removed %u log file(s)\n", delcount);
×
521

522
    FILE* f = NULL;
3✔
523
    _sir_fopen(&f, logfilename, "w");
3✔
524

525
    if (!f)
3✔
526
        return print_os_error();
×
527

528
    if (0 != fseek(f, fillsize, SEEK_SET)) {
3✔
529
        handle_os_error(true, "fseek in file %s failed!", logfilename);
×
530
        fclose(f);
×
531
        return false;
×
532
    }
533

534
    if (EOF == fputc('\0', f)) {
3✔
535
        handle_os_error(true, "fputc in file %s failed!", logfilename);
×
536
        fclose(f);
×
537
        return false;
×
538
    }
539

540
    fclose(f);
3✔
541

542
    INIT(si, 0, 0, 0, 0);
3✔
543
    bool pass = si_init;
×
544

545
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
3✔
546
    pass &= 0 != fileid;
3✔
547

548
    if (pass) {
3✔
549
        /* write an (approximately) known quantity until we should have rolled */
550
        size_t written  = 0;
×
551
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
3✔
552

553
        do {
554
            pass &= sir_debug("%zu %s", written, line);
228✔
555
            written += linesize;
228✔
556
        } while (pass && (written < deltasize + (linesize * 50)));
228✔
557

558
        /* look for files matching the original name. */
559
        unsigned foundlogs = 0;
3✔
560
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
3✔
561
            handle_os_error(false, "failed to enumerate log files with base name: %s!",
×
562
                logbasename);
563
            pass = false;
×
564
        }
565

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

571
    pass &= sir_remfile(fileid);
3✔
572

573
    delcount = 0;
3✔
574
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
3✔
575
        handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
576
        return false;
×
577
    }
578

579
    if (delcount > 0)
3✔
580
        printf("\tfound and removed %u log file(s)\n", delcount);
×
581

582
    sir_cleanup();
3✔
583
    return print_result_and_return(pass);
3✔
584
}
585

586
bool sirtest_failwithoutinit(void) {
3✔
587
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
3✔
588

589
    if (pass)
3✔
590
        print_expected_error();
3✔
591

592
    return print_result_and_return(pass);
3✔
593
}
594

595
bool sirtest_failinittwice(void) {
3✔
596
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
597
    bool pass = si_init;
×
598

599
    INIT(si2, SIRL_ALL, 0, 0, 0);
3✔
600
    pass &= !si2_init;
3✔
601

602
    if (pass)
3✔
603
        print_expected_error();
3✔
604

605
    sir_cleanup();
3✔
606
    return print_result_and_return(pass);
3✔
607
}
608

609
bool sirtest_failinvalidinitdata(void) {
3✔
610
    sirinit si;
611

612
    /* fill with bad data. */
613
    memset(&si, 0xab, sizeof(sirinit));
×
614

615
    printf("\tcalling sir_init with invalid data...\n");
×
616
    bool pass = !sir_init(&si);
3✔
617

618
    if (pass)
3✔
619
        print_expected_error();
3✔
620

621
    sir_cleanup();
3✔
622
    return print_result_and_return(pass);
3✔
623
}
624

625
bool sirtest_initcleanupinit(void) {
3✔
626
    INIT(si1, SIRL_ALL, 0, 0, 0);
3✔
627
    bool pass = si1_init;
×
628

629
    pass &= sir_info("init called once; testing output...");
3✔
630
    sir_cleanup();
3✔
631

632
    INIT(si2, SIRL_ALL, 0, 0, 0);
3✔
633
    pass &= si2_init;
×
634

635
    pass &= sir_info("init called again after re-init; testing output...");
3✔
636
    sir_cleanup();
3✔
637

638
    return print_result_and_return(pass);
3✔
639
}
640

641
bool sirtest_initmakeinit(void) {
3✔
642
    bool pass = true;
×
643

644
    sirinit si;
645
    pass &= sir_makeinit(&si);
3✔
646
    pass &= sir_init(&si);
3✔
647
    pass &= sir_info("initialized with sir_makeinit");
3✔
648
    pass &= sir_cleanup();
3✔
649

650
    return print_result_and_return(pass);
3✔
651
}
652

653
bool sirtest_failaftercleanup(void) {
3✔
654
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
655
    bool pass = si_init;
×
656

657
    sir_cleanup();
3✔
658
    pass &= !sir_info("already cleaned up; this needs to fail");
3✔
659

660
    if (pass)
3✔
661
        print_expected_error();
3✔
662

663
    return print_result_and_return(pass);
3✔
664
}
665

666
bool sirtest_errorsanity(void) {
3✔
667
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
668
    bool pass = si_init;
×
669

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

700
    char message[SIR_MAXERROR] = {0};
3✔
701
    for (size_t n = 0; n < _sir_countof(errors); n++) {
75✔
702
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
72✔
703
        memset(message, 0, SIR_MAXERROR);
×
704
        uint16_t err = sir_geterror(message);
72✔
705
        pass &= errors[n].code == err && *message != '\0';
72✔
706
        printf("\t%s = %s\n", errors[n].name, message);
72✔
707
    }
708

709
    sir_cleanup();
3✔
710
    return print_result_and_return(pass);
3✔
711
}
712

713
bool sirtest_textstylesanity(void) {
3✔
714
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
715
    bool pass = si_init;
×
716

717
    printf("\t" WHITEB("--- explicitly invalid ---") "\n");
×
718
    pass &= !sir_settextstyle(SIRL_INFO, 0xbbb, 800, 920);
3✔
719
    pass &= sir_info("I have set an invalid text style.");
3✔
720

721
    pass &= !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK);
3✔
722
    pass &= sir_info("oops, did it again...");
3✔
723

724
    pass &= !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff);
3✔
725
    pass &= sir_info("and again.");
3✔
726
    PRINT_PASS(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
3✔
727

728
    printf("\t" WHITEB("--- unusual but valid ---") "\n");
×
729
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT);
3✔
730
    pass &= sir_info("system default fg and bg");
3✔
731
    PRINT_PASS(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
3✔
732

733
    printf("\t" WHITEB("--- override defaults ---") "\n");
×
734
    pass &= sir_resettextstyles();
3✔
735

736
    pass &= sir_debug("default style");
3✔
737
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY);
3✔
738
    pass &= sir_debug("override style");
3✔
739

740
    pass &= sir_info("default style");
3✔
741
    pass &= sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA);
3✔
742
    pass &= sir_info("override style");
3✔
743

744
    pass &= sir_notice("default style");
3✔
745
    pass &= sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW);
3✔
746
    pass &= sir_notice("override style");
3✔
747

748
    pass &= sir_warn("default style");
3✔
749
    pass &= sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE);
3✔
750
    pass &= sir_warn("override style");
3✔
751

752
    pass &= sir_error("default style");
3✔
753
    pass &= sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE);
3✔
754
    pass &= sir_error("override style");
3✔
755

756
    pass &= sir_crit("default style");
3✔
757
    pass &= sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN);
3✔
758
    pass &= sir_crit("override style");
3✔
759

760
    pass &= sir_alert("default style");
3✔
761
    pass &= sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT);
3✔
762
    pass &= sir_alert("override style");
3✔
763

764
    pass &= sir_emerg("default style");
3✔
765
    pass &= sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT);
3✔
766
    pass &= sir_emerg("override style");
3✔
767
    PRINT_PASS(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
3✔
768

769
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
×
770
    pass &= sir_resettextstyles();
3✔
771

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

782
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
×
783
    pass &= sir_setcolormode(SIRCM_256);
3✔
784

785
    for (sir_textcolor fg = 0, bg = 255; fg < 256; fg++, bg--) {
771✔
786
        if (fg != bg) {
768✔
787
            pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
768✔
788
            pass &= sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
768✔
789
                fg, bg);
790
        }
791
    }
792

793
    PRINT_PASS(pass, "\t--- change mode: 256-color: %s ---\n\n", PRN_PASS(pass));
3✔
794

795
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
×
796
    pass &= sir_setcolormode(SIRCM_RGB);
3✔
797

798
    for (size_t n = 0; n < 256; n++) {
771✔
799
        sir_textcolor fg = sir_makergb(getrand(255), getrand(255), getrand(255));
768✔
800
        sir_textcolor bg = sir_makergb(getrand(255), getrand(255), getrand(255));
768✔
801
        pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
768✔
802
        pass &= sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
768✔
803
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
768✔
804
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
768✔
805
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg));
768✔
806
    }
807
    PRINT_PASS(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
3✔
808

809
    printf("\t" WHITEB("--- change mode: invalid mode ---") "\n");
×
810
    pass &= !sir_setcolormode(SIRCM_INVALID);
3✔
811
    sir_textcolor fg = sir_makergb(255, 0, 0);
3✔
812
    sir_textcolor bg = sir_makergb(0, 0, 0);
3✔
813
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg);
3✔
814
    pass &= sir_debug("this is still RGB color mode");
3✔
815
    PRINT_PASS(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
3✔
816

817
    printf("\t" WHITEB("--- change mode: 16-color ---") "\n");
×
818
    pass &= sir_setcolormode(SIRCM_16);
3✔
819
    pass &= sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT);
3✔
820
    pass &= sir_debug("this is 16-color mode (fg: %"PRIu32", bg: default)",
3✔
821
        SIRTC_BMAGENTA);
822
    PRINT_PASS(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
3✔
823

824
    sir_cleanup();
3✔
825

826
    return print_result_and_return(pass);
3✔
827
}
828

829
bool sirtest_optionssanity(void) {
3✔
830
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
831
    bool pass = si_init;
×
832

833
    static const size_t iterations = 10;
834

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

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

870
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
×
871
    uint32_t last_count = SIR_NUMOPTIONS;
×
872
    for (size_t n = 0; n < iterations; n++) {
33✔
873
        sir_options opts    = 0;
×
874
        uint32_t rand_count = 0;
×
875
        size_t last_idx     = 0;
×
876

877
        do {
878
            rand_count = getrand(SIR_NUMOPTIONS);
42✔
879
        } while (rand_count == last_count || rand_count <= 1);
42✔
880

881
        last_count = rand_count;
×
882

883
        for (size_t i = 0; i < rand_count; i++) {
159✔
884
            size_t rand_idx = 0;
×
885
            size_t tries    = 0;
×
886

887
            do {
888
                if (++tries > SIR_NUMOPTIONS - 2)
222✔
889
                    break;
×
890
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
222✔
891

892
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
222✔
893

894
            last_idx = rand_idx;
×
895
            opts |= option_arr[rand_idx];
129✔
896
        }
897

898
        pass &= _sir_validopts(opts);
30✔
899
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
30✔
900
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
901
    }
902
    PRINT_PASS(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
3✔
903

904
    printf("\t" WHITEB("--- invalid values ---") "\n");
×
905

906
    /* the lowest byte is not valid. */
907
    sir_options invalid = 0x000000ff;
×
908
    pass &= !_sir_validopts(invalid);
3✔
909
    printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
×
910

911
    /* gaps inbetween valid options. */
912
    invalid = 0x0001ff00 & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
×
913
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
914
    pass &= !_sir_validopts(invalid);
3✔
915
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00: %08"PRIx32) "\n", invalid);
×
916

917
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
918
    for (sir_option o = 0x00008f00; o < SIRO_NOHDR; o += 0x1000) {
27✔
919
        pass &= !_sir_validopts(o);
24✔
920
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
×
921
    }
922

923
    /* greater than SIRO_NOHDR. */
924
    invalid = (0xFFFF0000 & ~SIRO_NOHDR);
×
925
    pass &= !_sir_validopts(invalid);
3✔
926
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
×
927

928
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
3✔
929

930
    sir_cleanup();
3✔
931
    return print_result_and_return(pass);
3✔
932
}
933

934
bool sirtest_levelssanity(void) {
3✔
935
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
936
    bool pass = si_init;
×
937

938
    static const size_t iterations = 10;
939

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

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

977
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
×
978
    uint32_t last_count = SIR_NUMLEVELS;
×
979
    for (size_t n = 0; n < iterations; n++) {
33✔
980
        sir_levels levels   = 0;
×
981
        uint32_t rand_count = 0;
×
982
        size_t last_idx     = 0;
×
983

984
        do {
985
            rand_count = getrand(SIR_NUMLEVELS);
63✔
986
        } while (rand_count == last_count || rand_count <= 1);
63✔
987

988
        last_count = rand_count;
×
989

990
        for (size_t i = 0; i < rand_count; i++) {
177✔
991
            size_t rand_idx = 0;
×
992
            size_t tries    = 0;
×
993

994
            do {
995
                if (++tries > SIR_NUMLEVELS - 2)
246✔
996
                    break;
×
997
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
240✔
998
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
240✔
999

1000
            last_idx = rand_idx;
×
1001
            levels |= levels_arr[rand_idx];
147✔
1002
        }
1003

1004
        pass &= _sir_validlevels(levels);
30✔
1005
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
30✔
1006
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
1007
    }
1008
    PRINT_PASS(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
3✔
1009

1010
    printf("\t" WHITEB("--- invalid values ---") "\n");
×
1011

1012
    /* greater than SIRL_ALL. */
1013
    sir_levels invalid = (0xffff & ~SIRL_ALL);
×
1014
    pass &= !_sir_validlevels(invalid);
3✔
1015
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
×
1016

1017
    /* individual invalid level. */
1018
    sir_level invalid2 = 0x1337;
×
1019
    pass &= !_sir_validlevel(invalid2);
3✔
1020
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx32) "\n", invalid2);
×
1021

1022
    PRINT_PASS(pass, "\t--- invalid values: %s ---\n\n", PRN_PASS(pass));
3✔
1023

1024
    sir_cleanup();
3✔
1025
    return print_result_and_return(pass);
3✔
1026
}
1027

1028
bool sirtest_mutexsanity(void) {
3✔
1029
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
1030
    bool pass = si_init;
×
1031

1032
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
×
1033
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
×
1034

1035
    sir_mutex m1 = SIR_MUTEX_INIT;
3✔
1036
    pass &= _sir_mutexcreate(&m1);
3✔
1037

1038
    print_test_error(pass, pass);
3✔
1039

1040
    if (pass) {
3✔
1041
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
×
1042
        pass &= _sir_mutexlock(&m1);
3✔
1043

1044
        print_test_error(pass, pass);
3✔
1045

1046
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
×
1047
        pass &= _sir_mutexunlock(&m1);
3✔
1048

1049
        print_test_error(pass, pass);
3✔
1050

1051
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
×
1052
        pass &= _sir_mutextrylock(&m1);
3✔
1053

1054
        print_test_error(pass, pass);
3✔
1055

1056
        printf(INDENT_ITEM WHITE("unlocking...") "\n");
×
1057
        pass &= _sir_mutexunlock(&m1);
3✔
1058

1059
        print_test_error(pass, pass);
3✔
1060

1061
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
×
1062
        pass &= _sir_mutexdestroy(&m1);
3✔
1063

1064
        print_test_error(pass, pass);
3✔
1065

1066
    }
1067
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
3✔
1068

1069
    printf("\t" WHITEB("invalid arguments") "\n");
×
1070
    printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
×
1071
    pass &= !_sir_mutexcreate(NULL);
3✔
1072
    printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
×
1073
    pass &= !_sir_mutexlock(NULL);
3✔
1074
    printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
×
1075
    pass &= !_sir_mutextrylock(NULL);
3✔
1076
    printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
×
1077
    pass &= !_sir_mutexunlock(NULL);
3✔
1078
    printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
×
1079
    pass &= !_sir_mutexdestroy(NULL);
3✔
1080
    PRINT_PASS(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
3✔
1081

1082
    pass &= sir_cleanup();
3✔
1083
    return print_result_and_return(pass);
3✔
1084
}
1085

1086
bool sirtest_perf(void) {
×
1087
    static const char* logbasename = "libsir-perf";
1088
    static const char* logext      = "";
1089

1090
#if !defined(__WIN__) && !defined(DUMA)
1091
    static const size_t perflines = 1000000;
1092
#else /* __WIN__ */
1093
    static const size_t perflines = 100000;
1094
#endif
1095

1096
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
×
1097
    bool pass = si_init;
×
1098

1099
    if (pass) {
×
1100
        float printfelapsed = 0.0f;
×
1101
        float stdioelapsed  = 0.0f;
×
1102
        float fileelapsed   = 0.0f;
×
1103

1104
        printf("\t" BLUE("%zu lines printf...") "\n", perflines);
×
1105

1106
        sir_timer printftimer = {0};
×
1107
        sirtimerstart(&printftimer);
×
1108

1109
        for (size_t n = 0; n < perflines; n++)
×
1110
            printf(WHITE("%.2f: lorem ipsum foo bar %s: %zu") "\n",
×
1111
                (double)sirtimerelapsed(&printftimer), "baz", 1234 + n);
×
1112

1113
        printfelapsed = sirtimerelapsed(&printftimer);
×
1114

1115
        printf("\t" BLUE("%zu lines libsir(stdout)...") "\n", perflines);
×
1116

1117
        sir_timer stdiotimer = {0};
×
1118
        sirtimerstart(&stdiotimer);
×
1119

1120
        for (size_t n = 0; n < perflines; n++)
×
1121
            sir_debug("%.2f: lorem ipsum foo bar %s: %zu",
×
1122
                (double)sirtimerelapsed(&stdiotimer), "baz", 1234 + n);
×
1123

1124
        stdioelapsed = sirtimerelapsed(&stdiotimer);
×
1125

1126
        sir_cleanup();
×
1127

1128
        INIT(si2, 0, 0, 0, 0);
×
1129
        pass &= si2_init;
×
1130

1131
        char logfilename[SIR_MAXPATH] = {0};
×
1132
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
×
1133

1134
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
×
1135
        pass &= 0 != logid;
×
1136

1137
        if (pass) {
×
1138
            printf("\t" BLUE("%zu lines libsir(log file)...") "\n", perflines);
×
1139

1140
            sir_timer filetimer = {0};
×
1141
            sirtimerstart(&filetimer);
×
1142

1143
            for (size_t n = 0; n < perflines; n++)
×
1144
                sir_debug("lorem ipsum foo bar %s: %zu", "baz", 1234 + n);
×
1145

1146
            fileelapsed = sirtimerelapsed(&filetimer);
×
1147

1148
            pass &= sir_remfile(logid);
×
1149
        }
1150

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

1167
    unsigned deleted = 0;
×
1168
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
×
1169

1170
    if (deleted > 0)
×
1171
        printf("\t" DGRAY("deleted %u log file(s)") "\n", deleted);
×
1172

1173
    sir_cleanup();
×
1174
    return print_result_and_return(pass);
×
1175
}
1176

1177
bool sirtest_updatesanity(void) {
3✔
1178
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
3✔
1179
    bool pass = si_init;
×
1180

1181
#define UPDATE_SANITY_ARRSIZE 10
1182

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

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

1200
    rmfile(logfile);
3✔
1201
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
3✔
1202
    pass &= 0 != id1;
3✔
1203

1204
    for (int i = 0; i < 10; i++) {
33✔
1205
        if (!pass)
30✔
1206
            break;
×
1207

1208
        /* reset to defaults*/
1209
        pass &= sir_stdoutlevels(SIRL_DEFAULT);
30✔
1210
        pass &= sir_stderrlevels(SIRL_DEFAULT);
30✔
1211
        pass &= sir_stdoutopts(SIRO_DEFAULT);
30✔
1212
        pass &= sir_stderropts(SIRO_DEFAULT);
30✔
1213

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

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

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

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

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

1249
    if (pass) {
3✔
1250
        /* restore to default config and run again */
1251
        sir_stdoutlevels(SIRL_DEFAULT);
3✔
1252
        sir_stderrlevels(SIRL_DEFAULT);
3✔
1253
        sir_stdoutopts(SIRO_DEFAULT);
3✔
1254
        sir_stderropts(SIRO_DEFAULT);
3✔
1255

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

1266
    pass &= sir_remfile(id1);
3✔
1267
    rmfile(logfile);
3✔
1268
    sir_cleanup();
3✔
1269

1270
    return print_result_and_return(pass);
3✔
1271
}
1272

1273
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1274
static bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
3✔
1275
    bool pass             = true;
×
1276
    static const int runs = 5;
1277

1278
    /* repeat initializing, opening, logging, closing, cleaning up n times. */
1279
    sir_timer timer = {0};
3✔
1280
    pass &= sirtimerstart(&timer);
3✔
1281

1282
    printf("\trunning %d passes of random configs (system logger: '%s', "
×
1283
           "identity: '%s', category: '%s')...\n",
1284
        runs, sl_name, identity, category);
1285

1286
    for (int i = 0; i < runs; i++) {
18✔
1287
        /* randomly skip setting process name, identity/category to thoroughly
1288
         * test fallback routines; randomly update the config mid-run. */
1289
        bool set_procname = getrand_bool((uint32_t)sirtimerelapsed(&timer));
15✔
1290
        bool set_identity = getrand_bool((uint32_t)sirtimerelapsed(&timer));
15✔
1291
        bool set_category = getrand_bool((uint32_t)sirtimerelapsed(&timer));
15✔
1292
        bool do_update    = getrand_bool((uint32_t)sirtimerelapsed(&timer));
15✔
1293

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

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

1301
        if (set_identity)
15✔
1302
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
9✔
1303

1304
        if (set_category)
15✔
1305
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
9✔
1306

1307
        si_init = sir_init(&si); //-V519
15✔
1308
        pass &= si_init;
×
1309

1310
        if (do_update)
15✔
1311
            pass &= sir_sysloglevels(SIRL_ALL);
5✔
1312

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

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

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

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

1330
        if (do_update)
15✔
1331
            pass &= sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID));
5✔
1332

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

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

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

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

1354
        sir_cleanup();
15✔
1355

1356
        if (!pass)
15✔
1357
            break;
×
1358
    }
1359

1360
    return print_result_and_return(pass);
3✔
1361
}
1362
#endif
1363

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

1372
    SIR_UNUSED(sl_name);
1373

1374
    printf("\tSIR_NO_SYSTEM_LOGGERS is defined; expecting calls to fail...\n");
×
1375

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

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

1383
    if (pass)
×
1384
        print_expected_error();
×
1385

1386
    printf("\tsetting options...\n");
×
1387
    pass &= !sir_syslogopts(SIRO_DEFAULT);
×
1388

1389
    if (pass)
×
1390
        print_expected_error();
×
1391

1392
    printf("\tsetting identity...\n");
×
1393
    pass &= !sir_syslogid(identity);
×
1394

1395
    if (pass)
×
1396
        print_expected_error();
×
1397

1398
    printf("\tsetting category...\n");
×
1399
    pass &= !sir_syslogcat(category);
×
1400

1401
    if (pass)
×
1402
        print_expected_error();
×
1403

1404
    pass &= sir_cleanup();
×
1405
    return print_result_and_return(pass);
×
1406
}
1407
#endif
1408

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

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

1434
bool sirtest_filesystem(void) {
3✔
1435
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
1436
    bool pass = si_init;
×
1437

1438
    /* current working directory. */
1439
    char* cwd = _sir_getcwd();
3✔
1440
    pass &= NULL != cwd;
3✔
1441
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
3✔
1442

1443
    if (NULL != cwd) {
3✔
1444
        /* path to this binary file. */
1445
        char* filename = _sir_getappfilename();
3✔
1446
        pass &= NULL != filename;
3✔
1447
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
3✔
1448

1449
        if (NULL != filename) {
3✔
1450
            /* _sir_get[base|dir]name() can potentially modify filename,
1451
             * so make a copy for each call. */
1452
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
3✔
1453
            pass &= NULL != filename2;
3✔
1454

1455
            if (NULL != filename2) {
3✔
1456
                /* filename, stripped of directory component(s). */
1457
                char* _basename = _sir_getbasename(filename2);
3✔
1458
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
3✔
1459

1460
                if (!_basename) {
3✔
1461
                    pass = false;
×
1462
                } else {
1463
                    /* the last strlen(_basename) chars of filename should match. */
1464
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
3✔
1465
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
3✔
1466
                    size_t n      = 0;
×
1467

1468
                    while (n < len) {
27✔
1469
                        if (filename[offset++] != _basename[n++]) {
24✔
1470
                            pass = false;
×
1471
                            break;
×
1472
                        }
1473
                    };
1474
                }
1475
            }
1476

1477
            /* directory this binary file resides in. */
1478
            char* appdir = _sir_getappdir();
3✔
1479
            pass &= NULL != appdir;
3✔
1480
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
3✔
1481

1482
            /* _sir_get[base|dir]name can potentially modify filename,
1483
             * so make a copy for each call. */
1484
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
3✔
1485
            pass &= NULL != filename3;
3✔
1486

1487
            if (NULL != appdir && NULL != filename3) {
3✔
1488
                /* should yield the same result as _sir_getappdir(). */
1489
                char* _dirname = _sir_getdirname(filename3);
3✔
1490
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
3✔
1491

1492
                pass &= 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH));
3✔
1493
                pass &= NULL != _dirname &&
6✔
1494
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH));
3✔
1495
            }
1496

1497
            _sir_safefree(&appdir);
3✔
1498
            _sir_safefree(&filename);
3✔
1499
            _sir_safefree(&filename2);
3✔
1500
            _sir_safefree(&filename3);
3✔
1501
        }
1502

1503
        _sir_safefree(&cwd);
3✔
1504
    }
1505

1506
    /* this next section doesn't really yield any useful boolean pass/fail
1507
     * information, but could be helpful to review manually. */
1508
    char* dubious_dirnames[] = {
3✔
1509
#if !defined(__WIN__)
1510
        "/foo",
1511
        "/foo/",
1512
        "/foo/bar",
1513
        "/foo/bar/bad:filename",
1514
        "/",
1515
        ""
1516
#else /* __WIN__ */
1517
        "C:\\foo",
1518
        "C:\\foo\\",
1519
        "C:\\foo\\bar",
1520
        "C:\\foo\\bar\\bad:>filename",
1521
        "C:\\",
1522
        "//network-share/myfolder",
1523
        ""
1524
#endif
1525
    };
1526

1527
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
21✔
1528
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
18✔
1529
        if (NULL != tmp) {
18✔
1530
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
18✔
1531
                tmp, _sir_getdirname(tmp));
1532
            _sir_safefree(&tmp);
18✔
1533
        }
1534
    }
1535

1536
    char* dubious_filenames[] = {
3✔
1537
#if !defined(__WIN__)
1538
        "foo/bar/file-or-directory",
1539
        "/foo/bar/file-or-directory",
1540
        "/foo/bar/illegal:filename",
1541
        "/",
1542
        ""
1543
#else /* __WIN__ */
1544
        "foo\\bar\\file.with.many.full.stops",
1545
        "C:\\foo\\bar\\poorly-renamed.txt.pdf",
1546
        "C:\\foo\\bar\\illegal>filename.txt",
1547
        "C:\\",
1548
        "\\Program Files\\foo.bar",
1549
        ""
1550
#endif
1551
    };
1552

1553
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
18✔
1554
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
15✔
1555
        if (NULL != tmp) {
15✔
1556
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
15✔
1557
                tmp, _sir_getbasename(tmp));
1558
            _sir_safefree(&tmp);
15✔
1559
        }
1560
    }
1561

1562
    /* absolute/relative paths. */
1563
    static const struct {
1564
        const char* const path;
1565
        bool abs;
1566
    } abs_or_rel_paths[] = {
1567
        {"this/is/relative", false},
1568
        {"relative", false},
1569
        {"./relative", false},
1570
        {"../../relative", false},
1571
#if !defined(__WIN__)
1572
        {"/usr/local/bin", true},
1573
        {"/", true},
1574
        {"/home/foo/.config", true},
1575
        {"~/.config", true}
1576
#else /* __WIN__ */
1577
        {"D:\\absolute", true},
1578
        {"C:\\Program Files\\FooBar", true},
1579
        {"C:\\", true},
1580
        {"\\absolute", true},
1581
#endif
1582
    };
1583

1584
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
27✔
1585
        bool relative = false;
24✔
1586
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
24✔
1587

1588
        pass &= ret;
×
1589
        if (!ret) {
24✔
1590
            bool unused = print_test_error(false, false);
×
1591
            SIR_UNUSED(unused);
1592
            continue;
×
1593
        }
1594

1595
        if (relative == abs_or_rel_paths[n].abs) {
24✔
1596
            pass = false;
×
1597
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1598
                relative ? "true" : "false");
×
1599
        } else {
1600
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
24✔
1601
                relative ? "true" : "false");
×
1602
        }
1603
    }
1604

1605
    /* file existence. */
1606
    static const struct {
1607
        const char* const path;
1608
        bool exists;
1609
    } real_or_not[] = {
1610
        {"../foobarbaz", false},
1611
        {"foobarbaz", false},
1612
#if !defined(__WIN__)
1613
        {"/", true},
1614
# if !defined(__HAIKU__)
1615
        {"/usr/bin", true},
1616
# else
1617
        {"/bin", true},
1618
# endif
1619
        {"/dev", true},
1620
#else /* __WIN__ */
1621
        {"\\Windows", true},
1622
        {"\\Program Files", true},
1623
#endif
1624
        {"../../LICENSES/MIT.txt", true},
1625
        {"../../msvs/libsir.sln", true},
1626
        {"../", true},
1627
        {"file.exists", true}
1628
    };
1629

1630
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
30✔
1631
        bool exists = false;
27✔
1632
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
27✔
1633

1634
        pass &= ret;
×
1635
        if (!ret) {
27✔
1636
            bool unused = print_test_error(false, false);
×
1637
            SIR_UNUSED(unused);
1638
            continue;
×
1639
        }
1640

1641
        if (exists != real_or_not[n].exists) {
27✔
1642
            pass = false;
×
1643
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
×
1644
                exists ? "true" : "false");
×
1645
        } else {
1646
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
27✔
1647
                exists ? "true" : "false");
×
1648
        }
1649
    }
1650

1651
    /* checking file descriptors. */
1652
    static const int bad_fds[] = {
1653
        0,
1654
        1,
1655
        2,
1656
        1234
1657
    };
1658

1659
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
15✔
1660
        if (_sir_validfd(bad_fds[n])) {
12✔
1661
            pass = false;
×
1662
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1663
        } else {
1664
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
×
1665
        }
1666
    }
1667

1668
    FILE* f = NULL;
3✔
1669
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
3✔
1670
    if (!ret) {
3✔
1671
        pass = false;
×
1672
        handle_os_error(true, "fopen(%s) failed!", "file.exists");
×
1673
    } else {
1674
        int fd = fileno(f);
3✔
1675
        if (!_sir_validfd(fd)) {
3✔
1676
            pass = false;
×
1677
            printf("\t" RED("_sir_validfd(%d) = false") "\n", fd);
×
1678
        } else {
1679
            printf("\t" GREEN("_sir_validfd(%d) = true") "\n", fd);
×
1680
        }
1681
    }
1682

1683
    _sir_safefclose(&f);
3✔
1684

1685
    sir_cleanup();
3✔
1686
    return print_result_and_return(pass);
3✔
1687
}
1688

1689
bool sirtest_squelchspam(void) {
3✔
1690
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
1691
    bool pass = si_init;
×
1692

1693
    static const size_t alternate   = 50;
1694
    static const size_t sequence[3] = {
1695
        1000, /* non-repeating messages. */
1696
        1000, /* repeating messages. */
1697
        1000  /* alternating repeating and non-repeating messages. */
1698
    };
1699

1700
    sir_timer timer;
1701
    sirtimerstart(&timer);
3✔
1702

1703
    printf("\t" BLUE("%zu non-repeating messages...") "\n", sequence[0]);
×
1704

1705
    for (size_t n = 0, ascii_idx = 33; n < sequence[0]; n++, ascii_idx++) {
3,003✔
1706
        pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
3,000✔
1707
            (char)ascii_idx + 1);
3,000✔
1708

1709
        if (ascii_idx == 125)
3,000✔
1710
            ascii_idx = 33;
×
1711
    }
1712

1713
    printf("\t" BLUE("%zu repeating messages...") "\n", sequence[1]);
×
1714

1715
    for (size_t n = 0; n < sequence[1]; n++) {
3,003✔
1716
        bool ret = sir_debug("a repeating message");
3,000✔
1717

1718
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
3,000✔
1719
            pass &= !ret;
2,988✔
1720
        else
1721
            pass &= ret;
×
1722
    }
1723

1724
    printf("\t" BLUE("%zu alternating repeating and non-repeating messages...")
×
1725
           "\n", sequence[2]);
×
1726

1727
    bool repeating   = false;
×
1728
    size_t counter   = 0;
×
1729
    size_t repeat_id = 0;
×
1730
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
3,003✔
1731
        if (!repeating) {
3,000✔
1732
            pass &= sir_debug("%c%c a non-repeating message", (char)ascii_idx,
1,503✔
1733
                (char)ascii_idx + 1);
1,503✔
1734
        } else {
1735
            bool ret = sir_debug("%zu a repeating message", repeat_id);
1,497✔
1736

1737
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
1,497✔
1738
                pass &= !ret;
1,377✔
1739
            else
1740
                pass &= ret;
×
1741
        }
1742

1743
        if (counter == alternate) {
3,000✔
1744
            repeating = !repeating;
×
1745
            counter = 0;
×
1746
            repeat_id++;
57✔
1747
        }
1748

1749
        if (ascii_idx == 125)
3,000✔
1750
            ascii_idx = 33;
×
1751
    }
1752

1753
    sir_cleanup();
3✔
1754
    return print_result_and_return(pass);
3✔
1755
}
1756

1757
bool sirtest_pluginloader(void) {
3✔
1758
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
1759
    bool pass = si_init;
×
1760

1761
#if !defined(__WIN__)
1762
# define PLUGIN_EXT "so"
1763
#else
1764
# define PLUGIN_EXT "dll"
1765
#endif
1766

1767
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1768
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1769
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1770
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1771
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1772
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1773
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1774
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1775

1776
#if defined(SIR_NO_PLUGINS)
1777
    SIR_UNUSED(plugin2);
1778
    SIR_UNUSED(plugin3);
1779
    SIR_UNUSED(plugin4);
1780
    SIR_UNUSED(plugin5);
1781
    SIR_UNUSED(plugin6);
1782
    SIR_UNUSED(plugin7);
1783
    SIR_UNUSED(plugin8);
1784

1785
    printf("\tSIR_NO_PLUGINS is defined; expecting calls to fail\n");
×
1786

1787
    printf("\tloading good plugin: '%s'...\n", plugin1);
×
1788
    /* load a valid, well-behaved plugin. */
1789
    sirpluginid id = sir_loadplugin(plugin1);
×
1790
    pass &= 0 == id;
×
1791

1792
    if (pass)
×
1793
        print_expected_error();
×
1794

1795
    printf("\tunloading good plugin: '%s'...\n", plugin1);
×
1796
    /* also try the unload function. */
1797
    pass &= !sir_unloadplugin(id);
×
1798

1799
    if (pass)
×
1800
        print_expected_error();
×
1801
#else
1802
    /* load a valid, well-behaved plugin. */
1803
    printf("\tloading good plugin: '%s'...\n", plugin1);
3✔
1804
    sirpluginid id = sir_loadplugin(plugin1);
3✔
1805
    pass &= 0 != id;
3✔
1806

1807
    print_test_error(pass, pass);
3✔
1808

1809
    pass &= sir_info("this message will be dispatched to the plugin.");
3✔
1810
    pass &= sir_warn("this message will *not* be dispatched to the plugin.");
3✔
1811

1812
    /* re-loading the same plugin should fail. */
1813
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
3✔
1814
    sirpluginid badid = sir_loadplugin(plugin1);
3✔
1815
    pass &= 0 == badid;
3✔
1816

1817
    print_test_error(pass, pass);
3✔
1818

1819
    /* the following are all invalid or misbehaved, and should all fail. */
1820
    printf("\tloading bad plugin: '%s'...\n", plugin2);
3✔
1821
    badid = sir_loadplugin(plugin2);
3✔
1822
    pass &= 0 == badid;
3✔
1823

1824
    print_test_error(pass, pass);
3✔
1825

1826
    printf("\tloading bad plugin: '%s'...\n", plugin3);
3✔
1827
    badid = sir_loadplugin(plugin3);
3✔
1828
    pass &= 0 == badid;
3✔
1829

1830
    print_test_error(pass, pass);
3✔
1831

1832
    printf("\tloading bad plugin: '%s'...\n", plugin4);
3✔
1833
    badid = sir_loadplugin(plugin4);
3✔
1834
    pass &= 0 == badid;
3✔
1835

1836
    print_test_error(pass, pass);
3✔
1837

1838
    printf("\tloading bad plugin: '%s'...\n", plugin5);
3✔
1839
    badid = sir_loadplugin(plugin5);
3✔
1840
    pass &= 0 == badid;
3✔
1841

1842
    print_test_error(pass, pass);
3✔
1843

1844
    printf("\tloading bad plugin: '%s'...\n", plugin6);
3✔
1845
    badid = sir_loadplugin(plugin6);
3✔
1846
    pass &= 0 == badid;
3✔
1847

1848
    print_test_error(pass, pass);
3✔
1849

1850
    printf("\tloading bad plugin: '%s'...\n", plugin7);
3✔
1851
    badid = sir_loadplugin(plugin7);
3✔
1852
    pass &= 0 != badid; /* this one should load, just return false from write */
3✔
1853

1854
    pass &= !sir_info("this should fail, because one plugin failed to process"
3✔
1855
                      " the message.");
1856

1857
    print_test_error(pass, pass);
3✔
1858

1859
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
3✔
1860
    badid = sir_loadplugin(plugin8);
3✔
1861
    pass &= 0 == badid;
3✔
1862

1863
    print_test_error(pass, pass);
3✔
1864

1865
    /* unload the good plugin manually. */
1866
    printf("\tunloading good plugin: '%s'...\n", plugin1);
3✔
1867
    pass &= sir_unloadplugin(id);
3✔
1868

1869
    print_test_error(pass, pass);
3✔
1870

1871
    /* try to unload the plugin again. */
1872
    printf("\nunloading already unloaded plugin '%s'...\n", plugin1);
3✔
1873
    pass &= !sir_unloadplugin(id);
3✔
1874

1875
    print_test_error(pass, pass);
3✔
1876

1877
    /* test bad paths. */
1878
    printf("\ntrying to  load plugin with NULL path...\n");
×
1879
    badid = sir_loadplugin(NULL);
3✔
1880
    pass &= 0 == badid;
3✔
1881

1882
    print_test_error(pass, pass);
3✔
1883
#endif
1884
    pass &= sir_cleanup();
3✔
1885
    return print_result_and_return(pass);
3✔
1886
}
1887

1888
bool sirtest_getversioninfo(void) {
3✔
1889
    INIT(si, SIRL_ALL, 0, 0, 0);
3✔
1890
    bool pass = si_init;
×
1891

1892
    printf("\tchecking version retrieval functions...\n");
×
1893

1894
    const char* str = sir_getversionstring();
3✔
1895
    pass &= _sir_validstrnofail(str);
3✔
1896

1897
    printf("\tversion as string: '%s'\n", _SIR_PRNSTR(str));
3✔
1898

1899
    uint32_t hex = sir_getversionhex();
3✔
1900
    pass &= 0 != hex;
3✔
1901

1902
    printf("\tversion as hex: 0x%08"PRIx32"\n", hex);
×
1903

1904
    bool prerel = sir_isprerelease();
3✔
1905
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
3✔
1906

1907
    pass &= sir_cleanup();
3✔
1908
    return print_result_and_return(pass);
3✔
1909
}
1910

1911
enum {
1912
    NUM_THREADS = 4
1913
};
1914

1915
static bool threadpool_pseudojob(void* arg) {
21✔
1916
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
21✔
1917
#if !defined(__WIN__)
1918
    sleep(1);
21✔
1919
#else
1920
    Sleep(1000);
1921
#endif
1922
    return true;
19✔
1923
}
1924

1925
bool sirtest_threadpool(void) {
3✔
1926
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
3✔
1927
    bool pass = si_init;
×
1928

1929
    static const size_t num_jobs = 30;
1930
    sir_threadpool* pool         = NULL;
3✔
1931

1932
    pass &= _sir_threadpool_create(&pool, NUM_THREADS);
3✔
1933
    if (pass) {
3✔
1934
        /* dispatch a whole bunch of jobs. */
1935
        for (size_t n = 0; n < num_jobs; n++) {
93✔
1936
            sir_threadpool_job* job = calloc(1, sizeof(sir_threadpool_job));
90✔
1937
            pass &= NULL != job;
90✔
1938
            if (job) {
90✔
1939
                job->fn = &threadpool_pseudojob;
90✔
1940
                job->data = (void*)(n + 1);
90✔
1941
                pass &= _sir_threadpool_add_job(pool, job);
90✔
1942
                pass &= sir_info("dispatched job (fn: %"PRIxPTR", data: %p)",
90✔
1943
                    (uintptr_t)job->fn, job->data);
90✔
1944
            }
1945
        }
1946

1947
#if !defined(__WIN__)
1948
        sleep(1);
3✔
1949
#else
1950
        Sleep(1000);
1951
#endif
1952

1953
        pass &= sir_info("destroying thread pool...");
3✔
1954
        pass &= _sir_threadpool_destroy(&pool);
3✔
1955
    }
1956

1957
    pass &= sir_cleanup();
3✔
1958
    return print_result_and_return(pass);
3✔
1959
}
1960

1961
#if !defined(__WIN__)
1962
static void* threadrace_thread(void* arg);
1963
#else /* __WIN__ */
1964
static unsigned threadrace_thread(void* arg);
1965
#endif
1966

1967
bool sirtest_threadrace(void) {
3✔
1968
#if !defined(__WIN__)
1969
    pthread_t thrds[NUM_THREADS] = {0};
3✔
1970
#else /* __WIN__ */
1971
    uintptr_t thrds[NUM_THREADS] = {0};
1972
#endif
1973

1974
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
3✔
1975
    bool pass           = si_init;
×
1976
    bool any_created    = false;
×
1977
    size_t last_created = 0;
×
1978

1979
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
3✔
1980
    pass &= NULL != heap_args;
3✔
1981
    if (!heap_args) {
3✔
1982
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
×
1983
        return false;
×
1984
    }
1985

1986
    for (size_t n = 0; n < NUM_THREADS; n++) {
15✔
1987
        if (!pass)
12✔
1988
            break;
×
1989

1990
        heap_args[n].pass = true;
12✔
1991
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
12✔
1992
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
1993

1994
#if !defined(__WIN__)
1995
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
12✔
1996
        if (0 != create) {
12✔
1997
            errno = create;
×
1998
            handle_os_error(true, "pthread_create() for thread #%zu failed!", n + 1);
×
1999
#else /* __WIN__ */
2000
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2001
        if (0 == thrds[n]) {
2002
            handle_os_error(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2003
#endif
2004
            pass = false;
×
2005
            break;
×
2006
        }
2007

2008
        last_created = n;
×
2009
        any_created  = true;
×
2010
    }
2011

2012
    if (any_created) {
3✔
2013
        for (size_t j = 0; j < last_created + 1; j++) {
15✔
2014
            bool joined = true;
×
2015
            printf("\twaiting for thread %zu/%zu...\n", j + 1, last_created + 1);
12✔
2016
#if !defined(__WIN__)
2017
            int join = pthread_join(thrds[j], NULL);
12✔
2018
            if (0 != join) {
12✔
2019
                joined = false;
×
2020
                errno  = join;
×
2021
                handle_os_error(true, "pthread_join() for thread #%zu failed!", j + 1);
×
2022
            }
2023
#else /* __WIN__ */
2024
            DWORD wait = WaitForSingleObject((HANDLE)thrds[j], INFINITE);
2025
            if (WAIT_OBJECT_0 != wait) {
2026
                joined = false;
2027
                handle_os_error(false, "WaitForSingleObject() for thread #%zu (%p) failed!", j + 1,
2028
                    (HANDLE)thrds[j]);
2029
            }
2030
#endif
2031
            pass &= joined;
×
2032
            if (joined) {
×
2033
                printf("\tthread %zu/%zu joined\n", j + 1, last_created + 1);
×
2034

2035
                pass &= heap_args[j].pass;
12✔
2036
                if (heap_args[j].pass)
12✔
2037
                    printf("\t" GREEN("thread #%zu returned pass = true") "\n", j + 1);
×
2038
                else
2039
                    printf("\t" RED("thread #%zu returned pass = false!") "\n", j + 1);
×
2040
            }
2041
        }
2042
    }
2043

2044
    _sir_safefree(&heap_args);
3✔
2045

2046
    sir_cleanup();
3✔
2047
    return print_result_and_return(pass);
3✔
2048
}
2049

2050
#if !defined(__WIN__)
2051
static void* threadrace_thread(void* arg) {
12✔
2052
#else /* __WIN__ */
2053
unsigned threadrace_thread(void* arg) {
2054
#endif
2055
    pid_t threadid       = _sir_gettid();
12✔
2056
    thread_args* my_args = (thread_args*)arg;
×
2057

2058
    rmfile(my_args->log_file);
12✔
2059
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
12✔
2060

2061
    if (0 == id) {
12✔
2062
        bool unused = print_test_error(false, false);
×
2063
        SIR_UNUSED(unused);
2064
#if !defined(__WIN__)
2065
        return NULL;
×
2066
#else /* __WIN__ */
2067
        return 0;
2068
#endif
2069
    }
2070

2071
    printf("\thi, i'm thread (id: %d), logging to: '%s'...\n",
×
2072
            PID_CAST threadid, my_args->log_file);
×
2073

2074
#if !defined(DUMA)
2075
# define NUM_ITERATIONS 1000
2076
#else
2077
# define NUM_ITERATIONS 100
2078
#endif
2079

2080
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
12,012✔
2081
        /* choose a random level, and colors. */
2082
        sir_textcolor fg = SIRTC_INVALID;
×
2083
        sir_textcolor bg = SIRTC_INVALID;
×
2084

2085
        if (n % 2 == 0) {
12,000✔
2086
            fg = SIRTC_CYAN;
×
2087
            bg = SIRTC_BLACK;
×
2088
            sir_debug("log message #%zu", n);
6,000✔
2089
        } else {
2090
            fg = SIRTC_BLACK;
×
2091
            bg = SIRTC_CYAN;
×
2092
            sir_info("log message #%zu", n);
6,000✔
2093
        }
2094

2095
        /* sometimes remove and re-add the log file, and set some options/styles.
2096
         * other times, just set different options/styles. */
2097
        uint32_t rnd = (uint32_t)(n + threadid);
12,000✔
2098
        if (getrand_bool((uint32_t)(rnd > 1 ? rnd : 1))) {
12,000✔
2099
            my_args->pass = print_test_error(sir_remfile(id), false);
6,153✔
2100
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
6,153✔
2101
                SIRL_ALL, SIRO_MSGONLY), false);
2102

2103
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
12,306✔
2104
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
6,153✔
2105
            my_args->pass = print_test_error(test, false);
6,153✔
2106

2107
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
6,153✔
2108
            my_args->pass = print_test_error(test, false);
6,153✔
2109
        } else {
2110
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
11,694✔
2111
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
5,847✔
2112
            my_args->pass = print_test_error(test, false);
5,847✔
2113

2114
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
5,847✔
2115
            my_args->pass = print_test_error(test, false);
5,847✔
2116

2117
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
5,847✔
2118
            my_args->pass = print_test_error(test, false);
5,847✔
2119
        }
2120

2121
        if (!my_args->pass)
12,000✔
2122
            break;
×
2123
    }
2124

2125
    my_args->pass = print_test_error(sir_remfile(id), false);
12✔
2126

2127
    rmfile(my_args->log_file);
12✔
2128

2129
#if !defined(__WIN__)
2130
    return NULL;
12✔
2131
#else /* __WIN__ */
2132
    return 0;
2133
#endif
2134
}
2135

2136
/*
2137
bool sirtest_XXX(void) {
2138
    INIT(si, SIRL_ALL, 0, 0, 0);
2139
    bool pass = si_init;
2140

2141
    pass &= sir_cleanup();
2142
    return print_result_and_return(pass);
2143
}
2144
*/
2145

2146
/* ========================== end tests ========================== */
2147

2148
bool print_test_error(bool result, bool expected) {
42,357✔
2149
    char message[SIR_MAXERROR] = {0};
42,357✔
2150
    uint16_t code              = sir_geterror(message);
42,357✔
2151

2152
    if (!expected && !result && SIR_E_NOERROR != code)
42,357✔
2153
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
×
2154
    else if (expected && SIR_E_NOERROR != code)
42,357✔
2155
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
87✔
2156

2157
    return result;
42,357✔
2158
}
2159

2160
bool print_os_error(void) {
×
2161
    char message[SIR_MAXERROR] = {0};
×
2162
    uint16_t code              = sir_geterror(message);
×
2163
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
×
2164
    return false;
×
2165
}
2166

2167
bool filter_error(bool pass, uint16_t err) {
240✔
2168
    if (!pass) {
240✔
2169
        char message[SIR_MAXERROR] = {0};
153✔
2170
        uint16_t code              = sir_geterror(message);
153✔
2171
        if (code != err)
153✔
2172
            return false;
×
2173
    }
2174
    return true;
×
2175
}
2176

2177
uint32_t getrand(uint32_t upper_bound) {
17,487✔
2178
#if !defined(__WIN__)
2179
# if defined(__MACOS__) || defined(__BSD__)
2180
    return arc4random_uniform(upper_bound);
2181
# else
2182
    return (uint32_t)(random() % upper_bound);
17,487✔
2183
# endif
2184
#else /* __WIN__ */
2185
    uint32_t ctx = 0;
2186
    if (0 != rand_s(&ctx))
2187
        ctx = (uint32_t)rand();
2188
    return ctx % upper_bound;
2189
#endif
2190
}
2191

2192
bool rmfile(const char* filename) {
147✔
2193
    bool removed = false;
×
2194

2195
    /* return true if leave_logs is true. */
2196
    if (leave_logs) {
147✔
2197
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
×
2198
            filename, _cl_arg_list[3].flag);
×
2199
        return true;
×
2200
    }
2201

2202
    /* return true if the file doesn't exist. */
2203
    struct stat st;
2204
    if (0 != stat(filename, &st)) {
147✔
2205
        if (ENOENT == errno)
66✔
2206
            return true;
×
2207

2208
        handle_os_error(true, "failed to stat %s!", filename);
×
2209
        return false;
×
2210
    }
2211

2212
    if (!_sir_deletefile(filename)) {
81✔
2213
        handle_os_error(false, "failed to delete %s!", filename);
×
2214
    } else {
2215
        printf("\t" DGRAY("deleted %s (%ld bytes)...") "\n", filename,
×
2216
            (long)st.st_size);
81✔
2217
    }
2218

2219
    return removed;
×
2220
}
2221

2222
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
18✔
2223
    if (strstr(filename, search)) {
18✔
2224
        char filepath[SIR_MAXPATH];
2225
        (void)snprintf(filepath, SIR_MAXPATH, "%s%s", path, filename);
×
2226

2227
        rmfile(filepath);
6✔
2228
        (*data)++;
6✔
2229
    }
2230
}
18✔
2231

2232
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
12✔
2233
    SIR_UNUSED(path);
2234
    if (strstr(filename, search))
12✔
2235
        (*data)++;
6✔
2236
}
12✔
2237

2238
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
9✔
2239
#if !defined(__WIN__)
2240
    DIR* d = opendir(path);
9✔
2241
    if (!d)
9✔
2242
        return print_os_error();
×
2243

2244
    rewinddir(d);
9✔
2245
    struct dirent* di = readdir(d);
9✔
2246
    if (!di) {
9✔
2247
        closedir(d);
×
2248
        return print_os_error();
×
2249
    }
2250

2251
    while (NULL != di) {
39✔
2252
        cb(search, path, di->d_name, data);
30✔
2253
        di = readdir(d);
30✔
2254
    };
2255

2256
    closedir(d);
9✔
2257
    d = NULL;
×
2258
#else /* __WIN__ */
2259
    WIN32_FIND_DATA finddata = {0};
2260
    char buf[SIR_MAXPATH]    = {0};
2261

2262
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2263

2264
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2265

2266
    if (INVALID_HANDLE_VALUE == enumerator)
2267
        return false;
2268

2269
    do {
2270
        cb(search, path, finddata.cFileName, data);
2271
    } while (FindNextFile(enumerator, &finddata) > 0);
2272

2273
    FindClose(enumerator);
2274
    enumerator = NULL;
2275
#endif
2276

2277
    return true;
9✔
2278
}
2279

2280
bool sirtimerstart(sir_timer* timer) {
9✔
2281
#if !defined(__WIN__)
2282
    int gettime = clock_gettime(SIRTEST_CLOCK, &timer->ts);
9✔
2283
    if (0 != gettime) {
9✔
2284
        handle_os_error(true, "clock_gettime(%d) failed!", CLOCK_CAST SIRTEST_CLOCK);
×
2285
    }
2286

2287
    return 0 == gettime;
9✔
2288
#else /* __WIN__ */
2289
    GetSystemTimePreciseAsFileTime(&timer->ft);
2290
    return true;
2291
#endif
2292
}
2293

2294
float sirtimerelapsed(const sir_timer* timer) {
63✔
2295
#if !defined(__WIN__)
2296
    struct timespec now;
2297
    if (0 == clock_gettime(SIRTEST_CLOCK, &now)) {
63✔
2298
        return (float)(((double)now.tv_sec * (double)1e3)
63✔
2299
            + ((double)now.tv_nsec / (double)1e6)
63✔
2300
            - ((double)timer->ts.tv_sec * (double)1e3)
63✔
2301
            + ((double)timer->ts.tv_nsec / (double)1e6));
63✔
2302
    } else {
2303
        handle_os_error(true, "clock_gettime(%d) failed!", CLOCK_CAST SIRTEST_CLOCK);
×
2304
    }
2305
    return 0.0f;
×
2306
#else /* __WIN__ */
2307
    FILETIME now;
2308
    GetSystemTimePreciseAsFileTime(&now);
2309
    ULARGE_INTEGER start   = {0};
2310
    start.LowPart          = timer->ft.dwLowDateTime;
2311
    start.HighPart         = timer->ft.dwHighDateTime;
2312
    ULARGE_INTEGER n100sec = {0};
2313
    n100sec.LowPart        = now.dwLowDateTime;
2314
    n100sec.HighPart       = now.dwHighDateTime;
2315
    return (float)((n100sec.QuadPart - start.QuadPart) / 1e4);
2316
#endif
2317
}
2318

2319
long sirtimergetres(void) {
×
2320
    long retval = 0;
×
2321
#if !defined(__WIN__)
2322
    struct timespec res;
2323
    if (0 == clock_getres(SIRTEST_CLOCK, &res)) {
×
2324
        retval = res.tv_nsec;
×
2325
    } else {
2326
        handle_os_error(true, "clock_getres(%d) failed!", CLOCK_CAST SIRTEST_CLOCK); // GCOVR_EXCL_LINE
2327
    }
2328
#else /* __WIN__ */
2329
    retval = 100;
2330
#endif
2331
    return retval;
×
2332
}
2333

2334
#if defined(SIR_OS_LOG_ENABLED)
2335
void os_log_parent_activity(void* ctx) {
2336
    sir_debug("confirming with ground control that we are a go...");
2337
    sir_info("all systems go; initiating launch sequence");
2338
    sir_warn("getting some excessive vibration here");
2339
    sir_info("safely reached escape velocity. catch you on the flip side");
2340
    sir_info("(3 days later) we have landed on the lunar surface");
2341
    sir_notice("beginning rock counting...");
2342

2343
    os_activity_t parent = (os_activity_t)ctx;
2344
    os_activity_t child = os_activity_create("counting moon rocks", parent,
2345
        OS_ACTIVITY_FLAG_DEFAULT);
2346

2347
    float rock_count = 0.0f;
2348
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2349
    sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2350
        rock_count);
2351
}
2352

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

2356
    for (size_t n = 0; n < 10; n++) {
2357
        sir_info("counting rocks in sector %zu...", n);
2358
    }
2359

2360
    float* rock_count = (float*)ctx;
2361
    *rock_count = 1e12;
2362
    sir_info("all sectors counted; heading back to the lunar lander");
2363
}
2364
#endif
2365

2366
bool mark_test_to_run(const char* name) {
1✔
2367
    bool found = false;
1✔
2368
    for (size_t t = 0; t < _sir_countof(sir_tests); t++) {
32✔
2369
        if (_sir_strsame(name, sir_tests[t].name,
31✔
2370
            strnlen(sir_tests[t].name, SIR_MAXTESTNAME))) {
2371
            found = sir_tests[t].run = true;
×
2372
            break;
×
2373
        }
2374
    }
2375

2376
    if (!found)
1✔
2377
        _sir_selflog("warning: unable to locate '%s' in test array", name); // GCOVR_EXCL_LINE
2378

2379
    return found;
1✔
2380
}
2381

2382
void print_usage_info(void) {
4✔
2383
    size_t longest = 0;
4✔
2384
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2385
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2386
        if (len > longest)
28✔
2387
            longest = len;
8✔
2388
    }
2389

2390
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2391

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

2395
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2396
        if (len < longest)
28✔
2397
            for (size_t n = len; n < longest; n++)
156✔
2398
                fprintf(stderr, " ");
132✔
2399

2400
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2401
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2402
            _cl_arg_list[i].desc);
28✔
2403
    }
2404

2405
    fprintf(stderr, "\n");
4✔
2406
}
4✔
2407

2408
void print_test_list(void) {
1✔
2409
    size_t longest = 0;
1✔
2410
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
32✔
2411
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2412
        if (len > longest)
31✔
2413
            longest = len;
3✔
2414
    }
2415

2416
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2417

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

2421
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
31✔
2422
        if (len < longest)
31✔
2423
            for (size_t n = len; n < longest; n++)
270✔
2424
                printf(" ");
240✔
2425

2426
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
31✔
2427
            printf("\n");
16✔
2428
    }
2429

2430
    printf("\n");
1✔
2431
}
1✔
2432

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