t/unit-tests: update to 10e96bc

Update to 10e96bc (Merge pull request #127 from pks-gitlab/pks-ci-improvements, 2025-09-22). This commit includes a couple of changes: - The GitHub CI has been updated to include a 32 bit CI job. Furthermore, the jobs now compile with "-Werror" and more warnings enabled. - An issue was addressed where `uintptr_t` is not available on NonStop [1]. - The clar selftests have been restructured so that it is now possible to add small test suites more readily. This was done to add tests for the above addressed issue, where we now use "%p" to print pointers in a platform dependent way. - An issue was addressed where the test output had a trailing whitespace with certain output formats, which caused whitespace issues in the test expectation files. [1]: <01c101dc2842$38903640$a9b0a2c0$@nexbridge.com> Reported-by: Randall S. Becker <rsbecker@nexbridge.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Sep 22, 2025 at 15:16 UTC 93dbb6b3c572fc8877b56233730b5d12b327a7a4
24 files changed +320 -236
t/unit-tests/clar/.github/workflows/ci.yml
+19 -2
@@ -13,30 +13,47 @@ jobs:
13 platform:
14 - os: ubuntu-latest
15 generator: Unix Makefiles
16 + env:
17 + CFLAGS: "-Werror -Wall -Wextra"
18 - os: ubuntu-latest
19 generator: Unix Makefiles
20 env:
21 CC: "clang"
20 - CFLAGS: "-fsanitize=leak"
22 + CFLAGS: "-Werror -Wall -Wextra -fsanitize=leak"
23 + - os: ubuntu-latest
24 + generator: Unix Makefiles
25 + image: i386/debian:latest
26 + env:
27 + CFLAGS: "-Werror -Wall -Wextra"
28 - os: macos-latest
29 generator: Unix Makefiles
30 + env:
31 + CFLAGS: "-Werror -Wall -Wextra"
32 - os: windows-latest
33 generator: Visual Studio 17 2022
34 - os: windows-latest
35 generator: MSYS Makefiles
36 + env:
37 + CFLAGS: "-Werror -Wall -Wextra"
38 - os: windows-latest
39 generator: MinGW Makefiles
40 + env:
41 + CFLAGS: "-Werror -Wall -Wextra"
42 fail-fast: false
43
44 runs-on: ${{ matrix.platform.os }}
45 + container: ${{matrix.platform.image}}
46
47 env:
48 CC: ${{matrix.platform.env.CC}}
49 CFLAGS: ${{matrix.platform.env.CFLAGS}}
50
51 steps:
52 + - name: Prepare 32 bit container image
53 + if: matrix.platform.image == 'i386/debian:latest'
54 + run: apt -q update && apt -q -y install cmake gcc libc6-amd64 lib64stdc++6 make python3
55 - name: Check out
39 - uses: actions/checkout@v2
56 + uses: actions/checkout@v4
57 - name: Build
58 shell: bash
59 run: |
t/unit-tests/clar/clar.c
+3 -8
@@ -195,7 +195,7 @@ struct clar_suite {
195 };
196
197 /* From clar_print_*.c */
198 -static void clar_print_init(int test_count, int suite_count, const char *suite_names);
198 +static void clar_print_init(int test_count, int suite_count);
199 static void clar_print_shutdown(int test_count, int suite_count, int error_count);
200 static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error);
201 static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed);
@@ -592,11 +592,7 @@ clar_test_init(int argc, char **argv)
592 if (argc > 1)
593 clar_parse_args(argc, argv);
594
595 - clar_print_init(
596 - (int)_clar_callback_count,
597 - (int)_clar_suite_count,
598 - ""
599 - );
595 + clar_print_init((int)_clar_callback_count, (int)_clar_suite_count);
596
597 if (!_clar.summary_filename &&
598 (summary_env = getenv("CLAR_SUMMARY")) != NULL) {
@@ -875,8 +871,7 @@ void clar__assert_equal(
871 void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);
872 is_equal = (p1 == p2);
873 if (!is_equal)
878 - p_snprintf(buf, sizeof(buf), "0x%"PRIxPTR" != 0x%"PRIxPTR,
879 - (uintptr_t)p1, (uintptr_t)p2);
874 + p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2);
875 }
876 else {
877 int i1 = va_arg(args, int), i2 = va_arg(args, int);
t/unit-tests/clar/clar/print.h
+5 -6
@@ -1,13 +1,13 @@
1 /* clap: clar protocol, the traditional clar output format */
2
3 -static void clar_print_clap_init(int test_count, int suite_count, const char *suite_names)
3 +static void clar_print_clap_init(int test_count, int suite_count)
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);
10 + printf("Loaded %d suites:\n", (int)suite_count);
11 printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
12 }
13
@@ -103,11 +103,10 @@ static void clar_print_clap_onabort(const char *fmt, va_list arg)
103
104 /* tap: test anywhere protocol format */
105
106 -static void clar_print_tap_init(int test_count, int suite_count, const char *suite_names)
106 +static void clar_print_tap_init(int test_count, int suite_count)
107 {
108 (void)test_count;
109 (void)suite_count;
110 - (void)suite_names;
110 printf("TAP version 13\n");
111 }
112
@@ -207,9 +206,9 @@ static void clar_print_tap_onabort(const char *fmt, va_list arg)
206 } \
207 } while (0)
208
210 -static void clar_print_init(int test_count, int suite_count, const char *suite_names)
209 +static void clar_print_init(int test_count, int suite_count)
210 {
212 - PRINT(init, test_count, suite_count, suite_names);
211 + PRINT(init, test_count, suite_count);
212 }
213
214 static void clar_print_shutdown(int test_count, int suite_count, int error_count)
t/unit-tests/clar/generate.py
+20 -8
@@ -158,17 +158,24 @@ class TestSuite(object):
158
159 def find_modules(self):
160 modules = []
161 - for root, _, files in os.walk(self.path):
162 - module_root = root[len(self.path):]
163 - module_root = [c for c in module_root.split(os.sep) if c]
161
165 - tests_in_module = fnmatch.filter(files, "*.c")
162 + if os.path.isfile(self.path):
163 + full_path = os.path.abspath(self.path)
164 + module_name = os.path.basename(self.path)
165 + module_name = os.path.splitext(module_name)[0]
166 + modules.append((full_path, module_name))
167 + else:
168 + for root, _, files in os.walk(self.path):
169 + module_root = root[len(self.path):]
170 + module_root = [c for c in module_root.split(os.sep) if c]
171
167 - for test_file in tests_in_module:
168 - full_path = os.path.join(root, test_file)
169 - module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_")
172 + tests_in_module = fnmatch.filter(files, "*.c")
173
171 - modules.append((full_path, module_name))
174 + for test_file in tests_in_module:
175 + full_path = os.path.join(root, test_file)
176 + module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_")
177 +
178 + modules.append((full_path, module_name))
179
180 return modules
181
@@ -217,6 +224,7 @@ class TestSuite(object):
224
225 def write(self):
226 output = os.path.join(self.output, 'clar.suite')
227 + os.makedirs(self.output, exist_ok=True)
228
229 if not self.should_generate(output):
230 return False
@@ -258,7 +266,11 @@ if __name__ == '__main__':
266 sys.exit(1)
267
268 path = args.pop() if args else '.'
269 + if os.path.isfile(path) and not options.output:
270 + print("Must provide --output when specifying a file")
271 + sys.exit(1)
272 output = options.output or path
273 +
274 suite = TestSuite(path, output)
275 suite.load(options.force)
276 suite.disable(options.excluded)
t/unit-tests/clar/test/CMakeLists.txt
+3 -8
@@ -1,5 +1,3 @@
1 -add_subdirectory(selftest_suite)
2 -
1 find_package(Python COMPONENTS Interpreter REQUIRED)
2
3 add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite"
@@ -40,15 +38,12 @@ target_include_directories(selftest PRIVATE
38 )
39 target_link_libraries(selftest clar)
40
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 -
41 add_test(NAME build_selftest
42 COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest
43 )
44 set_tests_properties(build_selftest PROPERTIES FIXTURES_SETUP clar_test_fixture)
45
53 -add_test(NAME selftest COMMAND "${CMAKE_CURRENT_BINARY_DIR}/selftest" "$<TARGET_FILE:selftest_suite>")
46 +add_subdirectory(suites)
47 +
48 +add_test(NAME selftest COMMAND "${CMAKE_CURRENT_BINARY_DIR}/selftest" $<TARGET_FILE_DIR:combined_suite>)
49 set_tests_properties(selftest PROPERTIES FIXTURES_REQUIRED clar_test_fixture)
t/unit-tests/clar/test/expected/help
+1 -1
@@ -1,4 +1,4 @@
1 -Usage: selftest [options]
1 +Usage: combined [options]
2
3 Options:
4 -sname Run only the suite with `name` (can go to individual test name)
t/unit-tests/clar/test/expected/quiet
+10 -15
@@ -1,49 +1,44 @@
1 1) Failure:
2 -selftest::suite::1 [file:42]
2 +combined::1 [file:42]
3 Function call failed: -1
4
5 2) Failure:
6 -selftest::suite::2 [file:42]
6 +combined::2 [file:42]
7 Expression is not true: 100 == 101
8
9 3) Failure:
10 -selftest::suite::strings [file:42]
10 +combined::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]
15 +combined::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]
20 +combined::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]
25 +combined::int_fmt [file:42]
26 022 != value
27 0022 != 0144
28
29 7) Failure:
30 -selftest::suite::bool [file:42]
30 +combined::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]
35 +combined::multiline_description [file:42]
36 Function call failed: -1
37 description line 1
38 description line 2
39
45 - 10) Failure:
46 -selftest::suite::null_string [file:42]
40 + 9) Failure:
41 +combined::null_string [file:42]
42 String mismatch: "expected" != actual ("this one fails")
43 'expected' != NULL
44
t/unit-tests/clar/test/expected/specific_test
+2 -2
@@ -1,9 +1,9 @@
1 -Loaded 1 suites:
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]
6 +combined::bool [file:42]
7 0 != value
8 0 != 1
9
t/unit-tests/clar/test/expected/stop_on_failure
+2 -2
@@ -1,8 +1,8 @@
1 -Loaded 1 suites:
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]
6 +combined::1 [file:42]
7 Function call failed: -1
8
t/unit-tests/clar/test/expected/suite_names
+1 -1
@@ -1,2 +1,2 @@
1 Test suites (use -s<name> to run just one):
2 - 0: selftest::suite
2 + 0: combined
t/unit-tests/clar/test/expected/summary.xml
-4
@@ -27,10 +27,6 @@
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>
30 </testcase>
31 <testcase name="multiline_description" classname="selftest" time="0.00">
32 <failure type="assert"><![CDATA[Function call failed: −1
t/unit-tests/clar/test/expected/summary_with_filename
+12 -17
@@ -1,53 +1,48 @@
1 -Loaded 1 suites:
1 +Loaded 1 suites:
2 Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 -FFFFFFFFFF
3 +FFFFFFFFF
4
5 1) Failure:
6 -selftest::suite::1 [file:42]
6 +combined::1 [file:42]
7 Function call failed: -1
8
9 2) Failure:
10 -selftest::suite::2 [file:42]
10 +combined::2 [file:42]
11 Expression is not true: 100 == 101
12
13 3) Failure:
14 -selftest::suite::strings [file:42]
14 +combined::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]
19 +combined::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]
24 +combined::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]
29 +combined::int_fmt [file:42]
30 022 != value
31 0022 != 0144
32
33 7) Failure:
34 -selftest::suite::bool [file:42]
34 +combined::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]
39 +combined::multiline_description [file:42]
40 Function call failed: -1
41 description line 1
42 description line 2
43
49 - 10) Failure:
50 -selftest::suite::null_string [file:42]
44 + 9) Failure:
45 +combined::null_string [file:42]
46 String mismatch: "expected" != actual ("this one fails")
47 'expected' != NULL
48
t/unit-tests/clar/test/expected/summary_without_filename
+12 -17
@@ -1,53 +1,48 @@
1 -Loaded 1 suites:
1 +Loaded 1 suites:
2 Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 -FFFFFFFFFF
3 +FFFFFFFFF
4
5 1) Failure:
6 -selftest::suite::1 [file:42]
6 +combined::1 [file:42]
7 Function call failed: -1
8
9 2) Failure:
10 -selftest::suite::2 [file:42]
10 +combined::2 [file:42]
11 Expression is not true: 100 == 101
12
13 3) Failure:
14 -selftest::suite::strings [file:42]
14 +combined::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]
19 +combined::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]
24 +combined::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]
29 +combined::int_fmt [file:42]
30 022 != value
31 0022 != 0144
32
33 7) Failure:
34 -selftest::suite::bool [file:42]
34 +combined::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]
39 +combined::multiline_description [file:42]
40 Function call failed: -1
41 description line 1
42 description line 2
43
49 - 10) Failure:
50 -selftest::suite::null_string [file:42]
44 + 9) Failure:
45 +combined::null_string [file:42]
46 String mismatch: "expected" != actual ("this one fails")
47 'expected' != NULL
48
t/unit-tests/clar/test/expected/tap
+11 -21
@@ -1,6 +1,6 @@
1 TAP version 13
2 -# start of suite 1: selftest::suite
3 -not ok 1 - selftest::suite::1
2 +# start of suite 1: combined
3 +not ok 1 - combined::1
4 ---
5 reason: |
6 Function call failed: -1
@@ -9,7 +9,7 @@ not ok 1 - selftest::suite::1
9 line: 42
10 function: 'func'
11 ---
12 -not ok 2 - selftest::suite::2
12 +not ok 2 - combined::2
13 ---
14 reason: |
15 Expression is not true: 100 == 101
@@ -18,7 +18,7 @@ not ok 2 - selftest::suite::2
18 line: 42
19 function: 'func'
20 ---
21 -not ok 3 - selftest::suite::strings
21 +not ok 3 - combined::strings
22 ---
23 reason: |
24 String mismatch: "mismatched" != actual ("this one fails")
@@ -28,7 +28,7 @@ not ok 3 - selftest::suite::strings
28 line: 42
29 function: 'func'
30 ---
31 -not ok 4 - selftest::suite::strings_with_length
31 +not ok 4 - combined::strings_with_length
32 ---
33 reason: |
34 String mismatch: "exactly" != actual ("this one fails")
@@ -38,7 +38,7 @@ not ok 4 - selftest::suite::strings_with_length
38 line: 42
39 function: 'func'
40 ---
41 -not ok 5 - selftest::suite::int
41 +not ok 5 - combined::int
42 ---
43 reason: |
44 101 != value ("extra note on failing test")
@@ -48,7 +48,7 @@ not ok 5 - selftest::suite::int
48 line: 42
49 function: 'func'
50 ---
51 -not ok 6 - selftest::suite::int_fmt
51 +not ok 6 - combined::int_fmt
52 ---
53 reason: |
54 022 != value
@@ -58,7 +58,7 @@ not ok 6 - selftest::suite::int_fmt
58 line: 42
59 function: 'func'
60 ---
61 -not ok 7 - selftest::suite::bool
61 +not ok 7 - combined::bool
62 ---
63 reason: |
64 0 != value
@@ -68,17 +68,7 @@ not ok 7 - selftest::suite::bool
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
71 +not ok 8 - combined::multiline_description
72 ---
73 reason: |
74 Function call failed: -1
@@ -89,7 +79,7 @@ not ok 9 - selftest::suite::multiline_description
79 line: 42
80 function: 'func'
81 ---
92 -not ok 10 - selftest::suite::null_string
82 +not ok 9 - combined::null_string
83 ---
84 reason: |
85 String mismatch: "expected" != actual ("this one fails")
@@ -99,4 +89,4 @@ not ok 10 - selftest::suite::null_string
89 line: 42
90 function: 'func'
91 ---
102 -1..10
92 +1..9
t/unit-tests/clar/test/expected/without_arguments
+12 -17
@@ -1,53 +1,48 @@
1 -Loaded 1 suites:
1 +Loaded 1 suites:
2 Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')
3 -FFFFFFFFFF
3 +FFFFFFFFF
4
5 1) Failure:
6 -selftest::suite::1 [file:42]
6 +combined::1 [file:42]
7 Function call failed: -1
8
9 2) Failure:
10 -selftest::suite::2 [file:42]
10 +combined::2 [file:42]
11 Expression is not true: 100 == 101
12
13 3) Failure:
14 -selftest::suite::strings [file:42]
14 +combined::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]
19 +combined::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]
24 +combined::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]
29 +combined::int_fmt [file:42]
30 022 != value
31 0022 != 0144
32
33 7) Failure:
34 -selftest::suite::bool [file:42]
34 +combined::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]
39 +combined::multiline_description [file:42]
40 Function call failed: -1
41 description line 1
42 description line 2
43
49 - 10) Failure:
50 -selftest::suite::null_string [file:42]
44 + 9) Failure:
45 +combined::null_string [file:42]
46 String mismatch: "expected" != actual ("this one fails")
47 'expected' != NULL
48
t/unit-tests/clar/test/main.c
+3 -3
@@ -3,7 +3,7 @@
3
4 #include "selftest.h"
5
6 -const char *selftest_binary_path;
6 +const char *selftest_suite_directory;
7
8 #ifdef _WIN32
9 int __cdecl main(int argc, char *argv[])
@@ -12,12 +12,12 @@ int main(int argc, char *argv[])
12 #endif
13 {
14 if (argc < 2) {
15 - fprintf(stderr, "usage: %s <selftest-suite-executable> <options>\n",
15 + fprintf(stderr, "usage: %s <selftest-suite-directory> <options>\n",
16 argv[0]);
17 exit(1);
18 }
19
20 - selftest_binary_path = argv[1];
20 + selftest_suite_directory = argv[1];
21 memmove(argv + 1, argv + 2, argc - 1);
22 argc -= 1;
23
t/unit-tests/clar/test/selftest.c
+127 -46
@@ -59,38 +59,34 @@ static char *read_file(const char *path)
59 return content;
60 }
61
62 -static void run(const char *expected_output_file, int expected_error_code, ...)
62 +static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)
63 {
64 SECURITY_ATTRIBUTES security_attributes = { 0 };
65 PROCESS_INFORMATION process_info = { 0 };
66 STARTUPINFO startup_info = { 0 };
67 + char binary_path[4096] = { 0 };
68 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;
73 + size_t i;
74 +
75 + snprintf(binary_path, sizeof(binary_path), "%s/%s_suite.exe",
76 + selftest_suite_directory, suite);
77
78 /*
79 * Assemble command line arguments. In theory we'd have to properly
80 * quote them. In practice none of our tests actually care.
81 */
79 - va_start(ap, expected_error_code);
80 - snprintf(cmdline, sizeof(cmdline), "selftest");
81 - while (1) {
82 + snprintf(cmdline, sizeof(cmdline), suite);
83 + for (i = 0; i < nargs; i++) {
84 size_t cmdline_len = strlen(cmdline);
83 - const char *arg;
84 -
85 - arg = va_arg(ap, const char *);
86 - if (!arg)
87 - break;
88 -
85 + const char *arg = args[i];
86 cl_assert(cmdline_len + strlen(arg) < sizeof(cmdline));
87 snprintf(cmdline + cmdline_len, sizeof(cmdline) - cmdline_len,
88 " %s", arg);
89 }
93 - va_end(ap);
90
91 /*
92 * Create a pipe that we will use to read data from the child process.
@@ -110,17 +106,39 @@ static void run(const char *expected_output_file, int expected_error_code, ...)
106 startup_info.hStdError = stdout_write;
107 startup_info.hStdOutput = stdout_write;
108 startup_info.dwFlags |= STARTF_USESTDHANDLES;
113 - cl_assert_equal_b(1, CreateProcess(selftest_binary_path, cmdline, NULL, NULL, TRUE,
109 + cl_assert_equal_b(1, CreateProcess(binary_path, cmdline, NULL, NULL, TRUE,
110 0, NULL, NULL, &startup_info, &process_info));
111 cl_assert_equal_b(1, CloseHandle(stdout_write));
112
113 output = read_full(stdout_read, 1);
114 cl_assert_equal_b(1, CloseHandle(stdout_read));
115 cl_assert_equal_b(1, GetExitCodeProcess(process_info.hProcess, &exit_code));
116 + cl_assert_equal_i(exit_code, expected_error_code);
117 +
118 + return output;
119 +}
120 +
121 +static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)
122 +{
123 + char *expected_output = NULL;
124 + char *output = NULL;
125 + const char *args[16];
126 + va_list ap;
127 + size_t i;
128 +
129 + va_start(ap, expected_error_code);
130 + for (i = 0; ; i++) {
131 + const char *arg = va_arg(ap, const char *);
132 + if (!arg)
133 + break;
134 + cl_assert(i < sizeof(args) / sizeof(*args));
135 + args[i] = arg;
136 + }
137 + va_end(ap);
138
139 + output = execute(suite, expected_error_code, args, i);
140 expected_output = read_file(cl_fixture(expected_output_file));
141 cl_assert_equal_s(output, expected_output);
123 - cl_assert_equal_i(exit_code, expected_error_code);
142
143 free(expected_output);
144 free(output);
@@ -180,29 +198,25 @@ static char *read_file(const char *path)
198 return data;
199 }
200
183 -static void run(const char *expected_output_file, int expected_error_code, ...)
201 +static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)
202 {
185 - const char *argv[16];
203 int pipe_fds[2];
187 - va_list ap;
204 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);
205
206 cl_must_pass(pipe(pipe_fds));
207
208 pid = fork();
209 if (!pid) {
210 + const char *final_args[17] = { NULL };
211 + char binary_path[4096];
212 + size_t len = 0;
213 + size_t i;
214 +
215 + cl_assert(nargs < sizeof(final_args) / sizeof(*final_args));
216 + final_args[0] = suite;
217 + for (i = 0; i < nargs; i++)
218 + final_args[i + 1] = args[i];
219 +
220 if (dup2(pipe_fds[1], STDOUT_FILENO) < 0 ||
221 dup2(pipe_fds[1], STDERR_FILENO) < 0 ||
222 close(0) < 0 ||
@@ -210,11 +224,29 @@ static void run(const char *expected_output_file, int expected_error_code, ...)
224 close(pipe_fds[1]) < 0)
225 exit(1);
226
213 - execv(selftest_binary_path, (char **) argv);
227 + cl_assert(len + strlen(selftest_suite_directory) < sizeof(binary_path));
228 + strcpy(binary_path, selftest_suite_directory);
229 + len += strlen(selftest_suite_directory);
230 +
231 + cl_assert(len + 1 < sizeof(binary_path));
232 + binary_path[len] = '/';
233 + len += 1;
234 +
235 + cl_assert(len + strlen(suite) < sizeof(binary_path));
236 + strcpy(binary_path + len, suite);
237 + len += strlen(suite);
238 +
239 + cl_assert(len + strlen("_suite") < sizeof(binary_path));
240 + strcpy(binary_path + len, "_suite");
241 + len += strlen("_suite");
242 +
243 + binary_path[len] = '\0';
244 +
245 + execv(binary_path, (char **) final_args);
246 exit(1);
247 } else if (pid > 0) {
248 pid_t waited_pid;
217 - char *expected_output, *output;
249 + char *output;
250 int stat;
251
252 cl_must_pass(close(pipe_fds[1]));
@@ -226,56 +258,78 @@ static void run(const char *expected_output_file, int expected_error_code, ...)
258 cl_assert(WIFEXITED(stat));
259 cl_assert_equal_i(WEXITSTATUS(stat), expected_error_code);
260
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);
261 + return output;
262 } else {
263 cl_fail("Fork failed.");
264 }
265 +
266 + return NULL;
267 +}
268 +
269 +static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)
270 +{
271 + char *expected_output, *output;
272 + const char *args[16];
273 + va_list ap;
274 + size_t i;
275 +
276 + va_start(ap, expected_error_code);
277 + for (i = 0; ; i++) {
278 + cl_assert(i < sizeof(args) / sizeof(*args));
279 + args[i] = va_arg(ap, const char *);
280 + if (!args[i])
281 + break;
282 + }
283 + va_end(ap);
284 +
285 + output = execute(suite, expected_error_code, args, i);
286 + expected_output = read_file(cl_fixture(expected_output_file));
287 + cl_assert_equal_s(output, expected_output);
288 +
289 + free(expected_output);
290 + free(output);
291 }
292 #endif
293
294 void test_selftest__help(void)
295 {
242 - cl_invoke(run("help", 1, "-h", NULL));
296 + cl_invoke(assert_output("combined", "help", 1, "-h", NULL));
297 }
298
299 void test_selftest__without_arguments(void)
300 {
247 - cl_invoke(run("without_arguments", 10, NULL));
301 + cl_invoke(assert_output("combined", "without_arguments", 9, NULL));
302 }
303
304 void test_selftest__specific_test(void)
305 {
252 - cl_invoke(run("specific_test", 1, "-sselftest::suite::bool", NULL));
306 + cl_invoke(assert_output("combined", "specific_test", 1, "-scombined::bool", NULL));
307 }
308
309 void test_selftest__stop_on_failure(void)
310 {
257 - cl_invoke(run("stop_on_failure", 1, "-Q", NULL));
311 + cl_invoke(assert_output("combined", "stop_on_failure", 1, "-Q", NULL));
312 }
313
314 void test_selftest__quiet(void)
315 {
262 - cl_invoke(run("quiet", 10, "-q", NULL));
316 + cl_invoke(assert_output("combined", "quiet", 9, "-q", NULL));
317 }
318
319 void test_selftest__tap(void)
320 {
267 - cl_invoke(run("tap", 10, "-t", NULL));
321 + cl_invoke(assert_output("combined", "tap", 9, "-t", NULL));
322 }
323
324 void test_selftest__suite_names(void)
325 {
272 - cl_invoke(run("suite_names", 0, "-l", NULL));
326 + cl_invoke(assert_output("combined", "suite_names", 0, "-l", NULL));
327 }
328
329 void test_selftest__summary_without_filename(void)
330 {
331 struct stat st;
278 - cl_invoke(run("summary_without_filename", 10, "-r", NULL));
332 + cl_invoke(assert_output("combined", "summary_without_filename", 9, "-r", NULL));
333 /* The summary contains timestamps, so we cannot verify its contents. */
334 cl_must_pass(stat("summary.xml", &st));
335 }
@@ -283,7 +337,34 @@ void test_selftest__summary_without_filename(void)
337 void test_selftest__summary_with_filename(void)
338 {
339 struct stat st;
286 - cl_invoke(run("summary_with_filename", 10, "-rdifferent.xml", NULL));
340 + cl_invoke(assert_output("combined", "summary_with_filename", 9, "-rdifferent.xml", NULL));
341 /* The summary contains timestamps, so we cannot verify its contents. */
342 cl_must_pass(stat("different.xml", &st));
343 }
344 +
345 +void test_selftest__pointer_equal(void)
346 +{
347 + const char *args[] = {
348 + "-spointer::equal",
349 + "-t"
350 + };
351 + char *output = execute("pointer", 0, args, 2);
352 + cl_assert_equal_s(output,
353 + "TAP version 13\n"
354 + "# start of suite 1: pointer\n"
355 + "ok 1 - pointer::equal\n"
356 + "1..1\n"
357 + );
358 + free(output);
359 +}
360 +
361 +void test_selftest__pointer_unequal(void)
362 +{
363 + const char *args[] = {
364 + "-spointer::unequal",
365 + };
366 + char *output = execute("pointer", 1, args, 1);
367 + cl_assert(output);
368 + cl_assert(strstr(output, "Pointer mismatch: "));
369 + free(output);
370 +}
t/unit-tests/clar/test/selftest.h
+1 -1
@@ -1,3 +1,3 @@
1 #include "clar.h"
2
3 -extern const char *selftest_binary_path;
3 +extern const char *selftest_suite_directory;
t/unit-tests/clar/test/selftest_suite/CMakeLists.txt deleted
-40
@@ -1,40 +0,0 @@
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/suites/CMakeLists.txt new
+53
@@ -0,0 +1,53 @@
1 +list(APPEND suites
2 + "combined"
3 + "pointer"
4 +)
5 +
6 +foreach(suite IN LISTS suites)
7 + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite"
8 + COMMAND "${Python_EXECUTABLE}"
9 + "${CMAKE_SOURCE_DIR}/generate.py"
10 + "${CMAKE_CURRENT_SOURCE_DIR}/${suite}.c"
11 + --output "${CMAKE_CURRENT_BINARY_DIR}/${suite}"
12 + DEPENDS ${suite}.c
13 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
14 + )
15 +
16 + add_executable(${suite}_suite)
17 + set_target_properties(${suite}_suite PROPERTIES
18 + C_STANDARD 90
19 + C_STANDARD_REQUIRED ON
20 + C_EXTENSIONS OFF
21 + )
22 +
23 + # MSVC generates all kinds of warnings. We may want to fix these in the future
24 + # and then unconditionally treat warnings as errors.
25 + if(NOT MSVC)
26 + set_target_properties(${suite}_suite PROPERTIES
27 + COMPILE_WARNING_AS_ERROR ON
28 + )
29 + endif()
30 +
31 + target_sources(${suite}_suite PRIVATE
32 + main.c
33 + ${suite}.c
34 + "${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite"
35 + )
36 + target_compile_definitions(${suite}_suite PRIVATE
37 + CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/"
38 + CLAR_SELFTEST
39 + )
40 + target_compile_options(${suite}_suite PRIVATE
41 + $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>
42 + )
43 + target_include_directories(${suite}_suite PRIVATE
44 + "${CMAKE_SOURCE_DIR}"
45 + "${CMAKE_CURRENT_BINARY_DIR}/${suite}"
46 + )
47 + target_link_libraries(${suite}_suite clar)
48 +
49 + add_test(NAME build_${suite}_suite
50 + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest
51 + )
52 + set_tests_properties(build_${suite}_suite PROPERTIES FIXTURES_SETUP clar_test_fixture)
53 +endforeach()
t/unit-tests/clar/test/suites/combined.c renamed
+10 -17
@@ -11,14 +11,14 @@ static int file_size(const char *filename)
11 return -1;
12 }
13
14 -void test_selftest_suite__cleanup(void)
14 +void test_combined__cleanup(void)
15 {
16 cl_fixture_cleanup("test");
17
18 cl_assert(file_size("test/file") == -1);
19 }
20
21 -void test_selftest_suite__1(void)
21 +void test_combined__1(void)
22 {
23 cl_assert(1);
24 cl_must_pass(0); /* 0 == success */
@@ -26,7 +26,7 @@ void test_selftest_suite__1(void)
26 cl_must_pass(-1); /* demonstrate a failing call */
27 }
28
29 -void test_selftest_suite__2(void)
29 +void test_combined__2(void)
30 {
31 cl_fixture_sandbox("test");
32
@@ -35,7 +35,7 @@ void test_selftest_suite__2(void)
35 cl_assert(100 == 101);
36 }
37
38 -void test_selftest_suite__strings(void)
38 +void test_combined__strings(void)
39 {
40 const char *actual = "expected";
41 cl_assert_equal_s("expected", actual);
@@ -43,7 +43,7 @@ void test_selftest_suite__strings(void)
43 cl_assert_equal_s_("mismatched", actual, "this one fails");
44 }
45
46 -void test_selftest_suite__strings_with_length(void)
46 +void test_combined__strings_with_length(void)
47 {
48 const char *actual = "expected";
49 cl_assert_equal_strn("expected_", actual, 8);
@@ -52,39 +52,32 @@ void test_selftest_suite__strings_with_length(void)
52 cl_assert_equal_strn_("exactly", actual, 3, "this one fails");
53 }
54
55 -void test_selftest_suite__int(void)
55 +void test_combined__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
62 -void test_selftest_suite__int_fmt(void)
62 +void test_combined__int_fmt(void)
63 {
64 int value = 100;
65 cl_assert_equal_i_fmt(022, value, "%04o");
66 }
67
68 -void test_selftest_suite__bool(void)
68 +void test_combined__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
75 -void test_selftest_suite__ptr(void)
76 -{
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)
75 +void test_combined__multiline_description(void)
76 {
77 cl_must_pass_(-1, "description line 1\ndescription line 2");
78 }
79
87 -void test_selftest_suite__null_string(void)
80 +void test_combined__null_string(void)
81 {
82 const char *actual = NULL;
83 cl_assert_equal_s(actual, actual);
t/unit-tests/clar/test/suites/main.c renamed
t/unit-tests/clar/test/suites/pointer.c new
+13
@@ -0,0 +1,13 @@
1 +#include "clar.h"
2 +
3 +void test_pointer__equal(void)
4 +{
5 + void *p1 = (void *)0x1;
6 + cl_assert_equal_p(p1, p1);
7 +}
8 +
9 +void test_pointer__unequal(void)
10 +{
11 + void *p1 = (void *)0x1, *p2 = (void *)0x2;
12 + cl_assert_equal_p(p1, p2);
13 +}
t/unit-tests/clar/test/suites/resources/test/file renamed