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

aremmell / libsir / 489

07 Sep 2023 07:23AM UTC coverage: 94.735% (-0.05%) from 94.782%
489

Pull #268

gitlab-ci

aremmell
set perf test lines back to 100k
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%)

3113 of 3286 relevant lines covered (94.74%)

614543.78 hits per line

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

97.83
/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) {
33✔
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) {
33✔
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;
29✔
90
    bool only     = false;
29✔
91
    size_t to_run = 0;
29✔
92

93
    for (int n = 1; n < argc; n++) {
36✔
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);
26✔
144
    size_t tgt_tests = (only ? to_run : _sir_countof(sir_tests) - first);
26✔
145
    size_t passed    = 0;
23✔
146
    size_t ran       = 0;
23✔
147
    sir_time timer  = {0};
26✔
148

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

154
    for (size_t n = first; n < _sir_countof(sir_tests); n++) {
860✔
155
        if (only && !sir_tests[n].run) {
834✔
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);
770✔
161

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

166
        ran++;
674✔
167

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

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

174
    if (passed == tgt_tests) {
26✔
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: ")
17✔
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));
17✔
184

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

191
    if (wait) {
26✔
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;
26✔
199
}
200

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

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

209
    _sir_andeql(pass, sir_info("%s", toobig));
24✔
210

211
    _sir_andeql(pass, sir_cleanup());
24✔
212
    return print_result_and_return(pass);
24✔
213
}
214

215
bool sirtest_logwritesanity(void) {
24✔
216
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
217
    bool pass = si_init;
21✔
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);
24✔
233
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
24✔
234
    _sir_andeql(pass, 0U != id);
24✔
235

236
    print_test_error(pass, false);
24✔
237

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

240
    _sir_andeql(pass, sir_debug("%s", message));
24✔
241

242
    print_test_error(pass, false);
24✔
243

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

247
    print_test_error(pass, false);
24✔
248

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

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

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

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

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

271
    _sir_andeql(pass, sir_cleanup());
24✔
272
    return print_result_and_return(pass);
24✔
273
}
274

275
bool sirtest_threadidsanity(void)
24✔
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);
24✔
282
    bool pass = si_init;
21✔
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);
24✔
288
    sirfileid id = sir_addfile(logfilename, SIRL_DEBUG, SIRO_NOHDR | SIRO_NOHOST);
24✔
289
    _sir_andeql(pass, 0U != id);
24✔
290

291
    print_test_error(pass, false);
24✔
292

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

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

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

301
    print_test_error(pass, false);
24✔
302

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

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

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

310
    print_test_error(pass, false);
24✔
311

312
    _sir_andeql(pass, sir_debug("this is a test of the libsir system after clearing thread name"));
24✔
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);
24✔
316
    _sir_andeql(pass, sir_remfile(id));
24✔
317

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

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

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

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

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

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

356
    _sir_andeql(pass, sir_cleanup());
24✔
357
    return print_result_and_return(pass);
24✔
358
}
359

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

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

366
    _sir_andeql(pass, !sir_notice("this goes nowhere!"));
24✔
367

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

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

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

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

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

387
    _sir_andeql(pass, sir_cleanup());
24✔
388
    return print_result_and_return(pass);
24✔
389
}
390

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

395
    _sir_andeql(pass, !sir_init(NULL));
24✔
396

397
    if (pass)
21✔
398
        print_expected_error();
24✔
399

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

403
    if (pass)
21✔
404
        print_expected_error();
18✔
405

406
    _sir_andeql(pass, 0U == sir_addfile(NULL, SIRL_ALL, SIRO_MSGONLY));
24✔
407

408
    if (pass)
21✔
409
        print_expected_error();
18✔
410

411
    _sir_andeql(pass, !sir_remfile(0U));
24✔
412

413
    if (pass)
21✔
414
        print_expected_error();
18✔
415

416
    _sir_andeql(pass, sir_cleanup());
24✔
417
    return print_result_and_return(pass);
24✔
418
}
419

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

424
    _sir_andeql(pass, !sir_debug("%s", ""));
24✔
425

426
    _sir_andeql(pass, sir_cleanup());
24✔
427
    return print_result_and_return(pass);
24✔
428
}
429

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

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

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

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

448
    _sir_andeql(pass, sir_info("test test test"));
24✔
449

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

454
    if (pass)
21✔
455
        print_expected_error();
18✔
456

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

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

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

466
    do {
1,153✔
467
        size_t rnd = (size_t)getrand(SIR_MAXFILES);
1,336✔
468
        bool skip  = false;
1,174✔
469

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

476
        if (skip)
1,336✔
477
            continue;
952✔
478

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

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

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

490
    for (size_t n = 0; n < SIR_MAXFILES; n++) {
408✔
491
        _sir_andeql(pass, sir_remfile(ids[removeorder[n]]));
384✔
492

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

498
    _sir_andeql(pass, sir_info("test test test"));
24✔
499

500
    _sir_andeql(pass, sir_cleanup());
24✔
501
    return print_result_and_return(pass);
24✔
502
}
503

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

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

510
    if (pass)
21✔
511
        print_expected_error();
18✔
512

513
    _sir_andeql(pass, sir_cleanup());
24✔
514
    return print_result_and_return(pass);
24✔
515
}
516

517
bool sirtest_failfilebadpermission(void) {
24✔
518
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
519
    bool pass = si_init;
21✔
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_andeql(pass, 0U == sir_addfile(path, SIRL_ALL, SIRO_MSGONLY));
24✔
537

538
    if (pass)
21✔
539
        print_expected_error();
18✔
540

541
    _sir_andeql(pass, sir_cleanup());
24✔
542
    return print_result_and_return(pass);
24✔
543
}
544

545
bool sirtest_faildupefile(void) {
24✔
546
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
547
    bool pass = si_init;
21✔
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);
24✔
561

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

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

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

571
    if (pass)
21✔
572
        print_expected_error();
18✔
573

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

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

580
    if (pass)
21✔
581
        print_expected_error();
18✔
582

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

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

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

593
    _sir_andeql(pass, sir_info("hello three valid files"));
24✔
594

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

598
    if (pass)
21✔
599
        print_expected_error();
18✔
600

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

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

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

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

618
    sirfileid invalidid = 9999999;
21✔
619
    _sir_andeql(pass, !sir_remfile(invalidid));
24✔
620

621
    if (pass)
21✔
622
        print_expected_error();
18✔
623

624
    _sir_andeql(pass, sir_cleanup());
24✔
625
    return print_result_and_return(pass);
24✔
626
}
627

628
bool sirtest_rollandarchivefile(void) {
25✔
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;
22✔
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};
25✔
637
    (void)snprintf(logfilename, SIR_MAXPATH, MAKE_LOG_NAME("%s%s"), logbasename, logext);
25✔
638

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

641
    unsigned delcount = 0U;
25✔
642
    if (!enumfiles(SIR_TESTLOGDIR, logbasename, deletefiles, &delcount)) {
25✔
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)
23✔
649
        printf("\tfound and removed %u log file(s)\n", delcount);
×
650

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

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

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

659
    if (0 != fseek(f, fillsize, SEEK_SET)) {
23✔
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)) {
22✔
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);
21✔
672

673
    INIT(si, 0, 0, 0, 0);
21✔
674
    bool pass = si_init;
18✔
675

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

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

681
    print_test_error(pass, false);
21✔
682

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

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

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

696
        /* look for files matching the original name. */
697
        unsigned foundlogs = 0U;
16✔
698
        if (!enumfiles(SIR_TESTLOGDIR, logbasename, countfiles, &foundlogs)) {
16✔
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);
16✔
705
        _sir_andeql(pass, foundlogs >= 2U);
16✔
706
    }
707

708
    _sir_andeql(pass, sir_remfile(fileid));
21✔
709

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

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

719
    _sir_andeql(pass, sir_cleanup());
21✔
720
    return print_result_and_return(pass);
21✔
721
}
722

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

726
    if (pass)
24✔
727
        print_expected_error();
21✔
728

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

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

736
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
737
    _sir_andeql(pass, !si2_init);
24✔
738

739
    if (pass)
24✔
740
        print_expected_error();
16✔
741

742
    _sir_andeql(pass, sir_cleanup());
24✔
743
    return print_result_and_return(pass);
24✔
744
}
745

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

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

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

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

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

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

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

769
    INIT(si2, SIRL_ALL, 0, 0, 0);
24✔
770
    _sir_andeql(pass, si2_init);
24✔
771

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

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

778
bool sirtest_initmakeinit(void) {
24✔
779
    bool pass = true;
21✔
780

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

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

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

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

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

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

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

807
    struct {
808
        uint16_t code;
809
        const char* name;
810
    } errors[] = {
24✔
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};
24✔
838
    for (size_t n = 0; n < _sir_countof(errors); n++) {
600✔
839
        (void)_sir_seterror(_sir_mkerror(errors[n].code));
576✔
840
        memset(message, 0, SIR_MAXERROR);
504✔
841
        uint16_t err = sir_geterror(message);
576✔
842
        _sir_andeql(pass, errors[n].code == err && *message != '\0');
576✔
843
        printf("\t%s = %s\n", errors[n].name, message);
576✔
844
    }
845

846
    _sir_andeql(pass, sir_cleanup());
24✔
847
    return print_result_and_return(pass);
24✔
848
}
849

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

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

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

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

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

870
    printf("\t" WHITEB("--- override defaults ---") "\n");
21✔
871
    _sir_andeql(pass, sir_resettextstyles());
24✔
872

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

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

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

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

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

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

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

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

906
    printf("\t" WHITEB("--- reset to defaults ---") "\n");
21✔
907
    _sir_andeql(pass, sir_resettextstyles());
24✔
908

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

919
    printf("\t" WHITEB("--- change mode: 256-color ---") "\n");
21✔
920
    _sir_andeql(pass, sir_setcolormode(SIRCM_256));
24✔
921

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

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

932
    printf("\t" WHITEB("--- change mode: RGB-color ---") "\n");
21✔
933
    _sir_andeql(pass, sir_setcolormode(SIRCM_RGB));
24✔
934

935
    for (size_t n = 0; n < 256; n++) {
6,168✔
936
        sir_textcolor fg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
937
        sir_textcolor bg = sir_makergb(getrand(255U), getrand(255U), getrand(255U));
6,144✔
938
        _sir_andeql(pass, sir_settextstyle(SIRL_DEBUG, SIRTA_NORMAL, fg, bg));
6,144✔
939
        _sir_andeql(pass, sir_debug("this is RGB-color mode (fg: %"PRIu32", %"PRIu32", %"PRIu32
6,144✔
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));
24✔
945

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

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

961
    _sir_andeql(pass, sir_cleanup());
24✔
962

963
    return print_result_and_return(pass);
24✔
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) {
24✔
971
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
972
    bool pass = si_init;
21✔
973

974
    static const size_t iterations = 10;
975

976
    /* these should all be valid. */
977
    printf("\t" WHITEB("--- individual valid options ---") "\n");
21✔
978
    _sir_andeql(pass, _sir_validopts(SIRO_ALL));
24✔
979
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_ALL);
21✔
980
    _sir_andeql(pass, _sir_validopts(SIRO_NOTIME));
24✔
981
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTIME);
21✔
982
    _sir_andeql(pass, _sir_validopts(SIRO_NOHOST));
24✔
983
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHOST);
21✔
984
    _sir_andeql(pass, _sir_validopts(SIRO_NOLEVEL));
24✔
985
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOLEVEL);
21✔
986
    _sir_andeql(pass, _sir_validopts(SIRO_NONAME));
24✔
987
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NONAME);
21✔
988
    _sir_andeql(pass, _sir_validopts(SIRO_NOPID));
24✔
989
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOPID);
21✔
990
    _sir_andeql(pass, _sir_validopts(SIRO_NOTID));
24✔
991
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOTID);
21✔
992
    _sir_andeql(pass, _sir_validopts(SIRO_NOHDR));
24✔
993
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_NOHDR);
21✔
994
    _sir_andeql(pass, _sir_validopts(SIRO_MSGONLY));
24✔
995
    printf(INDENT_ITEM WHITE("valid option: %08"PRIx32) "\n", SIRO_MSGONLY);
21✔
996
    PRINT_PASS(pass, "\t--- individual valid options: %s ---\n\n", PRN_PASS(pass));
24✔
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");
21✔
1012
    uint32_t last_count = SIR_NUMOPTIONS;
21✔
1013
    for (size_t n = 0; n < iterations; n++) {
264✔
1014
        sir_options opts    = 0;
210✔
1015
        uint32_t rand_count = 0;
210✔
1016
        size_t last_idx     = 0;
210✔
1017

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

1022
        last_count = rand_count;
210✔
1023

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

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

1034
            last_idx = rand_idx;
896✔
1035
            opts |= option_arr[rand_idx];
1,025✔
1036
        }
1037

1038
        _sir_andeql(pass, _sir_validopts(opts));
240✔
1039
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32
240✔
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));
24✔
1043

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

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

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

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

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

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

1070
    _sir_andeql(pass, sir_cleanup());
24✔
1071
    return print_result_and_return(pass);
24✔
1072
}
1073

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

1078
    static const size_t iterations = 10;
1079

1080
    /* these should all be valid. */
1081
    printf("\t" WHITEB("--- individual valid levels ---") "\n");
21✔
1082
    _sir_andeql(pass, _sir_validlevel(SIRL_INFO) && _sir_validlevels(SIRL_INFO));
24✔
1083
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_INFO);
21✔
1084
    _sir_andeql(pass, _sir_validlevel(SIRL_DEBUG) && _sir_validlevels(SIRL_DEBUG));
24✔
1085
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_DEBUG);
21✔
1086
    _sir_andeql(pass, _sir_validlevel(SIRL_NOTICE) && _sir_validlevels(SIRL_NOTICE));
24✔
1087
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_NOTICE);
21✔
1088
    _sir_andeql(pass, _sir_validlevel(SIRL_WARN) && _sir_validlevels(SIRL_WARN));
24✔
1089
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_WARN);
21✔
1090
    _sir_andeql(pass, _sir_validlevel(SIRL_ERROR) && _sir_validlevels(SIRL_ERROR));
24✔
1091
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ERROR);
21✔
1092
    _sir_andeql(pass, _sir_validlevel(SIRL_CRIT) && _sir_validlevels(SIRL_CRIT));
24✔
1093
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_CRIT);
21✔
1094
    _sir_andeql(pass, _sir_validlevel(SIRL_ALERT) && _sir_validlevels(SIRL_ALERT));
24✔
1095
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_ALERT);
21✔
1096
    _sir_andeql(pass, _sir_validlevel(SIRL_EMERG) && _sir_validlevels(SIRL_EMERG));
24✔
1097
    printf(INDENT_ITEM WHITE("valid level: %04x") "\n", SIRL_EMERG);
21✔
1098
    _sir_andeql(pass, _sir_validlevels(SIRL_ALL));
24✔
1099
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_ALL);
21✔
1100
    _sir_andeql(pass, _sir_validlevels(SIRL_NONE));
24✔
1101
    printf(INDENT_ITEM WHITE("valid levels: %04x") "\n", SIRL_NONE);
21✔
1102
    PRINT_PASS(pass, "\t--- individual valid levels: %s ---\n\n", PRN_PASS(pass));
24✔
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");
21✔
1118
    uint32_t last_count = SIR_NUMLEVELS;
21✔
1119
    for (size_t n = 0; n < iterations; n++) {
264✔
1120
        sir_levels levels   = 0U;
210✔
1121
        uint32_t rand_count = 0U;
210✔
1122
        size_t last_idx     = 0UL;
210✔
1123

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

1128
        last_count = rand_count;
210✔
1129

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

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

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

1144
        _sir_andeql(pass, _sir_validlevels(levels));
240✔
1145
        printf(INDENT_ITEM WHITE("(%zu/%zu): random valid (count: %"PRIu32", levels:"
240✔
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));
24✔
1149

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

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

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

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

1164
    _sir_andeql(pass, sir_cleanup());
24✔
1165
    return print_result_and_return(pass);
24✔
1166
}
1167

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

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

1175
    sir_mutex m1 = SIR_MUTEX_INIT;
24✔
1176
    _sir_andeql(pass, _sir_mutexcreate(&m1));
24✔
1177

1178
    print_test_error(pass, pass);
24✔
1179

1180
    if (pass) {
24✔
1181
        printf(INDENT_ITEM WHITE("locking (wait)...") "\n");
20✔
1182
        _sir_andeql(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_andeql(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_andeql(pass, _sir_mutextrylock(&m1));
23✔
1193

1194
        print_test_error(pass, pass);
23✔
1195

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

1199
        print_test_error(pass, pass);
23✔
1200

1201
        printf(INDENT_ITEM WHITE("destryoing...") "\n");
20✔
1202
        _sir_andeql(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));
24✔
1208

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

1222
    _sir_andeql(pass, sir_cleanup());
24✔
1223
    return print_result_and_return(pass); // -V1020
24✔
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_andeql(pass, sir_cleanup());
1✔
1277

1278
        INIT(si2, 0, 0, 0, 0);
1✔
1279
        _sir_andeql(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_andeql(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_andeql(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_andeql(pass, sir_cleanup());
1✔
1325
    return print_result_and_return(pass);
1✔
1326
}
1327

1328
bool sirtest_updatesanity(void) {
24✔
1329
    INIT_N(si, SIRL_DEFAULT, 0, SIRL_DEFAULT, 0, "update_sanity");
24✔
1330
    bool pass = si_init;
21✔
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);
24✔
1352
    sirfileid id1 = sir_addfile(logfile, SIRL_DEFAULT, SIRO_DEFAULT);
24✔
1353
    _sir_andeql(pass, 0 != id1);
24✔
1354

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

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

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

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

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

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

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

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

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

1417
    _sir_andeql(pass, sir_remfile(id1));
24✔
1418
    (void)rmfile(logfile);
24✔
1419

1420
    _sir_andeql(pass, sir_cleanup());
24✔
1421
    return print_result_and_return(pass);
24✔
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) {
18✔
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', "
15✔
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());
18✔
1435
# else
1436
    uint32_t rnd = (uint32_t)GetTickCount();
1437
# endif
1438

1439
    bool pass = true;
15✔
1440
    for (int i = 1; i <= runs; i++) {
98✔
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);
82✔
1444
        bool set_identity = getrand_bool(rnd ^ 0xc9c9c9c9U);
82✔
1445
        bool set_category = getrand_bool(rnd ^ 0x32323232U);
82✔
1446
        bool do_update    = getrand_bool(rnd ^ 0xe7e7e7e7U);
82✔
1447

1448
        printf("\tset_procname: %d, set_identity: %d, set_category: %d, do_update: %d\n",
82✔
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" : ""));
91✔
1452
        si.d_syslog.opts   = SIRO_DEFAULT;
82✔
1453
        si.d_syslog.levels = SIRL_DEFAULT;
82✔
1454

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

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

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

1464
        if (do_update)
82✔
1465
            _sir_andeql(pass, sir_sysloglevels(SIRL_ALL));
34✔
1466

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

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

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

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

1484
        if (do_update)
82✔
1485
            _sir_andeql(pass, sir_syslogopts(SIRO_MSGONLY & ~(SIRO_NOLEVEL | SIRO_NOPID)));
34✔
1486

1487
        _sir_andeql(pass, sir_crit("%d/%d: this critical message sent to stdout and %s.", i, runs, sl_name));
82✔
1488
        _sir_andeql(pass, sir_alert("%d/%d: this alert message sent to stdout and %s.", i, runs, sl_name));
82✔
1489
        _sir_andeql(pass, sir_emerg("%d/%d: this emergency message sent to stdout and %s.", i, runs, sl_name));
82✔
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_andeql(pass, sir_cleanup());
82✔
1509

1510
        if (!pass)
82✔
1511
            break;
2✔
1512
    }
1513

1514
    return print_result_and_return(pass);
18✔
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_andeql(pass, sir_init(&si));
6✔
1532

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

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

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

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

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

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

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

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

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

1563
bool sirtest_syslog(void) {
24✔
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");
18✔
1574
    return print_result_and_return(pass);
18✔
1575
#endif
1576
}
1577

1578
bool sirtest_os_log(void) {
24✔
1579
#if !defined(SIR_OS_LOG_ENABLED)
1580
    printf("\t" DGRAY("SIR_OS_LOG_ENABLED is not defined; skipping") "\n");
21✔
1581
    return true;
24✔
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) {
48✔
1589
#if !defined(__WIN__)
1590
    return NULL;
48✔
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) {
24✔
1609
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1610
    bool pass = si_init;
21✔
1611

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

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

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

1627
        if (NULL != filename) {
24✔
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));
21✔
1631
            _sir_andeql(pass, NULL != filename2);
21✔
1632

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

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

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

1655
            /* directory this binary file resides in. */
1656
            char* appdir = _sir_getappdir();
21✔
1657
            _sir_andeql(pass, NULL != appdir);
21✔
1658
            printf("\t_sir_getappdir: '%s'\n", PRN_STR(appdir));
21✔
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));
21✔
1663
            _sir_andeql(pass, NULL != filename3);
21✔
1664

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

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

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

1681
        _sir_safefree(&cwd);
24✔
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[] = {
24✔
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++) {
168✔
1706
        char* tmp = strndup(dubious_dirnames[n], strnlen(dubious_dirnames[n], SIR_MAXPATH));
144✔
1707
        if (NULL != tmp) {
144✔
1708
            printf("\t_sir_getdirname(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
138✔
1709
                tmp, _sir_getdirname(tmp));
1710
            _sir_safefree(&tmp);
138✔
1711
        }
1712
    }
1713

1714
    char* dubious_filenames[] = {
24✔
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++) {
144✔
1732
        char* tmp = strndup(dubious_filenames[n], strnlen(dubious_filenames[n], SIR_MAXPATH));
120✔
1733
        if (NULL != tmp) {
120✔
1734
            printf("\t_sir_getbasename(" WHITE("'%s'") ") = " WHITE("'%s'") "\n",
115✔
1735
                tmp, _sir_getbasename(tmp));
1736
            _sir_safefree(&tmp);
115✔
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++) {
216✔
1763
        bool relative = false;
192✔
1764
        bool ret      = _sir_ispathrelative(abs_or_rel_paths[n].path, &relative);
192✔
1765

1766
        if (relative == abs_or_rel_paths[n].abs) {
192✔
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,
192✔
1772
                relative ? "true" : "false");
168✔
1773
        }
1774

1775
        _sir_andeql(pass, ret);
192✔
1776
        if (!ret) {
192✔
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++) {
264✔
1812
        bool exists = false;
240✔
1813
        bool ret    = _sir_pathexists(real_or_not[n].path, &exists, SIR_PATH_REL_TO_APP);
240✔
1814

1815
        if (exists != real_or_not[n].exists) {
240✔
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,
217✔
1821
                exists ? "true" : "false");
187✔
1822
        }
1823

1824
        _sir_andeql(pass, ret);
240✔
1825
        if (!ret) {
240✔
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
24✔
1839
        bad_fds[3] = 0;
×
1840
    }
1841

1842
    for (size_t n = 0; n < _sir_countof(bad_fds); n++) {
120✔
1843
        if (_sir_validfd(bad_fds[n])) {
96✔
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]);
96✔
1848
        }
1849
    }
1850

1851
    FILE* f = NULL;
24✔
1852
    bool ret = _sir_openfile(&f, "file.exists", "r", SIR_PATH_REL_TO_APP);
24✔
1853
    if (!ret) {
24✔
1854
        pass = false;
4✔
1855
        handle_os_error(true, "fopen(%s) failed!", "file.exists");
4✔
1856
    } else {
1857
        int fd = fileno(f);
20✔
1858
        if (!_sir_validfd(fd)) {
20✔
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);
17✔
1863
        }
1864
    }
1865

1866
    _sir_safefclose(&f);
24✔
1867

1868
    _sir_andeql(pass, sir_cleanup());
24✔
1869
    return print_result_and_return(pass);
24✔
1870
}
1871

1872
bool sirtest_squelchspam(void) {
24✔
1873
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1874
    bool pass = si_init;
21✔
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);
24✔
1885

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

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

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

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

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

1901
        if (n >= SIR_SQUELCH_THRESHOLD - 1)
24,000✔
1902
            _sir_andeql(pass, !ret);
23,904✔
1903
        else
1904
            _sir_andeql(pass, ret);
96✔
1905
    }
1906

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

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

1920
            if (counter - 1 >= SIR_SQUELCH_THRESHOLD - 1)
11,976✔
1921
                _sir_andeql(pass, !ret);
11,016✔
1922
            else
1923
                _sir_andeql(pass, ret);
960✔
1924
        }
1925

1926
        if (counter == alternate) {
24,000✔
1927
            repeating = !repeating;
456✔
1928
            counter = 0;
399✔
1929
            repeat_id++;
456✔
1930
        }
1931

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

1936
    _sir_andeql(pass, sir_cleanup());
24✔
1937
    return print_result_and_return(pass);
24✔
1938
}
1939

1940
bool sirtest_pluginloader(void) {
24✔
1941
    INIT(si, SIRL_ALL, 0, 0, 0);
24✔
1942
    bool pass = si_init;
21✔
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_andeql(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_andeql(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);
23✔
1987
    sirpluginid id = sir_loadplugin(plugin1);
23✔
1988
    _sir_andeql(pass, 0 != id);
23✔
1989

1990
    print_test_error(pass, pass);
23✔
1991

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

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

2000
    print_test_error(pass, pass);
23✔
2001

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

2007
    print_test_error(pass, pass);
23✔
2008

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

2013
    print_test_error(pass, pass);
23✔
2014

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

2019
    print_test_error(pass, pass);
23✔
2020

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

2025
    print_test_error(pass, pass);
23✔
2026

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

2031
    print_test_error(pass, pass);
23✔
2032

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

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

2039
    print_test_error(pass, pass);
23✔
2040

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

2045
    print_test_error(pass, pass);
23✔
2046

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

2051
    print_test_error(pass, pass);
23✔
2052

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

2057
    print_test_error(pass, pass);
23✔
2058

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

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

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

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

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

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

2081
    uint32_t hex = sir_getversionhex();
24✔
2082
    _sir_andeql(pass, 0 != hex);
24✔
2083

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

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

2089
    _sir_andeql(pass, sir_cleanup());
24✔
2090
    return print_result_and_return(pass);
24✔
2091
}
2092

2093
enum {
2094
    NUM_THREADS = 4
2095
};
2096

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

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

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

2114
    _sir_andeql(pass, _sir_threadpool_create(&pool, NUM_THREADS));
24✔
2115
    if (pass) {
21✔
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_andeql(pass, NULL != job);
630✔
2120
            if (job) {
630✔
2121
                job->fn = &threadpool_pseudojob;
630✔
2122
                job->data = (void*)(n + 1);
630✔
2123
                _sir_andeql(pass, _sir_threadpool_add_job(pool, job));
630✔
2124
                _sir_andeql(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_andeql(pass, sir_info("destroying thread pool..."));
21✔
2136
        _sir_andeql(pass, _sir_threadpool_destroy(&pool));
21✔
2137
    }
2138

2139
    _sir_andeql(pass, sir_cleanup());
24✔
2140
    return print_result_and_return(pass);
24✔
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) {
24✔
2150
#if !defined(__WIN__)
2151
    pthread_t thrds[NUM_THREADS] = {0};
24✔
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");
24✔
2157
    bool pass           = si_init;
21✔
2158
    bool any_created    = false;
21✔
2159
    size_t last_created = 0;
21✔
2160

2161
    thread_args* heap_args = (thread_args*)calloc(NUM_THREADS, sizeof(thread_args));
24✔
2162
    _sir_andeql(pass, NULL != heap_args);
24✔
2163
    if (!heap_args) {
24✔
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++) {
111✔
2169
        if (!pass)
89✔
2170
            break;
×
2171

2172
        heap_args[n].pass = true;
89✔
2173
        (void)snprintf(heap_args[n].log_file, SIR_MAXPATH,
89✔
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]);
89✔
2178
        if (0 != create) {
89✔
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;
76✔
2191
        any_created  = true;
76✔
2192
    }
2193

2194
    if (any_created) {
23✔
2195
        for (size_t j = 0; j < last_created + 1; j++) {
110✔
2196
            bool joined = true;
76✔
2197
            printf("\twaiting for thread %zu/%zu...\n", j + 1, last_created + 1);
88✔
2198
#if !defined(__WIN__)
2199
            int join = pthread_join(thrds[j], NULL);
88✔
2200
            if (0 != join) {
88✔
2201
                joined = false;
4✔
2202
                errno  = join;
4✔
2203
                handle_os_error(true, "pthread_join() for thread #%zu failed!", j + 1);
4✔
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_andeql(pass, joined);
76✔
2214
            if (joined) {
76✔
2215
                printf("\tthread %zu/%zu joined\n", j + 1, last_created + 1);
72✔
2216

2217
                _sir_andeql(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);
23✔
2227

2228
    _sir_andeql(pass, sir_cleanup());
23✔
2229
    return print_result_and_return(pass);
23✔
2230
}
2231

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

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

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

2253
    printf("\thi, i'm thread (id: %d), logging to: '%s'...\n",
66✔
2254
            PID_CAST threadid, my_args->log_file);
66✔
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,365✔
2263
        /* choose a random level, and colors. */
2264
        sir_textcolor fg = SIRTC_DEFAULT;
64,289✔
2265
        sir_textcolor bg = SIRTC_DEFAULT;
64,289✔
2266

2267
        if (n % 2 == 0) {
76,289✔
2268
            fg = SIRTC_CYAN;
32,145✔
2269
            bg = SIRTC_BLACK;
32,145✔
2270
            sir_debug("log message #%zu", n);
38,145✔
2271
        } else {
2272
            fg = SIRTC_BLACK;
32,144✔
2273
            bg = SIRTC_CYAN;
32,144✔
2274
            sir_info("log message #%zu", n);
38,144✔
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,289✔
2280
        if (getrand_bool(rnd > 1U ? rnd : 1U)) {
76,289✔
2281
            my_args->pass = print_test_error(sir_remfile(id), false);
38,390✔
2282
            my_args->pass = print_test_error(0 != sir_addfile(my_args->log_file,
38,390✔
2283
                SIRL_ALL, SIRO_MSGONLY), false);
2284

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

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

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

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

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

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

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

2311
#if !defined(__WIN__)
2312
    return NULL;
78✔
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_andeql(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) {
269,017✔
2331
    char message[SIR_MAXERROR] = {0};
269,017✔
2332
    uint16_t code              = sir_geterror(message);
269,017✔
2333

2334
    if (!expected && !result && SIR_E_NOERROR != code)
268,999✔
2335
        printf("\t" RED("!! Unexpected (%"PRIu16", %s)") "\n", code, message);
683✔
2336
    else if (expected && SIR_E_NOERROR != code)
268,316✔
2337
        printf("\t" GREEN("Expected (%"PRIu16", %s)") "\n", code, message);
434✔
2338

2339
    return result;
269,000✔
2340
}
2341

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

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

2359
uint32_t getrand(uint32_t upper_bound) {
119,826✔
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);
119,826✔
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,196✔
2381
    bool removed = false;
1,043✔
2382

2383
    /* return true if leave_logs is true. */
2384
    if (leave_logs) {
1,196✔
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,145✔
2393
        if (ENOENT == errno)
599✔
2394
            return true;
485✔
2395

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

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

2407
    return removed;
459✔
2408
}
2409

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

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

2420
void countfiles(const char* search, const char* path, const char* filename, unsigned* data) {
110✔
2421
    SIR_UNUSED(path);
2422
    if (strstr(filename, search))
110✔
2423
        (*data)++;
30✔
2424
}
110✔
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) {
415✔
2440
        cb(search, path, di->d_name, data);
354✔
2441
        di = readdir(d);
354✔
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,029✔
2469
    sir_time now;
2470
    return _sir_msec_since(timer, &now);
2,000,029✔
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) {
48✔
2494
    if (0U == msec)
48✔
2495
        return;
×
2496

2497
#if !defined(__WIN__)
2498
    struct timespec ts = { msec / 1000, (msec % 1000) * 1000000 };
48✔
2499
    (void)nanosleep(&ts, NULL);
48✔
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) {
78✔
2506
    if (!f || !buf || 0 == size)
78✔
2507
        return 0;
×
2508

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

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

2519
    return (0 == ferror(f)) ? idx : 0;
78✔
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