Skip to content
12 changes: 9 additions & 3 deletions include/criterion/internal/assert/exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(...) ,
Expand All @@ -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(...) ,
Expand All @@ -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_ */
10 changes: 7 additions & 3 deletions include/criterion/internal/assert/op.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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); \
Expand All @@ -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; \
Expand Down
4 changes: 2 additions & 2 deletions include/criterion/internal/assert/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand All @@ -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, ...);
Expand Down
60 changes: 33 additions & 27 deletions include/criterion/internal/new_asserts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; \
Expand All @@ -98,77 +96,85 @@
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)

/* 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_ALL_INDIRECT(Cond, E); \
cri_cond_un = CRI_OBSTRUCT_N(CRI_SPECIFIER_INDIRECT)()(E); \
#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) ")"; \
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; \
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; \
} 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); } \
Cond = Cond && cri_cond_un

#define CRI_ASSERT_TEST_SPECIFIER_all(...) ,
#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; \
int cri_cond_def = 1, cri_cond_un; \
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_prevnode = cri_node; \
*cri_pass = cri_cond; \
} 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); } \
cri_cond_expect = !cri_cond_expect; \
Cond = Cond && !(cri_cond_un)

#define CRI_ASSERT_TEST_SPECIFIER_none(...) ,
#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; \
int cri_cond_def = 1, cri_cond_un; \
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_prevnode = cri_node; \
*cri_pass = cri_cond; \
} 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(...) ,
#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; \
int cri_cond_def = 0; \
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_prevnode = cri_node; \
*cri_pass = cri_cond; \
} while (0); cri_pass = cri_pass_orig

#undef cr_assert_user
Expand Down
44 changes: 32 additions & 12 deletions src/core/assert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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]);
Expand Down Expand Up @@ -117,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;
Expand Down Expand Up @@ -158,8 +160,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 {
Expand All @@ -176,9 +183,22 @@ 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 || tree->negated)) {
*res = (criterion_protocol_result) {
.repr = (char *) tree->repr,
.message = (char *) tree->message,
.has_negated = true,
.negated = tree->negated,
};
++res;
}

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);
}
Expand Down
13 changes: 10 additions & 3 deletions src/core/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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")) {
Expand Down Expand Up @@ -590,7 +596,8 @@ bool handle_assert(struct server_ctx *sctx, struct client_ctx *ctx, const criter
break;

default:
log(assert_formatted, &asrt_stats, "@@@ <no message or difference -- this is a user bug in the object stringifier>");
/* No value payload: a combinator group header; its
failing operands follow as results of their own. */
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/theories.c
Original file line number Diff line number Diff line change
Expand Up @@ -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] == ' '))
Expand Down
Loading
Loading