diff --git a/cpp_test.cpp b/cpp_test.cpp new file mode 100644 index 0000000..a37207e --- /dev/null +++ b/cpp_test.cpp @@ -0,0 +1,8 @@ +#include "set.h" +#include "set_asserts.h" + +TEST(Basic) { ASSERT_EQ(2, 2); } + +SUIT(Basic) { ADD_TEST(Basic); } + +BUNDLE() { ADD_SUIT(Basic); } diff --git a/set.c b/set.c index 6464991..e6edef1 100644 --- a/set.c +++ b/set.c @@ -3,9 +3,17 @@ #include #include #include +#include +#include +#include +#include + +#include #include "set_asserts.h" +#ifdef COLORIZED + #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_YELLOW "\x1b[33m" @@ -14,113 +22,165 @@ #define ANSI_COLOR_CYAN "\x1b[36m" #define ANSI_COLOR_RESET "\x1b[0m" +#else + +#define ANSI_COLOR_RED +#define ANSI_COLOR_GREEN +#define ANSI_COLOR_YELLOW +#define ANSI_COLOR_BLUE +#define ANSI_COLOR_MAGENTA +#define ANSI_COLOR_CYAN +#define ANSI_COLOR_RESET + +#endif + // clang-format off -#define BANNER \ -"" \ -" _____ _ _ _____ _ _____ _ \n"\ -" / ___| | | | | ___| | | |_ _| | | \n"\ -" \\ `--. _ __ ___ __ _| | | | |__ _ __ ___ _ _ __ _| |__ | | ___ ___| |_ ___ _ __ \n"\ -" `--. | '_ ` _ \\ / _` | | | | __| '_ \\ / _ \\| | | |/ _` | '_ \\ | |/ _ / __| __/ _ | '__|\n"\ -" /\\__/ | | | | | | (_| | | | | |__| | | | (_) | |_| | (_| | | | | | | __\\__ | || __| | \n"\ -" \\____/|_| |_| |_|\\__,_|_|_| \\____|_| |_|\\___/ \\__,_|\\__, |_| |_| \\_/\\___|___/\\__\\___|_| \n"\ -" __/ | \n"\ -" |___/ \n"\ -"" +#define BANNER \ + "" \ + " _____ _ _ _____ _ " \ + "_____ _ \n" \ + " / ___| | | | | ___| | | |_ " \ + "_| | | \n" \ + " \\ `--. _ __ ___ __ _| | | | |__ _ __ ___ _ _ __ _| |__ | " \ + "| ___ ___| |_ ___ _ __ \n" \ + " `--. | '_ ` _ \\ / _` | | | | __| '_ \\ / _ \\| | | |/ _` | '_ \\ " \ + " | |/ _ / __| __/ _ | '__|\n" \ + " /\\__/ | | | | | | (_| | | | | |__| | | | (_) | |_| | (_| | | | | | " \ + "| __\\__ | || __| | \n" \ + " \\____/|_| |_| |_|\\__,_|_|_| \\____|_| |_|\\___/ \\__,_|\\__, |_| " \ + "|_| \\_/\\___|___/\\__\\___|_| \n" \ + " __/ | " \ + " \n" \ + " |___/ " \ + " \n" \ + "" //clang-format on -char* format_string(const char* fmt, ...) +char *format_string(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - size_t size = vsnprintf(NULL, 0, fmt, args); - char* out = (char*)malloc(size + 1); + va_list args; + va_start(args, fmt); + size_t size = vsnprintf(NULL, 0, fmt, args); + char *out = (char *)malloc(size + 1); - if (!out) { - fprintf(stderr, "%s", "Failed to allocate for format string.\n"); - } + if (!out) + { + fprintf(stderr, "%s", "Failed to allocate for format string.\n"); + } - va_end(args); + va_end(args); - va_start(args, fmt); - vsnprintf(out, size + 1, fmt, args); - va_end(args); + va_start(args, fmt); + vsnprintf(out, size + 1, fmt, args); + va_end(args); - return out; + return out; } -static void log_test_start(const char* name) { printf("Running test: %s", name); } - -static void log_test_summary(struct SETest* test) +int create_shared_suit_space(size_t size) { - if (test->passed) { -#ifdef COLORIZED - printf(ANSI_COLOR_GREEN " - passed.\n" ANSI_COLOR_RESET); -#else - printf(" - passed.\n"); -#endif - } else { -#ifdef COLORIZED - printf(ANSI_COLOR_RED " - failed.\n" ANSI_COLOR_RESET " Reason: \n %s\n\n", - test->error_msg); -#else - printf(" - failed.\n Reason: \n %s\n\n", test->error_msg); -#endif - } -} + int suit_space_id = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600); -static void dispatch_tests(struct SETSuit* suit) -{ - printf("\n\n==== Running suit: %s ====\n\n", suit->name); - for (int i = 0; i < suit->len; i++) { - struct SETest* test = suit->tests[i]; - log_test_start(test->name); - test->function(test); - log_test_summary(test); - fflush(stdout); - } + if (suit_space_id == -1) + { + fprintf(stdout, "Couldn't create suite space of size: %lu\n", size); + perror("shmget"); + exit(1); + } + + return suit_space_id; } -static void free_suit(struct SETSuit* suit) +static void log_test_summary(struct SETest *test) { - for (int i = 0; i < suit->len; i++) { - struct SETest* test = suit->tests[i]; - free((void*)test->error_msg); - free((void*)test); - } - - free(suit->tests); - free(suit); + if (test->passed) + { + printf("Test: %s" ANSI_COLOR_GREEN " - passed." ANSI_COLOR_RESET"\n", + test->name); + } + else + { + printf("Test: %s" ANSI_COLOR_RED " - failed.\n" ANSI_COLOR_RESET + " Reason: \n %s\n\n", + test->name, test->error_msg); + } } - -static void dispatch_test_suits(struct SETSuit** suits, int counter) +static void log_abnormal_termination(int status) { - for (int i = 0; i < counter; i++) - { - dispatch_tests(suits[i]); - free_suit(suits[i]); - } + fprintf(stderr, + ANSI_COLOR_RED "A test terminated with signal: %d" ANSI_COLOR_RESET, + WTERMSIG(status)); } -int main(int argc, char** argv) +static void dispatch_single_test(struct SETest *test) { - #ifndef NO_BANNER - - printf("\n\n%s", BANNER); - #endif /* ifndef NO_BANNER */ - - int counter = 0; + pid_t testPID = fork(); - set_bundle_suits(NULL, &counter, true); - - struct SETSuit ** suits = (struct SETSuit**)malloc(counter * sizeof(struct SETSuit*)); - - counter = 0; - - set_bundle_suits(suits, &counter, false); - - dispatch_test_suits(suits, counter); - - free(suits); + if (testPID == 0) + { + test->function(test); + log_test_summary(test); + exit(0); + } +} + +static void dispatch_tests(struct SETSuit *suit) +{ + printf("\n\n==== Running suit: %s (%d) ====\n\n", suit->name, suit->len); + struct SETest *test = suit->tests; + for (int i = 0; i < suit->len; i++) + { + dispatch_single_test(&test[i]); + } + + int status = 0; + // Wait for all tests. + while (wait(&status) > 0) + { + if (WTERMSIG(status) != 0) + log_abnormal_termination(status); + }; + if (shmdt(suit->tests) < 0) + { + fprintf(stderr, "%s\n", "Couldn't detach suit space after dispatch."); + perror("shmat"); + exit(1); + } + if (shmctl(suit->shm_key, IPC_RMID, 0) == -1) + { + fprintf(stderr, "%s\n", "Couldn't remove suit space after dispatch."); + perror("shctl"); + exit(1); + } +} + +static void dispatch_test_suits(struct SETSuit **suits, int counter) +{ + for (int i = 0; i < counter; i++) + { + dispatch_tests(suits[i]); + munmap((void*)suits[i], sizeof(struct SETSuit)); + } +} + +int main(int argc, char **argv) +{ +#ifndef NO_BANNER + + printf("\n\n%s", BANNER); +#endif /* ifndef NO_BANNER */ + + int counter = 0; + + set_bundle_suits(NULL, &counter, true); + + struct SETSuit *suits[counter]; + + counter = 0; + + set_bundle_suits(suits, &counter, false); + + dispatch_test_suits(suits, counter); } diff --git a/set.h b/set.h index b72aa09..fc21800 100644 --- a/set.h +++ b/set.h @@ -1,24 +1,30 @@ #include #include #include +#include +#include #ifndef INCLUDE_SET_H #define INCLUDE_SET_H +#define SET_MAX_ERROR_MSG_SIZE 256 +#define SET_MAX_NAME_SIZE 64 + struct SETest { - char *name; void (*function)(struct SETest *test); + const char *error_msg; + const char *name; bool passed; - char *error_msg; }; struct SETSuit { - char *name; - struct SETest **tests; - bool passed; + const char *name; + struct SETest *tests; int len; + int shm_key; + bool passed; }; char *format_string(const char *fmt, ...); @@ -26,6 +32,8 @@ char *format_string(const char *fmt, ...); // To be implemented by user. void set_bundle_suits(struct SETSuit **suits, int *counter, bool count); +int create_shared_suit_space(size_t size); + #define BUNDLE() \ void set_bundle_suits(struct SETSuit **suits, int *counter, bool count) @@ -36,16 +44,28 @@ void set_bundle_suits(struct SETSuit **suits, int *counter, bool count); } \ (*counter)++; +// We manually allocate suits via mmap be able to mark them DONTFORK. +// Since we don't need the suits in a test fork. #define SUIT(suit_name) \ void suit_name##_suit(struct SETSuit *suit, bool count); \ struct SETSuit *suit_name##_suit_contructor() \ { \ struct SETSuit *suit = \ - (struct SETSuit *)malloc(sizeof(struct SETSuit)); \ - suit->name = #suit_name; \ + mmap(NULL, sizeof(struct SETSuit), PROT_READ | PROT_WRITE, \ + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \ + madvise(suit, sizeof(struct SETSuit), MADV_DONTFORK); \ + suit->len = 0; \ suit_name##_suit(suit, true); \ - suit->tests = \ - (struct SETest **)malloc(suit->len * sizeof(struct SETest *)); \ + suit->shm_key = \ + create_shared_suit_space(suit->len * sizeof(struct SETest)); \ + suit->name = #suit_name; \ + suit->tests = shmat(suit->shm_key, 0, 0); \ + if (suit->tests == (struct SETest *)-1) \ + { \ + fprintf(stderr, "Couldn't attach suit space.\n"); \ + perror("shmat"); \ + exit(1); \ + } \ suit->len = 0; \ suit_name##_suit(suit, false); \ return suit; \ @@ -55,21 +75,19 @@ void set_bundle_suits(struct SETSuit **suits, int *counter, bool count); #define ADD_TEST(test_name) \ if (!count) \ { \ - suit->tests[suit->len] = test_name##_test_constructor(); \ + test_name##_test_constructor(&suit->tests[suit->len]); \ } \ suit->len++; // TEST MACRO #define TEST(test_name) \ void test_name##_test(struct SETest *test); \ - struct SETest *test_name##_test_constructor() \ + void test_name##_test_constructor(struct SETest *test) \ { \ - struct SETest *test = (struct SETest *)malloc(sizeof(struct SETest)); \ test->name = #test_name; \ test->function = &test_name##_test; \ test->passed = true; \ test->error_msg = NULL; \ - return test; \ } \ void test_name##_test(struct SETest *test) diff --git a/set_asserts.h b/set_asserts.h index 06cd0a3..32d9209 100644 --- a/set_asserts.h +++ b/set_asserts.h @@ -21,7 +21,7 @@ { \ test->passed = false; \ test->error_msg = \ - format_string("Expect %d to be %d\n", act, exp) return; \ + format_string("Expect %d to be %d.\n", act, exp) return; \ } #define ASSERT_EQ_MSG(exp, act, msg) \ diff --git a/test b/test new file mode 100755 index 0000000..884353f Binary files /dev/null and b/test differ diff --git a/testtest.c b/testtest.c index 6852684..876b394 100644 --- a/testtest.c +++ b/testtest.c @@ -1,13 +1,17 @@ #include "set.h" +#include + #include "set_asserts.h" int fac(int n) { - if (n <= 1) + if (n == 1) return 1; return n * fac(n - 1); } +void heavy_load() { sleep(5); } + TEST(Faculty_Basic) { ASSERT_EQ(fac(5), 120);