diff --git a/src/assertions.c b/src/assertions.c index 71dcde6..b79a756 100644 --- a/src/assertions.c +++ b/src/assertions.c @@ -1,27 +1,15 @@ #include -#define _GNU_SOURCE #include -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; } @@ -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; } diff --git a/src/assertions.h b/src/assertions.h index 248e730..4fd6f12 100644 --- a/src/assertions.h +++ b/src/assertions.h @@ -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 diff --git a/src/evaluators.c b/src/evaluators.c new file mode 100644 index 0000000..2f920a6 --- /dev/null +++ b/src/evaluators.c @@ -0,0 +1,68 @@ +#include "evaluators.h" +#include +#include + +// 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); + } +} diff --git a/src/evaluators.h b/src/evaluators.h new file mode 100644 index 0000000..00da1e4 --- /dev/null +++ b/src/evaluators.h @@ -0,0 +1,66 @@ +#include +#include + +#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 diff --git a/src/formatters.c b/src/formatters.c new file mode 100644 index 0000000..a90e745 --- /dev/null +++ b/src/formatters.c @@ -0,0 +1,41 @@ +#include "formatters.h" + +#define _GNU_SOURCE +#include + +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; +} diff --git a/src/formatters.h b/src/formatters.h new file mode 100644 index 0000000..aa1f5bf --- /dev/null +++ b/src/formatters.h @@ -0,0 +1,17 @@ +#define _GNU_SOURCE +#include +#include + +#ifndef CAUGHT_FORMATTERS +#define CAUGHT_FORMATTERS + +// Formatters take a value of a certain type +// they must return a dynamically allocated string that represents that value + +char *caught_internal_formatter_ptr(void *value); +char *caught_internal_formatter_bool(bool value); +char *caught_internal_formatter_int(int value); +char *caught_internal_formatter_char(char value); +char *caught_internal_formatter_str(char *value); + +#endif diff --git a/src/output.c b/src/output.c index 0ebde54..22d5eac 100644 --- a/src/output.c +++ b/src/output.c @@ -80,22 +80,27 @@ void caught_output_status_tag(int pass) caught_output_reset(); } -void caught_output_assertion(caught_internal_assertion *assertion) +void caught_output_assertion_result(caught_internal_assertion_result assertion_result) { printf("\n"); - caught_output_status_tag(assertion->pass); - printf("./%s:%i:\n", assertion->file, assertion->line); + caught_output_status_tag(assertion_result.pass); + printf("./%s:%i:\n", assertion_result.file, assertion_result.line); caught_output_info(); - printf(" %s\n", assertion->call); + printf(" %s\n", assertion_result.expression); caught_output_reset(); - printf(" expected: "); + + const char *expected_statement = "expected"; + const char *to_be_statement = caught_operator_to_to_be_statement(assertion_result.operator); + int statement_padding = (strlen(expected_statement) > strlen(to_be_statement)) ? strlen(expected_statement) : strlen(to_be_statement); + + printf(" %s:%*s ", expected_statement, statement_padding - (int)strlen(expected_statement), ""); caught_output_success(); - caught_internal_fancy_str(assertion->expected); + printf("%s", assertion_result.lhs == NULL ? "NULL" : assertion_result.lhs); caught_output_reset(); - printf("\n got: "); - assertion->pass ? caught_output_success() : caught_output_fail(); - caught_internal_fancy_str(assertion->got); + printf("\n %s:%*s ", to_be_statement, statement_padding - (int)strlen(to_be_statement), ""); + assertion_result.pass ? caught_output_success() : caught_output_fail(); + printf("%s", assertion_result.rhs == NULL ? "NULL" : assertion_result.rhs); caught_output_reset(); printf("\n"); } diff --git a/src/output.h b/src/output.h index f9d4de6..63b947d 100644 --- a/src/output.h +++ b/src/output.h @@ -15,7 +15,7 @@ void caught_output_reset(); void caught_output_header(); void caught_output_status_tag(int pass); -void caught_output_assertion(caught_internal_assertion *assertion); +void caught_output_assertion_result(caught_internal_assertion_result assertion_result); void caught_output_test_summary(const char *test_name, int passed, int failed); void caught_output_generic_summary(const char *prefix, int passed, int failed); void caught_output_summary(const char *prefix, int passed, int failed); diff --git a/tests/bool.c b/tests/bool.c index 6eb2f70..37b40b7 100644 --- a/tests/bool.c +++ b/tests/bool.c @@ -6,6 +6,20 @@ TEST("bool - basic") { - EXPECT_EQUAL_BOOL(true, true); - EXPECT_EQUAL_BOOL(false, false); + EXPECT_BOOL(true, ==, true); + EXPECT_BOOL(false, ==, false); + EXPECT_BOOL(true, !=, false); + EXPECT_BOOL(false, !=, true); +} + +TEST("bool - but bools are ints!") +{ + + EXPECT_BOOL(true, >, false); + EXPECT_BOOL(true, >=, false); + EXPECT_BOOL(true, >=, true); + + EXPECT_BOOL(false, <, true); + EXPECT_BOOL(false, <=, true); + EXPECT_BOOL(false, <=, false); } diff --git a/tests/char.c b/tests/char.c index 9c41f7a..4959079 100644 --- a/tests/char.c +++ b/tests/char.c @@ -6,12 +6,22 @@ TEST("char - basic") { - EXPECT_EQUAL_CHAR('a', 'a'); - EXPECT_EQUAL_CHAR('z', 'z'); + EXPECT_CHAR('a', ==, 'a'); + EXPECT_CHAR('a', !=, 'z'); } -TEST("char - basic") +TEST("char - inequalities") +{ + EXPECT_CHAR('B', <, 'c'); + EXPECT_CHAR('a', <=, 'z'); + EXPECT_CHAR('z', <=, 'z'); + EXPECT_CHAR('a', >, 'W'); + EXPECT_CHAR('c', >=, 'b'); + EXPECT_CHAR('c', >=, 'c'); +} + +TEST("char - strstr") { - EXPECT_EQUAL_CHAR('a', *strchr("what a day", 'a')); - EXPECT_EQUAL_CHAR('d', *strchr("what a day", 'd')); + EXPECT_CHAR('a', ==, *strchr("what a day", 'a')); + EXPECT_CHAR('d', ==, *strchr("what a day", 'd')); } diff --git a/tests/int.c b/tests/int.c index 47a8dca..47c1c1e 100644 --- a/tests/int.c +++ b/tests/int.c @@ -2,14 +2,21 @@ // For example, it could be: #include "caught.h" #include "../src/caught.h" -TEST("int - 1 + 1 = 2") +TEST("int - simple math") { - EXPECT_EQUAL_INT(2, 1 + 1); + EXPECT_INT(2, ==, 1 + 1); + EXPECT_INT(7, ==, 3 + 4); + EXPECT_INT(42, !=, 6 * 9); } -TEST("int - 3 + 4 = 7") +TEST("int - inequalities") { - EXPECT_EQUAL_INT(7, 3 + 4); + EXPECT_INT(734, >, 5); + EXPECT_INT(-411, <, -1); + EXPECT_INT(6, >=, 5); + EXPECT_INT(5, >=, 5); + EXPECT_INT(2, <=, 3); + EXPECT_INT(3, <=, 3); } int factorial(n) @@ -23,10 +30,10 @@ int factorial(n) TEST("int - factorial") { - EXPECT_EQUAL_INT(1, factorial(1)); - EXPECT_EQUAL_INT(2, factorial(2)); - EXPECT_EQUAL_INT(6, factorial(3)); - EXPECT_EQUAL_INT(24, factorial(4)); - EXPECT_EQUAL_INT(120, factorial(5)); - EXPECT_EQUAL_INT(720, factorial(6)); + EXPECT_INT(1, ==, factorial(1)); + EXPECT_INT(2, ==, factorial(2)); + EXPECT_INT(6, ==, factorial(3)); + EXPECT_INT(24, ==, factorial(4)); + EXPECT_INT(120, ==, factorial(5)); + EXPECT_INT(720, ==, factorial(6)); } diff --git a/tests/ptr.c b/tests/ptr.c index 0c58359..070ec01 100644 --- a/tests/ptr.c +++ b/tests/ptr.c @@ -2,10 +2,20 @@ // For example, it could be: #include "caught.h" #include "../src/caught.h" +static int array[] = {1, 2, 3, 4, 5}; TEST("ptr - basic") { - int array[] = {1, 2, 3, 4, 5}; - EXPECT_EQUAL_PTR(array, array); - EXPECT_EQUAL_PTR(array + 1, array + 1); - EXPECT_EQUAL_PTR(array + 3, array + 3); + EXPECT_PTR(array, ==, array); + EXPECT_PTR(array + 1, ==, array + 1); + EXPECT_PTR(array + 3, !=, array + 2); +} + +TEST("ptr - inequalities") +{ + EXPECT_PTR(array + 5, >, array + 2); + EXPECT_PTR(array + 1, <, array + 3); + EXPECT_PTR(array + 8, >=, array + 7); + EXPECT_PTR(array + 8, >=, array + 8); + EXPECT_PTR(array + 5, <=, array + 6); + EXPECT_PTR(array + 7, <=, array + 7); } diff --git a/tests/str.c b/tests/str.c index f742a6a..8686bc0 100644 --- a/tests/str.c +++ b/tests/str.c @@ -5,12 +5,14 @@ TEST("str - basic") { - EXPECT_EQUAL_STR("abc", "abc"); - EXPECT_EQUAL_STR("def", "def"); + EXPECT_STR("abc", ==, "abc"); + EXPECT_STR("def", ==, "def"); + EXPECT_STR(NULL, !=, "other"); + EXPECT_STR("one string", !=, "other"); } TEST("str - strstr") { char *str = "a long string that can be indexed"; - EXPECT_EQUAL_STR("be indexed", strstr(str, "be")); + EXPECT_STR("be indexed", ==, strstr(str, "be")); }