├── .gitignore ├── README.md ├── tests ├── picotest.h ├── picotest_logger.h ├── z_filesystem.c └── z_io.c ├── z_filesystem.h └── z_io.h /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /.vs 3 | /build 4 | /a.out 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Z Headers 2 | ========= 3 | 4 | Public domain single-file libraries - stb style. 5 | 6 | ## Libraries 7 | 8 | Name | Description 9 | -------------- | -------------------- 10 | z_filesystem.h | Filesystem functions 11 | z_io.h | I/O library for reading/writing files or memory 12 | -------------------------------------------------------------------------------- /tests/picotest.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file picotest.h 3 | * 4 | * This file defines a minimalist unit testing framework for C programs. 5 | * 6 | * The assertion mechanism relies on `setjmp()` / `longjmp()`. While these 7 | * functions are discouraged for production code, their usage is acceptable in 8 | * the context of unit testing: in our case, `longjmp()` is only called when an 9 | * assertion fails, a situation where the actual process state is no longer 10 | * reliable anyway. Moreover, they constitute the only standard exception 11 | * handling mechanism for plain C code. 12 | * 13 | * @par License 14 | * 15 | * PicoTest is released under the terms of the The 3-Clause BSD License: 16 | * 17 | * https://opensource.org/licenses/BSD-3-Clause 18 | * 19 | * Copyright (c) 2018 Frederic Bonnet 20 | * 21 | * Redistribution and use in source and binary forms, with or without 22 | * modification, are permitted provided that the following conditions are met: 23 | * 24 | * 1. Redistributions of source code must retain the above copyright notice, 25 | * this list of conditions and the following disclaimer. 26 | * 27 | * 2. Redistributions in binary form must reproduce the above copyright notice, 28 | * this list of conditions and the following disclaimer in the documentation 29 | * and/or other materials provided with the distribution. 30 | * 31 | * 3. Neither the name of the copyright holder nor the names of its contributors 32 | * may be used to endorse or promote products derived from this software 33 | * without specific prior written permission. 34 | * 35 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 36 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 39 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 40 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 41 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 42 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 43 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 45 | * POSSIBILITY OF SUCH DAMAGE. 46 | */ 47 | 48 | #ifndef _PICOTEST 49 | #define _PICOTEST 50 | 51 | #include 52 | #include 53 | #include 54 | 55 | #if defined(_MSC_VER) 56 | /** \internal 57 | * MSVC is buggy wrt. (__VA_ARGS__) syntax. The workaround involves the use of a 58 | * dummy macro before the parentheses. See the following for an alternate 59 | * solution: 60 | * http://www.gamedev.net/community/forums/topic.asp?topic_id=567686 61 | */ 62 | #define _PICOTEST_PARENS 63 | #endif /* defined(_MSC_VER) */ 64 | 65 | /*! 66 | * \defgroup public_interface Public interface 67 | * \{ 68 | */ 69 | 70 | /*! 71 | * \name Version 72 | * PicoTest follows the Semantic Versioning Specification (SemVer) 2.0.0: 73 | * 74 | * https://semver.org/spec/v2.0.0.html 75 | * \{ 76 | */ 77 | 78 | #define PICOTEST_VERSION "1.4.1" 79 | #define PICOTEST_VERSION_MAJOR 1 80 | #define PICOTEST_VERSION_MINOR 4 81 | #define PICOTEST_VERSION_PATCH 1 82 | 83 | /*! \} End of Version */ 84 | 85 | /*! 86 | * \name Test Functions 87 | * \{ 88 | */ 89 | 90 | /** 91 | * Signature of test functions. 92 | * 93 | * Both @ref test_suites and @ref test_cases follow this signature. 94 | * 95 | * @param cond Test filtering condition, or **NULL**. In the former case, 96 | * passed to the active test filter before running the test. 97 | * 98 | * @return Number of failed tests. 99 | * 100 | * @see PICOTEST_SUITE 101 | * @see PICOTEST_CASE 102 | * @see PICOTEST_FILTER 103 | */ 104 | typedef int(PicoTestProc)(const char *cond); 105 | 106 | /** 107 | * Test metadata. 108 | */ 109 | typedef struct PicoTestMetadata { 110 | const char *name; /*!< Test name. */ 111 | const char *file; /*!< Test file location. */ 112 | int line; /*!< Test line location. */ 113 | PicoTestProc *const test; /*!< Test function. */ 114 | int nbSubtests; /*!< Number of subtests. */ 115 | const struct PicoTestMetadata * 116 | *subtests; /*!< Subtests (NULL-terminated array for test suites, NULL 117 | pointer for test cases). */ 118 | } PicoTestMetadata; 119 | 120 | /** 121 | * Declare an extern test for metadata access. 122 | * 123 | * @param _testName Test name. 124 | * 125 | * @see PICOTEST_METADATA 126 | */ 127 | #define PICOTEST_EXTERN(_testName) \ 128 | extern PicoTestProc _testName; \ 129 | extern PicoTestMetadata _PICOTEST_METADATA(_testName); 130 | 131 | /** 132 | * Get test metadata. 133 | * 134 | * @note Tests in other modules need to be declared first with PICOTEST_EXTERN. 135 | * 136 | * @param _testName Test name. 137 | * 138 | * @see PicoTestMetadata 139 | * @see PICOTEST_EXTERN 140 | */ 141 | #define PICOTEST_METADATA(_testName) &_PICOTEST_METADATA(_testName) 142 | 143 | /*! \cond IGNORE */ 144 | #define _PICOTEST_METADATA(_testName) _testName##_metadata 145 | #define _PICOTEST_TEST_DECLARE(_testName, _nbSubtests, _subtests) \ 146 | PicoTestProc _testName; \ 147 | PicoTestMetadata _PICOTEST_METADATA(_testName) = { \ 148 | _PICOTEST_STRINGIZE(_testName), \ 149 | __FILE__, \ 150 | __LINE__, \ 151 | _testName, \ 152 | _nbSubtests, \ 153 | (const struct PicoTestMetadata **)_subtests}; 154 | /*! \endcond */ 155 | 156 | /*! \} End of Test Functions */ 157 | 158 | /*! 159 | * \name Test Filters 160 | * 161 | * PicoTest provides a way for client code to select tests to be run using 162 | * custom filter functions. 163 | * \{ 164 | */ 165 | 166 | /** 167 | * Result of test filter functions. 168 | * 169 | * @par Examples 170 | * @example_file{filter.c} 171 | * @example_file{tags.c} 172 | * 173 | * @see PicoTestFilterProc 174 | */ 175 | typedef enum PicoTestFilterResult { 176 | /** Test does not match the condition, skip this test and all its 177 | * subtests. */ 178 | PICOTEST_FILTER_SKIP = 0, 179 | 180 | /** Test matches the condition, run this test and all its subtests. */ 181 | PICOTEST_FILTER_PASS = 1, 182 | 183 | /** Test does not match the condition, skip this test but filter its 184 | * subtests. */ 185 | PICOTEST_FILTER_SKIP_PROPAGATE = 2, 186 | 187 | /** Test matches the condition, run this test but filter its subtests. */ 188 | PICOTEST_FILTER_PASS_PROPAGATE = 3, 189 | } PicoTestFilterResult; 190 | 191 | /** 192 | * Signature of test filter functions. 193 | * 194 | * A test called with a non- **NULL** condition must match this condition to be 195 | * run. The test filter is set using the @ref PICOTEST_FILTER macro. 196 | * 197 | * @param test Test function to filter. 198 | * @param testName Name of test to filter. 199 | * @param cond Test filtering condition. 200 | * 201 | * @return a @ref PicoTestFilterResult value 202 | * 203 | * @par Usage 204 | * @snippet filter.c PICOTEST_FILTER example 205 | * 206 | * @par Examples 207 | * @example_file{filter.c} 208 | * @example_file{tags.c} 209 | * 210 | * @see PICOTEST_SUITE 211 | * @see PICOTEST_CASE 212 | * @see PICOTEST_FILTER 213 | * @see PicoTestFilterResult 214 | */ 215 | typedef PicoTestFilterResult(PicoTestFilterProc)(PicoTestProc *test, 216 | const char *testName, 217 | const char *cond); 218 | 219 | /** \internal 220 | * Implementation of default test filter function. 221 | * 222 | * Does a simple string equality test between **testName** and **cond**, and 223 | * propagates to subtests if it doesn't match. 224 | * 225 | * @see PicoTestFailureLoggerProc 226 | * @see PICOTEST_FAILURE_LOGGER 227 | * @see PICOTEST_FAILURE_LOGGER_DEFAULT 228 | */ 229 | static PicoTestFilterResult _picoTest_filterByName(PicoTestProc *test, 230 | const char *testName, 231 | const char *cond) { 232 | return (strcmp(testName, cond) == 0 ? PICOTEST_FILTER_PASS 233 | : PICOTEST_FILTER_SKIP_PROPAGATE); 234 | } 235 | 236 | /** 237 | * Default test filter function. 238 | * 239 | * Does a simple string equality test between **testName** and **cond**, and 240 | * propagates to subtests if it doesn't match. 241 | * 242 | * @see PicoTestFilterProc 243 | * @see PICOTEST_FILTER 244 | */ 245 | #define PICOTEST_FILTER_DEFAULT _picoTest_filterByName 246 | 247 | /** 248 | * Define the test filter function. 249 | * 250 | * Called before calling a test with a non- **NULL** condition. 251 | * 252 | * The default filter does a simple string equality test between its 253 | * **testName** and **cond** arguments, and propagates to subtests if it 254 | * doesn't match. Redefine this macro to use a custom filter function, which 255 | * must follow the @ref PicoTestFilterProc signature. 256 | * 257 | * @note Custom functions only apply to the tests defined after the macro 258 | * redefinition. As macros can be redefined several times, this means that 259 | * different functions may apply for the same source. 260 | * 261 | * @par Usage 262 | * @snippet filter.c PICOTEST_FILTER example 263 | * 264 | * @par Examples 265 | * @example_file{filter.c} 266 | * @example_file{tags.c} 267 | * 268 | * @see PicoTestFilterProc 269 | * @see PICOTEST_FILTER_DEFAULT 270 | */ 271 | #define PICOTEST_FILTER PICOTEST_FILTER_DEFAULT 272 | 273 | /*! \} End of Test Filters */ 274 | 275 | /*! 276 | * \name Test hierarchy traversal 277 | * 278 | * Tests can form hierarchies of test suites and test cases. PicoTest provides 279 | * two ways to traverse such hierarchies with a simple visitor pattern. This can 280 | * be used for e.g. test list discovery in build systems. 281 | * \{ 282 | */ 283 | 284 | /** 285 | * Function signature of test traversal proc. 286 | * 287 | * @param name Name of traversed test. 288 | * @param nb Number of subtests (zero for simple test cases, at least one 289 | * for test suites). 290 | * 291 | * @par Usage 292 | * @snippet traverse.c PicoTestTraverseProc example 293 | * 294 | * @par Examples 295 | * @example_file{traverse.c} 296 | * 297 | * @see PICOTEST_TRAVERSE 298 | */ 299 | typedef void(PicoTestTraverseProc)(const char *name, int nb); 300 | 301 | /** 302 | * Traverse a test hierarchy depth-first. 303 | * 304 | * This feature covers simple use cases such as getting the flat list of all 305 | * test names. For more advanced usage, see #PICOTEST_VISIT. 306 | * 307 | * @param _testName Name of the traversed test. 308 | * @param _proc Test traversal proc. Must follow the @ref 309 | * PicoTestTraverseProc signature. 310 | * 311 | * @par Examples 312 | * @example_file{traverse.c} 313 | * 314 | * @see PicoTestTraverseProc 315 | * @see PICOTEST_VISIT 316 | */ 317 | #define PICOTEST_TRAVERSE(_testName, _proc) \ 318 | _picoTest_traverse(PICOTEST_METADATA(_testName), _proc) 319 | 320 | /** \internal 321 | * Perform test traversal. 322 | * 323 | * @param metadata Metadata of test to traverse. 324 | * @param proc Test traversal proc. 325 | * 326 | * @see PicoTestTraverseProc 327 | * @see PicoTestMetadata 328 | * @see PICOTEST_TRAVERSE 329 | */ 330 | static void _picoTest_traverse(const PicoTestMetadata *metadata, 331 | PicoTestTraverseProc *proc) { 332 | const PicoTestMetadata **subtest = metadata->subtests; 333 | proc(metadata->name, metadata->nbSubtests); 334 | if (metadata->nbSubtests) { 335 | for (; *subtest; subtest++) { 336 | _picoTest_traverse(*subtest, proc); 337 | } 338 | } 339 | } 340 | 341 | /** 342 | * Test visit step. 343 | * 344 | * @see PicoTestVisitProc 345 | * @see PICOTEST_VISIT 346 | */ 347 | typedef enum PicoTestVisitStep { 348 | /** Enter the test. */ 349 | PICOTEST_VISIT_ENTER = 0, 350 | 351 | /** Leave the test. */ 352 | PICOTEST_VISIT_LEAVE = 1, 353 | } PicoTestVisitStep; 354 | 355 | /** 356 | * Function signature of test visit proc. 357 | * 358 | * Proc is called once for each value of #PicoTestVisitStep. 359 | * 360 | * @param metadata Metadata of the visited test. 361 | * @param step Visit step. 362 | * 363 | * @see PICOTEST_VISIT 364 | * @see PicoTestVisitStep 365 | */ 366 | typedef void(PicoTestVisitProc)(const PicoTestMetadata *metadata, 367 | PicoTestVisitStep step); 368 | 369 | /** 370 | * Visit a test hierarchy depth-first. 371 | * 372 | * This feature covers more advanced use cases than #PICOTEST_TRAVERSE, such as 373 | * exporting the test hierarchy as a structured format such as XML or JSON, 374 | * or accessing test metadata. 375 | * 376 | * @param _testName Name of the visited test. 377 | * @param _proc Test visit proc. Must follow the @ref 378 | * PicoTestVisitProc signature. 379 | * 380 | * @see PicoTestVisitProc 381 | * @see PICOTEST_TRAVERSE 382 | */ 383 | #define PICOTEST_VISIT(_testName, _proc) \ 384 | _picoTest_visit(PICOTEST_METADATA(_testName), _proc) 385 | 386 | /** \internal 387 | * Perform test visit. 388 | * 389 | * @param metadata Metadata of test to visit. 390 | * @param proc Test visit proc. 391 | * 392 | * @see PicoTestVisitProc 393 | * @see PicoTestMetadata 394 | * @see PICOTEST_VISIT 395 | */ 396 | static void _picoTest_visit(const PicoTestMetadata *metadata, 397 | PicoTestVisitProc *proc) { 398 | const PicoTestMetadata **subtest = metadata->subtests; 399 | proc(metadata, PICOTEST_VISIT_ENTER); 400 | if (metadata->nbSubtests) { 401 | for (; *subtest; subtest++) { 402 | _picoTest_visit(*subtest, proc); 403 | } 404 | } 405 | proc(metadata, PICOTEST_VISIT_LEAVE); 406 | } 407 | 408 | /*! \} End of Test Traversal */ 409 | 410 | /*! 411 | * \name Logging 412 | * 413 | * PicoTest provides a way for client code to intercept test failure events. 414 | * This can be used for e.g. logging purpose or reporting. 415 | * \{ 416 | */ 417 | 418 | /** 419 | * Function signature of test failure log handlers. 420 | * 421 | * @param file File name where the test was defined. 422 | * @param line Location of test in file. 423 | * @param type Type of test that failed (e.g. ``"ASSERT"``). 424 | * @param test Tested expression. 425 | * @param msg Optional message format string, or **NULL**. 426 | * @param args Optional message string parameter list, or **NULL**. 427 | * 428 | * @note **msg** and **args** are suitable arguments to **vprintf()**. 429 | * 430 | * @par Usage 431 | * @snippet logger.c PICOTEST_FAILURE_LOGGER example 432 | * 433 | * @par Examples 434 | * @example_file{logger.c} 435 | * 436 | * @see PICOTEST_FAILURE_LOGGER 437 | */ 438 | typedef void(PicoTestFailureLoggerProc)(const char *file, int line, 439 | const char *type, const char *test, 440 | const char *msg, va_list args); 441 | 442 | /** \internal 443 | * Implementation of default test failure log handler. Does nothing. 444 | * 445 | * @see PicoTestFailureLoggerProc 446 | * @see PICOTEST_FAILURE_LOGGER 447 | * @see PICOTEST_FAILURE_LOGGER_DEFAULT 448 | */ 449 | static void _picoTest_logFailure(const char *file, int line, const char *type, 450 | const char *test, const char *msg, 451 | va_list args) {} 452 | 453 | /** 454 | * Default test failure log handler. Does nothing. 455 | * 456 | * @see PicoTestFailureLoggerProc 457 | * @see PICOTEST_FAILURE_LOGGER 458 | */ 459 | #define PICOTEST_FAILURE_LOGGER_DEFAULT _picoTest_logFailure 460 | 461 | /** 462 | * Define the test failure log handler. Called when a test fails. 463 | * 464 | * The default handler does nothing. Redefine this macro to use a custom 465 | * handler, which must follow the @ref PicoTestFailureLoggerProc signature. 466 | * 467 | * @note Custom functions only apply to the tests defined after the macro 468 | * redefinition. As macros can be redefined several times, this means that 469 | * different functions may apply for the same source. 470 | * 471 | * @par Usage 472 | * @snippet logger.c PICOTEST_FAILURE_LOGGER example 473 | * 474 | * @par Examples 475 | * @example_file{logger.c} 476 | * 477 | * @see PicoTestFailureLoggerProc 478 | * @see PICOTEST_FAILURE_LOGGER_DEFAULT 479 | */ 480 | #define PICOTEST_FAILURE_LOGGER PICOTEST_FAILURE_LOGGER_DEFAULT 481 | 482 | /*! \} End of Logging */ 483 | 484 | /*! 485 | * \defgroup test_cases Test Cases 486 | * 487 | * Test cases are the most elementary test functions. They are defined as simple 488 | * functions blocks with assertions that checks the validity of the outcome. 489 | * \{ 490 | */ 491 | 492 | /*! 493 | * \name Test Case Definitions 494 | * \{ 495 | */ 496 | 497 | /** 498 | * Test case declaration. 499 | * 500 | * This macro defines a @ref PicoTestProc of the given name that can be called 501 | * directly. 502 | * 503 | * @param _testName Name of the test case. 504 | * @param _fixtureName (optional) Name of the test fixture used by the test. 505 | * @param _context (optional) Fixture context structure defined using 506 | * PICOTEST_FIXTURE_CONTEXT(_fixtureName). 507 | * 508 | * @return Number of failed tests. 509 | * 510 | * @par Usage 511 | * @snippet mainSuite.inc PICOTEST_CASE examples 512 | * 513 | * @par Examples 514 | * @example_file{mainSuite.inc} 515 | * 516 | * @see PicoTestProc 517 | * @see PICOTEST_FIXTURE_CONTEXT 518 | */ 519 | #if defined(_PICOTEST_PARENS) 520 | #define PICOTEST_CASE(...) \ 521 | _PICOTEST_CONCATENATE(_PICOTEST_CASE_, _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 522 | _PICOTEST_PARENS(__VA_ARGS__) 523 | #else 524 | #define PICOTEST_CASE(...) \ 525 | _PICOTEST_CONCATENATE(_PICOTEST_CASE_, _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 526 | (__VA_ARGS__) 527 | #endif /* defined(_PICOTEST_PARENS) */ 528 | 529 | /*! \cond IGNORE */ 530 | #define _PICOTEST_CASE_DECLARE(_testName) \ 531 | _PICOTEST_TEST_DECLARE(_testName, 0, NULL); \ 532 | static int _testName##_testCaseRunner(void); \ 533 | int _testName(const char *cond) { \ 534 | int fail = 0; \ 535 | PicoTestFilterResult filterResult = \ 536 | (cond == NULL) \ 537 | ? PICOTEST_FILTER_PASS \ 538 | : PICOTEST_FILTER(_testName, _PICOTEST_STRINGIZE(_testName), \ 539 | cond); \ 540 | switch (filterResult) { \ 541 | case PICOTEST_FILTER_PASS: \ 542 | case PICOTEST_FILTER_PASS_PROPAGATE: \ 543 | fail += _testName##_testCaseRunner(); \ 544 | break; \ 545 | } \ 546 | return fail; \ 547 | } 548 | 549 | #define _PICOTEST_CASE_RUNNER_BEGIN(_testName) \ 550 | static int _testName##_testCaseRunner() { \ 551 | int abort; \ 552 | jmp_buf failureEnv; \ 553 | jmp_buf *oldEnv = _picoTest_failureEnv; \ 554 | int fail, oldFail = _picoTest_fail; \ 555 | _picoTest_failureEnv = &failureEnv; \ 556 | _picoTest_fail = 0; \ 557 | PICOTEST_CASE_ENTER(_PICOTEST_STRINGIZE(_testName)); \ 558 | abort = setjmp(failureEnv); 559 | 560 | #define _PICOTEST_CASE_RUNNER_END(_testName) \ 561 | fail = _picoTest_fail; \ 562 | PICOTEST_CASE_LEAVE(_PICOTEST_STRINGIZE(_testName), fail); \ 563 | _picoTest_failureEnv = oldEnv; \ 564 | _picoTest_fail = oldFail; \ 565 | return fail; \ 566 | } 567 | 568 | #define _PICOTEST_CASE_1(_testName) \ 569 | _PICOTEST_CASE_DECLARE(_testName) \ 570 | static void _testName##_testCase(void); \ 571 | _PICOTEST_CASE_RUNNER_BEGIN(_testName) \ 572 | if (!abort) { \ 573 | _testName##_testCase(); \ 574 | } \ 575 | _PICOTEST_CASE_RUNNER_END(_testName) \ 576 | static void _testName##_testCase(void) 577 | 578 | #define _PICOTEST_CASE_2(_testName, _fixtureName) \ 579 | _PICOTEST_CASE_DECLARE(_testName) \ 580 | static void _testName##_testCase(void); \ 581 | _PICOTEST_CASE_RUNNER_BEGIN(_testName) \ 582 | if (!abort) { \ 583 | _PICOTEST_FIXTURE_CALL_SETUP(_fixtureName, _testName, NULL); \ 584 | _testName##_testCase(); \ 585 | } \ 586 | _PICOTEST_FIXTURE_CALL_TEARDOWN(_fixtureName, _testName, NULL, \ 587 | _picoTest_fail); \ 588 | _PICOTEST_CASE_RUNNER_END(_testName) \ 589 | static void _testName##_testCase() 590 | 591 | #define _PICOTEST_CASE_3(_testName, _fixtureName, _context) \ 592 | _PICOTEST_CASE_DECLARE(_testName) \ 593 | static void _testName##_testCase(struct _fixtureName##_Context *); \ 594 | _PICOTEST_CASE_RUNNER_BEGIN(_testName) { \ 595 | struct _fixtureName##_Context context; \ 596 | if (!abort) { \ 597 | _PICOTEST_FIXTURE_CALL_SETUP(_fixtureName, _testName, &context); \ 598 | _testName##_testCase(&context); \ 599 | } \ 600 | _PICOTEST_FIXTURE_CALL_TEARDOWN(_fixtureName, _testName, &context, \ 601 | _picoTest_fail); \ 602 | } \ 603 | _PICOTEST_CASE_RUNNER_END(_testName) \ 604 | static void _testName##_testCase(struct _fixtureName##_Context *_context) 605 | /*! \endcond */ 606 | 607 | /*! \} End of Test Case Definitions */ 608 | 609 | /*! 610 | * \name Test Case Hooks 611 | * 612 | * PicoTest provides a way for client code to intercept test case events. This 613 | * can be used for e.g. logging purpose or reporting. 614 | * \{ 615 | */ 616 | 617 | /** 618 | * Function signature of test case enter hooks. 619 | * 620 | * Called before running the test case. 621 | * 622 | * @param testName Test case name. 623 | * 624 | * @par Usage 625 | * @snippet hooks.c PICOTEST_CASE_ENTER example 626 | * 627 | * @par Examples 628 | * @example_file{hooks.c} 629 | * 630 | * @see PICOTEST_CASE_ENTER 631 | */ 632 | typedef void(PicoTestCaseEnterProc)(const char *testName); 633 | 634 | /** 635 | * Default test case enter hook. Does nothing. 636 | * 637 | * @see PicoTestCaseEnterProc 638 | * @see PICOTEST_CASE_ENTER 639 | */ 640 | #define PICOTEST_CASE_ENTER_DEFAULT(testName) 641 | 642 | /** 643 | * Define the test case enter hook. 644 | * 645 | * The default hook does nothing. Redefine this macro to use a custom hook, 646 | * which must follow the @ref PicoTestCaseEnterProc signature. 647 | * 648 | * @note Custom functions only apply to the tests defined after the macro 649 | * redefinition. As macros can be redefined several times, this means that 650 | * different functions may apply for the same source. 651 | * 652 | * @par Usage 653 | * @snippet hooks.c PICOTEST_CASE_ENTER example 654 | * 655 | * @par Examples 656 | * @example_file{hooks.c} 657 | * 658 | * @see PicoTestCaseEnterProc 659 | * @see PICOTEST_CASE_ENTER_DEFAULT 660 | * @see PICOTEST_CASE_LEAVE 661 | */ 662 | #define PICOTEST_CASE_ENTER PICOTEST_CASE_ENTER_DEFAULT 663 | 664 | /** 665 | * Function signature of test case leave hooks. 666 | * 667 | * Called after running the test case. 668 | * 669 | * @param testName Test case name. 670 | * @param fail Failed tests (including its subtests if any). 671 | * 672 | * @par Usage 673 | * @snippet hooks.c PICOTEST_CASE_LEAVE example 674 | * 675 | * @par Examples 676 | * @example_file{hooks.c} 677 | * 678 | * @see PICOTEST_CASE_LEAVE 679 | */ 680 | typedef void(PicoTestCaseLeaveProc)(const char *testName, int fail); 681 | 682 | /** 683 | * Default test case enter hook. Does nothing. 684 | * 685 | * @see PicoTestCaseLeaveProc 686 | * @see PICOTEST_CASE_LEAVE 687 | */ 688 | #define PICOTEST_CASE_LEAVE_DEFAULT(testName, fail) 689 | 690 | /** 691 | * Define the test case leave hook. 692 | * 693 | * The default hook does nothing. Redefine this macro to use a custom hook, 694 | * which must follow the @ref PicoTestCaseLeaveProc signature. 695 | * 696 | * @note Custom functions only apply to the tests defined after the macro 697 | * redefinition. As macros can be redefined several times, this means that 698 | * different functions may apply for the same source. 699 | * 700 | * @par Usage 701 | * @snippet hooks.c PICOTEST_CASE_LEAVE example 702 | * 703 | * @par Examples 704 | * @example_file{hooks.c} 705 | * 706 | * @see PicoTestCaseLeaveProc 707 | * @see PICOTEST_CASE_LEAVE_DEFAULT 708 | * @see PICOTEST_CASE_ENTER 709 | */ 710 | #define PICOTEST_CASE_LEAVE PICOTEST_CASE_LEAVE_DEFAULT 711 | 712 | /*! \} End of Test Case Hooks */ 713 | 714 | /*! \} End of Test Cases */ 715 | 716 | /*! 717 | * \defgroup assertions Assertions 718 | * 719 | * Assertions are the basic building blocks of test cases. 720 | * \{ 721 | */ 722 | 723 | /*! 724 | * \name Assertion Definitions 725 | * \{ 726 | */ 727 | 728 | /** 729 | * Hard assertion. Logs an error if the given value is false, then stops the 730 | * test with PICOTEST_ABORT(). 731 | * 732 | * PICOTEST_FAILURE_LOGGER() is called with the **type** argument set to 733 | * ``"ASSERT"``. 734 | * 735 | * @param x Value to test. Evaluated once, so it can be an expression with 736 | * side effects. 737 | * @param msg (optional) Message format string. 738 | * @param ... (optional) Message string arguments. 739 | * 740 | * @note **msg** and following arguments arguments are suitable arguments to 741 | * **printf()**. 742 | * 743 | * @par Examples 744 | * @example_file{mainSuite.inc} 745 | * 746 | * @see PICOTEST_FAILURE_LOGGER 747 | * @see PICOTEST_ABORT 748 | * @see PICOTEST_VERIFY 749 | */ 750 | #define PICOTEST_ASSERT(x, /* msg, */...) \ 751 | { _PICOTEST_ASSERT(x, #x, ##__VA_ARGS__); } 752 | 753 | /*! \cond IGNORE */ 754 | #define _PICOTEST_ASSERT(x, ...) \ 755 | PICOTEST_ASSERT_BEFORE("ASSERT", #x); \ 756 | { \ 757 | int _PICOTEST_FAIL = !(x); \ 758 | PICOTEST_ASSERT_AFTER("ASSERT", #x, _PICOTEST_FAIL); \ 759 | if (_PICOTEST_FAIL) { \ 760 | PICOTEST_FAILURE("ASSERT", ##__VA_ARGS__); \ 761 | PICOTEST_ABORT(); \ 762 | } \ 763 | } 764 | /*! \endcond */ 765 | 766 | /** 767 | * Soft assertion. Logs an error if the given value is false, but let the test 768 | * continue. 769 | * 770 | * PICOTEST_FAILURE_LOGGER() is called with the **type** argument set to 771 | * ``"VERIFY"``. 772 | * 773 | * @param x Value to test. Evaluated once, so it can be an expression with 774 | * side effects. 775 | * @param msg (optional) Message format string. 776 | * @param ... (optional) Message string arguments. 777 | * 778 | * @note **msg** and following arguments arguments are suitable arguments to 779 | * **printf()**. 780 | * 781 | * @par Examples 782 | * @example_file{mainSuite.inc} 783 | * 784 | * @see PICOTEST_FAILURE_LOGGER 785 | * @see PICOTEST_ASSERT 786 | */ 787 | #define PICOTEST_VERIFY(x, /* msg, */...) \ 788 | { _PICOTEST_VERIFY(x, #x, ##__VA_ARGS__); } 789 | 790 | /*! \cond IGNORE */ 791 | #define _PICOTEST_VERIFY(x, ...) \ 792 | PICOTEST_ASSERT_BEFORE("VERIFY", #x); \ 793 | { \ 794 | int _PICOTEST_FAIL = !(x); \ 795 | PICOTEST_ASSERT_AFTER("VERIFY", #x, _PICOTEST_FAIL); \ 796 | if (_PICOTEST_FAIL) { \ 797 | PICOTEST_FAILURE("VERIFY", ##__VA_ARGS__); \ 798 | } \ 799 | } 800 | /*! \endcond */ 801 | 802 | /** 803 | * Generic failure. 804 | * 805 | * PICOTEST_FAILURE_LOGGER() is called with the provided **type**, **test** and 806 | * **msg** arguments. 807 | * 808 | * This can be used to implement custom testing logic. 809 | * 810 | * @param type Type of test that failed (e.g. "ASSERT"). 811 | * @param test Failed test. 812 | * @param msg (optional) Message format string. 813 | * @param ... (optional) Message string arguments. 814 | */ 815 | #define PICOTEST_FAILURE(type, test, /* msg, */...) \ 816 | _PICOTEST_FAILURE(type, test, ##__VA_ARGS__) 817 | 818 | /*! \cond IGNORE */ 819 | #define _PICOTEST_FAILURE(type, ...) \ 820 | _picoTest_fail++; \ 821 | _picoTest_assertFailed(PICOTEST_FAILURE_LOGGER, __FILE__, __LINE__, type, \ 822 | _PICOTEST_ARGCOUNT(__VA_ARGS__), __VA_ARGS__); \ 823 | /*! \endcond */ 824 | 825 | /** \internal 826 | * Internal failure counter. 827 | * 828 | * @see PICOTEST_FAILURE 829 | */ 830 | static int _picoTest_fail = 0; 831 | 832 | /** \internal 833 | * Tag used by **setjmp()** and **longjmp()** to jump out of failed tests. 834 | * 835 | * @see PICOTEST_ABORT 836 | * @see PICOTEST_CASE 837 | */ 838 | static jmp_buf *_picoTest_failureEnv = NULL; 839 | 840 | /** 841 | * Abort a test case. 842 | * 843 | * This can be used to implement custom testing logic. 844 | * 845 | * @see PICOTEST_CASE 846 | */ 847 | #define PICOTEST_ABORT() longjmp(*_picoTest_failureEnv, 1) 848 | 849 | /** \internal 850 | * Called when an assertion fails. 851 | * 852 | * @param proc Test failure log handler. 853 | * @param file File name where the test was defined. 854 | * @param line Location of test in file. 855 | * @param type Type of test that failed (e.g. "ASSERT"). 856 | * @param count Number of arguments after **test**. 857 | * @param test Tested expression. 858 | * @param ... Optional message format string and parameters. 859 | * 860 | * @see PICOTEST_ASSERT 861 | * @see PICOTEST_VERIFY 862 | */ 863 | static void _picoTest_assertFailed(PicoTestFailureLoggerProc *proc, 864 | const char *file, int line, const char *type, 865 | int count, const char *test, ...) { 866 | if (count > 1) { 867 | /* Extra args after **test** */ 868 | va_list args; 869 | const char *msg; 870 | va_start(args, test); 871 | msg = va_arg(args, const char *); 872 | proc(file, line, type, test, msg, args); 873 | } else { 874 | proc(file, line, type, test, NULL, NULL); 875 | } 876 | } 877 | 878 | /*! \} End of Assertion Definitions */ 879 | 880 | /*! 881 | * \name Assertion Hooks 882 | * 883 | * PicoTest provides a way for client code to intercept assertions events. This 884 | * can be used for e.g. logging purpose or reporting. 885 | * \{ 886 | */ 887 | 888 | /** 889 | * Function signature of assert before hooks. 890 | * 891 | * Called before running an assertion. 892 | * 893 | * @param type Type of test (e.g. "ASSERT"). 894 | * @param test Test. 895 | * 896 | * @par Usage 897 | * @snippet hooks.c PICOTEST_ASSERT_BEFORE example 898 | * 899 | * @par Examples 900 | * @example_file{hooks.c} 901 | * 902 | * @see PICOTEST_ASSERT_BEFORE 903 | */ 904 | typedef void(PicoTestAssertBeforeProc)(const char *type, const char *test); 905 | 906 | /** 907 | * Default assert before hook. Does nothing. 908 | * 909 | * @see PicoTestAssertBeforeProc 910 | * @see PICOTEST_ASSERT_BEFORE 911 | */ 912 | #define PICOTEST_ASSERT_BEFORE_DEFAULT(type, test) 913 | 914 | /** 915 | * Define the assert before hook. 916 | * 917 | * The default hook does nothing. Redefine this macro to use a custom hook, 918 | * which must follow the @ref PicoTestAssertBeforeProc signature. 919 | * 920 | * @note Custom functions only apply to the tests defined after the macro 921 | * redefinition. As macros can be redefined several times, this means that 922 | * different functions may apply for the same source. 923 | * 924 | * @par Usage 925 | * @snippet hooks.c PICOTEST_ASSERT_BEFORE example 926 | * 927 | * @par Examples 928 | * @example_file{hooks.c} 929 | * 930 | * @see PicoTestAssertBeforeProc 931 | * @see PICOTEST_ASSERT_BEFORE_DEFAULT 932 | * @see PICOTEST_ASSERT_AFTER 933 | */ 934 | #define PICOTEST_ASSERT_BEFORE PICOTEST_ASSERT_BEFORE_DEFAULT 935 | 936 | /** 937 | * Function signature of assert after hooks. 938 | * 939 | * Called after running an assertion. 940 | * 941 | * @param type Type of test (e.g. "ASSERT"). 942 | * @param test Test. 943 | * @param fail Test result: zero for success, non-zero for failure. 944 | * 945 | * @par Usage 946 | * @snippet hooks.c PICOTEST_ASSERT_AFTER example 947 | * 948 | * @par Examples 949 | * @example_file{hooks.c} 950 | * 951 | * @see PICOTEST_ASSERT_AFTER 952 | */ 953 | typedef void(PicoTestAssertAfterProc)(const char *type, const char *test, 954 | int fail); 955 | 956 | /** 957 | * Default assert after hook. Does nothing. 958 | * 959 | * @see PicoTestAssertAfterProc 960 | * @see PICOTEST_ASSERT_AFTER 961 | */ 962 | #define PICOTEST_ASSERT_AFTER_DEFAULT(type, test, fail) 963 | 964 | /** 965 | * Define the assert after hook. 966 | * 967 | * The default hook does nothing. Redefine this macro to use a custom hook, 968 | * which must follow the @ref PicoTestAssertAfterProc signature. 969 | * 970 | * @note Custom functions only apply to the tests defined after the macro 971 | * redefinition. As macros can be redefined several times, this means that 972 | * different functions may apply for the same source. 973 | * 974 | * @par Usage 975 | * @snippet hooks.c PICOTEST_ASSERT_AFTER example 976 | * 977 | * @par Examples 978 | * @example_file{hooks.c} 979 | * 980 | * @see PicoTestAssertAfterProc 981 | * @see PICOTEST_ASSERT_AFTER_DEFAULT 982 | * @see PICOTEST_ASSERT_BEFORE 983 | */ 984 | #define PICOTEST_ASSERT_AFTER PICOTEST_ASSERT_AFTER_DEFAULT 985 | 986 | /*! \} End of Assertion Hooks */ 987 | 988 | /*! \} End of Assertions */ 989 | 990 | /*! 991 | * \defgroup fixtures Test Fixtures 992 | * 993 | * Test fixtures are used to establish the context needed to run test cases. 994 | * 995 | * A test fixture can be used by several, possibly unrelated test cases. 996 | * \{ 997 | */ 998 | 999 | /*! 1000 | * \name Test Fixture Definitions 1001 | * \{ 1002 | */ 1003 | 1004 | /** 1005 | * Test fixture context declaration. 1006 | * 1007 | * Fixtures can optionally define a context structure that is passed to its 1008 | * setup and teardown functions. 1009 | * 1010 | * @param _fixtureName Name of the fixture. 1011 | * 1012 | * @par Usage 1013 | * @snippet mainSuite.inc PICOTEST_FIXTURE_CONTEXT example 1014 | * 1015 | * @par Examples 1016 | * @example_file{mainSuite.inc} 1017 | * @example_file{fixtures.c} 1018 | * 1019 | * @see PICOTEST_FIXTURE_SETUP 1020 | * @see PICOTEST_FIXTURE_TEARDOWN 1021 | * @see PICOTEST_CASE 1022 | */ 1023 | #define PICOTEST_FIXTURE_CONTEXT(_fixtureName) struct _fixtureName##_Context 1024 | 1025 | /** 1026 | * Test fixture initialization. 1027 | * 1028 | * @param _fixtureName Name of the fixture. 1029 | * @param _context (optional) Fixture context structure defined using 1030 | * PICOTEST_FIXTURE_CONTEXT(_fixtureName). 1031 | * 1032 | * @par Usage 1033 | * A simple fixture with no context: 1034 | * @snippet mainSuite.inc Simple fixture 1035 | * 1036 | * A more complex example with a context structure: 1037 | * @snippet mainSuite.inc Fixture with context 1038 | * 1039 | * Fixtures may define an optional context that test cases don't need, in this 1040 | * case the context passed to the setup and teardown functions is **NULL**: 1041 | * @snippet mainSuite.inc Fixture with optional context 1042 | * Here is an example of such a test case: 1043 | * @snippet mainSuite.inc PICOTEST_CASE with fixture and optional context 1044 | * 1045 | * @par Examples 1046 | * @example_file{mainSuite.inc} 1047 | * @example_file{fixtures.c} 1048 | * 1049 | * @see PICOTEST_FIXTURE_CONTEXT 1050 | * @see PICOTEST_FIXTURE_TEARDOWN 1051 | * @see PICOTEST_CASE 1052 | */ 1053 | #if defined(_PICOTEST_PARENS) 1054 | #define PICOTEST_FIXTURE_SETUP(...) \ 1055 | _PICOTEST_CONCATENATE(_PICOTEST_FIXTURE_SETUP_, \ 1056 | _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 1057 | _PICOTEST_PARENS(__VA_ARGS__) 1058 | #else 1059 | #define PICOTEST_FIXTURE_SETUP(...) \ 1060 | _PICOTEST_CONCATENATE(_PICOTEST_FIXTURE_SETUP_, \ 1061 | _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 1062 | (__VA_ARGS__) 1063 | #endif /* defined(_PICOTEST_PARENS) */ 1064 | 1065 | /*! \cond IGNORE */ 1066 | #define _PICOTEST_FIXTURE_SETUP_1(_fixtureName) \ 1067 | static void _fixtureName##_setup(void *_fixtureName##_DUMMY) 1068 | 1069 | #define _PICOTEST_FIXTURE_SETUP_2(_fixtureName, _context) \ 1070 | static void _fixtureName##_setup(struct _fixtureName##_Context *_context) 1071 | 1072 | #define _PICOTEST_FIXTURE_CALL_SETUP(_fixtureName, _testName, context) \ 1073 | PICOTEST_FIXTURE_BEFORE_SETUP(_PICOTEST_STRINGIZE(_fixtureName), \ 1074 | _PICOTEST_STRINGIZE(_testName)); \ 1075 | _fixtureName##_setup(context); \ 1076 | PICOTEST_FIXTURE_AFTER_SETUP(_PICOTEST_STRINGIZE(_fixtureName), \ 1077 | _PICOTEST_STRINGIZE(_testName)); 1078 | /*! \endcond */ 1079 | 1080 | /** 1081 | * Test fixture cleanup. 1082 | * 1083 | * @param _fixtureName Name of the fixture. 1084 | * @param _context (optional) Fixture context structure defined using 1085 | * PICOTEST_FIXTURE_CONTEXT(_fixtureName). 1086 | * 1087 | * @par Usage 1088 | * A simple fixture with no context: 1089 | * @snippet mainSuite.inc Simple fixture 1090 | * 1091 | * A more complex example with a context structure: 1092 | * @snippet mainSuite.inc Fixture with context 1093 | * 1094 | * Fixtures may define an optional context that test cases don't need, in this 1095 | * case the context passed to the setup and teardown functions is **NULL**: 1096 | * @snippet mainSuite.inc Fixture with optional context 1097 | * Here is an example of such a test case: 1098 | * @snippet mainSuite.inc PICOTEST_CASE with fixture and optional context 1099 | * 1100 | * @par Examples 1101 | * @example_file{mainSuite.inc} 1102 | * @example_file{fixtures.c} 1103 | * 1104 | * @see PICOTEST_FIXTURE_CONTEXT 1105 | * @see PICOTEST_FIXTURE_SETUP 1106 | * @see PICOTEST_CASE 1107 | */ 1108 | #if defined(_PICOTEST_PARENS) 1109 | #define PICOTEST_FIXTURE_TEARDOWN(...) \ 1110 | _PICOTEST_CONCATENATE(_PICOTEST_FIXTURE_TEARDOWN_, \ 1111 | _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 1112 | _PICOTEST_PARENS(__VA_ARGS__) 1113 | #else 1114 | #define PICOTEST_FIXTURE_TEARDOWN(...) \ 1115 | _PICOTEST_CONCATENATE(_PICOTEST_FIXTURE_TEARDOWN_, \ 1116 | _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 1117 | (__VA_ARGS__) 1118 | #endif /* defined(_PICOTEST_PARENS) */ 1119 | 1120 | /*! \cond IGNORE */ 1121 | #define _PICOTEST_FIXTURE_TEARDOWN_1(_fixtureName) \ 1122 | static void _fixtureName##_teardown(int PICOTEST_FAIL, \ 1123 | void *_fixtureName##_DUMMY) 1124 | 1125 | #define _PICOTEST_FIXTURE_TEARDOWN_2(_fixtureName, _context) \ 1126 | static void _fixtureName##_teardown( \ 1127 | int PICOTEST_FAIL, struct _fixtureName##_Context *_context) 1128 | 1129 | #define _PICOTEST_FIXTURE_CALL_TEARDOWN(_fixtureName, _testName, context, \ 1130 | fail) \ 1131 | PICOTEST_FIXTURE_BEFORE_TEARDOWN(_PICOTEST_STRINGIZE(_fixtureName), \ 1132 | _PICOTEST_STRINGIZE(_testName), fail); \ 1133 | _fixtureName##_teardown(fail, context); \ 1134 | PICOTEST_FIXTURE_AFTER_TEARDOWN(_PICOTEST_STRINGIZE(_fixtureName), \ 1135 | _PICOTEST_STRINGIZE(_testName), fail); \ 1136 | /*! \endcond */ 1137 | 1138 | /*! \} End of Test Fixture Definitions */ 1139 | 1140 | /*! 1141 | * \name Test Fixture Hooks 1142 | * 1143 | * PicoTest provides a way for client code to intercept test fixture events. 1144 | * This can be used for e.g. logging purpose or reporting. 1145 | * \{ 1146 | */ 1147 | 1148 | /** 1149 | * Function signature of test fixture before setup hooks. 1150 | * 1151 | * Called before running the test fixture setup. 1152 | * 1153 | * @param fixtureName Test fixture name. 1154 | * @param testName Test case name. 1155 | * 1156 | * @par Usage 1157 | * @snippet hooks.c PICOTEST_FIXTURE_BEFORE_SETUP example 1158 | * 1159 | * @par Examples 1160 | * @example_file{hooks.c} 1161 | * 1162 | * @see PICOTEST_FIXTURE_BEFORE_SETUP 1163 | */ 1164 | typedef void(PicoTestFixtureBeforeSetupProc)(const char *fixtureName, 1165 | const char *testName); 1166 | 1167 | /** 1168 | * Default test fixture before setup hook. Does nothing. 1169 | * 1170 | * @see PicoTestFixtureBeforeSetupProc 1171 | * @see PICOTEST_FIXTURE_BEFORE_SETUP 1172 | */ 1173 | #define PICOTEST_FIXTURE_BEFORE_SETUP_DEFAULT(fixtureName, testName) 1174 | 1175 | /** 1176 | * Define the test fixture before setup hook. 1177 | * 1178 | * The default hook does nothing. Redefine this macro to use a custom hook, 1179 | * which must follow the @ref PicoTestFixtureBeforeSetupProc signature. 1180 | * 1181 | * @note Custom functions only apply to the tests defined after the macro 1182 | * redefinition. As macros can be redefined several times, this means that 1183 | * different functions may apply for the same source. 1184 | * 1185 | * @par Usage 1186 | * @snippet hooks.c PICOTEST_FIXTURE_BEFORE_SETUP example 1187 | * 1188 | * @par Examples 1189 | * @example_file{hooks.c} 1190 | * 1191 | * @see PicoTestFixtureBeforeSetupProc 1192 | * @see PICOTEST_FIXTURE_BEFORE_SETUP_DEFAULT 1193 | * @see PICOTEST_FIXTURE_AFTER_SETUP 1194 | */ 1195 | #define PICOTEST_FIXTURE_BEFORE_SETUP PICOTEST_FIXTURE_BEFORE_SETUP_DEFAULT 1196 | 1197 | /** 1198 | * Function signature of test fixture after setup hooks. 1199 | * 1200 | * Called after running the test fixture setup. 1201 | * 1202 | * @param fixtureName Test fixture name. 1203 | * @param testName Test case name. 1204 | * 1205 | * @par Usage 1206 | * @snippet hooks.c PICOTEST_FIXTURE_AFTER_SETUP example 1207 | * 1208 | * @par Examples 1209 | * @example_file{hooks.c} 1210 | * 1211 | * @see PICOTEST_FIXTURE_AFTER_SETUP 1212 | */ 1213 | typedef void(PicoTestFixtureAfterSetupProc)(const char *fixtureName, 1214 | const char *testName); 1215 | 1216 | /** 1217 | * Default test fixture after setup hook. Does nothing. 1218 | * 1219 | * @see PicoTestFixtureAfterSetupProc 1220 | * @see PICOTEST_FIXTURE_AFTER_SETUP 1221 | */ 1222 | #define PICOTEST_FIXTURE_AFTER_SETUP_DEFAULT(fixtureName, testName) 1223 | 1224 | /** 1225 | * Define the test fixture after setup hook. 1226 | * 1227 | * The default hook does nothing. Redefine this macro to use a custom hook, 1228 | * which must follow the @ref PicoTestFixtureAfterSetupProc signature. 1229 | * 1230 | * @note Custom functions only apply to the tests defined after the macro 1231 | * redefinition. As macros can be redefined several times, this means that 1232 | * different functions may apply for the same source. 1233 | * 1234 | * @par Usage 1235 | * @snippet hooks.c PICOTEST_FIXTURE_AFTER_SETUP example 1236 | * 1237 | * @par Examples 1238 | * @example_file{hooks.c} 1239 | * 1240 | * @see PicoTestFixtureAfterSetupProc 1241 | * @see PICOTEST_FIXTURE_AFTER_SETUP_DEFAULT 1242 | * @see PICOTEST_FIXTURE_BEFORE_SETUP 1243 | */ 1244 | #define PICOTEST_FIXTURE_AFTER_SETUP PICOTEST_FIXTURE_AFTER_SETUP_DEFAULT 1245 | 1246 | /** 1247 | * Function signature of test fixture before teardown hooks. 1248 | * 1249 | * Called before running the test fixture teardown. 1250 | * 1251 | * @param fixtureName Test fixture name. 1252 | * @param testName Test case name. 1253 | * @param fail Failed tests (including its subtests if any). 1254 | * 1255 | * @par Usage 1256 | * @snippet hooks.c PICOTEST_FIXTURE_BEFORE_TEARDOWN example 1257 | * 1258 | * @par Examples 1259 | * @example_file{hooks.c} 1260 | * 1261 | * @see PICOTEST_FIXTURE_BEFORE_TEARDOWN 1262 | */ 1263 | typedef void(PicoTestFixtureBeforeTeardownProc)(const char *fixtureName, 1264 | const char *testName, int fail); 1265 | 1266 | /** 1267 | * Default test fixture before teardown hook. Does nothing. 1268 | * 1269 | * @see PicoTestFixtureBeforeTeardownProc 1270 | * @see PICOTEST_FIXTURE_BEFORE_TEARDOWN 1271 | */ 1272 | #define PICOTEST_FIXTURE_BEFORE_TEARDOWN_DEFAULT(fixtureName, testName, fail) 1273 | 1274 | /** 1275 | * Define the test fixture before teardown hook. 1276 | * 1277 | * The default hook does nothing. Redefine this macro to use a custom hook, 1278 | * which must follow the @ref PicoTestFixtureBeforeTeardownProc signature. 1279 | * 1280 | * @note Custom functions only apply to the tests defined after the macro 1281 | * redefinition. As macros can be redefined several times, this means that 1282 | * different functions may apply for the same source. 1283 | * 1284 | * @par Usage 1285 | * @snippet hooks.c PICOTEST_FIXTURE_BEFORE_TEARDOWN example 1286 | * 1287 | * @par Examples 1288 | * @example_file{hooks.c} 1289 | * 1290 | * @see PicoTestFixtureBeforeTeardownProc 1291 | * @see PICOTEST_FIXTURE_BEFORE_TEARDOWN_DEFAULT 1292 | * @see PICOTEST_FIXTURE_AFTER_TEARDOWN 1293 | */ 1294 | #define PICOTEST_FIXTURE_BEFORE_TEARDOWN \ 1295 | PICOTEST_FIXTURE_BEFORE_TEARDOWN_DEFAULT 1296 | 1297 | /** 1298 | * Function signature of test fixture after teardown hooks. 1299 | * 1300 | * Called after running the test fixture teardown. 1301 | * 1302 | * @param fixtureName Test fixture name. 1303 | * @param testName Test case name. 1304 | * @param fail Failed tests (including its subtests if any). 1305 | * 1306 | * @par Usage 1307 | * @snippet hooks.c PICOTEST_FIXTURE_AFTER_TEARDOWN example 1308 | * 1309 | * @par Examples 1310 | * @example_file{hooks.c} 1311 | * 1312 | * @see PICOTEST_FIXTURE_AFTER_TEARDOWN 1313 | */ 1314 | typedef void(PicoTestFixtureAfterTeardownProc)(const char *fixtureName, 1315 | const char *testName, int fail); 1316 | 1317 | /** 1318 | * Default test fixture after teardown hook. Does nothing. 1319 | * 1320 | * @see PicoTestFixtureAfterTeardownProc 1321 | * @see PICOTEST_FIXTURE_AFTER_TEARDOWN 1322 | */ 1323 | #define PICOTEST_FIXTURE_AFTER_TEARDOWN_DEFAULT(fixtureName, testName, fail) 1324 | 1325 | /** 1326 | * Define the test fixture after teardown hook. 1327 | * 1328 | * The default hook does nothing. Redefine this macro to use a custom hook, 1329 | * which must follow the @ref PicoTestFixtureAfterTeardownProc signature. 1330 | * 1331 | * @note Custom functions only apply to the tests defined after the macro 1332 | * redefinition. As macros can be redefined several times, this means that 1333 | * different functions may apply for the same source. 1334 | * 1335 | * @par Usage 1336 | * @snippet hooks.c PICOTEST_FIXTURE_AFTER_TEARDOWN example 1337 | * 1338 | * @par Examples 1339 | * @example_file{hooks.c} 1340 | * 1341 | * @see PicoTestFixtureAfterTeardownProc 1342 | * @see PICOTEST_FIXTURE_AFTER_TEARDOWN_DEFAULT 1343 | * @see PICOTEST_FIXTURE_BEFORE_TEARDOWN 1344 | */ 1345 | #define PICOTEST_FIXTURE_AFTER_TEARDOWN PICOTEST_FIXTURE_AFTER_TEARDOWN_DEFAULT 1346 | 1347 | /*! \} End of Test Fixture Hooks */ 1348 | 1349 | /*! \} End of Test Fixtures */ 1350 | 1351 | /*! 1352 | * \defgroup test_suites Test Suites 1353 | * 1354 | * A test suite is a set of subtests in no special order. These subtests can 1355 | * themselves be test suites or test cases. 1356 | * \{ 1357 | */ 1358 | 1359 | /*! 1360 | * \name Test Suite Definitions 1361 | * \{ 1362 | */ 1363 | 1364 | /** 1365 | * Test suite declaration. 1366 | * 1367 | * A test suite is a test function that is made of one or several subtests. 1368 | * 1369 | * This macro defines a @ref PicoTestProc of the given name that can be called 1370 | * directly. 1371 | * 1372 | * @param _suiteName Name of the test suite. 1373 | * @param ... Names of the subtests in the suite. 1374 | * 1375 | * @return Number of failed tests. 1376 | * 1377 | * @see PicoTestProc 1378 | * @see PICOTEST_CASE 1379 | * 1380 | * @par Usage 1381 | * @snippet mainSuite.inc PICOTEST_SUITE examples 1382 | * 1383 | * @par Examples 1384 | * @example_file{mainSuite.inc} 1385 | */ 1386 | #define PICOTEST_SUITE(_suiteName, ...) \ 1387 | _PICOTEST_FOR_EACH(PICOTEST_EXTERN, __VA_ARGS__) \ 1388 | static PicoTestMetadata *_suiteName##_subtests[] = {_PICOTEST_FOR_EACH( \ 1389 | _PICOTEST_SUITE_ENUMERATE_SUBTEST, __VA_ARGS__) NULL}; \ 1390 | _PICOTEST_TEST_DECLARE(_suiteName, _PICOTEST_ARGCOUNT(__VA_ARGS__), \ 1391 | _suiteName##_subtests); \ 1392 | static int _suiteName##_testSuiteRunner(const char *cond) { \ 1393 | const int nb = _PICOTEST_ARGCOUNT(__VA_ARGS__); \ 1394 | PicoTestMetadata **subtest = _suiteName##_subtests; \ 1395 | int fail = 0; \ 1396 | PICOTEST_SUITE_ENTER(_PICOTEST_STRINGIZE(_suiteName), nb); \ 1397 | for (; *subtest; subtest++) { \ 1398 | const int index = (int)(subtest - _suiteName##_subtests); \ 1399 | int sfail = 0; \ 1400 | PICOTEST_SUITE_BEFORE_SUBTEST(_PICOTEST_STRINGIZE(_suiteName), nb, \ 1401 | fail, index, (*subtest)->name); \ 1402 | sfail = (*subtest)->test(cond); \ 1403 | fail += sfail; \ 1404 | PICOTEST_SUITE_AFTER_SUBTEST(_PICOTEST_STRINGIZE(_suiteName), nb, \ 1405 | fail, index, (*subtest)->name, \ 1406 | sfail); \ 1407 | } \ 1408 | PICOTEST_SUITE_LEAVE(_PICOTEST_STRINGIZE(_suiteName), nb, fail); \ 1409 | return fail; \ 1410 | } \ 1411 | int _suiteName(const char *cond) { \ 1412 | int fail = 0; \ 1413 | PicoTestFilterResult filterResult = \ 1414 | (cond == NULL) \ 1415 | ? PICOTEST_FILTER_PASS \ 1416 | : PICOTEST_FILTER(_suiteName, _PICOTEST_STRINGIZE(_suiteName), \ 1417 | cond); \ 1418 | switch (filterResult) { \ 1419 | case PICOTEST_FILTER_PASS: \ 1420 | cond = NULL; \ 1421 | case PICOTEST_FILTER_PASS_PROPAGATE: \ 1422 | fail += _suiteName##_testSuiteRunner(cond); \ 1423 | break; \ 1424 | case PICOTEST_FILTER_SKIP: \ 1425 | break; \ 1426 | case PICOTEST_FILTER_SKIP_PROPAGATE: { \ 1427 | PicoTestMetadata **subtest = _suiteName##_subtests; \ 1428 | for (; *subtest; subtest++) { \ 1429 | fail += (*subtest)->test(cond); \ 1430 | } \ 1431 | } break; \ 1432 | } \ 1433 | return fail; \ 1434 | } 1435 | 1436 | /*! \cond IGNORE */ 1437 | #define _PICOTEST_SUITE_ENUMERATE_SUBTEST(_testName) \ 1438 | PICOTEST_METADATA(_testName), 1439 | /*! \endcond */ 1440 | 1441 | /*! \} End of Test Suite Definitions */ 1442 | 1443 | /*! 1444 | * \name Test Suite Hooks 1445 | * 1446 | * PicoTest provides a way for client code to intercept test execution events 1447 | * on test suites and their subtests. This can be used for e.g. logging purpose 1448 | * or reporting. 1449 | * \{ 1450 | */ 1451 | 1452 | /** 1453 | * Function signature of test suite enter hooks. 1454 | * 1455 | * Called before running the first subtest. 1456 | * 1457 | * @param suiteName Test suite name. 1458 | * @param nb Number of subtests. 1459 | * 1460 | * @par Usage 1461 | * @snippet hooks.c PICOTEST_SUITE_ENTER example 1462 | * 1463 | * @par Examples 1464 | * @example_file{hooks.c} 1465 | * 1466 | * @see PICOTEST_SUITE_ENTER 1467 | */ 1468 | typedef void(PicoTestSuiteEnterProc)(const char *suiteName, int nb); 1469 | 1470 | /** 1471 | * Default test suite enter hook. Does nothing. 1472 | * 1473 | * @see PicoTestSuiteEnterProc 1474 | * @see PICOTEST_SUITE_ENTER 1475 | */ 1476 | #define PICOTEST_SUITE_ENTER_DEFAULT(suiteName, nb) 1477 | 1478 | /** 1479 | * Define the test suite enter hook. 1480 | * 1481 | * The default hook does nothing. Redefine this macro to use a custom hook, 1482 | * which must follow the @ref PicoTestSuiteEnterProc signature. 1483 | * 1484 | * @note Custom functions only apply to the tests defined after the macro 1485 | * redefinition. As macros can be redefined several times, this means that 1486 | * different functions may apply for the same source. 1487 | * 1488 | * @par Usage 1489 | * @snippet hooks.c PICOTEST_SUITE_ENTER example 1490 | * 1491 | * @par Examples 1492 | * @example_file{hooks.c} 1493 | * 1494 | * @see PicoTestSuiteEnterProc 1495 | * @see PICOTEST_SUITE_ENTER_DEFAULT 1496 | * @see PICOTEST_SUITE_LEAVE 1497 | */ 1498 | #define PICOTEST_SUITE_ENTER PICOTEST_SUITE_ENTER_DEFAULT 1499 | 1500 | /** 1501 | * Function signature of test suite leave hooks. 1502 | * 1503 | * @param suiteName Test suite name. 1504 | * @param nb Number of subtests. 1505 | * @param fail Number of failed subtests (including the subtests' 1506 | * subtests if any). 1507 | * 1508 | * @par Usage 1509 | * @snippet hooks.c PICOTEST_SUITE_LEAVE example 1510 | * 1511 | * @par Examples 1512 | * @example_file{hooks.c} 1513 | * 1514 | * @see PICOTEST_SUITE_LEAVE 1515 | */ 1516 | typedef void(PicoTestSuiteLeaveProc)(const char *suiteName, int nb, int fail); 1517 | 1518 | /** 1519 | * Default test suite leave hook. Does nothing. 1520 | * 1521 | * @see PicoTestSuiteLeaveProc 1522 | * @see PICOTEST_SUITE_LEAVE 1523 | */ 1524 | #define PICOTEST_SUITE_LEAVE_DEFAULT(suiteName, nb, fail) 1525 | 1526 | /** 1527 | * Define the test suite leave hook. 1528 | * 1529 | * Called after running all subtests. 1530 | * 1531 | * The default hook does nothing. Redefine this macro to use a custom hook, 1532 | * which must follow the @ref PicoTestSuiteLeaveProc signature. 1533 | * 1534 | * @note Custom functions only apply to the tests defined after the macro 1535 | * redefinition. As macros can be redefined several times, this means that 1536 | * different functions may apply for the same source. 1537 | * 1538 | * @par Usage 1539 | * @snippet hooks.c PICOTEST_SUITE_LEAVE example 1540 | * 1541 | * @par Examples 1542 | * @example_file{hooks.c} 1543 | * 1544 | * @see PicoTestSuiteLeaveProc 1545 | * @see PICOTEST_SUITE_LEAVE_DEFAULT 1546 | * @see PICOTEST_SUITE_ENTER 1547 | */ 1548 | #define PICOTEST_SUITE_LEAVE PICOTEST_SUITE_LEAVE_DEFAULT 1549 | 1550 | /** 1551 | * Function signature of test suite before subtest hooks. 1552 | * 1553 | * Called before running each subtest. 1554 | * 1555 | * @param suiteName Test suite name. 1556 | * @param nb Number of subtests. 1557 | * @param fail Failed test suite subtests so far (including its 1558 | * subtests' subtests if any). 1559 | * @param index Index of subtest. 1560 | * @param testName Name of subtest. 1561 | * 1562 | * @par Usage 1563 | * @snippet hooks.c PICOTEST_SUITE_BEFORE_SUBTEST example 1564 | * 1565 | * @par Examples 1566 | * @example_file{hooks.c} 1567 | * 1568 | * @see PICOTEST_SUITE_BEFORE_SUBTEST 1569 | */ 1570 | typedef void(PicoTestSuiteBeforeSubtestProc)(const char *suiteName, int nb, 1571 | int fail, int index, 1572 | const char *testName); 1573 | 1574 | /** 1575 | * Default test suite before subtest hook. Does nothing. 1576 | * 1577 | * @see PicoTestSuiteBeforeSubtestProc 1578 | * @see PICOTEST_SUITE_BEFORE_SUBTEST 1579 | */ 1580 | #define PICOTEST_SUITE_BEFORE_SUBTEST_DEFAULT(suiteName, nb, fail, index, \ 1581 | testName) 1582 | 1583 | /** 1584 | * Define the test suite before subset hook. 1585 | * 1586 | * The default hook does nothing. Redefine this macro to use a custom hook, 1587 | * which must follow the @ref PicoTestSuiteBeforeSubtestProc signature. 1588 | * 1589 | * @note Custom functions only apply to the tests defined after the macro 1590 | * redefinition. As macros can be redefined several times, this means that 1591 | * different functions may apply for the same source. 1592 | * 1593 | * @par Usage 1594 | * @snippet hooks.c PICOTEST_SUITE_BEFORE_SUBTEST example 1595 | * 1596 | * @par Examples 1597 | * @example_file{hooks.c} 1598 | * 1599 | * @see PicoTestSuiteBeforeSubtestProc 1600 | * @see PICOTEST_SUITE_BEFORE_SUBTEST_DEFAULT 1601 | * @see PICOTEST_SUITE_AFTER_SUBTEST 1602 | */ 1603 | #define PICOTEST_SUITE_BEFORE_SUBTEST PICOTEST_SUITE_BEFORE_SUBTEST_DEFAULT 1604 | 1605 | /** 1606 | * Function signature of test suite after subtest hooks. 1607 | * 1608 | * Called before running each subtest. 1609 | * 1610 | * @param suiteName Test suite name. 1611 | * @param nb Number of subtests. 1612 | * @param fail Failed test suite subtests so far (including its 1613 | * subtests' subtests if any). 1614 | * @param index Index of subtest. 1615 | * @param testName Name of subtest. 1616 | * @param sfail The subtest's failed tests (including its subtests if 1617 | * any). 1618 | * 1619 | * @par Usage 1620 | * @snippet hooks.c PICOTEST_SUITE_AFTER_SUBTEST example 1621 | * 1622 | * @par Examples 1623 | * @example_file{hooks.c} 1624 | * 1625 | * @see PICOTEST_SUITE_AFTER_SUBTEST 1626 | */ 1627 | typedef void(PicoTestSuiteAfterSubtestProc)(const char *suiteName, int nb, 1628 | int fail, int index, 1629 | const char *testName, int sfail); 1630 | 1631 | /** 1632 | * Default test suite after subtest hook. Does nothing. 1633 | * 1634 | * @see PicoTestSuiteAfterSubtestProc 1635 | * @see PICOTEST_SUITE_AFTER_SUBTEST 1636 | */ 1637 | #define PICOTEST_SUITE_AFTER_SUBTEST_DEFAULT(suiteName, nb, fail, index, \ 1638 | testName, sfail) 1639 | 1640 | /** 1641 | * Define the test suite after subset hook. 1642 | * 1643 | * The default hook does nothing. Redefine this macro to use a custom hook, 1644 | * which must follow the @ref PicoTestSuiteAfterSubtestProc signature. 1645 | * 1646 | * @note Custom functions only apply to the tests defined after the macro 1647 | * redefinition. As macros can be redefined several times, this means that 1648 | * different functions may apply for the same source. 1649 | * 1650 | * @par Usage 1651 | * @snippet hooks.c PICOTEST_SUITE_AFTER_SUBTEST example 1652 | * 1653 | * @par Examples 1654 | * @example_file{hooks.c} 1655 | * 1656 | * @see PicoTestSuiteAfterSubtestProc 1657 | * @see PICOTEST_SUITE_AFTER_SUBTEST_DEFAULT 1658 | * @see PICOTEST_SUITE_BEFORE_SUBTEST 1659 | */ 1660 | #define PICOTEST_SUITE_AFTER_SUBTEST PICOTEST_SUITE_AFTER_SUBTEST_DEFAULT 1661 | 1662 | /*! \} End of Test Suite Hooks */ 1663 | 1664 | /*! \} End of Test Suites */ 1665 | 1666 | /*! \} End of Public Interface */ 1667 | 1668 | /*! \internal 1669 | * \defgroup utilities Utilities 1670 | * 1671 | * Utility macros and building blocks. 1672 | * \{ 1673 | */ 1674 | 1675 | /*! \internal 1676 | * \name Basic Utilities 1677 | * \{ 1678 | */ 1679 | 1680 | /** \internal 1681 | * Turn argument into a C string. 1682 | */ 1683 | #define _PICOTEST_STRINGIZE(arg) #arg 1684 | 1685 | /** \internal 1686 | * Concatenate both arguments. 1687 | */ 1688 | #define _PICOTEST_CONCATENATE(arg1, arg2) _PICOTEST_CONCATENATE1(arg1, arg2) 1689 | 1690 | /*! \cond IGNORE */ 1691 | #define _PICOTEST_CONCATENATE1(arg1, arg2) _PICOTEST_CONCATENATE2(arg1, arg2) 1692 | #define _PICOTEST_CONCATENATE2(arg1, arg2) arg1##arg2 1693 | /*! \endcond */ 1694 | 1695 | /*! \} End of Basic Utilities */ 1696 | 1697 | /*! \internal 1698 | * \name Variadic Macro Utilities 1699 | * 1700 | * Macro hackery for accessing args passed to variadic macros. 1701 | * 1702 | * @see 1703 | * http://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1 1704 | * \{ 1705 | */ 1706 | 1707 | /** \internal 1708 | * Get the number of args passed to it. 1709 | * 1710 | * @param ... Arguments passed to the variadic macro. 1711 | * 1712 | * @warning Argument length must be between 1 and 63. Empty lists return zero 1713 | * due to limitations of the C preprocessor. 1714 | */ 1715 | #if defined(_PICOTEST_PARENS) 1716 | #define _PICOTEST_ARGCOUNT(...) \ 1717 | _PICOTEST_LASTARG _PICOTEST_PARENS( \ 1718 | __VA_ARGS__, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ 1719 | 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, \ 1720 | 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, \ 1721 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 1722 | #else 1723 | #define _PICOTEST_ARGCOUNT(...) \ 1724 | _PICOTEST_LASTARG(__VA_ARGS__, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, \ 1725 | 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, \ 1726 | 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, \ 1727 | 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ 1728 | 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 1729 | #endif /* defined(_PICOTEST_PARENS) */ 1730 | 1731 | /*! \cond IGNORE */ 1732 | #define _PICOTEST_LASTARG( \ 1733 | _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \ 1734 | _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \ 1735 | _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \ 1736 | _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \ 1737 | _62, _63, N, ...) \ 1738 | N 1739 | /*! \endcond */ 1740 | 1741 | /** \internal 1742 | * Iterate over the args passed to it. 1743 | * 1744 | * @param what Function taking one argument, applied to all remaining 1745 | * arguments. 1746 | * @param ... Arguments passed to the variadic macro. 1747 | * 1748 | * @warning Limited to 63 arguments. 1749 | */ 1750 | 1751 | #if defined(_PICOTEST_PARENS) 1752 | #define _PICOTEST_FOR_EACH(what, ...) \ 1753 | _PICOTEST_CONCATENATE(_PICOTEST_FOR_EACH_, \ 1754 | _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 1755 | _PICOTEST_PARENS(what, __VA_ARGS__) 1756 | #else 1757 | #define _PICOTEST_FOR_EACH(what, ...) \ 1758 | _PICOTEST_CONCATENATE(_PICOTEST_FOR_EACH_, \ 1759 | _PICOTEST_ARGCOUNT(__VA_ARGS__)) \ 1760 | (what, __VA_ARGS__) 1761 | #endif /* defined(_PICOTEST_PARENS) */ 1762 | 1763 | /*! \cond IGNORE */ 1764 | #if defined(_PICOTEST_PARENS) 1765 | #define _PICOTEST_FOR_EACH_1(what, x) what(x) 1766 | #define _PICOTEST_FOR_EACH_2(what, x, ...) \ 1767 | what(x) _PICOTEST_FOR_EACH_1 _PICOTEST_PARENS(what, __VA_ARGS__) 1768 | #define _PICOTEST_FOR_EACH_3(what, x, ...) \ 1769 | what(x) _PICOTEST_FOR_EACH_2 _PICOTEST_PARENS(what, __VA_ARGS__) 1770 | #define _PICOTEST_FOR_EACH_4(what, x, ...) \ 1771 | what(x) _PICOTEST_FOR_EACH_3 _PICOTEST_PARENS(what, __VA_ARGS__) 1772 | #define _PICOTEST_FOR_EACH_5(what, x, ...) \ 1773 | what(x) _PICOTEST_FOR_EACH_4 _PICOTEST_PARENS(what, __VA_ARGS__) 1774 | #define _PICOTEST_FOR_EACH_6(what, x, ...) \ 1775 | what(x) _PICOTEST_FOR_EACH_5 _PICOTEST_PARENS(what, __VA_ARGS__) 1776 | #define _PICOTEST_FOR_EACH_7(what, x, ...) \ 1777 | what(x) _PICOTEST_FOR_EACH_6 _PICOTEST_PARENS(what, __VA_ARGS__) 1778 | #define _PICOTEST_FOR_EACH_8(what, x, ...) \ 1779 | what(x) _PICOTEST_FOR_EACH_7 _PICOTEST_PARENS(what, __VA_ARGS__) 1780 | #define _PICOTEST_FOR_EACH_9(what, x, ...) \ 1781 | what(x) _PICOTEST_FOR_EACH_8 _PICOTEST_PARENS(what, __VA_ARGS__) 1782 | #define _PICOTEST_FOR_EACH_10(what, x, ...) \ 1783 | what(x) _PICOTEST_FOR_EACH_9 _PICOTEST_PARENS(what, __VA_ARGS__) 1784 | #define _PICOTEST_FOR_EACH_11(what, x, ...) \ 1785 | what(x) _PICOTEST_FOR_EACH_10 _PICOTEST_PARENS(what, __VA_ARGS__) 1786 | #define _PICOTEST_FOR_EACH_12(what, x, ...) \ 1787 | what(x) _PICOTEST_FOR_EACH_11 _PICOTEST_PARENS(what, __VA_ARGS__) 1788 | #define _PICOTEST_FOR_EACH_13(what, x, ...) \ 1789 | what(x) _PICOTEST_FOR_EACH_12 _PICOTEST_PARENS(what, __VA_ARGS__) 1790 | #define _PICOTEST_FOR_EACH_14(what, x, ...) \ 1791 | what(x) _PICOTEST_FOR_EACH_13 _PICOTEST_PARENS(what, __VA_ARGS__) 1792 | #define _PICOTEST_FOR_EACH_15(what, x, ...) \ 1793 | what(x) _PICOTEST_FOR_EACH_14 _PICOTEST_PARENS(what, __VA_ARGS__) 1794 | #define _PICOTEST_FOR_EACH_16(what, x, ...) \ 1795 | what(x) _PICOTEST_FOR_EACH_15 _PICOTEST_PARENS(what, __VA_ARGS__) 1796 | #define _PICOTEST_FOR_EACH_17(what, x, ...) \ 1797 | what(x) _PICOTEST_FOR_EACH_16 _PICOTEST_PARENS(what, __VA_ARGS__) 1798 | #define _PICOTEST_FOR_EACH_18(what, x, ...) \ 1799 | what(x) _PICOTEST_FOR_EACH_17 _PICOTEST_PARENS(what, __VA_ARGS__) 1800 | #define _PICOTEST_FOR_EACH_19(what, x, ...) \ 1801 | what(x) _PICOTEST_FOR_EACH_18 _PICOTEST_PARENS(what, __VA_ARGS__) 1802 | #define _PICOTEST_FOR_EACH_20(what, x, ...) \ 1803 | what(x) _PICOTEST_FOR_EACH_19 _PICOTEST_PARENS(what, __VA_ARGS__) 1804 | #define _PICOTEST_FOR_EACH_21(what, x, ...) \ 1805 | what(x) _PICOTEST_FOR_EACH_20 _PICOTEST_PARENS(what, __VA_ARGS__) 1806 | #define _PICOTEST_FOR_EACH_22(what, x, ...) \ 1807 | what(x) _PICOTEST_FOR_EACH_21 _PICOTEST_PARENS(what, __VA_ARGS__) 1808 | #define _PICOTEST_FOR_EACH_23(what, x, ...) \ 1809 | what(x) _PICOTEST_FOR_EACH_22 _PICOTEST_PARENS(what, __VA_ARGS__) 1810 | #define _PICOTEST_FOR_EACH_24(what, x, ...) \ 1811 | what(x) _PICOTEST_FOR_EACH_23 _PICOTEST_PARENS(what, __VA_ARGS__) 1812 | #define _PICOTEST_FOR_EACH_25(what, x, ...) \ 1813 | what(x) _PICOTEST_FOR_EACH_24 _PICOTEST_PARENS(what, __VA_ARGS__) 1814 | #define _PICOTEST_FOR_EACH_26(what, x, ...) \ 1815 | what(x) _PICOTEST_FOR_EACH_25 _PICOTEST_PARENS(what, __VA_ARGS__) 1816 | #define _PICOTEST_FOR_EACH_27(what, x, ...) \ 1817 | what(x) _PICOTEST_FOR_EACH_26 _PICOTEST_PARENS(what, __VA_ARGS__) 1818 | #define _PICOTEST_FOR_EACH_28(what, x, ...) \ 1819 | what(x) _PICOTEST_FOR_EACH_27 _PICOTEST_PARENS(what, __VA_ARGS__) 1820 | #define _PICOTEST_FOR_EACH_29(what, x, ...) \ 1821 | what(x) _PICOTEST_FOR_EACH_28 _PICOTEST_PARENS(what, __VA_ARGS__) 1822 | #define _PICOTEST_FOR_EACH_30(what, x, ...) \ 1823 | what(x) _PICOTEST_FOR_EACH_29 _PICOTEST_PARENS(what, __VA_ARGS__) 1824 | #define _PICOTEST_FOR_EACH_31(what, x, ...) \ 1825 | what(x) _PICOTEST_FOR_EACH_30 _PICOTEST_PARENS(what, __VA_ARGS__) 1826 | #define _PICOTEST_FOR_EACH_32(what, x, ...) \ 1827 | what(x) _PICOTEST_FOR_EACH_31 _PICOTEST_PARENS(what, __VA_ARGS__) 1828 | #define _PICOTEST_FOR_EACH_33(what, x, ...) \ 1829 | what(x) _PICOTEST_FOR_EACH_32 _PICOTEST_PARENS(what, __VA_ARGS__) 1830 | #define _PICOTEST_FOR_EACH_34(what, x, ...) \ 1831 | what(x) _PICOTEST_FOR_EACH_33 _PICOTEST_PARENS(what, __VA_ARGS__) 1832 | #define _PICOTEST_FOR_EACH_35(what, x, ...) \ 1833 | what(x) _PICOTEST_FOR_EACH_34 _PICOTEST_PARENS(what, __VA_ARGS__) 1834 | #define _PICOTEST_FOR_EACH_36(what, x, ...) \ 1835 | what(x) _PICOTEST_FOR_EACH_35 _PICOTEST_PARENS(what, __VA_ARGS__) 1836 | #define _PICOTEST_FOR_EACH_37(what, x, ...) \ 1837 | what(x) _PICOTEST_FOR_EACH_36 _PICOTEST_PARENS(what, __VA_ARGS__) 1838 | #define _PICOTEST_FOR_EACH_38(what, x, ...) \ 1839 | what(x) _PICOTEST_FOR_EACH_37 _PICOTEST_PARENS(what, __VA_ARGS__) 1840 | #define _PICOTEST_FOR_EACH_39(what, x, ...) \ 1841 | what(x) _PICOTEST_FOR_EACH_38 _PICOTEST_PARENS(what, __VA_ARGS__) 1842 | #define _PICOTEST_FOR_EACH_40(what, x, ...) \ 1843 | what(x) _PICOTEST_FOR_EACH_39 _PICOTEST_PARENS(what, __VA_ARGS__) 1844 | #define _PICOTEST_FOR_EACH_41(what, x, ...) \ 1845 | what(x) _PICOTEST_FOR_EACH_40 _PICOTEST_PARENS(what, __VA_ARGS__) 1846 | #define _PICOTEST_FOR_EACH_42(what, x, ...) \ 1847 | what(x) _PICOTEST_FOR_EACH_41 _PICOTEST_PARENS(what, __VA_ARGS__) 1848 | #define _PICOTEST_FOR_EACH_43(what, x, ...) \ 1849 | what(x) _PICOTEST_FOR_EACH_42 _PICOTEST_PARENS(what, __VA_ARGS__) 1850 | #define _PICOTEST_FOR_EACH_44(what, x, ...) \ 1851 | what(x) _PICOTEST_FOR_EACH_43 _PICOTEST_PARENS(what, __VA_ARGS__) 1852 | #define _PICOTEST_FOR_EACH_45(what, x, ...) \ 1853 | what(x) _PICOTEST_FOR_EACH_44 _PICOTEST_PARENS(what, __VA_ARGS__) 1854 | #define _PICOTEST_FOR_EACH_46(what, x, ...) \ 1855 | what(x) _PICOTEST_FOR_EACH_45 _PICOTEST_PARENS(what, __VA_ARGS__) 1856 | #define _PICOTEST_FOR_EACH_47(what, x, ...) \ 1857 | what(x) _PICOTEST_FOR_EACH_46 _PICOTEST_PARENS(what, __VA_ARGS__) 1858 | #define _PICOTEST_FOR_EACH_48(what, x, ...) \ 1859 | what(x) _PICOTEST_FOR_EACH_47 _PICOTEST_PARENS(what, __VA_ARGS__) 1860 | #define _PICOTEST_FOR_EACH_49(what, x, ...) \ 1861 | what(x) _PICOTEST_FOR_EACH_48 _PICOTEST_PARENS(what, __VA_ARGS__) 1862 | #define _PICOTEST_FOR_EACH_50(what, x, ...) \ 1863 | what(x) _PICOTEST_FOR_EACH_49 _PICOTEST_PARENS(what, __VA_ARGS__) 1864 | #define _PICOTEST_FOR_EACH_51(what, x, ...) \ 1865 | what(x) _PICOTEST_FOR_EACH_50 _PICOTEST_PARENS(what, __VA_ARGS__) 1866 | #define _PICOTEST_FOR_EACH_52(what, x, ...) \ 1867 | what(x) _PICOTEST_FOR_EACH_51 _PICOTEST_PARENS(what, __VA_ARGS__) 1868 | #define _PICOTEST_FOR_EACH_53(what, x, ...) \ 1869 | what(x) _PICOTEST_FOR_EACH_52 _PICOTEST_PARENS(what, __VA_ARGS__) 1870 | #define _PICOTEST_FOR_EACH_54(what, x, ...) \ 1871 | what(x) _PICOTEST_FOR_EACH_53 _PICOTEST_PARENS(what, __VA_ARGS__) 1872 | #define _PICOTEST_FOR_EACH_55(what, x, ...) \ 1873 | what(x) _PICOTEST_FOR_EACH_54 _PICOTEST_PARENS(what, __VA_ARGS__) 1874 | #define _PICOTEST_FOR_EACH_56(what, x, ...) \ 1875 | what(x) _PICOTEST_FOR_EACH_55 _PICOTEST_PARENS(what, __VA_ARGS__) 1876 | #define _PICOTEST_FOR_EACH_57(what, x, ...) \ 1877 | what(x) _PICOTEST_FOR_EACH_56 _PICOTEST_PARENS(what, __VA_ARGS__) 1878 | #define _PICOTEST_FOR_EACH_58(what, x, ...) \ 1879 | what(x) _PICOTEST_FOR_EACH_57 _PICOTEST_PARENS(what, __VA_ARGS__) 1880 | #define _PICOTEST_FOR_EACH_59(what, x, ...) \ 1881 | what(x) _PICOTEST_FOR_EACH_58 _PICOTEST_PARENS(what, __VA_ARGS__) 1882 | #define _PICOTEST_FOR_EACH_60(what, x, ...) \ 1883 | what(x) _PICOTEST_FOR_EACH_59 _PICOTEST_PARENS(what, __VA_ARGS__) 1884 | #define _PICOTEST_FOR_EACH_61(what, x, ...) \ 1885 | what(x) _PICOTEST_FOR_EACH_60 _PICOTEST_PARENS(what, __VA_ARGS__) 1886 | #define _PICOTEST_FOR_EACH_62(what, x, ...) \ 1887 | what(x) _PICOTEST_FOR_EACH_61 _PICOTEST_PARENS(what, __VA_ARGS__) 1888 | #define _PICOTEST_FOR_EACH_63(what, x, ...) \ 1889 | what(x) _PICOTEST_FOR_EACH_62 _PICOTEST_PARENS(what, __VA_ARGS__) 1890 | #else 1891 | #define _PICOTEST_FOR_EACH_1(what, x) what(x) 1892 | #define _PICOTEST_FOR_EACH_2(what, x, ...) \ 1893 | what(x) _PICOTEST_FOR_EACH_1(what, __VA_ARGS__) 1894 | #define _PICOTEST_FOR_EACH_3(what, x, ...) \ 1895 | what(x) _PICOTEST_FOR_EACH_2(what, __VA_ARGS__) 1896 | #define _PICOTEST_FOR_EACH_4(what, x, ...) \ 1897 | what(x) _PICOTEST_FOR_EACH_3(what, __VA_ARGS__) 1898 | #define _PICOTEST_FOR_EACH_5(what, x, ...) \ 1899 | what(x) _PICOTEST_FOR_EACH_4(what, __VA_ARGS__) 1900 | #define _PICOTEST_FOR_EACH_6(what, x, ...) \ 1901 | what(x) _PICOTEST_FOR_EACH_5(what, __VA_ARGS__) 1902 | #define _PICOTEST_FOR_EACH_7(what, x, ...) \ 1903 | what(x) _PICOTEST_FOR_EACH_6(what, __VA_ARGS__) 1904 | #define _PICOTEST_FOR_EACH_8(what, x, ...) \ 1905 | what(x) _PICOTEST_FOR_EACH_7(what, __VA_ARGS__) 1906 | #define _PICOTEST_FOR_EACH_9(what, x, ...) \ 1907 | what(x) _PICOTEST_FOR_EACH_8(what, __VA_ARGS__) 1908 | #define _PICOTEST_FOR_EACH_10(what, x, ...) \ 1909 | what(x) _PICOTEST_FOR_EACH_9(what, __VA_ARGS__) 1910 | #define _PICOTEST_FOR_EACH_11(what, x, ...) \ 1911 | what(x) _PICOTEST_FOR_EACH_10(what, __VA_ARGS__) 1912 | #define _PICOTEST_FOR_EACH_12(what, x, ...) \ 1913 | what(x) _PICOTEST_FOR_EACH_11(what, __VA_ARGS__) 1914 | #define _PICOTEST_FOR_EACH_13(what, x, ...) \ 1915 | what(x) _PICOTEST_FOR_EACH_12(what, __VA_ARGS__) 1916 | #define _PICOTEST_FOR_EACH_14(what, x, ...) \ 1917 | what(x) _PICOTEST_FOR_EACH_13(what, __VA_ARGS__) 1918 | #define _PICOTEST_FOR_EACH_15(what, x, ...) \ 1919 | what(x) _PICOTEST_FOR_EACH_14(what, __VA_ARGS__) 1920 | #define _PICOTEST_FOR_EACH_16(what, x, ...) \ 1921 | what(x) _PICOTEST_FOR_EACH_15(what, __VA_ARGS__) 1922 | #define _PICOTEST_FOR_EACH_17(what, x, ...) \ 1923 | what(x) _PICOTEST_FOR_EACH_16(what, __VA_ARGS__) 1924 | #define _PICOTEST_FOR_EACH_18(what, x, ...) \ 1925 | what(x) _PICOTEST_FOR_EACH_17(what, __VA_ARGS__) 1926 | #define _PICOTEST_FOR_EACH_19(what, x, ...) \ 1927 | what(x) _PICOTEST_FOR_EACH_18(what, __VA_ARGS__) 1928 | #define _PICOTEST_FOR_EACH_20(what, x, ...) \ 1929 | what(x) _PICOTEST_FOR_EACH_19(what, __VA_ARGS__) 1930 | #define _PICOTEST_FOR_EACH_21(what, x, ...) \ 1931 | what(x) _PICOTEST_FOR_EACH_20(what, __VA_ARGS__) 1932 | #define _PICOTEST_FOR_EACH_22(what, x, ...) \ 1933 | what(x) _PICOTEST_FOR_EACH_21(what, __VA_ARGS__) 1934 | #define _PICOTEST_FOR_EACH_23(what, x, ...) \ 1935 | what(x) _PICOTEST_FOR_EACH_22(what, __VA_ARGS__) 1936 | #define _PICOTEST_FOR_EACH_24(what, x, ...) \ 1937 | what(x) _PICOTEST_FOR_EACH_23(what, __VA_ARGS__) 1938 | #define _PICOTEST_FOR_EACH_25(what, x, ...) \ 1939 | what(x) _PICOTEST_FOR_EACH_24(what, __VA_ARGS__) 1940 | #define _PICOTEST_FOR_EACH_26(what, x, ...) \ 1941 | what(x) _PICOTEST_FOR_EACH_25(what, __VA_ARGS__) 1942 | #define _PICOTEST_FOR_EACH_27(what, x, ...) \ 1943 | what(x) _PICOTEST_FOR_EACH_26(what, __VA_ARGS__) 1944 | #define _PICOTEST_FOR_EACH_28(what, x, ...) \ 1945 | what(x) _PICOTEST_FOR_EACH_27(what, __VA_ARGS__) 1946 | #define _PICOTEST_FOR_EACH_29(what, x, ...) \ 1947 | what(x) _PICOTEST_FOR_EACH_28(what, __VA_ARGS__) 1948 | #define _PICOTEST_FOR_EACH_30(what, x, ...) \ 1949 | what(x) _PICOTEST_FOR_EACH_29(what, __VA_ARGS__) 1950 | #define _PICOTEST_FOR_EACH_31(what, x, ...) \ 1951 | what(x) _PICOTEST_FOR_EACH_30(what, __VA_ARGS__) 1952 | #define _PICOTEST_FOR_EACH_32(what, x, ...) \ 1953 | what(x) _PICOTEST_FOR_EACH_31(what, __VA_ARGS__) 1954 | #define _PICOTEST_FOR_EACH_33(what, x, ...) \ 1955 | what(x) _PICOTEST_FOR_EACH_32(what, __VA_ARGS__) 1956 | #define _PICOTEST_FOR_EACH_34(what, x, ...) \ 1957 | what(x) _PICOTEST_FOR_EACH_33(what, __VA_ARGS__) 1958 | #define _PICOTEST_FOR_EACH_35(what, x, ...) \ 1959 | what(x) _PICOTEST_FOR_EACH_34(what, __VA_ARGS__) 1960 | #define _PICOTEST_FOR_EACH_36(what, x, ...) \ 1961 | what(x) _PICOTEST_FOR_EACH_35(what, __VA_ARGS__) 1962 | #define _PICOTEST_FOR_EACH_37(what, x, ...) \ 1963 | what(x) _PICOTEST_FOR_EACH_36(what, __VA_ARGS__) 1964 | #define _PICOTEST_FOR_EACH_38(what, x, ...) \ 1965 | what(x) _PICOTEST_FOR_EACH_37(what, __VA_ARGS__) 1966 | #define _PICOTEST_FOR_EACH_39(what, x, ...) \ 1967 | what(x) _PICOTEST_FOR_EACH_38(what, __VA_ARGS__) 1968 | #define _PICOTEST_FOR_EACH_40(what, x, ...) \ 1969 | what(x) _PICOTEST_FOR_EACH_39(what, __VA_ARGS__) 1970 | #define _PICOTEST_FOR_EACH_41(what, x, ...) \ 1971 | what(x) _PICOTEST_FOR_EACH_40(what, __VA_ARGS__) 1972 | #define _PICOTEST_FOR_EACH_42(what, x, ...) \ 1973 | what(x) _PICOTEST_FOR_EACH_41(what, __VA_ARGS__) 1974 | #define _PICOTEST_FOR_EACH_43(what, x, ...) \ 1975 | what(x) _PICOTEST_FOR_EACH_42(what, __VA_ARGS__) 1976 | #define _PICOTEST_FOR_EACH_44(what, x, ...) \ 1977 | what(x) _PICOTEST_FOR_EACH_43(what, __VA_ARGS__) 1978 | #define _PICOTEST_FOR_EACH_45(what, x, ...) \ 1979 | what(x) _PICOTEST_FOR_EACH_44(what, __VA_ARGS__) 1980 | #define _PICOTEST_FOR_EACH_46(what, x, ...) \ 1981 | what(x) _PICOTEST_FOR_EACH_45(what, __VA_ARGS__) 1982 | #define _PICOTEST_FOR_EACH_47(what, x, ...) \ 1983 | what(x) _PICOTEST_FOR_EACH_46(what, __VA_ARGS__) 1984 | #define _PICOTEST_FOR_EACH_48(what, x, ...) \ 1985 | what(x) _PICOTEST_FOR_EACH_47(what, __VA_ARGS__) 1986 | #define _PICOTEST_FOR_EACH_49(what, x, ...) \ 1987 | what(x) _PICOTEST_FOR_EACH_48(what, __VA_ARGS__) 1988 | #define _PICOTEST_FOR_EACH_50(what, x, ...) \ 1989 | what(x) _PICOTEST_FOR_EACH_49(what, __VA_ARGS__) 1990 | #define _PICOTEST_FOR_EACH_51(what, x, ...) \ 1991 | what(x) _PICOTEST_FOR_EACH_50(what, __VA_ARGS__) 1992 | #define _PICOTEST_FOR_EACH_52(what, x, ...) \ 1993 | what(x) _PICOTEST_FOR_EACH_51(what, __VA_ARGS__) 1994 | #define _PICOTEST_FOR_EACH_53(what, x, ...) \ 1995 | what(x) _PICOTEST_FOR_EACH_52(what, __VA_ARGS__) 1996 | #define _PICOTEST_FOR_EACH_54(what, x, ...) \ 1997 | what(x) _PICOTEST_FOR_EACH_53(what, __VA_ARGS__) 1998 | #define _PICOTEST_FOR_EACH_55(what, x, ...) \ 1999 | what(x) _PICOTEST_FOR_EACH_54(what, __VA_ARGS__) 2000 | #define _PICOTEST_FOR_EACH_56(what, x, ...) \ 2001 | what(x) _PICOTEST_FOR_EACH_55(what, __VA_ARGS__) 2002 | #define _PICOTEST_FOR_EACH_57(what, x, ...) \ 2003 | what(x) _PICOTEST_FOR_EACH_56(what, __VA_ARGS__) 2004 | #define _PICOTEST_FOR_EACH_58(what, x, ...) \ 2005 | what(x) _PICOTEST_FOR_EACH_57(what, __VA_ARGS__) 2006 | #define _PICOTEST_FOR_EACH_59(what, x, ...) \ 2007 | what(x) _PICOTEST_FOR_EACH_58(what, __VA_ARGS__) 2008 | #define _PICOTEST_FOR_EACH_60(what, x, ...) \ 2009 | what(x) _PICOTEST_FOR_EACH_59(what, __VA_ARGS__) 2010 | #define _PICOTEST_FOR_EACH_61(what, x, ...) \ 2011 | what(x) _PICOTEST_FOR_EACH_60(what, __VA_ARGS__) 2012 | #define _PICOTEST_FOR_EACH_62(what, x, ...) \ 2013 | what(x) _PICOTEST_FOR_EACH_61(what, __VA_ARGS__) 2014 | #define _PICOTEST_FOR_EACH_63(what, x, ...) \ 2015 | what(x) _PICOTEST_FOR_EACH_62(what, __VA_ARGS__) 2016 | #endif /* defined(_PICOTEST_PARENS) */ 2017 | /*! \endcond */ 2018 | 2019 | /*! \} End of Variadic Macro Utilities */ 2020 | 2021 | /*! \} End of Utilities */ 2022 | 2023 | #endif /* _PICOTEST */ 2024 | -------------------------------------------------------------------------------- /tests/picotest_logger.h: -------------------------------------------------------------------------------- 1 | #include "picotest.h" 2 | 3 | #include 4 | 5 | PicoTestFailureLoggerProc logFailure; 6 | #undef PICOTEST_FAILURE_LOGGER 7 | #define PICOTEST_FAILURE_LOGGER logFailure 8 | 9 | /* Test failure logger function. */ 10 | void logFailure(const char *file, int line, const char *type, const char *test, const char *msg, va_list args) 11 | { 12 | /* Error type. */ 13 | printf("[%s] ", type); 14 | 15 | /* Location in source code. */ 16 | printf("%s(%d) : ", file, line); 17 | 18 | /* Failed expression. */ 19 | printf("%s", test); 20 | 21 | /* Optional message. */ 22 | if (msg) { 23 | printf(" | "); 24 | vprintf(msg, args); 25 | } 26 | 27 | printf("\n"); 28 | } 29 | 30 | /* Hooks */ 31 | PicoTestCaseEnterProc logEnter; 32 | PicoTestCaseLeaveProc logLeave; 33 | #undef PICOTEST_CASE_ENTER 34 | #undef PICOTEST_CASE_LEAVE 35 | #define PICOTEST_CASE_ENTER logEnter 36 | #define PICOTEST_CASE_LEAVE logLeave 37 | 38 | int g_level = 0; 39 | void indent(int level) { 40 | while (level--) printf(" "); 41 | } 42 | void logEnter(const char *name) { 43 | indent(g_level++); 44 | printf("begin %s\n", name); 45 | } 46 | void logLeave(const char *name, int fail) { 47 | g_level--; 48 | if (!fail) 49 | { 50 | indent(g_level); 51 | printf("end %s\n", name); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/z_filesystem.c: -------------------------------------------------------------------------------- 1 | #include "picotest_logger.h" 2 | 3 | #define Z_FS_IMPLEMENTATION 4 | //#define Z_FS_NO_PATH 5 | //#define Z_FS_NO_FILE 6 | //#define Z_FS_NO_DIRECTORY 7 | //#define Z_FS_ALWAYS_FORWARD_SLASH 8 | #include "z_filesystem.h" 9 | 10 | static void assert_strcmp(const char *str1, const char *str2) 11 | { 12 | PICOTEST_ASSERT(strcmp(str1, str2) == 0, "\"%s\" and \"%s\" do not match", str1, str2); 13 | } 14 | 15 | #ifndef Z_FS_NO_PATH 16 | static void assert_normalized_strcmp(const char *str1, const char *str2) 17 | { 18 | char str1_norm[50]; 19 | char str2_norm[50]; 20 | zfs_path_normalize(str1_norm, sizeof(str1_norm), str1); 21 | zfs_path_normalize(str2_norm, sizeof(str2_norm), str2); 22 | PICOTEST_ASSERT(strcmp(str1_norm, str2_norm) == 0, "\"%s\" and \"%s\" do not match", str1_norm, str2_norm); 23 | } 24 | 25 | PICOTEST_CASE(path) 26 | { 27 | char buffer[50]; 28 | 29 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin", "file"); 30 | assert_normalized_strcmp(buffer, "/usr/bin/file"); 31 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin/", "file"); 32 | assert_normalized_strcmp(buffer, "/usr/bin/file"); 33 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin/", "/file"); 34 | assert_normalized_strcmp(buffer, "/usr/bin/file"); 35 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin/", ""); 36 | assert_normalized_strcmp(buffer, "/usr/bin/"); 37 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin", ""); 38 | assert_normalized_strcmp(buffer, "/usr/bin/"); 39 | zfs_path_join(buffer, sizeof(buffer), "", "/file"); 40 | assert_normalized_strcmp(buffer, "/file"); 41 | 42 | zfs_path_without_extension(buffer, sizeof(buffer), "/usr/bin/file.txt"); 43 | assert_strcmp(buffer, "/usr/bin/file"); 44 | zfs_path_without_extension(buffer, sizeof(buffer), "file.txt"); 45 | assert_strcmp(buffer, "file"); 46 | zfs_path_without_extension(buffer, sizeof(buffer), "/file"); 47 | assert_strcmp(buffer, "/file"); 48 | zfs_path_without_extension(buffer, sizeof(buffer), "file"); 49 | assert_strcmp(buffer, "file"); 50 | zfs_path_without_extension(buffer, sizeof(buffer), "/weird.path/to/file.txt"); 51 | assert_strcmp(buffer, "/weird.path/to/file"); 52 | zfs_path_without_extension(buffer, sizeof(buffer), "/wierd.path/to/file"); 53 | assert_strcmp(buffer, "/wierd.path/to/file"); 54 | zfs_path_without_extension(buffer, sizeof(buffer), "file.with.multiple.extensions.txt"); 55 | assert_strcmp(buffer, "file.with.multiple.extensions"); 56 | 57 | zfs_path_extension(buffer, sizeof(buffer), "/usr/bin/file.txt"); 58 | assert_strcmp(buffer, ".txt"); 59 | zfs_path_extension(buffer, sizeof(buffer), "file.txt"); 60 | assert_strcmp(buffer, ".txt"); 61 | zfs_path_extension(buffer, sizeof(buffer), "/file"); 62 | assert_strcmp(buffer, ""); 63 | zfs_path_extension(buffer, sizeof(buffer), "file"); 64 | assert_strcmp(buffer, ""); 65 | zfs_path_extension(buffer, sizeof(buffer), "/weird.path/to/file.txt"); 66 | assert_strcmp(buffer, ".txt"); 67 | zfs_path_extension(buffer, sizeof(buffer), "/wierd.path/to/file"); 68 | assert_strcmp(buffer, ""); 69 | zfs_path_extension(buffer, sizeof(buffer), "file.with.multiple.extensions.txt"); 70 | assert_strcmp(buffer, ".txt"); 71 | 72 | zfs_path_set_extension(buffer, sizeof(buffer), "/usr/bin/file.txt", ".bmp"); 73 | assert_strcmp(buffer, "/usr/bin/file.bmp"); 74 | zfs_path_set_extension(buffer, sizeof(buffer), "file.txt", ".bmp"); 75 | assert_strcmp(buffer, "file.bmp"); 76 | zfs_path_set_extension(buffer, sizeof(buffer), "/file", ".bmp"); 77 | assert_strcmp(buffer, "/file.bmp"); 78 | zfs_path_set_extension(buffer, sizeof(buffer), "file", ".bmp"); 79 | assert_strcmp(buffer, "file.bmp"); 80 | zfs_path_set_extension(buffer, sizeof(buffer), "/weird.path/to/file.txt", ".bmp"); 81 | assert_strcmp(buffer, "/weird.path/to/file.bmp"); 82 | zfs_path_set_extension(buffer, sizeof(buffer), "/wierd.path/to/file", ".bmp"); 83 | assert_strcmp(buffer, "/wierd.path/to/file.bmp"); 84 | zfs_path_set_extension(buffer, sizeof(buffer), "file.with.multiple.extensions.txt", ".bmp"); 85 | assert_strcmp(buffer, "file.with.multiple.extensions.bmp"); 86 | 87 | zfs_path_basename(buffer, sizeof(buffer), "/usr/bin/file"); 88 | assert_strcmp(buffer, "file"); 89 | zfs_path_basename(buffer, sizeof(buffer), "file.txt"); 90 | assert_strcmp(buffer, "file.txt"); 91 | zfs_path_basename(buffer, sizeof(buffer), "/file.txt"); 92 | assert_strcmp(buffer, "file.txt"); 93 | 94 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/usr/bin/file"); 95 | assert_strcmp(buffer, "file"); 96 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "file.txt"); 97 | assert_strcmp(buffer, "file"); 98 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/file.txt"); 99 | assert_strcmp(buffer, "file"); 100 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/file"); 101 | assert_strcmp(buffer, "file"); 102 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "file"); 103 | assert_strcmp(buffer, "file"); 104 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/weird.path/to/file"); 105 | assert_strcmp(buffer, "file"); 106 | 107 | zfs_path_directory(buffer, sizeof(buffer), "/usr/bin/file"); 108 | assert_normalized_strcmp(buffer, "/usr/bin/"); 109 | zfs_path_directory(buffer, sizeof(buffer), "file.txt"); 110 | assert_normalized_strcmp(buffer, ""); 111 | zfs_path_directory(buffer, sizeof(buffer), "/file.txt"); 112 | assert_normalized_strcmp(buffer, "/"); 113 | 114 | zfs_path_normalize(buffer, sizeof(buffer), "/mixed\\separators/here\\"); 115 | assert_normalized_strcmp(buffer, "/mixed/separators/here/"); 116 | 117 | strcpy(buffer, "/mixed\\separators/here\\"); 118 | zfs_path_normalize_inplace(buffer); 119 | assert_normalized_strcmp(buffer, "/mixed/separators/here/"); 120 | 121 | PICOTEST_ASSERT(zfs_path_working_directory(buffer, sizeof(buffer)) == ZFS_TRUE); 122 | zfs_path_full(buffer, sizeof(buffer), "file.txt"); 123 | zfs_path_full(buffer, sizeof(buffer), "/usr/bin/file.txt"); 124 | } 125 | 126 | PICOTEST_CASE(path_tiny_buffer) 127 | { 128 | char buffer[5]; 129 | 130 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin", "file"); 131 | assert_normalized_strcmp(buffer, "/usr"); 132 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin/", "file"); 133 | assert_normalized_strcmp(buffer, "/usr"); 134 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin/", "/file"); 135 | assert_normalized_strcmp(buffer, "/usr"); 136 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin/", ""); 137 | assert_normalized_strcmp(buffer, "/usr"); 138 | zfs_path_join(buffer, sizeof(buffer), "/usr/bin", ""); 139 | assert_normalized_strcmp(buffer, "/usr"); 140 | zfs_path_join(buffer, sizeof(buffer), "", "/file"); 141 | assert_normalized_strcmp(buffer, "/fil"); 142 | 143 | zfs_path_without_extension(buffer, sizeof(buffer), "/usr/bin/file.txt"); 144 | assert_strcmp(buffer, "/usr"); 145 | zfs_path_without_extension(buffer, sizeof(buffer), "file.txt"); 146 | assert_strcmp(buffer, "file"); 147 | zfs_path_without_extension(buffer, sizeof(buffer), "/file"); 148 | assert_strcmp(buffer, "/fil"); 149 | zfs_path_without_extension(buffer, sizeof(buffer), "file"); 150 | assert_strcmp(buffer, "file"); 151 | zfs_path_without_extension(buffer, sizeof(buffer), "/weird.path/to/file.txt"); 152 | assert_strcmp(buffer, "/wei"); 153 | zfs_path_without_extension(buffer, sizeof(buffer), "/wierd.path/to/file"); 154 | assert_strcmp(buffer, "/wie"); 155 | zfs_path_without_extension(buffer, sizeof(buffer), "file.with.multiple.extensions.txt"); 156 | assert_strcmp(buffer, "file"); 157 | 158 | zfs_path_extension(buffer, sizeof(buffer), "/usr/bin/file.txt"); 159 | assert_strcmp(buffer, ".txt"); 160 | zfs_path_extension(buffer, sizeof(buffer), "file.txt"); 161 | assert_strcmp(buffer, ".txt"); 162 | zfs_path_extension(buffer, sizeof(buffer), "/file"); 163 | assert_strcmp(buffer, ""); 164 | zfs_path_extension(buffer, sizeof(buffer), "file"); 165 | assert_strcmp(buffer, ""); 166 | zfs_path_extension(buffer, sizeof(buffer), "/weird.path/to/file.txt"); 167 | assert_strcmp(buffer, ".txt"); 168 | zfs_path_extension(buffer, sizeof(buffer), "/wierd.path/to/file"); 169 | assert_strcmp(buffer, ""); 170 | zfs_path_extension(buffer, sizeof(buffer), "file.with.multiple.extensions.txt"); 171 | assert_strcmp(buffer, ".txt"); 172 | 173 | zfs_path_set_extension(buffer, sizeof(buffer), "/usr/bin/file.txt", ".bmp"); 174 | assert_strcmp(buffer, "/usr"); 175 | zfs_path_set_extension(buffer, sizeof(buffer), "file.txt", ".bmp"); 176 | assert_strcmp(buffer, "file"); 177 | zfs_path_set_extension(buffer, sizeof(buffer), "/file", ".bmp"); 178 | assert_strcmp(buffer, "/fil"); 179 | zfs_path_set_extension(buffer, sizeof(buffer), "file", ".bmp"); 180 | assert_strcmp(buffer, "file"); 181 | zfs_path_set_extension(buffer, sizeof(buffer), "/weird.path/to/file.txt", ".bmp"); 182 | assert_strcmp(buffer, "/wei"); 183 | zfs_path_set_extension(buffer, sizeof(buffer), "/wierd.path/to/file", ".bmp"); 184 | assert_strcmp(buffer, "/wie"); 185 | zfs_path_set_extension(buffer, sizeof(buffer), "file.with.multiple.extensions.txt", ".bmp"); 186 | assert_strcmp(buffer, "file"); 187 | 188 | zfs_path_basename(buffer, sizeof(buffer), "/usr/bin/file"); 189 | assert_strcmp(buffer, "file"); 190 | zfs_path_basename(buffer, sizeof(buffer), "file.txt"); 191 | assert_strcmp(buffer, "file"); 192 | zfs_path_basename(buffer, sizeof(buffer), "/file.txt"); 193 | assert_strcmp(buffer, "file"); 194 | 195 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/usr/bin/file"); 196 | assert_strcmp(buffer, "file"); 197 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "file.txt"); 198 | assert_strcmp(buffer, "file"); 199 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/file.txt"); 200 | assert_strcmp(buffer, "file"); 201 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/file"); 202 | assert_strcmp(buffer, "file"); 203 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "file"); 204 | assert_strcmp(buffer, "file"); 205 | zfs_path_basename_without_extension(buffer, sizeof(buffer), "/weird.path/to/file"); 206 | assert_strcmp(buffer, "file"); 207 | 208 | zfs_path_directory(buffer, sizeof(buffer), "/usr/bin/file"); 209 | assert_normalized_strcmp(buffer, "/usr"); 210 | zfs_path_directory(buffer, sizeof(buffer), "file.txt"); 211 | assert_normalized_strcmp(buffer, ""); 212 | zfs_path_directory(buffer, sizeof(buffer), "/file.txt"); 213 | assert_normalized_strcmp(buffer, "/"); 214 | 215 | zfs_path_normalize(buffer, sizeof(buffer), "/m\\s"); 216 | assert_normalized_strcmp(buffer, "/m/s"); 217 | 218 | strcpy(buffer, "/m\\s"); 219 | zfs_path_normalize_inplace(buffer); 220 | assert_normalized_strcmp(buffer, "/m/s"); 221 | } 222 | 223 | PICOTEST_CASE(path_buffer_left) 224 | { 225 | char buffer[50]; 226 | 227 | strcpy(buffer, "/root"); 228 | zfs_path_join(buffer, sizeof(buffer), buffer, "another/path"); 229 | assert_normalized_strcmp(buffer, "/root/another/path"); 230 | 231 | zfs_path_set_extension(buffer, sizeof(buffer), buffer, ".bmp"); 232 | assert_normalized_strcmp(buffer, "/root/another/path.bmp"); 233 | } 234 | #endif 235 | 236 | #ifndef Z_FS_NO_FILE 237 | PICOTEST_CASE(file) 238 | { 239 | PICOTEST_ASSERT(zfs_file_touch("test.txt") == ZFS_TRUE); 240 | PICOTEST_ASSERT(zfs_file_exists("test.txt") == ZFS_TRUE); 241 | PICOTEST_ASSERT(zfs_file_rename("test.txt", "test2.txt") == ZFS_TRUE); 242 | PICOTEST_ASSERT(zfs_file_touch("test2.txt") == ZFS_TRUE); 243 | PICOTEST_ASSERT(zfs_file_exists("test2.txt") == ZFS_TRUE); 244 | PICOTEST_ASSERT(zfs_file_copy("test2.txt", "test.txt") == ZFS_TRUE); 245 | PICOTEST_ASSERT(zfs_file_delete("test.txt") == ZFS_TRUE); 246 | PICOTEST_ASSERT(zfs_file_exists("test.txt") == ZFS_FALSE); 247 | PICOTEST_ASSERT(zfs_file_delete("test2.txt") == ZFS_TRUE); 248 | PICOTEST_ASSERT(zfs_file_exists("test2.txt") == ZFS_FALSE); 249 | } 250 | #endif 251 | 252 | #ifndef Z_FS_NO_DIRECTORY 253 | PICOTEST_CASE(directory) 254 | { 255 | ZFSDir dir; 256 | if (zfs_directory_begin(&dir, "tests")) 257 | { 258 | do 259 | { 260 | const char *filename = zfs_directory_current_filename(&dir); 261 | zfs_bool is_dir = zfs_directory_is_directory(&dir); 262 | printf("%s = %i\n", filename, is_dir); 263 | } while (zfs_directory_next(&dir)); 264 | zfs_directory_end(&dir); 265 | } 266 | } 267 | #endif 268 | 269 | int main(void) 270 | { 271 | int fails = 0; 272 | #ifndef Z_FS_NO_PATH 273 | fails += path(NULL); 274 | fails += path_tiny_buffer(NULL); 275 | fails += path_buffer_left(NULL); 276 | #endif 277 | #ifndef Z_FS_NO_FILE 278 | fails += file(NULL); 279 | #endif 280 | #ifndef Z_FS_NO_DIRECTORY 281 | fails += directory(NULL); 282 | #endif 283 | return fails; 284 | } 285 | -------------------------------------------------------------------------------- /tests/z_io.c: -------------------------------------------------------------------------------- 1 | #include "picotest_logger.h" 2 | 3 | #define Z_IO_IMPLEMENTATION 4 | #include "z_io.h" 5 | 6 | #include 7 | 8 | const char TEST_TEXT[] = "This is a test\n"; 9 | 10 | static void write_test(ZIOHandle *handle) 11 | { 12 | zio_ll size = sizeof(TEST_TEXT) - 1; 13 | PICOTEST_ASSERT(zio_write(handle, TEST_TEXT, size) == size); 14 | } 15 | 16 | static void write_test_should_fail(ZIOHandle *handle) 17 | { 18 | PICOTEST_ASSERT(zio_write(handle, TEST_TEXT, sizeof(TEST_TEXT) - 1) == ZIO_ERROR); 19 | } 20 | 21 | static void read_test(ZIOHandle *handle) 22 | { 23 | char read_text[sizeof(TEST_TEXT)]; 24 | zio_ll size = sizeof(read_text) - 1; 25 | PICOTEST_ASSERT(zio_read(handle, read_text, size) == size); 26 | read_text[sizeof(read_text) - 1] = '\0'; 27 | 28 | PICOTEST_ASSERT(strcmp(TEST_TEXT, read_text) == 0); 29 | } 30 | 31 | static void read_test_should_fail(ZIOHandle *handle) 32 | { 33 | char read_text[sizeof(TEST_TEXT)]; 34 | memset(read_text, 0, sizeof(read_text)); 35 | PICOTEST_ASSERT(zio_read(handle, read_text, sizeof(read_text) - 1) == ZIO_ERROR); 36 | read_text[sizeof(read_text) - 1] = '\0'; 37 | 38 | PICOTEST_ASSERT(strcmp(TEST_TEXT, read_text) != 0); 39 | } 40 | 41 | PICOTEST_CASE(file) 42 | { 43 | ZIOHandle handle; 44 | PICOTEST_ASSERT(zio_open_file(&handle, "test.txt", ZIOM_WRITE | ZIOM_READ) == ZIO_OK); 45 | write_test(&handle); 46 | PICOTEST_ASSERT(zio_seek(&handle, 0, ZIO_SEEK_SET) == ZIO_OK); 47 | read_test(&handle); 48 | PICOTEST_ASSERT(zio_close(&handle) == ZIO_OK); 49 | 50 | PICOTEST_ASSERT(zio_open_file(&handle, "test.txt", ZIOM_WRITE) == ZIO_OK); 51 | write_test(&handle); 52 | PICOTEST_ASSERT(zio_seek(&handle, 0, ZIO_SEEK_SET) == ZIO_OK); 53 | read_test_should_fail(&handle); 54 | PICOTEST_ASSERT(zio_close(&handle) == ZIO_OK); 55 | 56 | PICOTEST_ASSERT(zio_open_file(&handle, "test.txt", ZIOM_READ) == ZIO_OK); 57 | write_test_should_fail(&handle); 58 | PICOTEST_ASSERT(zio_seek(&handle, 0, ZIO_SEEK_SET) == ZIO_OK); 59 | read_test(&handle); 60 | PICOTEST_ASSERT(zio_close(&handle) == ZIO_OK); 61 | 62 | remove("test.txt"); 63 | } 64 | 65 | PICOTEST_CASE(memory) 66 | { 67 | char mem[100]; 68 | ZIOHandle handle; 69 | PICOTEST_ASSERT(zio_open_memory(&handle, mem, sizeof(mem)) == ZIO_OK); 70 | write_test(&handle); 71 | PICOTEST_ASSERT(zio_seek(&handle, 0, ZIO_SEEK_SET) == ZIO_OK); 72 | read_test(&handle); 73 | PICOTEST_ASSERT(zio_close(&handle) == ZIO_OK); 74 | } 75 | 76 | PICOTEST_CASE(const_memory) 77 | { 78 | char mem[100]; 79 | strcpy(mem, TEST_TEXT); 80 | 81 | ZIOHandle handle; 82 | zio_open_const_memory(&handle, mem, sizeof(mem)); 83 | write_test_should_fail(&handle); 84 | PICOTEST_ASSERT(zio_seek(&handle, 0, ZIO_SEEK_SET) == ZIO_OK); 85 | read_test(&handle); 86 | PICOTEST_ASSERT(zio_close(&handle) == ZIO_OK); 87 | } 88 | 89 | int main(void) 90 | { 91 | int fails = 0; 92 | fails += file(NULL); 93 | fails += memory(NULL); 94 | fails += const_memory(NULL); 95 | return fails; 96 | } 97 | -------------------------------------------------------------------------------- /z_filesystem.h: -------------------------------------------------------------------------------- 1 | /* 2 | z_filesystem - Filesystem functions 3 | 4 | PLATFORMS 5 | Supports Windows and Linux at the moment, but Mac support should not be hard to implement. 6 | 7 | USAGE 8 | #define Z_FS_IMPLEMENTATION 9 | before you include this file in *one* C or C++ file to create the implementation. 10 | 11 | #define Z_FS_STATIC 12 | before you include this file to create a private implementation. 13 | 14 | #define Z_FS_NO_PATH 15 | to disable path functions. 16 | If directory traversal is enabled, path functions will be included anyway, 17 | but as internal (static) functions. 18 | 19 | #define Z_FS_NO_FILE 20 | to disable file functions. 21 | 22 | #define Z_FS_NO_DIRECTORY 23 | to disable directory traversal functions. 24 | 25 | #define Z_FS_ALWAYS_FORWARD_SLASH 26 | to always use / as the directory separator, even on Windows. 27 | 28 | UNLICENSE 29 | This is free and unencumbered software released into the public domain. 30 | Anyone is free to copy, modify, publish, use, compile, sell, or 31 | distribute this software, either in source code form or as a compiled 32 | binary, for any purpose, commercial or non-commercial, and by any 33 | means. 34 | In jurisdictions that recognize copyright laws, the author or authors 35 | of this software dedicate any and all copyright interest in the 36 | software to the public domain. We make this dedication for the benefit 37 | of the public at large and to the detriment of our heirs and 38 | successors. We intend this dedication to be an overt act of 39 | relinquishment in perpetuity of all present and future rights to this 40 | software under copyright law. 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 42 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 43 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 44 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 45 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 46 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 47 | OTHER DEALINGS IN THE SOFTWARE. 48 | For more information, please refer to 49 | */ 50 | 51 | // EXAMPLE 52 | #if 0 53 | char path[50]; 54 | zfs_path_full(path, sizeof(path), "file.txt"); 55 | zfs_file_touch(path); 56 | zfs_file_delete(path); 57 | 58 | char dir_path[50]; 59 | zfs_path_directory(dir_path, sizeof(dir_path), path); 60 | 61 | ZFSDir dir; 62 | if (zfs_directory_begin(&dir, dir_path)) 63 | { 64 | do 65 | { 66 | const char *filename = zfs_directory_current_filename(&dir); 67 | zfs_bool is_dir = zfs_directory_is_directory(&dir); 68 | } while (zfs_directory_next(&dir)); 69 | zfs_directory_end(&dir); 70 | } 71 | #endif 72 | 73 | #ifndef ZFS_INCLUDED_FILESYSTEM_H 74 | #define ZFS_INCLUDED_FILESYSTEM_H 75 | 76 | #ifdef __cplusplus 77 | extern "C" { 78 | #endif 79 | 80 | #ifdef Z_FS_STATIC 81 | #define ZFSDEF static 82 | #else 83 | #define ZFSDEF extern 84 | #endif 85 | 86 | #if defined(__linux) 87 | #define ZFS_POSIX 88 | #elif defined(_WIN32) 89 | #define ZFS_WINDOWS 90 | #else 91 | #error Unsupported platform 92 | #endif 93 | 94 | typedef long long zfs_ll; 95 | typedef int zfs_bool; 96 | 97 | enum 98 | { 99 | ZFS_FALSE, 100 | ZFS_TRUE, 101 | }; 102 | 103 | // Path functions 104 | #ifndef Z_FS_NO_PATH 105 | // Joins two paths. 106 | ZFSDEF void zfs_path_join(char *result, zfs_ll result_size, const char *left, const char *right); 107 | 108 | // Returns the path without the extension, given "/path/to/file.txt" it returns "/path/to/file". 109 | ZFSDEF void zfs_path_without_extension(char *result, zfs_ll result_size, const char *path); 110 | 111 | // Returns the extension of a file including the period (".txt"). 112 | ZFSDEF void zfs_path_extension(char *result, zfs_ll result_size, const char *path); 113 | 114 | // Returns the path as-is, but with a replaced extension 115 | ZFSDEF void zfs_path_set_extension(char *result, zfs_ll result_size, const char *path, const char *new_extension); 116 | 117 | // Returns the file part of a path, given "/path/to/file.txt" it returns "file.txt". 118 | ZFSDEF void zfs_path_basename(char *result, zfs_ll result_size, const char *path); 119 | 120 | // Returns the file part of a path without the extension, given "/path/to/file.txt" it returns "file". 121 | ZFSDEF void zfs_path_basename_without_extension(char *result, zfs_ll result_size, const char *path); 122 | 123 | // Returns the directory part of a path including a trailing /, given "/path/to/file.txt" it returns "/path/to/". 124 | ZFSDEF void zfs_path_directory(char *result, zfs_ll result_size, const char *path); 125 | 126 | // Replaces all directory separators with the native separator. \ on Windows, / on Linux. 127 | ZFSDEF void zfs_path_normalize_inplace(char *path); 128 | 129 | // Replaces all directory separators with the native separator. \ on Windows, / on Linux. Operates on a copy of the string. 130 | ZFSDEF void zfs_path_normalize(char *result, zfs_ll result_size, const char *path); 131 | 132 | // Sets 'result' buffer to the current working directory. 133 | // Returns false if the current working directory could not be found. 134 | ZFSDEF zfs_bool zfs_path_working_directory(char *result, zfs_ll result_size); 135 | 136 | // If 'path' is a full path, it returns 'path', otherwise it joins 'path' with the working directory. 137 | ZFSDEF void zfs_path_full(char *result, zfs_ll result_size, const char *path); 138 | #endif // Z_FS_NO_PATH 139 | 140 | // File functions 141 | #ifndef Z_FS_NO_FILE 142 | // If 'filename' exists, the access and modified times are updated, otherwise the file is created. 143 | // Returns false if it failed. 144 | ZFSDEF zfs_bool zfs_file_touch(const char *filename); 145 | 146 | // Returns whether 'filename' exists or not. 147 | ZFSDEF zfs_bool zfs_file_exists(const char *filename); 148 | 149 | // Renames 'old_filename' to 'new_filename'. 150 | // Returns false if it failed. 151 | ZFSDEF zfs_bool zfs_file_rename(const char *old_filename, const char *new_filename); 152 | 153 | // Copies 'source_filename' to 'destination_filename'. 154 | // Copies in 32k byte chunks to conserve memory. 155 | // Returns false if it failed. 156 | ZFSDEF zfs_bool zfs_file_copy(const char *source_filename, const char *destination_filename); 157 | 158 | // Deletes 'filename'. 159 | // Returns false if it failed. 160 | ZFSDEF zfs_bool zfs_file_delete(const char *filename); 161 | #endif // Z_FS_NO_FILE 162 | 163 | // Directory traversal 164 | #ifndef Z_FS_NO_DIRECTORY 165 | typedef struct ZFSDir 166 | { 167 | void *handle; 168 | #if defined(ZFS_POSIX) 169 | void *data; 170 | #elif defined(ZFS_WINDOWS) 171 | char data[320]; // sizeof(WIN32_FIND_DATAA) == 320 172 | #endif 173 | } ZFSDir; 174 | 175 | // Initializes directory traversal. 176 | // 'path' can contain a trailing / or not, but should not include "/*". 177 | // 'context' can be either malloc'ed or simply created on the stack 178 | // Returns false if it failed. 179 | ZFSDEF zfs_bool zfs_directory_begin(ZFSDir *context, const char *path); 180 | 181 | // Steps the directory traversal forward. 182 | // Returns false if we've reached the end. 183 | ZFSDEF zfs_bool zfs_directory_next(ZFSDir *context); 184 | 185 | // Cleans up the context. 186 | // Does not need to be called if zfs_directory_begin() returned false. 187 | ZFSDEF void zfs_directory_end(ZFSDir *context); 188 | 189 | // Returns the filename of the file or directory the context currently points at. 190 | // The char pointer is only valid until the next call to zfs_directory_next() or zfs_directory_end(). 191 | // May crash if called after zfs_directory_begin() or zfs_directory_next() returns false, or after 192 | // calling zfs_directory_end(). 193 | ZFSDEF const char *zfs_directory_current_filename(ZFSDir *context); 194 | 195 | // Returns whether the context currently points at a directory. 196 | ZFSDEF zfs_bool zfs_directory_is_directory(ZFSDir *context); 197 | #endif // Z_FS_NO_DIRECTORY 198 | 199 | #ifdef __cplusplus 200 | } 201 | #endif 202 | 203 | #endif // ZFS_INCLUDED_FILESYSTEM_H 204 | 205 | #ifdef Z_FS_IMPLEMENTATION 206 | 207 | #include // For FILE API 208 | #include // For memcpy, strlen, strcmp 209 | 210 | #if defined(ZFS_POSIX) 211 | #include // For directory walking API 212 | #include // For utimes 213 | #include // For stat 214 | #include // For access, getcwd 215 | #elif defined(ZFS_WINDOWS) 216 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 217 | #define _CRT_SECURE_NO_WARNINGS 218 | #endif 219 | #define WIN32_LEAN_AND_MEAN 220 | #define NOMINMAX 221 | #include // For everything 222 | #undef WIN32_LEAN_AND_MEAN 223 | #undef NOMINMAX 224 | #endif 225 | 226 | #if !defined(Z_FS_NO_PATH) || !defined(Z_FS_NO_DIRECTORY) 227 | 228 | // If Z_FS_NO_PATH is defined, but not Z_FS_NO_DIRECTORY, 229 | // include path functions anyway, but as internal (static) functions. 230 | #if defined(Z_FS_NO_PATH) && !defined(Z_FS_NO_DIRECTORY) 231 | #define ZFSPATHDEF static 232 | #else 233 | #define ZFSPATHDEF ZFSDEF 234 | #endif 235 | 236 | #if defined(ZFS_POSIX) || defined(Z_FS_ALWAYS_FORWARD_SLASH) 237 | static const char ZFS__DIR_SEP = '/'; 238 | #elif defined(ZFS_WINDOWS) 239 | static const char ZFS__DIR_SEP = '\\'; 240 | #endif 241 | 242 | static inline zfs_bool zfs__is_dir_sep(const char c) 243 | { 244 | return (c == '/' || c == '\\'); 245 | } 246 | 247 | static inline zfs_ll zfs__find_last_dir_sep(const char *path, zfs_ll len) 248 | { 249 | zfs_ll index = len; 250 | while (index-- > 0 && !zfs__is_dir_sep(path[index])); 251 | return index; 252 | } 253 | 254 | static inline zfs_ll zfs__find_last_char(const char *path, zfs_ll len, char c) 255 | { 256 | zfs_ll index = len; 257 | while (index-- > 0 && path[index] != c); 258 | return index; 259 | } 260 | 261 | ZFSPATHDEF void zfs_path_join(char *result, zfs_ll result_size, const char *left, const char *right) 262 | { 263 | zfs_ll left_len = strlen(left); 264 | zfs_ll right_len = strlen(right); 265 | 266 | if (result != left) 267 | { 268 | if (left_len >= result_size) 269 | left_len = result_size - 1; 270 | memcpy(result, left, left_len); 271 | } 272 | 273 | if (left_len + 1 < result_size && left_len > 0 && !zfs__is_dir_sep(result[left_len - 1])) 274 | result[left_len++] = ZFS__DIR_SEP; 275 | 276 | if (left_len > 0 && right_len > 0 && zfs__is_dir_sep(right[0])) 277 | { 278 | ++right; 279 | --right_len; 280 | } 281 | 282 | if (left_len + right_len >= result_size) 283 | right_len = result_size - left_len - 1; 284 | memcpy(result + left_len, right, right_len); 285 | 286 | result[left_len + right_len] = '\0'; 287 | } 288 | 289 | ZFSPATHDEF void zfs_path_without_extension(char *result, zfs_ll result_size, const char *path) 290 | { 291 | zfs_ll len = strlen(path); 292 | zfs_ll dir_sep_index = zfs__find_last_dir_sep(path, len) + 1; 293 | zfs_ll ext_index = zfs__find_last_char(path, len, '.'); 294 | if (ext_index < dir_sep_index) 295 | ext_index = len; 296 | 297 | len = ext_index; 298 | if (len >= result_size) 299 | len = result_size - 1; 300 | memcpy(result, path, len); 301 | result[len] = '\0'; 302 | } 303 | 304 | ZFSPATHDEF void zfs_path_extension(char *result, zfs_ll result_size, const char *path) 305 | { 306 | zfs_ll len = strlen(path); 307 | zfs_ll dir_sep_index = zfs__find_last_dir_sep(path, len) + 1; 308 | zfs_ll ext_index = zfs__find_last_char(path, len, '.'); 309 | if (ext_index < dir_sep_index) 310 | ext_index = len; 311 | 312 | len -= ext_index; 313 | if (len >= result_size) 314 | len = result_size - 1; 315 | memcpy(result, path + ext_index, len); 316 | result[len] = '\0'; 317 | } 318 | 319 | ZFSPATHDEF void zfs_path_set_extension(char *result, zfs_ll result_size, const char *path, const char *new_extension) 320 | { 321 | zfs_ll len = strlen(path); 322 | zfs_ll dir_sep_index = zfs__find_last_dir_sep(path, len) + 1; 323 | zfs_ll ext_index = zfs__find_last_char(path, len, '.'); 324 | if (ext_index < dir_sep_index) 325 | ext_index = len; 326 | 327 | if (result != path) 328 | { 329 | if (ext_index >= result_size) 330 | ext_index = result_size - 1; 331 | memcpy(result, path, ext_index); 332 | } 333 | 334 | zfs_ll ext_len = strlen(new_extension); 335 | if (ext_index + ext_len >= result_size) 336 | ext_len = result_size - ext_index - 1; 337 | memcpy(result + ext_index, new_extension, ext_len); 338 | 339 | result[ext_index + ext_len] = '\0'; 340 | } 341 | 342 | ZFSPATHDEF void zfs_path_basename(char *result, zfs_ll result_size, const char *path) 343 | { 344 | zfs_ll len = strlen(path); 345 | zfs_ll offset = zfs__find_last_dir_sep(path, len) + 1; 346 | len -= offset; 347 | if (len >= result_size) 348 | len = result_size - 1; 349 | memcpy(result, path + offset, len); 350 | result[len] = '\0'; 351 | } 352 | 353 | ZFSPATHDEF void zfs_path_basename_without_extension(char *result, zfs_ll result_size, const char *path) 354 | { 355 | zfs_ll len = strlen(path); 356 | zfs_ll dir_sep_index = zfs__find_last_dir_sep(path, len) + 1; 357 | zfs_ll ext_index = zfs__find_last_char(path, len, '.'); 358 | if (ext_index < dir_sep_index) 359 | ext_index = len; 360 | 361 | len = ext_index - dir_sep_index; 362 | if (len >= result_size) 363 | len = result_size - 1; 364 | memcpy(result, path + dir_sep_index, len); 365 | result[len] = '\0'; 366 | } 367 | 368 | ZFSPATHDEF void zfs_path_directory(char *result, zfs_ll result_size, const char *path) 369 | { 370 | zfs_ll len = strlen(path); 371 | zfs_ll dir_sep_index = zfs__find_last_dir_sep(path, len); 372 | len = dir_sep_index + 1; 373 | if (len >= result_size) 374 | len = result_size - 1; 375 | memcpy(result, path, len); 376 | result[len] = '\0'; 377 | } 378 | 379 | ZFSPATHDEF void zfs_path_normalize_inplace(char *path) 380 | { 381 | while (*path) 382 | { 383 | if (zfs__is_dir_sep(*path)) 384 | (*path) = ZFS__DIR_SEP; 385 | ++path; 386 | } 387 | } 388 | 389 | ZFSPATHDEF void zfs_path_normalize(char *result, zfs_ll result_size, const char *path) 390 | { 391 | zfs_ll len = strlen(path); 392 | if (len >= result_size) 393 | len = result_size - 1; 394 | memcpy(result, path, len); 395 | result[len] = '\0'; 396 | 397 | zfs_path_normalize_inplace(result); 398 | } 399 | 400 | ZFSPATHDEF zfs_bool zfs_path_working_directory(char *result, zfs_ll result_size) 401 | { 402 | #if defined(ZFS_POSIX) 403 | return (getcwd(result, result_size) != NULL); 404 | #elif defined(ZFS_WINDOWS) 405 | return (GetCurrentDirectoryA((DWORD)result_size, result) != 0); 406 | #endif 407 | } 408 | 409 | ZFSPATHDEF void zfs_path_full(char *result, zfs_ll result_size, const char *path) 410 | { 411 | if (zfs__is_dir_sep(path[0])) 412 | { 413 | zfs_path_join(result, result_size, "", path); 414 | return; 415 | } 416 | 417 | if (!zfs_path_working_directory(result, result_size)) 418 | { 419 | if (result_size > 0) 420 | result[0] = '\0'; 421 | } 422 | zfs_path_join(result, result_size, result, path); 423 | } 424 | #endif // Z_FS_NO_PATH 425 | 426 | #ifndef Z_FS_NO_FILE 427 | ZFSDEF zfs_bool zfs_file_touch(const char *filename) 428 | { 429 | if (zfs_file_exists(filename)) 430 | { 431 | #if defined(ZFS_POSIX) 432 | return (utimes(filename, NULL) == 0); 433 | #elif defined(ZFS_WINDOWS) 434 | SYSTEMTIME st; 435 | GetSystemTime(&st); 436 | FILETIME ft; 437 | if (!SystemTimeToFileTime(&st, &ft)) 438 | return ZFS_FALSE; 439 | 440 | HANDLE handle = CreateFileA(filename, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 441 | if (handle == INVALID_HANDLE_VALUE) 442 | return ZFS_FALSE; 443 | zfs_bool result = (SetFileTime(handle, NULL, &ft, &ft) != 0); 444 | CloseHandle(handle); 445 | return result; 446 | #endif 447 | } 448 | else 449 | { 450 | FILE *file = fopen(filename, "wb"); 451 | if (file == NULL) 452 | return ZFS_FALSE; 453 | fclose(file); 454 | return ZFS_TRUE; 455 | } 456 | } 457 | 458 | ZFSDEF zfs_bool zfs_file_exists(const char *filename) 459 | { 460 | #if defined(ZFS_POSIX) 461 | return (access(filename, F_OK) == 0); 462 | #elif defined(ZFS_WINDOWS) 463 | HANDLE handle = CreateFileA(filename, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 464 | if (handle == INVALID_HANDLE_VALUE) 465 | return ZFS_FALSE; 466 | CloseHandle(handle); 467 | return ZFS_TRUE; 468 | #endif 469 | } 470 | 471 | ZFSDEF zfs_bool zfs_file_rename(const char *old_filename, const char *new_filename) 472 | { 473 | return (rename(old_filename, new_filename) == 0); 474 | } 475 | 476 | ZFSDEF zfs_bool zfs_file_copy(const char *source_filename, const char *destination_filename) 477 | { 478 | FILE *source_file = fopen(source_filename, "rb"); 479 | if (!source_file) 480 | return ZFS_FALSE; 481 | 482 | FILE *destination_file = fopen(destination_filename, "wb"); 483 | if (!destination_file) 484 | { 485 | fclose(source_file); 486 | return ZFS_FALSE; 487 | } 488 | 489 | char buffer[32768]; // 32k 490 | 491 | size_t n; 492 | while ((n = fread(buffer, 1, sizeof(buffer), source_file)) > 0) 493 | { 494 | if (fwrite(buffer, 1, n, destination_file) != n) 495 | { 496 | fclose(source_file); 497 | fclose(destination_file); 498 | return ZFS_FALSE; 499 | } 500 | } 501 | 502 | fclose(source_file); 503 | fclose(destination_file); 504 | return ZFS_TRUE; 505 | } 506 | 507 | ZFSDEF zfs_bool zfs_file_delete(const char *filename) 508 | { 509 | return (remove(filename) == 0); 510 | } 511 | #endif // Z_FS_NO_FILE 512 | 513 | #ifndef Z_FS_NO_DIRECTORY 514 | static inline zfs_bool zfs__skip_directory(ZFSDir *context) 515 | { 516 | const char *name = zfs_directory_current_filename(context); 517 | return (strcmp(name, ".") == 0 || strcmp(name, "..") == 0); 518 | } 519 | 520 | ZFSDEF zfs_bool zfs_directory_begin(ZFSDir *context, const char *path) 521 | { 522 | #if defined(ZFS_POSIX) 523 | context->handle = opendir(path); 524 | context->data = NULL; 525 | if (!context->handle) 526 | return ZFS_FALSE; 527 | 528 | if (!zfs_directory_next(context)) 529 | { 530 | zfs_directory_end(context); 531 | return ZFS_FALSE; 532 | } 533 | 534 | return ZFS_TRUE; 535 | #elif defined(ZFS_WINDOWS) 536 | char new_path[MAX_PATH]; 537 | zfs_path_join(new_path, sizeof(new_path), path, "\\*"); 538 | 539 | context->handle = FindFirstFileA(new_path, (LPWIN32_FIND_DATAA)&context->data); 540 | if (context->handle == INVALID_HANDLE_VALUE) 541 | return ZFS_FALSE; 542 | 543 | while (zfs__skip_directory(context)) 544 | { 545 | if (!FindNextFileA(context->handle, (LPWIN32_FIND_DATAA)&context->data)) 546 | { 547 | zfs_directory_end(context); 548 | return ZFS_FALSE; 549 | } 550 | } 551 | 552 | return ZFS_TRUE; 553 | #endif 554 | } 555 | 556 | ZFSDEF zfs_bool zfs_directory_next(ZFSDir *context) 557 | { 558 | #if defined(ZFS_POSIX) 559 | do 560 | { 561 | context->data = readdir((DIR*)context->handle); 562 | if (!context->data) 563 | return ZFS_FALSE; 564 | } while (zfs__skip_directory(context)); 565 | return ZFS_TRUE; 566 | #elif defined(ZFS_WINDOWS) 567 | do 568 | { 569 | if (!FindNextFileA(context->handle, (LPWIN32_FIND_DATAA)&context->data)) 570 | return ZFS_FALSE; 571 | } while (zfs__skip_directory(context)); 572 | return ZFS_TRUE; 573 | #endif 574 | } 575 | 576 | ZFSDEF void zfs_directory_end(ZFSDir *context) 577 | { 578 | #if defined(ZFS_POSIX) 579 | closedir((DIR*)context->handle); 580 | context->handle = NULL; 581 | context->data = NULL; 582 | #elif defined(ZFS_WINDOWS) 583 | FindClose(context->handle); 584 | context->handle = NULL; 585 | memset(&context->data, 0, sizeof(context->data)); 586 | #endif 587 | } 588 | 589 | ZFSDEF const char *zfs_directory_current_filename(ZFSDir *context) 590 | { 591 | #if defined(ZFS_POSIX) 592 | return ((struct dirent*)context->data)->d_name; 593 | #elif defined(ZFS_WINDOWS) 594 | return ((LPWIN32_FIND_DATAA)context->data)->cFileName; 595 | #endif 596 | } 597 | 598 | ZFSDEF zfs_bool zfs_directory_is_directory(ZFSDir *context) 599 | { 600 | #if defined(ZFS_POSIX) 601 | #ifdef _DIRENT_HAVE_D_TYPE 602 | return (S_ISDIR(DTTOIF(((struct dirent*)context->data)->d_type)) != 0); 603 | #else 604 | //TODO(johannes): Does not work yet, we need to convert d_name to full path first 605 | struct stat buf; 606 | if (stat(((struct dirent*)context->data)->d_name, &buf) != 0) 607 | return ZFS_FALSE; 608 | return (S_ISDIR(buf.st_mode) != 0); 609 | #endif 610 | #elif defined(ZFS_WINDOWS) 611 | return (((LPWIN32_FIND_DATAA)context->data)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 612 | #endif 613 | } 614 | #endif // Z_FS_NO_DIRECTORY 615 | 616 | #endif // Z_FS_IMPLEMENTATION 617 | 618 | #undef _CRT_SECURE_NO_WARNINGS 619 | #undef ZFS_POSIX 620 | #undef ZFS_WINDOWS 621 | -------------------------------------------------------------------------------- /z_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | z_io - I/O library for reading/writing files or memory 3 | 4 | USAGE 5 | #define Z_IO_IMPLEMENTATION 6 | before you include this file in *one* C or C++ file to create the implementation. 7 | 8 | #define Z_IO_STATIC 9 | before you include this file to create a private implementation. 10 | 11 | EXAMPLE 12 | // Allocated on stack 13 | ZIOHandle handle; 14 | zio_open_file(&handle, filename, ZIOM_READ); 15 | zio_close(&handle); 16 | 17 | // Allocated on heap 18 | ZIOHandle *handle = (ZIOHandle*)malloc(sizeof(ZIOHandle)); 19 | zio_open_file(handle, filename, ZIOM_READ); 20 | zio_close(handle); 21 | free(handle); 22 | 23 | UNLICENSE 24 | This is free and unencumbered software released into the public domain. 25 | 26 | Anyone is free to copy, modify, publish, use, compile, sell, or 27 | distribute this software, either in source code form or as a compiled 28 | binary, for any purpose, commercial or non-commercial, and by any 29 | means. 30 | 31 | In jurisdictions that recognize copyright laws, the author or authors 32 | of this software dedicate any and all copyright interest in the 33 | software to the public domain. We make this dedication for the benefit 34 | of the public at large and to the detriment of our heirs and 35 | successors. We intend this dedication to be an overt act of 36 | relinquishment in perpetuity of all present and future rights to this 37 | software under copyright law. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 40 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 41 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 42 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 43 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 44 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 45 | OTHER DEALINGS IN THE SOFTWARE. 46 | 47 | For more information, please refer to 48 | */ 49 | #ifndef ZIO_INCLUDED_IO_H 50 | #define ZIO_INCLUDED_IO_H 51 | 52 | #ifdef __cplusplus 53 | extern "C" { 54 | #endif 55 | 56 | #ifdef Z_IO_STATIC 57 | #define ZIODEF static 58 | #else 59 | #define ZIODEF extern 60 | #endif 61 | 62 | typedef long long zio_ll; 63 | typedef int zio_result; 64 | 65 | typedef enum 66 | { 67 | ZIOM_WRITE = 1<<0, 68 | ZIOM_READ = 1<<1, 69 | } ZIOMode; 70 | 71 | typedef enum 72 | { 73 | ZIO_SEEK_SET, // Seek from the beginning of the data 74 | ZIO_SEEK_CUR, // Seek from the current position 75 | ZIO_SEEK_END, // Seek relative to the end of the data 76 | } ZIOSeek; 77 | 78 | enum 79 | { 80 | ZIO_ERROR = -1, 81 | ZIO_OK = 0, 82 | }; 83 | 84 | typedef struct ZIOHandle ZIOHandle; 85 | 86 | struct ZIOHandle 87 | { 88 | zio_result (*close)(ZIOHandle *handle); 89 | zio_ll (*size)(ZIOHandle *handle); 90 | zio_ll (*seek)(ZIOHandle *handle, zio_ll offset, ZIOSeek whence); 91 | zio_ll (*read)(ZIOHandle *handle, void *destination, zio_ll size); 92 | zio_ll (*write)(ZIOHandle *handle, const void *source, zio_ll size); 93 | 94 | const char *last_error; 95 | 96 | union 97 | { 98 | struct 99 | { 100 | void *handle; 101 | } file; 102 | struct 103 | { 104 | char *begin; 105 | char *pos; 106 | char *end; 107 | } mem; 108 | } data; 109 | }; 110 | 111 | // 'handle' can be either malloc'ed or simply created on the stack 112 | ZIODEF zio_result zio_open_file(ZIOHandle *handle, const char *filename, ZIOMode mode); 113 | ZIODEF zio_result zio_open_memory(ZIOHandle *handle, void *memory, zio_ll size); 114 | ZIODEF zio_result zio_open_const_memory(ZIOHandle *handle, const void *memory, zio_ll size); 115 | 116 | static inline zio_result zio_close(ZIOHandle *handle) { return handle->close(handle); } 117 | 118 | // Returns size of data, or ZIO_ERROR 119 | static inline zio_ll zio_size(ZIOHandle *handle) { return handle->size(handle); } 120 | 121 | // Returns position in data after seek, or ZIO_ERROR 122 | static inline zio_ll zio_seek(ZIOHandle *handle, zio_ll offset, ZIOSeek whence) { return handle->seek(handle, offset, whence); } 123 | 124 | // Returns position in data, or ZIO_ERROR 125 | static inline zio_ll zio_tell(ZIOHandle *handle) { return handle->seek(handle, 0, ZIO_SEEK_CUR); } 126 | 127 | // Returns bytes read, or ZIO_ERROR 128 | static inline zio_ll zio_read(ZIOHandle *handle, void *destination, zio_ll size) { return handle->read(handle, destination, size); } 129 | 130 | // Return bytes written, or ZIO_ERROR 131 | static inline zio_ll zio_write(ZIOHandle *handle, const void *source, zio_ll size) { return handle->write(handle, source, size); } 132 | 133 | static inline const char *zio_last_error(ZIOHandle *handle) { return handle->last_error; } 134 | 135 | #define ZIO_TYPES(ZIO_FN_DECL) \ 136 | ZIO_FN_DECL(i8, signed char) \ 137 | ZIO_FN_DECL(u8, unsigned char) \ 138 | ZIO_FN_DECL(i16, short) \ 139 | ZIO_FN_DECL(u16, unsigned short) \ 140 | ZIO_FN_DECL(i32, int) \ 141 | ZIO_FN_DECL(u32, unsigned int) \ 142 | ZIO_FN_DECL(i64, long long) \ 143 | ZIO_FN_DECL(u64, unsigned long long) \ 144 | ZIO_FN_DECL(f32, float) \ 145 | ZIO_FN_DECL(f64, double) 146 | 147 | #define ZIO_READ_FN(suffix, type) static inline type zio_read_##suffix(ZIOHandle *handle) { type value; handle->read(handle, &value, sizeof(value)); return value; } 148 | #define ZIO_WRITE_FN(suffix, type) static inline void zio_write_##suffix(ZIOHandle *handle, type value) { handle->write(handle, &value, sizeof(value)); } 149 | 150 | ZIO_TYPES(ZIO_READ_FN) 151 | ZIO_TYPES(ZIO_WRITE_FN) 152 | 153 | #undef ZIO_TYPES 154 | #undef ZIO_READ_FN 155 | #undef ZIO_WRITE_FN 156 | 157 | #ifdef __cplusplus 158 | } 159 | #endif 160 | 161 | #endif // ZIO_INCLUDED_IO_H 162 | 163 | #ifdef Z_IO_IMPLEMENTATION 164 | 165 | #include 166 | #include 167 | #include 168 | 169 | static inline void zio__zero_handle(ZIOHandle *handle) 170 | { 171 | memset(handle, 0, sizeof(ZIOHandle)); 172 | } 173 | 174 | static inline int zio__test_flag(int flags, int test) 175 | { 176 | return ((flags & test) == test); 177 | } 178 | 179 | static inline zio_result zio__set_error(ZIOHandle *handle, const char *error_string) 180 | { 181 | handle->last_error = error_string; 182 | return ZIO_ERROR; 183 | } 184 | 185 | // File I/O 186 | static zio_result zio__file_close(ZIOHandle *handle) 187 | { 188 | if (fclose((FILE*)handle->data.file.handle) != 0) 189 | return zio__set_error(handle, strerror(errno)); 190 | zio__zero_handle(handle); 191 | return ZIO_OK; 192 | } 193 | static zio_ll zio__file_size(ZIOHandle *handle) 194 | { 195 | zio_ll pos = zio_tell(handle); 196 | if (pos == ZIO_ERROR) 197 | return ZIO_ERROR; 198 | zio_ll size = zio_seek(handle, 0, ZIO_SEEK_END); 199 | zio_seek(handle, pos, ZIO_SEEK_SET); 200 | return size; 201 | } 202 | static zio_ll zio__file_seek(ZIOHandle *handle, zio_ll offset, ZIOSeek whence) 203 | { 204 | if (fseek((FILE*)handle->data.file.handle, (long)offset, whence) != 0) 205 | return zio__set_error(handle, strerror(errno)); 206 | return ftell((FILE*)handle->data.file.handle); 207 | } 208 | static zio_ll zio__file_read(ZIOHandle *handle, void *destination, zio_ll size) 209 | { 210 | zio_ll read_count = fread(destination, size, 1, (FILE*)handle->data.file.handle); 211 | if (read_count == 0 && ferror((FILE*)handle->data.file.handle)) 212 | return zio__set_error(handle, strerror(errno)); 213 | return read_count * size; 214 | } 215 | static zio_ll zio__file_write(ZIOHandle *handle, const void *source, zio_ll size) 216 | { 217 | zio_ll write_count = fwrite(source, size, 1, (FILE*)handle->data.file.handle); 218 | if (write_count == 0 && ferror((FILE*)handle->data.file.handle)) 219 | return zio__set_error(handle, strerror(errno));; 220 | return write_count * size; 221 | } 222 | 223 | // Memory I/O 224 | static zio_result zio__memory_close(ZIOHandle *handle) 225 | { 226 | zio__zero_handle(handle); 227 | return ZIO_OK; 228 | } 229 | static zio_ll zio__memory_size(ZIOHandle *handle) 230 | { 231 | zio_ll size = (handle->data.mem.end - handle->data.mem.begin); 232 | return size; 233 | } 234 | static zio_ll zio__memory_seek(ZIOHandle *handle, zio_ll offset, ZIOSeek whence) 235 | { 236 | char *new_pos; 237 | switch (whence) 238 | { 239 | case ZIO_SEEK_SET: 240 | new_pos = handle->data.mem.begin + offset; 241 | break; 242 | case ZIO_SEEK_CUR: 243 | new_pos = handle->data.mem.pos + offset; 244 | break; 245 | case ZIO_SEEK_END: 246 | new_pos = handle->data.mem.end + offset; 247 | break; 248 | default: 249 | return zio__set_error(handle, "Invalid whence value"); 250 | } 251 | 252 | if (new_pos < handle->data.mem.begin) 253 | new_pos = handle->data.mem.begin; 254 | if (new_pos > handle->data.mem.end) 255 | new_pos = handle->data.mem.end; 256 | 257 | handle->data.mem.pos = new_pos; 258 | zio_ll pos = (handle->data.mem.pos - handle->data.mem.begin); 259 | return pos; 260 | } 261 | static zio_ll zio__memory_read(ZIOHandle *handle, void *destination, zio_ll size) 262 | { 263 | if (size <= 0) 264 | return zio__set_error(handle, "Invalid size"); 265 | 266 | zio_ll mem_available = (handle->data.mem.end - handle->data.mem.pos); 267 | zio_ll total_bytes = size; 268 | if (total_bytes > mem_available) 269 | total_bytes = mem_available; 270 | 271 | memcpy(destination, handle->data.mem.pos, total_bytes); 272 | handle->data.mem.pos += total_bytes; 273 | return total_bytes; 274 | } 275 | static zio_ll zio__memory_write(ZIOHandle *handle, const void *source, zio_ll size) 276 | { 277 | if (size <= 0) 278 | return zio__set_error(handle, "Invalid size"); 279 | 280 | zio_ll mem_available = (handle->data.mem.end - handle->data.mem.pos); 281 | zio_ll total_bytes = size; 282 | if (total_bytes > mem_available) 283 | total_bytes = mem_available; 284 | 285 | memcpy(handle->data.mem.pos, source, total_bytes); 286 | handle->data.mem.pos += total_bytes; 287 | return total_bytes; 288 | } 289 | static zio_ll zio__const_memory_write(ZIOHandle *handle, const void *source, zio_ll size) 290 | { 291 | return zio__set_error(handle, "Cannot write to const memory"); 292 | } 293 | 294 | ZIODEF zio_result zio_open_file(ZIOHandle *handle, const char *filename, ZIOMode mode) 295 | { 296 | char mode_flags[7]; 297 | 298 | // Build mode string 299 | { 300 | int i = 0; 301 | if (zio__test_flag(mode, ZIOM_WRITE)) 302 | mode_flags[i++] = 'w'; 303 | else if (zio__test_flag(mode, ZIOM_READ)) 304 | mode_flags[i++] = 'r'; 305 | if (zio__test_flag(mode, ZIOM_WRITE | ZIOM_READ)) 306 | mode_flags[i++] = '+'; 307 | mode_flags[i++] = 'b'; // Always open in binary mode 308 | mode_flags[i] = '\0'; 309 | } 310 | 311 | zio__zero_handle(handle); 312 | 313 | FILE *file = fopen(filename, mode_flags); 314 | if (!file) 315 | return zio__set_error(handle, strerror(errno)); 316 | 317 | handle->data.file.handle = file; 318 | 319 | handle->close = zio__file_close; 320 | handle->size = zio__file_size; 321 | handle->seek = zio__file_seek; 322 | handle->read = zio__file_read; 323 | handle->write = zio__file_write; 324 | return ZIO_OK; 325 | } 326 | 327 | ZIODEF zio_result zio_open_memory(ZIOHandle *handle, void *memory, zio_ll size) 328 | { 329 | zio__zero_handle(handle); 330 | 331 | if (!memory || size < 0) 332 | return zio__set_error(handle, "Invalid memory or size"); 333 | 334 | handle->data.mem.begin = (char*)memory; 335 | handle->data.mem.pos = handle->data.mem.begin; 336 | handle->data.mem.end = handle->data.mem.begin + size; 337 | 338 | handle->close = zio__memory_close; 339 | handle->size = zio__memory_size; 340 | handle->seek = zio__memory_seek; 341 | handle->read = zio__memory_read; 342 | handle->write = zio__memory_write; 343 | return ZIO_OK; 344 | } 345 | 346 | ZIODEF zio_result zio_open_const_memory(ZIOHandle *handle, const void *memory, zio_ll size) 347 | { 348 | zio__zero_handle(handle); 349 | 350 | if (!memory || size < 0) 351 | return zio__set_error(handle, "Invalid memory or size"); 352 | 353 | handle->data.mem.begin = (char*)memory; 354 | handle->data.mem.pos = handle->data.mem.begin; 355 | handle->data.mem.end = handle->data.mem.begin + size; 356 | 357 | handle->close = zio__memory_close; 358 | handle->size = zio__memory_size; 359 | handle->seek = zio__memory_seek; 360 | handle->read = zio__memory_read; 361 | handle->write = zio__const_memory_write; 362 | return ZIO_OK; 363 | } 364 | 365 | #endif // Z_IO_IMPLEMENTATION 366 | --------------------------------------------------------------------------------