{"thread":{"id":"64181","subject":"[PATCH] t/unit-tests: update to 10e96bc","startedAt":"2025-09-22T13:16:19Z","lastAt":"2025-09-22T19:09:29Z","messageCount":2,"participants":["Patrick Steinhardt","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"526935","messageId":"20250922-pks-clar-update-v1-1-9154e7d1b7a1@pks.im","threadId":"64181","inReplyTo":null,"subject":"[PATCH] t/unit-tests: update to 10e96bc","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-09-22T13:16:11Z","receivedAt":"2025-09-22T13:16:19Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Update to 10e96bc (Merge pull request #127 from\npks-gitlab/pks-ci-improvements, 2025-09-22). This commit includes a\ncouple of changes:\n\n  - The GitHub CI has been updated to include a 32 bit CI job.\n    Furthermore, the jobs now compile with \"-Werror\" and more warnings\n    enabled.\n\n  - An issue was addressed where `uintptr_t` is not available on\n    NonStop [1].\n\n  - The clar selftests have been restructured so that it is now possible\n    to add small test suites more readily. This was done to add tests\n    for the above addressed issue, where we now use \"%p\" to print\n    pointers in a platform dependent way.\n\n  - An issue was addressed where the test output had a trailing\n    whitespace with certain output formats, which caused whitespace\n    issues in the test expectation files.\n\n[1]: <01c101dc2842$38903640$a9b0a2c0$@nexbridge.com>\n\nReported-by: Randall S. Becker <rsbecker@nexbridge.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\nAs reported in [1]. This is built on top of master at ca2559c1d6 (The\ntenth batch, 2025-09-18) with e7f04f651a (t/unit-tests: update clar to\nfcbed04, 2025-09-10) merged into it.\n\nThanks!\n\nPatrick\n\n[1]: <01c101dc2842$38903640$a9b0a2c0$@nexbridge.com>\n---\n t/unit-tests/clar/.github/workflows/ci.yml         |  21 ++-\n t/unit-tests/clar/clar.c                           |  11 +-\n t/unit-tests/clar/clar/print.h                     |  11 +-\n t/unit-tests/clar/generate.py                      |  28 +++-\n t/unit-tests/clar/test/CMakeLists.txt              |  11 +-\n t/unit-tests/clar/test/expected/help               |   2 +-\n t/unit-tests/clar/test/expected/quiet              |  25 ++-\n t/unit-tests/clar/test/expected/specific_test      |   4 +-\n t/unit-tests/clar/test/expected/stop_on_failure    |   4 +-\n t/unit-tests/clar/test/expected/suite_names        |   2 +-\n t/unit-tests/clar/test/expected/summary.xml        |   4 -\n .../clar/test/expected/summary_with_filename       |  29 ++--\n .../clar/test/expected/summary_without_filename    |  29 ++--\n t/unit-tests/clar/test/expected/tap                |  32 ++--\n t/unit-tests/clar/test/expected/without_arguments  |  29 ++--\n t/unit-tests/clar/test/main.c                      |   6 +-\n t/unit-tests/clar/test/selftest.c                  | 173 +++++++++++++++------\n t/unit-tests/clar/test/selftest.h                  |   2 +-\n .../clar/test/selftest_suite/CMakeLists.txt        |  40 -----\n t/unit-tests/clar/test/suites/CMakeLists.txt       |  53 +++++++\n .../selftest_suite.c => suites/combined.c}         |  27 ++--\n .../clar/test/{selftest_suite => suites}/main.c    |   0\n t/unit-tests/clar/test/suites/pointer.c            |  13 ++\n .../{selftest_suite => suites}/resources/test/file |   0\n 24 files changed, 320 insertions(+), 236 deletions(-)\n\ndiff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml\nindex c41f55f6ff5..4d4724222c3 100644\n--- a/t/unit-tests/clar/.github/workflows/ci.yml\n+++ b/t/unit-tests/clar/.github/workflows/ci.yml\n@@ -13,30 +13,47 @@ jobs:\n         platform:\n           - os: ubuntu-latest\n             generator: Unix Makefiles\n+            env:\n+              CFLAGS: \"-Werror -Wall -Wextra\"\n           - os: ubuntu-latest\n             generator: Unix Makefiles\n             env:\n               CC: \"clang\"\n-              CFLAGS: \"-fsanitize=leak\"\n+              CFLAGS: \"-Werror -Wall -Wextra -fsanitize=leak\"\n+          - os: ubuntu-latest\n+            generator: Unix Makefiles\n+            image: i386/debian:latest\n+            env:\n+              CFLAGS: \"-Werror -Wall -Wextra\"\n           - os: macos-latest\n             generator: Unix Makefiles\n+            env:\n+              CFLAGS: \"-Werror -Wall -Wextra\"\n           - os: windows-latest\n             generator: Visual Studio 17 2022\n           - os: windows-latest\n             generator: MSYS Makefiles\n+            env:\n+              CFLAGS: \"-Werror -Wall -Wextra\"\n           - os: windows-latest\n             generator: MinGW Makefiles\n+            env:\n+              CFLAGS: \"-Werror -Wall -Wextra\"\n       fail-fast: false\n \n     runs-on: ${{ matrix.platform.os }}\n+    container: ${{matrix.platform.image}}\n \n     env:\n       CC: ${{matrix.platform.env.CC}}\n       CFLAGS: ${{matrix.platform.env.CFLAGS}}\n \n     steps:\n+    - name: Prepare 32 bit container image\n+      if: matrix.platform.image == 'i386/debian:latest'\n+      run: apt -q update && apt -q -y install cmake gcc libc6-amd64 lib64stdc++6 make python3\n     - name: Check out\n-      uses: actions/checkout@v2\n+      uses: actions/checkout@v4\n     - name: Build\n       shell: bash\n       run: |\ndiff --git a/t/unit-tests/clar/clar.c b/t/unit-tests/clar/clar.c\nindex 80c53594252..d6176e50b22 100644\n--- a/t/unit-tests/clar/clar.c\n+++ b/t/unit-tests/clar/clar.c\n@@ -195,7 +195,7 @@ struct clar_suite {\n };\n \n /* From clar_print_*.c */\n-static void clar_print_init(int test_count, int suite_count, const char *suite_names);\n+static void clar_print_init(int test_count, int suite_count);\n static void clar_print_shutdown(int test_count, int suite_count, int error_count);\n static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error);\n static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed);\n@@ -592,11 +592,7 @@ clar_test_init(int argc, char **argv)\n \tif (argc > 1)\n \t\tclar_parse_args(argc, argv);\n \n-\tclar_print_init(\n-\t\t(int)_clar_callback_count,\n-\t\t(int)_clar_suite_count,\n-\t\t\"\"\n-\t);\n+\tclar_print_init((int)_clar_callback_count, (int)_clar_suite_count);\n \n \tif (!_clar.summary_filename &&\n \t    (summary_env = getenv(\"CLAR_SUMMARY\")) != NULL) {\n@@ -875,8 +871,7 @@ void clar__assert_equal(\n \t\tvoid *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);\n \t\tis_equal = (p1 == p2);\n \t\tif (!is_equal)\n-\t\t\tp_snprintf(buf, sizeof(buf), \"0x%\"PRIxPTR\" != 0x%\"PRIxPTR,\n-\t\t\t\t   (uintptr_t)p1, (uintptr_t)p2);\n+\t\t\tp_snprintf(buf, sizeof(buf), \"%p != %p\", p1, p2);\n \t}\n \telse {\n \t\tint i1 = va_arg(args, int), i2 = va_arg(args, int);\ndiff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h\nindex 0282aaa1381..89b66591d75 100644\n--- a/t/unit-tests/clar/clar/print.h\n+++ b/t/unit-tests/clar/clar/print.h\n@@ -1,13 +1,13 @@\n /* clap: clar protocol, the traditional clar output format */\n \n-static void clar_print_clap_init(int test_count, int suite_count, const char *suite_names)\n+static void clar_print_clap_init(int test_count, int suite_count)\n {\n \t(void)test_count;\n \n \tif (_clar.verbosity < 0)\n \t\treturn;\n \n-\tprintf(\"Loaded %d suites: %s\\n\", (int)suite_count, suite_names);\n+\tprintf(\"Loaded %d suites:\\n\", (int)suite_count);\n \tprintf(\"Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\\n\");\n }\n \n@@ -103,11 +103,10 @@ static void clar_print_clap_onabort(const char *fmt, va_list arg)\n \n /* tap: test anywhere protocol format */\n \n-static void clar_print_tap_init(int test_count, int suite_count, const char *suite_names)\n+static void clar_print_tap_init(int test_count, int suite_count)\n {\n \t(void)test_count;\n \t(void)suite_count;\n-\t(void)suite_names;\n \tprintf(\"TAP version 13\\n\");\n }\n \n@@ -207,9 +206,9 @@ static void clar_print_tap_onabort(const char *fmt, va_list arg)\n \t\t} \\\n \t} while (0)\n \n-static void clar_print_init(int test_count, int suite_count, const char *suite_names)\n+static void clar_print_init(int test_count, int suite_count)\n {\n-\tPRINT(init, test_count, suite_count, suite_names);\n+\tPRINT(init, test_count, suite_count);\n }\n \n static void clar_print_shutdown(int test_count, int suite_count, int error_count)\ndiff --git a/t/unit-tests/clar/generate.py b/t/unit-tests/clar/generate.py\nindex 80996ac3e71..fd2f0ee83b5 100755\n--- a/t/unit-tests/clar/generate.py\n+++ b/t/unit-tests/clar/generate.py\n@@ -158,17 +158,24 @@ def should_generate(self, path):\n \n     def find_modules(self):\n         modules = []\n-        for root, _, files in os.walk(self.path):\n-            module_root = root[len(self.path):]\n-            module_root = [c for c in module_root.split(os.sep) if c]\n \n-            tests_in_module = fnmatch.filter(files, \"*.c\")\n+        if os.path.isfile(self.path):\n+            full_path = os.path.abspath(self.path)\n+            module_name = os.path.basename(self.path)\n+            module_name = os.path.splitext(module_name)[0]\n+            modules.append((full_path, module_name))\n+        else:\n+            for root, _, files in os.walk(self.path):\n+                module_root = root[len(self.path):]\n+                module_root = [c for c in module_root.split(os.sep) if c]\n \n-            for test_file in tests_in_module:\n-                full_path = os.path.join(root, test_file)\n-                module_name = \"_\".join(module_root + [test_file[:-2]]).replace(\"-\", \"_\")\n+                tests_in_module = fnmatch.filter(files, \"*.c\")\n \n-                modules.append((full_path, module_name))\n+                for test_file in tests_in_module:\n+                    full_path = os.path.join(root, test_file)\n+                    module_name = \"_\".join(module_root + [test_file[:-2]]).replace(\"-\", \"_\")\n+\n+                    modules.append((full_path, module_name))\n \n         return modules\n \n@@ -217,6 +224,7 @@ def callback_count(self):\n \n     def write(self):\n         output = os.path.join(self.output, 'clar.suite')\n+        os.makedirs(self.output, exist_ok=True)\n \n         if not self.should_generate(output):\n             return False\n@@ -258,7 +266,11 @@ def write(self):\n         sys.exit(1)\n \n     path = args.pop() if args else '.'\n+    if os.path.isfile(path) and not options.output:\n+        print(\"Must provide --output when specifying a file\")\n+        sys.exit(1)\n     output = options.output or path\n+\n     suite = TestSuite(path, output)\n     suite.load(options.force)\n     suite.disable(options.excluded)\ndiff --git a/t/unit-tests/clar/test/CMakeLists.txt b/t/unit-tests/clar/test/CMakeLists.txt\nindex 96abd6ed931..f2401664397 100644\n--- a/t/unit-tests/clar/test/CMakeLists.txt\n+++ b/t/unit-tests/clar/test/CMakeLists.txt\n@@ -1,5 +1,3 @@\n-add_subdirectory(selftest_suite)\n-\n find_package(Python COMPONENTS Interpreter REQUIRED)\n \n add_custom_command(OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/clar.suite\"\n@@ -40,15 +38,12 @@ target_include_directories(selftest PRIVATE\n )\n target_link_libraries(selftest clar)\n \n-add_test(NAME build_selftest_suite\n-\tCOMMAND \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --config \"$<CONFIG>\" --target selftest_suite\n-)\n-set_tests_properties(build_selftest_suite PROPERTIES FIXTURES_SETUP clar_test_fixture)\n-\n add_test(NAME build_selftest\n \tCOMMAND \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --config \"$<CONFIG>\" --target selftest\n )\n set_tests_properties(build_selftest PROPERTIES FIXTURES_SETUP clar_test_fixture)\n \n-add_test(NAME selftest COMMAND \"${CMAKE_CURRENT_BINARY_DIR}/selftest\" \"$<TARGET_FILE:selftest_suite>\")\n+add_subdirectory(suites)\n+\n+add_test(NAME selftest COMMAND \"${CMAKE_CURRENT_BINARY_DIR}/selftest\" $<TARGET_FILE_DIR:combined_suite>)\n set_tests_properties(selftest PROPERTIES FIXTURES_REQUIRED clar_test_fixture)\ndiff --git a/t/unit-tests/clar/test/expected/help b/t/unit-tests/clar/test/expected/help\nindex 4b2be69f973..9428def2d71 100644\n--- a/t/unit-tests/clar/test/expected/help\n+++ b/t/unit-tests/clar/test/expected/help\n@@ -1,4 +1,4 @@\n-Usage: selftest [options]\n+Usage: combined [options]\n \n Options:\n   -sname        Run only the suite with `name` (can go to individual test name)\ndiff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet\nindex 975164147ff..280c99d8ad5 100644\n--- a/t/unit-tests/clar/test/expected/quiet\n+++ b/t/unit-tests/clar/test/expected/quiet\n@@ -1,49 +1,44 @@\n   1) Failure:\n-selftest::suite::1 [file:42]\n+combined::1 [file:42]\n   Function call failed: -1\n \n   2) Failure:\n-selftest::suite::2 [file:42]\n+combined::2 [file:42]\n   Expression is not true: 100 == 101\n \n   3) Failure:\n-selftest::suite::strings [file:42]\n+combined::strings [file:42]\n   String mismatch: \"mismatched\" != actual (\"this one fails\")\n   'mismatched' != 'expected' (at byte 0)\n \n   4) Failure:\n-selftest::suite::strings_with_length [file:42]\n+combined::strings_with_length [file:42]\n   String mismatch: \"exactly\" != actual (\"this one fails\")\n   'exa' != 'exp' (at byte 2)\n \n   5) Failure:\n-selftest::suite::int [file:42]\n+combined::int [file:42]\n   101 != value (\"extra note on failing test\")\n   101 != 100\n \n   6) Failure:\n-selftest::suite::int_fmt [file:42]\n+combined::int_fmt [file:42]\n   022 != value\n   0022 != 0144\n \n   7) Failure:\n-selftest::suite::bool [file:42]\n+combined::bool [file:42]\n   0 != value\n   0 != 1\n \n   8) Failure:\n-selftest::suite::ptr [file:42]\n-  Pointer mismatch: p1 != p2\n-  0x1 != 0x2\n-\n-  9) Failure:\n-selftest::suite::multiline_description [file:42]\n+combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  10) Failure:\n-selftest::suite::null_string [file:42]\n+  9) Failure:\n+combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \ndiff --git a/t/unit-tests/clar/test/expected/specific_test b/t/unit-tests/clar/test/expected/specific_test\nindex afa21509804..6c22e9f507d 100644\n--- a/t/unit-tests/clar/test/expected/specific_test\n+++ b/t/unit-tests/clar/test/expected/specific_test\n@@ -1,9 +1,9 @@\n-Loaded 1 suites: \n+Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n F\n \n   1) Failure:\n-selftest::suite::bool [file:42]\n+combined::bool [file:42]\n   0 != value\n   0 != 1\n \ndiff --git a/t/unit-tests/clar/test/expected/stop_on_failure b/t/unit-tests/clar/test/expected/stop_on_failure\nindex 1156ade0f92..c23610754f8 100644\n--- a/t/unit-tests/clar/test/expected/stop_on_failure\n+++ b/t/unit-tests/clar/test/expected/stop_on_failure\n@@ -1,8 +1,8 @@\n-Loaded 1 suites: \n+Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n F\n \n   1) Failure:\n-selftest::suite::1 [file:42]\n+combined::1 [file:42]\n   Function call failed: -1\n \ndiff --git a/t/unit-tests/clar/test/expected/suite_names b/t/unit-tests/clar/test/expected/suite_names\nindex 1b0f6397eb3..10d1538427e 100644\n--- a/t/unit-tests/clar/test/expected/suite_names\n+++ b/t/unit-tests/clar/test/expected/suite_names\n@@ -1,2 +1,2 @@\n Test suites (use -s<name> to run just one):\n-   0: selftest::suite\n+   0: combined\ndiff --git a/t/unit-tests/clar/test/expected/summary.xml b/t/unit-tests/clar/test/expected/summary.xml\nindex 9034a03d1fa..9a89d43a593 100644\n--- a/t/unit-tests/clar/test/expected/summary.xml\n+++ b/t/unit-tests/clar/test/expected/summary.xml\n@@ -27,10 +27,6 @@\n \t\t<testcase name=\"bool\" classname=\"selftest\" time=\"0.00\">\n \t\t\t<failure type=\"assert\"><![CDATA[0 != value\n 0 != 1]]></failure>\n-\t\t</testcase>\n-\t\t<testcase name=\"ptr\" classname=\"selftest\" time=\"0.00\">\n-\t\t\t<failure type=\"assert\"><![CDATA[Pointer mismatch: p1 != p2\n-0x1 != 0x2]]></failure>\n \t\t</testcase>\n \t\t<testcase name=\"multiline_description\" classname=\"selftest\" time=\"0.00\">\n \t\t\t<failure type=\"assert\"><![CDATA[Function call failed: −1\ndiff --git a/t/unit-tests/clar/test/expected/summary_with_filename b/t/unit-tests/clar/test/expected/summary_with_filename\nindex a5f4d405370..460160791d1 100644\n--- a/t/unit-tests/clar/test/expected/summary_with_filename\n+++ b/t/unit-tests/clar/test/expected/summary_with_filename\n@@ -1,53 +1,48 @@\n-Loaded 1 suites: \n+Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFFF\n+FFFFFFFFF\n \n   1) Failure:\n-selftest::suite::1 [file:42]\n+combined::1 [file:42]\n   Function call failed: -1\n \n   2) Failure:\n-selftest::suite::2 [file:42]\n+combined::2 [file:42]\n   Expression is not true: 100 == 101\n \n   3) Failure:\n-selftest::suite::strings [file:42]\n+combined::strings [file:42]\n   String mismatch: \"mismatched\" != actual (\"this one fails\")\n   'mismatched' != 'expected' (at byte 0)\n \n   4) Failure:\n-selftest::suite::strings_with_length [file:42]\n+combined::strings_with_length [file:42]\n   String mismatch: \"exactly\" != actual (\"this one fails\")\n   'exa' != 'exp' (at byte 2)\n \n   5) Failure:\n-selftest::suite::int [file:42]\n+combined::int [file:42]\n   101 != value (\"extra note on failing test\")\n   101 != 100\n \n   6) Failure:\n-selftest::suite::int_fmt [file:42]\n+combined::int_fmt [file:42]\n   022 != value\n   0022 != 0144\n \n   7) Failure:\n-selftest::suite::bool [file:42]\n+combined::bool [file:42]\n   0 != value\n   0 != 1\n \n   8) Failure:\n-selftest::suite::ptr [file:42]\n-  Pointer mismatch: p1 != p2\n-  0x1 != 0x2\n-\n-  9) Failure:\n-selftest::suite::multiline_description [file:42]\n+combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  10) Failure:\n-selftest::suite::null_string [file:42]\n+  9) Failure:\n+combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \ndiff --git a/t/unit-tests/clar/test/expected/summary_without_filename b/t/unit-tests/clar/test/expected/summary_without_filename\nindex 5984502773e..7874c1d98bc 100644\n--- a/t/unit-tests/clar/test/expected/summary_without_filename\n+++ b/t/unit-tests/clar/test/expected/summary_without_filename\n@@ -1,53 +1,48 @@\n-Loaded 1 suites: \n+Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFFF\n+FFFFFFFFF\n \n   1) Failure:\n-selftest::suite::1 [file:42]\n+combined::1 [file:42]\n   Function call failed: -1\n \n   2) Failure:\n-selftest::suite::2 [file:42]\n+combined::2 [file:42]\n   Expression is not true: 100 == 101\n \n   3) Failure:\n-selftest::suite::strings [file:42]\n+combined::strings [file:42]\n   String mismatch: \"mismatched\" != actual (\"this one fails\")\n   'mismatched' != 'expected' (at byte 0)\n \n   4) Failure:\n-selftest::suite::strings_with_length [file:42]\n+combined::strings_with_length [file:42]\n   String mismatch: \"exactly\" != actual (\"this one fails\")\n   'exa' != 'exp' (at byte 2)\n \n   5) Failure:\n-selftest::suite::int [file:42]\n+combined::int [file:42]\n   101 != value (\"extra note on failing test\")\n   101 != 100\n \n   6) Failure:\n-selftest::suite::int_fmt [file:42]\n+combined::int_fmt [file:42]\n   022 != value\n   0022 != 0144\n \n   7) Failure:\n-selftest::suite::bool [file:42]\n+combined::bool [file:42]\n   0 != value\n   0 != 1\n \n   8) Failure:\n-selftest::suite::ptr [file:42]\n-  Pointer mismatch: p1 != p2\n-  0x1 != 0x2\n-\n-  9) Failure:\n-selftest::suite::multiline_description [file:42]\n+combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  10) Failure:\n-selftest::suite::null_string [file:42]\n+  9) Failure:\n+combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \ndiff --git a/t/unit-tests/clar/test/expected/tap b/t/unit-tests/clar/test/expected/tap\nindex 3dc4973dfa1..bddbd5dfe98 100644\n--- a/t/unit-tests/clar/test/expected/tap\n+++ b/t/unit-tests/clar/test/expected/tap\n@@ -1,6 +1,6 @@\n TAP version 13\n-# start of suite 1: selftest::suite\n-not ok 1 - selftest::suite::1\n+# start of suite 1: combined\n+not ok 1 - combined::1\n     ---\n     reason: |\n       Function call failed: -1\n@@ -9,7 +9,7 @@ not ok 1 - selftest::suite::1\n       line: 42\n       function: 'func'\n     ---\n-not ok 2 - selftest::suite::2\n+not ok 2 - combined::2\n     ---\n     reason: |\n       Expression is not true: 100 == 101\n@@ -18,7 +18,7 @@ not ok 2 - selftest::suite::2\n       line: 42\n       function: 'func'\n     ---\n-not ok 3 - selftest::suite::strings\n+not ok 3 - combined::strings\n     ---\n     reason: |\n       String mismatch: \"mismatched\" != actual (\"this one fails\")\n@@ -28,7 +28,7 @@ not ok 3 - selftest::suite::strings\n       line: 42\n       function: 'func'\n     ---\n-not ok 4 - selftest::suite::strings_with_length\n+not ok 4 - combined::strings_with_length\n     ---\n     reason: |\n       String mismatch: \"exactly\" != actual (\"this one fails\")\n@@ -38,7 +38,7 @@ not ok 4 - selftest::suite::strings_with_length\n       line: 42\n       function: 'func'\n     ---\n-not ok 5 - selftest::suite::int\n+not ok 5 - combined::int\n     ---\n     reason: |\n       101 != value (\"extra note on failing test\")\n@@ -48,7 +48,7 @@ not ok 5 - selftest::suite::int\n       line: 42\n       function: 'func'\n     ---\n-not ok 6 - selftest::suite::int_fmt\n+not ok 6 - combined::int_fmt\n     ---\n     reason: |\n       022 != value\n@@ -58,7 +58,7 @@ not ok 6 - selftest::suite::int_fmt\n       line: 42\n       function: 'func'\n     ---\n-not ok 7 - selftest::suite::bool\n+not ok 7 - combined::bool\n     ---\n     reason: |\n       0 != value\n@@ -68,17 +68,7 @@ not ok 7 - selftest::suite::bool\n       line: 42\n       function: 'func'\n     ---\n-not ok 8 - selftest::suite::ptr\n-    ---\n-    reason: |\n-      Pointer mismatch: p1 != p2\n-      0x1 != 0x2\n-    at:\n-      file: 'file'\n-      line: 42\n-      function: 'func'\n-    ---\n-not ok 9 - selftest::suite::multiline_description\n+not ok 8 - combined::multiline_description\n     ---\n     reason: |\n       Function call failed: -1\n@@ -89,7 +79,7 @@ not ok 9 - selftest::suite::multiline_description\n       line: 42\n       function: 'func'\n     ---\n-not ok 10 - selftest::suite::null_string\n+not ok 9 - combined::null_string\n     ---\n     reason: |\n       String mismatch: \"expected\" != actual (\"this one fails\")\n@@ -99,4 +89,4 @@ not ok 10 - selftest::suite::null_string\n       line: 42\n       function: 'func'\n     ---\n-1..10\n+1..9\ndiff --git a/t/unit-tests/clar/test/expected/without_arguments b/t/unit-tests/clar/test/expected/without_arguments\nindex 08b67b874cb..1111d418a06 100644\n--- a/t/unit-tests/clar/test/expected/without_arguments\n+++ b/t/unit-tests/clar/test/expected/without_arguments\n@@ -1,53 +1,48 @@\n-Loaded 1 suites: \n+Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFFF\n+FFFFFFFFF\n \n   1) Failure:\n-selftest::suite::1 [file:42]\n+combined::1 [file:42]\n   Function call failed: -1\n \n   2) Failure:\n-selftest::suite::2 [file:42]\n+combined::2 [file:42]\n   Expression is not true: 100 == 101\n \n   3) Failure:\n-selftest::suite::strings [file:42]\n+combined::strings [file:42]\n   String mismatch: \"mismatched\" != actual (\"this one fails\")\n   'mismatched' != 'expected' (at byte 0)\n \n   4) Failure:\n-selftest::suite::strings_with_length [file:42]\n+combined::strings_with_length [file:42]\n   String mismatch: \"exactly\" != actual (\"this one fails\")\n   'exa' != 'exp' (at byte 2)\n \n   5) Failure:\n-selftest::suite::int [file:42]\n+combined::int [file:42]\n   101 != value (\"extra note on failing test\")\n   101 != 100\n \n   6) Failure:\n-selftest::suite::int_fmt [file:42]\n+combined::int_fmt [file:42]\n   022 != value\n   0022 != 0144\n \n   7) Failure:\n-selftest::suite::bool [file:42]\n+combined::bool [file:42]\n   0 != value\n   0 != 1\n \n   8) Failure:\n-selftest::suite::ptr [file:42]\n-  Pointer mismatch: p1 != p2\n-  0x1 != 0x2\n-\n-  9) Failure:\n-selftest::suite::multiline_description [file:42]\n+combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  10) Failure:\n-selftest::suite::null_string [file:42]\n+  9) Failure:\n+combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \ndiff --git a/t/unit-tests/clar/test/main.c b/t/unit-tests/clar/test/main.c\nindex b1ba2996f13..94af4406437 100644\n--- a/t/unit-tests/clar/test/main.c\n+++ b/t/unit-tests/clar/test/main.c\n@@ -3,7 +3,7 @@\n \n #include \"selftest.h\"\n \n-const char *selftest_binary_path;\n+const char *selftest_suite_directory;\n \n #ifdef _WIN32\n int __cdecl main(int argc, char *argv[])\n@@ -12,12 +12,12 @@ int main(int argc, char *argv[])\n #endif\n {\n \tif (argc < 2) {\n-\t\tfprintf(stderr, \"usage: %s <selftest-suite-executable> <options>\\n\",\n+\t\tfprintf(stderr, \"usage: %s <selftest-suite-directory> <options>\\n\",\n \t\t\targv[0]);\n \t\texit(1);\n \t}\n \n-\tselftest_binary_path = argv[1];\n+\tselftest_suite_directory = argv[1];\n \tmemmove(argv + 1, argv + 2, argc - 1);\n \targc -= 1;\n \ndiff --git a/t/unit-tests/clar/test/selftest.c b/t/unit-tests/clar/test/selftest.c\nindex abd585f4e4a..eed83e45120 100644\n--- a/t/unit-tests/clar/test/selftest.c\n+++ b/t/unit-tests/clar/test/selftest.c\n@@ -59,38 +59,34 @@ static char *read_file(const char *path)\n \treturn content;\n }\n \n-static void run(const char *expected_output_file, int expected_error_code, ...)\n+static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)\n {\n \tSECURITY_ATTRIBUTES security_attributes = { 0 };\n \tPROCESS_INFORMATION process_info = { 0 };\n \tSTARTUPINFO startup_info = { 0 };\n+\tchar binary_path[4096] = { 0 };\n \tchar cmdline[4096] = { 0 };\n-\tchar *expected_output = NULL;\n \tchar *output = NULL;\n \tHANDLE stdout_write;\n \tHANDLE stdout_read;\n \tDWORD exit_code;\n-\tva_list ap;\n+\tsize_t i;\n+\n+\tsnprintf(binary_path, sizeof(binary_path), \"%s/%s_suite.exe\",\n+\t\t selftest_suite_directory, suite);\n \n \t/*\n \t * Assemble command line arguments. In theory we'd have to properly\n \t * quote them. In practice none of our tests actually care.\n \t */\n-\tva_start(ap, expected_error_code);\n-\tsnprintf(cmdline, sizeof(cmdline), \"selftest\");\n-\twhile (1) {\n+\tsnprintf(cmdline, sizeof(cmdline), suite);\n+\tfor (i = 0; i < nargs; i++) {\n \t\tsize_t cmdline_len = strlen(cmdline);\n-\t\tconst char *arg;\n-\n-\t\targ = va_arg(ap, const char *);\n-\t\tif (!arg)\n-\t\t\tbreak;\n-\n+\t\tconst char *arg = args[i];\n \t\tcl_assert(cmdline_len + strlen(arg) < sizeof(cmdline));\n \t\tsnprintf(cmdline + cmdline_len, sizeof(cmdline) - cmdline_len,\n \t\t\t \" %s\", arg);\n \t}\n-\tva_end(ap);\n \n \t/*\n \t * Create a pipe that we will use to read data from the child process.\n@@ -110,17 +106,39 @@ static void run(const char *expected_output_file, int expected_error_code, ...)\n \tstartup_info.hStdError = stdout_write;\n \tstartup_info.hStdOutput = stdout_write;\n \tstartup_info.dwFlags |= STARTF_USESTDHANDLES;\n-\tcl_assert_equal_b(1, CreateProcess(selftest_binary_path, cmdline, NULL, NULL, TRUE,\n+\tcl_assert_equal_b(1, CreateProcess(binary_path, cmdline, NULL, NULL, TRUE,\n \t\t\t\t\t   0, NULL, NULL, &startup_info, &process_info));\n \tcl_assert_equal_b(1, CloseHandle(stdout_write));\n \n \toutput = read_full(stdout_read, 1);\n \tcl_assert_equal_b(1, CloseHandle(stdout_read));\n \tcl_assert_equal_b(1, GetExitCodeProcess(process_info.hProcess, &exit_code));\n+\tcl_assert_equal_i(exit_code, expected_error_code);\n+\n+\treturn output;\n+}\n+\n+static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)\n+{\n+\tchar *expected_output = NULL;\n+\tchar *output = NULL;\n+\tconst char *args[16];\n+\tva_list ap;\n+\tsize_t i;\n+\n+\tva_start(ap, expected_error_code);\n+\tfor (i = 0; ; i++) {\n+\t\tconst char *arg = va_arg(ap, const char *);\n+\t\tif (!arg)\n+\t\t\tbreak;\n+\t\tcl_assert(i < sizeof(args) / sizeof(*args));\n+\t\targs[i] = arg;\n+\t}\n+\tva_end(ap);\n \n+\toutput = execute(suite, expected_error_code, args, i);\n \texpected_output = read_file(cl_fixture(expected_output_file));\n \tcl_assert_equal_s(output, expected_output);\n-\tcl_assert_equal_i(exit_code, expected_error_code);\n \n \tfree(expected_output);\n \tfree(output);\n@@ -180,29 +198,25 @@ static char *read_file(const char *path)\n \treturn data;\n }\n \n-static void run(const char *expected_output_file, int expected_error_code, ...)\n+static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs)\n {\n-\tconst char *argv[16];\n \tint pipe_fds[2];\n-\tva_list ap;\n \tpid_t pid;\n-\tint i;\n-\n-\tva_start(ap, expected_error_code);\n-\targv[0] = \"selftest\";\n-\tfor (i = 1; ; i++) {\n-\t\tcl_assert(i < sizeof(argv) / sizeof(*argv));\n-\n-\t\targv[i] = va_arg(ap, const char *);\n-\t\tif (!argv[i])\n-\t\t\tbreak;\n-\t}\n-\tva_end(ap);\n \n \tcl_must_pass(pipe(pipe_fds));\n \n \tpid = fork();\n \tif (!pid) {\n+\t\tconst char *final_args[17] = { NULL };\n+\t\tchar binary_path[4096];\n+\t\tsize_t len = 0;\n+\t\tsize_t i;\n+\n+\t\tcl_assert(nargs < sizeof(final_args) / sizeof(*final_args));\n+\t\tfinal_args[0] = suite;\n+\t\tfor (i = 0; i < nargs; i++)\n+\t\t\tfinal_args[i + 1] = args[i];\n+\n \t\tif (dup2(pipe_fds[1], STDOUT_FILENO) < 0 ||\n \t\t    dup2(pipe_fds[1], STDERR_FILENO) < 0 ||\n \t\t    close(0) < 0 ||\n@@ -210,11 +224,29 @@ static void run(const char *expected_output_file, int expected_error_code, ...)\n \t\t    close(pipe_fds[1]) < 0)\n \t\t\texit(1);\n \n-\t\texecv(selftest_binary_path, (char **) argv);\n+\t\tcl_assert(len + strlen(selftest_suite_directory) < sizeof(binary_path));\n+\t\tstrcpy(binary_path, selftest_suite_directory);\n+\t\tlen += strlen(selftest_suite_directory);\n+\n+\t\tcl_assert(len + 1 < sizeof(binary_path));\n+\t\tbinary_path[len] = '/';\n+\t\tlen += 1;\n+\n+\t\tcl_assert(len + strlen(suite) < sizeof(binary_path));\n+\t\tstrcpy(binary_path + len, suite);\n+\t\tlen += strlen(suite);\n+\n+\t\tcl_assert(len + strlen(\"_suite\") < sizeof(binary_path));\n+\t\tstrcpy(binary_path + len, \"_suite\");\n+\t\tlen += strlen(\"_suite\");\n+\n+\t\tbinary_path[len] = '\\0';\n+\n+\t\texecv(binary_path, (char **) final_args);\n \t\texit(1);\n \t} else if (pid > 0) {\n \t\tpid_t waited_pid;\n-\t\tchar *expected_output, *output;\n+\t\tchar *output;\n \t\tint stat;\n \n \t\tcl_must_pass(close(pipe_fds[1]));\n@@ -226,56 +258,78 @@ static void run(const char *expected_output_file, int expected_error_code, ...)\n \t\tcl_assert(WIFEXITED(stat));\n \t\tcl_assert_equal_i(WEXITSTATUS(stat), expected_error_code);\n \n-\t\texpected_output = read_file(cl_fixture(expected_output_file));\n-\t\tcl_assert_equal_s(output, expected_output);\n-\n-\t\tfree(expected_output);\n-\t\tfree(output);\n+\t\treturn output;\n \t} else {\n \t\tcl_fail(\"Fork failed.\");\n \t}\n+\n+\treturn NULL;\n+}\n+\n+static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...)\n+{\n+\tchar *expected_output, *output;\n+\tconst char *args[16];\n+\tva_list ap;\n+\tsize_t i;\n+\n+\tva_start(ap, expected_error_code);\n+\tfor (i = 0; ; i++) {\n+\t\tcl_assert(i < sizeof(args) / sizeof(*args));\n+\t\targs[i] = va_arg(ap, const char *);\n+\t\tif (!args[i])\n+\t\t\tbreak;\n+\t}\n+\tva_end(ap);\n+\n+\toutput = execute(suite, expected_error_code, args, i);\n+\texpected_output = read_file(cl_fixture(expected_output_file));\n+\tcl_assert_equal_s(output, expected_output);\n+\n+\tfree(expected_output);\n+\tfree(output);\n }\n #endif\n \n void test_selftest__help(void)\n {\n-\tcl_invoke(run(\"help\", 1, \"-h\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"help\", 1, \"-h\", NULL));\n }\n \n void test_selftest__without_arguments(void)\n {\n-\tcl_invoke(run(\"without_arguments\", 10, NULL));\n+\tcl_invoke(assert_output(\"combined\", \"without_arguments\", 9, NULL));\n }\n \n void test_selftest__specific_test(void)\n {\n-\tcl_invoke(run(\"specific_test\", 1, \"-sselftest::suite::bool\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"specific_test\", 1, \"-scombined::bool\", NULL));\n }\n \n void test_selftest__stop_on_failure(void)\n {\n-\tcl_invoke(run(\"stop_on_failure\", 1, \"-Q\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"stop_on_failure\", 1, \"-Q\", NULL));\n }\n \n void test_selftest__quiet(void)\n {\n-\tcl_invoke(run(\"quiet\", 10, \"-q\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"quiet\", 9, \"-q\", NULL));\n }\n \n void test_selftest__tap(void)\n {\n-\tcl_invoke(run(\"tap\", 10, \"-t\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"tap\", 9, \"-t\", NULL));\n }\n \n void test_selftest__suite_names(void)\n {\n-\tcl_invoke(run(\"suite_names\", 0, \"-l\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"suite_names\", 0, \"-l\", NULL));\n }\n \n void test_selftest__summary_without_filename(void)\n {\n \tstruct stat st;\n-\tcl_invoke(run(\"summary_without_filename\", 10, \"-r\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"summary_without_filename\", 9, \"-r\", NULL));\n \t/* The summary contains timestamps, so we cannot verify its contents. */\n \tcl_must_pass(stat(\"summary.xml\", &st));\n }\n@@ -283,7 +337,34 @@ void test_selftest__summary_without_filename(void)\n void test_selftest__summary_with_filename(void)\n {\n \tstruct stat st;\n-\tcl_invoke(run(\"summary_with_filename\", 10, \"-rdifferent.xml\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"summary_with_filename\", 9, \"-rdifferent.xml\", NULL));\n \t/* The summary contains timestamps, so we cannot verify its contents. */\n \tcl_must_pass(stat(\"different.xml\", &st));\n }\n+\n+void test_selftest__pointer_equal(void)\n+{\n+\tconst char *args[] = {\n+\t\t\"-spointer::equal\",\n+\t\t\"-t\"\n+\t};\n+\tchar *output = execute(\"pointer\", 0, args, 2);\n+\tcl_assert_equal_s(output,\n+\t\t   \"TAP version 13\\n\"\n+\t\t   \"# start of suite 1: pointer\\n\"\n+\t\t   \"ok 1 - pointer::equal\\n\"\n+\t\t   \"1..1\\n\"\n+\t);\n+\tfree(output);\n+}\n+\n+void test_selftest__pointer_unequal(void)\n+{\n+\tconst char *args[] = {\n+\t\t\"-spointer::unequal\",\n+\t};\n+\tchar *output = execute(\"pointer\", 1, args, 1);\n+\tcl_assert(output);\n+\tcl_assert(strstr(output, \"Pointer mismatch: \"));\n+\tfree(output);\n+}\ndiff --git a/t/unit-tests/clar/test/selftest.h b/t/unit-tests/clar/test/selftest.h\nindex 220a350c504..c24e0c5af41 100644\n--- a/t/unit-tests/clar/test/selftest.h\n+++ b/t/unit-tests/clar/test/selftest.h\n@@ -1,3 +1,3 @@\n #include \"clar.h\"\n \n-extern const char *selftest_binary_path;\n+extern const char *selftest_suite_directory;\ndiff --git a/t/unit-tests/clar/test/selftest_suite/CMakeLists.txt b/t/unit-tests/clar/test/selftest_suite/CMakeLists.txt\ndeleted file mode 100644\nindex 9597d6711a7..00000000000\n--- a/t/unit-tests/clar/test/selftest_suite/CMakeLists.txt\n+++ /dev/null\n@@ -1,40 +0,0 @@\n-find_package(Python COMPONENTS Interpreter REQUIRED)\n-\n-add_custom_command(OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/clar.suite\"\n-\tCOMMAND \"${Python_EXECUTABLE}\" \"${CMAKE_SOURCE_DIR}/generate.py\" --output \"${CMAKE_CURRENT_BINARY_DIR}\"\n-\tDEPENDS main.c selftest_suite.c\n-\tWORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"\n-)\n-\n-add_executable(selftest_suite)\n-set_target_properties(selftest_suite PROPERTIES\n-\tC_STANDARD 90\n-\tC_STANDARD_REQUIRED ON\n-\tC_EXTENSIONS OFF\n-)\n-\n-# MSVC generates all kinds of warnings. We may want to fix these in the future\n-# and then unconditionally treat warnings as errors.\n-if(NOT MSVC)\n-\tset_target_properties(selftest_suite PROPERTIES\n-\t\tCOMPILE_WARNING_AS_ERROR ON\n-\t)\n-endif()\n-\n-target_sources(selftest_suite PRIVATE\n-\tmain.c\n-\tselftest_suite.c\n-\t\"${CMAKE_CURRENT_BINARY_DIR}/clar.suite\"\n-)\n-target_compile_definitions(selftest_suite PRIVATE\n-\tCLAR_FIXTURE_PATH=\"${CMAKE_CURRENT_SOURCE_DIR}/resources/\"\n-\tCLAR_SELFTEST\n-)\n-target_compile_options(selftest_suite PRIVATE\n-\t$<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>\n-)\n-target_include_directories(selftest_suite PRIVATE\n-\t\"${CMAKE_SOURCE_DIR}\"\n-\t\"${CMAKE_CURRENT_BINARY_DIR}\"\n-)\n-target_link_libraries(selftest_suite clar)\ndiff --git a/t/unit-tests/clar/test/suites/CMakeLists.txt b/t/unit-tests/clar/test/suites/CMakeLists.txt\nnew file mode 100644\nindex 00000000000..fa8ab9416a8\n--- /dev/null\n+++ b/t/unit-tests/clar/test/suites/CMakeLists.txt\n@@ -0,0 +1,53 @@\n+list(APPEND suites\n+\t\"combined\"\n+\t\"pointer\"\n+)\n+\n+foreach(suite IN LISTS suites)\n+\tadd_custom_command(OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite\"\n+\t\tCOMMAND \"${Python_EXECUTABLE}\"\n+\t\t\t\"${CMAKE_SOURCE_DIR}/generate.py\"\n+\t\t\t\"${CMAKE_CURRENT_SOURCE_DIR}/${suite}.c\"\n+\t\t\t--output \"${CMAKE_CURRENT_BINARY_DIR}/${suite}\"\n+\t\tDEPENDS ${suite}.c\n+\t\tWORKING_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}\"\n+\t)\n+\n+\tadd_executable(${suite}_suite)\n+\tset_target_properties(${suite}_suite PROPERTIES\n+\t\tC_STANDARD 90\n+\t\tC_STANDARD_REQUIRED ON\n+\t\tC_EXTENSIONS OFF\n+\t)\n+\n+\t# MSVC generates all kinds of warnings. We may want to fix these in the future\n+\t# and then unconditionally treat warnings as errors.\n+\tif(NOT MSVC)\n+\t\tset_target_properties(${suite}_suite PROPERTIES\n+\t\t\tCOMPILE_WARNING_AS_ERROR ON\n+\t\t)\n+\tendif()\n+\n+\ttarget_sources(${suite}_suite PRIVATE\n+\t\tmain.c\n+\t\t${suite}.c\n+\t\t\"${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite\"\n+\t)\n+\ttarget_compile_definitions(${suite}_suite PRIVATE\n+\t\tCLAR_FIXTURE_PATH=\"${CMAKE_CURRENT_SOURCE_DIR}/resources/\"\n+\t\tCLAR_SELFTEST\n+\t)\n+\ttarget_compile_options(${suite}_suite PRIVATE\n+\t\t$<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall>\n+\t)\n+\ttarget_include_directories(${suite}_suite PRIVATE\n+\t\t\"${CMAKE_SOURCE_DIR}\"\n+\t\t\"${CMAKE_CURRENT_BINARY_DIR}/${suite}\"\n+\t)\n+\ttarget_link_libraries(${suite}_suite clar)\n+\n+\tadd_test(NAME build_${suite}_suite\n+\t\tCOMMAND \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --config \"$<CONFIG>\" --target selftest\n+\t)\n+\tset_tests_properties(build_${suite}_suite PROPERTIES FIXTURES_SETUP clar_test_fixture)\n+endforeach()\ndiff --git a/t/unit-tests/clar/test/selftest_suite/selftest_suite.c b/t/unit-tests/clar/test/suites/combined.c\nsimilarity index 72%\nrename from t/unit-tests/clar/test/selftest_suite/selftest_suite.c\nrename to t/unit-tests/clar/test/suites/combined.c\nindex 77f872128c7..e8b41c98c37 100644\n--- a/t/unit-tests/clar/test/selftest_suite/selftest_suite.c\n+++ b/t/unit-tests/clar/test/suites/combined.c\n@@ -11,14 +11,14 @@ static int file_size(const char *filename)\n \treturn -1;\n }\n \n-void test_selftest_suite__cleanup(void)\n+void test_combined__cleanup(void)\n {\n \tcl_fixture_cleanup(\"test\");\n \n \tcl_assert(file_size(\"test/file\") == -1);\n }\n \n-void test_selftest_suite__1(void)\n+void test_combined__1(void)\n {\n \tcl_assert(1);\n \tcl_must_pass(0);  /* 0 == success */\n@@ -26,7 +26,7 @@ void test_selftest_suite__1(void)\n \tcl_must_pass(-1); /* demonstrate a failing call */\n }\n \n-void test_selftest_suite__2(void)\n+void test_combined__2(void)\n {\n \tcl_fixture_sandbox(\"test\");\n \n@@ -35,7 +35,7 @@ void test_selftest_suite__2(void)\n \tcl_assert(100 == 101);\n }\n \n-void test_selftest_suite__strings(void)\n+void test_combined__strings(void)\n {\n \tconst char *actual = \"expected\";\n \tcl_assert_equal_s(\"expected\", actual);\n@@ -43,7 +43,7 @@ void test_selftest_suite__strings(void)\n \tcl_assert_equal_s_(\"mismatched\", actual, \"this one fails\");\n }\n \n-void test_selftest_suite__strings_with_length(void)\n+void test_combined__strings_with_length(void)\n {\n \tconst char *actual = \"expected\";\n \tcl_assert_equal_strn(\"expected_\", actual, 8);\n@@ -52,39 +52,32 @@ void test_selftest_suite__strings_with_length(void)\n \tcl_assert_equal_strn_(\"exactly\", actual, 3, \"this one fails\");\n }\n \n-void test_selftest_suite__int(void)\n+void test_combined__int(void)\n {\n \tint value = 100;\n \tcl_assert_equal_i(100, value);\n \tcl_assert_equal_i_(101, value, \"extra note on failing test\");\n }\n \n-void test_selftest_suite__int_fmt(void)\n+void test_combined__int_fmt(void)\n {\n \tint value = 100;\n \tcl_assert_equal_i_fmt(022, value, \"%04o\");\n }\n \n-void test_selftest_suite__bool(void)\n+void test_combined__bool(void)\n {\n \tint value = 100;\n \tcl_assert_equal_b(1, value);       /* test equality as booleans */\n \tcl_assert_equal_b(0, value);\n }\n \n-void test_selftest_suite__ptr(void)\n-{\n-\tvoid *p1 = (void *)0x1, *p2 = (void *)0x2;\n-\tcl_assert_equal_p(p1, p1); /* pointers to same object */\n-\tcl_assert_equal_p(p1, p2);\n-}\n-\n-void test_selftest_suite__multiline_description(void)\n+void test_combined__multiline_description(void)\n {\n \tcl_must_pass_(-1, \"description line 1\\ndescription line 2\");\n }\n \n-void test_selftest_suite__null_string(void)\n+void test_combined__null_string(void)\n {\n \tconst char *actual = NULL;\n \tcl_assert_equal_s(actual, actual);\ndiff --git a/t/unit-tests/clar/test/selftest_suite/main.c b/t/unit-tests/clar/test/suites/main.c\nsimilarity index 100%\nrename from t/unit-tests/clar/test/selftest_suite/main.c\nrename to t/unit-tests/clar/test/suites/main.c\ndiff --git a/t/unit-tests/clar/test/suites/pointer.c b/t/unit-tests/clar/test/suites/pointer.c\nnew file mode 100644\nindex 00000000000..20535b159e0\n--- /dev/null\n+++ b/t/unit-tests/clar/test/suites/pointer.c\n@@ -0,0 +1,13 @@\n+#include \"clar.h\"\n+\n+void test_pointer__equal(void)\n+{\n+\tvoid *p1 = (void *)0x1;\n+\tcl_assert_equal_p(p1, p1);\n+}\n+\n+void test_pointer__unequal(void)\n+{\n+\tvoid *p1 = (void *)0x1, *p2 = (void *)0x2;\n+\tcl_assert_equal_p(p1, p2);\n+}\ndiff --git a/t/unit-tests/clar/test/selftest_suite/resources/test/file b/t/unit-tests/clar/test/suites/resources/test/file\nsimilarity index 100%\nrename from t/unit-tests/clar/test/selftest_suite/resources/test/file\nrename to t/unit-tests/clar/test/suites/resources/test/file\n\n---\nbase-commit: 0e5211b3ff1912409264bc2ec5e5563157fa6f9a\nchange-id: 20250918-pks-clar-update-b90848d97d1a\n\n"},{"id":"526964","messageId":"20250922190928.GB2205919@coredump.intra.peff.net","threadId":"64181","inReplyTo":"20250922-pks-clar-update-v1-1-9154e7d1b7a1@pks.im","subject":"Re: [PATCH] t/unit-tests: update to 10e96bc","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-09-22T19:09:28Z","receivedAt":"2025-09-22T19:09:29Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Sep 22, 2025 at 03:16:11PM +0200, Patrick Steinhardt wrote:\n\n> Update to 10e96bc (Merge pull request #127 from\n> pks-gitlab/pks-ci-improvements, 2025-09-22). This commit includes a\n> couple of changes:\n> \n>   - The GitHub CI has been updated to include a 32 bit CI job.\n>     Furthermore, the jobs now compile with \"-Werror\" and more warnings\n>     enabled.\n> \n>   - An issue was addressed where `uintptr_t` is not available on\n>     NonStop [1].\n\nThanks, the uintptr_t solution looks reasonable to me based on the\nearlier discussion. I don't really have the context to review the rest\nof it, but it builds and passes tests for me here. ;)\n\n-Peff\n"}]}