From 6d4c0a2e0c727e112c7c33d304452315d8c87d52 Mon Sep 17 00:00:00 2001
From: Brendan Bartels <bbartels@iastate.edu>
Date: Thu, 9 Feb 2017 23:02:08 -0600
Subject: [PATCH] quad: add testing suite

---
 quad/lib/test/.gitignore |  1 +
 quad/lib/test/Makefile   |  5 ++++
 quad/lib/test/README.md  | 30 ++++++++++++++++++++
 quad/lib/test/example.c  | 24 ++++++++++++++++
 quad/lib/test/test.c     | 61 ++++++++++++++++++++++++++++++++++++++++
 quad/lib/test/test.h     | 19 +++++++++++++
 6 files changed, 140 insertions(+)
 create mode 100644 quad/lib/test/.gitignore
 create mode 100644 quad/lib/test/Makefile
 create mode 100644 quad/lib/test/README.md
 create mode 100644 quad/lib/test/example.c
 create mode 100644 quad/lib/test/test.c
 create mode 100644 quad/lib/test/test.h

diff --git a/quad/lib/test/.gitignore b/quad/lib/test/.gitignore
new file mode 100644
index 000000000..96236f815
--- /dev/null
+++ b/quad/lib/test/.gitignore
@@ -0,0 +1 @@
+example
\ No newline at end of file
diff --git a/quad/lib/test/Makefile b/quad/lib/test/Makefile
new file mode 100644
index 000000000..d44ec7765
--- /dev/null
+++ b/quad/lib/test/Makefile
@@ -0,0 +1,5 @@
+example: example.c test.c test.h
+	gcc -g -o example example.c test.c test.h
+
+clean:
+	rm example
diff --git a/quad/lib/test/README.md b/quad/lib/test/README.md
new file mode 100644
index 000000000..8bec810e2
--- /dev/null
+++ b/quad/lib/test/README.md
@@ -0,0 +1,30 @@
+Basic Testing Suite in C
+----
+This test suite helps you run tests. It handles the result of every test
+whether it be a success, failure, or segfault, and keeps running until
+all tests have been executed. It then gives a summary of the test results.
+
+To use, just write your tests using functions that return `int`s to indicate
+failure. 1 means failure. 0 means success.
+
+Then in your main function for your tests, pass these functions to the `test()`
+function along with a name you want included in the test report.
+
+```c
+int main() {
+    test(test_func, "this test will pass!");
+    test(another_func, "this one might fail...");
+    ...
+```
+
+Then at the end of your main function, call the `test_summary()` function, and
+return its return value from your main function.
+
+```c
+int main() {
+    ...
+    return test_summary();
+}
+```
+
+An `example.c` file is included for reference.
\ No newline at end of file
diff --git a/quad/lib/test/example.c b/quad/lib/test/example.c
new file mode 100644
index 000000000..b757c6e5d
--- /dev/null
+++ b/quad/lib/test/example.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include "test.h"
+
+int test1() {
+  puts("hi world.");
+  return 0;
+}
+
+int test2() {
+  return 1;
+}
+
+int test3() {
+  int *bad = NULL;
+  int x = *bad;
+  return 0;
+}
+
+int main() {
+  test(test1, "print hello world");
+  test(test2, "just fail");
+  test(test3, "survive segfault");
+  return test_summary();
+}
diff --git a/quad/lib/test/test.c b/quad/lib/test/test.c
new file mode 100644
index 000000000..dc2ef00ef
--- /dev/null
+++ b/quad/lib/test/test.c
@@ -0,0 +1,61 @@
+#include "test.h"
+
+static int num_tests = 0;
+static struct Test tests[128];
+static int longest_test_name = 0;
+
+void test(int (*function)(), char *test_name) {
+  int test_name_length = strlen(test_name);
+  if (test_name_length > longest_test_name) {
+    longest_test_name = test_name_length;
+  }
+
+  pid_t pid = fork();
+  if (pid == 0) {
+    // test process
+    int exit_status = function();
+    exit(exit_status);
+  } else {
+    int status;
+    waitpid(pid, &status, 0);
+
+    struct Test test;
+    strcpy(test.name, test_name);
+
+    if (WIFEXITED(status)) {
+      int exit_status = WEXITSTATUS(status);
+      if (exit_status == 0) {
+	test.failed = 0;
+	strcpy(test.result_msg, "passed");
+      } else {
+	test.failed = 1;
+	strcpy(test.result_msg,  "FAILED");
+      }
+    } else if (WIFSIGNALED(status)) {
+      test.failed = 1;
+      strcpy(test.result_msg, "ERROR!");
+    }
+
+    tests[num_tests++] = test;
+  }
+}
+
+int test_summary() {
+  unsigned char at_least_one_test_failed = 0;
+  int num_failed = 0;
+  puts("---------------------------------");
+  puts("Test results:");
+  puts("");
+  int i = 0;
+  for (i = 0; i < num_tests; i += 1) {
+    printf("#%3d: %-*s (%s)\n", i + 1, longest_test_name, tests[i].name, tests[i].result_msg);
+    num_failed += tests[i].failed;
+    at_least_one_test_failed |= tests[i].failed;
+  }
+  puts("");
+  printf("Total: %3d of %-3d tests passed\n", num_tests - num_failed, num_tests);
+  puts("---------------------------------");
+  num_tests = 0;
+  longest_test_name = 0;
+  return at_least_one_test_failed;
+}
diff --git a/quad/lib/test/test.h b/quad/lib/test/test.h
new file mode 100644
index 000000000..862769fc3
--- /dev/null
+++ b/quad/lib/test/test.h
@@ -0,0 +1,19 @@
+#ifndef TEST_H
+#define TEST_H
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+struct Test {
+  char name[64];
+  char result_msg[32];
+  unsigned char failed;
+};
+
+void test(int (*)(), char *);
+int test_summary();
+
+#endif
-- 
GitLab