Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion 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 Down
51 changes: 30 additions & 21 deletions include/criterion/internal/new_asserts.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,69 +105,78 @@
/* 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) ")"; \
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); } \
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__) ")"; \
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_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; \
#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(...) ,
#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__) ")"; \
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_pass = cri_cond; \
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(...) ,
#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__) ")"; \
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_pass = cri_cond; \
cri_prevnode = cri_node; \
} while (0); cri_pass = cri_pass_orig

Expand Down
24 changes: 23 additions & 1 deletion src/core/assert.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ 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;
tree->negated = !tree->negated;
}

CR_API void cri_assert_node_term(struct cri_assert_node *tree)
Expand Down Expand Up @@ -117,10 +118,16 @@ 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;
res->has_combinator = false;
res->combinator = false;

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 @@ -176,6 +183,21 @@ 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,
.has_combinator = true,
.combinator = true,
};
++res;
}

for (size_t i = 0; i < tree->nchild; ++i) {
struct cri_assert_node *node = &tree->children[i];
if (node->pass)
Expand Down
20 changes: 18 additions & 2 deletions src/core/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,18 @@ 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);

/* Combinator results are value-less headers; their failing
operands follow as results of their own. */
if (res->has_combinator && res->combinator)
continue;

switch (res->which_value) {
case criterion_protocol_result_params_tag:
Expand All @@ -556,7 +567,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,6 +601,11 @@ bool handle_assert(struct server_ctx *sctx, struct client_ctx *ctx, const criter
break;

default:
/* Negated results always carry their operands; only
non-negated ones can lose their value to an empty
diff, which points at a broken user stringifier. */
if (negated)
break;
log(assert_formatted, &asrt_stats, "@@@ <no message or difference -- this is a user bug in the object stringifier>");
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
14 changes: 11 additions & 3 deletions src/protocol/criterion.pb.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ typedef struct _criterion_protocol_result {
struct _criterion_protocol_param_list *params;
char *formatted;
} value;
bool has_negated;
bool negated;
bool has_combinator;
bool combinator;
} criterion_protocol_result;

typedef struct _criterion_protocol_ack {
Expand Down Expand Up @@ -168,7 +172,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, 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}
Expand All @@ -180,7 +184,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, 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}
Expand All @@ -196,6 +200,8 @@ 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_result_combinator_tag 6
#define criterion_protocol_ack_status_code_tag 1
#define criterion_protocol_ack_message_tag 2
#define criterion_protocol_assert_message_tag 1
Expand Down Expand Up @@ -276,7 +282,9 @@ 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) \
X(a, STATIC, OPTIONAL, BOOL, combinator, 6)
#define criterion_protocol_result_CALLBACK NULL
#define criterion_protocol_result_DEFAULT NULL
#define criterion_protocol_result_value_params_MSGTYPE criterion_protocol_param_list
Expand Down
4 changes: 4 additions & 0 deletions src/protocol/criterion.proto
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ message result {
param_list params = 3;
string formatted = 4;
}

optional bool negated = 5;

optional bool combinator = 6;
}

message assert {
Expand Down
Loading
Loading