{"thread":{"id":"64582","subject":"[PATCH 0/2] Update clar for improved integer handling","startedAt":"2025-12-05T12:58:05Z","lastAt":"2026-01-08T12:46:18Z","messageCount":20,"participants":["Patrick Steinhardt","Jeff King","Junio C Hamano","Karthik Nayak"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"531698","messageId":"20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im","threadId":"64582","inReplyTo":null,"subject":"[PATCH 0/2] Update clar for improved integer handling","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-05T12:57:51Z","receivedAt":"2025-12-05T12:58:05Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis patch series updates clar. Most importantly, the update contains\nproperly typed handling of integers as well as a set of new asserts that\nperform relative comparisons, like \"less than\" or \"greater or equal\".\n\nThanks!\n\nPatrick\n\n---\nPatrick Steinhardt (2):\n      t/unit-tests: update clar to 39f11fe\n      t/unit-tests: demonstrate use of integer comparison assertions\n\n t/unit-tests/clar/.github/workflows/ci.yml         |   2 +-\n t/unit-tests/clar/clar.c                           | 146 ++++++++++++++++++++-\n t/unit-tests/clar/clar.h                           |  82 +++++++++++-\n t/unit-tests/clar/clar/print.h                     |   2 +-\n t/unit-tests/clar/test/expected/quiet              |  40 +++++-\n .../clar/test/expected/summary_with_filename       |  42 +++++-\n .../clar/test/expected/summary_without_filename    |  42 +++++-\n t/unit-tests/clar/test/expected/tap                |  88 +++++++++++--\n t/unit-tests/clar/test/expected/without_arguments  |  42 +++++-\n t/unit-tests/clar/test/selftest.c                  |  10 +-\n t/unit-tests/clar/test/suites/combined.c           |  65 ++++++++-\n t/unit-tests/u-reftable-record.c                   |  22 ++--\n t/unit-tests/unit-test.h                           |   6 -\n 13 files changed, 519 insertions(+), 70 deletions(-)\n\n\n---\nbase-commit: f0ef5b6d9bcc258e4cbef93839d1b7465d5212b9\nchange-id: 20251205-b4-pks-clar-update-4fb022908ced\n\n"},{"id":"531699","messageId":"20251205-b4-pks-clar-update-v1-1-fd70aac2ab90@pks.im","threadId":"64582","inReplyTo":"20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im","subject":"[PATCH 1/2] t/unit-tests: update clar to 39f11fe","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-05T12:57:52Z","receivedAt":"2025-12-05T12:58:07Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Update clar to commit 39f11fe (Merge pull request #131 from\npks-gitlab/pks-integer-double-evaluation, 2025-12-05). This commit\nincludes the following changes relevant to Git:\n\n  - There are now typesafe integer comparison functions. Furthermore,\n    the range of comparison functions has been included to also have\n    relative comparisons, like \"greater than\".\n\n  - There is a new `cl_failf()` macro that allows the caller to specify\n    an error message with formatting directives.\n\n  - The TAP format has been fixed to correctly terminate YAML blocks\n    with \"...\\n\" instead of \"---\\n\".\n\nNote that we already had a `cl_failf()` function declared in our own\nsources. This function is equivalent to the upstreamed function, so we\ncan simply drop it now.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/unit-tests/clar/.github/workflows/ci.yml         |   2 +-\n t/unit-tests/clar/clar.c                           | 146 ++++++++++++++++++++-\n t/unit-tests/clar/clar.h                           |  82 +++++++++++-\n t/unit-tests/clar/clar/print.h                     |   2 +-\n t/unit-tests/clar/test/expected/quiet              |  40 +++++-\n .../clar/test/expected/summary_with_filename       |  42 +++++-\n .../clar/test/expected/summary_without_filename    |  42 +++++-\n t/unit-tests/clar/test/expected/tap                |  88 +++++++++++--\n t/unit-tests/clar/test/expected/without_arguments  |  42 +++++-\n t/unit-tests/clar/test/selftest.c                  |  10 +-\n t/unit-tests/clar/test/suites/combined.c           |  65 ++++++++-\n t/unit-tests/unit-test.h                           |   6 -\n 12 files changed, 508 insertions(+), 59 deletions(-)\n\ndiff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml\nindex 4d4724222c..14cb4ed1d4 100644\n--- a/t/unit-tests/clar/.github/workflows/ci.yml\n+++ b/t/unit-tests/clar/.github/workflows/ci.yml\n@@ -53,7 +53,7 @@ jobs:\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@v4\n+      uses: actions/checkout@v6\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 d6176e50b2..e959a5ae02 100644\n--- a/t/unit-tests/clar/clar.c\n+++ b/t/unit-tests/clar/clar.c\n@@ -24,6 +24,14 @@\n #include <sys/types.h>\n #include <sys/stat.h>\n \n+#ifndef va_copy\n+#\tifdef __va_copy\n+#\t\tdefine va_copy(dst, src) __va_copy(dst, src)\n+#\telse\n+#\t\tdefine va_copy(dst, src) ((dst) = (src))\n+#\tendif\n+#endif\n+\n #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_WCHAR__)\n \t/*\n \t * uClibc can optionally be built without wchar support, in which case\n@@ -76,8 +84,10 @@\n #\t\t\tdefine S_ISDIR(x) ((x & _S_IFDIR) != 0)\n #\t\tendif\n #\t\tdefine p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)\n+#\t\tdefine p_vsnprintf _vsnprintf\n #\telse\n #\t\tdefine p_snprintf snprintf\n+#\t\tdefine p_vsnprintf vsnprintf\n #\tendif\n \n #\tdefine localtime_r(timer, buf) (localtime_s(buf, timer) == 0 ? buf : NULL)\n@@ -86,6 +96,7 @@\n #\tinclude <unistd.h>\n #\tdefine _MAIN_CC\n #\tdefine p_snprintf snprintf\n+#\tdefine p_vsnprintf vsnprintf\n \ttypedef struct stat STAT_T;\n #endif\n \n@@ -699,13 +710,14 @@ void clar__skip(void)\n \tabort_test();\n }\n \n-void clar__fail(\n+static void clar__failv(\n \tconst char *file,\n \tconst char *function,\n \tsize_t line,\n+\tint should_abort,\n \tconst char *error_msg,\n \tconst char *description,\n-\tint should_abort)\n+\tva_list args)\n {\n \tstruct clar_error *error;\n \n@@ -725,9 +737,19 @@ void clar__fail(\n \terror->line_number = _clar.invoke_line ? _clar.invoke_line : line;\n \terror->error_msg = error_msg;\n \n-\tif (description != NULL &&\n-\t    (error->description = strdup(description)) == NULL)\n-\t\tclar_abort(\"Failed to allocate description.\\n\");\n+\tif (description != NULL) {\n+\t\tva_list args_copy;\n+\t\tint len;\n+\n+\t\tva_copy(args_copy, args);\n+\t\tif ((len = p_vsnprintf(NULL, 0, description, args_copy)) < 0)\n+\t\t\tclar_abort(\"Failed to compute description.\");\n+\t\tva_end(args_copy);\n+\n+\t\tif ((error->description = calloc(1, len + 1)) == NULL)\n+\t\t\tclar_abort(\"Failed to allocate buffer.\");\n+\t\tp_vsnprintf(error->description, len + 1, description, args);\n+\t}\n \n \t_clar.total_errors++;\n \t_clar.last_report->status = CL_TEST_FAILURE;\n@@ -736,6 +758,34 @@ void clar__fail(\n \t\tabort_test();\n }\n \n+void clar__failf(\n+\tconst char *file,\n+\tconst char *function,\n+\tsize_t line,\n+\tint should_abort,\n+\tconst char *error_msg,\n+\tconst char *description,\n+\t...)\n+{\n+\tva_list args;\n+\tva_start(args, description);\n+\tclar__failv(file, function, line, should_abort, error_msg,\n+\t\t    description, args);\n+\tva_end(args);\n+}\n+\n+void clar__fail(\n+\tconst char *file,\n+\tconst char *function,\n+\tsize_t line,\n+\tconst char *error_msg,\n+\tconst char *description,\n+\tint should_abort)\n+{\n+\tclar__failf(file, function, line, should_abort, error_msg,\n+\t\t    description ? \"%s\" : NULL, description);\n+}\n+\n void clar__assert(\n \tint condition,\n \tconst char *file,\n@@ -889,6 +939,92 @@ void clar__assert_equal(\n \t\tclar__fail(file, function, line, err, buf, should_abort);\n }\n \n+void clar__assert_compare_i(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tintmax_t value1,\n+\tintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...)\n+{\n+\tint fulfilled;\n+\tswitch (cmp) {\n+\tcase CLAR_COMPARISON_EQ:\n+\t\tfulfilled = value1 == value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LT:\n+\t\tfulfilled = value1 < value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LE:\n+\t\tfulfilled = value1 <= value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GT:\n+\t\tfulfilled = value1 > value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GE:\n+\t\tfulfilled = value1 >= value2;\n+\t\tbreak;\n+\tdefault:\n+\t\tcl_assert(0);\n+\t\treturn;\n+\t}\n+\n+\tif (!fulfilled) {\n+\t\tva_list args;\n+\t\tva_start(args, description);\n+\t\tclar__failv(file, func, line, should_abort, error,\n+\t\t\t    description, args);\n+\t\tva_end(args);\n+\t}\n+}\n+\n+void clar__assert_compare_u(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tuintmax_t value1,\n+\tuintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...)\n+{\n+\tint fulfilled;\n+\tswitch (cmp) {\n+\tcase CLAR_COMPARISON_EQ:\n+\t\tfulfilled = value1 == value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LT:\n+\t\tfulfilled = value1 < value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LE:\n+\t\tfulfilled = value1 <= value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GT:\n+\t\tfulfilled = value1 > value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GE:\n+\t\tfulfilled = value1 >= value2;\n+\t\tbreak;\n+\tdefault:\n+\t\tcl_assert(0);\n+\t\treturn;\n+\t}\n+\n+\tif (!fulfilled) {\n+\t\tva_list args;\n+\t\tva_start(args, description);\n+\t\tclar__failv(file, func, line, should_abort, error,\n+\t\t\t    description, args);\n+\t\tva_end(args);\n+\t}\n+}\n+\n void cl_set_cleanup(void (*cleanup)(void *), void *opaque)\n {\n \t_clar.local_cleanup = cleanup;\ndiff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h\nindex ca72292ae9..f7e4363022 100644\n--- a/t/unit-tests/clar/clar.h\n+++ b/t/unit-tests/clar/clar.h\n@@ -7,6 +7,7 @@\n #ifndef __CLAR_TEST_H__\n #define __CLAR_TEST_H__\n \n+#include <inttypes.h>\n #include <stdlib.h>\n #include <limits.h>\n \n@@ -149,6 +150,7 @@ const char *cl_fixture_basename(const char *fixture_name);\n  * Forced failure/warning\n  */\n #define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Test failed.\", desc, 1)\n+#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n #define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Warning during test execution:\", desc, 0)\n \n #define cl_skip() clar__skip()\n@@ -168,9 +170,42 @@ const char *cl_fixture_basename(const char *fixture_name);\n #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))\n #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))\n \n-#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))\n-#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))\n-#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))\n+#define cl_assert_compare_i_(i1, i2, cmp, error, ...) clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t\t\t\t\t\t\t     (i1), (i2), \"Expected comparison to hold: \" error, __VA_ARGS__)\n+#define cl_assert_compare_i(i1, i2, cmp, error, fmt) do { \\\n+\tintmax_t v1 = (i1), v2 = (i2); \\\n+\tclar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t       v1, v2, \"Expected comparison to hold: \" error, fmt, v1, v2); \\\n+} while (0)\n+#define cl_assert_equal_i_(i1, i2, ...)    cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2, __VA_ARGS__)\n+#define cl_assert_equal_i(i1, i2)          cl_assert_compare_i (i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2, \"%\"PRIdMAX \" != %\"PRIdMAX)\n+#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2,  fmt \" != \" fmt, (int)(i1), (int)(i2))\n+#define cl_assert_lt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LT, #i1 \" < \" #i2, __VA_ARGS__)\n+#define cl_assert_lt_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LT, #i1 \" < \" #i2, \"%\"PRIdMAX \" >= %\"PRIdMAX)\n+#define cl_assert_le_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LE, #i1 \" <= \" #i2, __VA_ARGS__)\n+#define cl_assert_le_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LE, #i1 \" <= \" #i2, \"%\"PRIdMAX \" > %\"PRIdMAX)\n+#define cl_assert_gt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GT, #i1 \" > \" #i2, __VA_ARGS__)\n+#define cl_assert_gt_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GT, #i1 \" > \" #i2, \"%\"PRIdMAX \" <= %\"PRIdMAX)\n+#define cl_assert_ge_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GE, #i1 \" >= \" #i2, __VA_ARGS__)\n+#define cl_assert_ge_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GE, #i1 \" >= \" #i2, \"%\"PRIdMAX \" < %\"PRIdMAX)\n+\n+#define cl_assert_compare_u_(u1, u2, cmp, error, ...) clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t\t\t\t\t\t\t     (u1), (u2), \"Expected comparison to hold: \" error, __VA_ARGS__)\n+#define cl_assert_compare_u(u1, u2, cmp, error, fmt) do { \\\n+\tuintmax_t v1 = (u1), v2 = (u2); \\\n+\tclar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t       v1, v2, \"Expected comparison to hold: \" error, fmt, v1, v2); \\\n+} while (0)\n+#define cl_assert_equal_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_EQ, #u1 \" == \" #u2, __VA_ARGS__)\n+#define cl_assert_equal_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_EQ, #u1 \" == \" #u2, \"%\"PRIuMAX \" != %\"PRIuMAX)\n+#define cl_assert_lt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LT, #u1 \" < \" #u2, __VA_ARGS__)\n+#define cl_assert_lt_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LT, #u1 \" < \" #u2, \"%\"PRIuMAX \" >= %\"PRIuMAX)\n+#define cl_assert_le_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LE, #u1 \" <= \" #u2, __VA_ARGS__)\n+#define cl_assert_le_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LE, #u1 \" <= \" #u2, \"%\"PRIuMAX \" > %\"PRIuMAX)\n+#define cl_assert_gt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GT, #u1 \" > \" #u2, __VA_ARGS__)\n+#define cl_assert_gt_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GT, #u1 \" > \" #u2, \"%\"PRIuMAX \" <= %\"PRIuMAX)\n+#define cl_assert_ge_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GE, #u1 \" >= \" #u2, __VA_ARGS__)\n+#define cl_assert_ge_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GE, #u1 \" >= \" #u2, \"%\"PRIuMAX \" < %\"PRIuMAX)\n \n #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))\n \n@@ -186,6 +221,15 @@ void clar__fail(\n \tconst char *description,\n \tint should_abort);\n \n+void clar__failf(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tconst char *error,\n+\tconst char *description,\n+\t...);\n+\n void clar__assert(\n \tint condition,\n \tconst char *file,\n@@ -204,6 +248,38 @@ void clar__assert_equal(\n \tconst char *fmt,\n \t...);\n \n+enum clar_comparison {\n+\tCLAR_COMPARISON_EQ,\n+\tCLAR_COMPARISON_LT,\n+\tCLAR_COMPARISON_LE,\n+\tCLAR_COMPARISON_GT,\n+\tCLAR_COMPARISON_GE,\n+};\n+\n+void clar__assert_compare_i(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tintmax_t value1,\n+\tintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...);\n+\n+void clar__assert_compare_u(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tuintmax_t value1,\n+\tuintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...);\n+\n void clar__set_invokepoint(\n \tconst char *file,\n \tconst char *func,\ndiff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h\nindex 89b66591d7..6a2321b399 100644\n--- a/t/unit-tests/clar/clar/print.h\n+++ b/t/unit-tests/clar/clar/print.h\n@@ -164,7 +164,7 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,\n \t\t\tprintf(\"      file: '\"); print_escaped(error->file); printf(\"'\\n\");\n \t\t\tprintf(\"      line: %\" PRIuMAX \"\\n\", error->line_number);\n \t\t\tprintf(\"      function: '%s'\\n\", error->function);\n-\t\t\tprintf(\"    ---\\n\");\n+\t\t\tprintf(\"    ...\\n\");\n \t\t}\n \n \t\tbreak;\ndiff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet\nindex 280c99d8ad..a93273b5a2 100644\n--- a/t/unit-tests/clar/test/expected/quiet\n+++ b/t/unit-tests/clar/test/expected/quiet\n@@ -18,27 +18,57 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\ndiff --git a/t/unit-tests/clar/test/expected/summary_with_filename b/t/unit-tests/clar/test/expected/summary_with_filename\nindex 460160791d..a9471cc7d5 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,6 +1,6 @@\n Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFF\n+FFFFFFFFFFFFFFF\n \n   1) Failure:\n combined::1 [file:42]\n@@ -22,28 +22,58 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n written summary file to different.xml\ndiff --git a/t/unit-tests/clar/test/expected/summary_without_filename b/t/unit-tests/clar/test/expected/summary_without_filename\nindex 7874c1d98b..83ba770d00 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,6 +1,6 @@\n Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFF\n+FFFFFFFFFFFFFFF\n \n   1) Failure:\n combined::1 [file:42]\n@@ -22,28 +22,58 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n written summary file to summary.xml\ndiff --git a/t/unit-tests/clar/test/expected/tap b/t/unit-tests/clar/test/expected/tap\nindex bddbd5dfe9..e67118d3ae 100644\n--- a/t/unit-tests/clar/test/expected/tap\n+++ b/t/unit-tests/clar/test/expected/tap\n@@ -8,7 +8,7 @@ not ok 1 - combined::1\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 2 - combined::2\n     ---\n     reason: |\n@@ -17,7 +17,7 @@ not ok 2 - combined::2\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 3 - combined::strings\n     ---\n     reason: |\n@@ -27,7 +27,7 @@ not ok 3 - combined::strings\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 4 - combined::strings_with_length\n     ---\n     reason: |\n@@ -37,28 +37,38 @@ not ok 4 - combined::strings_with_length\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 5 - combined::int\n     ---\n     reason: |\n-      101 != value (\"extra note on failing test\")\n+      Expected comparison to hold: 101 == value\n       101 != 100\n     at:\n       file: 'file'\n       line: 42\n       function: 'func'\n+    ...\n+not ok 6 - combined::int_note\n     ---\n-not ok 6 - combined::int_fmt\n+    reason: |\n+      Expected comparison to hold: 101 == value\n+      extra note on failing test\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 7 - combined::int_fmt\n     ---\n     reason: |\n-      022 != value\n+      Expected comparison to hold: 022 == value\n       0022 != 0144\n     at:\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n-not ok 7 - combined::bool\n+    ...\n+not ok 8 - combined::bool\n     ---\n     reason: |\n       0 != value\n@@ -67,8 +77,8 @@ not ok 7 - combined::bool\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n-not ok 8 - combined::multiline_description\n+    ...\n+not ok 9 - combined::multiline_description\n     ---\n     reason: |\n       Function call failed: -1\n@@ -78,8 +88,8 @@ not ok 8 - combined::multiline_description\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n-not ok 9 - combined::null_string\n+    ...\n+not ok 10 - combined::null_string\n     ---\n     reason: |\n       String mismatch: \"expected\" != actual (\"this one fails\")\n@@ -88,5 +98,55 @@ not ok 9 - combined::null_string\n       file: 'file'\n       line: 42\n       function: 'func'\n+    ...\n+not ok 11 - combined::failf\n+    ---\n+    reason: |\n+      Test failed.\n+      some reason: foo\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 12 - combined::compare_i\n     ---\n-1..9\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      2 >= 1\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 13 - combined::compare_i_with_format\n+    ---\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      foo: bar\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 14 - combined::compare_u\n+    ---\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      2 >= 1\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 15 - combined::compare_u_with_format\n+    ---\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      foo: bar\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+1..15\ndiff --git a/t/unit-tests/clar/test/expected/without_arguments b/t/unit-tests/clar/test/expected/without_arguments\nindex 1111d418a0..9891f45a70 100644\n--- a/t/unit-tests/clar/test/expected/without_arguments\n+++ b/t/unit-tests/clar/test/expected/without_arguments\n@@ -1,6 +1,6 @@\n Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFF\n+FFFFFFFFFFFFFFF\n \n   1) Failure:\n combined::1 [file:42]\n@@ -22,27 +22,57 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\ndiff --git a/t/unit-tests/clar/test/selftest.c b/t/unit-tests/clar/test/selftest.c\nindex eed83e4512..6eadc64c48 100644\n--- a/t/unit-tests/clar/test/selftest.c\n+++ b/t/unit-tests/clar/test/selftest.c\n@@ -298,7 +298,7 @@ void test_selftest__help(void)\n \n void test_selftest__without_arguments(void)\n {\n-\tcl_invoke(assert_output(\"combined\", \"without_arguments\", 9, NULL));\n+\tcl_invoke(assert_output(\"combined\", \"without_arguments\", 15, NULL));\n }\n \n void test_selftest__specific_test(void)\n@@ -313,12 +313,12 @@ void test_selftest__stop_on_failure(void)\n \n void test_selftest__quiet(void)\n {\n-\tcl_invoke(assert_output(\"combined\", \"quiet\", 9, \"-q\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"quiet\", 15, \"-q\", NULL));\n }\n \n void test_selftest__tap(void)\n {\n-\tcl_invoke(assert_output(\"combined\", \"tap\", 9, \"-t\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"tap\", 15, \"-t\", NULL));\n }\n \n void test_selftest__suite_names(void)\n@@ -329,7 +329,7 @@ void test_selftest__suite_names(void)\n void test_selftest__summary_without_filename(void)\n {\n \tstruct stat st;\n-\tcl_invoke(assert_output(\"combined\", \"summary_without_filename\", 9, \"-r\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"summary_without_filename\", 15, \"-r\", NULL));\n \t/* The summary contains timestamps, so we cannot verify its contents. */\n \tcl_must_pass(stat(\"summary.xml\", &st));\n }\n@@ -337,7 +337,7 @@ void test_selftest__summary_without_filename(void)\n void test_selftest__summary_with_filename(void)\n {\n \tstruct stat st;\n-\tcl_invoke(assert_output(\"combined\", \"summary_with_filename\", 9, \"-rdifferent.xml\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"summary_with_filename\", 15, \"-rdifferent.xml\", NULL));\n \t/* The summary contains timestamps, so we cannot verify its contents. */\n \tcl_must_pass(stat(\"different.xml\", &st));\n }\ndiff --git a/t/unit-tests/clar/test/suites/combined.c b/t/unit-tests/clar/test/suites/combined.c\nindex e8b41c98c3..9e9dbc2fb1 100644\n--- a/t/unit-tests/clar/test/suites/combined.c\n+++ b/t/unit-tests/clar/test/suites/combined.c\n@@ -55,7 +55,12 @@ void test_combined__strings_with_length(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);\n+}\n+\n+void test_combined__int_note(void)\n+{\n+\tint value = 100;\n \tcl_assert_equal_i_(101, value, \"extra note on failing test\");\n }\n \n@@ -83,3 +88,61 @@ void test_combined__null_string(void)\n \tcl_assert_equal_s(actual, actual);\n \tcl_assert_equal_s_(\"expected\", actual, \"this one fails\");\n }\n+\n+void test_combined__failf(void)\n+{\n+\tcl_failf(\"some reason: %s\", \"foo\");\n+}\n+\n+void test_combined__compare_i(void)\n+{\n+\tint one = 1, two = 2;\n+\n+\tcl_assert_equal_i(one, 1);\n+\tcl_assert_equal_i(one, 1);\n+\tcl_assert_equal_i_(one, 1, \"format\");\n+\tcl_assert_lt_i(one, 2);\n+\tcl_assert_lt_i_(one, 2, \"format\");\n+\tcl_assert_le_i(one, 2);\n+\tcl_assert_le_i(two, 2);\n+\tcl_assert_le_i_(two, 2, \"format\");\n+\tcl_assert_gt_i(two, 1);\n+\tcl_assert_gt_i_(two, 1, \"format\");\n+\tcl_assert_ge_i(two, 2);\n+\tcl_assert_ge_i(3, two);\n+\tcl_assert_ge_i_(3, two, \"format\");\n+\n+\tcl_assert_lt_i(two, 1); /* this one fails */\n+}\n+\n+void test_combined__compare_i_with_format(void)\n+{\n+\tint two = 2;\n+\tcl_assert_lt_i_(two, 1, \"foo: %s\", \"bar\");\n+}\n+\n+void test_combined__compare_u(void)\n+{\n+\tunsigned one = 1, two = 2;\n+\n+\tcl_assert_equal_u(one, 1);\n+\tcl_assert_equal_u_(one, 1, \"format\");\n+\tcl_assert_lt_u(one, 2);\n+\tcl_assert_lt_u_(one, 2, \"format\");\n+\tcl_assert_le_u(one, 2);\n+\tcl_assert_le_u(two, 2);\n+\tcl_assert_le_u_(two, 2, \"format\");\n+\tcl_assert_gt_u(two, 1);\n+\tcl_assert_gt_u_(two, 1, \"format\");\n+\tcl_assert_ge_u(two, 2);\n+\tcl_assert_ge_u(3, two);\n+\tcl_assert_ge_u_(3, two, \"format\");\n+\n+\tcl_assert_lt_u(two, 1); /* this one fails */\n+}\n+\n+void test_combined__compare_u_with_format(void)\n+{\n+\tunsigned two = 2;\n+\tcl_assert_lt_u_(two, 1, \"foo: %s\", \"bar\");\n+}\ndiff --git a/t/unit-tests/unit-test.h b/t/unit-tests/unit-test.h\nindex 39a0b72a05..5398b44917 100644\n--- a/t/unit-tests/unit-test.h\n+++ b/t/unit-tests/unit-test.h\n@@ -7,9 +7,3 @@\n #else\n # include GIT_CLAR_DECLS_H\n #endif\n-\n-#define cl_failf(fmt, ...) do { \\\n-\tchar desc[4096]; \\\n-\tsnprintf(desc, sizeof(desc), fmt, __VA_ARGS__); \\\n-\tclar__fail(__FILE__, __func__, __LINE__, \"Test failed.\", desc, 1); \\\n-} while (0)\n\n-- \n2.52.0.239.gd5f0c6e74e.dirty\n\n"},{"id":"531700","messageId":"20251205-b4-pks-clar-update-v1-2-fd70aac2ab90@pks.im","threadId":"64582","inReplyTo":"20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im","subject":"[PATCH 2/2] t/unit-tests: demonstrate use of integer comparison assertions","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-05T12:57:53Z","receivedAt":"2025-12-05T12:58:10Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The clar project has introduced a couple of new assertions that perform\nrelative integer comparisons, like \"greater than\" or \"less or equal\".\nAdapt the reftable-record unit tests to demonstrate their usage.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/unit-tests/u-reftable-record.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/unit-tests/u-reftable-record.c b/t/unit-tests/u-reftable-record.c\nindex 6c8c0d5374..1bf2e170dc 100644\n--- a/t/unit-tests/u-reftable-record.c\n+++ b/t/unit-tests/u-reftable-record.c\n@@ -51,10 +51,10 @@ void test_reftable_record__varint_roundtrip(void)\n \t\tint n = put_var_int(&out, in);\n \t\tuint64_t got = 0;\n \n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \t\tout.len = n;\n \t\tn = get_var_int(&got, &out);\n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \n \t\tcl_assert_equal_i(got, in);\n \t}\n@@ -110,7 +110,7 @@ void test_reftable_record__ref_record_comparison(void)\n \tcl_assert(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\tREFTABLE_HASH_SIZE_SHA1) == 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \n \tin[1].u.ref.value_type = in[0].u.ref.value_type;\n \tcl_assert(reftable_record_equal(&in[0], &in[1],\n@@ -184,7 +184,7 @@ void test_reftable_record__ref_record_roundtrip(void)\n \n \t\treftable_record_key(&in, &key);\n \t\tn = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);\n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \n \t\t/* decode into a non-zero reftable_record to test for leaks. */\n \t\tm = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch);\n@@ -228,11 +228,11 @@ void test_reftable_record__log_record_comparison(void)\n \tcl_assert_equal_i(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\t\tREFTABLE_HASH_SIZE_SHA1), 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \t/* comparison should be reversed for equal keys, because\n \t * comparison is now performed on the basis of update indices */\n \tcl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);\n-\tcl_assert(cmp < 0);\n+\tcl_assert_lt_i(cmp, 0);\n \n \tin[1].u.log.update_index = in[0].u.log.update_index;\n \tcl_assert(reftable_record_equal(&in[0], &in[1],\n@@ -344,7 +344,7 @@ void test_reftable_record__log_record_roundtrip(void)\n \t\treftable_record_key(&rec, &key);\n \n \t\tn = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1);\n-\t\tcl_assert(n >= 0);\n+\t\tcl_assert_ge_i(n, 0);\n \t\tvaltype = reftable_record_val_type(&rec);\n \t\tm = reftable_record_decode(&out, key, valtype, dest,\n \t\t\t\t\t   REFTABLE_HASH_SIZE_SHA1, &scratch);\n@@ -382,7 +382,7 @@ void test_reftable_record__key_roundtrip(void)\n \textra = 6;\n \tn = reftable_encode_key(&restart, dest, last_key, key, extra);\n \tcl_assert(!restart);\n-\tcl_assert(n > 0);\n+\tcl_assert_gt_i(n, 0);\n \n \tcl_assert_equal_i(reftable_buf_addstr(&roundtrip,\n \t\t\t\t\t      \"refs/heads/master\"), 0);\n@@ -432,7 +432,7 @@ void test_reftable_record__obj_record_comparison(void)\n \tcl_assert_equal_i(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\t\tREFTABLE_HASH_SIZE_SHA1), 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \n \tin[1].u.obj.offset_len = in[0].u.obj.offset_len;\n \tcl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);\n@@ -485,7 +485,7 @@ void test_reftable_record__obj_record_roundtrip(void)\n \t\tt_copy(&in);\n \t\treftable_record_key(&in, &key);\n \t\tn = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);\n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \t\textra = reftable_record_val_type(&in);\n \t\tm = reftable_record_decode(&out, key, extra, dest,\n \t\t\t\t\t   REFTABLE_HASH_SIZE_SHA1, &scratch);\n@@ -535,7 +535,7 @@ void test_reftable_record__index_record_comparison(void)\n \tcl_assert_equal_i(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\t\tREFTABLE_HASH_SIZE_SHA1), 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \n \tin[1].u.idx.offset = in[0].u.idx.offset;\n \tcl_assert(reftable_record_equal(&in[0], &in[1],\n\n-- \n2.52.0.239.gd5f0c6e74e.dirty\n\n"},{"id":"531723","messageId":"20251205184020.GB33447@coredump.intra.peff.net","threadId":"64582","inReplyTo":"20251205-b4-pks-clar-update-v1-1-fd70aac2ab90@pks.im","subject":"Re: [PATCH 1/2] t/unit-tests: update clar to 39f11fe","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2025-12-05T18:40:20Z","receivedAt":"2025-12-05T18:40:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Dec 05, 2025 at 01:57:52PM +0100, Patrick Steinhardt wrote:\n\n> -#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))\n> -#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))\n> -#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))\n> +#define cl_assert_compare_i_(i1, i2, cmp, error, ...) clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n> +\t\t\t\t\t\t\t\t\t     (i1), (i2), \"Expected comparison to hold: \" error, __VA_ARGS__)\n\nOK, so \"i\" use to always mean \"int\", and now...\n\n> +#define cl_assert_compare_i(i1, i2, cmp, error, fmt) do { \\\n> +\tintmax_t v1 = (i1), v2 = (i2); \\\n> +\tclar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n> +\t\t\t       v1, v2, \"Expected comparison to hold: \" error, fmt, v1, v2); \\\n> +} while (0)\n\nwe use intmax_t. That seems OK, and lets us handle any size integer; the\ncaller just needs to distinguish signed from unsigned (having \"i\"\nmeaning \"signed\" is a little funny, but I don't have a better\nsuggestion).\n\nThis one is a little odd, though:\n\n> +#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2,  fmt \" != \" fmt, (int)(i1), (int)(i2))\n\nHere we still cast to int. I guess we have to in order to keep custom\n\"%o\" and friends working. They probably ought to be using PRIoMAX, but\nthat would require changes to the tests to do so (and I wonder if we\nmight hit any portability issues).\n\n-Peff\n"},{"id":"531741","messageId":"xmqq7bv05gdg.fsf@gitster.g","threadId":"64582","inReplyTo":"20251205-b4-pks-clar-update-v1-1-fd70aac2ab90@pks.im","subject":"Re: [PATCH 1/2] t/unit-tests: update clar to 39f11fe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-06T05:27:55Z","receivedAt":"2025-12-06T05:27:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> diff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet\n> index 280c99d8ad..a93273b5a2 100644\n> --- a/t/unit-tests/clar/test/expected/quiet\n> +++ b/t/unit-tests/clar/test/expected/quiet\n> @@ -18,27 +18,57 @@ combined::strings_with_length [file:42]\n> ...\n> +  15) Failure:\n> +combined::compare_u_with_format [file:42]\n> +  Expected comparison to hold: two < 1\n> +  foo: bar\n> +\n> diff ...\n\nIf these files in t/unit-tests/clar/test/expected/ directory are\nexpected to end in a blank line, can we teach our .gitattributes\nfile that these are OK?\n\nThanks.\n"},{"id":"531743","messageId":"aTQV1B_7s11Cad9w@pks.im","threadId":"64582","inReplyTo":"20251205184020.GB33447@coredump.intra.peff.net","subject":"Re: [PATCH 1/2] t/unit-tests: update clar to 39f11fe","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-06T11:39:00Z","receivedAt":"2025-12-06T11:39:08Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Dec 05, 2025 at 01:40:20PM -0500, Jeff King wrote:\n> On Fri, Dec 05, 2025 at 01:57:52PM +0100, Patrick Steinhardt wrote:\n> > +#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2,  fmt \" != \" fmt, (int)(i1), (int)(i2))\n> \n> Here we still cast to int. I guess we have to in order to keep custom\n> \"%o\" and friends working. They probably ought to be using PRIoMAX, but\n> that would require changes to the tests to do so (and I wonder if we\n> might hit any portability issues).\n\nIt certainly is the oddball in this context, yes. I would honestly just\nwant to drop it entirely, but I can't because it would potentially break\nother users. libgit2 for example uses it :/\n\nPatrick\n"},{"id":"531745","messageId":"aTQXhoIvqn7f3dzb@pks.im","threadId":"64582","inReplyTo":"xmqq7bv05gdg.fsf@gitster.g","subject":"Re: [PATCH 1/2] t/unit-tests: update clar to 39f11fe","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-06T11:46:14Z","receivedAt":"2025-12-06T11:46:24Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Sat, Dec 06, 2025 at 02:27:55PM +0900, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > diff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet\n> > index 280c99d8ad..a93273b5a2 100644\n> > --- a/t/unit-tests/clar/test/expected/quiet\n> > +++ b/t/unit-tests/clar/test/expected/quiet\n> > @@ -18,27 +18,57 @@ combined::strings_with_length [file:42]\n> > ...\n> > +  15) Failure:\n> > +combined::compare_u_with_format [file:42]\n> > +  Expected comparison to hold: two < 1\n> > +  foo: bar\n> > +\n> > diff ...\n> \n> If these files in t/unit-tests/clar/test/expected/ directory are\n> expected to end in a blank line, can we teach our .gitattributes\n> file that these are OK?\n\nYup, will do. Thanks!\n\nPatrick\n"},{"id":"531746","messageId":"20251206-b4-pks-clar-update-v2-0-9a14b10c1a36@pks.im","threadId":"64582","inReplyTo":"20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im","subject":"[PATCH v2 0/3] Update clar for improved integer handling","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-06T11:47:31Z","receivedAt":"2025-12-06T11:47:56Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Hi,\n\nthis patch series updates clar. Most importantly, the update contains\nproperly typed handling of integers as well as a set of new asserts that\nperform relative comparisons, like \"less than\" or \"greater or equal\".\n\nThanks!\n\nPatrick\n\n---\nChanges in v2:\n- EDITME: describe what is new in this series revision.\n- EDITME: use bulletpoints and terse descriptions.\n- Link to v1: https://lore.kernel.org/r/20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im\n\n---\nPatrick Steinhardt (3):\n      t/unit-tests: update clar to 39f11fe\n      t/unit-tests: demonstrate use of integer comparison assertions\n      gitattributes: disable blank-at-eof errors for clar test expectations\n\n .gitattributes                                     |   1 +\n t/unit-tests/clar/.github/workflows/ci.yml         |   2 +-\n t/unit-tests/clar/clar.c                           | 146 ++++++++++++++++++++-\n t/unit-tests/clar/clar.h                           |  82 +++++++++++-\n t/unit-tests/clar/clar/print.h                     |   2 +-\n t/unit-tests/clar/test/expected/quiet              |  40 +++++-\n .../clar/test/expected/summary_with_filename       |  42 +++++-\n .../clar/test/expected/summary_without_filename    |  42 +++++-\n t/unit-tests/clar/test/expected/tap                |  88 +++++++++++--\n t/unit-tests/clar/test/expected/without_arguments  |  42 +++++-\n t/unit-tests/clar/test/selftest.c                  |  10 +-\n t/unit-tests/clar/test/suites/combined.c           |  65 ++++++++-\n t/unit-tests/u-reftable-record.c                   |  22 ++--\n t/unit-tests/unit-test.h                           |   6 -\n 14 files changed, 520 insertions(+), 70 deletions(-)\n\nRange-diff versus v1:\n\n1:  58f8836bde = 1:  d709d75f25 t/unit-tests: update clar to 39f11fe\n2:  1b4e93d5b4 = 2:  6b56ad714e t/unit-tests: demonstrate use of integer comparison assertions\n-:  ---------- > 3:  a1b6aec468 gitattributes: disable blank-at-eof errors for clar test expectations\n\n---\nbase-commit: f0ef5b6d9bcc258e4cbef93839d1b7465d5212b9\nchange-id: 20251205-b4-pks-clar-update-4fb022908ced\n\n"},{"id":"531747","messageId":"20251206-b4-pks-clar-update-v2-1-9a14b10c1a36@pks.im","threadId":"64582","inReplyTo":"20251206-b4-pks-clar-update-v2-0-9a14b10c1a36@pks.im","subject":"[PATCH v2 1/3] t/unit-tests: update clar to 39f11fe","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-06T11:47:32Z","receivedAt":"2025-12-06T11:48:01Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"Update clar to commit 39f11fe (Merge pull request #131 from\npks-gitlab/pks-integer-double-evaluation, 2025-12-05). This commit\nincludes the following changes relevant to Git:\n\n  - There are now typesafe integer comparison functions. Furthermore,\n    the range of comparison functions has been included to also have\n    relative comparisons, like \"greater than\".\n\n  - There is a new `cl_failf()` macro that allows the caller to specify\n    an error message with formatting directives.\n\n  - The TAP format has been fixed to correctly terminate YAML blocks\n    with \"...\\n\" instead of \"---\\n\".\n\nNote that we already had a `cl_failf()` function declared in our own\nsources. This function is equivalent to the upstreamed function, so we\ncan simply drop it now.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/unit-tests/clar/.github/workflows/ci.yml         |   2 +-\n t/unit-tests/clar/clar.c                           | 146 ++++++++++++++++++++-\n t/unit-tests/clar/clar.h                           |  82 +++++++++++-\n t/unit-tests/clar/clar/print.h                     |   2 +-\n t/unit-tests/clar/test/expected/quiet              |  40 +++++-\n .../clar/test/expected/summary_with_filename       |  42 +++++-\n .../clar/test/expected/summary_without_filename    |  42 +++++-\n t/unit-tests/clar/test/expected/tap                |  88 +++++++++++--\n t/unit-tests/clar/test/expected/without_arguments  |  42 +++++-\n t/unit-tests/clar/test/selftest.c                  |  10 +-\n t/unit-tests/clar/test/suites/combined.c           |  65 ++++++++-\n t/unit-tests/unit-test.h                           |   6 -\n 12 files changed, 508 insertions(+), 59 deletions(-)\n\ndiff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml\nindex 4d4724222c..14cb4ed1d4 100644\n--- a/t/unit-tests/clar/.github/workflows/ci.yml\n+++ b/t/unit-tests/clar/.github/workflows/ci.yml\n@@ -53,7 +53,7 @@ jobs:\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@v4\n+      uses: actions/checkout@v6\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 d6176e50b2..e959a5ae02 100644\n--- a/t/unit-tests/clar/clar.c\n+++ b/t/unit-tests/clar/clar.c\n@@ -24,6 +24,14 @@\n #include <sys/types.h>\n #include <sys/stat.h>\n \n+#ifndef va_copy\n+#\tifdef __va_copy\n+#\t\tdefine va_copy(dst, src) __va_copy(dst, src)\n+#\telse\n+#\t\tdefine va_copy(dst, src) ((dst) = (src))\n+#\tendif\n+#endif\n+\n #if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_WCHAR__)\n \t/*\n \t * uClibc can optionally be built without wchar support, in which case\n@@ -76,8 +84,10 @@\n #\t\t\tdefine S_ISDIR(x) ((x & _S_IFDIR) != 0)\n #\t\tendif\n #\t\tdefine p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)\n+#\t\tdefine p_vsnprintf _vsnprintf\n #\telse\n #\t\tdefine p_snprintf snprintf\n+#\t\tdefine p_vsnprintf vsnprintf\n #\tendif\n \n #\tdefine localtime_r(timer, buf) (localtime_s(buf, timer) == 0 ? buf : NULL)\n@@ -86,6 +96,7 @@\n #\tinclude <unistd.h>\n #\tdefine _MAIN_CC\n #\tdefine p_snprintf snprintf\n+#\tdefine p_vsnprintf vsnprintf\n \ttypedef struct stat STAT_T;\n #endif\n \n@@ -699,13 +710,14 @@ void clar__skip(void)\n \tabort_test();\n }\n \n-void clar__fail(\n+static void clar__failv(\n \tconst char *file,\n \tconst char *function,\n \tsize_t line,\n+\tint should_abort,\n \tconst char *error_msg,\n \tconst char *description,\n-\tint should_abort)\n+\tva_list args)\n {\n \tstruct clar_error *error;\n \n@@ -725,9 +737,19 @@ void clar__fail(\n \terror->line_number = _clar.invoke_line ? _clar.invoke_line : line;\n \terror->error_msg = error_msg;\n \n-\tif (description != NULL &&\n-\t    (error->description = strdup(description)) == NULL)\n-\t\tclar_abort(\"Failed to allocate description.\\n\");\n+\tif (description != NULL) {\n+\t\tva_list args_copy;\n+\t\tint len;\n+\n+\t\tva_copy(args_copy, args);\n+\t\tif ((len = p_vsnprintf(NULL, 0, description, args_copy)) < 0)\n+\t\t\tclar_abort(\"Failed to compute description.\");\n+\t\tva_end(args_copy);\n+\n+\t\tif ((error->description = calloc(1, len + 1)) == NULL)\n+\t\t\tclar_abort(\"Failed to allocate buffer.\");\n+\t\tp_vsnprintf(error->description, len + 1, description, args);\n+\t}\n \n \t_clar.total_errors++;\n \t_clar.last_report->status = CL_TEST_FAILURE;\n@@ -736,6 +758,34 @@ void clar__fail(\n \t\tabort_test();\n }\n \n+void clar__failf(\n+\tconst char *file,\n+\tconst char *function,\n+\tsize_t line,\n+\tint should_abort,\n+\tconst char *error_msg,\n+\tconst char *description,\n+\t...)\n+{\n+\tva_list args;\n+\tva_start(args, description);\n+\tclar__failv(file, function, line, should_abort, error_msg,\n+\t\t    description, args);\n+\tva_end(args);\n+}\n+\n+void clar__fail(\n+\tconst char *file,\n+\tconst char *function,\n+\tsize_t line,\n+\tconst char *error_msg,\n+\tconst char *description,\n+\tint should_abort)\n+{\n+\tclar__failf(file, function, line, should_abort, error_msg,\n+\t\t    description ? \"%s\" : NULL, description);\n+}\n+\n void clar__assert(\n \tint condition,\n \tconst char *file,\n@@ -889,6 +939,92 @@ void clar__assert_equal(\n \t\tclar__fail(file, function, line, err, buf, should_abort);\n }\n \n+void clar__assert_compare_i(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tintmax_t value1,\n+\tintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...)\n+{\n+\tint fulfilled;\n+\tswitch (cmp) {\n+\tcase CLAR_COMPARISON_EQ:\n+\t\tfulfilled = value1 == value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LT:\n+\t\tfulfilled = value1 < value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LE:\n+\t\tfulfilled = value1 <= value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GT:\n+\t\tfulfilled = value1 > value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GE:\n+\t\tfulfilled = value1 >= value2;\n+\t\tbreak;\n+\tdefault:\n+\t\tcl_assert(0);\n+\t\treturn;\n+\t}\n+\n+\tif (!fulfilled) {\n+\t\tva_list args;\n+\t\tva_start(args, description);\n+\t\tclar__failv(file, func, line, should_abort, error,\n+\t\t\t    description, args);\n+\t\tva_end(args);\n+\t}\n+}\n+\n+void clar__assert_compare_u(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tuintmax_t value1,\n+\tuintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...)\n+{\n+\tint fulfilled;\n+\tswitch (cmp) {\n+\tcase CLAR_COMPARISON_EQ:\n+\t\tfulfilled = value1 == value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LT:\n+\t\tfulfilled = value1 < value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_LE:\n+\t\tfulfilled = value1 <= value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GT:\n+\t\tfulfilled = value1 > value2;\n+\t\tbreak;\n+\tcase CLAR_COMPARISON_GE:\n+\t\tfulfilled = value1 >= value2;\n+\t\tbreak;\n+\tdefault:\n+\t\tcl_assert(0);\n+\t\treturn;\n+\t}\n+\n+\tif (!fulfilled) {\n+\t\tva_list args;\n+\t\tva_start(args, description);\n+\t\tclar__failv(file, func, line, should_abort, error,\n+\t\t\t    description, args);\n+\t\tva_end(args);\n+\t}\n+}\n+\n void cl_set_cleanup(void (*cleanup)(void *), void *opaque)\n {\n \t_clar.local_cleanup = cleanup;\ndiff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h\nindex ca72292ae9..f7e4363022 100644\n--- a/t/unit-tests/clar/clar.h\n+++ b/t/unit-tests/clar/clar.h\n@@ -7,6 +7,7 @@\n #ifndef __CLAR_TEST_H__\n #define __CLAR_TEST_H__\n \n+#include <inttypes.h>\n #include <stdlib.h>\n #include <limits.h>\n \n@@ -149,6 +150,7 @@ const char *cl_fixture_basename(const char *fixture_name);\n  * Forced failure/warning\n  */\n #define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Test failed.\", desc, 1)\n+#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n #define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Warning during test execution:\", desc, 0)\n \n #define cl_skip() clar__skip()\n@@ -168,9 +170,42 @@ const char *cl_fixture_basename(const char *fixture_name);\n #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))\n #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))\n \n-#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))\n-#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))\n-#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))\n+#define cl_assert_compare_i_(i1, i2, cmp, error, ...) clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t\t\t\t\t\t\t     (i1), (i2), \"Expected comparison to hold: \" error, __VA_ARGS__)\n+#define cl_assert_compare_i(i1, i2, cmp, error, fmt) do { \\\n+\tintmax_t v1 = (i1), v2 = (i2); \\\n+\tclar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t       v1, v2, \"Expected comparison to hold: \" error, fmt, v1, v2); \\\n+} while (0)\n+#define cl_assert_equal_i_(i1, i2, ...)    cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2, __VA_ARGS__)\n+#define cl_assert_equal_i(i1, i2)          cl_assert_compare_i (i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2, \"%\"PRIdMAX \" != %\"PRIdMAX)\n+#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2,  fmt \" != \" fmt, (int)(i1), (int)(i2))\n+#define cl_assert_lt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LT, #i1 \" < \" #i2, __VA_ARGS__)\n+#define cl_assert_lt_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LT, #i1 \" < \" #i2, \"%\"PRIdMAX \" >= %\"PRIdMAX)\n+#define cl_assert_le_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LE, #i1 \" <= \" #i2, __VA_ARGS__)\n+#define cl_assert_le_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LE, #i1 \" <= \" #i2, \"%\"PRIdMAX \" > %\"PRIdMAX)\n+#define cl_assert_gt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GT, #i1 \" > \" #i2, __VA_ARGS__)\n+#define cl_assert_gt_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GT, #i1 \" > \" #i2, \"%\"PRIdMAX \" <= %\"PRIdMAX)\n+#define cl_assert_ge_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GE, #i1 \" >= \" #i2, __VA_ARGS__)\n+#define cl_assert_ge_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GE, #i1 \" >= \" #i2, \"%\"PRIdMAX \" < %\"PRIdMAX)\n+\n+#define cl_assert_compare_u_(u1, u2, cmp, error, ...) clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t\t\t\t\t\t\t     (u1), (u2), \"Expected comparison to hold: \" error, __VA_ARGS__)\n+#define cl_assert_compare_u(u1, u2, cmp, error, fmt) do { \\\n+\tuintmax_t v1 = (u1), v2 = (u2); \\\n+\tclar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n+\t\t\t       v1, v2, \"Expected comparison to hold: \" error, fmt, v1, v2); \\\n+} while (0)\n+#define cl_assert_equal_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_EQ, #u1 \" == \" #u2, __VA_ARGS__)\n+#define cl_assert_equal_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_EQ, #u1 \" == \" #u2, \"%\"PRIuMAX \" != %\"PRIuMAX)\n+#define cl_assert_lt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LT, #u1 \" < \" #u2, __VA_ARGS__)\n+#define cl_assert_lt_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LT, #u1 \" < \" #u2, \"%\"PRIuMAX \" >= %\"PRIuMAX)\n+#define cl_assert_le_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LE, #u1 \" <= \" #u2, __VA_ARGS__)\n+#define cl_assert_le_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LE, #u1 \" <= \" #u2, \"%\"PRIuMAX \" > %\"PRIuMAX)\n+#define cl_assert_gt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GT, #u1 \" > \" #u2, __VA_ARGS__)\n+#define cl_assert_gt_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GT, #u1 \" > \" #u2, \"%\"PRIuMAX \" <= %\"PRIuMAX)\n+#define cl_assert_ge_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GE, #u1 \" >= \" #u2, __VA_ARGS__)\n+#define cl_assert_ge_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GE, #u1 \" >= \" #u2, \"%\"PRIuMAX \" < %\"PRIuMAX)\n \n #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))\n \n@@ -186,6 +221,15 @@ void clar__fail(\n \tconst char *description,\n \tint should_abort);\n \n+void clar__failf(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tconst char *error,\n+\tconst char *description,\n+\t...);\n+\n void clar__assert(\n \tint condition,\n \tconst char *file,\n@@ -204,6 +248,38 @@ void clar__assert_equal(\n \tconst char *fmt,\n \t...);\n \n+enum clar_comparison {\n+\tCLAR_COMPARISON_EQ,\n+\tCLAR_COMPARISON_LT,\n+\tCLAR_COMPARISON_LE,\n+\tCLAR_COMPARISON_GT,\n+\tCLAR_COMPARISON_GE,\n+};\n+\n+void clar__assert_compare_i(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tintmax_t value1,\n+\tintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...);\n+\n+void clar__assert_compare_u(\n+\tconst char *file,\n+\tconst char *func,\n+\tsize_t line,\n+\tint should_abort,\n+\tenum clar_comparison cmp,\n+\tuintmax_t value1,\n+\tuintmax_t value2,\n+\tconst char *error,\n+\tconst char *description,\n+\t...);\n+\n void clar__set_invokepoint(\n \tconst char *file,\n \tconst char *func,\ndiff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h\nindex 89b66591d7..6a2321b399 100644\n--- a/t/unit-tests/clar/clar/print.h\n+++ b/t/unit-tests/clar/clar/print.h\n@@ -164,7 +164,7 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name,\n \t\t\tprintf(\"      file: '\"); print_escaped(error->file); printf(\"'\\n\");\n \t\t\tprintf(\"      line: %\" PRIuMAX \"\\n\", error->line_number);\n \t\t\tprintf(\"      function: '%s'\\n\", error->function);\n-\t\t\tprintf(\"    ---\\n\");\n+\t\t\tprintf(\"    ...\\n\");\n \t\t}\n \n \t\tbreak;\ndiff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet\nindex 280c99d8ad..a93273b5a2 100644\n--- a/t/unit-tests/clar/test/expected/quiet\n+++ b/t/unit-tests/clar/test/expected/quiet\n@@ -18,27 +18,57 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\ndiff --git a/t/unit-tests/clar/test/expected/summary_with_filename b/t/unit-tests/clar/test/expected/summary_with_filename\nindex 460160791d..a9471cc7d5 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,6 +1,6 @@\n Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFF\n+FFFFFFFFFFFFFFF\n \n   1) Failure:\n combined::1 [file:42]\n@@ -22,28 +22,58 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n written summary file to different.xml\ndiff --git a/t/unit-tests/clar/test/expected/summary_without_filename b/t/unit-tests/clar/test/expected/summary_without_filename\nindex 7874c1d98b..83ba770d00 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,6 +1,6 @@\n Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFF\n+FFFFFFFFFFFFFFF\n \n   1) Failure:\n combined::1 [file:42]\n@@ -22,28 +22,58 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n written summary file to summary.xml\ndiff --git a/t/unit-tests/clar/test/expected/tap b/t/unit-tests/clar/test/expected/tap\nindex bddbd5dfe9..e67118d3ae 100644\n--- a/t/unit-tests/clar/test/expected/tap\n+++ b/t/unit-tests/clar/test/expected/tap\n@@ -8,7 +8,7 @@ not ok 1 - combined::1\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 2 - combined::2\n     ---\n     reason: |\n@@ -17,7 +17,7 @@ not ok 2 - combined::2\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 3 - combined::strings\n     ---\n     reason: |\n@@ -27,7 +27,7 @@ not ok 3 - combined::strings\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 4 - combined::strings_with_length\n     ---\n     reason: |\n@@ -37,28 +37,38 @@ not ok 4 - combined::strings_with_length\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n+    ...\n not ok 5 - combined::int\n     ---\n     reason: |\n-      101 != value (\"extra note on failing test\")\n+      Expected comparison to hold: 101 == value\n       101 != 100\n     at:\n       file: 'file'\n       line: 42\n       function: 'func'\n+    ...\n+not ok 6 - combined::int_note\n     ---\n-not ok 6 - combined::int_fmt\n+    reason: |\n+      Expected comparison to hold: 101 == value\n+      extra note on failing test\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 7 - combined::int_fmt\n     ---\n     reason: |\n-      022 != value\n+      Expected comparison to hold: 022 == value\n       0022 != 0144\n     at:\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n-not ok 7 - combined::bool\n+    ...\n+not ok 8 - combined::bool\n     ---\n     reason: |\n       0 != value\n@@ -67,8 +77,8 @@ not ok 7 - combined::bool\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n-not ok 8 - combined::multiline_description\n+    ...\n+not ok 9 - combined::multiline_description\n     ---\n     reason: |\n       Function call failed: -1\n@@ -78,8 +88,8 @@ not ok 8 - combined::multiline_description\n       file: 'file'\n       line: 42\n       function: 'func'\n-    ---\n-not ok 9 - combined::null_string\n+    ...\n+not ok 10 - combined::null_string\n     ---\n     reason: |\n       String mismatch: \"expected\" != actual (\"this one fails\")\n@@ -88,5 +98,55 @@ not ok 9 - combined::null_string\n       file: 'file'\n       line: 42\n       function: 'func'\n+    ...\n+not ok 11 - combined::failf\n+    ---\n+    reason: |\n+      Test failed.\n+      some reason: foo\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 12 - combined::compare_i\n     ---\n-1..9\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      2 >= 1\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 13 - combined::compare_i_with_format\n+    ---\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      foo: bar\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 14 - combined::compare_u\n+    ---\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      2 >= 1\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+not ok 15 - combined::compare_u_with_format\n+    ---\n+    reason: |\n+      Expected comparison to hold: two < 1\n+      foo: bar\n+    at:\n+      file: 'file'\n+      line: 42\n+      function: 'func'\n+    ...\n+1..15\ndiff --git a/t/unit-tests/clar/test/expected/without_arguments b/t/unit-tests/clar/test/expected/without_arguments\nindex 1111d418a0..9891f45a70 100644\n--- a/t/unit-tests/clar/test/expected/without_arguments\n+++ b/t/unit-tests/clar/test/expected/without_arguments\n@@ -1,6 +1,6 @@\n Loaded 1 suites:\n Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n-FFFFFFFFF\n+FFFFFFFFFFFFFFF\n \n   1) Failure:\n combined::1 [file:42]\n@@ -22,27 +22,57 @@ combined::strings_with_length [file:42]\n \n   5) Failure:\n combined::int [file:42]\n-  101 != value (\"extra note on failing test\")\n+  Expected comparison to hold: 101 == value\n   101 != 100\n \n   6) Failure:\n+combined::int_note [file:42]\n+  Expected comparison to hold: 101 == value\n+  extra note on failing test\n+\n+  7) Failure:\n combined::int_fmt [file:42]\n-  022 != value\n+  Expected comparison to hold: 022 == value\n   0022 != 0144\n \n-  7) Failure:\n+  8) Failure:\n combined::bool [file:42]\n   0 != value\n   0 != 1\n \n-  8) Failure:\n+  9) Failure:\n combined::multiline_description [file:42]\n   Function call failed: -1\n   description line 1\n   description line 2\n \n-  9) Failure:\n+  10) Failure:\n combined::null_string [file:42]\n   String mismatch: \"expected\" != actual (\"this one fails\")\n   'expected' != NULL\n \n+  11) Failure:\n+combined::failf [file:42]\n+  Test failed.\n+  some reason: foo\n+\n+  12) Failure:\n+combined::compare_i [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  13) Failure:\n+combined::compare_i_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\n+  14) Failure:\n+combined::compare_u [file:42]\n+  Expected comparison to hold: two < 1\n+  2 >= 1\n+\n+  15) Failure:\n+combined::compare_u_with_format [file:42]\n+  Expected comparison to hold: two < 1\n+  foo: bar\n+\ndiff --git a/t/unit-tests/clar/test/selftest.c b/t/unit-tests/clar/test/selftest.c\nindex eed83e4512..6eadc64c48 100644\n--- a/t/unit-tests/clar/test/selftest.c\n+++ b/t/unit-tests/clar/test/selftest.c\n@@ -298,7 +298,7 @@ void test_selftest__help(void)\n \n void test_selftest__without_arguments(void)\n {\n-\tcl_invoke(assert_output(\"combined\", \"without_arguments\", 9, NULL));\n+\tcl_invoke(assert_output(\"combined\", \"without_arguments\", 15, NULL));\n }\n \n void test_selftest__specific_test(void)\n@@ -313,12 +313,12 @@ void test_selftest__stop_on_failure(void)\n \n void test_selftest__quiet(void)\n {\n-\tcl_invoke(assert_output(\"combined\", \"quiet\", 9, \"-q\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"quiet\", 15, \"-q\", NULL));\n }\n \n void test_selftest__tap(void)\n {\n-\tcl_invoke(assert_output(\"combined\", \"tap\", 9, \"-t\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"tap\", 15, \"-t\", NULL));\n }\n \n void test_selftest__suite_names(void)\n@@ -329,7 +329,7 @@ void test_selftest__suite_names(void)\n void test_selftest__summary_without_filename(void)\n {\n \tstruct stat st;\n-\tcl_invoke(assert_output(\"combined\", \"summary_without_filename\", 9, \"-r\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"summary_without_filename\", 15, \"-r\", NULL));\n \t/* The summary contains timestamps, so we cannot verify its contents. */\n \tcl_must_pass(stat(\"summary.xml\", &st));\n }\n@@ -337,7 +337,7 @@ void test_selftest__summary_without_filename(void)\n void test_selftest__summary_with_filename(void)\n {\n \tstruct stat st;\n-\tcl_invoke(assert_output(\"combined\", \"summary_with_filename\", 9, \"-rdifferent.xml\", NULL));\n+\tcl_invoke(assert_output(\"combined\", \"summary_with_filename\", 15, \"-rdifferent.xml\", NULL));\n \t/* The summary contains timestamps, so we cannot verify its contents. */\n \tcl_must_pass(stat(\"different.xml\", &st));\n }\ndiff --git a/t/unit-tests/clar/test/suites/combined.c b/t/unit-tests/clar/test/suites/combined.c\nindex e8b41c98c3..9e9dbc2fb1 100644\n--- a/t/unit-tests/clar/test/suites/combined.c\n+++ b/t/unit-tests/clar/test/suites/combined.c\n@@ -55,7 +55,12 @@ void test_combined__strings_with_length(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);\n+}\n+\n+void test_combined__int_note(void)\n+{\n+\tint value = 100;\n \tcl_assert_equal_i_(101, value, \"extra note on failing test\");\n }\n \n@@ -83,3 +88,61 @@ void test_combined__null_string(void)\n \tcl_assert_equal_s(actual, actual);\n \tcl_assert_equal_s_(\"expected\", actual, \"this one fails\");\n }\n+\n+void test_combined__failf(void)\n+{\n+\tcl_failf(\"some reason: %s\", \"foo\");\n+}\n+\n+void test_combined__compare_i(void)\n+{\n+\tint one = 1, two = 2;\n+\n+\tcl_assert_equal_i(one, 1);\n+\tcl_assert_equal_i(one, 1);\n+\tcl_assert_equal_i_(one, 1, \"format\");\n+\tcl_assert_lt_i(one, 2);\n+\tcl_assert_lt_i_(one, 2, \"format\");\n+\tcl_assert_le_i(one, 2);\n+\tcl_assert_le_i(two, 2);\n+\tcl_assert_le_i_(two, 2, \"format\");\n+\tcl_assert_gt_i(two, 1);\n+\tcl_assert_gt_i_(two, 1, \"format\");\n+\tcl_assert_ge_i(two, 2);\n+\tcl_assert_ge_i(3, two);\n+\tcl_assert_ge_i_(3, two, \"format\");\n+\n+\tcl_assert_lt_i(two, 1); /* this one fails */\n+}\n+\n+void test_combined__compare_i_with_format(void)\n+{\n+\tint two = 2;\n+\tcl_assert_lt_i_(two, 1, \"foo: %s\", \"bar\");\n+}\n+\n+void test_combined__compare_u(void)\n+{\n+\tunsigned one = 1, two = 2;\n+\n+\tcl_assert_equal_u(one, 1);\n+\tcl_assert_equal_u_(one, 1, \"format\");\n+\tcl_assert_lt_u(one, 2);\n+\tcl_assert_lt_u_(one, 2, \"format\");\n+\tcl_assert_le_u(one, 2);\n+\tcl_assert_le_u(two, 2);\n+\tcl_assert_le_u_(two, 2, \"format\");\n+\tcl_assert_gt_u(two, 1);\n+\tcl_assert_gt_u_(two, 1, \"format\");\n+\tcl_assert_ge_u(two, 2);\n+\tcl_assert_ge_u(3, two);\n+\tcl_assert_ge_u_(3, two, \"format\");\n+\n+\tcl_assert_lt_u(two, 1); /* this one fails */\n+}\n+\n+void test_combined__compare_u_with_format(void)\n+{\n+\tunsigned two = 2;\n+\tcl_assert_lt_u_(two, 1, \"foo: %s\", \"bar\");\n+}\ndiff --git a/t/unit-tests/unit-test.h b/t/unit-tests/unit-test.h\nindex 39a0b72a05..5398b44917 100644\n--- a/t/unit-tests/unit-test.h\n+++ b/t/unit-tests/unit-test.h\n@@ -7,9 +7,3 @@\n #else\n # include GIT_CLAR_DECLS_H\n #endif\n-\n-#define cl_failf(fmt, ...) do { \\\n-\tchar desc[4096]; \\\n-\tsnprintf(desc, sizeof(desc), fmt, __VA_ARGS__); \\\n-\tclar__fail(__FILE__, __func__, __LINE__, \"Test failed.\", desc, 1); \\\n-} while (0)\n\n-- \n2.52.0.239.gd5f0c6e74e.dirty\n\n"},{"id":"531748","messageId":"20251206-b4-pks-clar-update-v2-2-9a14b10c1a36@pks.im","threadId":"64582","inReplyTo":"20251206-b4-pks-clar-update-v2-0-9a14b10c1a36@pks.im","subject":"[PATCH v2 2/3] t/unit-tests: demonstrate use of integer comparison assertions","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-06T11:47:33Z","receivedAt":"2025-12-06T11:48:05Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The clar project has introduced a couple of new assertions that perform\nrelative integer comparisons, like \"greater than\" or \"less or equal\".\nAdapt the reftable-record unit tests to demonstrate their usage.\n\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n t/unit-tests/u-reftable-record.c | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/unit-tests/u-reftable-record.c b/t/unit-tests/u-reftable-record.c\nindex 6c8c0d5374..1bf2e170dc 100644\n--- a/t/unit-tests/u-reftable-record.c\n+++ b/t/unit-tests/u-reftable-record.c\n@@ -51,10 +51,10 @@ void test_reftable_record__varint_roundtrip(void)\n \t\tint n = put_var_int(&out, in);\n \t\tuint64_t got = 0;\n \n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \t\tout.len = n;\n \t\tn = get_var_int(&got, &out);\n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \n \t\tcl_assert_equal_i(got, in);\n \t}\n@@ -110,7 +110,7 @@ void test_reftable_record__ref_record_comparison(void)\n \tcl_assert(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\tREFTABLE_HASH_SIZE_SHA1) == 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \n \tin[1].u.ref.value_type = in[0].u.ref.value_type;\n \tcl_assert(reftable_record_equal(&in[0], &in[1],\n@@ -184,7 +184,7 @@ void test_reftable_record__ref_record_roundtrip(void)\n \n \t\treftable_record_key(&in, &key);\n \t\tn = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);\n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \n \t\t/* decode into a non-zero reftable_record to test for leaks. */\n \t\tm = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch);\n@@ -228,11 +228,11 @@ void test_reftable_record__log_record_comparison(void)\n \tcl_assert_equal_i(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\t\tREFTABLE_HASH_SIZE_SHA1), 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \t/* comparison should be reversed for equal keys, because\n \t * comparison is now performed on the basis of update indices */\n \tcl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0);\n-\tcl_assert(cmp < 0);\n+\tcl_assert_lt_i(cmp, 0);\n \n \tin[1].u.log.update_index = in[0].u.log.update_index;\n \tcl_assert(reftable_record_equal(&in[0], &in[1],\n@@ -344,7 +344,7 @@ void test_reftable_record__log_record_roundtrip(void)\n \t\treftable_record_key(&rec, &key);\n \n \t\tn = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1);\n-\t\tcl_assert(n >= 0);\n+\t\tcl_assert_ge_i(n, 0);\n \t\tvaltype = reftable_record_val_type(&rec);\n \t\tm = reftable_record_decode(&out, key, valtype, dest,\n \t\t\t\t\t   REFTABLE_HASH_SIZE_SHA1, &scratch);\n@@ -382,7 +382,7 @@ void test_reftable_record__key_roundtrip(void)\n \textra = 6;\n \tn = reftable_encode_key(&restart, dest, last_key, key, extra);\n \tcl_assert(!restart);\n-\tcl_assert(n > 0);\n+\tcl_assert_gt_i(n, 0);\n \n \tcl_assert_equal_i(reftable_buf_addstr(&roundtrip,\n \t\t\t\t\t      \"refs/heads/master\"), 0);\n@@ -432,7 +432,7 @@ void test_reftable_record__obj_record_comparison(void)\n \tcl_assert_equal_i(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\t\tREFTABLE_HASH_SIZE_SHA1), 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \n \tin[1].u.obj.offset_len = in[0].u.obj.offset_len;\n \tcl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0);\n@@ -485,7 +485,7 @@ void test_reftable_record__obj_record_roundtrip(void)\n \t\tt_copy(&in);\n \t\treftable_record_key(&in, &key);\n \t\tn = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1);\n-\t\tcl_assert(n > 0);\n+\t\tcl_assert_gt_i(n, 0);\n \t\textra = reftable_record_val_type(&in);\n \t\tm = reftable_record_decode(&out, key, extra, dest,\n \t\t\t\t\t   REFTABLE_HASH_SIZE_SHA1, &scratch);\n@@ -535,7 +535,7 @@ void test_reftable_record__index_record_comparison(void)\n \tcl_assert_equal_i(reftable_record_equal(&in[1], &in[2],\n \t\t\t\t\t\tREFTABLE_HASH_SIZE_SHA1), 0);\n \tcl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0);\n-\tcl_assert(cmp > 0);\n+\tcl_assert_gt_i(cmp, 0);\n \n \tin[1].u.idx.offset = in[0].u.idx.offset;\n \tcl_assert(reftable_record_equal(&in[0], &in[1],\n\n-- \n2.52.0.239.gd5f0c6e74e.dirty\n\n"},{"id":"531749","messageId":"20251206-b4-pks-clar-update-v2-3-9a14b10c1a36@pks.im","threadId":"64582","inReplyTo":"20251206-b4-pks-clar-update-v2-0-9a14b10c1a36@pks.im","subject":"[PATCH v2 3/3] gitattributes: disable blank-at-eof errors for clar test expectations","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-06T11:47:34Z","receivedAt":"2025-12-06T11:48:11Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"The clar unit testing framework carries a couple of files that contain\nexpected output for its self-tests. Some of these files expectedly end\nwith a blank line at the end of the file, which Git would consider to be\na whitespace error by default.\n\nTeach our gitattributes to ignore those errors.\n\nSuggested-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n .gitattributes | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/.gitattributes b/.gitattributes\nindex 700743c3f5..38b1c52fe0 100644\n--- a/.gitattributes\n+++ b/.gitattributes\n@@ -17,3 +17,4 @@ CODE_OF_CONDUCT.md -whitespace\n /Documentation/gitk.adoc conflict-marker-size=32\n /Documentation/user-manual.adoc conflict-marker-size=32\n /t/t????-*.sh conflict-marker-size=32\n+/t/unit-tests/clar/test/expected/* whitespace=-blank-at-eof\n\n-- \n2.52.0.239.gd5f0c6e74e.dirty\n\n"},{"id":"533127","messageId":"CAOLa=ZQZnYVuK8mDi6Yb8_+hqw_TMugn6i7BJCj1gbNHOruNWA@mail.gmail.com","threadId":"64582","inReplyTo":"20251206-b4-pks-clar-update-v2-1-9a14b10c1a36@pks.im","subject":"Re: [PATCH v2 1/3] t/unit-tests: update clar to 39f11fe","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2026-01-06T10:59:21Z","receivedAt":"2026-01-06T10:59:23Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Update clar to commit 39f11fe (Merge pull request #131 from\n> pks-gitlab/pks-integer-double-evaluation, 2025-12-05). This commit\n> includes the following changes relevant to Git:\n>\n\nNit: There is a newer commit merged into the clar repository, but I\ndon't think it is so important to include.\n\n>   - There are now typesafe integer comparison functions. Furthermore,\n>     the range of comparison functions has been included to also have\n>     relative comparisons, like \"greater than\".\n>\n>   - There is a new `cl_failf()` macro that allows the caller to specify\n>     an error message with formatting directives.\n>\n>   - The TAP format has been fixed to correctly terminate YAML blocks\n>     with \"...\\n\" instead of \"---\\n\".\n>\n> Note that we already had a `cl_failf()` function declared in our own\n> sources. This function is equivalent to the upstreamed function, so we\n> can simply drop it now.\n>\n\n[snip]\n\n> @@ -149,6 +150,7 @@ const char *cl_fixture_basename(const char *fixture_name);\n>   * Forced failure/warning\n>   */\n>  #define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Test failed.\", desc, 1)\n> +#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n\nNit: While most of the function accept description with variable\narguments, this is the only one which has the '...f()' format explicitly\nseparated out. It would be nicer if we simply make this part of\n'cl_fail()', no?\n\n>  #define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Warning during test execution:\", desc, 0)\n>\n>  #define cl_skip() clar__skip()\n> @@ -168,9 +170,42 @@ const char *cl_fixture_basename(const char *fixture_name);\n>  #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))\n>  #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))\n>\n> -#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))\n> -#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))\n> -#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))\n> +#define cl_assert_compare_i_(i1, i2, cmp, error, ...) clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n> +\t\t\t\t\t\t\t\t\t     (i1), (i2), \"Expected comparison to hold: \" error, __VA_ARGS__)\n> +#define cl_assert_compare_i(i1, i2, cmp, error, fmt) do { \\\n> +\tintmax_t v1 = (i1), v2 = (i2); \\\n> +\tclar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n> +\t\t\t       v1, v2, \"Expected comparison to hold: \" error, fmt, v1, v2); \\\n> +} while (0)\n\nSo we use local variables here to avoid double evaluation of the\narguments passed. We also use 'intmax_t' since this would avoid any size\ntruncation. Looks good.\n\n> +#define cl_assert_equal_i_(i1, i2, ...)    cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2, __VA_ARGS__)\n> +#define cl_assert_equal_i(i1, i2)          cl_assert_compare_i (i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2, \"%\"PRIdMAX \" != %\"PRIdMAX)\n> +#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 \" == \" #i2,  fmt \" != \" fmt, (int)(i1), (int)(i2))\n> +#define cl_assert_lt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LT, #i1 \" < \" #i2, __VA_ARGS__)\n> +#define cl_assert_lt_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LT, #i1 \" < \" #i2, \"%\"PRIdMAX \" >= %\"PRIdMAX)\n> +#define cl_assert_le_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LE, #i1 \" <= \" #i2, __VA_ARGS__)\n> +#define cl_assert_le_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LE, #i1 \" <= \" #i2, \"%\"PRIdMAX \" > %\"PRIdMAX)\n> +#define cl_assert_gt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GT, #i1 \" > \" #i2, __VA_ARGS__)\n> +#define cl_assert_gt_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GT, #i1 \" > \" #i2, \"%\"PRIdMAX \" <= %\"PRIdMAX)\n> +#define cl_assert_ge_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GE, #i1 \" >= \" #i2, __VA_ARGS__)\n> +#define cl_assert_ge_i(i1, i2)       cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GE, #i1 \" >= \" #i2, \"%\"PRIdMAX \" < %\"PRIdMAX)\n> +\n> +#define cl_assert_compare_u_(u1, u2, cmp, error, ...) clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n> +\t\t\t\t\t\t\t\t\t     (u1), (u2), \"Expected comparison to hold: \" error, __VA_ARGS__)\n> +#define cl_assert_compare_u(u1, u2, cmp, error, fmt) do { \\\n> +\tuintmax_t v1 = (u1), v2 = (u2); \\\n> +\tclar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \\\n> +\t\t\t       v1, v2, \"Expected comparison to hold: \" error, fmt, v1, v2); \\\n> +} while (0)\n> +#define cl_assert_equal_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_EQ, #u1 \" == \" #u2, __VA_ARGS__)\n> +#define cl_assert_equal_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_EQ, #u1 \" == \" #u2, \"%\"PRIuMAX \" != %\"PRIuMAX)\n> +#define cl_assert_lt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LT, #u1 \" < \" #u2, __VA_ARGS__)\n> +#define cl_assert_lt_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LT, #u1 \" < \" #u2, \"%\"PRIuMAX \" >= %\"PRIuMAX)\n> +#define cl_assert_le_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LE, #u1 \" <= \" #u2, __VA_ARGS__)\n> +#define cl_assert_le_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LE, #u1 \" <= \" #u2, \"%\"PRIuMAX \" > %\"PRIuMAX)\n> +#define cl_assert_gt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GT, #u1 \" > \" #u2, __VA_ARGS__)\n> +#define cl_assert_gt_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GT, #u1 \" > \" #u2, \"%\"PRIuMAX \" <= %\"PRIuMAX)\n> +#define cl_assert_ge_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GE, #u1 \" >= \" #u2, __VA_ARGS__)\n> +#define cl_assert_ge_u(u1, u2)       cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GE, #u1 \" >= \" #u2, \"%\"PRIuMAX \" < %\"PRIuMAX)\n>\n>  #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))\n\n[snip]\n\n[1]: https://github.com/clar-test/clar/commits/main/\n"},{"id":"533128","messageId":"CAOLa=ZQrx2T=mPC58oSW=1Y4i1cOCtneN+U7rDUmKAke9ifp7g@mail.gmail.com","threadId":"64582","inReplyTo":"20251206-b4-pks-clar-update-v2-0-9a14b10c1a36@pks.im","subject":"Re: [PATCH v2 0/3] Update clar for improved integer handling","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2026-01-06T11:01:51Z","receivedAt":"2026-01-06T11:01:53Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> Hi,\n>\n> this patch series updates clar. Most importantly, the update contains\n> properly typed handling of integers as well as a set of new asserts that\n> perform relative comparisons, like \"less than\" or \"greater or equal\".\n>\n> Thanks!\n>\n\nI went through the changes and they look good. I didn't know about\ndouble evaluation of arguments in macros before, so that was good to\nread about. Thanks\n\n> Patrick\n>\n> ---\n> Changes in v2:\n> - EDITME: describe what is new in this series revision.\n> - EDITME: use bulletpoints and terse descriptions.\n> - Link to v1: https://lore.kernel.org/r/20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im\n>\n\nEaster egg? :)\n\n- Karthik\n"},{"id":"533131","messageId":"aVzvBnwsuPB_zd3_@pks.im","threadId":"64582","inReplyTo":"CAOLa=ZQrx2T=mPC58oSW=1Y4i1cOCtneN+U7rDUmKAke9ifp7g@mail.gmail.com","subject":"Re: [PATCH v2 0/3] Update clar for improved integer handling","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-06T11:16:22Z","receivedAt":"2026-01-06T11:16:29Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Jan 06, 2026 at 03:01:51AM -0800, Karthik Nayak wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > Hi,\n> >\n> > this patch series updates clar. Most importantly, the update contains\n> > properly typed handling of integers as well as a set of new asserts that\n> > perform relative comparisons, like \"less than\" or \"greater or equal\".\n> >\n> > Thanks!\n> >\n> \n> I went through the changes and they look good. I didn't know about\n> double evaluation of arguments in macros before, so that was good to\n> read about. Thanks\n> \n> > Patrick\n> >\n> > ---\n> > Changes in v2:\n> > - EDITME: describe what is new in this series revision.\n> > - EDITME: use bulletpoints and terse descriptions.\n> > - Link to v1: https://lore.kernel.org/r/20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im\n> >\n> \n> Easter egg? :)\n\nOops :) Thanks for your review!\n\nPatrick\n"},{"id":"533132","messageId":"aVzvDGVEI2qVJv2F@pks.im","threadId":"64582","inReplyTo":"CAOLa=ZQZnYVuK8mDi6Yb8_+hqw_TMugn6i7BJCj1gbNHOruNWA@mail.gmail.com","subject":"Re: [PATCH v2 1/3] t/unit-tests: update clar to 39f11fe","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-06T11:16:28Z","receivedAt":"2026-01-06T11:16:32Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Jan 06, 2026 at 02:59:21AM -0800, Karthik Nayak wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> > Update clar to commit 39f11fe (Merge pull request #131 from\n> > pks-gitlab/pks-integer-double-evaluation, 2025-12-05). This commit\n> > includes the following changes relevant to Git:\n> >\n> \n> Nit: There is a newer commit merged into the clar repository, but I\n> don't think it is so important to include.\n\nYeah, I don't really think it's necessary. If this series needs a reroll\nI'll include it, but otherwise I'll keep this series as-is.\n\n> > @@ -149,6 +150,7 @@ const char *cl_fixture_basename(const char *fixture_name);\n> >   * Forced failure/warning\n> >   */\n> >  #define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Test failed.\", desc, 1)\n> > +#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n> \n> Nit: While most of the function accept description with variable\n> arguments, this is the only one which has the '...f()' format explicitly\n> separated out. It would be nicer if we simply make this part of\n> 'cl_fail()', no?\n\nThe problem is that we cannot do so easily. Varargs require at least one\nargument to be present, so we cannot make this `cl_fail(desc, ...)`\nwithout breaking the case where there are no variable arguments:\n\n  In file included from ../t/unit-tests/clar/clar.c:1053:\n  ../t/unit-tests/clar/clar/fs.h:460:3: error: expected expression\n    460 |                 cl_fail(\"Cannot copy; cannot stat destination\");\n        |                 ^\n  ../t/unit-tests/clar/clar.h:152:132: note: expanded from macro 'cl_fail'\n    152 | #define cl_fail(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n        |                                                                                                                                    ^\n\nThe alternative would be to make this `cl_fail(...)` instead, but to the\nbest of my knowledge this isn't even a valid construct.\n\nPatrick\n"},{"id":"533151","messageId":"CAOLa=ZTd7Tq6jPCefua07Rw1-zyOY98tjR2g-Em6CM8wui4KtQ@mail.gmail.com","threadId":"64582","inReplyTo":"aVzvDGVEI2qVJv2F@pks.im","subject":"Re: [PATCH v2 1/3] t/unit-tests: update clar to 39f11fe","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2026-01-06T16:28:15Z","receivedAt":"2026-01-06T16:28:18Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Tue, Jan 06, 2026 at 02:59:21AM -0800, Karthik Nayak wrote:\n>> Patrick Steinhardt <ps@pks.im> writes:\n>>\n>> > Update clar to commit 39f11fe (Merge pull request #131 from\n>> > pks-gitlab/pks-integer-double-evaluation, 2025-12-05). This commit\n>> > includes the following changes relevant to Git:\n>> >\n>>\n>> Nit: There is a newer commit merged into the clar repository, but I\n>> don't think it is so important to include.\n>\n> Yeah, I don't really think it's necessary. If this series needs a reroll\n> I'll include it, but otherwise I'll keep this series as-is.\n>\n\nAgreed.\n\n>> > @@ -149,6 +150,7 @@ const char *cl_fixture_basename(const char *fixture_name);\n>> >   * Forced failure/warning\n>> >   */\n>> >  #define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, \"Test failed.\", desc, 1)\n>> > +#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n>>\n>> Nit: While most of the function accept description with variable\n>> arguments, this is the only one which has the '...f()' format explicitly\n>> separated out. It would be nicer if we simply make this part of\n>> 'cl_fail()', no?\n>\n> The problem is that we cannot do so easily. Varargs require at least one\n> argument to be present, so we cannot make this `cl_fail(desc, ...)`\n> without breaking the case where there are no variable arguments:\n>\n>   In file included from ../t/unit-tests/clar/clar.c:1053:\n>   ../t/unit-tests/clar/clar/fs.h:460:3: error: expected expression\n>     460 |                 cl_fail(\"Cannot copy; cannot stat destination\");\n>         |                 ^\n>   ../t/unit-tests/clar/clar.h:152:132: note: expanded from macro 'cl_fail'\n>     152 | #define cl_fail(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n>         |                                                                                                                                    ^\n>\n> The alternative would be to make this `cl_fail(...)` instead, but to the\n> best of my knowledge this isn't even a valid construct.\n>\n> Patrick\n\nAh right. Thanks for the explanation.\n"},{"id":"533179","messageId":"xmqqjyxuhyd9.fsf@gitster.g","threadId":"64582","inReplyTo":"aVzvBnwsuPB_zd3_@pks.im","subject":"Re: [PATCH v2 0/3] Update clar for improved integer handling","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-07T03:53:38Z","receivedAt":"2026-01-07T03:53:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>> > Changes in v2:\n>> > - EDITME: describe what is new in this series revision.\n>> > - EDITME: use bulletpoints and terse descriptions.\n>> > - Link to v1: https://lore.kernel.org/r/20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im\n>> >\n>> \n>> Easter egg? :)\n>\n> Oops :) Thanks for your review!\n\nLooking good.  Shall we mark it for 'next'?\n"},{"id":"533183","messageId":"aV4EwmNdnHyv9wXR@pks.im","threadId":"64582","inReplyTo":"xmqqjyxuhyd9.fsf@gitster.g","subject":"Re: [PATCH v2 0/3] Update clar for improved integer handling","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-01-07T07:01:22Z","receivedAt":"2026-01-07T07:01:36Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Jan 07, 2026 at 12:53:38PM +0900, Junio C Hamano wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> \n> >> > Changes in v2:\n> >> > - EDITME: describe what is new in this series revision.\n> >> > - EDITME: use bulletpoints and terse descriptions.\n> >> > - Link to v1: https://lore.kernel.org/r/20251205-b4-pks-clar-update-v1-0-fd70aac2ab90@pks.im\n> >> >\n> >> \n> >> Easter egg? :)\n> >\n> > Oops :) Thanks for your review!\n> \n> Looking good.  Shall we mark it for 'next'?\n\nYeah, I think this topic should be good to go. Thanks!\n\nPatrick\n"},{"id":"533184","messageId":"20260107074000.GA102520@coredump.intra.peff.net","threadId":"64582","inReplyTo":"aVzvDGVEI2qVJv2F@pks.im","subject":"Re: [PATCH v2 1/3] t/unit-tests: update clar to 39f11fe","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-01-07T07:40:00Z","receivedAt":"2026-01-07T07:40:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Jan 06, 2026 at 12:16:28PM +0100, Patrick Steinhardt wrote:\n\n> The problem is that we cannot do so easily. Varargs require at least one\n> argument to be present, so we cannot make this `cl_fail(desc, ...)`\n> without breaking the case where there are no variable arguments:\n> \n>   In file included from ../t/unit-tests/clar/clar.c:1053:\n>   ../t/unit-tests/clar/clar/fs.h:460:3: error: expected expression\n>     460 |                 cl_fail(\"Cannot copy; cannot stat destination\");\n>         |                 ^\n>   ../t/unit-tests/clar/clar.h:152:132: note: expanded from macro 'cl_fail'\n>     152 | #define cl_fail(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, \"Test failed.\", desc, __VA_ARGS__)\n>         |                                                                                                                                    ^\n> \n> The alternative would be to make this `cl_fail(...)` instead, but to the\n> best of my knowledge this isn't even a valid construct.\n\nIt is valid; that's how we define BUG(), for example.\n\nThere are other options like GNU's \"## __VA_ARGS__\", but I think eating\nthe format argument with the \"...\" is the only portable way, at least\nfor standard versions we'd support. I think C23 added __VA_OPT__.\n\n-Peff\n"},{"id":"533281","messageId":"xmqqwm1sff1k.fsf@gitster.g","threadId":"64582","inReplyTo":"20260107074000.GA102520@coredump.intra.peff.net","subject":"Re: [PATCH v2 1/3] t/unit-tests: update clar to 39f11fe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-01-08T12:46:15Z","receivedAt":"2026-01-08T12:46:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> The alternative would be to make this `cl_fail(...)` instead, but to the\n>> best of my knowledge this isn't even a valid construct.\n>\n> It is valid; that's how we define BUG(), for example.\n\nThanks.  I was wondering how that thing was working after seeing\nPatrick's message ;-).\n\n> There are other options like GNU's \"## __VA_ARGS__\", but I think eating\n> the format argument with the \"...\" is the only portable way, at least\n> for standard versions we'd support. I think C23 added __VA_OPT__.\n>\n> -Peff\n"}]}