From 553bd7dee4568abc3b88d3d4a989baa20f02a153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sat, 18 Jul 2026 23:06:50 +0200 Subject: [PATCH 01/10] theories: fix compiler warning --- src/core/theories.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/theories.c b/src/core/theories.c index de81cc37..4ff5008d 100644 --- a/src/core/theories.c +++ b/src/core/theories.c @@ -100,7 +100,7 @@ void cr_theory_call(struct cri_theory_ctx *ctx, void (*fnptr)(void)) static bool contains_word(const char *str, const char *pattern, size_t sz) { - char *res = strstr(str, pattern); + const char *res = strstr(str, pattern); return res && (res == str || (res > str && res[-1] == ' ')) From b789322b47b736cec1640f81514c1804776b3ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Fri, 17 Jul 2026 23:04:39 +0200 Subject: [PATCH 02/10] new-asserts: fix result propagation of nested assertion combinators --- include/criterion/internal/new_asserts.h | 6 +++--- test/cram/bugs.t | 16 ++++++++++++++++ test/full/combinators.c | 21 +++++++++++++++++++++ test/full/meson.build | 1 + 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 test/full/combinators.c diff --git a/include/criterion/internal/new_asserts.h b/include/criterion/internal/new_asserts.h index f40b51bb..04d14b18 100644 --- a/include/criterion/internal/new_asserts.h +++ b/include/criterion/internal/new_asserts.h @@ -127,7 +127,7 @@ int cri_cond = cri_cond_def \ CRITERION_APPLY(CRI_ASSERT_SPECIFIER_ALL_INDIRECT, cri_cond, __VA_ARGS__); \ cri_node->pass = !!cri_cond; \ - *cri_pass = *cri_pass && cri_cond; \ + *cri_pass = cri_cond; \ cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig @@ -149,7 +149,7 @@ int cri_cond = cri_cond_def \ CRITERION_APPLY(CRI_ASSERT_SPECIFIER_NONE_INDIRECT, cri_cond, __VA_ARGS__); \ cri_node->pass = !!cri_cond; \ - *cri_pass = *cri_pass && cri_cond; \ + *cri_pass = cri_cond; \ cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig @@ -167,7 +167,7 @@ int cri_cond = cri_cond_def \ CRITERION_APPLY(CRI_ASSERT_SPECIFIER_ANY_INDIRECT, cri_cond, __VA_ARGS__); \ cri_node->pass = !!cri_cond; \ - *cri_pass = *cri_pass || cri_cond; \ + *cri_pass = cri_cond; \ cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig diff --git a/test/cram/bugs.t b/test/cram/bugs.t index 7f42d220..2922746a 100644 --- a/test/cram/bugs.t +++ b/test/cram/bugs.t @@ -11,3 +11,19 @@ https://github.com/Snaipe/Criterion/issues/463 [FAIL] bug463::nonl [====] Synthesis: Tested: 2 | Passing: 0 | Failing: 2 | Crashing: 0 +Nested assertion combinators must resolve and compose correctly + + $ combinators.c.bin + [----] combinators.c:20: Assertion Failed + [----] eq(i32, 1, 0): + [----] diff: [-1-]{+0+} + [----] eq(i32, 1, 0): + [----] diff: [-1-]{+0+} + [FAIL] combinators::nested_any_fails + [----] combinators.c:16: Assertion Failed + [----] eq(i32, 1, 1): + [----] @@@ + [----] eq(i32, 2, 2): + [----] @@@ + [FAIL] combinators::not_all_fails + [====] Synthesis: Tested: 4 | Passing: 2 | Failing: 2 | Crashing: 0 diff --git a/test/full/combinators.c b/test/full/combinators.c new file mode 100644 index 00000000..b2d8fa3f --- /dev/null +++ b/test/full/combinators.c @@ -0,0 +1,21 @@ +#include +#include + +Test(combinators, all_inside_any) { + cr_expect(any(eq(i32, 1, 0), all(eq(i32, 1, 1), eq(i32, 2, 2)))); + cr_expect(any(eq(i32, 1, 0), none(eq(i32, 1, 0)))); + cr_expect(all(any(eq(i32, 1, 0), all(eq(i32, 1, 1), eq(i32, 2, 2))), eq(i32, 3, 3))); +} + +Test(combinators, not_all) { + cr_expect(not(all(eq(i32, 1, 0), eq(i32, 1, 1)))); + cr_expect(not(none(eq(i32, 1, 1)))); +} + +Test(combinators, not_all_fails) { + cr_expect(not(all(eq(i32, 1, 1), eq(i32, 2, 2)))); +} + +Test(combinators, nested_any_fails) { + cr_expect(any(eq(i32, 1, 0), all(eq(i32, 1, 0), eq(i32, 1, 1)))); +} diff --git a/test/full/meson.build b/test/full/meson.build index 9d30b0aa..33ce160a 100644 --- a/test/full/meson.build +++ b/test/full/meson.build @@ -1,4 +1,5 @@ full_tests = [ + 'combinators.c', 'exit.c', 'failmessages.c', 'flood.c', From beed57ecc7ad31e347fcf62da19d1ee843bce40d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sat, 18 Jul 2026 01:16:38 +0200 Subject: [PATCH 03/10] new-asserts: allow sibling combinators --- include/criterion/internal/new_asserts.h | 20 ++++++++++---------- test/cram/bugs.t | 6 +++--- test/full/combinators.c | 6 ++++++ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/include/criterion/internal/new_asserts.h b/include/criterion/internal/new_asserts.h index 04d14b18..664a8b31 100644 --- a/include/criterion/internal/new_asserts.h +++ b/include/criterion/internal/new_asserts.h @@ -113,8 +113,8 @@ if (cri_cond_un != cri_cond_expect) \ cri_assert_node_negate(cri_prevnode) -#define CRI_ASSERT_SPECIFIER_ALL_INDIRECT(Cond, E); \ - cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); \ +#define CRI_ASSERT_SPECIFIER_ALL_INDIRECT(Cond, E); \ + { cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); } \ Cond = Cond && cri_cond_un #define CRI_ASSERT_TEST_SPECIFIER_all(...) , @@ -131,12 +131,12 @@ cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig -#define CRI_ASSERT_SPECIFIER_NONE_INDIRECT(Cond, E); \ - cri_cond_expect = !cri_cond_expect; \ - cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); \ - if (cri_cond_un != cri_cond_expect) \ - cri_assert_node_negate(cri_prevnode); \ - cri_cond_expect = !cri_cond_expect; \ +#define CRI_ASSERT_SPECIFIER_NONE_INDIRECT(Cond, E); \ + cri_cond_expect = !cri_cond_expect; \ + { cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); } \ + if (cri_cond_un != cri_cond_expect) \ + cri_assert_node_negate(cri_prevnode); \ + cri_cond_expect = !cri_cond_expect; \ Cond = Cond && !(cri_cond_un) #define CRI_ASSERT_TEST_SPECIFIER_none(...) , @@ -153,8 +153,8 @@ cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig -#define CRI_ASSERT_SPECIFIER_ANY_INDIRECT(Cond, E) \ - ; cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); \ +#define CRI_ASSERT_SPECIFIER_ANY_INDIRECT(Cond, E) \ + ; { cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); } \ Cond = Cond || cri_cond_un #define CRI_ASSERT_TEST_SPECIFIER_any(...) , diff --git a/test/cram/bugs.t b/test/cram/bugs.t index 2922746a..7c52f10f 100644 --- a/test/cram/bugs.t +++ b/test/cram/bugs.t @@ -14,16 +14,16 @@ https://github.com/Snaipe/Criterion/issues/463 Nested assertion combinators must resolve and compose correctly $ combinators.c.bin - [----] combinators.c:20: Assertion Failed + [----] combinators.c:26: Assertion Failed [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [FAIL] combinators::nested_any_fails - [----] combinators.c:16: Assertion Failed + [----] combinators.c:22: Assertion Failed [----] eq(i32, 1, 1): [----] @@@ [----] eq(i32, 2, 2): [----] @@@ [FAIL] combinators::not_all_fails - [====] Synthesis: Tested: 4 | Passing: 2 | Failing: 2 | Crashing: 0 + [====] Synthesis: Tested: 5 | Passing: 3 | Failing: 2 | Crashing: 0 diff --git a/test/full/combinators.c b/test/full/combinators.c index b2d8fa3f..ed88fa54 100644 --- a/test/full/combinators.c +++ b/test/full/combinators.c @@ -12,6 +12,12 @@ Test(combinators, not_all) { cr_expect(not(none(eq(i32, 1, 1)))); } +Test(combinators, siblings) { + cr_expect(all(any(eq(i32, 1, 1), eq(i32, 1, 0)), any(eq(i32, 2, 2)))); + cr_expect(any(all(eq(i32, 1, 0)), all(eq(i32, 2, 2)))); + cr_expect(none(any(eq(i32, 1, 0)), any(eq(i32, 0, 1)))); +} + Test(combinators, not_all_fails) { cr_expect(not(all(eq(i32, 1, 1), eq(i32, 2, 2)))); } From 98bf391385eec522dc4296b6ce4bfe2cac55f530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sat, 18 Jul 2026 22:30:30 +0200 Subject: [PATCH 04/10] new-asserts: rebuild not() as a single-operand combinator group --- include/criterion/internal/new_asserts.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/include/criterion/internal/new_asserts.h b/include/criterion/internal/new_asserts.h index 664a8b31..1224b3ce 100644 --- a/include/criterion/internal/new_asserts.h +++ b/include/criterion/internal/new_asserts.h @@ -105,13 +105,19 @@ /* Logical specifiers */ #define CRI_ASSERT_TEST_SPECIFIER_not(...) , -#define CRI_ASSERT_SPECIFIER_not(E) \ - cri_cond_expect = !cri_cond_expect; \ - CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); \ - cri_cond_expect = !cri_cond_expect; \ - cri_cond_un = !cri_cond_un; \ - if (cri_cond_un != cri_cond_expect) \ - cri_assert_node_negate(cri_prevnode) +#define CRI_ASSERT_SPECIFIER_not(E) \ + cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ + cri_assert_node_init(&cri_tmpn); \ + cri_tmpn.repr = "not(" CR_STR(E) ")"; \ + struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ + struct cri_assert_node *cri_node = cri_tmp; \ + int cri_cond_def = 1, cri_cond_un; \ + int cri_cond = cri_cond_def \ + CRITERION_APPLY(CRI_ASSERT_SPECIFIER_NONE_INDIRECT, cri_cond, E); \ + cri_node->pass = !!cri_cond; \ + *cri_pass = cri_cond; \ + cri_prevnode = cri_node; \ + } while (0); cri_pass = cri_pass_orig #define CRI_ASSERT_SPECIFIER_ALL_INDIRECT(Cond, E); \ { cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); } \ From 4e2170bc842871c192b931d0bff30e8eeb48f784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Tue, 1 Sep 2026 18:46:38 +0200 Subject: [PATCH 05/10] assert: fall back to raw params when the value diff is empty git_diff_buffers() produces no output for byte-identical inputs, and collect_leaves() sent the resulting NULL through the formatted oneof, which nanopb silently drops from the wire. The runner then received a value-less result and could only print a generic guess: "no message or difference -- this is a user bug in the object stringifier" Send the raw actual/expected params instead; the runner already detects equal params and correctly blames the user stringifier, matching how short undiffable values are reported. --- src/core/assert.c | 9 +++++++-- test/cram/bugs.t | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/assert.c b/src/core/assert.c index 6a8bdede..47cac1dd 100644 --- a/src/core/assert.c +++ b/src/core/assert.c @@ -158,8 +158,13 @@ static criterion_protocol_result *collect_leaves( actual.ptr[actual_len] = '\n'; int rc = cri_diff_buffer_to_buffer(&expected, &actual, &diff); - if (rc < 0) - res->value.formatted = NULL; + if (rc < 0 || !diff.ptr) { + /* An empty or failed diff can't explain the mismatch; + fall back to the raw values. */ + expected.ptr[expected_len] = '\0'; + actual.ptr[actual_len] = '\0'; + goto process_params; + } res->value.formatted = diff.ptr; } else { diff --git a/test/cram/bugs.t b/test/cram/bugs.t index 7c52f10f..ee73406e 100644 --- a/test/cram/bugs.t +++ b/test/cram/bugs.t @@ -3,7 +3,7 @@ https://github.com/Snaipe/Criterion/issues/463 $ bug463.c.bin [----] bug463.c:16: Assertion Failed [----] eq(type(struct T1), (struct T1){}, (struct T1){}): - [----] @@@ + [----] @@@ [FAIL] bug463::nl [----] bug463.c:31: Assertion Failed [----] eq(type(struct T2), (struct T2){}, (struct T2){}): From bf20a24685e8884829d1d585682e1cbc03c851b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Tue, 1 Sep 2026 19:32:27 +0200 Subject: [PATCH 06/10] new-asserts: fix how negated results are recorded To handle none() and not(), the code walked the recorded results and flipped their pass flag. After the flip, the tree no longer showed what really happened, so the error report could not explain it. Now every result keeps two values: pass (did the check pass) and negated (was it expected to fail). A result counts as a failure when the two are equal. --- include/criterion/internal/assert/exceptions.h | 12 +++++++++--- include/criterion/internal/assert/op.h | 10 +++++++--- include/criterion/internal/assert/types.h | 4 ++-- include/criterion/internal/new_asserts.h | 15 ++++++--------- src/core/assert.c | 16 +++++++--------- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/include/criterion/internal/assert/exceptions.h b/include/criterion/internal/assert/exceptions.h index 79e6cd3b..e3de0f66 100644 --- a/include/criterion/internal/assert/exceptions.h +++ b/include/criterion/internal/assert/exceptions.h @@ -56,7 +56,9 @@ cri_tmpn.params[0].type = CRI_ASSERT_RT_STR; \ cri_tmpn.params[0].data = cri_str; \ } \ - cri_prevnode = cri_assert_node_add(cri_node, &cri_tmpn); \ + cri_tmpn.pass = !!cri_cond_un; \ + cri_tmpn.negated = !cri_cond_expect; \ + cri_assert_node_add(cri_node, &cri_tmpn); \ } while (0) #define CRI_ASSERT_TEST_SPECIFIER_nothrow(...) , @@ -82,7 +84,9 @@ cri_tmpn.params[0].type = CRI_ASSERT_RT_STR; \ cri_tmpn.params[0].data = cri_str; \ } \ - cri_prevnode = cri_assert_node_add(cri_node, &cri_tmpn); \ + cri_tmpn.pass = !!cri_cond_un; \ + cri_tmpn.negated = !cri_cond_expect; \ + cri_assert_node_add(cri_node, &cri_tmpn); \ } while (0) #define CRI_ASSERT_TEST_SPECIFIER_anythrow(...) , @@ -101,7 +105,9 @@ cri_tmpn.params[0].type = CRI_ASSERT_RT_STR; \ cri_tmpn.params[0].data = cri_str; \ } \ - cri_prevnode = cri_assert_node_add(cri_node, &cri_tmpn); \ + cri_tmpn.pass = !!cri_cond_un; \ + cri_tmpn.negated = !cri_cond_expect; \ + cri_assert_node_add(cri_node, &cri_tmpn); \ } while (0) #endif /* !CRITERION_INTERNAL_ASSERT_EXCEPTIONS_H_ */ diff --git a/include/criterion/internal/assert/op.h b/include/criterion/internal/assert/op.h index 98271bcb..3ba04fba 100644 --- a/include/criterion/internal/assert/op.h +++ b/include/criterion/internal/assert/op.h @@ -317,7 +317,8 @@ size_t cri_paramidx = 0; \ CRITERION_APPLY(CRI_ASSERT_IT_MKNODE_AUTO, , __VA_ARGS__) \ cri_tmpn.pass = !!cri_cond_un; \ - cri_prevnode = cri_assert_node_add(cri_node, &cri_tmpn); \ + cri_tmpn.negated = !cri_cond_expect; \ + cri_assert_node_add(cri_node, &cri_tmpn); \ } \ } while (0) #else @@ -349,7 +350,8 @@ size_t cri_paramidx = 0; \ CRITERION_APPLY(CRI_ASSERT_IT_MKNODE, Tag, __VA_ARGS__) \ cri_tmpn.pass = !!cri_cond_un; \ - cri_prevnode = cri_assert_node_add(cri_node, &cri_tmpn); \ + cri_tmpn.negated = !cri_cond_expect; \ + cri_assert_node_add(cri_node, &cri_tmpn); \ } \ } while (0) @@ -364,6 +366,7 @@ ) ")"; \ cri_tmpn.repr = cri_repr; \ cri_tmpn.pass = 1; \ + cri_tmpn.negated = !cri_cond_expect; \ size_t cri_paramidx = 0; \ CRITERION_APPLY(CRI_ASSERT_IT_MKNODE_ARR, Tag, __VA_ARGS__) \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ @@ -377,7 +380,8 @@ CRITERION_APPLY(CRI_ASSERT_IT_MKNODE_SUBSCRIPT, Tag, __VA_ARGS__) \ cri_tmpn.pass = !!(CRI_ASSERT_OP_APPLY(Op, \ Tag CRITERION_APPLY(CRI_ASSERT_IT_SUNPACK, , __VA_ARGS__))); \ - cri_prevnode = cri_assert_node_add(cri_node, &cri_tmpn); \ + cri_tmpn.negated = !cri_cond_expect; \ + cri_assert_node_add(cri_node, &cri_tmpn); \ cri_node->pass = cri_node->pass && cri_tmpn.pass; \ } \ cri_cond_un = cri_node->pass; \ diff --git a/include/criterion/internal/assert/types.h b/include/criterion/internal/assert/types.h index 55ea54bc..3767c8a1 100644 --- a/include/criterion/internal/assert/types.h +++ b/include/criterion/internal/assert/types.h @@ -44,7 +44,8 @@ struct cri_assert_node { unsigned pass : 1; unsigned dynrepr : 1; - uint32_t nchild : 30; + unsigned negated : 1; + uint32_t nchild : 29; uint32_t maxchild; struct cri_assert_node *children; }; @@ -54,7 +55,6 @@ CR_BEGIN_C_API CR_API void cri_assert_node_init(struct cri_assert_node *node); CR_API struct cri_assert_node *cri_assert_node_add(struct cri_assert_node *tree, struct cri_assert_node *node); -CR_API void cri_assert_node_negate(struct cri_assert_node *tree); CR_API void cri_assert_node_term(struct cri_assert_node *tree); CR_API void cri_assert_node_send(const char *file, size_t line, struct cri_assert_node *tree); CR_API char *cri_assert_message(const char *fmt, ...); diff --git a/include/criterion/internal/new_asserts.h b/include/criterion/internal/new_asserts.h index 1224b3ce..0e1216e8 100644 --- a/include/criterion/internal/new_asserts.h +++ b/include/criterion/internal/new_asserts.h @@ -69,10 +69,8 @@ #define CRI_ASSERT_CALL(File, Line, Fail, Condition, ...) \ CR_EVAL(do { \ struct cri_assert_node cri_tmpn, cri_root, *cri_node = &cri_root; \ - struct cri_assert_node *cri_prevnode; \ (void) cri_tmpn; \ (void) cri_node; \ - (void) cri_prevnode; \ cri_assert_node_init(&cri_root); \ int cri_cond, cri_cond_un, *cri_pass = &cri_cond_un; \ int cri_cond_def = 1; \ @@ -98,7 +96,8 @@ cri_assert_node_init(&cri_tmpn); \ cri_tmpn.repr = CR_STR(Val); \ cri_tmpn.pass = !!cri_cond_un; \ - cri_prevnode = cri_assert_node_add(cri_node, &cri_tmpn); \ + cri_tmpn.negated = !cri_cond_expect; \ + cri_assert_node_add(cri_node, &cri_tmpn); \ } \ } while (0) @@ -109,6 +108,7 @@ cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ cri_assert_node_init(&cri_tmpn); \ cri_tmpn.repr = "not(" CR_STR(E) ")"; \ + cri_tmpn.negated = !cri_cond_expect; \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ struct cri_assert_node *cri_node = cri_tmp; \ int cri_cond_def = 1, cri_cond_un; \ @@ -116,7 +116,6 @@ CRITERION_APPLY(CRI_ASSERT_SPECIFIER_NONE_INDIRECT, cri_cond, E); \ cri_node->pass = !!cri_cond; \ *cri_pass = cri_cond; \ - cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig #define CRI_ASSERT_SPECIFIER_ALL_INDIRECT(Cond, E); \ @@ -127,6 +126,7 @@ #define CRI_ASSERT_SPECIFIER_all(...) \ cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ cri_assert_node_init(&cri_tmpn); \ + cri_tmpn.negated = !cri_cond_expect; \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ struct cri_assert_node *cri_node = cri_tmp; \ int cri_cond_def = 1, cri_cond_un; \ @@ -134,14 +134,11 @@ CRITERION_APPLY(CRI_ASSERT_SPECIFIER_ALL_INDIRECT, cri_cond, __VA_ARGS__); \ cri_node->pass = !!cri_cond; \ *cri_pass = cri_cond; \ - cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig #define CRI_ASSERT_SPECIFIER_NONE_INDIRECT(Cond, E); \ cri_cond_expect = !cri_cond_expect; \ { cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); } \ - if (cri_cond_un != cri_cond_expect) \ - cri_assert_node_negate(cri_prevnode); \ cri_cond_expect = !cri_cond_expect; \ Cond = Cond && !(cri_cond_un) @@ -149,6 +146,7 @@ #define CRI_ASSERT_SPECIFIER_none(...) \ cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ cri_assert_node_init(&cri_tmpn); \ + cri_tmpn.negated = !cri_cond_expect; \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ struct cri_assert_node *cri_node = cri_tmp; \ int cri_cond_def = 1, cri_cond_un; \ @@ -156,7 +154,6 @@ CRITERION_APPLY(CRI_ASSERT_SPECIFIER_NONE_INDIRECT, cri_cond, __VA_ARGS__); \ cri_node->pass = !!cri_cond; \ *cri_pass = cri_cond; \ - cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig #define CRI_ASSERT_SPECIFIER_ANY_INDIRECT(Cond, E) \ @@ -167,6 +164,7 @@ #define CRI_ASSERT_SPECIFIER_any(...) \ cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ cri_assert_node_init(&cri_tmpn); \ + cri_tmpn.negated = !cri_cond_expect; \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ struct cri_assert_node *cri_node = cri_tmp; \ int cri_cond_def = 0; \ @@ -174,7 +172,6 @@ CRITERION_APPLY(CRI_ASSERT_SPECIFIER_ANY_INDIRECT, cri_cond, __VA_ARGS__); \ cri_node->pass = !!cri_cond; \ *cri_pass = cri_cond; \ - cri_prevnode = cri_node; \ } while (0); cri_pass = cri_pass_orig #undef cr_assert_user diff --git a/src/core/assert.c b/src/core/assert.c index 47cac1dd..ff3a88c4 100644 --- a/src/core/assert.c +++ b/src/core/assert.c @@ -54,13 +54,6 @@ CR_API struct cri_assert_node *cri_assert_node_add(struct cri_assert_node *tree, return &tree->children[tree->nchild - 1]; } -CR_API void cri_assert_node_negate(struct cri_assert_node *tree) -{ - for (size_t i = 0; i < tree->nchild; ++i) - cri_assert_node_negate(&tree->children[i]); - tree->pass = !tree->pass; -} - CR_API void cri_assert_node_term(struct cri_assert_node *tree) { for (struct cri_assert_param *p = &tree->params[0]; p->name; ++p) @@ -73,13 +66,18 @@ CR_API void cri_assert_node_term(struct cri_assert_node *tree) free((char *) tree->repr); } +static bool node_failed(const struct cri_assert_node *node) +{ + return node->pass == node->negated; +} + static size_t leaf_count(struct cri_assert_node *tree) { size_t count = 0; for (size_t i = 0; i < tree->nchild; ++i) { struct cri_assert_node *node = &tree->children[i]; - if (!node->pass) + if (node_failed(node)) ++count; if (node->nchild > 0) count += leaf_count(&tree->children[i]); @@ -183,7 +181,7 @@ static criterion_protocol_result *collect_leaves( for (size_t i = 0; i < tree->nchild; ++i) { struct cri_assert_node *node = &tree->children[i]; - if (node->pass) + if (!node_failed(node)) continue; res = collect_leaves(res, node); } From 8cccd71261edf23fd175700fb6664a0a3a9ede8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Tue, 1 Sep 2026 19:33:51 +0200 Subject: [PATCH 07/10] protocol: add a negated flag to assert results An operand under none() or not() fails because it matched. The runner needs to know this so it can print the value instead of an empty diff. --- src/protocol/criterion.proto | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/protocol/criterion.proto b/src/protocol/criterion.proto index 01bb1556..ac87c620 100644 --- a/src/protocol/criterion.proto +++ b/src/protocol/criterion.proto @@ -67,6 +67,8 @@ message result { param_list params = 3; string formatted = 4; } + + optional bool negated = 5; } message assert { From add8e9b5fd557102440022d9cbad25c419e29f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Tue, 1 Sep 2026 19:33:58 +0200 Subject: [PATCH 08/10] protocol: update generated nanopb files --- src/protocol/criterion.pb.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/protocol/criterion.pb.h b/src/protocol/criterion.pb.h index dc7a12c6..8807809b 100644 --- a/src/protocol/criterion.pb.h +++ b/src/protocol/criterion.pb.h @@ -59,6 +59,8 @@ typedef struct _criterion_protocol_result { struct _criterion_protocol_param_list *params; char *formatted; } value; + bool has_negated; + bool negated; } criterion_protocol_result; typedef struct _criterion_protocol_ack { @@ -168,7 +170,7 @@ extern "C" { #define criterion_protocol_statistic_init_default {NULL, 0, {0}} #define criterion_protocol_param_entry_init_default {NULL, 0, {NULL}} #define criterion_protocol_param_list_init_default {0, NULL} -#define criterion_protocol_result_init_default {NULL, NULL, 0, {NULL}} +#define criterion_protocol_result_init_default {NULL, NULL, 0, {NULL}, false, false} #define criterion_protocol_assert_init_default {NULL, 0, NULL, false, 0, false, 0, 0, NULL} #define criterion_protocol_log_init_default {_criterion_protocol_log_level_MIN, NULL, false, 0} #define criterion_protocol_ack_init_default {_criterion_protocol_ack_status_MIN, NULL} @@ -180,7 +182,7 @@ extern "C" { #define criterion_protocol_statistic_init_zero {NULL, 0, {0}} #define criterion_protocol_param_entry_init_zero {NULL, 0, {NULL}} #define criterion_protocol_param_list_init_zero {0, NULL} -#define criterion_protocol_result_init_zero {NULL, NULL, 0, {NULL}} +#define criterion_protocol_result_init_zero {NULL, NULL, 0, {NULL}, false, false} #define criterion_protocol_assert_init_zero {NULL, 0, NULL, false, 0, false, 0, 0, NULL} #define criterion_protocol_log_init_zero {_criterion_protocol_log_level_MIN, NULL, false, 0} #define criterion_protocol_ack_init_zero {_criterion_protocol_ack_status_MIN, NULL} @@ -196,6 +198,7 @@ extern "C" { #define criterion_protocol_result_message_tag 2 #define criterion_protocol_result_params_tag 3 #define criterion_protocol_result_formatted_tag 4 +#define criterion_protocol_result_negated_tag 5 #define criterion_protocol_ack_status_code_tag 1 #define criterion_protocol_ack_message_tag 2 #define criterion_protocol_assert_message_tag 1 @@ -276,7 +279,8 @@ X(a, POINTER, REPEATED, MESSAGE, list, 1) X(a, POINTER, REQUIRED, STRING, repr, 1) \ X(a, POINTER, OPTIONAL, STRING, message, 2) \ X(a, POINTER, ONEOF, MESSAGE, (value,params,value.params), 3) \ -X(a, POINTER, ONEOF, STRING, (value,formatted,value.formatted), 4) +X(a, POINTER, ONEOF, STRING, (value,formatted,value.formatted), 4) \ +X(a, STATIC, OPTIONAL, BOOL, negated, 5) #define criterion_protocol_result_CALLBACK NULL #define criterion_protocol_result_DEFAULT NULL #define criterion_protocol_result_value_params_MSGTYPE criterion_protocol_param_list From 7bfe5b76024c3ebe0150688e3c0ba9aa99966a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Tue, 1 Sep 2026 19:56:26 +0200 Subject: [PATCH 09/10] new-asserts: print failing combinators above their operands A failing all(), any(), none() or not() now prints a header line with its own text, so the failing operands below it can be read in context. --- include/criterion/internal/new_asserts.h | 3 + src/core/assert.c | 11 ++++ src/core/client.c | 3 +- test/cram/asserts.t | 81 ++++++++++++++++++++++++ test/cram/bugs.t | 4 ++ 5 files changed, 101 insertions(+), 1 deletion(-) diff --git a/include/criterion/internal/new_asserts.h b/include/criterion/internal/new_asserts.h index 0e1216e8..fb22720d 100644 --- a/include/criterion/internal/new_asserts.h +++ b/include/criterion/internal/new_asserts.h @@ -126,6 +126,7 @@ #define CRI_ASSERT_SPECIFIER_all(...) \ cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ cri_assert_node_init(&cri_tmpn); \ + cri_tmpn.repr = "all(" CR_STR(__VA_ARGS__) ")"; \ cri_tmpn.negated = !cri_cond_expect; \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ struct cri_assert_node *cri_node = cri_tmp; \ @@ -146,6 +147,7 @@ #define CRI_ASSERT_SPECIFIER_none(...) \ cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ cri_assert_node_init(&cri_tmpn); \ + cri_tmpn.repr = "none(" CR_STR(__VA_ARGS__) ")"; \ cri_tmpn.negated = !cri_cond_expect; \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ struct cri_assert_node *cri_node = cri_tmp; \ @@ -164,6 +166,7 @@ #define CRI_ASSERT_SPECIFIER_any(...) \ cri_cond_def; int *cri_pass_orig = cri_pass; cri_pass = &cri_cond_un; do { \ cri_assert_node_init(&cri_tmpn); \ + cri_tmpn.repr = "any(" CR_STR(__VA_ARGS__) ")"; \ cri_tmpn.negated = !cri_cond_expect; \ struct cri_assert_node *cri_tmp = cri_assert_node_add(cri_node, &cri_tmpn); \ struct cri_assert_node *cri_node = cri_tmp; \ diff --git a/src/core/assert.c b/src/core/assert.c index ff3a88c4..ee2d907d 100644 --- a/src/core/assert.c +++ b/src/core/assert.c @@ -179,6 +179,17 @@ static criterion_protocol_result *collect_leaves( return res + 1; } + /* Combinator group nodes announce themselves in a value-less header + result before their failing operands are reported. The root node + has no repr and stays silent. */ + if (tree->repr && tree->nchild > 0) { + *res = (criterion_protocol_result) { + .repr = (char *) tree->repr, + .message = (char *) tree->message, + }; + ++res; + } + for (size_t i = 0; i < tree->nchild; ++i) { struct cri_assert_node *node = &tree->children[i]; if (!node_failed(node)) diff --git a/src/core/client.c b/src/core/client.c index 5fc346a8..92a82590 100644 --- a/src/core/client.c +++ b/src/core/client.c @@ -590,7 +590,8 @@ bool handle_assert(struct server_ctx *sctx, struct client_ctx *ctx, const criter break; default: - log(assert_formatted, &asrt_stats, "@@@ "); + /* No value payload: a combinator group header; its + failing operands follow as results of their own. */ break; } } diff --git a/test/cram/asserts.t b/test/cram/asserts.t index f4354356..92cb1dff 100644 --- a/test/cram/asserts.t +++ b/test/cram/asserts.t @@ -22,9 +22,11 @@ Testing all assert messages $ failmessages.c.bin [----] failmessages.c:216: Assertion Failed + [----] all(eq(i32, 1, 1), eq(i32, 1, 1), eq(i32, 1, 0)): [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [----] failmessages.c:217: Assertion Failed + [----] any(eq(i32, 1, 0), eq(i32, 1, 0), eq(i32, 1, 0)): [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [----] eq(i32, 1, 0): @@ -32,10 +34,12 @@ Testing all assert messages [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [----] failmessages.c:218: Assertion Failed + [----] none(eq(i32, 1, 0), eq(i32, 1, 0), eq(i32, 1, 1)): [----] eq(i32, 1, 1): [----] @@@ [FAIL] message::compo [----] failmessages.c:168: Assertion Failed + [----] all(lt(i8, 1, 0), le(i8, 1, 0), gt(i8, 0, 1), ge(i8, 0, 1)): [----] lt(i8, 1, 0): [----] actual: 1 [----] reference: 0 @@ -49,6 +53,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:169: Assertion Failed + [----] all(lt(i16, 1, 0), le(i16, 1, 0), gt(i16, 0, 1), ge(i16, 0, 1)): [----] lt(i16, 1, 0): [----] actual: 1 [----] reference: 0 @@ -62,6 +67,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:170: Assertion Failed + [----] all(lt(i32, 1, 0), le(i32, 1, 0), gt(i32, 0, 1), ge(i32, 0, 1)): [----] lt(i32, 1, 0): [----] actual: 1 [----] reference: 0 @@ -75,6 +81,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:171: Assertion Failed + [----] all(lt(i64, 1, 0), le(i64, 1, 0), gt(i64, 0, 1), ge(i64, 0, 1)): [----] lt(i64, 1, 0): [----] actual: 1 [----] reference: 0 @@ -88,6 +95,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:172: Assertion Failed + [----] all(lt(u8, 1, 0), le(u8, 1, 0), gt(u8, 0, 1), ge(u8, 0, 1)): [----] lt(u8, 1, 0): [----] actual: 1 [----] reference: 0 @@ -101,6 +109,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:173: Assertion Failed + [----] all(lt(u16, 1, 0), le(u16, 1, 0), gt(u16, 0, 1), ge(u16, 0, 1)): [----] lt(u16, 1, 0): [----] actual: 1 [----] reference: 0 @@ -114,6 +123,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:174: Assertion Failed + [----] all(lt(u32, 1, 0), le(u32, 1, 0), gt(u32, 0, 1), ge(u32, 0, 1)): [----] lt(u32, 1, 0): [----] actual: 1 [----] reference: 0 @@ -127,6 +137,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:175: Assertion Failed + [----] all(lt(u64, 1, 0), le(u64, 1, 0), gt(u64, 0, 1), ge(u64, 0, 1)): [----] lt(u64, 1, 0): [----] actual: 1 [----] reference: 0 @@ -140,6 +151,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 1 [----] failmessages.c:176: Assertion Failed + [----] all(lt(iptr, 1, 0), le(iptr, 1, 0), gt(iptr, 0, 1), ge(iptr, 0, 1)): [----] lt(iptr, 1, 0): [----] actual: 0x1 [----] reference: 0x0 @@ -153,6 +165,7 @@ Testing all assert messages [----] actual: 0x0 [----] reference: 0x1 [----] failmessages.c:177: Assertion Failed + [----] all(lt(uptr, 1, 0), le(uptr, 1, 0), gt(uptr, 0, 1), ge(uptr, 0, 1)): [----] lt(uptr, 1, 0): [----] actual: 0x1 [----] reference: 0x0 @@ -166,6 +179,7 @@ Testing all assert messages [----] actual: 0x0 [----] reference: 0x1 [----] failmessages.c:178: Assertion Failed + [----] all(lt(flt, 1 / 3.f, 0), le(flt, 1 / 3.f, 0), gt(flt, 0, 1 / 3.f), ge(flt, 0, 1 / 3.f)): [----] lt(flt, 1 / 3.f, 0): [----] actual: 0.333333343 [----] reference: 0 @@ -179,6 +193,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 0.333333343 [----] failmessages.c:179: Assertion Failed + [----] all(lt(dbl, 1 / 3., 0), le(dbl, 1 / 3., 0), gt(dbl, 0, 1 / 3.), ge(dbl, 0, 1 / 3.)): [----] lt(dbl, 1 / 3., 0): [----] actual: 0.33333333333333331 [----] reference: 0 @@ -192,6 +207,7 @@ Testing all assert messages [----] actual: 0 [----] reference: 0.33333333333333331 [----] failmessages.c:180: Assertion Failed + [----] all(lt(ldbl, 1 / 3.l, 0), le(ldbl, 1 / 3.l, 0), gt(ldbl, 0, 1 / 3.l), ge(ldbl, 0, 1 / 3.l)): [----] lt(ldbl, 1 / 3.l, 0): \[----\] actual: 0\.3.* (re) [----] reference: 0 @@ -205,6 +221,7 @@ Testing all assert messages [----] actual: 0 \[----\] reference: 0\.3.* (re) [----] failmessages.c:183: Assertion Failed + [----] all(lt(ptr, (void *) 1, (void *) 0), le(ptr, (void *) 1, (void *) 0), gt(ptr, (void *) 0, (void *) 1), ge(ptr, (void *) 0, (void *) 1)): [----] lt(ptr, (void *) 1, (void *) 0): [----] actual: 0x1 [----] reference: 0x0 @@ -218,6 +235,7 @@ Testing all assert messages [----] actual: 0x0 [----] reference: 0x1 [----] failmessages.c:185: Assertion Failed + [----] all(lt(str, "cba", "abc"), le(str, "cba", "abc"), gt(str, "abc", "cba"), ge(str, "abc", "cba")): [----] lt(str, "cba", "abc"): [----] actual: "cba" [----] reference: "abc" @@ -231,6 +249,7 @@ Testing all assert messages [----] actual: "abc" [----] reference: "cba" [----] failmessages.c:186: Assertion Failed + [----] all(lt(str, "cba\ncba", "abc\nabc"), le(str, "cba\ncba", "abc\nabc"), gt(str, "abc\nabc", "cba\ncba"), ge(str, "abc\nabc", "cba\ncba")): [----] lt(str, "cba\ncba", "abc\nabc"): [----] actual: "cba\n" [----] "cba" @@ -252,6 +271,7 @@ Testing all assert messages [----] reference: "cba\n" [----] "cba" [----] failmessages.c:188: Assertion Failed + [----] all(lt(wcs, L"cba", L"abc"), le(wcs, L"cba", L"abc"), gt(wcs, L"abc", L"cba"), ge(wcs, L"abc", L"cba")): [----] lt(wcs, L"cba", L"abc"): [----] actual: L"cba" [----] reference: L"abc" @@ -265,6 +285,7 @@ Testing all assert messages [----] actual: L"abc" [----] reference: L"cba" [----] failmessages.c:189: Assertion Failed + [----] all(lt(wcs, L"cba\ncba", L"abc\nabc"), le(wcs, L"cba\ncba", L"abc\nabc"), gt(wcs, L"abc\nabc", L"cba\ncba"), ge(wcs, L"abc\nabc", L"cba\ncba")): [----] lt(wcs, L"cba\ncba", L"abc\nabc"): [----] actual: L"cba\n" [----] L"cba" @@ -286,6 +307,7 @@ Testing all assert messages [----] reference: L"cba\n" [----] L"cba" [----] failmessages.c:209: Assertion Failed + [----] all(lt(stream, shi, slo), le(stream, shi, slo), gt(stream, slo, shi), ge(stream, slo, shi)): [----] lt(stream, shi, slo): [----] actual: 00: 68656c6c 6f20776f 726c6400 hello world. [----] @@ -452,54 +474,71 @@ Testing all assert messages [----] [FAIL] messages::report_escape [----] failmessages.c:53: Assertion Failed + [----] not(zero(i8, 0)): [----] zero(i8, 0): [----] value: 0 [----] failmessages.c:54: Assertion Failed + [----] not(zero(i16, 0)): [----] zero(i16, 0): [----] value: 0 [----] failmessages.c:55: Assertion Failed + [----] not(zero(i32, 0)): [----] zero(i32, 0): [----] value: 0 [----] failmessages.c:56: Assertion Failed + [----] not(zero(i64, 0)): [----] zero(i64, 0): [----] value: 0 [----] failmessages.c:57: Assertion Failed + [----] not(zero(u8, 0)): [----] zero(u8, 0): [----] value: 0 [----] failmessages.c:58: Assertion Failed + [----] not(zero(u16, 0)): [----] zero(u16, 0): [----] value: 0 [----] failmessages.c:59: Assertion Failed + [----] not(zero(u32, 0)): [----] zero(u32, 0): [----] value: 0 [----] failmessages.c:60: Assertion Failed + [----] not(zero(u64, 0)): [----] zero(u64, 0): [----] value: 0 [----] failmessages.c:61: Assertion Failed + [----] not(zero(iptr, 0)): [----] zero(iptr, 0): [----] value: 0x0 [----] failmessages.c:62: Assertion Failed + [----] not(zero(uptr, 0)): [----] zero(uptr, 0): [----] value: 0x0 [----] failmessages.c:63: Assertion Failed + [----] not(zero(flt, 0)): [----] zero(flt, 0): [----] value: 0 [----] failmessages.c:64: Assertion Failed + [----] not(zero(dbl, 0)): [----] zero(dbl, 0): [----] value: 0 [----] failmessages.c:65: Assertion Failed + [----] not(zero(ldbl, 0)): [----] zero(ldbl, 0): [----] value: 0 [----] failmessages.c:68: Assertion Failed + [----] not(zero(ptr, 0)): [----] zero(ptr, 0): [----] value: 0x0 [----] failmessages.c:69: Assertion Failed + [----] not(zero(str, "")): [----] zero(str, ""): [----] value: "" [----] failmessages.c:70: Assertion Failed + [----] not(zero(wcs, L"")): [----] zero(wcs, L""): [----] value: L"" [----] failmessages.c:75: Assertion Failed + [----] not(zero(type(struct dummy_struct), dummy1)): [----] zero(type(struct dummy_struct), dummy1): [----] value: (struct dummy_struct) { [----] \t.a = 0, (esc) @@ -546,9 +585,11 @@ C++ equivalents $ failmessages.cc.bin [----] failmessages.cc:216: Assertion Failed + [----] all(eq(i32, 1, 1), eq(i32, 1, 1), eq(i32, 1, 0)): [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [----] failmessages.cc:217: Assertion Failed + [----] any(eq(i32, 1, 0), eq(i32, 1, 0), eq(i32, 1, 0)): [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [----] eq(i32, 1, 0): @@ -556,6 +597,7 @@ C++ equivalents [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [----] failmessages.cc:218: Assertion Failed + [----] none(eq(i32, 1, 0), eq(i32, 1, 0), eq(i32, 1, 1)): [----] eq(i32, 1, 1): [----] @@@ [FAIL] message::compo @@ -563,12 +605,14 @@ C++ equivalents [----] throw(std::bad_alloc, throw std::invalid_argument("exception message")): [----] message: "exception message" [----] failmessages.cc:223: Assertion Failed + [----] not(throw(std::invalid_argument, throw std::invalid_argument("exception message"))): [----] failmessages.cc:224: Assertion Failed [----] nothrow(throw std::invalid_argument("exception message")): [----] message: "exception message" [----] failmessages.cc:225: Assertion Failed [FAIL] message::exception [----] failmessages.cc:174: Assertion Failed + [----] all(lt(i8, 1, 0), le(i8, 1, 0), gt(i8, 0, 1), ge(i8, 0, 1)): [----] lt(i8, 1, 0): [----] actual: 1 [----] reference: 0 @@ -582,6 +626,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:175: Assertion Failed + [----] all(lt(i16, 1, 0), le(i16, 1, 0), gt(i16, 0, 1), ge(i16, 0, 1)): [----] lt(i16, 1, 0): [----] actual: 1 [----] reference: 0 @@ -595,6 +640,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:176: Assertion Failed + [----] all(lt(i32, 1, 0), le(i32, 1, 0), gt(i32, 0, 1), ge(i32, 0, 1)): [----] lt(i32, 1, 0): [----] actual: 1 [----] reference: 0 @@ -608,6 +654,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:177: Assertion Failed + [----] all(lt(i64, 1, 0), le(i64, 1, 0), gt(i64, 0, 1), ge(i64, 0, 1)): [----] lt(i64, 1, 0): [----] actual: 1 [----] reference: 0 @@ -621,6 +668,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:178: Assertion Failed + [----] all(lt(u8, 1, 0), le(u8, 1, 0), gt(u8, 0, 1), ge(u8, 0, 1)): [----] lt(u8, 1, 0): [----] actual: 1 [----] reference: 0 @@ -634,6 +682,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:179: Assertion Failed + [----] all(lt(u16, 1, 0), le(u16, 1, 0), gt(u16, 0, 1), ge(u16, 0, 1)): [----] lt(u16, 1, 0): [----] actual: 1 [----] reference: 0 @@ -647,6 +696,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:180: Assertion Failed + [----] all(lt(u32, 1, 0), le(u32, 1, 0), gt(u32, 0, 1), ge(u32, 0, 1)): [----] lt(u32, 1, 0): [----] actual: 1 [----] reference: 0 @@ -660,6 +710,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:181: Assertion Failed + [----] all(lt(u64, 1, 0), le(u64, 1, 0), gt(u64, 0, 1), ge(u64, 0, 1)): [----] lt(u64, 1, 0): [----] actual: 1 [----] reference: 0 @@ -673,6 +724,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:182: Assertion Failed + [----] all(lt(iptr, 1, 0), le(iptr, 1, 0), gt(iptr, 0, 1), ge(iptr, 0, 1)): [----] lt(iptr, 1, 0): [----] actual: 1 [----] reference: 0 @@ -686,6 +738,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:183: Assertion Failed + [----] all(lt(uptr, 1, 0), le(uptr, 1, 0), gt(uptr, 0, 1), ge(uptr, 0, 1)): [----] lt(uptr, 1, 0): [----] actual: 1 [----] reference: 0 @@ -699,6 +752,7 @@ C++ equivalents [----] actual: 0 [----] reference: 1 [----] failmessages.cc:184: Assertion Failed + [----] all(lt(flt, 1 / 3.f, 0), le(flt, 1 / 3.f, 0), gt(flt, 0, 1 / 3.f), ge(flt, 0, 1 / 3.f)): [----] lt(flt, 1 / 3.f, 0): [----] actual: 0.333333 [----] reference: 0 @@ -712,6 +766,7 @@ C++ equivalents [----] actual: 0 [----] reference: 0.333333 [----] failmessages.cc:185: Assertion Failed + [----] all(lt(dbl, 1 / 3., 0), le(dbl, 1 / 3., 0), gt(dbl, 0, 1 / 3.), ge(dbl, 0, 1 / 3.)): [----] lt(dbl, 1 / 3., 0): [----] actual: 0.333333 [----] reference: 0 @@ -725,6 +780,7 @@ C++ equivalents [----] actual: 0 [----] reference: 0.333333 [----] failmessages.cc:186: Assertion Failed + [----] all(lt(ldbl, 1 / 3.l, 0), le(ldbl, 1 / 3.l, 0), gt(ldbl, 0, 1 / 3.l), ge(ldbl, 0, 1 / 3.l)): [----] lt(ldbl, 1 / 3.l, 0): [----] actual: 0.333333 [----] reference: 0 @@ -738,6 +794,7 @@ C++ equivalents [----] actual: 0 [----] reference: 0.333333 [----] failmessages.cc:189: Assertion Failed + [----] all(lt(ptr, (void *) 1, (void *) 0), le(ptr, (void *) 1, (void *) 0), gt(ptr, (void *) 0, (void *) 1), ge(ptr, (void *) 0, (void *) 1)): [----] lt(ptr, (void *) 1, (void *) 0): [----] actual: @1 [----] reference: nullptr @@ -751,6 +808,7 @@ C++ equivalents [----] actual: nullptr [----] reference: @1 [----] failmessages.cc:191: Assertion Failed + [----] all(lt(str, "cba", "abc"), le(str, "cba", "abc"), gt(str, "abc", "cba"), ge(str, "abc", "cba")): [----] lt(str, "cba", "abc"): [----] actual: "cba" [----] reference: "abc" @@ -764,6 +822,7 @@ C++ equivalents [----] actual: "abc" [----] reference: "cba" [----] failmessages.cc:192: Assertion Failed + [----] all(lt(str, "cba\ncba", "abc\nabc"), le(str, "cba\ncba", "abc\nabc"), gt(str, "abc\nabc", "cba\ncba"), ge(str, "abc\nabc", "cba\ncba")): [----] lt(str, "cba\ncba", "abc\nabc"): [----] actual: "cba\n" [----] "cba" @@ -785,6 +844,7 @@ C++ equivalents [----] reference: "cba\n" [----] "cba" [----] failmessages.cc:194: Assertion Failed + [----] all(lt(wcs, L"cba", L"abc"), le(wcs, L"cba", L"abc"), gt(wcs, L"abc", L"cba"), ge(wcs, L"abc", L"cba")): [----] lt(wcs, L"cba", L"abc"): [----] actual: L"cba" [----] reference: L"abc" @@ -798,6 +858,7 @@ C++ equivalents [----] actual: L"abc" [----] reference: L"cba" [----] failmessages.cc:195: Assertion Failed + [----] all(lt(wcs, L"cba\ncba", L"abc\nabc"), le(wcs, L"cba\ncba", L"abc\nabc"), gt(wcs, L"abc\nabc", L"cba\ncba"), ge(wcs, L"abc\nabc", L"cba\ncba")): [----] lt(wcs, L"cba\ncba", L"abc\nabc"): [----] actual: L"cba\n" [----] L"cba" @@ -819,6 +880,7 @@ C++ equivalents [----] reference: L"cba\n" [----] L"cba" [----] failmessages.cc:212: Assertion Failed + [----] all(lt(stream, shi, slo), le(stream, shi, slo), gt(stream, slo, shi), ge(stream, slo, shi)): [----] lt(stream, shi, slo): [----] actual: 00: 68656c6c 6f20776f 726c6400 hello world. [----] @@ -1002,63 +1064,82 @@ C++ equivalents [----] [FAIL] messages::report_escape [----] failmessages.cc:50: Assertion Failed + [----] not(zero(i8, 0)): [----] zero(i8, 0): [----] value: 0 [----] failmessages.cc:51: Assertion Failed + [----] not(zero(i16, 0)): [----] zero(i16, 0): [----] value: 0 [----] failmessages.cc:52: Assertion Failed + [----] not(zero(i32, 0)): [----] zero(i32, 0): [----] value: 0 [----] failmessages.cc:53: Assertion Failed + [----] not(zero(i64, 0)): [----] zero(i64, 0): [----] value: 0 [----] failmessages.cc:54: Assertion Failed + [----] not(zero(u8, 0)): [----] zero(u8, 0): [----] value: 0 [----] failmessages.cc:55: Assertion Failed + [----] not(zero(u16, 0)): [----] zero(u16, 0): [----] value: 0 [----] failmessages.cc:56: Assertion Failed + [----] not(zero(u32, 0)): [----] zero(u32, 0): [----] value: 0 [----] failmessages.cc:57: Assertion Failed + [----] not(zero(u64, 0)): [----] zero(u64, 0): [----] value: 0 [----] failmessages.cc:58: Assertion Failed + [----] not(zero(iptr, 0)): [----] zero(iptr, 0): [----] value: 0 [----] failmessages.cc:59: Assertion Failed + [----] not(zero(uptr, 0)): [----] zero(uptr, 0): [----] value: 0 [----] failmessages.cc:60: Assertion Failed + [----] not(zero(flt, 0)): [----] zero(flt, 0): [----] value: 0 [----] failmessages.cc:61: Assertion Failed + [----] not(zero(dbl, 0)): [----] zero(dbl, 0): [----] value: 0 [----] failmessages.cc:62: Assertion Failed + [----] not(zero(ldbl, 0)): [----] zero(ldbl, 0): [----] value: 0 [----] failmessages.cc:65: Assertion Failed + [----] not(zero(ptr, 0)): [----] zero(ptr, 0): [----] value: nullptr [----] failmessages.cc:66: Assertion Failed + [----] not(zero(str, "")): [----] zero(str, ""): [----] value: "" [----] failmessages.cc:67: Assertion Failed + [----] not(zero(wcs, L"")): [----] zero(wcs, L""): [----] value: L"" [----] failmessages.cc:69: Assertion Failed + [----] not(zero(type(dummy_struct), dummy_struct{})): [----] zero(type(dummy_struct), dummy_struct{}): [----] value: (struct dummy_struct) { [----] \t.a = 0, (esc) [----] \t.b = 0 (esc) [----] } [----] failmessages.cc:73: Assertion Failed + [----] not(zero(type(int_vect), int_vect{})): [----] zero(type(int_vect), int_vect{}): [----] value: {} [----] failmessages.cc:76: Assertion Failed + [----] not(zero(type(string_int_map), string_int_map{})): [----] zero(type(string_int_map), string_int_map{}): [----] value: {} [FAIL] messages::zero diff --git a/test/cram/bugs.t b/test/cram/bugs.t index ee73406e..b4bbcbca 100644 --- a/test/cram/bugs.t +++ b/test/cram/bugs.t @@ -15,12 +15,16 @@ Nested assertion combinators must resolve and compose correctly $ combinators.c.bin [----] combinators.c:26: Assertion Failed + [----] any(eq(i32, 1, 0), all(eq(i32, 1, 0), eq(i32, 1, 1))): [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} + [----] all(eq(i32, 1, 0), eq(i32, 1, 1)): [----] eq(i32, 1, 0): [----] diff: [-1-]{+0+} [FAIL] combinators::nested_any_fails [----] combinators.c:22: Assertion Failed + [----] not(all(eq(i32, 1, 1), eq(i32, 2, 2))): + [----] all(eq(i32, 1, 1), eq(i32, 2, 2)): [----] eq(i32, 1, 1): [----] @@@ [----] eq(i32, 2, 2): From ac0583bcdc0be2ec72fd00d895a0cccd7b9b0694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= Date: Sat, 18 Jul 2026 22:31:14 +0200 Subject: [PATCH 10/10] new-asserts: fix reporting negated operands Operands under none() or not() are reported because they matched. The runner still tried to diff their equal values, found nothing, and blamed the user's stringifier (#594): cr_expect(none(eq(int, 1, 1), eq(int, 3, 3), eq(int, 3, 3))); [----] eq(int, 1, 1): [----] @@@ Matched operands are now printed as-is, with a clear message: [----] none(eq(int, 1, 1), eq(int, 3, 3), eq(int, 3, 3)): [----] eq(int, 1, 1): passed, but was expected to fail [----] actual: 1 [----] expected: 1 --- src/core/assert.c | 10 ++++-- src/core/client.c | 10 ++++-- test/cram/asserts.t | 83 ++++++++++++++++++++++--------------------- test/cram/bugs.t | 60 ++++++++++++++++++++++++++++--- test/full/bug594.c | 35 ++++++++++++++++++ test/full/meson.build | 1 + 6 files changed, 150 insertions(+), 49 deletions(-) create mode 100644 test/full/bug594.c diff --git a/src/core/assert.c b/src/core/assert.c index ee2d907d..bc50cf72 100644 --- a/src/core/assert.c +++ b/src/core/assert.c @@ -115,10 +115,14 @@ static criterion_protocol_result *collect_leaves( if (nbparams > 0) { res->repr = (char *) tree->repr; res->message = (char *) tree->message; + res->has_negated = true; + res->negated = tree->negated; const size_t display_threshold = 40; - if (nbparams == 2) { + /* Negated operands are recorded because they matched; diffing + them would come up empty, so always send them verbatim. */ + if (nbparams == 2 && !tree->negated) { if (strcmp(tree->params[0].name, "actual") || strcmp(tree->params[1].name, "expected")) { goto process_params; @@ -182,10 +186,12 @@ static criterion_protocol_result *collect_leaves( /* Combinator group nodes announce themselves in a value-less header result before their failing operands are reported. The root node has no repr and stays silent. */ - if (tree->repr && tree->nchild > 0) { + if (tree->repr && (tree->nchild > 0 || tree->negated)) { *res = (criterion_protocol_result) { .repr = (char *) tree->repr, .message = (char *) tree->message, + .has_negated = true, + .negated = tree->negated, }; ++res; } diff --git a/src/core/client.c b/src/core/client.c index 92a82590..25297d9a 100644 --- a/src/core/client.c +++ b/src/core/client.c @@ -546,7 +546,13 @@ bool handle_assert(struct server_ctx *sctx, struct client_ctx *ctx, const criter for (size_t i = 0; i < asrt->results_count; ++i) { criterion_protocol_result *res = &asrt->results[i]; - log(assert_sub, &asrt_stats, res->repr, res->message); + bool negated = res->has_negated && res->negated; + + const char *msg = res->message; + if (negated && (!msg || !msg[0])) + msg = "passed, but was expected to fail"; + + log(assert_sub, &asrt_stats, res->repr, msg); switch (res->which_value) { case criterion_protocol_result_params_tag: @@ -556,7 +562,7 @@ bool handle_assert(struct server_ctx *sctx, struct client_ctx *ctx, const criter } size_t j = 0; - if (res->value.params->list_count >= 2) { + if (!negated && res->value.params->list_count >= 2) { criterion_protocol_param_entry *actual = &res->value.params->list[0]; criterion_protocol_param_entry *expected = &res->value.params->list[1]; if (!strcmp(actual->name, "actual") && !strcmp(expected->name, "expected")) { diff --git a/test/cram/asserts.t b/test/cram/asserts.t index 92cb1dff..21a99983 100644 --- a/test/cram/asserts.t +++ b/test/cram/asserts.t @@ -35,8 +35,9 @@ Testing all assert messages [----] diff: [-1-]{+0+} [----] failmessages.c:218: Assertion Failed [----] none(eq(i32, 1, 0), eq(i32, 1, 0), eq(i32, 1, 1)): - [----] eq(i32, 1, 1): - [----] @@@ + [----] eq(i32, 1, 1): passed, but was expected to fail + [----] actual: 1 + [----] expected: 1 [FAIL] message::compo [----] failmessages.c:168: Assertion Failed [----] all(lt(i8, 1, 0), le(i8, 1, 0), gt(i8, 0, 1), ge(i8, 0, 1)): @@ -475,71 +476,71 @@ Testing all assert messages [FAIL] messages::report_escape [----] failmessages.c:53: Assertion Failed [----] not(zero(i8, 0)): - [----] zero(i8, 0): + [----] zero(i8, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:54: Assertion Failed [----] not(zero(i16, 0)): - [----] zero(i16, 0): + [----] zero(i16, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:55: Assertion Failed [----] not(zero(i32, 0)): - [----] zero(i32, 0): + [----] zero(i32, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:56: Assertion Failed [----] not(zero(i64, 0)): - [----] zero(i64, 0): + [----] zero(i64, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:57: Assertion Failed [----] not(zero(u8, 0)): - [----] zero(u8, 0): + [----] zero(u8, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:58: Assertion Failed [----] not(zero(u16, 0)): - [----] zero(u16, 0): + [----] zero(u16, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:59: Assertion Failed [----] not(zero(u32, 0)): - [----] zero(u32, 0): + [----] zero(u32, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:60: Assertion Failed [----] not(zero(u64, 0)): - [----] zero(u64, 0): + [----] zero(u64, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:61: Assertion Failed [----] not(zero(iptr, 0)): - [----] zero(iptr, 0): + [----] zero(iptr, 0): passed, but was expected to fail [----] value: 0x0 [----] failmessages.c:62: Assertion Failed [----] not(zero(uptr, 0)): - [----] zero(uptr, 0): + [----] zero(uptr, 0): passed, but was expected to fail [----] value: 0x0 [----] failmessages.c:63: Assertion Failed [----] not(zero(flt, 0)): - [----] zero(flt, 0): + [----] zero(flt, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:64: Assertion Failed [----] not(zero(dbl, 0)): - [----] zero(dbl, 0): + [----] zero(dbl, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:65: Assertion Failed [----] not(zero(ldbl, 0)): - [----] zero(ldbl, 0): + [----] zero(ldbl, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.c:68: Assertion Failed [----] not(zero(ptr, 0)): - [----] zero(ptr, 0): + [----] zero(ptr, 0): passed, but was expected to fail [----] value: 0x0 [----] failmessages.c:69: Assertion Failed [----] not(zero(str, "")): - [----] zero(str, ""): + [----] zero(str, ""): passed, but was expected to fail [----] value: "" [----] failmessages.c:70: Assertion Failed [----] not(zero(wcs, L"")): - [----] zero(wcs, L""): + [----] zero(wcs, L""): passed, but was expected to fail [----] value: L"" [----] failmessages.c:75: Assertion Failed [----] not(zero(type(struct dummy_struct), dummy1)): - [----] zero(type(struct dummy_struct), dummy1): + [----] zero(type(struct dummy_struct), dummy1): passed, but was expected to fail [----] value: (struct dummy_struct) { [----] \t.a = 0, (esc) [----] \t.b = 0 (esc) @@ -598,14 +599,16 @@ C++ equivalents [----] diff: [-1-]{+0+} [----] failmessages.cc:218: Assertion Failed [----] none(eq(i32, 1, 0), eq(i32, 1, 0), eq(i32, 1, 1)): - [----] eq(i32, 1, 1): - [----] @@@ + [----] eq(i32, 1, 1): passed, but was expected to fail + [----] actual: 1 + [----] expected: 1 [FAIL] message::compo [----] failmessages.cc:222: Assertion Failed [----] throw(std::bad_alloc, throw std::invalid_argument("exception message")): [----] message: "exception message" [----] failmessages.cc:223: Assertion Failed [----] not(throw(std::invalid_argument, throw std::invalid_argument("exception message"))): + [----] throw(std::invalid_argument, throw std::invalid_argument("exception message")): passed, but was expected to fail [----] failmessages.cc:224: Assertion Failed [----] nothrow(throw std::invalid_argument("exception message")): [----] message: "exception message" @@ -1065,82 +1068,82 @@ C++ equivalents [FAIL] messages::report_escape [----] failmessages.cc:50: Assertion Failed [----] not(zero(i8, 0)): - [----] zero(i8, 0): + [----] zero(i8, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:51: Assertion Failed [----] not(zero(i16, 0)): - [----] zero(i16, 0): + [----] zero(i16, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:52: Assertion Failed [----] not(zero(i32, 0)): - [----] zero(i32, 0): + [----] zero(i32, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:53: Assertion Failed [----] not(zero(i64, 0)): - [----] zero(i64, 0): + [----] zero(i64, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:54: Assertion Failed [----] not(zero(u8, 0)): - [----] zero(u8, 0): + [----] zero(u8, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:55: Assertion Failed [----] not(zero(u16, 0)): - [----] zero(u16, 0): + [----] zero(u16, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:56: Assertion Failed [----] not(zero(u32, 0)): - [----] zero(u32, 0): + [----] zero(u32, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:57: Assertion Failed [----] not(zero(u64, 0)): - [----] zero(u64, 0): + [----] zero(u64, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:58: Assertion Failed [----] not(zero(iptr, 0)): - [----] zero(iptr, 0): + [----] zero(iptr, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:59: Assertion Failed [----] not(zero(uptr, 0)): - [----] zero(uptr, 0): + [----] zero(uptr, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:60: Assertion Failed [----] not(zero(flt, 0)): - [----] zero(flt, 0): + [----] zero(flt, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:61: Assertion Failed [----] not(zero(dbl, 0)): - [----] zero(dbl, 0): + [----] zero(dbl, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:62: Assertion Failed [----] not(zero(ldbl, 0)): - [----] zero(ldbl, 0): + [----] zero(ldbl, 0): passed, but was expected to fail [----] value: 0 [----] failmessages.cc:65: Assertion Failed [----] not(zero(ptr, 0)): - [----] zero(ptr, 0): + [----] zero(ptr, 0): passed, but was expected to fail [----] value: nullptr [----] failmessages.cc:66: Assertion Failed [----] not(zero(str, "")): - [----] zero(str, ""): + [----] zero(str, ""): passed, but was expected to fail [----] value: "" [----] failmessages.cc:67: Assertion Failed [----] not(zero(wcs, L"")): - [----] zero(wcs, L""): + [----] zero(wcs, L""): passed, but was expected to fail [----] value: L"" [----] failmessages.cc:69: Assertion Failed [----] not(zero(type(dummy_struct), dummy_struct{})): - [----] zero(type(dummy_struct), dummy_struct{}): + [----] zero(type(dummy_struct), dummy_struct{}): passed, but was expected to fail [----] value: (struct dummy_struct) { [----] \t.a = 0, (esc) [----] \t.b = 0 (esc) [----] } [----] failmessages.cc:73: Assertion Failed [----] not(zero(type(int_vect), int_vect{})): - [----] zero(type(int_vect), int_vect{}): + [----] zero(type(int_vect), int_vect{}): passed, but was expected to fail [----] value: {} [----] failmessages.cc:76: Assertion Failed [----] not(zero(type(string_int_map), string_int_map{})): - [----] zero(type(string_int_map), string_int_map{}): + [----] zero(type(string_int_map), string_int_map{}): passed, but was expected to fail [----] value: {} [FAIL] messages::zero [====] Synthesis: Tested: 7 | Passing: 0 | Failing: 7 | Crashing: 0 diff --git a/test/cram/bugs.t b/test/cram/bugs.t index b4bbcbca..824950e2 100644 --- a/test/cram/bugs.t +++ b/test/cram/bugs.t @@ -24,10 +24,60 @@ Nested assertion combinators must resolve and compose correctly [FAIL] combinators::nested_any_fails [----] combinators.c:22: Assertion Failed [----] not(all(eq(i32, 1, 1), eq(i32, 2, 2))): - [----] all(eq(i32, 1, 1), eq(i32, 2, 2)): - [----] eq(i32, 1, 1): - [----] @@@ - [----] eq(i32, 2, 2): - [----] @@@ + [----] all(eq(i32, 1, 1), eq(i32, 2, 2)): passed, but was expected to fail + [----] eq(i32, 1, 1): passed, but was expected to fail + [----] actual: 1 + [----] expected: 1 + [----] eq(i32, 2, 2): passed, but was expected to fail + [----] actual: 2 + [----] expected: 2 [FAIL] combinators::not_all_fails [====] Synthesis: Tested: 5 | Passing: 3 | Failing: 2 | Crashing: 0 + +https://github.com/Snaipe/Criterion/issues/463 + + $ bug594.c.bin + [----] bug594.c:19: Assertion Failed + [----] all(eq(int, 1, 1), eq(int, 1, 2)): + [----] eq(int, 1, 2): + [----] diff: [-1-]{+2+} + [FAIL] bug594::all + [----] bug594.c:23: Assertion Failed + [----] any(eq(int, 1, 2), eq(int, 3, 4)): + [----] eq(int, 1, 2): + [----] diff: [-1-]{+2+} + [----] eq(int, 3, 4): + [----] diff: [-3-]{+4+} + [FAIL] bug594::any + [----] bug594.c:27: Assertion Failed + [----] not(all(eq(int, 1, 1))): + [----] all(eq(int, 1, 1)): passed, but was expected to fail + [----] eq(int, 1, 1): passed, but was expected to fail + [----] actual: 1 + [----] expected: 1 + [FAIL] bug594::negated_combinator + [----] bug594.c:5: Assertion Failed + [----] none(eq(int, 1, 1), eq(int, 3, 3), eq(int, 3, 3)): + [----] eq(int, 1, 1): passed, but was expected to fail + [----] actual: 1 + [----] expected: 1 + [----] eq(int, 3, 3): passed, but was expected to fail + [----] actual: 3 + [----] expected: 3 + [----] eq(int, 3, 3): passed, but was expected to fail + [----] actual: 3 + [----] expected: 3 + [FAIL] bug594::none + [----] bug594.c:9: Assertion Failed + [----] not(eq(int, 1, 1)): + [----] eq(int, 1, 1): passed, but was expected to fail + [----] actual: 1 + [----] expected: 1 + [FAIL] bug594::not + [----] bug594.c:15: Assertion Failed + [----] not(eq(str, s, s)): + [----] eq(str, s, s): passed, but was expected to fail + [----] actual: "a string well over the forty character display threshold" + [----] expected: "a string well over the forty character display threshold" + [FAIL] bug594::not_long + [====] Synthesis: Tested: 7 | Passing: 1 | Failing: 6 | Crashing: 0 diff --git a/test/full/bug594.c b/test/full/bug594.c new file mode 100644 index 00000000..2d24be18 --- /dev/null +++ b/test/full/bug594.c @@ -0,0 +1,35 @@ +#include +#include + +Test(bug594, none) { + cr_expect(none(eq(int, 1, 1), eq(int, 3, 3), eq(int, 3, 3))); +} + +Test(bug594, not) { + cr_expect(not(eq(int, 1, 1))); +} + +Test(bug594, not_long) { + char s[] = "a string well over the forty character display threshold"; + + cr_expect(not(eq(str, s, s))); +} + +Test(bug594, all) { + cr_expect(all(eq(int, 1, 1), eq(int, 1, 2))); +} + +Test(bug594, any) { + cr_expect(any(eq(int, 1, 2), eq(int, 3, 4))); +} + +Test(bug594, negated_combinator) { + cr_expect(not(all(eq(int, 1, 1)))); +} + +Test(bug594, passing) { + cr_expect(none(eq(int, 1, 2), eq(int, 3, 4))); + cr_expect(not(eq(int, 1, 2))); + cr_expect(all(eq(int, 1, 1), not(eq(int, 1, 2)))); + cr_expect(any(eq(int, 1, 2), not(not(eq(int, 3, 3))))); +} diff --git a/test/full/meson.build b/test/full/meson.build index 33ce160a..43bed466 100644 --- a/test/full/meson.build +++ b/test/full/meson.build @@ -9,6 +9,7 @@ full_tests = [ # bug-specific programs 'bug463.c', + 'bug594.c', ] if get_option('theories').enabled()