Finished infrastructure that runs all userspace tests.
authoralfongj <alfongj@gmail.com>
Thu, 1 May 2014 01:45:06 +0000 (18:45 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 5 May 2014 23:23:30 +0000 (16:23 -0700)
kern/Kconfig.testing
user/tests/Makefile
user/tests/example.c
user/tests/include/testing.h
user/tests/runall.sh

index d00726c..4787b5d 100644 (file)
@@ -253,7 +253,7 @@ menuconfig USERSPACE_TESTING
 config USERSPACE_TESTING_SCRIPT
     depends on USERSPACE_TESTING
     string "Path to test launcher script."
-    default fill/me/in
+    default /bin/tests/user/runall.sh
     help
         Run userspace unit tests from the specified path. 
 
index c21ba02..0bcc62d 100644 (file)
@@ -18,7 +18,7 @@ EXECS_CPP = $(patsubst $(SRCDIR)%.cc, $(OBJDIR)/%, $(SRCS_CPP))
 EXECS = $(EXECS_C) $(EXECS_CPP)
 
 STATIC := $(findstring static,$(CFLAGS_USER))
-$(OBJDIR)/%: $(SRCS_C)
+$(OBJDIR)/%: $(SRCS_C) $(HEADERS)
        @echo + cc [TESTS] $<
        @mkdir -p $(@D)
        $(Q)$(CC) $(CFLAGS_USER) -I$(INCDIR) -o $@ $< $(LDLIBS)
@@ -29,7 +29,7 @@ $(OBJDIR)/%: $(SRCS_C)
 
 # Note that we don't disassemble CPPs by default, even if they aren't static.
 # The files are pretty large regardless (9MB for a simple stream test asm).
-$(OBJDIR)/%: $(SRCS_CPP)
+$(OBJDIR)/%: $(SRCS_CPP) $(HEADERS)
        @echo + cc [TESTS] $<
        @mkdir -p $(@D)
        $(Q)$(CPP) $(CXXFLAGS_USER) -I$(INCDIR) -o $@ $< $(LDLIBS)
index f4b6a2b..87b7650 100644 (file)
@@ -1,3 +1,35 @@
 #include <testing.h>
 
-void main() {}
+TEST_SUITE("EXAMPLE");
+
+
+
+/* <--- Begin definition of test cases ---> */
+
+bool test_one(void) {
+       UT_ASSERT_M("One plus one should equal 2", 1+1 == 2);
+}
+
+bool test_two(void) {
+       UT_ASSERT_M("One minus one should equal 0", 1-1 == 0);
+}
+
+bool test_three(void) {
+       UT_ASSERT_M("One should equal 0", 1 == 0);
+}
+
+/* <--- End definition of test cases ---> */
+
+
+
+struct usertest usertests[] = {
+       U_TEST_REG(one),
+       U_TEST_REG(two),
+       U_TEST_REG(three) // This one will fail.
+};
+
+int main(int argc, char *argv[]) {
+       // Run test suite passing it all the args as whitelist of what tests to run.
+       RUN_TEST_SUITE(&argv[1], argc-1);
+}
+
index 3bbdeb2..515cc4f 100644 (file)
@@ -1,5 +1,123 @@
 #ifndef TESTS_TESTING_H
 #define TESTS_TESTING_H
 
-#endif // TESTS_TESTING_H
+/*
+ * Header file with infrastructure needed for userspace unit tests:
+ *  - Assertion functions.
+ *  - Test structures.
+ *  - Launcher functions for test suites.
+ */
+
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <timing.h>
+
+/* 
+ * Macros for assertions. 
+ * UT_ASSERT_M prints a message in case of failure, UT_ASSERT just asserts.
+ */
+#define UT_ASSERT_M(message, test)                                               \
+       do {                                                                         \
+               if (!(test)) {                                                           \
+                       char prefix[] = "Assertion failed: ";                                \
+                       int msg_size = sizeof(prefix) + sizeof(message) - 1;                 \
+                       test_msg = (char*) malloc(msg_size);                                 \
+                       snprintf(test_msg, msg_size, "%s%s", prefix, message);               \
+                       return false;                                                        \
+               }                                                                        \
+       } while (0)
+
+#define UT_ASSERT(test)                                                          \
+       do {                                                                         \
+               if (!(test)) {                                                           \
+                       return false;                                                        \
+               }                                                                        \
+       } while (0)
+
+
+
+/*
+ * Structs and macros for registering test cases.
+ */
+struct usertest {
+       char name[256]; // Name of the test function.
+       bool (*func)(void); // Name of the test function, should be equal to 'name'.
+       bool enabled; // Whether or not to run the test.
+};
+
+/* Used for defining an userspace test structure entry inline. */
+#define U_TEST_REG(name) \
+       {"test_" #name, test_##name, true}
+
+
+
+/*
+ * Creates all the runnable code for a test suite.
+ */
+#define TEST_SUITE(__suite_name)                                                 \
+       char *test_msg;                                                              \
+       char suite_name[] = __suite_name;
 
+#define RUN_TEST_SUITE(whitelist, whitelist_len)                                 \
+       int num_tests = sizeof(usertests) / sizeof(struct usertest);                 \
+       testing_main(whitelist, whitelist_len, usertests, num_tests);
+
+/* Disables all the tests not passed through a whitelist. */
+static void apply_whitelist(char *whitelist[], int whitelist_len,
+                               struct usertest tests[], int num_tests) {
+       for (int i=0; i<num_tests; i++) {
+               struct usertest *test = &tests[i];
+               if (test->enabled) {
+                       for (int j = 1; j < whitelist_len; ++j) {
+                               test->enabled = false;
+                               if (strcmp(test->name, whitelist[j]) == 0) {
+                                       test->enabled = true;
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+
+
+static int testing_main(char *whitelist[], int whitelist_len, 
+                           struct usertest tests[], int num_tests) {
+       extern char *test_msg, suite_name[];
+       printf("<-- BEGIN_USERSPACE_%s_TESTS -->\n", suite_name);
+
+       // If any arguments are passed, treat them as a whitelist of what tests
+       // to run (i.e., disable all the rest).
+       if (whitelist_len > 0) {
+               apply_whitelist(whitelist, whitelist_len, tests, num_tests);
+       }
+
+       // Run tests.
+       for (int i=0; i<num_tests; i++) {
+               struct usertest *test = &tests[i];
+               if (test->enabled) {
+                       uint64_t start = read_tsc();
+                       bool result = test->func();
+                       uint64_t end = read_tsc();
+                       uint64_t et_us = tsc2usec(end - start) % 1000000;
+                       uint64_t et_s = tsc2sec(end - start);
+
+                       if (result) {
+                               printf("\tPASSED   [%s](%llu.%06llus)\n", test->name, et_s, 
+                                      et_us);
+                       } else {
+                               printf("\tFAILED   [%s](%llu.%06llus)  %s\n", test->name, et_s, 
+                                      et_us, test_msg);
+                               free(test_msg);
+                       }
+               } else {
+                       printf("\tDISABLED [%s]\n", test->name);
+               }
+       }
+
+       printf("<-- END_USERSPACE_%s_TESTS -->\n", suite_name);
+}
+
+#endif // TESTS_TESTING_H
index e69de29..b881ecf 100644 (file)
@@ -0,0 +1,12 @@
+#!/bin/ash
+#
+# runall
+#
+# Runs all userspace tests
+TEST_DIR=/bin/tests/user
+
+# Run all test suites in test directory.
+for file in $TEST_DIR/*[!.sh]
+do
+       $file
+done