Skip to content

Commit

Permalink
Refactor syntax (#1)
Browse files Browse the repository at this point in the history
Convert syntax from EXPECT_EQUAL_INT(a, b) to
EXPECT_INT(a, ==, b). This is much cleaner.

Also, implements support for other equalities,
and makes defining custom assertions much easier.
  • Loading branch information
Timothy-Gonzalez authored Feb 4, 2024
1 parent 06ab933 commit 6430355
Show file tree
Hide file tree
Showing 13 changed files with 330 additions and 159 deletions.
75 changes: 9 additions & 66 deletions src/assertions.c
Original file line number Diff line number Diff line change
@@ -1,27 +1,15 @@
#include <stddef.h>
#define _GNU_SOURCE
#include <stdio.h>
extern int asprintf(char **strp, const char *fmt, ...);

#include "assertions.h"
#include "output.h"

void caught_internal_fancy_str(const char *str)
{
if (str == NULL)
{
printf("NULL");
}
else
{
printf("%s", str);
}
}

void caught_internal_handle_assertion(caught_internal_assertion *assertion)
// Processes the assertion result by updating the internal state & outputting result of assertion if needed.
// Finally, returns true if assertion failed and test should exit
bool caught_internal_handle_assertion_result(caught_internal_assertion_result assertion_result)
{
caught_internal.assertions += 1;
if (assertion->pass)
if (assertion_result.pass)
{
caught_internal.passed_assertions += 1;
}
Expand All @@ -32,58 +20,13 @@ void caught_internal_handle_assertion(caught_internal_assertion *assertion)
show_regardless_of_pass = 1;
#endif

if (!assertion->pass || show_regardless_of_pass)
if (!assertion_result.pass || show_regardless_of_pass)
{
caught_output_assertion(assertion);
caught_output_assertion_result(assertion_result);
}

free(assertion->expected);
free(assertion->got);
}

#define CAUGHT_INTERNAL_READ_EXPECTED_AND_GET(type, format, expected_exp, got_exp) \
type caught_internal_expected = (expected_exp); \
type caught_internal_got = (got_exp); \
asprintf(&assertion.expected, format, caught_internal_expected); \
asprintf(&assertion.got, format, caught_internal_got);

int caught_internal_expect_equal_ptr(caught_internal_assertion *assertion, void *expected, void *got)
{
char *format = "%p";
asprintf(&assertion->expected, format, expected);
asprintf(&assertion->got, format, got);

return expected == got;
}
int caught_internal_expect_equal_bool(caught_internal_assertion *assertion, bool expected, bool got)
{
assertion->expected = strdup(expected ? "true" : "false");
assertion->got = strdup(got ? "true" : "false");

return expected == got;
}
int caught_internal_expect_equal_int(caught_internal_assertion *assertion, int expected, int got)
{
char *format = "%d";
asprintf(&assertion->expected, format, expected);
asprintf(&assertion->got, format, got);

return expected == got;
}
int caught_internal_expect_equal_char(caught_internal_assertion *assertion, char expected, char got)
{
char *format = "'%c'";
asprintf(&assertion->expected, format, expected);
asprintf(&assertion->got, format, got);

return expected == got;
}
int caught_internal_expect_equal_str(caught_internal_assertion *assertion, char *expected, char *got)
{
char *format = "\"%s\"";
asprintf(&assertion->expected, format, expected);
asprintf(&assertion->got, format, got);
free(assertion_result.lhs);
free(assertion_result.rhs);

int null_case = expected != got && (expected == NULL || got == NULL);
return !null_case && (strcmp(expected, got) == 0);
return !assertion_result.pass;
}
106 changes: 47 additions & 59 deletions src/assertions.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,60 @@

#include "state.h"
#include "config.h"
#include "evaluators.h"
#include "formatters.h"

#ifndef CAUGHT_ASSERTIONS
#define CAUGHT_ASSERTIONS

typedef struct caught_internal_assertion
typedef struct caught_internal_assertion_result
{
const char *file;
const int line;
const char *call;
char *expected;
char *got;
int pass;
} caught_internal_assertion;

void caught_internal_fancy_str(const char *str);
void caught_internal_handle_assertion(caught_internal_assertion *assertion);

int caught_internal_expect_equal_ptr(caught_internal_assertion *assertion, void *expected, void *got);
int caught_internal_expect_equal_bool(caught_internal_assertion *assertion, bool expected, bool got);
int caught_internal_expect_equal_int(caught_internal_assertion *assertion, int expected, int got);
int caught_internal_expect_equal_char(caught_internal_assertion *assertion, char expected, char got);
int caught_internal_expect_equal_str(caught_internal_assertion *assertion, char *expected, char *got);

#define CAUGHT_INTERNAL_MAKE_ASSERTION(func_name, expected, got) \
caught_internal_assertion caught_assertion = { \
.file = __FILE__, \
.line = __LINE__, \
.call = func_name "( " expected ", " got " )", \
};

#define CAUGHT_INTERNAL_READ_EXPECTED_AND_GET(type, format, expected_exp, got_exp) \
type caught_internal_expected = (expected_exp); \
type caught_internal_got = (got_exp); \
asprintf(&assertion.expected, format, caught_internal_expected); \
asprintf(&assertion.got, format, caught_internal_got);

#define CAUGHT_INTERNAL_PASS_ASSERTION(pass_exp) \
caught_assertion.pass = (pass_exp); \
if (!caught_assertion.pass) \
{ \
caught_internal_handle_assertion(&caught_assertion); \
return; \
} \
caught_internal_handle_assertion(&caught_assertion);

#define CAUGHT_INTERNAL_EXPECT_HANDLE(func, handler, expected_exp, got_exp) \
do \
{ \
CAUGHT_INTERNAL_MAKE_ASSERTION(func, #expected_exp, #got_exp) \
CAUGHT_INTERNAL_PASS_ASSERTION(handler(&caught_assertion, expected_exp, got_exp)) \
const char *expression;
char *lhs;
char *rhs;
enum caught_operator operator;
bool pass;
} caught_internal_assertion_result;

bool caught_internal_handle_assertion_result(caught_internal_assertion_result assertion_result);

// This is used by every expect define to handle taking lhs, op, rhs, & send them into their handlers.
// These handlers then determine how to display (format) the passed data, and whether the assertion passed (comparators).
// Finally, these results are combined into a assertion result and sent to the result handler, which outputs and keeps track
// of assertions accordingly.
//
// Note: do while is required to have non-conflicting scope if multiple assertions are used
#define CAUGHT_INTERNAL_EXPECT_HANDLE(func_postfix, type_exp, lhs_exp, operator_exp, rhs_exp, assertion_handler, formatter) \
do \
{ \
type_exp caught_internal_lhs = (lhs_exp); \
type_exp caught_internal_rhs = (rhs_exp); \
caught_internal_assertion_result caught_internal_assertion_result = { \
.file = __FILE__, \
.line = __LINE__, \
.expression = "EXPECT_" #func_postfix "( " #lhs_exp " " #operator_exp " " #rhs_exp " )", \
.lhs = formatter(caught_internal_lhs), \
.rhs = formatter(caught_internal_rhs), \
.operator= caught_str_to_operator(#operator_exp), \
.pass = assertion_handler(caught_internal_lhs, caught_str_to_operator(#operator_exp), caught_internal_rhs), \
}; \
if (caught_internal_handle_assertion_result(caught_internal_assertion_result)) \
{ \
return; \
} \
} while (0)

#define EXPECT_EQUAL_PTR(expected_exp, got_exp) \
CAUGHT_INTERNAL_EXPECT_HANDLE("EXPECT_EQUAL_PTR", caught_internal_expect_equal_ptr, expected_exp, got_exp)
#define EXPECT_EQUAL_BOOL(expected_exp, got_exp) \
CAUGHT_INTERNAL_EXPECT_HANDLE("EXPECT_EQUAL_BOOL", caught_internal_expect_equal_bool, expected_exp, got_exp)
#define EXPECT_EQUAL_INT(expected_exp, got_exp) \
CAUGHT_INTERNAL_EXPECT_HANDLE("EXPECT_EQUAL_INT", caught_internal_expect_equal_int, expected_exp, got_exp)
#define EXPECT_EQUAL_CHAR(expected_exp, got_exp) \
CAUGHT_INTERNAL_EXPECT_HANDLE("EXPECT_EQUAL_CHAR", caught_internal_expect_equal_char, expected_exp, got_exp)
#define EXPECT_EQUAL_STR(expected_exp, got_exp) \
CAUGHT_INTERNAL_EXPECT_HANDLE("EXPECT_EQUAL_STR", caught_internal_expect_equal_str, expected_exp, got_exp)

#define CAUGHT_INTERNAL_READ_EXPECTED_AND_GET(type, format, expected_exp, got_exp) \
type caught_internal_expected = (expected_exp); \
type caught_internal_got = (got_exp); \
asprintf(&assertion.expected, format, caught_internal_expected); \
asprintf(&assertion.got, format, caught_internal_got);
#define EXPECT_PTR(lhs, op, rhs) \
CAUGHT_INTERNAL_EXPECT_HANDLE(PTR, void *, lhs, op, rhs, caught_internal_evaluator_ptr, caught_internal_formatter_ptr)
#define EXPECT_BOOL(lhs, op, rhs) \
CAUGHT_INTERNAL_EXPECT_HANDLE(BOOL, bool, lhs, op, rhs, caught_internal_evaluator_int, caught_internal_formatter_bool)
#define EXPECT_INT(lhs, op, rhs) \
CAUGHT_INTERNAL_EXPECT_HANDLE(INT, int, lhs, op, rhs, caught_internal_evaluator_int, caught_internal_formatter_int)
#define EXPECT_CHAR(lhs, op, rhs) \
CAUGHT_INTERNAL_EXPECT_HANDLE(CHAR, char, lhs, op, rhs, caught_internal_evaluator_char, caught_internal_formatter_char)
#define EXPECT_STR(lhs, op, rhs) \
CAUGHT_INTERNAL_EXPECT_HANDLE(STR, char *, lhs, op, rhs, caught_internal_evaluator_str, caught_internal_formatter_str)

#endif
68 changes: 68 additions & 0 deletions src/evaluators.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "evaluators.h"
#include <stdlib.h>
#include <stdio.h>

// General purpose converter from string (==) to operator enum (CAUGHT_OP_EQUAL)
enum caught_operator
caught_str_to_operator(char *str)
{
int len = sizeof(CAUGHT_OPERATOR_STRS) / sizeof(CAUGHT_OPERATOR_STRS[0]);
int i;
for (i = 0; i < len; ++i)
{
if (strcmp(str, CAUGHT_OPERATOR_STRS[i]) == 0)
{
return i;
}
}
return -1;
}

// General purpose converter from enum (CAUGHT_OP_EQUAL) to operator string (==)
const char *caught_operator_to_str(enum caught_operator operator)
{
return CAUGHT_OPERATOR_STRS[operator];
}

// General purpose converter from enum (CAUGHT_OP_EQUAL) to a to be statement (to be, to not be, etc.)
const char *caught_operator_to_to_be_statement(enum caught_operator operator)
{
return CAUGHT_OPERATOR_TO_BES[operator];
}

// Evaluators take in a left hand size, operator, and right hand side
// they then evaluate the result of that expression
// CAUGHT_GENERATE_GENERIC_EVALUATOR just uses the default operators (==, <=, >=, ...)
// but more advanced definitions are needed for things like strings

bool caught_internal_evaluator_ptr(void *lhs, enum caught_operator operator, void * rhs)
{
CAUGHT_GENERATE_GENERIC_EVALUATOR
}
bool caught_internal_evaluator_bool(bool lhs, enum caught_operator operator, bool rhs)
{
CAUGHT_GENERATE_GENERIC_EVALUATOR
}

bool caught_internal_evaluator_int(int lhs, enum caught_operator operator, int rhs)
{
CAUGHT_GENERATE_GENERIC_EVALUATOR
}
bool caught_internal_evaluator_char(char lhs, enum caught_operator operator, char rhs)
{
CAUGHT_GENERATE_GENERIC_EVALUATOR
}
bool caught_internal_evaluator_str(char *lhs, enum caught_operator operator, char * rhs)
{
bool null_exists = (lhs == NULL) || (rhs == NULL);
switch (operator)
{
case CAUGHT_OP_EQUAL:
return (null_exists && lhs == rhs) || (!null_exists && strcmp(lhs, rhs) == 0);
case CAUGHT_OP_NOT_EQUAL:
return (null_exists && lhs != rhs) || (!null_exists && strcmp(lhs, rhs) != 0);
default:
fprintf(stderr, "Cannot compare strings with %s, only == and != are supported!", caught_operator_to_str(operator));
exit(1);
}
}
66 changes: 66 additions & 0 deletions src/evaluators.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include <stdbool.h>
#include <string.h>

#ifndef CAUGHT_EVALUATORS
#define CAUGHT_EVALUATORS

enum caught_operator
{
CAUGHT_OP_EQUAL,
CAUGHT_OP_NOT_EQUAL,
CAUGHT_OP_LESS_THAN,
CAUGHT_OP_GREATER_THAN,
CAUGHT_OP_LESS_THAN_EQ,
CAUGHT_OP_GREATER_THAN_EQ,
};

static char *CAUGHT_OPERATOR_STRS[] = {
"==",
"!=",
"<",
">",
"<=",
">=",
};

static char *CAUGHT_OPERATOR_TO_BES[] = {
"to be",
"to not be",
"to be less than",
"to be greater than",
"to be <= to",
"to be >= to",
};

enum caught_operator
caught_str_to_operator(char *str);
const char *caught_operator_to_str(enum caught_operator operator);
const char *caught_operator_to_to_be_statement(enum caught_operator operator);

bool caught_internal_evaluator_ptr(void *lhs, enum caught_operator operator, void * rhs);
bool caught_internal_evaluator_bool(bool lhs, enum caught_operator operator, bool rhs);
bool caught_internal_evaluator_int(int lhs, enum caught_operator operator, int rhs);
bool caught_internal_evaluator_char(char lhs, enum caught_operator operator, char rhs);
bool caught_internal_evaluator_str(char *lhs, enum caught_operator operator, char * rhs);

// Uses default operators (==, <=, >=, ...) to compare lhs to rhs
#define CAUGHT_GENERATE_GENERIC_EVALUATOR \
switch (operator) \
{ \
case CAUGHT_OP_EQUAL: \
return lhs == rhs; \
case CAUGHT_OP_NOT_EQUAL: \
return lhs != rhs; \
case CAUGHT_OP_LESS_THAN: \
return lhs < rhs; \
case CAUGHT_OP_GREATER_THAN: \
return lhs > rhs; \
case CAUGHT_OP_LESS_THAN_EQ: \
return lhs <= rhs; \
case CAUGHT_OP_GREATER_THAN_EQ: \
return lhs >= rhs; \
default: \
return false; \
}

#endif
41 changes: 41 additions & 0 deletions src/formatters.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "formatters.h"

#define _GNU_SOURCE
#include <stdio.h>

char *caught_internal_formatter_ptr(void *value)
{
char *result;
asprintf(&result, "%p", value);
return result;
}

char *caught_internal_formatter_bool(bool value)
{
return (value) ? strdup("true") : strdup("false");
}

char *caught_internal_formatter_int(int value)
{
char *result;
asprintf(&result, "%i", value);
return result;
}

char *caught_internal_formatter_char(char value)
{
char *result;
asprintf(&result, "%c", value);
return result;
}

char *caught_internal_formatter_str(char *value)
{
if (value == NULL)
{
return NULL;
}
char *result;
asprintf(&result, "\"%s\"", value);
return result;
}
Loading

0 comments on commit 6430355

Please sign in to comment.