t/unit-tests: update clar to fcbed04

Update clar to fcbed04 (Merge pull request #123 from pks-gitlab/pks-sandbox-ubsan, 2025-09-10). The most significant changes since the last version include: - Fixed platform support for HP-UX. - Fixes for how clar handles the `-q` flag. - A couple of leak fixes for reported clar errors. - A new `cl_invoke()` function that retains line information. - New infrastructure to create temporary directories. - Improved printing of error messages so that all lines are now properly indented. - Proper selftests for the clar. Most of these changes are somewhat irrelevant to us, but neither do we have to adjust to any of these changes, either. What _is_ interesting to us though is especially the fixed support for HP-UX, and eventually we may also want to use `cl_invoke()`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Sep 10, 2025 at 15:09 UTC e7f04f651ac4550db3572720027503617d62ffeb
32 files changed +1311 -238
t/unit-tests/clar/.github/workflows/ci.yml
+17 -1
@@ -13,6 +13,11 @@ jobs:
13 platform:
14 - os: ubuntu-latest
15 generator: Unix Makefiles
16 + - os: ubuntu-latest
17 + generator: Unix Makefiles
18 + env:
19 + CC: "clang"
20 + CFLAGS: "-fsanitize=leak"
21 - os: macos-latest
22 generator: Unix Makefiles
23 - os: windows-latest
@@ -21,15 +26,26 @@ jobs:
26 generator: MSYS Makefiles
27 - os: windows-latest
28 generator: MinGW Makefiles
29 + fail-fast: false
30
31 runs-on: ${{ matrix.platform.os }}
32
33 + env:
34 + CC: ${{matrix.platform.env.CC}}
35 + CFLAGS: ${{matrix.platform.env.CFLAGS}}
36 +
37 steps:
38 - name: Check out
39 uses: actions/checkout@v2
40 - name: Build
41 + shell: bash
42 run: |
43 mkdir build
44 cd build
45 cmake .. -G "${{matrix.platform.generator}}"
35 - cmake --build .
46 + cmake --build . --verbose
47 + - name: Test
48 + shell: bash
49 + run: |
50 + cd build
51 + CTEST_OUTPUT_ON_FAILURE=1 ctest --build-config Debug
t/unit-tests/clar/CMakeLists.txt
+12 -1
@@ -1,8 +1,15 @@
1 +include(CheckFunctionExists)
2 +
3 cmake_minimum_required(VERSION 3.16..3.29)
4
5 project(clar LANGUAGES C)
6
5 -option(BUILD_TESTS "Build test executable" ON)
7 +option(BUILD_EXAMPLE "Build the example." ON)
8 +
9 +check_function_exists(realpath CLAR_HAS_REALPATH)
10 +if(CLAR_HAS_REALPATH)
11 + add_compile_definitions(-DCLAR_HAS_REALPATH)
12 +endif()
13
14 add_library(clar INTERFACE)
15 target_sources(clar INTERFACE
@@ -25,4 +32,8 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
32 if(BUILD_TESTING)
33 add_subdirectory(test)
34 endif()
35 +
36 + if(BUILD_EXAMPLE)
37 + add_subdirectory(example)
38 + endif()
39 endif()
t/unit-tests/clar/README.md
+20 -17
@@ -26,8 +26,7 @@ Can you count to funk?
26 ~~~~ sh
27 $ mkdir tests
28 $ cp -r $CLAR_ROOT/clar* tests
29 - $ cp $CLAR_ROOT/test/clar_test.h tests
30 - $ cp $CLAR_ROOT/test/main.c.sample tests/main.c
29 + $ cp $CLAR_ROOT/example/*.c tests
30 ~~~~
31
32 - **One: Write some tests**
@@ -147,7 +146,7 @@ To use Clar:
146
147 1. copy the Clar boilerplate to your test directory
148 2. copy (and probably modify) the sample `main.c` (from
150 - `$CLAR_PATH/test/main.c.sample`)
149 + `$CLAR_PATH/example/main.c`)
150 3. run the Clar mixer (a.k.a. `generate.py`) to scan your test directory and
151 write out the test suite metadata.
152 4. compile your test files and the Clar boilerplate into a single test
@@ -159,7 +158,7 @@ The Clar boilerplate gives you a set of useful test assertions and features
158 the `clar.c` and `clar.h` files, plus the code in the `clar/` subdirectory.
159 You should not need to edit these files.
160
162 -The sample `main.c` (i.e. `$CLAR_PATH/test/main.c.sample`) file invokes
161 +The sample `main.c` (i.e. `$CLAR_PATH/example/main.c`) file invokes
162 `clar_test(argc, argv)` to run the tests. Usually, you will edit this file
163 to perform any framework specific initialization and teardown that you need.
164
@@ -251,11 +250,16 @@ suite.
250
251 - `cl_fixture(const char *)`: Gets the full path to a fixture file.
252
254 -Please do note that these methods are *always* available whilst running a
255 -test, even when calling auxiliary/static functions inside the same file.
253 +### Auxiliary / helper functions
254
257 -It's strongly encouraged to perform test assertions in auxiliary methods,
258 -instead of returning error values. This is considered good Clar style.
255 +The clar API is always available while running a test, even when calling
256 +"auxiliary" (helper) functions.
257 +
258 +You're encouraged to perform test assertions in those auxiliary
259 +methods, instead of returning error values. This is considered good
260 +Clar style. _However_, when you do this, you need to call `cl_invoke`
261 +to preserve the current state; this ensures that failures are reported
262 +as coming from the actual test, instead of the auxiliary method.
263
264 Style Example:
265
@@ -310,20 +314,19 @@ static void check_string(const char *str)
314
315 void test_example__a_test_with_auxiliary_methods(void)
316 {
313 - check_string("foo");
314 - check_string("bar");
317 + cl_invoke(check_string("foo"));
318 + cl_invoke(check_string("bar"));
319 }
320 ~~~~
321
322 About Clar
323 ==========
324
321 -Clar has been written from scratch by [Vicent Martí](https://github.com/vmg),
322 -to replace the old testing framework in [libgit2][libgit2].
323 -
324 -Do you know what languages are *in* on the SF startup scene? Node.js *and*
325 -Latin. Follow [@vmg](https://www.twitter.com/vmg) on Twitter to
326 -receive more lessons on word etymology. You can be hip too.
327 -
325 +Clar was originally written by [Vicent Martí](https://github.com/vmg),
326 +to replace the old testing framework in [libgit2][libgit2]. It is
327 +currently maintained by [Edward Thomson](https://github.com/ethomson),
328 +and used by the [libgit2][libgit2] and [git][git] projects, amongst
329 +others.
330
331 [libgit2]: https://github.com/libgit2/libgit2
332 +[git]: https://github.com/git/git
t/unit-tests/clar/clar.c
+108 -41
@@ -79,6 +79,8 @@
79 # else
80 # define p_snprintf snprintf
81 # endif
82 +
83 +# define localtime_r(timer, buf) (localtime_s(buf, timer) == 0 ? buf : NULL)
84 #else
85 # include <sys/wait.h> /* waitpid(2) */
86 # include <unistd.h>
@@ -150,7 +152,6 @@ static struct {
152
153 enum cl_output_format output_format;
154
153 - int report_errors_only;
155 int exit_on_error;
156 int verbosity;
157
@@ -164,6 +165,10 @@ static struct {
165 struct clar_report *reports;
166 struct clar_report *last_report;
167
168 + const char *invoke_file;
169 + const char *invoke_func;
170 + size_t invoke_line;
171 +
172 void (*local_cleanup)(void *);
173 void *local_cleanup_payload;
174
@@ -199,8 +204,10 @@ static void clar_print_onabortv(const char *msg, va_list argp);
204 static void clar_print_onabort(const char *msg, ...);
205
206 /* From clar_sandbox.c */
202 -static void clar_unsandbox(void);
203 -static void clar_sandbox(void);
207 +static void clar_tempdir_init(void);
208 +static void clar_tempdir_shutdown(void);
209 +static int clar_sandbox_create(const char *suite_name, const char *test_name);
210 +static int clar_sandbox_cleanup(void);
211
212 /* From summary.h */
213 static struct clar_summary *clar_summary_init(const char *filename);
@@ -304,6 +311,8 @@ clar_run_test(
311
312 CL_TRACE(CL_TRACE__TEST__BEGIN);
313
314 + clar_sandbox_create(suite->name, test->name);
315 +
316 _clar.last_report->start = time(NULL);
317 clar_time_now(&start);
318
@@ -328,9 +337,13 @@ clar_run_test(
337 if (_clar.local_cleanup != NULL)
338 _clar.local_cleanup(_clar.local_cleanup_payload);
339
340 + clar__clear_invokepoint();
341 +
342 if (cleanup->ptr != NULL)
343 cleanup->ptr();
344
345 + clar_sandbox_cleanup();
346 +
347 CL_TRACE(CL_TRACE__TEST__END);
348
349 _clar.tests_ran++;
@@ -339,11 +352,7 @@ clar_run_test(
352 _clar.local_cleanup = NULL;
353 _clar.local_cleanup_payload = NULL;
354
342 - if (_clar.report_errors_only) {
343 - clar_report_errors(_clar.last_report);
344 - } else {
345 - clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status);
346 - }
355 + clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status);
356 }
357
358 static void
@@ -360,8 +369,7 @@ clar_run_suite(const struct clar_suite *suite, const char *filter)
369 if (_clar.exit_on_error && _clar.total_errors)
370 return;
371
363 - if (!_clar.report_errors_only)
364 - clar_print_onsuite(suite->name, ++_clar.suites_ran);
372 + clar_print_onsuite(suite->name, ++_clar.suites_ran);
373
374 _clar.active_suite = suite->name;
375 _clar.active_test = NULL;
@@ -428,12 +436,12 @@ clar_usage(const char *arg)
436 printf(" -iname Include the suite with `name`\n");
437 printf(" -xname Exclude the suite with `name`\n");
438 printf(" -v Increase verbosity (show suite names)\n");
431 - printf(" -q Only report tests that had an error\n");
439 + printf(" -q Decrease verbosity, inverse to -v\n");
440 printf(" -Q Quit as soon as a test fails\n");
441 printf(" -t Display results in tap format\n");
442 printf(" -l Print suite names\n");
443 printf(" -r[filename] Write summary file (to the optional filename)\n");
436 - exit(-1);
444 + exit(1);
445 }
446
447 static void
@@ -441,18 +449,11 @@ clar_parse_args(int argc, char **argv)
449 {
450 int i;
451
444 - /* Verify options before execute */
452 for (i = 1; i < argc; ++i) {
453 char *argument = argv[i];
454
448 - if (argument[0] != '-' || argument[1] == '\0'
449 - || strchr("sixvqQtlr", argument[1]) == NULL) {
455 + if (argument[0] != '-' || argument[1] == '\0')
456 clar_usage(argv[0]);
451 - }
452 - }
453 -
454 - for (i = 1; i < argc; ++i) {
455 - char *argument = argv[i];
457
458 switch (argument[1]) {
459 case 's':
@@ -465,8 +466,13 @@ clar_parse_args(int argc, char **argv)
466 argument += offset;
467 arglen = strlen(argument);
468
468 - if (arglen == 0)
469 - clar_usage(argv[0]);
469 + if (arglen == 0) {
470 + if (i + 1 == argc)
471 + clar_usage(argv[0]);
472 +
473 + argument = argv[++i];
474 + arglen = strlen(argument);
475 + }
476
477 for (j = 0; j < _clar_suite_count; ++j) {
478 suitelen = strlen(_clar_suites[j].name);
@@ -483,9 +489,6 @@ clar_parse_args(int argc, char **argv)
489
490 ++found;
491
486 - if (!exact)
487 - _clar.verbosity = MAX(_clar.verbosity, 1);
488 -
492 switch (action) {
493 case 's': {
494 struct clar_explicit *explicit;
@@ -517,23 +520,37 @@ clar_parse_args(int argc, char **argv)
520
521 if (!found)
522 clar_abort("No suite matching '%s' found.\n", argument);
523 +
524 break;
525 }
526
527 case 'q':
524 - _clar.report_errors_only = 1;
528 + if (argument[2] != '\0')
529 + clar_usage(argv[0]);
530 +
531 + _clar.verbosity--;
532 break;
533
534 case 'Q':
535 + if (argument[2] != '\0')
536 + clar_usage(argv[0]);
537 +
538 _clar.exit_on_error = 1;
539 break;
540
541 case 't':
542 + if (argument[2] != '\0')
543 + clar_usage(argv[0]);
544 +
545 _clar.output_format = CL_OUTPUT_TAP;
546 break;
547
548 case 'l': {
549 size_t j;
550 +
551 + if (argument[2] != '\0')
552 + clar_usage(argv[0]);
553 +
554 printf("Test suites (use -s<name> to run just one):\n");
555 for (j = 0; j < _clar_suite_count; ++j)
556 printf(" %3d: %s\n", (int)j, _clar_suites[j].name);
@@ -542,23 +559,27 @@ clar_parse_args(int argc, char **argv)
559 }
560
561 case 'v':
562 + if (argument[2] != '\0')
563 + clar_usage(argv[0]);
564 +
565 _clar.verbosity++;
566 break;
567
568 case 'r':
569 _clar.write_summary = 1;
570 free(_clar.summary_filename);
571 +
572 if (*(argument + 2)) {
573 if ((_clar.summary_filename = strdup(argument + 2)) == NULL)
574 clar_abort("Failed to allocate summary filename.\n");
575 } else {
576 _clar.summary_filename = NULL;
577 }
578 +
579 break;
580
581 default:
560 - clar_abort("Unexpected commandline argument '%s'.\n",
561 - argument[1]);
582 + clar_usage(argv[0]);
583 }
584 }
585 }
@@ -591,7 +612,7 @@ clar_test_init(int argc, char **argv)
612 if (_clar.write_summary)
613 _clar.summary = clar_summary_init(_clar.summary_filename);
614
594 - clar_sandbox();
615 + clar_tempdir_init();
616 }
617
618 int
@@ -623,7 +644,7 @@ clar_test_shutdown(void)
644 _clar.total_errors
645 );
646
626 - clar_unsandbox();
647 + clar_tempdir_shutdown();
648
649 if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0)
650 clar_abort("Failed to write the summary file '%s: %s.\n",
@@ -635,6 +656,14 @@ clar_test_shutdown(void)
656 }
657
658 for (report = _clar.reports; report; report = report_next) {
659 + struct clar_error *error, *error_next;
660 +
661 + for (error = report->errors; error; error = error_next) {
662 + free(error->description);
663 + error_next = error->next;
664 + free(error);
665 + }
666 +
667 report_next = report->next;
668 free(report);
669 }
@@ -660,7 +689,7 @@ static void abort_test(void)
689 clar_print_onabort(
690 "Fatal error: a cleanup method raised an exception.\n");
691 clar_report_errors(_clar.last_report);
663 - exit(-1);
692 + exit(1);
693 }
694
695 CL_TRACE(CL_TRACE__TEST__LONGJMP);
@@ -695,9 +724,9 @@ void clar__fail(
724
725 _clar.last_report->last_error = error;
726
698 - error->file = file;
699 - error->function = function;
700 - error->line_number = line;
727 + error->file = _clar.invoke_file ? _clar.invoke_file : file;
728 + error->function = _clar.invoke_func ? _clar.invoke_func : function;
729 + error->line_number = _clar.invoke_line ? _clar.invoke_line : line;
730 error->error_msg = error_msg;
731
732 if (description != NULL &&
@@ -754,7 +783,12 @@ void clar__assert_equal(
783 p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
784 s1, s2, pos);
785 } else {
757 - p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
786 + const char *q1 = s1 ? "'" : "";
787 + const char *q2 = s2 ? "'" : "";
788 + s1 = s1 ? s1 : "NULL";
789 + s2 = s2 ? s2 : "NULL";
790 + p_snprintf(buf, sizeof(buf), "%s%s%s != %s%s%s",
791 + q1, s1, q1, q2, s2, q2);
792 }
793 }
794 }
@@ -767,12 +801,17 @@ void clar__assert_equal(
801 if (!is_equal) {
802 if (s1 && s2) {
803 int pos;
770 - for (pos = 0; s1[pos] == s2[pos] && pos < len; ++pos)
804 + for (pos = 0; pos < len && s1[pos] == s2[pos]; ++pos)
805 /* find differing byte offset */;
806 p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s' (at byte %d)",
807 len, s1, len, s2, pos);
808 } else {
775 - p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'", len, s1, len, s2);
809 + const char *q1 = s1 ? "'" : "";
810 + const char *q2 = s2 ? "'" : "";
811 + s1 = s1 ? s1 : "NULL";
812 + s2 = s2 ? s2 : "NULL";
813 + p_snprintf(buf, sizeof(buf), "%s%.*s%s != %s%.*s%s",
814 + q1, len, s1, q1, q2, len, s2, q2);
815 }
816 }
817 }
@@ -790,7 +829,12 @@ void clar__assert_equal(
829 p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)",
830 wcs1, wcs2, pos);
831 } else {
793 - p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2);
832 + const char *q1 = wcs1 ? "'" : "";
833 + const char *q2 = wcs2 ? "'" : "";
834 + wcs1 = wcs1 ? wcs1 : L"NULL";
835 + wcs2 = wcs2 ? wcs2 : L"NULL";
836 + p_snprintf(buf, sizeof(buf), "%s%ls%s != %s%ls%s",
837 + q1, wcs1, q1, q2, wcs2, q2);
838 }
839 }
840 }
@@ -803,12 +847,17 @@ void clar__assert_equal(
847 if (!is_equal) {
848 if (wcs1 && wcs2) {
849 int pos;
806 - for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos)
850 + for (pos = 0; pos < len && wcs1[pos] == wcs2[pos]; ++pos)
851 /* find differing byte offset */;
852 p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)",
853 len, wcs1, len, wcs2, pos);
854 } else {
811 - p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2);
855 + const char *q1 = wcs1 ? "'" : "";
856 + const char *q2 = wcs2 ? "'" : "";
857 + wcs1 = wcs1 ? wcs1 : L"NULL";
858 + wcs2 = wcs2 ? wcs2 : L"NULL";
859 + p_snprintf(buf, sizeof(buf), "%s%.*ls%s != %s%.*ls%s",
860 + q1, len, wcs1, q1, q2, len, wcs2, q2);
861 }
862 }
863 }
@@ -826,7 +875,8 @@ void clar__assert_equal(
875 void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);
876 is_equal = (p1 == p2);
877 if (!is_equal)
829 - p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2);
878 + p_snprintf(buf, sizeof(buf), "0x%"PRIxPTR" != 0x%"PRIxPTR,
879 + (uintptr_t)p1, (uintptr_t)p2);
880 }
881 else {
882 int i1 = va_arg(args, int), i2 = va_arg(args, int);
@@ -850,6 +900,23 @@ void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
900 _clar.local_cleanup_payload = opaque;
901 }
902
903 +void clar__set_invokepoint(
904 + const char *file,
905 + const char *func,
906 + size_t line)
907 +{
908 + _clar.invoke_file = file;
909 + _clar.invoke_func = func;
910 + _clar.invoke_line = line;
911 +}
912 +
913 +void clar__clear_invokepoint(void)
914 +{
915 + _clar.invoke_file = NULL;
916 + _clar.invoke_func = NULL;
917 + _clar.invoke_line = 0;
918 +}
919 +
920 #include "clar/sandbox.h"
921 #include "clar/fixtures.h"
922 #include "clar/fs.h"
t/unit-tests/clar/clar.h
+62 -21
@@ -8,6 +8,25 @@
8 #define __CLAR_TEST_H__
9
10 #include <stdlib.h>
11 +#include <limits.h>
12 +
13 +#if defined(_WIN32) && defined(CLAR_WIN32_LONGPATHS)
14 +# define CLAR_MAX_PATH 4096
15 +#elif defined(_WIN32)
16 +# define CLAR_MAX_PATH MAX_PATH
17 +#else
18 +# define CLAR_MAX_PATH PATH_MAX
19 +#endif
20 +
21 +#ifndef CLAR_SELFTEST
22 +# define CLAR_CURRENT_FILE __FILE__
23 +# define CLAR_CURRENT_LINE __LINE__
24 +# define CLAR_CURRENT_FUNC __func__
25 +#else
26 +# define CLAR_CURRENT_FILE "file"
27 +# define CLAR_CURRENT_LINE 42
28 +# define CLAR_CURRENT_FUNC "func"
29 +#endif
30
31 enum cl_test_status {
32 CL_TEST_OK,
@@ -30,6 +49,7 @@ void clar_test_shutdown(void);
49 int clar_test(int argc, char *argv[]);
50
51 const char *clar_sandbox_path(void);
52 +const char *clar_tempdir_path(void);
53
54 void cl_set_cleanup(void (*cleanup)(void *), void *opaque);
55 void cl_fs_cleanup(void);
@@ -83,19 +103,33 @@ void cl_fixture_cleanup(const char *fixture_name);
103 const char *cl_fixture_basename(const char *fixture_name);
104 #endif
105
106 +/**
107 + * Invoke a helper function, which itself will use `cl_assert`
108 + * constructs. This will preserve the stack information of the
109 + * current call point, so that function name and line number
110 + * information is shown from the line of the test, instead of
111 + * the helper function.
112 + */
113 +#define cl_invoke(expr) \
114 + do { \
115 + clar__set_invokepoint(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE); \
116 + expr; \
117 + clar__clear_invokepoint(); \
118 + } while(0)
119 +
120 /**
121 * Assertion macros with explicit error message
122 */
89 -#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 1)
90 -#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 1)
91 -#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 1)
123 +#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 1)
124 +#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 1)
125 +#define cl_assert_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 1)
126
127 /**
128 * Check macros with explicit error message
129 */
96 -#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 0)
97 -#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 0)
98 -#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 0)
130 +#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 0)
131 +#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 0)
132 +#define cl_check_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 0)
133
134 /**
135 * Assertion macros with no error message
@@ -114,33 +148,33 @@ const char *cl_fixture_basename(const char *fixture_name);
148 /**
149 * Forced failure/warning
150 */
117 -#define cl_fail(desc) clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1)
118 -#define cl_warning(desc) clar__fail(__FILE__, __func__, __LINE__, "Warning during test execution:", desc, 0)
151 +#define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Test failed.", desc, 1)
152 +#define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Warning during test execution:", desc, 0)
153
154 #define cl_skip() clar__skip()
155
156 /**
157 * Typed assertion macros
158 */
125 -#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
126 -#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
159 +#define cl_assert_equal_s(s1,s2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
160 +#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
161
128 -#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
129 -#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
162 +#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
163 +#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
164
131 -#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
132 -#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
165 +#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
166 +#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
167
134 -#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
135 -#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
168 +#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
169 +#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
170
137 -#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
138 -#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
139 -#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
171 +#define cl_assert_equal_i(i1,i2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
172 +#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
173 +#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
174
141 -#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__func__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
175 +#define cl_assert_equal_b(b1,b2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
176
143 -#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__func__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
177 +#define cl_assert_equal_p(p1,p2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
178
179 void clar__skip(void);
180
@@ -170,4 +204,11 @@ void clar__assert_equal(
204 const char *fmt,
205 ...);
206
207 +void clar__set_invokepoint(
208 + const char *file,
209 + const char *func,
210 + size_t line);
211 +
212 +void clar__clear_invokepoint(void);
213 +
214 #endif
t/unit-tests/clar/clar/fixtures.h
+3 -3
@@ -2,7 +2,7 @@
2 static const char *
3 fixture_path(const char *base, const char *fixture_name)
4 {
5 - static char _path[4096];
5 + static char _path[CLAR_MAX_PATH];
6 size_t root_len;
7
8 root_len = strlen(base);
@@ -28,7 +28,7 @@ const char *cl_fixture(const char *fixture_name)
28
29 void cl_fixture_sandbox(const char *fixture_name)
30 {
31 - fs_copy(cl_fixture(fixture_name), _clar_path);
31 + fs_copy(cl_fixture(fixture_name), clar_sandbox_path());
32 }
33
34 const char *cl_fixture_basename(const char *fixture_name)
@@ -45,6 +45,6 @@ const char *cl_fixture_basename(const char *fixture_name)
45
46 void cl_fixture_cleanup(const char *fixture_name)
47 {
48 - fs_rm(fixture_path(_clar_path, cl_fixture_basename(fixture_name)));
48 + fs_rm(fixture_path(clar_sandbox_path(), cl_fixture_basename(fixture_name)));
49 }
50 #endif
t/unit-tests/clar/clar/fs.h
+14 -15
@@ -8,12 +8,6 @@
8
9 #ifdef _WIN32
10
11 -#ifdef CLAR_WIN32_LONGPATHS
12 -# define CLAR_MAX_PATH 4096
13 -#else
14 -# define CLAR_MAX_PATH MAX_PATH
15 -#endif
16 -
11 #define RM_RETRY_COUNT 5
12 #define RM_RETRY_DELAY 10
13
@@ -296,7 +290,7 @@ void
290 cl_fs_cleanup(void)
291 {
292 #ifdef CLAR_FIXTURE_PATH
299 - fs_rm(fixture_path(_clar_path, "*"));
293 + fs_rm(fixture_path(clar_tempdir_path(), "*"));
294 #else
295 ((void)fs_copy); /* unused */
296 #endif
@@ -371,17 +365,19 @@ static void
365 fs_copydir_helper(const char *source, const char *dest, int dest_mode)
366 {
367 DIR *source_dir;
374 - struct dirent *d;
368
369 mkdir(dest, dest_mode);
370
371 cl_assert_(source_dir = opendir(source), "Could not open source dir");
379 - for (;;) {
372 + while (1) {
373 + struct dirent *d;
374 char *child;
375
376 errno = 0;
383 - if ((d = readdir(source_dir)) == NULL)
377 + d = readdir(source_dir);
378 + if (!d)
379 break;
380 +
381 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
382 continue;
383
@@ -479,15 +475,18 @@ static void
475 fs_rmdir_helper(const char *path)
476 {
477 DIR *dir;
482 - struct dirent *d;
478
479 cl_assert_(dir = opendir(path), "Could not open dir");
485 - for (;;) {
480 +
481 + while (1) {
482 + struct dirent *d;
483 char *child;
484
485 errno = 0;
489 - if ((d = readdir(dir)) == NULL)
486 + d = readdir(dir);
487 + if (!d)
488 break;
489 +
490 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
491 continue;
492
@@ -524,7 +523,7 @@ fs_rm(const char *path)
523 void
524 cl_fs_cleanup(void)
525 {
527 - clar_unsandbox();
528 - clar_sandbox();
526 + clar_tempdir_shutdown();
527 + clar_tempdir_init();
528 }
529 #endif
t/unit-tests/clar/clar/print.h
+45 -15
@@ -3,6 +3,10 @@
3 static void clar_print_clap_init(int test_count, int suite_count, const char *suite_names)
4 {
5 (void)test_count;
6 +
7 + if (_clar.verbosity < 0)
8 + return;
9 +
10 printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
11 printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
12 }
@@ -13,10 +17,27 @@ static void clar_print_clap_shutdown(int test_count, int suite_count, int error_
17 (void)suite_count;
18 (void)error_count;
19
16 - printf("\n\n");
20 + if (_clar.verbosity >= 0)
21 + printf("\n\n");
22 clar_report_all();
23 }
24
25 +
26 +static void clar_print_indented(const char *str, int indent)
27 +{
28 + const char *bol, *eol;
29 +
30 + for (bol = str; *bol; bol = eol) {
31 + eol = strchr(bol, '\n');
32 + if (eol)
33 + eol++;
34 + else
35 + eol = bol + strlen(bol);
36 + printf("%*s%.*s", indent, "", (int)(eol - bol), bol);
37 + }
38 + putc('\n', stdout);
39 +}
40 +
41 static void clar_print_clap_error(int num, const struct clar_report *report, const struct clar_error *error)
42 {
43 printf(" %d) Failure:\n", num);
@@ -27,10 +48,10 @@ static void clar_print_clap_error(int num, const struct clar_report *report, con
48 error->file,
49 error->line_number);
50
30 - printf(" %s\n", error->error_msg);
51 + clar_print_indented(error->error_msg, 2);
52
53 if (error->description != NULL)
33 - printf(" %s\n", error->description);
54 + clar_print_indented(error->description, 2);
55
56 printf("\n");
57 fflush(stdout);
@@ -41,14 +62,17 @@ static void clar_print_clap_ontest(const char *suite_name, const char *test_name
62 (void)test_name;
63 (void)test_number;
64
65 + if (_clar.verbosity < 0)
66 + return;
67 +
68 if (_clar.verbosity > 1) {
69 printf("%s::%s: ", suite_name, test_name);
70
71 switch (status) {
72 case CL_TEST_OK: printf("ok\n"); break;
73 case CL_TEST_FAILURE: printf("fail\n"); break;
50 - case CL_TEST_SKIP: printf("skipped"); break;
51 - case CL_TEST_NOTRUN: printf("notrun"); break;
74 + case CL_TEST_SKIP: printf("skipped\n"); break;
75 + case CL_TEST_NOTRUN: printf("notrun\n"); break;
76 }
77 } else {
78 switch (status) {
@@ -64,6 +88,8 @@ static void clar_print_clap_ontest(const char *suite_name, const char *test_name
88
89 static void clar_print_clap_onsuite(const char *suite_name, int suite_index)
90 {
91 + if (_clar.verbosity < 0)
92 + return;
93 if (_clar.verbosity == 1)
94 printf("\n%s", suite_name);
95
@@ -127,18 +153,20 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,
153 case CL_TEST_FAILURE:
154 printf("not ok %d - %s::%s\n", test_number, suite_name, test_name);
155
130 - printf(" ---\n");
131 - printf(" reason: |\n");
132 - printf(" %s\n", error->error_msg);
156 + if (_clar.verbosity >= 0) {
157 + printf(" ---\n");
158 + printf(" reason: |\n");
159 + clar_print_indented(error->error_msg, 6);
160
134 - if (error->description)
135 - printf(" %s\n", error->description);
161 + if (error->description)
162 + clar_print_indented(error->description, 6);
163
137 - printf(" at:\n");
138 - printf(" file: '"); print_escaped(error->file); printf("'\n");
139 - printf(" line: %" PRIuMAX "\n", error->line_number);
140 - printf(" function: '%s'\n", error->function);
141 - printf(" ---\n");
164 + printf(" at:\n");
165 + printf(" file: '"); print_escaped(error->file); printf("'\n");
166 + printf(" line: %" PRIuMAX "\n", error->line_number);
167 + printf(" function: '%s'\n", error->function);
168 + printf(" ---\n");
169 + }
170
171 break;
172 case CL_TEST_SKIP:
@@ -152,6 +180,8 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,
180
181 static void clar_print_tap_onsuite(const char *suite_name, int suite_index)
182 {
183 + if (_clar.verbosity < 0)
184 + return;
185 printf("# start of suite %d: %s\n", suite_index, suite_name);
186 }
187
t/unit-tests/clar/clar/sandbox.h
+179 -47
@@ -2,7 +2,17 @@
2 #include <sys/syslimits.h>
3 #endif
4
5 -static char _clar_path[4096 + 1];
5 +/*
6 + * The tempdir is the temporary directory for the entirety of the clar
7 + * process execution. The sandbox is an individual temporary directory
8 + * for the execution of an individual test. Sandboxes are deleted
9 + * entirely after test execution to avoid pollution across tests.
10 + */
11 +
12 +static char _clar_tempdir[CLAR_MAX_PATH];
13 +static size_t _clar_tempdir_len;
14 +
15 +static char _clar_sandbox[CLAR_MAX_PATH];
16
17 static int
18 is_valid_tmp_path(const char *path)
@@ -15,7 +25,10 @@ is_valid_tmp_path(const char *path)
25 if (!S_ISDIR(st.st_mode))
26 return 0;
27
18 - return (access(path, W_OK) == 0);
28 + if (access(path, W_OK) != 0)
29 + return 0;
30 +
31 + return (strlen(path) < CLAR_MAX_PATH);
32 }
33
34 static int
@@ -31,14 +44,11 @@ find_tmp_path(char *buffer, size_t length)
44
45 for (i = 0; i < var_count; ++i) {
46 const char *env = getenv(env_vars[i]);
47 +
48 if (!env)
49 continue;
50
51 if (is_valid_tmp_path(env)) {
38 -#ifdef __APPLE__
39 - if (length >= PATH_MAX && realpath(env, buffer) != NULL)
40 - return 0;
41 -#endif
52 strncpy(buffer, env, length - 1);
53 buffer[length - 1] = '\0';
54 return 0;
@@ -47,21 +57,18 @@ find_tmp_path(char *buffer, size_t length)
57
58 /* If the environment doesn't say anything, try to use /tmp */
59 if (is_valid_tmp_path("/tmp")) {
50 -#ifdef __APPLE__
51 - if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
52 - return 0;
53 -#endif
60 strncpy(buffer, "/tmp", length - 1);
61 buffer[length - 1] = '\0';
62 return 0;
63 }
64
65 #else
60 - DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
61 - if (env_len > 0 && env_len < (DWORD)length)
66 + DWORD len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
67 + if (len > 0 && len < (DWORD)length)
68 return 0;
69
64 - if (GetTempPath((DWORD)length, buffer))
70 + len = GetTempPath((DWORD)length, buffer);
71 + if (len > 0 && len < (DWORD)length)
72 return 0;
73 #endif
74
@@ -75,17 +82,53 @@ find_tmp_path(char *buffer, size_t length)
82 return -1;
83 }
84
78 -static void clar_unsandbox(void)
85 +static int canonicalize_tmp_path(char *buffer)
86 +{
87 +#ifdef _WIN32
88 + char tmp[CLAR_MAX_PATH], *p;
89 + DWORD ret;
90 +
91 + ret = GetFullPathName(buffer, CLAR_MAX_PATH, tmp, NULL);
92 +
93 + if (ret == 0 || ret > CLAR_MAX_PATH)
94 + return -1;
95 +
96 + ret = GetLongPathName(tmp, buffer, CLAR_MAX_PATH);
97 +
98 + if (ret == 0 || ret > CLAR_MAX_PATH)
99 + return -1;
100 +
101 + /* normalize path to POSIX forward slashes */
102 + for (p = buffer; *p; p++)
103 + if (*p == '\\')
104 + *p = '/';
105 +
106 + return 0;
107 +#elif defined(CLAR_HAS_REALPATH)
108 + char tmp[CLAR_MAX_PATH];
109 +
110 + if (realpath(buffer, tmp) == NULL)
111 + return -1;
112 +
113 + strcpy(buffer, tmp);
114 + return 0;
115 +#else
116 + (void)buffer;
117 + return 0;
118 +#endif
119 +}
120 +
121 +static void clar_tempdir_shutdown(void)
122 {
80 - if (_clar_path[0] == '\0')
123 + if (_clar_tempdir[0] == '\0')
124 return;
125
126 cl_must_pass(chdir(".."));
127
85 - fs_rm(_clar_path);
128 + fs_rm(_clar_tempdir);
129 }
130
88 -static int build_sandbox_path(void)
131 +static int build_tempdir_path(void)
132 {
133 #ifdef CLAR_TMPDIR
134 const char path_tail[] = CLAR_TMPDIR "_XXXXXX";
@@ -95,64 +138,153 @@ static int build_sandbox_path(void)
138
139 size_t len;
140
98 - if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
141 + if (find_tmp_path(_clar_tempdir, sizeof(_clar_tempdir)) < 0 ||
142 + canonicalize_tmp_path(_clar_tempdir) < 0)
143 return -1;
144
101 - len = strlen(_clar_path);
145 + len = strlen(_clar_tempdir);
146
103 -#ifdef _WIN32
104 - { /* normalize path to POSIX forward slashes */
105 - size_t i;
106 - for (i = 0; i < len; ++i) {
107 - if (_clar_path[i] == '\\')
108 - _clar_path[i] = '/';
109 - }
110 - }
111 -#endif
147 + if (len + strlen(path_tail) + 2 > CLAR_MAX_PATH)
148 + return -1;
149
113 - if (_clar_path[len - 1] != '/') {
114 - _clar_path[len++] = '/';
115 - }
150 + if (_clar_tempdir[len - 1] != '/')
151 + _clar_tempdir[len++] = '/';
152
117 - strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
153 + strncpy(_clar_tempdir + len, path_tail, sizeof(_clar_tempdir) - len);
154
155 #if defined(__MINGW32__)
120 - if (_mktemp(_clar_path) == NULL)
156 + if (_mktemp(_clar_tempdir) == NULL)
157 return -1;
158
123 - if (mkdir(_clar_path, 0700) != 0)
159 + if (mkdir(_clar_tempdir, 0700) != 0)
160 return -1;
161 #elif defined(_WIN32)
126 - if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
162 + if (_mktemp_s(_clar_tempdir, sizeof(_clar_tempdir)) != 0)
163 return -1;
164
129 - if (mkdir(_clar_path, 0700) != 0)
165 + if (mkdir(_clar_tempdir, 0700) != 0)
166 return -1;
131 -#elif defined(__sun) || defined(__TANDEM)
132 - if (mktemp(_clar_path) == NULL)
167 +#elif defined(__sun) || defined(__TANDEM) || defined(__hpux)
168 + if (mktemp(_clar_tempdir) == NULL)
169 return -1;
170
135 - if (mkdir(_clar_path, 0700) != 0)
171 + if (mkdir(_clar_tempdir, 0700) != 0)
172 return -1;
173 #else
138 - if (mkdtemp(_clar_path) == NULL)
174 + if (mkdtemp(_clar_tempdir) == NULL)
175 return -1;
176 #endif
177
178 + _clar_tempdir_len = strlen(_clar_tempdir);
179 return 0;
180 }
181
145 -static void clar_sandbox(void)
182 +static void clar_tempdir_init(void)
183 {
147 - if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
148 - clar_abort("Failed to build sandbox path.\n");
184 + if (_clar_tempdir[0] == '\0' && build_tempdir_path() < 0)
185 + clar_abort("Failed to build tempdir path.\n");
186
150 - if (chdir(_clar_path) != 0)
151 - clar_abort("Failed to change into sandbox directory '%s': %s.\n",
152 - _clar_path, strerror(errno));
187 + if (chdir(_clar_tempdir) != 0)
188 + clar_abort("Failed to change into tempdir '%s': %s.\n",
189 + _clar_tempdir, strerror(errno));
190 +
191 +#if !defined(CLAR_SANDBOX_TEST_NAMES) && defined(_WIN32)
192 + srand(clock() ^ (unsigned int)time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId());
193 +#elif !defined(CLAR_SANDBOX_TEST_NAMES)
194 + srand(clock() ^ time(NULL) ^ ((unsigned)getpid() << 16));
195 +#endif
196 +}
197 +
198 +static void append(char *dst, const char *src)
199 +{
200 + char *d;
201 + const char *s;
202 +
203 + for (d = dst; *d; d++)
204 + ;
205 +
206 + for (s = src; *s; d++, s++)
207 + if (*s == ':')
208 + *d = '_';
209 + else
210 + *d = *s;
211 +
212 + *d = '\0';
213 +}
214 +
215 +static int clar_sandbox_create(const char *suite_name, const char *test_name)
216 +{
217 +#ifndef CLAR_SANDBOX_TEST_NAMES
218 + char alpha[] = "0123456789abcdef";
219 + int num = rand();
220 +#endif
221 +
222 + cl_assert(_clar_sandbox[0] == '\0');
223 +
224 + /*
225 + * We may want to use test names as sandbox directory names for
226 + * readability, _however_ on platforms with restrictions for short
227 + * file / folder names (eg, Windows), this may be too long.
228 + */
229 +#ifdef CLAR_SANDBOX_TEST_NAMES
230 + cl_assert(strlen(_clar_tempdir) + strlen(suite_name) + strlen(test_name) + 3 < CLAR_MAX_PATH);
231 +
232 + strcpy(_clar_sandbox, _clar_tempdir);
233 + _clar_sandbox[_clar_tempdir_len] = '/';
234 + _clar_sandbox[_clar_tempdir_len + 1] = '\0';
235 +
236 + append(_clar_sandbox, suite_name);
237 + append(_clar_sandbox, "__");
238 + append(_clar_sandbox, test_name);
239 +#else
240 + ((void)suite_name);
241 + ((void)test_name);
242 + ((void)append);
243 +
244 + cl_assert(strlen(_clar_tempdir) + 9 < CLAR_MAX_PATH);
245 +
246 + strcpy(_clar_sandbox, _clar_tempdir);
247 + _clar_sandbox[_clar_tempdir_len] = '/';
248 +
249 + _clar_sandbox[_clar_tempdir_len + 1] = alpha[(num & 0xf0000000) >> 28];
250 + _clar_sandbox[_clar_tempdir_len + 2] = alpha[(num & 0x0f000000) >> 24];
251 + _clar_sandbox[_clar_tempdir_len + 3] = alpha[(num & 0x00f00000) >> 20];
252 + _clar_sandbox[_clar_tempdir_len + 4] = alpha[(num & 0x000f0000) >> 16];
253 + _clar_sandbox[_clar_tempdir_len + 5] = alpha[(num & 0x0000f000) >> 12];
254 + _clar_sandbox[_clar_tempdir_len + 6] = alpha[(num & 0x00000f00) >> 8];
255 + _clar_sandbox[_clar_tempdir_len + 7] = alpha[(num & 0x000000f0) >> 4];
256 + _clar_sandbox[_clar_tempdir_len + 8] = alpha[(num & 0x0000000f) >> 0];
257 + _clar_sandbox[_clar_tempdir_len + 9] = '\0';
258 +#endif
259 +
260 + if (mkdir(_clar_sandbox, 0700) != 0)
261 + return -1;
262 +
263 + if (chdir(_clar_sandbox) != 0)
264 + return -1;
265 +
266 + return 0;
267 +}
268 +
269 +static int clar_sandbox_cleanup(void)
270 +{
271 + cl_assert(_clar_sandbox[0] != '\0');
272 +
273 + if (chdir(_clar_tempdir) != 0)
274 + return -1;
275 +
276 + fs_rm(_clar_sandbox);
277 + _clar_sandbox[0] = '\0';
278 +
279 + return 0;
280 +}
281 +
282 +const char *clar_tempdir_path(void)
283 +{
284 + return _clar_tempdir;
285 }
286
287 const char *clar_sandbox_path(void)
288 {
157 - return _clar_path;
289 + return _clar_sandbox;
290 }
t/unit-tests/clar/clar/summary.h
+3 -2
@@ -23,10 +23,11 @@ static int clar_summary_testsuite(struct clar_summary *summary,
23 int idn, const char *name, time_t timestamp,
24 int test_count, int fail_count, int error_count)
25 {
26 - struct tm *tm = localtime(&timestamp);
26 + struct tm tm;
27 char iso_dt[20];
28
29 - if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", tm) == 0)
29 + localtime_r(&timestamp, &tm);
30 + if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", &tm) == 0)
31 return -1;
32
33 return fprintf(summary->fp, "\t<testsuite"
t/unit-tests/clar/example/CMakeLists.txt new
+28
@@ -0,0 +1,28 @@
1 +find_package(Python COMPONENTS Interpreter REQUIRED)
2 +
3 +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
4 + COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}"
5 + DEPENDS main.c example.c
6 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
7 +)
8 +
9 +add_executable(example)
10 +set_target_properties(example PROPERTIES
11 + C_STANDARD 90
12 + C_STANDARD_REQUIRED ON
13 + C_EXTENSIONS OFF
14 +)
15 +target_sources(example PRIVATE
16 + main.c
17 + example.c
18 + "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
19 +)
20 +target_compile_definitions(example PRIVATE)
21 +target_compile_options(example PRIVATE
22 + $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
23 +)
24 +target_include_directories(example PRIVATE
25 + "${CMAKE_SOURCE_DIR}"
26 + "${CMAKE_CURRENT_BINARY_DIR}"
27 +)
28 +target_link_libraries(example clar)
t/unit-tests/clar/example/example.c new
+6
@@ -0,0 +1,6 @@
1 +#include "clar.h"
2 +
3 +void test_example__simple_assert(void)
4 +{
5 + cl_assert_equal_i(1, 1);
6 +}
t/unit-tests/clar/example/main.c renamed
+1 -1
@@ -5,7 +5,7 @@
5 * For full terms see the included COPYING file.
6 */
7
8 -#include "clar_test.h"
8 +#include "clar.h"
9
10 /*
11 * Minimal main() for clar tests.
t/unit-tests/clar/test/CMakeLists.txt
+27 -12
@@ -1,13 +1,15 @@
1 +add_subdirectory(selftest_suite)
2 +
3 find_package(Python COMPONENTS Interpreter REQUIRED)
4
5 add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
6 COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}"
5 - DEPENDS main.c sample.c clar_test.h
7 + DEPENDS main.c selftest.c
8 WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
9 )
10
9 -add_executable(clar_test)
10 -set_target_properties(clar_test PROPERTIES
11 +add_executable(selftest)
12 +set_target_properties(selftest PROPERTIES
13 C_STANDARD 90
14 C_STANDARD_REQUIRED ON
15 C_EXTENSIONS OFF
@@ -15,25 +17,38 @@ set_target_properties(clar_test PROPERTIES
17
18 # MSVC generates all kinds of warnings. We may want to fix these in the future
19 # and then unconditionally treat warnings as errors.
18 -if(NOT MSVC)
19 - set_target_properties(clar_test PROPERTIES
20 +if (NOT MSVC)
21 + set_target_properties(selftest PROPERTIES
22 COMPILE_WARNING_AS_ERROR ON
23 )
24 endif()
25
24 -target_sources(clar_test PRIVATE
26 +target_sources(selftest PRIVATE
27 main.c
26 - sample.c
28 + selftest.c
29 "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
30 )
29 -target_compile_definitions(clar_test PRIVATE
30 - CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/"
31 +target_compile_definitions(selftest PRIVATE
32 + CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/expected/"
33 )
32 -target_compile_options(clar_test PRIVATE
34 +target_compile_options(selftest PRIVATE
35 $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
36 )
35 -target_include_directories(clar_test PRIVATE
37 +target_include_directories(selftest PRIVATE
38 "${CMAKE_SOURCE_DIR}"
39 "${CMAKE_CURRENT_BINARY_DIR}"
40 )
39 -target_link_libraries(clar_test clar)
41 +target_link_libraries(selftest clar)
42 +
43 +add_test(NAME build_selftest_suite
44 + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest_suite
45 +)
46 +set_tests_properties(build_selftest_suite PROPERTIES FIXTURES_SETUP clar_test_fixture)
47 +
48 +add_test(NAME build_selftest
49 + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest
50 +)
51 +set_tests_properties(build_selftest PROPERTIES FIXTURES_SETUP clar_test_fixture)
52 +
53 +add_test(NAME selftest COMMAND "${CMAKE_CURRENT_BINARY_DIR}/selftest" "$<TARGET_FILE:selftest_suite>")
54 +set_tests_properties(selftest PROPERTIES FIXTURES_REQUIRED clar_test_fixture)
t/unit-tests/clar/test/clar_test.h deleted
-16
@@ -1,16 +0,0 @@
1 -/*
2 - * Copyright (c) Vicent Marti. All rights reserved.
3 - *
4 - * This file is part of clar, distributed under the ISC license.
5 - * For full terms see the included COPYING file.
6 - */
7 -#ifndef __CLAR_TEST__
8 -#define __CLAR_TEST__
9 -
10 -/* Import the standard clar helper functions */
11 -#include "clar.h"
12 -
13 -/* Your custom shared includes / defines here */
14 -extern int global_test_counter;
15 -
16 -#endif
t/unit-tests/clar/test/expected/help new
+12
@@ -0,0 +1,12 @@
1 +Usage: selftest [options]
2 +
3 +Options:
4 + -sname Run only the suite with `name` (can go to individual test name)
5 + -iname Include the suite with `name`
6 + -xname Exclude the suite with `name`
7 + -v Increase verbosity (show suite names)
8 + -q Decrease verbosity, inverse to -v
9 + -Q Quit as soon as a test fails
10 + -t Display results in tap format
11 + -l Print suite names
12 + -r[filename] Write summary file (to the optional filename)
t/unit-tests/clar/test/expected/quiet new
+49
@@ -0,0 +1,49 @@
1 + 1) Failure:
2 +selftest::suite::1 [file:42]
3 + Function call failed: -1
4 +
5 + 2) Failure:
6 +selftest::suite::2 [file:42]
7 + Expression is not true: 100 == 101
8 +
9 + 3) Failure:
10 +selftest::suite::strings [file:42]
11 + String mismatch: "mismatched" != actual ("this one fails")
12 + 'mismatched' != 'expected' (at byte 0)
13 +
14 + 4) Failure:
15 +selftest::suite::strings_with_length [file:42]
16 + String mismatch: "exactly" != actual ("this one fails")
17 + 'exa' != 'exp' (at byte 2)
18 +
19 + 5) Failure:
20 +selftest::suite::int [file:42]
21 + 101 != value ("extra note on failing test")
22 + 101 != 100
23 +
24 + 6) Failure:
25 +selftest::suite::int_fmt [file:42]
26 + 022 != value
27 + 0022 != 0144
28 +
29 + 7) Failure:
30 +selftest::suite::bool [file:42]
31 + 0 != value
32 + 0 != 1
33 +
34 + 8) Failure:
35 +selftest::suite::ptr [file:42]
36 + Pointer mismatch: p1 != p2
37 + 0x1 != 0x2
38 +
39 + 9) Failure:
40 +selftest::suite::multiline_description [file:42]
41 + Function call failed: -1
42 + description line 1
43 + description line 2
44 +
45 + 10) Failure:
46 +selftest::suite::null_string [file:42]
47 + String mismatch: "expected" != actual ("this one fails")
48 + 'expected' != NULL
49 +
t/unit-tests/clar/test/expected/specific_test new
+9
@@ -0,0 +1,9 @@
1 +Loaded 1 suites:
2 +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 +F
4 +
5 + 1) Failure:
6 +selftest::suite::bool [file:42]
7 + 0 != value
8 + 0 != 1
9 +
t/unit-tests/clar/test/expected/stop_on_failure new
+8
@@ -0,0 +1,8 @@
1 +Loaded 1 suites:
2 +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 +F
4 +
5 + 1) Failure:
6 +selftest::suite::1 [file:42]
7 + Function call failed: -1
8 +
t/unit-tests/clar/test/expected/suite_names new
+2
@@ -0,0 +1,2 @@
1 +Test suites (use -s<name> to run just one):
2 + 0: selftest::suite
t/unit-tests/clar/test/expected/summary.xml new
+45
@@ -0,0 +1,45 @@
1 +<testsuites>
2 + <testsuite id="0" name="selftest" hostname="localhost" timestamp="2024-09-06T10:04:08" tests="8" failures="8" errors="0">
3 + <testcase name="1" classname="selftest" time="0.00">
4 + <failure type="assert"><![CDATA[Function call failed: -1
5 +(null)]]></failure>
6 + </testcase>
7 + <testcase name="2" classname="selftest" time="0.00">
8 + <failure type="assert"><![CDATA[Expression is not true: 100 == 101
9 +(null)]]></failure>
10 + </testcase>
11 + <testcase name="strings" classname="selftest" time="0.00">
12 + <failure type="assert"><![CDATA[String mismatch: "mismatched" != actual ("this one fails")
13 +'mismatched' != 'expected' (at byte 0)]]></failure>
14 + </testcase>
15 + <testcase name="strings_with_length" classname="selftest" time="0.00">
16 + <failure type="assert"><![CDATA[String mismatch: "exactly" != actual ("this one fails")
17 +'exa' != 'exp' (at byte 2)]]></failure>
18 + </testcase>
19 + <testcase name="int" classname="selftest" time="0.00">
20 + <failure type="assert"><![CDATA[101 != value ("extra note on failing test")
21 +101 != 100]]></failure>
22 + </testcase>
23 + <testcase name="int_fmt" classname="selftest" time="0.00">
24 + <failure type="assert"><![CDATA[022 != value
25 +0022 != 0144]]></failure>
26 + </testcase>
27 + <testcase name="bool" classname="selftest" time="0.00">
28 + <failure type="assert"><![CDATA[0 != value
29 +0 != 1]]></failure>
30 + </testcase>
31 + <testcase name="ptr" classname="selftest" time="0.00">
32 + <failure type="assert"><![CDATA[Pointer mismatch: p1 != p2
33 +0x1 != 0x2]]></failure>
34 + </testcase>
35 + <testcase name="multiline_description" classname="selftest" time="0.00">
36 + <failure type="assert"><![CDATA[Function call failed: −1
37 +description line 1
38 +description line 2]]></failure>
39 + </testcase>
40 + <testcase name="null_string" classname="selftest" time="0.00">
41 + <failure type="assert"><![CDATA[String mismatch: "expected" != actual ("this one fails")
42 +'expected' != NULL]]></failure>
43 + </testcase>
44 + </testsuite>
45 +</testsuites>
t/unit-tests/clar/test/expected/summary_with_filename new
+54
@@ -0,0 +1,54 @@
1 +Loaded 1 suites:
2 +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 +FFFFFFFFFF
4 +
5 + 1) Failure:
6 +selftest::suite::1 [file:42]
7 + Function call failed: -1
8 +
9 + 2) Failure:
10 +selftest::suite::2 [file:42]
11 + Expression is not true: 100 == 101
12 +
13 + 3) Failure:
14 +selftest::suite::strings [file:42]
15 + String mismatch: "mismatched" != actual ("this one fails")
16 + 'mismatched' != 'expected' (at byte 0)
17 +
18 + 4) Failure:
19 +selftest::suite::strings_with_length [file:42]
20 + String mismatch: "exactly" != actual ("this one fails")
21 + 'exa' != 'exp' (at byte 2)
22 +
23 + 5) Failure:
24 +selftest::suite::int [file:42]
25 + 101 != value ("extra note on failing test")
26 + 101 != 100
27 +
28 + 6) Failure:
29 +selftest::suite::int_fmt [file:42]
30 + 022 != value
31 + 0022 != 0144
32 +
33 + 7) Failure:
34 +selftest::suite::bool [file:42]
35 + 0 != value
36 + 0 != 1
37 +
38 + 8) Failure:
39 +selftest::suite::ptr [file:42]
40 + Pointer mismatch: p1 != p2
41 + 0x1 != 0x2
42 +
43 + 9) Failure:
44 +selftest::suite::multiline_description [file:42]
45 + Function call failed: -1
46 + description line 1
47 + description line 2
48 +
49 + 10) Failure:
50 +selftest::suite::null_string [file:42]
51 + String mismatch: "expected" != actual ("this one fails")
52 + 'expected' != NULL
53 +
54 +written summary file to different.xml
t/unit-tests/clar/test/expected/summary_without_filename new
+54
@@ -0,0 +1,54 @@
1 +Loaded 1 suites:
2 +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 +FFFFFFFFFF
4 +
5 + 1) Failure:
6 +selftest::suite::1 [file:42]
7 + Function call failed: -1
8 +
9 + 2) Failure:
10 +selftest::suite::2 [file:42]
11 + Expression is not true: 100 == 101
12 +
13 + 3) Failure:
14 +selftest::suite::strings [file:42]
15 + String mismatch: "mismatched" != actual ("this one fails")
16 + 'mismatched' != 'expected' (at byte 0)
17 +
18 + 4) Failure:
19 +selftest::suite::strings_with_length [file:42]
20 + String mismatch: "exactly" != actual ("this one fails")
21 + 'exa' != 'exp' (at byte 2)
22 +
23 + 5) Failure:
24 +selftest::suite::int [file:42]
25 + 101 != value ("extra note on failing test")
26 + 101 != 100
27 +
28 + 6) Failure:
29 +selftest::suite::int_fmt [file:42]
30 + 022 != value
31 + 0022 != 0144
32 +
33 + 7) Failure:
34 +selftest::suite::bool [file:42]
35 + 0 != value
36 + 0 != 1
37 +
38 + 8) Failure:
39 +selftest::suite::ptr [file:42]
40 + Pointer mismatch: p1 != p2
41 + 0x1 != 0x2
42 +
43 + 9) Failure:
44 +selftest::suite::multiline_description [file:42]
45 + Function call failed: -1
46 + description line 1
47 + description line 2
48 +
49 + 10) Failure:
50 +selftest::suite::null_string [file:42]
51 + String mismatch: "expected" != actual ("this one fails")
52 + 'expected' != NULL
53 +
54 +written summary file to summary.xml
t/unit-tests/clar/test/expected/tap new
+102
@@ -0,0 +1,102 @@
1 +TAP version 13
2 +# start of suite 1: selftest::suite
3 +not ok 1 - selftest::suite::1
4 + ---
5 + reason: |
6 + Function call failed: -1
7 + at:
8 + file: 'file'
9 + line: 42
10 + function: 'func'
11 + ---
12 +not ok 2 - selftest::suite::2
13 + ---
14 + reason: |
15 + Expression is not true: 100 == 101
16 + at:
17 + file: 'file'
18 + line: 42
19 + function: 'func'
20 + ---
21 +not ok 3 - selftest::suite::strings
22 + ---
23 + reason: |
24 + String mismatch: "mismatched" != actual ("this one fails")
25 + 'mismatched' != 'expected' (at byte 0)
26 + at:
27 + file: 'file'
28 + line: 42
29 + function: 'func'
30 + ---
31 +not ok 4 - selftest::suite::strings_with_length
32 + ---
33 + reason: |
34 + String mismatch: "exactly" != actual ("this one fails")
35 + 'exa' != 'exp' (at byte 2)
36 + at:
37 + file: 'file'
38 + line: 42
39 + function: 'func'
40 + ---
41 +not ok 5 - selftest::suite::int
42 + ---
43 + reason: |
44 + 101 != value ("extra note on failing test")
45 + 101 != 100
46 + at:
47 + file: 'file'
48 + line: 42
49 + function: 'func'
50 + ---
51 +not ok 6 - selftest::suite::int_fmt
52 + ---
53 + reason: |
54 + 022 != value
55 + 0022 != 0144
56 + at:
57 + file: 'file'
58 + line: 42
59 + function: 'func'
60 + ---
61 +not ok 7 - selftest::suite::bool
62 + ---
63 + reason: |
64 + 0 != value
65 + 0 != 1
66 + at:
67 + file: 'file'
68 + line: 42
69 + function: 'func'
70 + ---
71 +not ok 8 - selftest::suite::ptr
72 + ---
73 + reason: |
74 + Pointer mismatch: p1 != p2
75 + 0x1 != 0x2
76 + at:
77 + file: 'file'
78 + line: 42
79 + function: 'func'
80 + ---
81 +not ok 9 - selftest::suite::multiline_description
82 + ---
83 + reason: |
84 + Function call failed: -1
85 + description line 1
86 + description line 2
87 + at:
88 + file: 'file'
89 + line: 42
90 + function: 'func'
91 + ---
92 +not ok 10 - selftest::suite::null_string
93 + ---
94 + reason: |
95 + String mismatch: "expected" != actual ("this one fails")
96 + 'expected' != NULL
97 + at:
98 + file: 'file'
99 + line: 42
100 + function: 'func'
101 + ---
102 +1..10
t/unit-tests/clar/test/expected/without_arguments new
+53
@@ -0,0 +1,53 @@
1 +Loaded 1 suites:
2 +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 +FFFFFFFFFF
4 +
5 + 1) Failure:
6 +selftest::suite::1 [file:42]
7 + Function call failed: -1
8 +
9 + 2) Failure:
10 +selftest::suite::2 [file:42]
11 + Expression is not true: 100 == 101
12 +
13 + 3) Failure:
14 +selftest::suite::strings [file:42]
15 + String mismatch: "mismatched" != actual ("this one fails")
16 + 'mismatched' != 'expected' (at byte 0)
17 +
18 + 4) Failure:
19 +selftest::suite::strings_with_length [file:42]
20 + String mismatch: "exactly" != actual ("this one fails")
21 + 'exa' != 'exp' (at byte 2)
22 +
23 + 5) Failure:
24 +selftest::suite::int [file:42]
25 + 101 != value ("extra note on failing test")
26 + 101 != 100
27 +
28 + 6) Failure:
29 +selftest::suite::int_fmt [file:42]
30 + 022 != value
31 + 0022 != 0144
32 +
33 + 7) Failure:
34 +selftest::suite::bool [file:42]
35 + 0 != value
36 + 0 != 1
37 +
38 + 8) Failure:
39 +selftest::suite::ptr [file:42]
40 + Pointer mismatch: p1 != p2
41 + 0x1 != 0x2
42 +
43 + 9) Failure:
44 +selftest::suite::multiline_description [file:42]
45 + Function call failed: -1
46 + description line 1
47 + description line 2
48 +
49 + 10) Failure:
50 +selftest::suite::null_string [file:42]
51 + String mismatch: "expected" != actual ("this one fails")
52 + 'expected' != NULL
53 +
t/unit-tests/clar/test/main.c
+13 -28
@@ -1,23 +1,9 @@
1 -/*
2 - * Copyright (c) Vicent Marti. All rights reserved.
3 - *
4 - * This file is part of clar, distributed under the ISC license.
5 - * For full terms see the included COPYING file.
6 - */
1 +#include <stdio.h>
2 +#include <string.h>
3
8 -#include "clar_test.h"
4 +#include "selftest.h"
5
10 -/*
11 - * Sample main() for clar tests.
12 - *
13 - * You should write your own main routine for clar tests that does specific
14 - * setup and teardown as necessary for your application. The only required
15 - * line is the call to `clar_test(argc, argv)`, which will execute the test
16 - * suite. If you want to check the return value of the test application,
17 - * your main() should return the same value returned by clar_test().
18 - */
19 -
20 -int global_test_counter = 0;
6 +const char *selftest_binary_path;
7
8 #ifdef _WIN32
9 int __cdecl main(int argc, char *argv[])
@@ -25,16 +11,15 @@ int __cdecl main(int argc, char *argv[])
11 int main(int argc, char *argv[])
12 #endif
13 {
28 - int ret;
29 -
30 - /* Your custom initialization here */
31 - global_test_counter = 0;
32 -
33 - /* Run the test suite */
34 - ret = clar_test(argc, argv);
14 + if (argc < 2) {
15 + fprintf(stderr, "usage: %s <selftest-suite-executable> <options>\n",
16 + argv[0]);
17 + exit(1);
18 + }
19
36 - /* Your custom cleanup here */
37 - cl_assert_equal_i(8, global_test_counter);
20 + selftest_binary_path = argv[1];
21 + memmove(argv + 1, argv + 2, argc - 1);
22 + argc -= 1;
23
39 - return ret;
24 + return clar_test(argc, argv);
25 }
t/unit-tests/clar/test/selftest.c new
+289
@@ -0,0 +1,289 @@
1 +#include <stdarg.h>
2 +#include <stdio.h>
3 +#include <string.h>
4 +#include <sys/stat.h>
5 +
6 +#include "selftest.h"
7 +
8 +#ifdef _WIN32
9 +# define WIN32_LEAN_AND_MEAN
10 +# include <windows.h>
11 +
12 +static char *read_full(HANDLE h, int is_pipe)
13 +{
14 + char *data = NULL;
15 + size_t data_size = 0;
16 +
17 + while (1) {
18 + CHAR buf[4096];
19 + DWORD bytes_read;
20 +
21 + if (!ReadFile(h, buf, sizeof(buf), &bytes_read, NULL)) {
22 + if (!is_pipe)
23 + cl_fail("Failed reading file handle.");
24 + cl_assert_equal_i(GetLastError(), ERROR_BROKEN_PIPE);
25 + break;
26 + }
27 + if (!bytes_read)
28 + break;
29 +
30 + data = realloc(data, data_size + bytes_read);
31 + cl_assert(data);
32 + memcpy(data + data_size, buf, bytes_read);
33 + data_size += bytes_read;
34 + }
35 +
36 + data = realloc(data, data_size + 1);
37 + cl_assert(data);
38 + data[data_size] = '\0';
39 +
40 + while (strstr(data, "\r\n")) {
41 + char *ptr = strstr(data, "\r\n");
42 + memmove(ptr, ptr + 1, strlen(ptr));
43 + }
44 +
45 + return data;
46 +}
47 +
48 +static char *read_file(const char *path)
49 +{
50 + char *content;
51 + HANDLE file;
52 +
53 + file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
54 + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
55 + cl_assert(file != INVALID_HANDLE_VALUE);
56 + content = read_full(file, 0);
57 + cl_assert_equal_b(1, CloseHandle(file));
58 +
59 + return content;
60 +}
61 +
62 +static void run(const char *expected_output_file, int expected_error_code, ...)
63 +{
64 + SECURITY_ATTRIBUTES security_attributes = { 0 };
65 + PROCESS_INFORMATION process_info = { 0 };
66 + STARTUPINFO startup_info = { 0 };
67 + char cmdline[4096] = { 0 };
68 + char *expected_output = NULL;
69 + char *output = NULL;
70 + HANDLE stdout_write;
71 + HANDLE stdout_read;
72 + DWORD exit_code;
73 + va_list ap;
74 +
75 + /*
76 + * Assemble command line arguments. In theory we'd have to properly
77 + * quote them. In practice none of our tests actually care.
78 + */
79 + va_start(ap, expected_error_code);
80 + snprintf(cmdline, sizeof(cmdline), "selftest");
81 + while (1) {
82 + size_t cmdline_len = strlen(cmdline);
83 + const char *arg;
84 +
85 + arg = va_arg(ap, const char *);
86 + if (!arg)
87 + break;
88 +
89 + cl_assert(cmdline_len + strlen(arg) < sizeof(cmdline));
90 + snprintf(cmdline + cmdline_len, sizeof(cmdline) - cmdline_len,
91 + " %s", arg);
92 + }
93 + va_end(ap);
94 +
95 + /*
96 + * Create a pipe that we will use to read data from the child process.
97 + * The writing side needs to be inheritable such that the child can use
98 + * it as stdout and stderr. The reading side should only be used by the
99 + * parent.
100 + */
101 + security_attributes.nLength = sizeof(security_attributes);
102 + security_attributes.bInheritHandle = TRUE;
103 + cl_assert_equal_b(1, CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0));
104 + cl_assert_equal_b(1, SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0));
105 +
106 + /*
107 + * Create the child process with our pipe.
108 + */
109 + startup_info.cb = sizeof(startup_info);
110 + startup_info.hStdError = stdout_write;
111 + startup_info.hStdOutput = stdout_write;
112 + startup_info.dwFlags |= STARTF_USESTDHANDLES;
113 + cl_assert_equal_b(1, CreateProcess(selftest_binary_path, cmdline, NULL, NULL, TRUE,
114 + 0, NULL, NULL, &startup_info, &process_info));
115 + cl_assert_equal_b(1, CloseHandle(stdout_write));
116 +
117 + output = read_full(stdout_read, 1);
118 + cl_assert_equal_b(1, CloseHandle(stdout_read));
119 + cl_assert_equal_b(1, GetExitCodeProcess(process_info.hProcess, &exit_code));
120 +
121 + expected_output = read_file(cl_fixture(expected_output_file));
122 + cl_assert_equal_s(output, expected_output);
123 + cl_assert_equal_i(exit_code, expected_error_code);
124 +
125 + free(expected_output);
126 + free(output);
127 +}
128 +
129 +#else
130 +# include <errno.h>
131 +# include <fcntl.h>
132 +# include <limits.h>
133 +# include <unistd.h>
134 +# include <sys/wait.h>
135 +
136 +static char *read_full(int fd)
137 +{
138 + size_t data_bytes = 0;
139 + char *data = NULL;
140 +
141 + while (1) {
142 + char buf[4096];
143 + ssize_t n;
144 +
145 + n = read(fd, buf, sizeof(buf));
146 + if (n < 0) {
147 + if (errno == EAGAIN || errno == EINTR)
148 + continue;
149 + cl_fail("Failed reading from child process.");
150 + }
151 + if (!n)
152 + break;
153 +
154 + data = realloc(data, data_bytes + n);
155 + cl_assert(data);
156 +
157 + memcpy(data + data_bytes, buf, n);
158 + data_bytes += n;
159 + }
160 +
161 + data = realloc(data, data_bytes + 1);
162 + cl_assert(data);
163 + data[data_bytes] = '\0';
164 +
165 + return data;
166 +}
167 +
168 +static char *read_file(const char *path)
169 +{
170 + char *data;
171 + int fd;
172 +
173 + fd = open(path, O_RDONLY);
174 + if (fd < 0)
175 + cl_fail("Failed reading expected file.");
176 +
177 + data = read_full(fd);
178 + cl_must_pass(close(fd));
179 +
180 + return data;
181 +}
182 +
183 +static void run(const char *expected_output_file, int expected_error_code, ...)
184 +{
185 + const char *argv[16];
186 + int pipe_fds[2];
187 + va_list ap;
188 + pid_t pid;
189 + int i;
190 +
191 + va_start(ap, expected_error_code);
192 + argv[0] = "selftest";
193 + for (i = 1; ; i++) {
194 + cl_assert(i < sizeof(argv) / sizeof(*argv));
195 +
196 + argv[i] = va_arg(ap, const char *);
197 + if (!argv[i])
198 + break;
199 + }
200 + va_end(ap);
201 +
202 + cl_must_pass(pipe(pipe_fds));
203 +
204 + pid = fork();
205 + if (!pid) {
206 + if (dup2(pipe_fds[1], STDOUT_FILENO) < 0 ||
207 + dup2(pipe_fds[1], STDERR_FILENO) < 0 ||
208 + close(0) < 0 ||
209 + close(pipe_fds[0]) < 0 ||
210 + close(pipe_fds[1]) < 0)
211 + exit(1);
212 +
213 + execv(selftest_binary_path, (char **) argv);
214 + exit(1);
215 + } else if (pid > 0) {
216 + pid_t waited_pid;
217 + char *expected_output, *output;
218 + int stat;
219 +
220 + cl_must_pass(close(pipe_fds[1]));
221 +
222 + output = read_full(pipe_fds[0]);
223 +
224 + waited_pid = waitpid(pid, &stat, 0);
225 + cl_assert_equal_i(pid, waited_pid);
226 + cl_assert(WIFEXITED(stat));
227 + cl_assert_equal_i(WEXITSTATUS(stat), expected_error_code);
228 +
229 + expected_output = read_file(cl_fixture(expected_output_file));
230 + cl_assert_equal_s(output, expected_output);
231 +
232 + free(expected_output);
233 + free(output);
234 + } else {
235 + cl_fail("Fork failed.");
236 + }
237 +}
238 +#endif
239 +
240 +void test_selftest__help(void)
241 +{
242 + cl_invoke(run("help", 1, "-h", NULL));
243 +}
244 +
245 +void test_selftest__without_arguments(void)
246 +{
247 + cl_invoke(run("without_arguments", 10, NULL));
248 +}
249 +
250 +void test_selftest__specific_test(void)
251 +{
252 + cl_invoke(run("specific_test", 1, "-sselftest::suite::bool", NULL));
253 +}
254 +
255 +void test_selftest__stop_on_failure(void)
256 +{
257 + cl_invoke(run("stop_on_failure", 1, "-Q", NULL));
258 +}
259 +
260 +void test_selftest__quiet(void)
261 +{
262 + cl_invoke(run("quiet", 10, "-q", NULL));
263 +}
264 +
265 +void test_selftest__tap(void)
266 +{
267 + cl_invoke(run("tap", 10, "-t", NULL));
268 +}
269 +
270 +void test_selftest__suite_names(void)
271 +{
272 + cl_invoke(run("suite_names", 0, "-l", NULL));
273 +}
274 +
275 +void test_selftest__summary_without_filename(void)
276 +{
277 + struct stat st;
278 + cl_invoke(run("summary_without_filename", 10, "-r", NULL));
279 + /* The summary contains timestamps, so we cannot verify its contents. */
280 + cl_must_pass(stat("summary.xml", &st));
281 +}
282 +
283 +void test_selftest__summary_with_filename(void)
284 +{
285 + struct stat st;
286 + cl_invoke(run("summary_with_filename", 10, "-rdifferent.xml", NULL));
287 + /* The summary contains timestamps, so we cannot verify its contents. */
288 + cl_must_pass(stat("different.xml", &st));
289 +}
t/unit-tests/clar/test/selftest.h new
+3
@@ -0,0 +1,3 @@
1 +#include "clar.h"
2 +
3 +extern const char *selftest_binary_path;
t/unit-tests/clar/test/selftest_suite/CMakeLists.txt new
+40
@@ -0,0 +1,40 @@
1 +find_package(Python COMPONENTS Interpreter REQUIRED)
2 +
3 +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
4 + COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}"
5 + DEPENDS main.c selftest_suite.c
6 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
7 +)
8 +
9 +add_executable(selftest_suite)
10 +set_target_properties(selftest_suite PROPERTIES
11 + C_STANDARD 90
12 + C_STANDARD_REQUIRED ON
13 + C_EXTENSIONS OFF
14 +)
15 +
16 +# MSVC generates all kinds of warnings. We may want to fix these in the future
17 +# and then unconditionally treat warnings as errors.
18 +if(NOT MSVC)
19 + set_target_properties(selftest_suite PROPERTIES
20 + COMPILE_WARNING_AS_ERROR ON
21 + )
22 +endif()
23 +
24 +target_sources(selftest_suite PRIVATE
25 + main.c
26 + selftest_suite.c
27 + "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
28 +)
29 +target_compile_definitions(selftest_suite PRIVATE
30 + CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/"
31 + CLAR_SELFTEST
32 +)
33 +target_compile_options(selftest_suite PRIVATE
34 + $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
35 +)
36 +target_include_directories(selftest_suite PRIVATE
37 + "${CMAKE_SOURCE_DIR}"
38 + "${CMAKE_CURRENT_BINARY_DIR}"
39 +)
40 +target_link_libraries(selftest_suite clar)
t/unit-tests/clar/test/selftest_suite/main.c new
+27
@@ -0,0 +1,27 @@
1 +/*
2 + * Copyright (c) Vicent Marti. All rights reserved.
3 + *
4 + * This file is part of clar, distributed under the ISC license.
5 + * For full terms see the included COPYING file.
6 + */
7 +
8 +#include "clar.h"
9 +
10 +/*
11 + * Selftest main() for clar tests.
12 + *
13 + * You should write your own main routine for clar tests that does specific
14 + * setup and teardown as necessary for your application. The only required
15 + * line is the call to `clar_test(argc, argv)`, which will execute the test
16 + * suite. If you want to check the return value of the test application,
17 + * your main() should return the same value returned by clar_test().
18 + */
19 +
20 +#ifdef _WIN32
21 +int __cdecl main(int argc, char *argv[])
22 +#else
23 +int main(int argc, char *argv[])
24 +#endif
25 +{
26 + return clar_test(argc, argv);
27 +}
t/unit-tests/clar/test/selftest_suite/resources/test/file renamed
t/unit-tests/clar/test/selftest_suite/selftest_suite.c renamed
+26 -18
@@ -1,6 +1,7 @@
1 -#include "clar_test.h"
1 #include <sys/stat.h>
2
3 +#include "clar.h"
4 +
5 static int file_size(const char *filename)
6 {
7 struct stat st;
@@ -10,19 +11,14 @@ static int file_size(const char *filename)
11 return -1;
12 }
13
13 -void test_sample__initialize(void)
14 -{
15 - global_test_counter++;
16 -}
17 -
18 -void test_sample__cleanup(void)
14 +void test_selftest_suite__cleanup(void)
15 {
16 cl_fixture_cleanup("test");
17
18 cl_assert(file_size("test/file") == -1);
19 }
20
25 -void test_sample__1(void)
21 +void test_selftest_suite__1(void)
22 {
23 cl_assert(1);
24 cl_must_pass(0); /* 0 == success */
@@ -30,7 +26,7 @@ void test_sample__1(void)
26 cl_must_pass(-1); /* demonstrate a failing call */
27 }
28
33 -void test_sample__2(void)
29 +void test_selftest_suite__2(void)
30 {
31 cl_fixture_sandbox("test");
32
@@ -39,7 +35,7 @@ void test_sample__2(void)
35 cl_assert(100 == 101);
36 }
37
42 -void test_sample__strings(void)
38 +void test_selftest_suite__strings(void)
39 {
40 const char *actual = "expected";
41 cl_assert_equal_s("expected", actual);
@@ -47,7 +43,7 @@ void test_sample__strings(void)
43 cl_assert_equal_s_("mismatched", actual, "this one fails");
44 }
45
50 -void test_sample__strings_with_length(void)
46 +void test_selftest_suite__strings_with_length(void)
47 {
48 const char *actual = "expected";
49 cl_assert_equal_strn("expected_", actual, 8);
@@ -56,29 +52,41 @@ void test_sample__strings_with_length(void)
52 cl_assert_equal_strn_("exactly", actual, 3, "this one fails");
53 }
54
59 -void test_sample__int(void)
55 +void test_selftest_suite__int(void)
56 {
57 int value = 100;
58 cl_assert_equal_i(100, value);
59 cl_assert_equal_i_(101, value, "extra note on failing test");
60 }
61
66 -void test_sample__int_fmt(void)
62 +void test_selftest_suite__int_fmt(void)
63 {
64 int value = 100;
65 cl_assert_equal_i_fmt(022, value, "%04o");
66 }
67
72 -void test_sample__bool(void)
68 +void test_selftest_suite__bool(void)
69 {
70 int value = 100;
71 cl_assert_equal_b(1, value); /* test equality as booleans */
72 cl_assert_equal_b(0, value);
73 }
74
79 -void test_sample__ptr(void)
75 +void test_selftest_suite__ptr(void)
76 {
81 - const char *actual = "expected";
82 - cl_assert_equal_p(actual, actual); /* pointers to same object */
83 - cl_assert_equal_p(&actual, actual);
77 + void *p1 = (void *)0x1, *p2 = (void *)0x2;
78 + cl_assert_equal_p(p1, p1); /* pointers to same object */
79 + cl_assert_equal_p(p1, p2);
80 +}
81 +
82 +void test_selftest_suite__multiline_description(void)
83 +{
84 + cl_must_pass_(-1, "description line 1\ndescription line 2");
85 +}
86 +
87 +void test_selftest_suite__null_string(void)
88 +{
89 + const char *actual = NULL;
90 + cl_assert_equal_s(actual, actual);
91 + cl_assert_equal_s_("expected", actual, "this one fails");
92 }