Updated testing framework
This commit is contained in:
81
tests/core.c
Normal file
81
tests/core.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "ptest.h"
|
||||
#include "../mpc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool int_eq(void* x, void* y) { return (*(int*)x == *(int*)y); }
|
||||
static void int_print(void* x) { printf("'%i'", *((int*)x)); }
|
||||
static bool string_eq(void* x, void* y) { return (strcmp(x, y) == 0); }
|
||||
static void string_print(void* x) { printf("'%s'", (char*)x); }
|
||||
|
||||
void test_ident(void) {
|
||||
|
||||
/* ^[a-zA-Z_][a-zA-Z0-9_]*$ */
|
||||
|
||||
mpc_parser_t* Ident = mpc_ends(
|
||||
mpc_also(
|
||||
mpc_else(mpc_alpha(), mpc_underscore()),
|
||||
mpc_many1(mpc_or(3, mpc_alpha(), mpc_underscore(), mpc_digit()), mpcf_strfold),
|
||||
free, mpcf_strfold),
|
||||
free
|
||||
);
|
||||
|
||||
PT_ASSERT(mpc_match(Ident, "test", "test", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_unmatch(Ident, " blah", "", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_match(Ident, "anoth21er", "anoth21er", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_match(Ident, "du__de", "du__de", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_unmatch(Ident, "some spaces", "", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_unmatch(Ident, "", "", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_unmatch(Ident, "18nums", "", string_eq, free, string_print));
|
||||
|
||||
mpc_delete(Ident);
|
||||
|
||||
}
|
||||
|
||||
void test_maths(void) {
|
||||
|
||||
mpc_parser_t* Expr = mpc_new();
|
||||
mpc_parser_t* Factor = mpc_new();
|
||||
mpc_parser_t* Term = mpc_new();
|
||||
mpc_parser_t* Maths = mpc_new();
|
||||
|
||||
mpc_define(Expr, mpc_else(
|
||||
mpc_and(3, mpcf_maths, Factor, mpc_oneof("*/"), Factor, free, free),
|
||||
Factor
|
||||
));
|
||||
|
||||
mpc_define(Factor, mpc_else(
|
||||
mpc_and(3, mpcf_maths, Term, mpc_oneof("+-"), Term, free, free),
|
||||
Term
|
||||
));
|
||||
|
||||
mpc_define(Term, mpc_else(
|
||||
mpc_int(),
|
||||
mpc_parens(Expr, free)
|
||||
));
|
||||
|
||||
mpc_define(Maths, mpc_ends(Expr, free));
|
||||
|
||||
PT_ASSERT(mpc_match(Maths, "1", (int[]){ 1 }, int_eq, free, int_print));
|
||||
PT_ASSERT(mpc_match(Maths, "(5)", (int[]){ 5 }, int_eq, free, int_print));
|
||||
PT_ASSERT(mpc_match(Maths, "(4*2)+5", (int[]){ 13 }, int_eq, free, int_print));
|
||||
PT_ASSERT(mpc_unmatch(Maths, "a", (int[]){ 0 }, int_eq, free, int_print));
|
||||
PT_ASSERT(mpc_unmatch(Maths, "2b+4", (int[]){ 2 }, int_eq, free, int_print));
|
||||
|
||||
mpc_undefine(Expr);
|
||||
mpc_undefine(Factor);
|
||||
mpc_undefine(Term);
|
||||
mpc_undefine(Maths);
|
||||
|
||||
mpc_delete(Expr);
|
||||
mpc_delete(Factor);
|
||||
mpc_delete(Term);
|
||||
mpc_delete(Maths);
|
||||
|
||||
}
|
||||
|
||||
void suite_core(void) {
|
||||
pt_add_test(test_ident, "Test Ident", "Suite Core");
|
||||
pt_add_test(test_maths, "Test Maths", "Suite Core");
|
||||
}
|
56
tests/grammar.c
Normal file
56
tests/grammar.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "ptest.h"
|
||||
#include "../mpc.h"
|
||||
|
||||
bool ast_eq(void* x, void* y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void test_grammar(void) {
|
||||
|
||||
mpc_parser_t* Test = mpc_new();
|
||||
|
||||
mpc_define(Test, mpca_grammar("'c'*"));
|
||||
|
||||
mpc_print(Test);
|
||||
|
||||
mpc_undefine(Test);
|
||||
|
||||
mpc_delete(Test);
|
||||
|
||||
mpc_parser_t* Expression = mpc_new();
|
||||
mpc_parser_t* Product = mpc_new();
|
||||
mpc_parser_t* Value = mpc_new();
|
||||
|
||||
mpc_define(Expression, mpca_grammar("<0> (('+' | '-') <0>)*", Product));
|
||||
mpc_define(Product, mpca_grammar("<0> (('*' | '/') <0>)*", Value));
|
||||
mpc_define(Value, mpca_grammar("/[0-9]/ | '(' <0> ')'", Expression));
|
||||
|
||||
mpc_print(Expression);
|
||||
mpc_print(Product);
|
||||
mpc_print(Value);
|
||||
|
||||
mpc_ast_t* empty = mpc_ast_empty();
|
||||
|
||||
/*
|
||||
PT_ASSERT(mpc_match(Expression, "1", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
|
||||
PT_ASSERT(mpc_match(Expression, "(5)", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
|
||||
PT_ASSERT(mpc_match(Expression, "(4*2)+5", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
|
||||
PT_ASSERT(mpc_match(Expression, "a", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
|
||||
PT_ASSERT(mpc_match(Expression, "2b+4", empty, ast_eq, (mpc_dtor_t)mpc_ast_delete, (void(*)(void*))mpc_ast_print));
|
||||
*/
|
||||
|
||||
mpc_ast_delete(empty);
|
||||
|
||||
mpc_undefine(Expression);
|
||||
mpc_undefine(Product);
|
||||
mpc_undefine(Value);
|
||||
|
||||
mpc_delete(Expression);
|
||||
mpc_delete(Product);
|
||||
mpc_delete(Value);
|
||||
|
||||
}
|
||||
|
||||
void suite_grammar(void) {
|
||||
pt_add_test(test_grammar, "Test Grammar", "Suite Grammar");
|
||||
}
|
301
tests/ptest.c
Normal file
301
tests/ptest.c
Normal file
@@ -0,0 +1,301 @@
|
||||
#include "ptest.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Globals */
|
||||
|
||||
#define MAX_NAME 512
|
||||
#define MAX_ERROR 2048
|
||||
#define MAX_TESTS 2048
|
||||
|
||||
static bool test_passing = false;
|
||||
static bool suite_passing = false;
|
||||
|
||||
/* Colors */
|
||||
|
||||
enum {
|
||||
BLACK = 0x0,
|
||||
BLUE = 0x1,
|
||||
GREEN = 0x2,
|
||||
AQUA = 0x3,
|
||||
RED = 0x4,
|
||||
PURPLE = 0x5,
|
||||
YELLOW = 0x6,
|
||||
WHITE = 0x7,
|
||||
GRAY = 0x8,
|
||||
LIGHT_BLUE = 0x9,
|
||||
LIGHT_GREEN = 0xA,
|
||||
LIGHT_AQUA = 0xB,
|
||||
LIGHT_RED = 0xC,
|
||||
LIGHT_PURPLE = 0xD,
|
||||
LIGHT_YELLOW = 0xE,
|
||||
LIGHT_WHITE = 0xF,
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static void pt_color(int color) {
|
||||
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(hCon, color);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static const char* colors[] = {
|
||||
"\x1B[0m",
|
||||
"\x1B[34m",
|
||||
"\x1B[32m",
|
||||
"\x1B[36m",
|
||||
"\x1B[31m",
|
||||
"\x1B[35m",
|
||||
"\x1B[33m",
|
||||
"\x1B[37m",
|
||||
"",
|
||||
"\x1B[34m",
|
||||
"\x1B[32m",
|
||||
"\x1B[36m",
|
||||
"\x1B[31m",
|
||||
"\x1B[35m",
|
||||
"\x1B[33m",
|
||||
"\x1B[37m"
|
||||
};
|
||||
|
||||
static void pt_color(int color) {
|
||||
|
||||
printf("%s", colors[color]);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Asserts */
|
||||
|
||||
static int num_asserts = 0;
|
||||
static int num_assert_passes = 0;
|
||||
static int num_assert_fails = 0;
|
||||
|
||||
static char assert_err[MAX_ERROR];
|
||||
static char assert_err_buff[MAX_ERROR];
|
||||
static int assert_err_num = 0;
|
||||
|
||||
void pt_assert_run(bool result, const char* expr, const char* func, const char* file, int line) {
|
||||
|
||||
num_asserts++;
|
||||
test_passing = test_passing && result;
|
||||
|
||||
if (result) {
|
||||
num_assert_passes++;
|
||||
} else {
|
||||
sprintf(assert_err_buff, " %i. Assert [ %s ] (%s:%i)\n", assert_err_num+1, expr, file, line );
|
||||
strcat(assert_err, assert_err_buff);
|
||||
assert_err_num++;
|
||||
num_assert_fails++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void ptest_signal(int sig) {
|
||||
|
||||
test_passing = false;
|
||||
|
||||
switch( sig ) {
|
||||
case SIGFPE: sprintf(assert_err_buff, " %i. Division by Zero\n", assert_err_num+1); break;
|
||||
case SIGILL: sprintf(assert_err_buff, " %i. Illegal Instruction\n", assert_err_num+1); break;
|
||||
case SIGSEGV: sprintf(assert_err_buff, " %i. Segmentation Fault\n", assert_err_num+1); break;
|
||||
}
|
||||
|
||||
assert_err_num++;
|
||||
strcat(assert_err, assert_err_buff);
|
||||
|
||||
pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
|
||||
|
||||
printf(" | Stopping Execution.\n");
|
||||
fflush(stdout);
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
||||
/* Tests */
|
||||
|
||||
static void pt_title_case(char* output, const char* input) {
|
||||
|
||||
bool space = true;
|
||||
|
||||
strcpy(output, input);
|
||||
|
||||
unsigned int i;
|
||||
for(i = 0; i < strlen(output); i++) {
|
||||
|
||||
if (output[i] == '_' || output[i] == ' ') {
|
||||
space = true;
|
||||
output[i] = ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (space && output[i] >= 'a' && output[i] <= 'z') {
|
||||
output[i] = output[i] - 32;
|
||||
continue;
|
||||
}
|
||||
|
||||
space = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
void (*func)(void);
|
||||
char name[MAX_NAME];
|
||||
char suite[MAX_NAME];
|
||||
} test_t;
|
||||
|
||||
static test_t tests[MAX_TESTS];
|
||||
|
||||
static int num_tests = 0;
|
||||
static int num_tests_passes = 0;
|
||||
static int num_tests_fails = 0;
|
||||
|
||||
void pt_add_test(void (*func)(void), const char* name, const char* suite) {
|
||||
|
||||
if (num_tests == MAX_TESTS) {
|
||||
printf("ERROR: Exceeded maximum test count of %i!\n", MAX_TESTS); abort();
|
||||
}
|
||||
|
||||
if (strlen(name) >= MAX_NAME) {
|
||||
printf("ERROR: Test name '%s' too long (Maximum is %i characters)\n", name, MAX_NAME); abort();
|
||||
}
|
||||
|
||||
if (strlen(suite) >= MAX_NAME) {
|
||||
printf("ERROR: Test suite '%s' too long (Maximum is %i characters)\n", suite, MAX_NAME); abort();
|
||||
}
|
||||
|
||||
test_t test;
|
||||
test.func = func;
|
||||
pt_title_case(test.name, name);
|
||||
pt_title_case(test.suite, suite);
|
||||
|
||||
tests[num_tests] = test;
|
||||
num_tests++;
|
||||
|
||||
}
|
||||
|
||||
/* Suites */
|
||||
|
||||
static int num_suites = 0;
|
||||
static int num_suites_passes = 0;
|
||||
static int num_suites_fails = 0;
|
||||
|
||||
void pt_add_suite(void (*func)(void)) {
|
||||
num_suites++;
|
||||
func();
|
||||
}
|
||||
|
||||
/* Running */
|
||||
|
||||
static clock_t start, end;
|
||||
static char current_suite[MAX_NAME];
|
||||
|
||||
int pt_run(void) {
|
||||
|
||||
printf(" \n");
|
||||
printf(" +-------------------------------------------+\n");
|
||||
printf(" | ptest MicroTesting Magic for C |\n");
|
||||
printf(" | |\n");
|
||||
printf(" | http://github.com/orangeduck/ptest |\n");
|
||||
printf(" | |\n");
|
||||
printf(" | Daniel Holden (contact@theorangeduck.com) |\n");
|
||||
printf(" +-------------------------------------------+\n");
|
||||
|
||||
signal(SIGFPE, ptest_signal);
|
||||
signal(SIGILL, ptest_signal);
|
||||
signal(SIGSEGV, ptest_signal);
|
||||
|
||||
start = clock();
|
||||
strcpy(current_suite, "");
|
||||
|
||||
unsigned int i;
|
||||
for(i = 0; i < num_tests; i++) {
|
||||
|
||||
test_t test = tests[i];
|
||||
|
||||
/* Check for transition to a new suite */
|
||||
if (strcmp(test.suite, current_suite)) {
|
||||
|
||||
/* Don't increment any counter for first entrance */
|
||||
if (strcmp(current_suite, "")) {
|
||||
if (suite_passing) {
|
||||
num_suites_passes++;
|
||||
} else {
|
||||
num_suites_fails++;
|
||||
}
|
||||
}
|
||||
|
||||
suite_passing = true;
|
||||
strcpy(current_suite, test.suite);
|
||||
printf("\n\n ===== %s =====\n\n", current_suite);
|
||||
}
|
||||
|
||||
/* Run Test */
|
||||
|
||||
test_passing = true;
|
||||
strcpy(assert_err, "");
|
||||
strcpy(assert_err_buff, "");
|
||||
assert_err_num = 0;
|
||||
printf(" | %s ... ", test.name);
|
||||
|
||||
test.func();
|
||||
|
||||
suite_passing = suite_passing && test_passing;
|
||||
|
||||
if (test_passing) {
|
||||
num_tests_passes++;
|
||||
pt_color(GREEN); printf("Passed! \n"); pt_color(WHITE);
|
||||
} else {
|
||||
num_tests_fails++;
|
||||
pt_color(RED); printf("Failed! \n\n%s\n", assert_err); pt_color(WHITE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (suite_passing) {
|
||||
num_suites_passes++;
|
||||
} else {
|
||||
num_suites_fails++;
|
||||
}
|
||||
|
||||
end = clock();
|
||||
|
||||
printf(" \n");
|
||||
printf(" +---------------------------------------------------+\n");
|
||||
printf(" | Summary |\n");
|
||||
printf(" +---------++------------+-------------+-------------+\n");
|
||||
|
||||
printf(" | Suites ||");
|
||||
pt_color(YELLOW); printf(" Total %4d ", num_suites); pt_color(WHITE); printf("|");
|
||||
pt_color(GREEN); printf(" Passed %4d ", num_suites_passes); pt_color(WHITE); printf("|");
|
||||
pt_color(RED); printf(" Failed %4d ", num_suites_fails); pt_color(WHITE); printf("|\n");
|
||||
|
||||
printf(" | Tests ||");
|
||||
pt_color(YELLOW); printf(" Total %4d ", num_tests); pt_color(WHITE); printf("|");
|
||||
pt_color(GREEN); printf(" Passed %4d ", num_tests_passes); pt_color(WHITE); printf("|");
|
||||
pt_color(RED); printf(" Failed %4d ", num_tests_fails); pt_color(WHITE); printf("|\n");
|
||||
|
||||
printf(" | Asserts ||");
|
||||
pt_color(YELLOW); printf(" Total %4d ", num_asserts); pt_color(WHITE); printf("|");
|
||||
pt_color(GREEN); printf(" Passed %4d ", num_assert_passes); pt_color(WHITE); printf("|");
|
||||
pt_color(RED); printf(" Failed %4d ", num_assert_fails); pt_color(WHITE); printf("|\n");
|
||||
|
||||
printf(" +---------++------------+-------------+-------------+\n");
|
||||
printf(" \n");
|
||||
|
||||
double total = (double)(end - start) / CLOCKS_PER_SEC;
|
||||
|
||||
printf(" Total Running Time: %0.3fs\n\n", total);
|
||||
|
||||
if (num_suites_fails > 0) { return 1; } else { return 0; }
|
||||
}
|
19
tests/ptest.h
Normal file
19
tests/ptest.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef ptest_h
|
||||
#define ptest_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PT_SUITE(name) void name(void)
|
||||
#define PT_TEST(name) auto void name(void); pt_add_test(name, #name, __func__); void name(void)
|
||||
|
||||
#define PT_ASSERT(expr) pt_assert_run((bool)(expr), #expr, __func__, __FILE__, __LINE__)
|
||||
#define PT_ASSERT_STR_EQ(fst, snd) pt_assert_run(strcmp(fst, snd) == 0, "strcmp( " #fst ", " #snd " ) == 0", __func__, __FILE__, __LINE__)
|
||||
|
||||
void pt_assert_run(bool result, const char* expr, const char* func, const char* file, int line);
|
||||
|
||||
void pt_add_test(void (*func)(void), const char* name, const char* suite);
|
||||
void pt_add_suite(void (*func)(void));
|
||||
int pt_run(void);
|
||||
|
||||
#endif
|
@@ -1,27 +1,29 @@
|
||||
#include "ptest.h"
|
||||
#include "../mpc.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
static bool string_eq(void* x, void* y) { return (strcmp(x, y) == 0); }
|
||||
static void string_print(void* x) { printf("'%s'", (char*)x); }
|
||||
*/
|
||||
|
||||
bool suite_regex(void) {
|
||||
|
||||
void test_regex_basic(void) {
|
||||
|
||||
mpc_parser_t* re0 = mpc_re("abc|bcd");
|
||||
mpc_parser_t* re1 = mpc_re("abc|bcd|e");
|
||||
mpc_parser_t* re2 = mpc_re("abc(ab)*");
|
||||
mpc_parser_t* re2 = mpc_re("ab()c(ab)*");
|
||||
mpc_parser_t* re3 = mpc_re("abc(abdd)?");
|
||||
mpc_parser_t* re4 = mpc_re("ab|c(abdd)?");
|
||||
mpc_parser_t* re5 = mpc_re("abc(ab|dd)+g$");
|
||||
|
||||
mpc_print(re0);
|
||||
mpc_print(re1);
|
||||
mpc_print(re2);
|
||||
mpc_print(re3);
|
||||
mpc_print(re4);
|
||||
mpc_print(re5);
|
||||
PT_ASSERT(mpc_match(re0, "abc", "abc", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_match(re0, "bcd", "bcd", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_unmatch(re0, "bc", "bc", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_unmatch(re0, "ab", "ab", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_match(re1, "e", "e", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_match(re2, "abc", "abc", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_match(re2, "abcabab", "abcabab", string_eq, free, string_print));
|
||||
PT_ASSERT(mpc_match(re2, "abcababd", "abcabab", string_eq, free, string_print));
|
||||
|
||||
mpc_delete(re0);
|
||||
mpc_delete(re1);
|
||||
@@ -29,6 +31,29 @@ bool suite_regex(void) {
|
||||
mpc_delete(re3);
|
||||
mpc_delete(re4);
|
||||
mpc_delete(re5);
|
||||
|
||||
}
|
||||
|
||||
void test_regex_range(void) {
|
||||
|
||||
mpc_parser_t* re0 = mpc_re("abg[abcdef]");
|
||||
mpc_parser_t* re1 = mpc_re("y*[a-z]");
|
||||
mpc_parser_t* re2 = mpc_re("zz(p+)?[A-Z_0\\]123]*");
|
||||
mpc_parser_t* re3 = mpc_re("[^56hy].*$");
|
||||
|
||||
mpc_print(re0);
|
||||
mpc_print(re1);
|
||||
mpc_print(re2);
|
||||
mpc_print(re3);
|
||||
|
||||
return true;
|
||||
mpc_delete(re0);
|
||||
mpc_delete(re1);
|
||||
mpc_delete(re2);
|
||||
mpc_delete(re3);
|
||||
|
||||
}
|
||||
|
||||
void suite_regex(void) {
|
||||
pt_add_test(test_regex_basic, "Test Regex Basic", "Suite Regex");
|
||||
pt_add_test(test_regex_range, "Test Regex Range", "Suite Regex");
|
||||
}
|
19
tests/test.c
19
tests/test.c
@@ -1,15 +1,12 @@
|
||||
#include <stdbool.h>
|
||||
#include "ptest.h"
|
||||
|
||||
bool suite_ident(void);
|
||||
bool suite_math(void);
|
||||
bool suite_regex(void);
|
||||
void suite_core(void);
|
||||
void suite_regex(void);
|
||||
void suite_grammar(void);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
suite_ident();
|
||||
suite_math();
|
||||
suite_regex();
|
||||
|
||||
return 0;
|
||||
|
||||
pt_add_suite(suite_core);
|
||||
pt_add_suite(suite_regex);
|
||||
pt_add_suite(suite_grammar);
|
||||
return pt_run();
|
||||
}
|
Reference in New Issue
Block a user