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

aremmell / libsir / 503

07 Sep 2023 03:38PM UTC coverage: 94.705% (-0.02%) from 94.721%
503

Pull #268

gitlab-ci

web-flow
Merge branch 'master' into win-file-io
Pull Request #268: On Windows, use native Win32 API for file I/O

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

3112 of 3286 relevant lines covered (94.7%)

617431.29 hits per line

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

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

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

65
static bool leave_logs = false;
66

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

72
#include "tests_malloc.h"
73

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

89
    bool wait     = false;
28✔
90
    bool only     = false;
28✔
91
    size_t to_run = 0;
28✔
92

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

143
    size_t first     = (only ? 0 : 1);
25✔
144
    size_t tgt_tests = (only ? to_run : _sir_countof(sir_tests) - first);
25✔
145
    size_t passed    = 0;
22✔
146
    size_t ran       = 0;
22✔
147
    sir_time timer  = {0};
25✔
148

149
    printf(WHITEB("\n" ULINE("libsir") " %s (%s) running %zu %s...") "\n",
50✔
150
        sir_getversionstring(), (sir_isprerelease() ? "prerelease" : "release"),
25✔
151
        tgt_tests, TEST_S(tgt_tests));
152
    sir_timer_start(&timer);
25✔
153

154
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
827✔
155
        if (only && !sir_tests[n].run) {
802✔
156
            _sir_selflog("skipping '%s'; not marked to run", sir_tests[n].name);
64✔
157
            continue;
64✔
158
        }
159

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

162
        sir_tests[n].pass = sir_tests[n].fn();
738✔
163
        if (sir_tests[n].pass)
738✔
164
            passed++;
685✔
165

166
        ran++;
642✔
167

168
        printf(WHITEB("\n(%zu/%zu) '%s' finished: ") "%s\n", ran, tgt_tests, sir_tests[n].name,
738✔
169
            PRN_PASS(sir_tests[n].pass));
642✔
170
    }
171

172
    double elapsed = sir_timer_elapsed(&timer);
25✔
173

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

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

185
        for (size_t t = 0; t < _sir_countof(sir_tests); t++)
544✔
186
            if (!sir_tests[t].pass)
528✔
187
                printf(RED(INDENT_ITEM "%s\n"), sir_tests[t].name);
53✔
188
        printf("\n");
16✔
189
    }
190

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

198
    return passed == tgt_tests ? EXIT_SUCCESS : EXIT_FAILURE;
25✔
199
}
200

201
bool sirtest_exceedmaxsize(void) {
23✔
202
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
203
    bool pass = si_init;
20✔
204

205
    char toobig[SIR_MAXMESSAGE + 100] = {0};
20✔
206
    memset(toobig, 'a', SIR_MAXMESSAGE + 100);
20✔
207
    toobig[SIR_MAXMESSAGE + 99] = '\0';
23✔
208

209
    _sir_eqland(pass, sir_info("%s", toobig));
23✔
210

211
    _sir_eqland(pass, sir_cleanup());
23✔
212
    return print_result_and_return(pass);
23✔
213
}
214

215
bool sirtest_logwritesanity(void) {
23✔
216
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
217
    bool pass = si_init;
20✔
218

219
    static const char* logfilename = MAKE_LOG_NAME("write-validate.log");
220
    static const char* message     = "Lorem ipsum dolor sit amet, sea ei dicit"
221
                                     " regione laboramus, eos cu minim putent."
222
                                     " Sale omnium conceptam est in, cu nam possim"
223
                                     " prompta eleifend. Duo purto nostrud eu."
224
                                     " Alia accumsan has cu, mentitum invenire"
225
                                     " mel an, dicta noster legendos et pro."
226
                                     " Solum nobis laboramus et quo, nam putant"
227
                                     " dolores consequuntur ex. Sit veniam eruditi"
228
                                     " contentiones at. Cu ponderum oporteat"
229
                                     " oportere mel, has et saperet accusata"
230
                                     " complectitur.";
231

232
    printf("\tadding log file '%s' to libsir...\n", logfilename);
23✔
233
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
23✔
234
    _sir_eqland(pass, 0U != id);
23✔
235

236
    print_test_error(pass, false);
23✔
237

238
    printf("\twriting message to stdout and %s...\n", logfilename);
23✔
239

240
    _sir_eqland(pass, sir_debug("%s", message));
23✔
241

242
    print_test_error(pass, false);
23✔
243

244
    printf("\tremoving %s from libsir...\n", logfilename);
23✔
245
    _sir_eqland(pass, sir_remfile(id));
23✔
246

247
    print_test_error(pass, false);
23✔
248

249
    printf("\topening %s for reading...\n", logfilename);
23✔
250

251
    FILE* f = fopen(logfilename, "r");
23✔
252
    if (!f) {
23✔
253
        pass = false;
3✔
254
    } else {
255
        char buf[512] = {0};
20✔
256
        _sir_eqland(pass, 0 != sir_readline(f, buf, 512));
20✔
257

258
        bool found = NULL != strstr(buf, message);
20✔
259
        _sir_eqland(pass, found);
20✔
260

261
        if (found)
20✔
262
            printf("\t" GREEN("found '%s'") "\n", message);
17✔
263
        else
264
            printf("\t" RED("did not find '%s'") "\n", message);
×
265

266
        _sir_safefclose(&f);
20✔
267
        printf("\tdeleting %s...\n", logfilename);
20✔
268
        (void)rmfile(logfilename);
20✔
269
    }
270

271
    _sir_eqland(pass, sir_cleanup());
23✔
272
    return print_result_and_return(pass);
23✔
273
}
274

275
bool sirtest_threadidsanity(void)
23✔
276
{
277
#if defined(SIR_NO_THREAD_NAMES)
278
    printf("\t" DGRAY("test skipped for this system configuration") "\n");
279
    return true;
280
#endif
281
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
282
    bool pass = si_init;
20✔
283

284
    static const char* thread_name = "mythread";
285
    static const char* logfilename = MAKE_LOG_NAME("thread-id-name.log");
286

287
    printf("\tadding log file '%s' to libsir...\n", logfilename);
23✔
288
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
23✔
289
    _sir_eqland(pass, 0U != id);
23✔
290

291
    print_test_error(pass, false);
23✔
292

293
    printf("\tlogging a message normally...\n");
20✔
294
    _sir_eqland(pass, sir_debug("this is a test of the libsir system"));
23✔
295

296
    printf("\tsetting the thread name to '%s' and logging again...\n", thread_name);
23✔
297

298
    _sir_eqland(pass, _sir_setthreadname(thread_name));
23✔
299
    sir_sleep_msec((uint32_t)SIR_THRD_CHK_INTERVAL + 200U);
23✔
300

301
    print_test_error(pass, false);
23✔
302

303
    _sir_eqland(pass, sir_debug("this is a test of the libsir system after setting thread name"));
23✔
304

305
    printf("\tsetting the thread name to '' and logging again...\n");
20✔
306

307
    _sir_eqland(pass, _sir_setthreadname(""));
23✔
308
    sir_sleep_msec((uint32_t)SIR_THRD_CHK_INTERVAL + 200U);
23✔
309

310
    print_test_error(pass, false);
23✔
311

312
    _sir_eqland(pass, sir_debug("this is a test of the libsir system after clearing thread name"));
23✔
313

314
     /* remove the log file from libsir, then open it and read it line by line. */
315
    printf("\tremoving %s from libsir...\n", logfilename);
23✔
316
    _sir_eqland(pass, sir_remfile(id));
23✔
317

318
    printf("\topening %s for reading...\n", logfilename);
23✔
319

320
    FILE* f = fopen(logfilename, "r");
23✔
321
    if (!f) {
23✔
322
        pass = false;
3✔
323
    } else {
324
        /* look for, in order, TID, thread name, TID. */
325
        for (size_t n = 0; n < 3; n++) {
80✔
326
            char buf[256] = {0};
60✔
327
            _sir_eqland(pass, 0 != sir_readline(f, buf, 256));
60✔
328
            printf("\tread line %zu: '%s'\n", n, buf);
51✔
329

330
            char search[SIR_MAXPID] = {0};
60✔
331
            switch (n) {
60✔
332
                case 0:
40✔
333
                case 2:
334
                    (void)snprintf(search, SIR_MAXPID, SIR_PIDFORMAT, _sir_gettid());
40✔
335
                break;
34✔
336
                case 1:
20✔
337
                    (void)_sir_strncpy(search, SIR_MAXPID, thread_name, strlen(thread_name));
20✔
338
                break;
20✔
339
                default: break; // GCOVR_EXCL_LINE
340
            }
341

342
            bool found = NULL != strstr(buf, search);
60✔
343
            _sir_eqland(pass, found);
60✔
344

345
            if (found)
60✔
346
                printf("\t" GREEN("line %zu: found '%s'") "\n", n, search);
50✔
347
            else
348
                printf("\t" RED("line %zu: did not find '%s'") "\n", n, search);
1✔
349
        }
350

351
        _sir_safefclose(&f);
20✔
352
        printf("\tdeleting %s...\n", logfilename);
20✔
353
        (void)rmfile(logfilename);
20✔
354
    }
355

356
    _sir_eqland(pass, sir_cleanup());
23✔
357
    return print_result_and_return(pass);
23✔
358
}
359

360
bool sirtest_failnooutputdest(void) {
23✔
361
    INIT(si, 0, 0, 0, 0);
23✔
362
    bool pass = si_init;
20✔
363

364
    static const char* logfilename = MAKE_LOG_NAME("nodestination.log");
365

366
    _sir_eqland(pass, !sir_notice("this goes nowhere!"));
23✔
367

368
    if (pass) {
20✔
369
        print_expected_error();
23✔
370

371
        _sir_eqland(pass, sir_stdoutlevels(SIRL_INFO));
23✔
372
        _sir_eqland(pass, sir_info("this goes to stdout"));
23✔
373
        _sir_eqland(pass, sir_stdoutlevels(SIRL_NONE));
23✔
374

375
        sirfileid fid = sir_addfile(logfilename, SIRL_INFO, SIRO_DEFAULT);
23✔
376
        _sir_eqland(pass, 0U != fid);
23✔
377
        _sir_eqland(pass, sir_info("this goes to %s", logfilename));
23✔
378
        _sir_eqland(pass, sir_filelevels(fid, SIRL_NONE));
23✔
379
        _sir_eqland(pass, !sir_notice("this goes nowhere!"));
23✔
380

381
        if (0U != fid)
23✔
382
            _sir_eqland(pass, sir_remfile(fid));
20✔
383

384
        (void)rmfile(logfilename);
23✔
385
    }
386

387
    _sir_eqland(pass, sir_cleanup());
23✔
388
    return print_result_and_return(pass);
23✔
389
}
390

391
bool sirtest_failnulls(void) {
23✔
392
    INIT_BASE(si, SIRL_ALL, 0, 0, 0, "", false);
23✔
393
    bool pass = true;
20✔
394

395
    _sir_eqland(pass, !sir_init(NULL));
23✔
396

397
    if (pass)
20✔
398
        print_expected_error();
23✔
399

400
    _sir_eqland(pass, sir_init(&si));
23✔
401
    _sir_eqland(pass, !sir_info(NULL)); //-V575 //-V618
23✔
402

403
    if (pass)
20✔
404
        print_expected_error();
23✔
405

406
    _sir_eqland(pass, 0U == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY));
23✔
407

408
    if (pass)
20✔
409
        print_expected_error();
23✔
410

411
    _sir_eqland(pass, !sir_remfile(0U));
23✔
412

413
    if (pass)
20✔
414
        print_expected_error();
23✔
415

416
    _sir_eqland(pass, sir_cleanup());
23✔
417
    return print_result_and_return(pass);
23✔
418
}
419

420
bool sirtest_failemptymessage(void) {
23✔
421
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
422
    bool pass = si_init;
20✔
423

424
    _sir_eqland(pass, !sir_debug("%s", ""));
23✔
425

426
    _sir_eqland(pass, sir_cleanup());
23✔
427
    return print_result_and_return(pass);
23✔
428
}
429

430
bool sirtest_filecachesanity(void) {
23✔
431
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
432
    bool pass = si_init;
20✔
433

434
    size_t numfiles             = SIR_MAXFILES + 1;
20✔
435
    sirfileid ids[SIR_MAXFILES] = {0};
23✔
436

437
    sir_options even = SIRO_MSGONLY;
20✔
438
    sir_options odd  = SIRO_ALL;
20✔
439

440
    for (size_t n = 0; n < numfiles - 1; n++) {
391✔
441
        char path[SIR_MAXPATH] = {0};
368✔
442
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), n);
320✔
443
        (void)rmfile(path);
368✔
444
        ids[n] = sir_addfile(path, SIRL_ALL, (n % 2) ? odd : even);
392✔
445
        _sir_eqland(pass, 0U != ids[n] && sir_info("test %zu", n));
368✔
446
    }
447

448
    _sir_eqland(pass, sir_info("test test test"));
23✔
449

450
    /* this one should fail; max files already added. */
451
    _sir_eqland(pass, 0U == sir_addfile(MAKE_LOG_NAME("should-fail.log"),
23✔
452
        SIRL_ALL, SIRO_MSGONLY));
453

454
    if (pass)
20✔
455
        print_expected_error();
19✔
456

457
    sir_info("test test test");
23✔
458

459
    /* now remove previously added files in a different order. */
460
    size_t removeorder[SIR_MAXFILES];
461
    memset(removeorder, -1, sizeof(removeorder));
20✔
462

463
    long processed = 0L;
20✔
464
    printf("\tcreating random file ID order...\n");
20✔
465

466
    do {
1,104✔
467
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,286✔
468
        bool skip  = false;
1,124✔
469

470
        for (size_t n = 0; n < SIR_MAXFILES; n++)
12,280✔
471
            if (removeorder[n] == rnd) {
11,912✔
472
                skip = true;
804✔
473
                break;
804✔
474
            }
475

476
        if (skip)
1,286✔
477
            continue;
918✔
478

479
        removeorder[processed++] = rnd;
368✔
480

481
        if (processed == SIR_MAXFILES)
368✔
482
            break;
20✔
483
    } while (true);
484

485
    printf("\tremove order: {");
20✔
486
    for (size_t n = 0; n < SIR_MAXFILES; n++)
391✔
487
        printf(" %zu%s", removeorder[n], (n < SIR_MAXFILES - 1) ? "," : "");
371✔
488
    printf(" }...\n");
20✔
489

490
    for (size_t n = 0; n < SIR_MAXFILES; n++) {
391✔
491
        _sir_eqland(pass, sir_remfile(ids[removeorder[n]]));
368✔
492

493
        char path[SIR_MAXPATH] = {0};
368✔
494
        (void)snprintf(path, SIR_MAXPATH, MAKE_LOG_NAME("test-%zu.log"), removeorder[n]);
368✔
495
        (void)rmfile(path);
368✔
496
    }
497

498
    _sir_eqland(pass, sir_info("test test test"));
23✔
499

500
    _sir_eqland(pass, sir_cleanup());
23✔
501
    return print_result_and_return(pass);
23✔
502
}
503

504
bool sirtest_failinvalidfilename(void) {
23✔
505
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
506
    bool pass = si_init;
20✔
507

508
    _sir_eqland(pass, 0U == sir_addfile("bad file!/name", SIRL_ALL, SIRO_MSGONLY));
23✔
509

510
    if (pass)
20✔
511
        print_expected_error();
23✔
512

513
    _sir_eqland(pass, sir_cleanup());
23✔
514
    return print_result_and_return(pass);
23✔
515
}
516

517
bool sirtest_failfilebadpermission(void) {
23✔
518
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
519
    bool pass = si_init;
20✔
520

521
#if !defined(__WIN__)
522
    static const char* path = "/noperms";
523
#else /* __WIN__ */
524
# if defined(__CYGWIN__)
525
    static const char* path = "/cygdrive/c/Windows/System32/noperms";
526
# else
527
    static const char* path;
528
    if (sirtest_get_wineversion()) {
529
        path = "Z:\\noperms";
530
    } else {
531
        path = "C:\\Windows\\System32\\noperms";
532
    }
533
# endif
534
#endif
535

536
    _sir_eqland(pass, 0U == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY));
23✔
537

538
    if (pass)
20✔
539
        print_expected_error();
23✔
540

541
    _sir_eqland(pass, sir_cleanup());
23✔
542
    return print_result_and_return(pass);
23✔
543
}
544

545
bool sirtest_faildupefile(void) {
23✔
546
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
547
    bool pass = si_init;
20✔
548

549
#if !defined(__WIN__)
550
    static const char* filename1 = "./logs/faildupefile.log";
551
    static const char* filename2 = "logs/faildupefile.log";
552
#else
553
    static const char* filename1 = "logs\\faildupefile.log";
554
    static const char* filename2 = "logs/faildupefile.log";
555
#endif
556

557
    static const char* filename3 = "logs/not-a-dupe.log";
558
    static const char* filename4 = "logs/also-not-a-dupe.log";
559

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

562
    /* should be fine; no other files added yet. */
563
    sirfileid fid = sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT);
23✔
564
    _sir_eqland(pass, 0U != fid);
23✔
565

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

568
    /* should fail. this is the same file we already added. */
569
    _sir_eqland(pass, 0U == sir_addfile(filename1, SIRL_ALL, SIRO_DEFAULT));
23✔
570

571
    if (pass)
20✔
572
        print_expected_error();
20✔
573

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

576
    /* should also fail. this is the same file we already added, even
577
     * if the path strings don't match. */
578
    _sir_eqland(pass, 0U == sir_addfile(filename2, SIRL_ALL, SIRO_DEFAULT));
23✔
579

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

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

585
    /* should pass. this is a different file. */
586
    sirfileid fid2 = sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT);
23✔
587
    _sir_eqland(pass, 0U != fid2);
23✔
588

589
    /* should also pass. */
590
    sirfileid fid3 = sir_addfile(filename4, SIRL_ALL, SIRO_DEFAULT);
23✔
591
    _sir_eqland(pass, 0U != fid3);
23✔
592

593
    _sir_eqland(pass, sir_info("hello three valid files"));
23✔
594

595
    /* should now fail since we added it earlier. */
596
    _sir_eqland(pass, 0U == sir_addfile(filename3, SIRL_ALL, SIRO_DEFAULT));
23✔
597

598
    if (pass)
20✔
599
        print_expected_error();
19✔
600

601
    /* don't remove all of the log files in order to also test
602
     * cache tear-down. */
603
    _sir_eqland(pass, sir_remfile(fid));
23✔
604
    _sir_eqland(pass, sir_cleanup());
23✔
605

606
    (void)rmfile(filename1);
23✔
607
    (void)rmfile(filename2);
23✔
608
    (void)rmfile(filename3);
23✔
609
    (void)rmfile(filename4);
23✔
610

611
    return print_result_and_return(pass);
23✔
612
}
613

614
bool sirtest_failremovebadfile(void) {
23✔
615
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
616
    bool pass = si_init;
20✔
617

618
    sirfileid invalidid = 9999999;
20✔
619
    _sir_eqland(pass, !sir_remfile(invalidid));
23✔
620

621
    if (pass)
20✔
622
        print_expected_error();
23✔
623

624
    _sir_eqland(pass, sir_cleanup());
23✔
625
    return print_result_and_return(pass);
23✔
626
}
627

628
bool sirtest_rollandarchivefile(void) {
24✔
629
    /* roll size minus 1KiB so we can write until it maxes. */
630
    static const long deltasize    = 1024L;
631
    const long fillsize            = SIR_FROLLSIZE - deltasize;
21✔
632
    static const char* logbasename = "rollandarchive";
633
    static const char* logext      = ".log";
634
    static const char* line        = "hello, i am some data. nice to meet you.";
635

636
    char logfilename[SIR_MAXPATH] = {0};
24✔
637
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
24✔
638

639
    printf("\tdeleting any stale logs from a previous run...\n");
21✔
640

641
    unsigned delcount = 0U;
24✔
642
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
24✔
643
        handle_os_error(false, "failed to enumerate log files with base name: %s!",
2✔
644
            logbasename);
645
        return false;
2✔
646
    }
647

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

651
    FILE* f = NULL;
22✔
652
    _sir_fopen(&f, logfilename, "w");
22✔
653

654
    if (!f)
22✔
655
        return print_os_error();
×
656

657
    printf("\tfilling %s nearly to SIR_FROLLSIZE...\n", logfilename);
19✔
658

659
    if (0 != fseek(f, fillsize, SEEK_SET)) {
22✔
660
        handle_os_error(true, "fseek in file %s failed!", logfilename);
1✔
661
        _sir_safefclose(&f);
1✔
662
        return false;
1✔
663
    }
664

665
    if (EOF == fputc('\0', f)) {
21✔
666
        handle_os_error(true, "fputc in file %s failed!", logfilename);
1✔
667
        _sir_safefclose(&f);
1✔
668
        return false;
1✔
669
    }
670

671
    _sir_safefclose(&f);
20✔
672

673
    INIT(si, 0, 0, 0, 0);
20✔
674
    bool pass = si_init;
17✔
675

676
    printf("\tadding %s to libsir...\n", logfilename);
17✔
677

678
    sirfileid fileid = sir_addfile(logfilename, SIRL_DEBUG, SIRO_MSGONLY | SIRO_NOHDR);
20✔
679
    _sir_eqland(pass, 0U != fileid);
20✔
680

681
    print_test_error(pass, false);
20✔
682

683
    if (pass) {
20✔
684
        printf("\twriting to %s until SIR_FROLLSIZE has been exceeded...\n", logfilename);
15✔
685
        /* write an (approximately) known quantity until we should have rolled */
686
        size_t written  = 0;
15✔
687
        size_t linesize = strnlen(line, SIR_MAXMESSAGE);
18✔
688

689
        do {
690
            _sir_eqland(pass, sir_debug("%zu %s", written, line));
1,368✔
691
            written += linesize;
1,368✔
692
        } while (pass && (written < deltasize + (linesize * 50)));
1,368✔
693

694
        printf("\tlooking for two log files, since it should have been rolled...\n");
15✔
695

696
        /* look for files matching the original name. */
697
        unsigned foundlogs = 0U;
18✔
698
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
18✔
699
            handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
700
            pass = false;
×
701
        }
702

703
        /* if two (or more) are present, the test is a pass. */
704
        printf("\tfound %u log files with base name: %s\n", foundlogs, logbasename);
18✔
705
        _sir_eqland(pass, foundlogs >= 2U);
18✔
706
    }
707

708
    _sir_eqland(pass, sir_remfile(fileid));
20✔
709

710
    delcount = 0U;
20✔
711
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
20✔
712
        handle_os_error(false, "failed to enumerate log files with base name: %s!", logbasename);
×
713
        pass = false;
×
714
    }
715

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

719
    _sir_eqland(pass, sir_cleanup());
20✔
720
    return print_result_and_return(pass);
20✔
721
}
722

723
bool sirtest_failwithoutinit(void) {
23✔
724
    bool pass = !sir_info("sir isn't initialized; this needs to fail");
23✔
725

726
    if (pass)
23✔
727
        print_expected_error();
23✔
728

729
    return print_result_and_return(pass);
23✔
730
}
731

732
bool sirtest_failinittwice(void) {
23✔
733
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
734
    bool pass = si_init;
20✔
735

736
    INIT(si2, SIRL_ALL, 0, 0, 0);
23✔
737
    _sir_eqland(pass, !si2_init);
23✔
738

739
    if (pass)
23✔
740
        print_expected_error();
23✔
741

742
    _sir_eqland(pass, sir_cleanup());
23✔
743
    return print_result_and_return(pass);
23✔
744
}
745

746
bool sirtest_failinvalidinitdata(void) {
23✔
747
    sirinit si;
748

749
    /* fill with bad data. */
750
    memset(&si, 0xab, sizeof(sirinit));
20✔
751

752
    printf("\tcalling sir_init with invalid data...\n");
20✔
753
    bool pass = !sir_init(&si);
23✔
754

755
    if (pass)
23✔
756
        print_expected_error();
23✔
757

758
    (void)sir_cleanup();
23✔
759
    return print_result_and_return(pass);
23✔
760
}
761

762
bool sirtest_initcleanupinit(void) {
23✔
763
    INIT(si1, SIRL_ALL, 0, 0, 0);
23✔
764
    bool pass = si1_init;
20✔
765

766
    _sir_eqland(pass, sir_info("init called once; testing output..."));
23✔
767
    _sir_eqland(pass, sir_cleanup());
23✔
768

769
    INIT(si2, SIRL_ALL, 0, 0, 0);
23✔
770
    _sir_eqland(pass, si2_init);
23✔
771

772
    _sir_eqland(pass, sir_info("init called again after re-init; testing output..."));
23✔
773
    _sir_eqland(pass, sir_cleanup());
23✔
774

775
    return print_result_and_return(pass);
23✔
776
}
777

778
bool sirtest_initmakeinit(void) {
23✔
779
    bool pass = true;
20✔
780

781
    sirinit si;
782
    _sir_eqland(pass, sir_makeinit(&si));
23✔
783
    _sir_eqland(pass, sir_init(&si));
23✔
784
    _sir_eqland(pass, sir_info("initialized with sir_makeinit"));
23✔
785
    _sir_eqland(pass, sir_cleanup());
23✔
786

787
    return print_result_and_return(pass);
23✔
788
}
789

790
bool sirtest_failaftercleanup(void) {
23✔
791
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
792
    bool pass = si_init;
20✔
793

794
    _sir_eqland(pass, sir_cleanup());
23✔
795
    _sir_eqland(pass, !sir_info("already cleaned up; this needs to fail"));
23✔
796

797
    if (pass)
20✔
798
        print_expected_error();
23✔
799

800
    return print_result_and_return(pass);
23✔
801
}
802

803
bool sirtest_errorsanity(void) {
23✔
804
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
805
    bool pass = si_init;
20✔
806

807
    struct {
808
        uint16_t code;
809
        const char* name;
810
    } errors[] = {
23✔
811
        {SIR_E_NOERROR,   "SIR_E_NOERROR"},   /**< The operation completed successfully (0) */
812
        {SIR_E_NOTREADY,  "SIR_E_NOTREADY"},  /**< libsir has not been initialized (1) */
813
        {SIR_E_ALREADY,   "SIR_E_ALREADY"},   /**< libsir is already initialized (2) */
814
        {SIR_E_DUPITEM,   "SIR_E_DUPITEM"},   /**< Item already managed by libsir (3) */
815
        {SIR_E_NOITEM,    "SIR_E_NOITEM"},    /**< Item not managed by libsir (4) */
816
        {SIR_E_NOROOM,    "SIR_E_NOROOM"},    /**< Maximum number of items already stored (5) */
817
        {SIR_E_OPTIONS,   "SIR_E_OPTIONS"},   /**< Option flags are invalid (6) */
818
        {SIR_E_LEVELS,    "SIR_E_LEVELS"},    /**< Level flags are invalid (7) */
819
        {SIR_E_TEXTSTYLE, "SIR_E_TEXTSTYLE"}, /**< Text style is invalid (8) */
820
        {SIR_E_STRING,    "SIR_E_STRING"},    /**< Invalid string argument (9) */
821
        {SIR_E_NULLPTR,   "SIR_E_NULLPTR"},   /**< NULL pointer argument (10) */
822
        {SIR_E_INVALID,   "SIR_E_INVALID"},   /**< Invalid argument (11) */
823
        {SIR_E_NODEST,    "SIR_E_NODEST"},    /**< No destinations registered for level (12) */
824
        {SIR_E_UNAVAIL,   "SIR_E_UNAVAIL"},   /**< Feature is disabled or unavailable (13) */
825
        {SIR_E_INTERNAL,  "SIR_E_INTERNAL"},  /**< An internal error has occurred (14) */
826
        {SIR_E_COLORMODE, "SIR_E_COLORMODE"}, /**< Invalid color mode (15) */
827
        {SIR_E_TEXTATTR,  "SIR_E_TEXTATTR"},  /**< Invalid text attributes (16) */
828
        {SIR_E_TEXTCOLOR, "SIR_E_TEXTCOLOR"}, /**< Invalid text color (17) */
829
        {SIR_E_PLUGINBAD, "SIR_E_PLUGINBAD"}, /**< Plugin module is malformed (18) */
830
        {SIR_E_PLUGINDAT, "SIR_E_PLUGINDAT"}, /**< Data produced by plugin is invalid (19) */
831
        {SIR_E_PLUGINVER, "SIR_E_PLUGINVER"}, /**< Plugin interface version unsupported (20) */
832
        {SIR_E_PLUGINERR, "SIR_E_PLUGINERR"}, /**< Plugin reported failure (21) */
833
        {SIR_E_PLATFORM,  "SIR_E_PLATFORM"},  /**< Platform error code %d: %s (22) */
834
        {SIR_E_UNKNOWN,   "SIR_E_UNKNOWN"},   /**< Unknown error (4095) */
835
    };
836

837
    char message[SIR_MAXERROR] = {0};
23✔
838
    for (size_t n = 0; n < _sir_countof(errors); n++) {
575✔
839
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
552✔
840
        memset(message, 0, SIR_MAXERROR);
480✔
841
        uint16_t err = sir_geterror(message);
552✔
842
        _sir_eqland(pass, errors[n].code == err && *message != '\0');
552✔
843
        printf("\t%s = %s\n", errors[n].name, message);
552✔
844
    }
845

846
    _sir_eqland(pass, sir_cleanup());
23✔
847
    return print_result_and_return(pass);
23✔
848
}
849

850
bool sirtest_textstylesanity(void) {
23✔
851
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
852
    bool pass = si_init;
20✔
853

854
    printf("\t" WHITEB("--- explicitly invalid ---") "\n");
20✔
855
    _sir_eqland(pass, !sir_settextstyle(SIRL_INFO, (sir_textattr)0xbbb, 800, 920));
23✔
856
    _sir_eqland(pass, sir_info("I have set an invalid text style."));
23✔
857

858
    _sir_eqland(pass, !sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BLACK));
23✔
859
    _sir_eqland(pass, sir_info("oops, did it again..."));
23✔
860

861
    _sir_eqland(pass, !sir_settextstyle(SIRL_ALERT, SIRTA_NORMAL, 0xff, 0xff));
23✔
862
    _sir_eqland(pass, sir_info("and again."));
23✔
863
    PRINT_PASS(pass, "\t--- explicitly invalid: %s ---\n\n", PRN_PASS(pass));
20✔
864

865
    printf("\t" WHITEB("--- unusual but valid ---") "\n");
20✔
866
    _sir_eqland(pass, sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_DEFAULT, SIRTC_DEFAULT));
23✔
867
    _sir_eqland(pass, sir_info("system default fg and bg"));
23✔
868
    PRINT_PASS(pass, "\t--- unusual but valid: %s ---\n\n", PRN_PASS(pass));
20✔
869

870
    printf("\t" WHITEB("--- override defaults ---") "\n");
20✔
871
    _sir_eqland(pass, sir_resettextstyles());
23✔
872

873
    _sir_eqland(pass, sir_debug("default style"));
23✔
874
    _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, SIRTC_BYELLOW, SIRTC_DGRAY));
23✔
875
    _sir_eqland(pass, sir_debug("override style"));
23✔
876

877
    _sir_eqland(pass, sir_info("default style"));
23✔
878
    _sir_eqland(pass, sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, SIRTC_GREEN, SIRTC_MAGENTA));
23✔
879
    _sir_eqland(pass, sir_info("override style"));
23✔
880

881
    _sir_eqland(pass, sir_notice("default style"));
23✔
882
    _sir_eqland(pass, sir_settextstyle(SIRL_NOTICE, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_BYELLOW));
23✔
883
    _sir_eqland(pass, sir_notice("override style"));
23✔
884

885
    _sir_eqland(pass, sir_warn("default style"));
23✔
886
    _sir_eqland(pass, sir_settextstyle(SIRL_WARN, SIRTA_NORMAL, SIRTC_BLACK, SIRTC_WHITE));
23✔
887
    _sir_eqland(pass, sir_warn("override style"));
23✔
888

889
    _sir_eqland(pass, sir_error("default style"));
23✔
890
    _sir_eqland(pass, sir_settextstyle(SIRL_ERROR, SIRTA_NORMAL, SIRTC_WHITE, SIRTC_BLUE));
23✔
891
    _sir_eqland(pass, sir_error("override style"));
23✔
892

893
    _sir_eqland(pass, sir_crit("default style"));
23✔
894
    _sir_eqland(pass, sir_settextstyle(SIRL_CRIT, SIRTA_EMPH, SIRTC_DGRAY, SIRTC_BGREEN));
23✔
895
    _sir_eqland(pass, sir_crit("override style"));
23✔
896

897
    _sir_eqland(pass, sir_alert("default style"));
23✔
898
    _sir_eqland(pass, sir_settextstyle(SIRL_ALERT, SIRTA_ULINE, SIRTC_BBLUE, SIRTC_DEFAULT));
23✔
899
    _sir_eqland(pass, sir_alert("override style"));
23✔
900

901
    _sir_eqland(pass, sir_emerg("default style"));
23✔
902
    _sir_eqland(pass, sir_settextstyle(SIRL_EMERG, SIRTA_BOLD, SIRTC_DGRAY, SIRTC_DEFAULT));
23✔
903
    _sir_eqland(pass, sir_emerg("override style"));
23✔
904
    PRINT_PASS(pass, "\t--- override defaults: %s ---\n\n", PRN_PASS(pass));
20✔
905

906
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
20✔
907
    _sir_eqland(pass, sir_resettextstyles());
23✔
908

909
    _sir_eqland(pass, sir_debug("default style (debug)"));
23✔
910
    _sir_eqland(pass, sir_info("default style (info)"));
23✔
911
    _sir_eqland(pass, sir_notice("default style (notice)"));
23✔
912
    _sir_eqland(pass, sir_warn("default style (warning)"));
23✔
913
    _sir_eqland(pass, sir_error("default style (error)"));
23✔
914
    _sir_eqland(pass, sir_crit("default style (crit)"));
23✔
915
    _sir_eqland(pass, sir_alert("default style (alert)"));
23✔
916
    _sir_eqland(pass, sir_emerg("default style (emergency)"));
23✔
917
    PRINT_PASS(pass, "\t--- reset to defaults: %s ---\n\n", PRN_PASS(pass));
20✔
918

919
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
20✔
920
    _sir_eqland(pass, sir_setcolormode(SIRCM_256));
23✔
921

922
    for (sir_textcolor fg = 0, bg = 255; (fg < 256 && bg > 0); fg++, bg--) {
5,888✔
923
        if (fg != bg) {
5,865✔
924
            _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
5,865✔
925
            _sir_eqland(pass, sir_debug("this is 256-color mode (fg: %"PRIu32", bg: %"PRIu32")",
5,865✔
926
                fg, bg));
927
        }
928
    }
929

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

932
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
20✔
933
    _sir_eqland(pass, sir_setcolormode(SIRCM_RGB));
23✔
934

935
    for (size_t n = 0; n < 256; n++) {
5,911✔
936
        sir_textcolor fg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
5,888✔
937
        sir_textcolor bg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
5,888✔
938
        _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
5,888✔
939
        _sir_eqland(pass, sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
5,888✔
940
            ", bg: %"PRIu32", %"PRIu32", %"PRIu32")", _sir_getredfromcolor(fg),
941
            _sir_getgreenfromcolor(fg), _sir_getbluefromcolor(fg), _sir_getredfromcolor(bg),
942
            _sir_getgreenfromcolor(bg), _sir_getbluefromcolor(bg)));
943
    }
944
    PRINT_PASS(pass, "\t--- change mode: RGB-color: %s ---\n\n", PRN_PASS(pass));
23✔
945

946
    printf("\t" WHITEB("--- change mode: invalid mode ---") "\n");
20✔
947
    _sir_eqland(pass, !sir_setcolormode(SIRCM_INVALID));
23✔
948
    sir_textcolor fg = sir_makergb(255, 0, 0);
23✔
949
    sir_textcolor bg = sir_makergb(0, 0, 0);
23✔
950
    _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
23✔
951
    _sir_eqland(pass, sir_debug("this is still RGB color mode"));
23✔
952
    PRINT_PASS(pass, "\t--- change mode: invalid mode %s ---\n\n", PRN_PASS(pass));
20✔
953

954
    printf("\t" WHITEB("--- change mode: 16-color ---") "\n");
20✔
955
    _sir_eqland(pass, sir_setcolormode(SIRCM_16));
23✔
956
    _sir_eqland(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, SIRTC_BMAGENTA, SIRTC_DEFAULT));
23✔
957
    _sir_eqland(pass, sir_debug("this is 16-color mode (fg: %"PRId32", bg: default)",
23✔
958
        SIRTC_BMAGENTA));
959
    PRINT_PASS(pass, "\t--- change mode: 16-color: %s ---\n\n", PRN_PASS(pass));
20✔
960

961
    _sir_eqland(pass, sir_cleanup());
23✔
962

963
    return print_result_and_return(pass);
23✔
964
}
965

966
#if defined(__clang__) && !defined(__EMBARCADEROC__)
967
/* only Clang has implicit-conversion; GCC BZ#87454 */
968
SANITIZE_SUPPRESS("implicit-conversion")
969
#endif
970
bool sirtest_optionssanity(void) {
23✔
971
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
972
    bool pass = si_init;
20✔
973

974
    static const size_t iterations = 10;
975

976
    /* these should all be valid. */
977
    printf("\t" WHITEB("--- individual valid options ---") "\n");
20✔
978
    _sir_eqland(pass, _sir_validopts(SIRO_ALL));
23✔
979
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
20✔
980
    _sir_eqland(pass, _sir_validopts(SIRO_NOTIME));
23✔
981
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
20✔
982
    _sir_eqland(pass, _sir_validopts(SIRO_NOHOST));
23✔
983
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
20✔
984
    _sir_eqland(pass, _sir_validopts(SIRO_NOLEVEL));
23✔
985
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
20✔
986
    _sir_eqland(pass, _sir_validopts(SIRO_NONAME));
23✔
987
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
20✔
988
    _sir_eqland(pass, _sir_validopts(SIRO_NOPID));
23✔
989
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
20✔
990
    _sir_eqland(pass, _sir_validopts(SIRO_NOTID));
23✔
991
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
20✔
992
    _sir_eqland(pass, _sir_validopts(SIRO_NOHDR));
23✔
993
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
20✔
994
    _sir_eqland(pass, _sir_validopts(SIRO_MSGONLY));
23✔
995
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
20✔
996
    PRINT_PASS(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
23✔
997

998
    /* any combination these bitwise OR'd together
999
       to form a bitmask should also be valid. */
1000
    static const sir_option option_arr[SIR_NUMOPTIONS] = {
1001
        SIRO_NOTIME,
1002
        SIRO_NOHOST,
1003
        SIRO_NOLEVEL,
1004
        SIRO_NONAME,
1005
        SIRO_NOMSEC,
1006
        SIRO_NOPID,
1007
        SIRO_NOTID,
1008
        SIRO_NOHDR
1009
    };
1010

1011
    printf("\t" WHITEB("--- random bitmask of valid options ---") "\n");
20✔
1012
    uint32_t last_count = SIR_NUMOPTIONS;
20✔
1013
    for (size_t n = 0; n < iterations; n++) {
253✔
1014
        sir_options opts    = 0;
200✔
1015
        uint32_t rand_count = 0;
200✔
1016
        size_t last_idx     = 0;
200✔
1017

1018
        do {
1019
            rand_count = getrand(SIR_NUMOPTIONS);
330✔
1020
        } while (rand_count == last_count || rand_count <= 1);
330✔
1021

1022
        last_count = rand_count;
200✔
1023

1024
        for (size_t i = 0; i < rand_count; i++) {
1,211✔
1025
            size_t rand_idx = 0;
852✔
1026
            size_t tries    = 0;
852✔
1027

1028
            do {
1029
                if (++tries > SIR_NUMOPTIONS - 2)
1,654✔
1030
                    break;
×
1031
                rand_idx = (size_t)getrand(SIR_NUMOPTIONS);
1,654✔
1032
            } while (rand_idx == last_idx || _sir_bittest(opts, option_arr[rand_idx]));
1,654✔
1033

1034
            last_idx = rand_idx;
852✔
1035
            opts |= option_arr[rand_idx];
981✔
1036
        }
1037

1038
        _sir_eqland(pass, _sir_validopts(opts));
230✔
1039
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
230✔
1040
            ", options: %08"PRIx32")") "\n", n + 1, iterations, rand_count, opts);
1041
    }
1042
    PRINT_PASS(pass, "\t--- random bitmask of valid options: %s ---\n\n", PRN_PASS(pass));
23✔
1043

1044
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
1045

1046
    /* the lowest byte is not valid. */
1047
    sir_options invalid = 0x000000ff;
20✔
1048
    _sir_eqland(pass, !_sir_validopts(invalid));
23✔
1049
    printf(INDENT_ITEM WHITE("lowest byte: %08"PRIx32) "\n", invalid);
20✔
1050

1051
    /* gaps inbetween valid options. */
1052
    invalid = 0x0001ff00U & ~(SIRO_NOTIME | SIRO_NOHOST | SIRO_NOLEVEL | SIRO_NONAME |
20✔
1053
                             SIRO_NOMSEC | SIRO_NOPID | SIRO_NOTID  | SIRO_NOHDR);
1054
    _sir_eqland(pass, !_sir_validopts(invalid));
23✔
1055
    printf(INDENT_ITEM WHITE("gaps in 0x001ff00U: %08"PRIx32) "\n", invalid);
20✔
1056

1057
    /* greater than SIRO_MSGONLY and less than SIRO_NOHDR. */
1058
    for (sir_option o = 0x00008f00U; o < SIRO_NOHDR; o += 0x1000U) {
207✔
1059
        _sir_eqland(pass, !_sir_validopts(o));
184✔
1060
        printf(INDENT_ITEM WHITE("SIRO_MSGONLY >< SIRO_NOHDR: %08"PRIx32) "\n", o);
160✔
1061
    }
1062

1063
    /* greater than SIRO_NOHDR. */
1064
    invalid = (0xFFFF0000 & ~SIRO_NOHDR); /* implicit-conversion */
20✔
1065
    _sir_eqland(pass, !_sir_validopts(invalid));
23✔
1066
    printf(INDENT_ITEM WHITE("greater than SIRO_NOHDR: %08"PRIx32) "\n", invalid);
20✔
1067

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

1070
    _sir_eqland(pass, sir_cleanup());
23✔
1071
    return print_result_and_return(pass);
23✔
1072
}
1073

1074
bool sirtest_levelssanity(void) {
23✔
1075
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1076
    bool pass = si_init;
20✔
1077

1078
    static const size_t iterations = 10;
1079

1080
    /* these should all be valid. */
1081
    printf("\t" WHITEB("--- individual valid levels ---") "\n");
20✔
1082
    _sir_eqland(pass, _sir_validlevel(SIRL_INFO) && _sir_validlevels(SIRL_INFO));
23✔
1083
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_INFO);
20✔
1084
    _sir_eqland(pass, _sir_validlevel(SIRL_DEBUG) && _sir_validlevels(SIRL_DEBUG));
23✔
1085
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_DEBUG);
20✔
1086
    _sir_eqland(pass, _sir_validlevel(SIRL_NOTICE) && _sir_validlevels(SIRL_NOTICE));
23✔
1087
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_NOTICE);
20✔
1088
    _sir_eqland(pass, _sir_validlevel(SIRL_WARN) && _sir_validlevels(SIRL_WARN));
23✔
1089
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_WARN);
20✔
1090
    _sir_eqland(pass, _sir_validlevel(SIRL_ERROR) && _sir_validlevels(SIRL_ERROR));
23✔
1091
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ERROR);
20✔
1092
    _sir_eqland(pass, _sir_validlevel(SIRL_CRIT) && _sir_validlevels(SIRL_CRIT));
23✔
1093
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_CRIT);
20✔
1094
    _sir_eqland(pass, _sir_validlevel(SIRL_ALERT) && _sir_validlevels(SIRL_ALERT));
23✔
1095
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ALERT);
20✔
1096
    _sir_eqland(pass, _sir_validlevel(SIRL_EMERG) && _sir_validlevels(SIRL_EMERG));
23✔
1097
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_EMERG);
20✔
1098
    _sir_eqland(pass, _sir_validlevels(SIRL_ALL));
23✔
1099
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_ALL);
20✔
1100
    _sir_eqland(pass, _sir_validlevels(SIRL_NONE));
23✔
1101
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_NONE);
20✔
1102
    PRINT_PASS(pass, "\t--- individual valid levels: %s ---\n\n", PRN_PASS(pass));
23✔
1103

1104
    /* any combination these bitwise OR'd together
1105
       to form a bitmask should also be valid. */
1106
    static const sir_levels levels_arr[SIR_NUMLEVELS] = {
1107
        SIRL_EMERG,
1108
        SIRL_ALERT,
1109
        SIRL_CRIT,
1110
        SIRL_ERROR,
1111
        SIRL_WARN,
1112
        SIRL_NOTICE,
1113
        SIRL_INFO,
1114
        SIRL_DEBUG
1115
    };
1116

1117
    printf("\t" WHITEB("--- random bitmask of valid levels ---") "\n");
20✔
1118
    uint32_t last_count = SIR_NUMLEVELS;
20✔
1119
    for (size_t n = 0; n < iterations; n++) {
253✔
1120
        sir_levels levels   = 0U;
200✔
1121
        uint32_t rand_count = 0U;
200✔
1122
        size_t last_idx     = 0UL;
200✔
1123

1124
        do {
1125
            rand_count = getrand(SIR_NUMLEVELS);
451✔
1126
        } while (rand_count == last_count || rand_count <= 1U);
451✔
1127

1128
        last_count = rand_count;
200✔
1129

1130
        for (size_t i = 0; i < rand_count; i++) {
1,357✔
1131
            size_t rand_idx = 0;
980✔
1132
            size_t tries    = 0;
980✔
1133

1134
            do {
1135
                if (++tries > SIR_NUMLEVELS - 2)
1,846✔
1136
                    break;
32✔
1137
                rand_idx = (size_t)getrand(SIR_NUMLEVELS);
1,808✔
1138
            } while (rand_idx == last_idx || _sir_bittest(levels, levels_arr[rand_idx]));
1,808✔
1139

1140
            last_idx = rand_idx;
980✔
1141
            levels |= levels_arr[rand_idx];
1,127✔
1142
        }
1143

1144
        _sir_eqland(pass, _sir_validlevels(levels));
230✔
1145
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
230✔
1146
                                 " %04"PRIx16) ")\n", n + 1, iterations, rand_count, levels);
1147
    }
1148
    PRINT_PASS(pass, "\t--- random bitmask of valid levels: %s ---\n\n", PRN_PASS(pass));
23✔
1149

1150
    printf("\t" WHITEB("--- invalid values ---") "\n");
20✔
1151

1152
    /* greater than SIRL_ALL. */
1153
    sir_levels invalid = (0xffffu & ~SIRL_ALL);
20✔
1154
    _sir_eqland(pass, !_sir_validlevels(invalid));
23✔
1155
    printf(INDENT_ITEM WHITE("greater than SIRL_ALL: %04"PRIx16) "\n", invalid);
20✔
1156

1157
    /* individual invalid level. */
1158
    sir_level invalid2 = 0x1337U;
20✔
1159
    _sir_eqland(pass, !_sir_validlevel(invalid2));
23✔
1160
    printf(INDENT_ITEM WHITE("individual invalid level: %04"PRIx16) "\n", invalid2);
20✔
1161

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

1164
    _sir_eqland(pass, sir_cleanup());
23✔
1165
    return print_result_and_return(pass);
23✔
1166
}
1167

1168
bool sirtest_mutexsanity(void) {
23✔
1169
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1170
    bool pass = si_init;
20✔
1171

1172
    printf("\t" WHITEB("create, lock, unlock, destroy") "\n");
20✔
1173
    printf(INDENT_ITEM WHITE("creating mutex...") "\n");
20✔
1174

1175
    sir_mutex m1 = SIR_MUTEX_INIT;
23✔
1176
    _sir_eqland(pass, _sir_mutexcreate(&m1));
23✔
1177

1178
    print_test_error(pass, pass);
23✔
1179

1180
    if (pass) {
23✔
1181
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
20✔
1182
        _sir_eqland(pass, _sir_mutexlock(&m1));
23✔
1183

1184
        print_test_error(pass, pass);
23✔
1185

1186
        printf(INDENT_ITEM WHITE("entered; unlocking...") "\n");
20✔
1187
        _sir_eqland(pass, _sir_mutexunlock(&m1));
23✔
1188

1189
        print_test_error(pass, pass);
23✔
1190

1191
        printf(INDENT_ITEM WHITE("locking (without wait)...") "\n");
20✔
1192
        _sir_eqland(pass, _sir_mutextrylock(&m1));
23✔
1193

1194
        print_test_error(pass, pass);
23✔
1195

1196
        printf(INDENT_ITEM WHITE("unlocking...") "\n");
20✔
1197
        _sir_eqland(pass, _sir_mutexunlock(&m1));
23✔
1198

1199
        print_test_error(pass, pass);
23✔
1200

1201
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
20✔
1202
        _sir_eqland(pass, _sir_mutexdestroy(&m1));
23✔
1203

1204
        print_test_error(pass, pass);
23✔
1205

1206
    }
1207
    PRINT_PASS(pass, "\t--- create, lock, unlock, destroy: %s ---\n\n", PRN_PASS(pass));
23✔
1208

1209
    printf("\t" WHITEB("invalid arguments") "\n");
20✔
1210
    printf(INDENT_ITEM WHITE("create with NULL pointer...") "\n");
20✔
1211
    _sir_eqland(pass, !_sir_mutexcreate(NULL));
23✔
1212
    printf(INDENT_ITEM WHITE("lock with NULL pointer...") "\n");
20✔
1213
    _sir_eqland(pass, !_sir_mutexlock(NULL));
23✔
1214
    printf(INDENT_ITEM WHITE("trylock with NULL pointer...") "\n");
20✔
1215
    _sir_eqland(pass, !_sir_mutextrylock(NULL));
23✔
1216
    printf(INDENT_ITEM WHITE("unlock with NULL pointer...") "\n");
20✔
1217
    _sir_eqland(pass, !_sir_mutexunlock(NULL));
23✔
1218
    printf(INDENT_ITEM WHITE("destroy with NULL pointer...") "\n");
20✔
1219
    _sir_eqland(pass, !_sir_mutexdestroy(NULL));
23✔
1220
    PRINT_PASS(pass, "\t--- pass invalid arguments: %s ---\n\n", PRN_PASS(pass));
20✔
1221

1222
    _sir_eqland(pass, sir_cleanup());
23✔
1223
    return print_result_and_return(pass); // -V1020
23✔
1224
}
1225

1226
bool sirtest_perf(void) {
1✔
1227
    static const char* logbasename = "libsir-perf";
1228
    static const char* logext      = "";
1229

1230
#if !defined(DUMA)
1231
# if !defined(SIR_PERF_PROFILE)
1232
#  if !defined(__WIN__)
1233
    static const size_t perflines = 1000000;
1234
#  else
1235
    static const size_t perflines = 100000;
1236
#  endif
1237
# else
1238
    static const size_t perflines = 4000000;
1239
# endif
1240
#else /* DUMA */
1241
    static const size_t perflines = 100000;
1242
#endif
1243

1244
    INIT_N(si, SIRL_ALL, SIRO_NOMSEC | SIRO_NOHOST, 0, 0, "perf");
1✔
1245
    bool pass = si_init;
1✔
1246

1247
    if (pass) {
1✔
1248
        double stdioelapsed  = 0.0;
1✔
1249
        double fileelapsed   = 0.0;
1✔
1250
#if !defined(SIR_PERF_PROFILE)
1251
        double printfelapsed = 0.0;
1✔
1252

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

1255
        sir_time printftimer = {0};
1✔
1256
        sir_timer_start(&printftimer);
1✔
1257

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

1262
        printfelapsed = sir_timer_elapsed(&printftimer);
1✔
1263
#endif
1264

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

1267
        sir_time stdiotimer = {0};
1✔
1268
        sir_timer_start(&stdiotimer);
1✔
1269

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

1274
        stdioelapsed = sir_timer_elapsed(&stdiotimer);
1✔
1275

1276
        _sir_eqland(pass, sir_cleanup());
1✔
1277

1278
        INIT(si2, 0, 0, 0, 0);
1✔
1279
        _sir_eqland(pass, si2_init);
1✔
1280

1281
        char logfilename[SIR_MAXPATH] = {0};
1✔
1282
        (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
1✔
1283

1284
        sirfileid logid = sir_addfile(logfilename, SIRL_ALL, SIRO_NOMSEC | SIRO_NONAME);
1✔
1285
        _sir_eqland(pass, 0 != logid);
1✔
1286

1287
        if (pass) {
1✔
1288
            printf("\t" BLUE("%zu lines libsir (file)...") "\n", perflines);
1✔
1289

1290
            sir_time filetimer = {0};
1✔
1291
            sir_timer_start(&filetimer);
1✔
1292

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

1296
            fileelapsed = sir_timer_elapsed(&filetimer);
1✔
1297

1298
            _sir_eqland(pass, sir_remfile(logid));
1✔
1299
        }
1300

1301
        if (pass) {
1✔
1302
#if !defined(SIR_PERF_PROFILE)
1303
            printf("\t" WHITEB("printf: ") CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n",
1✔
1304
                perflines, printfelapsed / 1e3, (double)perflines / (printfelapsed / 1e3));
1✔
1305
#endif
1306
            printf("\t" WHITEB("libsir (stdout): ")
1✔
1307
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n", perflines,
1308
                    stdioelapsed / 1e3, (double)perflines / (stdioelapsed / 1e3));
1✔
1309

1310
            printf("\t" WHITEB("libsir (file): ")
1✔
1311
                   CYAN("%zu lines in %.3fsec (%.1f lines/sec)") "\n", perflines,
1312
                    fileelapsed / 1e3, (double)perflines / (fileelapsed / 1e3));
1✔
1313

1314
            printf("\t" WHITEB("timer resolution: ") CYAN("~%ldnsec") "\n", sir_timer_getres());
1✔
1315
        }
1316
    }
1317

1318
    unsigned deleted = 0U;
1✔
1319
    enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &deleted);
1✔
1320

1321
    if (deleted > 0U)
1✔
1322
        printf("\t" DGRAY("deleted %u log file(s)") "\n", deleted);
1✔
1323

1324
    _sir_eqland(pass, sir_cleanup());
1✔
1325
    return print_result_and_return(pass);
1✔
1326
}
1327

1328
bool sirtest_updatesanity(void) {
23✔
1329
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
23✔
1330
    bool pass = si_init;
20✔
1331

1332
#define UPDATE_SANITY_ARRSIZE 10
1333

1334
    static const char* logfile = MAKE_LOG_NAME("update-sanity.log");
1335
    static const sir_options opts_array[UPDATE_SANITY_ARRSIZE] = {
1336
        SIRO_NOHOST | SIRO_NOTIME | SIRO_NOLEVEL,
1337
        SIRO_MSGONLY, SIRO_NONAME | SIRO_NOTID,
1338
        SIRO_NOPID | SIRO_NOTIME,
1339
        SIRO_NOTIME | SIRO_NOLEVEL | SIRO_NONAME,
1340
        SIRO_NOTIME, SIRO_NOMSEC | SIRO_NOHOST,
1341
        SIRO_NOPID | SIRO_NOTID,
1342
        SIRO_NOHOST | SIRO_NOTID, SIRO_ALL
1343
    };
1344

1345
    static const sir_levels levels_array[UPDATE_SANITY_ARRSIZE] = {
1346
        SIRL_NONE, SIRL_ALL, SIRL_EMERG, SIRL_ALERT,
1347
        SIRL_CRIT, SIRL_ERROR, SIRL_WARN, SIRL_NOTICE,
1348
        SIRL_INFO, SIRL_DEBUG
1349
    };
1350

1351
    (void)rmfile(logfile);
23✔
1352
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
23✔
1353
    _sir_eqland(pass, 0 != id1);
23✔
1354

1355
    for (int i = 0; i < 10; i++) {
214✔
1356
        if (!pass)
195✔
1357
            break;
4✔
1358

1359
        /* reset to defaults*/
1360
        _sir_eqland(pass, sir_stdoutlevels(SIRL_DEFAULT));
191✔
1361
        _sir_eqland(pass, sir_stderrlevels(SIRL_DEFAULT));
191✔
1362
        _sir_eqland(pass, sir_stdoutopts(SIRO_DEFAULT));
191✔
1363
        _sir_eqland(pass, sir_stderropts(SIRO_DEFAULT));
191✔
1364

1365
        _sir_eqland(pass, sir_debug("default config (debug)"));
191✔
1366
        _sir_eqland(pass, sir_info("default config (info)"));
191✔
1367
        _sir_eqland(pass, sir_notice("default config (notice)"));
191✔
1368
        _sir_eqland(pass, sir_warn("default config (warning)"));
191✔
1369
        _sir_eqland(pass, sir_error("default config (error)"));
191✔
1370
        _sir_eqland(pass, sir_crit("default config (critical)"));
191✔
1371
        _sir_eqland(pass, sir_alert("default config (alert)"));
191✔
1372
        _sir_eqland(pass, sir_emerg("default config (emergency)"));
191✔
1373

1374
        /* pick random options to set/unset */
1375
        uint32_t rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1376
        _sir_eqland(pass, sir_stdoutlevels(levels_array[rnd]));
191✔
1377
        _sir_eqland(pass, sir_stdoutopts(opts_array[rnd]));
191✔
1378
        printf("\t" WHITE("set random config #%"PRIu32" for stdout") "\n", rnd);
161✔
1379

1380
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1381
        _sir_eqland(pass, sir_stderrlevels(levels_array[rnd]));
191✔
1382
        _sir_eqland(pass, sir_stderropts(opts_array[rnd]));
191✔
1383
        printf("\t" WHITE("set random config #%"PRIu32" for stderr") "\n", rnd);
161✔
1384

1385
        rnd = getrand(UPDATE_SANITY_ARRSIZE);
191✔
1386
        _sir_eqland(pass, sir_filelevels(id1, levels_array[rnd]));
191✔
1387
        _sir_eqland(pass, sir_fileopts(id1, opts_array[rnd]));
191✔
1388
        printf("\t" WHITE("set random config #%"PRIu32" for %s") "\n", rnd, logfile);
191✔
1389

1390
        _sir_eqland(pass, filter_error(sir_debug("modified config #%"PRIu32" (debug)", rnd), SIR_E_NODEST));
191✔
1391
        _sir_eqland(pass, filter_error(sir_info("modified config #%"PRIu32" (info)", rnd), SIR_E_NODEST));
191✔
1392
        _sir_eqland(pass, filter_error(sir_notice("modified config #%"PRIu32" (notice)", rnd), SIR_E_NODEST));
191✔
1393
        _sir_eqland(pass, filter_error(sir_warn("modified config #%"PRIu32" (warning)", rnd), SIR_E_NODEST));
191✔
1394
        _sir_eqland(pass, filter_error(sir_error("modified config #%"PRIu32" (error)", rnd), SIR_E_NODEST));
191✔
1395
        _sir_eqland(pass, filter_error(sir_crit("modified config #%"PRIu32" (critical)", rnd), SIR_E_NODEST));
191✔
1396
        _sir_eqland(pass, filter_error(sir_alert("modified config #%"PRIu32" (alert)", rnd), SIR_E_NODEST));
191✔
1397
        _sir_eqland(pass, filter_error(sir_emerg("modified config #%"PRIu32" (emergency)", rnd), SIR_E_NODEST));
191✔
1398
    }
1399

1400
    if (pass) {
23✔
1401
        /* restore to default config and run again */
1402
        sir_stdoutlevels(SIRL_DEFAULT);
19✔
1403
        sir_stderrlevels(SIRL_DEFAULT);
19✔
1404
        sir_stdoutopts(SIRO_DEFAULT);
19✔
1405
        sir_stderropts(SIRO_DEFAULT);
19✔
1406

1407
        _sir_eqland(pass, sir_debug("default config (debug)"));
19✔
1408
        _sir_eqland(pass, sir_info("default config (info)"));
19✔
1409
        _sir_eqland(pass, sir_notice("default config (notice)"));
19✔
1410
        _sir_eqland(pass, sir_warn("default config (warning)"));
19✔
1411
        _sir_eqland(pass, sir_error("default config (error)"));
19✔
1412
        _sir_eqland(pass, sir_crit("default config (critical)"));
19✔
1413
        _sir_eqland(pass, sir_alert("default config (alert)"));
19✔
1414
        _sir_eqland(pass, sir_emerg("default config (emergency)"));
19✔
1415
    }
1416

1417
    _sir_eqland(pass, sir_remfile(id1));
23✔
1418
    (void)rmfile(logfile);
23✔
1419

1420
    _sir_eqland(pass, sir_cleanup());
23✔
1421
    return print_result_and_return(pass);
23✔
1422
}
1423

1424
#if defined(SIR_SYSLOG_ENABLED) || defined(SIR_OS_LOG_ENABLED)
1425
static
1426
bool generic_syslog_test(const char* sl_name, const char* identity, const char* category) {
17✔
1427
    static const int runs = 5;
1428

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

1433
# if !defined(__WIN__)
1434
    uint32_t rnd = (uint32_t)(_sir_getpid() + _sir_gettid());
17✔
1435
# else
1436
    uint32_t rnd = (uint32_t)GetTickCount();
1437
# endif
1438

1439
    bool pass = true;
14✔
1440
    for (int i = 1; i <= runs; i++) {
102✔
1441
        /* randomly skip setting process name, identity/category to thoroughly
1442
         * test fallback routines; randomly update the config mid-run. */
1443
        bool set_procname = getrand_bool(rnd ^ 0x5a5a5a5aU);
85✔
1444
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9U);
85✔
1445
        bool set_category = getrand_bool(rnd ^ 0x32323232U);
85✔
1446
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7U);
85✔
1447

1448
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
85✔
1449
            set_procname, set_identity, set_category, do_update);
1450

1451
        INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0, 0, (set_procname ? "sir_sltest" : ""));
94✔
1452
        si.d_syslog.opts   = SIRO_DEFAULT;
85✔
1453
        si.d_syslog.levels = SIRL_DEFAULT;
85✔
1454

1455
        if (set_identity)
85✔
1456
            _sir_strncpy(si.d_syslog.identity, SIR_MAX_SYSLOG_CAT, identity, SIR_MAX_SYSLOG_ID);
49✔
1457

1458
        if (set_category)
85✔
1459
            _sir_strncpy(si.d_syslog.category, SIR_MAX_SYSLOG_CAT, category, SIR_MAX_SYSLOG_CAT);
50✔
1460

1461
        si_init = sir_init(&si); //-V519
85✔
1462
        _sir_eqland(pass, si_init);
85✔
1463

1464
        if (do_update)
85✔
1465
            _sir_eqland(pass, sir_sysloglevels(SIRL_ALL));
33✔
1466

1467
        _sir_eqland(pass, sir_debug("%d/%d: this debug message sent to stdout and %s.", i, runs, sl_name));
85✔
1468
        _sir_eqland(pass, sir_info("%d/%d: this info message sent to stdout and %s.", i, runs, sl_name));
85✔
1469

1470
        _sir_eqland(pass, sir_notice("%d/%d: this notice message sent to stdout and %s.", i, runs, sl_name));
85✔
1471
        _sir_eqland(pass, sir_warn("%d/%d: this warning message sent to stdout and %s.", i, runs, sl_name));
85✔
1472
        _sir_eqland(pass, sir_error("%d/%d: this error message sent to stdout and %s.", i, runs, sl_name));
85✔
1473

1474
        if (set_identity) {
85✔
1475
            _sir_eqland(pass, sir_syslogid("my test ID"));
49✔
1476
            _sir_eqland(pass, sir_syslogid("my test ID")); /* test deduping. */
49✔
1477
        }
1478

1479
        if (set_category) {
85✔
1480
            _sir_eqland(pass, sir_syslogcat("my test category"));
50✔
1481
            _sir_eqland(pass, sir_syslogcat("my test category")); /* test deduping. */
50✔
1482
        }
1483

1484
        if (do_update)
85✔
1485
            _sir_eqland(pass, sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID)));
33✔
1486

1487
        _sir_eqland(pass, sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name));
85✔
1488
        _sir_eqland(pass, sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name));
85✔
1489
        _sir_eqland(pass, sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name));
85✔
1490

1491
# if defined(SIR_OS_LOG_ENABLED)
1492
#  if defined(__MACOS__) && !defined(__INTEL_COMPILER)
1493
        if (i == runs -1 && 0 == strncmp(sl_name, "os_log", 6)) {
1494
            printf("\ttesting os_log activity feature...\n");
1495

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

1501
            /* execution now passes to os_log_parent_activity(), where some logging
1502
            * will occur, then a sub-activity will be created, and more logging. */
1503
            os_activity_apply_f(parent, (void*)parent, os_log_parent_activity);
1504
        }
1505
#  endif
1506
# endif
1507

1508
        _sir_eqland(pass, sir_cleanup());
85✔
1509

1510
        if (!pass)
85✔
1511
            break;
×
1512
    }
1513

1514
    return print_result_and_return(pass);
17✔
1515
}
1516
#endif
1517

1518
#if defined(SIR_NO_SYSTEM_LOGGERS)
1519
static bool generic_disabled_syslog_test(const char* sl_name, const char* identity,
6✔
1520
    const char* category) {
1521
    INIT_SL(si, SIRL_ALL, SIRO_NOHOST | SIRO_NOTID, 0U, 0U, "sir_disabled_sltest");
6✔
1522
    si.d_syslog.opts   = SIRO_DEFAULT;
6✔
1523
    si.d_syslog.levels = SIRL_DEFAULT;
6✔
1524
    bool pass = true;
6✔
1525

1526
    SIR_UNUSED(sl_name);
1527

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

1530
    /* init should just ignore the syslog settings. */
1531
    _sir_eqland(pass, sir_init(&si));
6✔
1532

1533
    /* these calls should all fail. */
1534
    printf("\tsetting levels...\n");
6✔
1535
    _sir_eqland(pass, !sir_sysloglevels(SIRL_ALL));
6✔
1536

1537
    if (pass)
6✔
1538
        print_expected_error();
6✔
1539

1540
    printf("\tsetting options...\n");
6✔
1541
    _sir_eqland(pass, !sir_syslogopts(SIRO_DEFAULT));
6✔
1542

1543
    if (pass)
6✔
1544
        print_expected_error();
6✔
1545

1546
    printf("\tsetting identity...\n");
6✔
1547
    _sir_eqland(pass, !sir_syslogid(identity));
6✔
1548

1549
    if (pass)
6✔
1550
        print_expected_error();
6✔
1551

1552
    printf("\tsetting category...\n");
6✔
1553
    _sir_eqland(pass, !sir_syslogcat(category));
6✔
1554

1555
    if (pass)
6✔
1556
        print_expected_error();
6✔
1557

1558
    _sir_eqland(pass, sir_cleanup());
6✔
1559
    return print_result_and_return(pass);
6✔
1560
}
1561
#endif
1562

1563
bool sirtest_syslog(void) {
23✔
1564
#if !defined(SIR_SYSLOG_ENABLED)
1565
# if defined(SIR_NO_SYSTEM_LOGGERS)
1566
    bool pass = generic_disabled_syslog_test("syslog", "sirtests", "tests");
6✔
1567
    return print_result_and_return(pass);
6✔
1568
# else
1569
    printf("\t" DGRAY("SIR_SYSLOG_ENABLED is not defined; skipping") "\n");
1570
    return true;
1571
# endif
1572
#else
1573
    bool pass = generic_syslog_test("syslog", "sirtests", "tests");
17✔
1574
    return print_result_and_return(pass);
17✔
1575
#endif
1576
}
1577

1578
bool sirtest_os_log(void) {
23✔
1579
#if !defined(SIR_OS_LOG_ENABLED)
1580
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
20✔
1581
    return true;
23✔
1582
#else
1583
    bool pass = generic_syslog_test("os_log", "com.aremmell.libsir.tests", "tests");
1584
    return print_result_and_return(pass);
1585
#endif
1586
}
1587

1588
char *sirtest_get_wineversion(void) {
46✔
1589
#if !defined(__WIN__)
1590
    return NULL;
46✔
1591
#else /* __WIN__ */
1592
    typedef char* (__stdcall *get_wine_ver_proc)(void);
1593
    static get_wine_ver_proc _p_wine_get_version = NULL;
1594

1595
    HMODULE _h_ntdll = GetModuleHandle("ntdll.dll");
1596
    if (_h_ntdll != NULL) {
1597
        _p_wine_get_version = (get_wine_ver_proc)GetProcAddress(_h_ntdll, "wine_get_version");
1598
        if (_p_wine_get_version) {
1599
            char *wine_version = _p_wine_get_version();
1600
            if (wine_version)
1601
                return wine_version;
1602
        }
1603
    }
1604
    return NULL;
1605
#endif
1606
}
1607

1608
bool sirtest_filesystem(void) {
23✔
1609
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1610
    bool pass = si_init;
20✔
1611

1612
    /* Wine version */
1613
    printf("\tRunning under Wine: %s\n",
23✔
1614
            sirtest_get_wineversion() ? sirtest_get_wineversion() : "no"); //-V547
23✔
1615

1616
    /* current working directory. */
1617
    char* cwd = _sir_getcwd();
23✔
1618
    _sir_eqland(pass, NULL != cwd);
23✔
1619
    printf("\t_sir_getcwd: '%s'\n", PRN_STR(cwd));
23✔
1620

1621
    if (NULL != cwd) {
23✔
1622
        /* path to this binary file. */
1623
        char* filename = _sir_getappfilename();
23✔
1624
        _sir_eqland(pass, NULL != filename);
23✔
1625
        printf("\t_sir_getappfilename: '%s'\n", PRN_STR(filename));
23✔
1626

1627
        if (NULL != filename) {
23✔
1628
            /* _sir_get[base|dir]name() can potentially modify filename,
1629
             * so make a copy for each call. */
1630
            char* filename2 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1631
            _sir_eqland(pass, NULL != filename2);
20✔
1632

1633
            if (NULL != filename2) {
20✔
1634
                /* filename, stripped of directory component(s). */
1635
                char* _basename = _sir_getbasename(filename2);
19✔
1636
                printf("\t_sir_getbasename: '%s'\n", PRN_STR(_basename));
19✔
1637

1638
                if (!_basename) {
19✔
1639
                    pass = false;
×
1640
                } else {
1641
                    /* the last strlen(_basename) chars of filename should match. */
1642
                    size_t len    = strnlen(_basename, SIR_MAXPATH);
19✔
1643
                    size_t offset = strnlen(filename, SIR_MAXPATH) - len;
19✔
1644
                    size_t n      = 0;
16✔
1645

1646
                    while (n < len) {
171✔
1647
                        if (filename[offset++] != _basename[n++]) {
152✔
1648
                            pass = false;
×
1649
                            break;
×
1650
                        }
1651
                    }
1652
                }
1653
            }
1654

1655
            /* directory this binary file resides in. */
1656
            char* appdir = _sir_getappdir();
20✔
1657
            _sir_eqland(pass, NULL != appdir);
20✔
1658
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
20✔
1659

1660
            /* _sir_get[base|dir]name can potentially modify filename,
1661
             * so make a copy for each call. */
1662
            char* filename3 = strndup(filename, strnlen(filename, SIR_MAXPATH));
20✔
1663
            _sir_eqland(pass, NULL != filename3);
20✔
1664

1665
            if (NULL != appdir && NULL != filename3) {
20✔
1666
                /* should yield the same result as _sir_getappdir(). */
1667
                char* _dirname = _sir_getdirname(filename3);
19✔
1668
                printf("\t_sir_getdirname: '%s'\n", PRN_STR(_dirname));
19✔
1669

1670
                _sir_eqland(pass, 0 == strncmp(filename, appdir, strnlen(appdir, SIR_MAXPATH)));
19✔
1671
                _sir_eqland(pass, NULL != _dirname &&
19✔
1672
                    0 == strncmp(filename, _dirname, strnlen(_dirname, SIR_MAXPATH)));
1673
            }
1674

1675
            _sir_safefree(&appdir);
20✔
1676
            _sir_safefree(&filename);
20✔
1677
            _sir_safefree(&filename2);
20✔
1678
            _sir_safefree(&filename3);
20✔
1679
        }
1680

1681
        _sir_safefree(&cwd);
23✔
1682
    }
1683

1684
    /* this next section doesn't really yield any useful boolean pass/fail
1685
     * information, but could be helpful to review manually. */
1686
    char* dubious_dirnames[] = {
23✔
1687
#if !defined(__WIN__)
1688
        "/foo",
1689
        "/foo/",
1690
        "/foo/bar",
1691
        "/foo/bar/bad:filename",
1692
        "/",
1693
        ""
1694
#else /* __WIN__ */
1695
        "C:\\foo",
1696
        "C:\\foo\\",
1697
        "C:\\foo\\bar",
1698
        "C:\\foo\\bar\\bad:>filename",
1699
        "C:\\",
1700
        "//network-share/myfolder",
1701
        ""
1702
#endif
1703
    };
1704

1705
    for (size_t n = 0; n < _sir_countof(dubious_dirnames); n++) {
161✔
1706
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
138✔
1707
        if (NULL != tmp) {
138✔
1708
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
132✔
1709
                tmp, _sir_getdirname(tmp));
1710
            _sir_safefree(&tmp);
132✔
1711
        }
1712
    }
1713

1714
    char* dubious_filenames[] = {
23✔
1715
#if !defined(__WIN__)
1716
        "foo/bar/file-or-directory",
1717
        "/foo/bar/file-or-directory",
1718
        "/foo/bar/illegal:filename",
1719
        "/",
1720
        ""
1721
#else /* __WIN__ */
1722
        "foo\\bar\\file.with.many.full.stops",
1723
        "C:\\foo\\bar\\poorly-renamed.txt.pdf",
1724
        "C:\\foo\\bar\\illegal>filename.txt",
1725
        "C:\\",
1726
        "\\Program Files\\foo.bar",
1727
        ""
1728
#endif
1729
    };
1730

1731
    for (size_t n = 0; n < _sir_countof(dubious_filenames); n++) {
138✔
1732
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
115✔
1733
        if (NULL != tmp) {
115✔
1734
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
110✔
1735
                tmp, _sir_getbasename(tmp));
1736
            _sir_safefree(&tmp);
110✔
1737
        }
1738
    }
1739

1740
    /* absolute/relative paths. */
1741
    static const struct {
1742
        const char* const path;
1743
        bool abs;
1744
    } abs_or_rel_paths[] = {
1745
        {"this/is/relative", false},
1746
        {"relative", false},
1747
        {"./relative", false},
1748
        {"../../relative", false},
1749
#if !defined(__WIN__)
1750
        {"/usr/local/bin", true},
1751
        {"/", true},
1752
        {"/home/foo/.config", true},
1753
        {"~/.config", true}
1754
#else /* __WIN__ */
1755
        {"D:\\absolute", true},
1756
        {"C:\\Program Files\\FooBar", true},
1757
        {"C:\\", true},
1758
        {"\\absolute", true},
1759
#endif
1760
    };
1761

1762
    for (size_t n = 0; n < _sir_countof(abs_or_rel_paths); n++) {
207✔
1763
        bool relative = false;
184✔
1764
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
184✔
1765

1766
        if (relative == abs_or_rel_paths[n].abs) {
184✔
1767
            pass = false;
×
1768
            printf("\t" RED("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
×
1769
                relative ? "true" : "false");
×
1770
        } else {
1771
            printf("\t" GREEN("_sir_ispathrelative('%s') = %s") "\n", abs_or_rel_paths[n].path,
184✔
1772
                relative ? "true" : "false");
160✔
1773
        }
1774

1775
        _sir_eqland(pass, ret);
184✔
1776
        if (!ret) {
184✔
1777
            bool unused = print_test_error(false, false);
×
1778
            SIR_UNUSED(unused);
1779
        }
1780
    }
1781

1782
    /* file existence. */
1783
    static const struct {
1784
        const char* const path;
1785
        bool exists;
1786
    } real_or_not[] = {
1787
        {"../foobarbaz", false},
1788
        {"foobarbaz", false},
1789
#if !defined(__WIN__)
1790
        {"/", true},
1791
# if !defined(__HAIKU__)
1792
        {"/usr/bin", true},
1793
# else
1794
        {"/bin", true},
1795
# endif
1796
        {"/dev", true},
1797
#else /* __WIN__ */
1798
        {"C:\\Windows", true},
1799
        {"C:\\Program Files", true},
1800
        {"\\", true},
1801
        {".\\", true},
1802
        {"..\\", true},
1803
#endif
1804
        {"../../LICENSES/MIT.txt", true},
1805
        {"../../msvs/libsir.sln", true},
1806
        {"./", true},
1807
        {"../", true},
1808
        {"file.exists", true}
1809
    };
1810

1811
    for (size_t n = 0; n < _sir_countof(real_or_not); n++) {
253✔
1812
        bool exists = false;
230✔
1813
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
230✔
1814

1815
        if (exists != real_or_not[n].exists) {
230✔
1816
            pass = false;
23✔
1817
            printf("\t" RED("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
23✔
1818
                exists ? "true" : "false");
23✔
1819
        } else {
1820
            printf("\t" GREEN("_sir_pathexists('%s') = %s") "\n", real_or_not[n].path,
207✔
1821
                exists ? "true" : "false");
177✔
1822
        }
1823

1824
        _sir_eqland(pass, ret);
230✔
1825
        if (!ret) {
230✔
1826
            bool unused = print_test_error(false, false);
28✔
1827
            SIR_UNUSED(unused);
1828
        }
1829
    }
1830

1831
    /* checking file descriptors. */
1832
    static int bad_fds[] = {
1833
        0,
1834
        1,
1835
        2,
1836
        1234
1837
    };
1838
    if (sirtest_get_wineversion()) { //-V547
23✔
1839
        bad_fds[3] = 0;
×
1840
    }
1841

1842
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
115✔
1843
        if (_sir_validfd(bad_fds[n])) {
92✔
1844
            pass = false;
×
1845
            printf("\t" RED("_sir_validfd(%d) = true") "\n", bad_fds[n]);
×
1846
        } else {
1847
            printf("\t" GREEN("_sir_validfd(%d) = false") "\n", bad_fds[n]);
92✔
1848
        }
1849
    }
1850

1851
    FILE* f = NULL;
23✔
1852
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
23✔
1853
    if (!ret) {
23✔
1854
        pass = false;
4✔
1855
        handle_os_error(true, "fopen(%s) failed!", "file.exists");
4✔
1856
    } else {
1857
        int fd = fileno(f);
19✔
1858
        if (!_sir_validfd(fd)) {
19✔
1859
            pass = false;
×
1860
            printf("\t" RED("_sir_validfd(%d) = false") "\n", fd);
×
1861
        } else {
1862
            printf("\t" GREEN("_sir_validfd(%d) = true") "\n", fd);
16✔
1863
        }
1864
    }
1865

1866
    _sir_safefclose(&f);
23✔
1867

1868
    _sir_eqland(pass, sir_cleanup());
23✔
1869
    return print_result_and_return(pass);
23✔
1870
}
1871

1872
bool sirtest_squelchspam(void) {
23✔
1873
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1874
    bool pass = si_init;
20✔
1875

1876
    static const size_t alternate   = 50;
1877
    static const size_t sequence[3] = {
1878
        1000, /* non-repeating messages. */
1879
        1000, /* repeating messages. */
1880
        1000  /* alternating repeating and non-repeating messages. */
1881
    };
1882

1883
    sir_time timer;
1884
    sir_timer_start(&timer);
23✔
1885

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

1888
    for (size_t n = 0, ascii_idx = 33; n < sequence[0]; n++, ascii_idx++) {
23,023✔
1889
        _sir_eqland(pass, sir_debug("%c%c a non-repeating message", (char)ascii_idx,
23,000✔
1890
            (char)ascii_idx + 1));
1891

1892
        if (ascii_idx == 125)
23,000✔
1893
            ascii_idx = 33;
200✔
1894
    }
1895

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

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

1901
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
23,000✔
1902
            _sir_eqland(pass, !ret);
22,908✔
1903
        else
1904
            _sir_eqland(pass, ret);
92✔
1905
    }
1906

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

1910
    bool repeating   = false;
20✔
1911
    size_t counter   = 0;
20✔
1912
    size_t repeat_id = 0;
20✔
1913
    for (size_t n = 0, ascii_idx = 33; n < sequence[2]; n++, counter++, ascii_idx++) {
23,023✔
1914
        if (!repeating) {
23,000✔
1915
            _sir_eqland(pass, sir_debug("%c%c a non-repeating message", (char)ascii_idx,
11,523✔
1916
                (char)ascii_idx + 1));
1917
        } else {
1918
            bool ret = sir_debug("%zu a repeating message", repeat_id);
11,477✔
1919

1920
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
11,477✔
1921
                _sir_eqland(pass, !ret);
10,557✔
1922
            else
1923
                _sir_eqland(pass, ret);
920✔
1924
        }
1925

1926
        if (counter == alternate) {
23,000✔
1927
            repeating = !repeating;
437✔
1928
            counter = 0;
380✔
1929
            repeat_id++;
437✔
1930
        }
1931

1932
        if (ascii_idx == 125)
23,000✔
1933
            ascii_idx = 33;
200✔
1934
    }
1935

1936
    _sir_eqland(pass, sir_cleanup());
23✔
1937
    return print_result_and_return(pass);
23✔
1938
}
1939

1940
bool sirtest_pluginloader(void) {
23✔
1941
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
1942
    bool pass = si_init;
20✔
1943

1944
#if !defined(__WIN__)
1945
# define PLUGIN_EXT "so"
1946
#else
1947
# define PLUGIN_EXT "dll"
1948
#endif
1949

1950
    static const char* plugin1 = "build/lib/plugin_dummy."PLUGIN_EXT;
1951
    static const char* plugin2 = "build/lib/plugin_dummy_bad1."PLUGIN_EXT;
1952
    static const char* plugin3 = "build/lib/plugin_dummy_bad2."PLUGIN_EXT;
1953
    static const char* plugin4 = "build/lib/plugin_dummy_bad3."PLUGIN_EXT;
1954
    static const char* plugin5 = "build/lib/plugin_dummy_bad4."PLUGIN_EXT;
1955
    static const char* plugin6 = "build/lib/plugin_dummy_bad5."PLUGIN_EXT;
1956
    static const char* plugin7 = "build/lib/plugin_dummy_bad6."PLUGIN_EXT;
1957
    static const char* plugin8 = "build/lib/i_dont_exist."PLUGIN_EXT;
1958

1959
#if defined(SIR_NO_PLUGINS)
1960
    SIR_UNUSED(plugin2);
1961
    SIR_UNUSED(plugin3);
1962
    SIR_UNUSED(plugin4);
1963
    SIR_UNUSED(plugin5);
1964
    SIR_UNUSED(plugin6);
1965
    SIR_UNUSED(plugin7);
1966
    SIR_UNUSED(plugin8);
1967

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

1970
    printf("\tloading good plugin: '%s'...\n", plugin1);
1✔
1971
    /* load a valid, well-behaved plugin. */
1972
    sirpluginid id = sir_loadplugin(plugin1);
1✔
1973
    _sir_eqland(pass, 0 == id);
1✔
1974

1975
    if (pass)
1✔
1976
        print_expected_error();
1✔
1977

1978
    printf("\tunloading good plugin: '%s'...\n", plugin1);
1✔
1979
    /* also try the unload function. */
1980
    _sir_eqland(pass, !sir_unloadplugin(id));
1✔
1981

1982
    if (pass)
1✔
1983
        print_expected_error();
1✔
1984
#else
1985
    /* load a valid, well-behaved plugin. */
1986
    printf("\tloading good plugin: '%s'...\n", plugin1);
22✔
1987
    sirpluginid id = sir_loadplugin(plugin1);
22✔
1988
    _sir_eqland(pass, 0 != id);
22✔
1989

1990
    print_test_error(pass, pass);
22✔
1991

1992
    _sir_eqland(pass, sir_info("this message will be dispatched to the plugin."));
22✔
1993
    _sir_eqland(pass, sir_warn("this message will *not* be dispatched to the plugin."));
22✔
1994

1995
    /* re-loading the same plugin should fail. */
1996
    printf("\tloading duplicate plugin: '%s'...\n", plugin1);
22✔
1997
    sirpluginid badid = sir_loadplugin(plugin1);
22✔
1998
    _sir_eqland(pass, 0 == badid);
22✔
1999

2000
    print_test_error(pass, pass);
22✔
2001

2002
    /* the following are all invalid or misbehaved, and should all fail. */
2003
    printf("\tloading bad plugin: '%s'...\n", plugin2);
22✔
2004
    badid = sir_loadplugin(plugin2);
22✔
2005
    _sir_eqland(pass, 0 == badid);
22✔
2006

2007
    print_test_error(pass, pass);
22✔
2008

2009
    printf("\tloading bad plugin: '%s'...\n", plugin3);
22✔
2010
    badid = sir_loadplugin(plugin3);
22✔
2011
    _sir_eqland(pass, 0 == badid);
22✔
2012

2013
    print_test_error(pass, pass);
22✔
2014

2015
    printf("\tloading bad plugin: '%s'...\n", plugin4);
22✔
2016
    badid = sir_loadplugin(plugin4);
22✔
2017
    _sir_eqland(pass, 0 == badid);
22✔
2018

2019
    print_test_error(pass, pass);
22✔
2020

2021
    printf("\tloading bad plugin: '%s'...\n", plugin5);
22✔
2022
    badid = sir_loadplugin(plugin5);
22✔
2023
    _sir_eqland(pass, 0 == badid);
22✔
2024

2025
    print_test_error(pass, pass);
22✔
2026

2027
    printf("\tloading bad plugin: '%s'...\n", plugin6);
22✔
2028
    badid = sir_loadplugin(plugin6);
22✔
2029
    _sir_eqland(pass, 0 == badid);
22✔
2030

2031
    print_test_error(pass, pass);
22✔
2032

2033
    printf("\tloading bad plugin: '%s'...\n", plugin7);
22✔
2034
    badid = sir_loadplugin(plugin7);
22✔
2035
    _sir_eqland(pass, 0 != badid); /* this one should load, just return false from write */
22✔
2036

2037
    _sir_eqland(pass, !sir_info("should fail; a plugin failed to process the message."));
22✔
2038

2039
    print_test_error(pass, pass);
22✔
2040

2041
    printf("\tloading nonexistent plugin: '%s'...\n", plugin8);
22✔
2042
    badid = sir_loadplugin(plugin8);
22✔
2043
    _sir_eqland(pass, 0 == badid);
22✔
2044

2045
    print_test_error(pass, pass);
22✔
2046

2047
    /* unload the good plugin manually. */
2048
    printf("\tunloading good plugin: '%s'...\n", plugin1);
22✔
2049
    _sir_eqland(pass, sir_unloadplugin(id));
22✔
2050

2051
    print_test_error(pass, pass);
22✔
2052

2053
    /* try to unload the plugin again. */
2054
    printf("\nunloading already unloaded plugin '%s'...\n", plugin1);
22✔
2055
    _sir_eqland(pass, !sir_unloadplugin(id));
22✔
2056

2057
    print_test_error(pass, pass);
22✔
2058

2059
    /* test bad paths. */
2060
    printf("\ntrying to load plugin with NULL path...\n");
19✔
2061
    badid = sir_loadplugin(NULL);
22✔
2062
    _sir_eqland(pass, 0 == badid);
22✔
2063

2064
    print_test_error(pass, pass);
22✔
2065
#endif
2066
    _sir_eqland(pass, sir_cleanup());
23✔
2067
    return print_result_and_return(pass);
23✔
2068
}
2069

2070
bool sirtest_getversioninfo(void) {
23✔
2071
    INIT(si, SIRL_ALL, 0, 0, 0);
23✔
2072
    bool pass = si_init;
20✔
2073

2074
    printf("\tchecking version retrieval functions...\n");
20✔
2075

2076
    const char* str = sir_getversionstring();
23✔
2077
    _sir_eqland(pass, _sir_validstrnofail(str));
23✔
2078

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

2081
    uint32_t hex = sir_getversionhex();
23✔
2082
    _sir_eqland(pass, 0 != hex);
23✔
2083

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

2086
    bool prerel = sir_isprerelease();
23✔
2087
    printf("\tprerelease: %s\n", prerel ? "true" : "false");
23✔
2088

2089
    _sir_eqland(pass, sir_cleanup());
23✔
2090
    return print_result_and_return(pass);
23✔
2091
}
2092

2093
enum {
2094
    NUM_THREADS = 4
2095
};
2096

2097
static bool threadpool_pseudojob(void* arg) {
165✔
2098
    sir_debug("this is a pseudo job that actually does nothing (arg: %p)", arg);
165✔
2099
#if !defined(__WIN__)
2100
    sleep(1);
165✔
2101
#else
2102
    Sleep(1000);
2103
#endif
2104
    return true;
164✔
2105
}
2106

2107
bool sirtest_threadpool(void) {
23✔
2108
    INIT(si, SIRL_ALL, SIRO_NOTIME | SIRO_NOHOST | SIRO_NONAME, 0, 0);
23✔
2109
    bool pass = si_init;
20✔
2110

2111
    static const size_t num_jobs = 30;
2112
    sir_threadpool* pool         = NULL;
23✔
2113

2114
    _sir_eqland(pass, _sir_threadpool_create(&pool, NUM_THREADS));
23✔
2115
    if (pass) {
20✔
2116
        /* dispatch a whole bunch of jobs. */
2117
        for (size_t n = 0; n < num_jobs; n++) {
651✔
2118
            sir_threadpool_job* job = calloc(1, sizeof(sir_threadpool_job));
630✔
2119
            _sir_eqland(pass, NULL != job);
630✔
2120
            if (job) {
630✔
2121
                job->fn = &threadpool_pseudojob;
630✔
2122
                job->data = (void*)(n + 1);
630✔
2123
                _sir_eqland(pass, _sir_threadpool_add_job(pool, job));
630✔
2124
                _sir_eqland(pass, sir_info("dispatched job (fn: %"PRIxPTR", data: %p)",
630✔
2125
                    (uintptr_t)job->fn, job->data));
2126
            }
2127
        }
2128

2129
#if !defined(__WIN__)
2130
        sleep(1);
21✔
2131
#else
2132
        Sleep(1000);
2133
#endif
2134

2135
        _sir_eqland(pass, sir_info("destroying thread pool..."));
21✔
2136
        _sir_eqland(pass, _sir_threadpool_destroy(&pool));
21✔
2137
    }
2138

2139
    _sir_eqland(pass, sir_cleanup());
23✔
2140
    return print_result_and_return(pass);
23✔
2141
}
2142

2143
#if !defined(__WIN__)
2144
static void* threadrace_thread(void* arg);
2145
#else /* __WIN__ */
2146
static unsigned __stdcall threadrace_thread(void* arg);
2147
#endif
2148

2149
bool sirtest_threadrace(void) {
23✔
2150
#if !defined(__WIN__)
2151
    pthread_t thrds[NUM_THREADS] = {0};
23✔
2152
#else /* __WIN__ */
2153
    uintptr_t thrds[NUM_THREADS] = {0};
2154
#endif
2155

2156
    INIT_N(si, SIRL_DEFAULT, SIRO_NOPID | SIRO_NOHOST, 0, 0, "thread-race");
23✔
2157
    bool pass           = si_init;
20✔
2158
    bool any_created    = false;
20✔
2159
    size_t last_created = 0;
20✔
2160

2161
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
23✔
2162
    _sir_eqland(pass, NULL != heap_args);
23✔
2163
    if (!heap_args) {
23✔
2164
        handle_os_error(true, "calloc(%zu) bytes failed!", NUM_THREADS * sizeof(thread_args));
1✔
2165
        return false;
1✔
2166
    }
2167

2168
    for (size_t n = 0; n < NUM_THREADS; n++) {
106✔
2169
        if (!pass)
85✔
2170
            break;
×
2171

2172
        heap_args[n].pass = true;
85✔
2173
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
85✔
2174
            MAKE_LOG_NAME("multi-thread-race-%zu.log"), n);
2175

2176
#if !defined(__WIN__)
2177
        int create = pthread_create(&thrds[n], NULL, threadrace_thread, (void*)&heap_args[n]);
85✔
2178
        if (0 != create) {
85✔
2179
            errno = create;
1✔
2180
            handle_os_error(true, "pthread_create() for thread #%zu failed!", n + 1);
1✔
2181
#else /* __WIN__ */
2182
        thrds[n] = _beginthreadex(NULL, 0, threadrace_thread, (void*)&heap_args[n], 0, NULL);
2183
        if (0 == thrds[n]) {
2184
            handle_os_error(true, "_beginthreadex() for thread #%zu failed!", n + 1);
2185
#endif
2186
            pass = false;
1✔
2187
            break;
1✔
2188
        }
2189

2190
        last_created = n;
72✔
2191
        any_created  = true;
72✔
2192
    }
2193

2194
    if (any_created) {
22✔
2195
        for (size_t j = 0; j < last_created + 1; j++) {
105✔
2196
            bool joined = true;
72✔
2197
            printf("\twaiting for thread %zu/%zu...\n", j + 1, last_created + 1);
84✔
2198
#if !defined(__WIN__)
2199
            int join = pthread_join(thrds[j], NULL);
84✔
2200
            if (0 != join) {
84✔
2201
                joined = false;
×
2202
                errno  = join;
×
2203
                handle_os_error(true, "pthread_join() for thread #%zu failed!", j + 1);
×
2204
            }
2205
#else /* __WIN__ */
2206
            DWORD wait = WaitForSingleObject((HANDLE)thrds[j], INFINITE);
2207
            if (WAIT_OBJECT_0 != wait) {
2208
                joined = false;
2209
                handle_os_error(false, "WaitForSingleObject() for thread #%zu (%p) failed!", j + 1,
2210
                    (HANDLE)thrds[j]);
2211
            }
2212
#endif
2213
            _sir_eqland(pass, joined);
72✔
2214
            if (joined) {
72✔
2215
                printf("\tthread %zu/%zu joined\n", j + 1, last_created + 1);
72✔
2216

2217
                _sir_eqland(pass, heap_args[j].pass);
84✔
2218
                if (heap_args[j].pass)
84✔
2219
                    printf("\t" GREEN("thread #%zu returned pass = true") "\n", j + 1);
72✔
2220
                else
2221
                    printf("\t" RED("thread #%zu returned pass = false!") "\n", j + 1);
×
2222
            }
2223
        }
2224
    }
2225

2226
    _sir_safefree(&heap_args);
22✔
2227

2228
    _sir_eqland(pass, sir_cleanup());
22✔
2229
    return print_result_and_return(pass);
22✔
2230
}
2231

2232
#if !defined(__WIN__)
2233
static void* threadrace_thread(void* arg) {
84✔
2234
#else /* __WIN__ */
2235
unsigned __stdcall threadrace_thread(void* arg) {
2236
#endif
2237
    pid_t threadid       = _sir_gettid();
84✔
2238
    thread_args* my_args = (thread_args*)arg;
72✔
2239

2240
    (void)rmfile(my_args->log_file);
84✔
2241
    sirfileid id = sir_addfile(my_args->log_file, SIRL_ALL, SIRO_MSGONLY);
84✔
2242

2243
    if (0U == id) {
84✔
2244
        bool unused = print_test_error(false, false);
8✔
2245
        SIR_UNUSED(unused);
2246
#if !defined(__WIN__)
2247
        return NULL;
8✔
2248
#else /* __WIN__ */
2249
        return 0;
2250
#endif
2251
    }
2252

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

2256
#if !defined(DUMA)
2257
# define NUM_ITERATIONS 1000
2258
#else
2259
# define NUM_ITERATIONS 100
2260
#endif
2261

2262
    for (size_t n = 0; n < NUM_ITERATIONS; n++) {
76,076✔
2263
        /* choose a random level, and colors. */
2264
        sir_textcolor fg = SIRTC_DEFAULT;
64,000✔
2265
        sir_textcolor bg = SIRTC_DEFAULT;
64,000✔
2266

2267
        if (n % 2 == 0) {
76,000✔
2268
            fg = SIRTC_CYAN;
32,000✔
2269
            bg = SIRTC_BLACK;
32,000✔
2270
            sir_debug("log message #%zu", n);
38,000✔
2271
        } else {
2272
            fg = SIRTC_BLACK;
32,000✔
2273
            bg = SIRTC_CYAN;
32,000✔
2274
            sir_info("log message #%zu", n);
38,000✔
2275
        }
2276

2277
        /* sometimes remove and re-add the log file, and set some options/styles.
2278
         * other times, just set different options/styles. */
2279
        uint32_t rnd = (uint32_t)(n + threadid);
76,000✔
2280
        if (getrand_bool(rnd > 1U ? rnd : 1U)) {
76,000✔
2281
            my_args->pass = print_test_error(sir_remfile(id), false);
38,215✔
2282
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
38,215✔
2283
                SIRL_ALL, SIRO_MSGONLY), false);
2284

2285
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_EMPH, fg, bg) &&
76,430✔
2286
                        sir_settextstyle(SIRL_INFO, SIRTA_BOLD, fg, bg);
38,215✔
2287
            my_args->pass = print_test_error(test, false);
38,215✔
2288

2289
            test = sir_stdoutopts(SIRO_NONAME | SIRO_NOHOST | SIRO_NOMSEC);
38,215✔
2290
            my_args->pass = print_test_error(test, false);
38,215✔
2291
        } else {
2292
            bool test = sir_settextstyle(SIRL_DEBUG, SIRTA_ULINE, fg, bg) &&
75,570✔
2293
                        sir_settextstyle(SIRL_INFO, SIRTA_NORMAL, fg, bg);
37,785✔
2294
            my_args->pass = print_test_error(test, false);
37,785✔
2295

2296
            test = sir_fileopts(id, SIRO_NOPID | SIRO_NOHOST);
37,785✔
2297
            my_args->pass = print_test_error(test, false);
37,784✔
2298

2299
            test = sir_stdoutopts(SIRO_NOTIME | SIRO_NOLEVEL);
37,784✔
2300
            my_args->pass = print_test_error(test, false);
37,785✔
2301
        }
2302

2303
        if (!my_args->pass)
76,000✔
2304
            break;
×
2305
    }
2306

2307
    my_args->pass = print_test_error(sir_remfile(id), false);
76✔
2308

2309
    (void)rmfile(my_args->log_file);
76✔
2310

2311
#if !defined(__WIN__)
2312
    return NULL;
76✔
2313
#else /* __WIN__ */
2314
    return 0U;
2315
#endif
2316
}
2317

2318
/*
2319
bool sirtest_XXX(void) {
2320
    INIT(si, SIRL_ALL, 0, 0, 0);
2321
    bool pass = si_init;
2322

2323
    _sir_eqland(pass, sir_cleanup());
2324
    return print_result_and_return(pass);
2325
}
2326
*/
2327

2328
/* ========================== end tests ========================== */
2329

2330
bool print_test_error(bool result, bool expected) {
267,964✔
2331
    char message[SIR_MAXERROR] = {0};
267,964✔
2332
    uint16_t code              = sir_geterror(message);
267,964✔
2333

2334
    if (!expected && !result && SIR_E_NOERROR != code)
267,966✔
2335
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
76✔
2336
    else if (expected && SIR_E_NOERROR != code)
267,890✔
2337
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
525✔
2338

2339
    return result;
267,960✔
2340
}
2341

2342
bool print_os_error(void) {
57✔
2343
    char message[SIR_MAXERROR] = {0};
57✔
2344
    uint16_t code              = sir_geterror(message);
57✔
2345
    fprintf(stderr, "\t" RED("OS error: (%"PRIu16", %s)") "\n", code, message);
57✔
2346
    return false;
57✔
2347
}
2348

2349
bool filter_error(bool pass, uint16_t err) {
1,528✔
2350
    if (!pass) {
1,528✔
2351
        char message[SIR_MAXERROR] = {0};
956✔
2352
        uint16_t code              = sir_geterror(message);
956✔
2353
        if (code != err)
956✔
2354
            return false;
2✔
2355
    }
2356
    return true;
1,286✔
2357
}
2358

2359
uint32_t getrand(uint32_t upper_bound) {
117,770✔
2360
#if !defined(__WIN__) || defined(__EMBARCADEROC__)
2361
# if defined(__MACOS__) || defined(__BSD__)
2362
    if (upper_bound < 2U)
2363
        upper_bound = 2U;
2364
    return arc4random_uniform(upper_bound);
2365
# else
2366
#  if defined(__EMBARCADEROC__)
2367
    return (uint32_t)(random(upper_bound));
2368
#  else
2369
    return (uint32_t)(random() % upper_bound);
117,770✔
2370
#  endif
2371
# endif
2372
#else /* __WIN__ */
2373
    uint32_t ctx = 0;
2374
    if (0 != rand_s(&ctx))
2375
        ctx = (uint32_t)rand();
2376
    return ctx % upper_bound;
2377
#endif
2378
}
2379

2380
bool rmfile(const char* filename) {
1,154✔
2381
    bool removed = false;
1,001✔
2382

2383
    /* return true if leave_logs is true. */
2384
    if (leave_logs) {
1,154✔
2385
        printf("\t" WHITE("not deleting '%s' due to '%s'") "\n",
51✔
2386
            filename, _cl_arg_list[3].flag);
51✔
2387
        return true;
51✔
2388
    }
2389

2390
    /* return true if the file doesn't exist. */
2391
    struct stat st;
2392
    if (0 != stat(filename, &st)) {
1,103✔
2393
        if (ENOENT == errno)
568✔
2394
            return true;
457✔
2395

2396
        handle_os_error(true, "failed to stat %s!", filename);
45✔
2397
        return false;
45✔
2398
    }
2399

2400
    if (!_sir_deletefile(filename)) {
535✔
2401
        handle_os_error(false, "failed to delete %s!", filename);
×
2402
    } else {
2403
        printf("\t" DGRAY("deleted %s (%ld bytes)") "\n", filename,
448✔
2404
            (long)st.st_size);
535✔
2405
    }
2406

2407
    return removed;
448✔
2408
}
2409

2410
void deletefiles(const char* search, const char* path, const char* filename, unsigned* data) {
239✔
2411
    if (strstr(filename, search)) {
239✔
2412
        char filepath[SIR_MAXPATH];
2413
        _sir_snprintf_trunc(filepath, SIR_MAXPATH, "%s%s", path, filename);
57✔
2414

2415
        (void)rmfile(filepath);
57✔
2416
        (*data)++;
57✔
2417
    }
2418
}
239✔
2419

2420
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
118✔
2421
    SIR_UNUSED(path);
2422
    if (strstr(filename, search))
118✔
2423
        (*data)++;
34✔
2424
}
118✔
2425

2426
bool enumfiles(const char* path, const char* search, fileenumproc cb, unsigned* data) {
63✔
2427
#if !defined(__WIN__)
2428
    DIR* d = opendir(path);
63✔
2429
    if (!d)
63✔
2430
        return print_os_error();
2✔
2431

2432
    rewinddir(d);
61✔
2433
    const struct dirent* di = readdir(d);
61✔
2434
    if (!di) {
61✔
2435
        closedir(d);
×
2436
        return print_os_error();
×
2437
    }
2438

2439
    while (NULL != di) {
418✔
2440
        cb(search, path, di->d_name, data);
357✔
2441
        di = readdir(d);
357✔
2442
    }
2443

2444
    closedir(d);
61✔
2445
    d = NULL;
52✔
2446
#else /* __WIN__ */
2447
    WIN32_FIND_DATA finddata = {0};
2448
    char buf[SIR_MAXPATH]    = {0};
2449

2450
    (void)snprintf(buf, SIR_MAXPATH, "%s/*", path);
2451

2452
    HANDLE enumerator = FindFirstFile(buf, &finddata);
2453

2454
    if (INVALID_HANDLE_VALUE == enumerator)
2455
        return false;
2456

2457
    do {
2458
        cb(search, path, finddata.cFileName, data);
2459
    } while (FindNextFile(enumerator, &finddata) > 0);
2460

2461
    FindClose(enumerator);
2462
    enumerator = NULL;
2463
#endif
2464

2465
    return true;
61✔
2466
}
2467

2468
double sir_timer_elapsed(const sir_time* timer) {
2,000,028✔
2469
    sir_time now;
2470
    return _sir_msec_since(timer, &now);
2,000,028✔
2471
}
2472

2473
long sir_timer_getres(void) {
1✔
2474
    long retval = 0L;
1✔
2475
#if !defined(__WIN__)
2476
    struct timespec res;
2477
    if (0 == clock_getres(SIR_INTERVALCLOCK, &res)) {
1✔
2478
        retval = res.tv_nsec;
1✔
2479
    } else {
2480
        handle_os_error(true, "clock_getres(%d) failed!", CLOCK_CAST SIR_INTERVALCLOCK); // GCOVR_EXCL_LINE
2481
    }
2482
#else /* __WIN__ */
2483
    LARGE_INTEGER cntr_freq;
2484
    (void)QueryPerformanceFrequency(&cntr_freq);
2485
    if (cntr_freq.QuadPart <= 0)
2486
        retval = 0L;
2487
    else
2488
        retval = (long)(ceil(((double)cntr_freq.QuadPart) / 1e9));
2489
#endif
2490
    return retval;
1✔
2491
}
2492

2493
void sir_sleep_msec(uint32_t msec) {
46✔
2494
    if (0U == msec)
46✔
2495
        return;
×
2496

2497
#if !defined(__WIN__)
2498
    struct timespec ts = { msec / 1000, (msec % 1000) * 1000000 };
46✔
2499
    (void)nanosleep(&ts, NULL);
46✔
2500
#else /* __WIN__ */
2501
    (void)SleepEx((DWORD)msec, TRUE);
2502
#endif
2503
}
2504

2505
size_t sir_readline(FILE* f, char* buf, size_t size) {
80✔
2506
    if (!f || !buf || 0 == size)
80✔
2507
        return 0;
×
2508

2509
    int ch     = 0;
68✔
2510
    size_t idx = 0;
68✔
2511

2512
    while (idx < size) {
13,966✔
2513
        ch = getc(f);
13,966✔
2514
        if (EOF == ch || '\n' == ch)
13,966✔
2515
            break;
2516
        buf[idx++] = (char)ch;
13,886✔
2517
    }
2518

2519
    return (0 == ferror(f)) ? idx : 0;
80✔
2520
}
2521

2522
#if defined(SIR_OS_LOG_ENABLED)
2523
void os_log_parent_activity(void* ctx) {
2524
    sir_debug("confirming with ground control that we are a go...");
2525
    sir_info("all systems go; initiating launch sequence");
2526
    sir_warn("getting some excessive vibration here");
2527
    sir_info("safely reached escape velocity. catch you on the flip side");
2528
    sir_info("(3 days later) we have landed on the lunar surface");
2529
    sir_notice("beginning rock counting...");
2530

2531
    os_activity_t parent = (os_activity_t)ctx;
2532
    os_activity_t child = os_activity_create("counting moon rocks", parent, // -V530
2533
        OS_ACTIVITY_FLAG_DEFAULT);
2534

2535
    float rock_count = 0.0f;
2536
    os_activity_apply_f(child, (void*)&rock_count, os_log_child_activity);
2537
    sir_info("astronauts safely back on board. official count: ~%.02f moon rocks",
2538
        (double)rock_count);
2539
}
2540

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

2544
    for (size_t n = 0; n < 10; n++) {
2545
        sir_info("counting rocks in sector %zu...", n);
2546
    }
2547

2548
    float* rock_count = (float*)ctx;
2549
    *rock_count = 1e12f;
2550
    sir_info("all sectors counted; heading back to the lunar lander");
2551
}
2552
#endif
2553

2554
bool mark_test_to_run(const char* name) {
3✔
2555
    bool found = false;
3✔
2556
    for (size_t t = 0; t < _sir_countof(sir_tests); t++) {
48✔
2557
        if (_sir_strsame(name, sir_tests[t].name,
47✔
2558
            strnlen(sir_tests[t].name, SIR_MAXTESTNAME))) {
2559
            found = sir_tests[t].run = true;
2✔
2560
            break;
2✔
2561
        }
2562
    }
2563

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

2567
    return found;
3✔
2568
}
2569

2570
void print_usage_info(void) {
4✔
2571
    size_t longest = 0;
4✔
2572
    for (size_t i = 0; i < _sir_countof(_cl_arg_list); i++) {
32✔
2573
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2574
        if (len > longest)
28✔
2575
            longest = len;
8✔
2576
    }
2577

2578
    fprintf(stderr, "\n" WHITE("Usage:") "\n\n");
4✔
2579

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

2583
        size_t len = strnlen(_cl_arg_list[i].flag, SIR_MAXCLIFLAG);
28✔
2584
        if (len < longest)
28✔
2585
            for (size_t n = len; n < longest; n++)
156✔
2586
                fprintf(stderr, " ");
132✔
2587

2588
        fprintf(stderr, "%s%s%s\n", _cl_arg_list[i].usage,
28✔
2589
            strnlen(_cl_arg_list[i].usage, SIR_MAXUSAGE) > 0 ? " " : "",
28✔
2590
            _cl_arg_list[i].desc);
28✔
2591
    }
2592

2593
    fprintf(stderr, "\n");
4✔
2594
}
4✔
2595

2596
void print_test_list(void) {
1✔
2597
    size_t longest = 0;
1✔
2598
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
34✔
2599
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
33✔
2600
        if (len > longest)
33✔
2601
            longest = len;
3✔
2602
    }
2603

2604
    printf("\n" WHITE("Available tests:") "\n\n");
1✔
2605

2606
    for (size_t i = 0; i < _sir_countof(sir_tests); i++) {
34✔
2607
        printf("\t%s\t", sir_tests[i].name);
33✔
2608

2609
        size_t len = strnlen(sir_tests[i].name, SIR_MAXTESTNAME);
33✔
2610
        if (len < longest)
33✔
2611
            for (size_t n = len; n < longest; n++)
284✔
2612
                printf(" ");
252✔
2613

2614
        if ((i % 2) != 0 || i == _sir_countof(sir_tests) - 1)
33✔
2615
            printf("\n");
17✔
2616
    }
2617

2618
    printf("\n");
1✔
2619
}
1✔
2620

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