#include <config.h>
+#define UNITTESTS 1
+
#endif
inc_dir = $(top_srcdir)/include
-corbenik_SOURCES = patch/reboot.c patch/svc.c patch/module.c patch/emunand.c main.c std/fs.c std/draw.c std/memory.c std/abort.c std/allocator.c menu.c firm/util.c firm/keys.c firm/firmlaunch.c firm/version.c firm/firm.c firm/decryptor.c interpreter.c input.c patcher.c chainloader.c config-backend-file.c menu-backend.c start.s interrupt.c arm11.c
+corbenik_SOURCES = patch/reboot.c patch/svc.c patch/module.c patch/emunand.c main.c std/fs.c std/draw.c std/memory.c std/abort.c std/allocator.c menu.c firm/util.c firm/keys.c firm/firmlaunch.c firm/version.c firm/firm.c firm/decryptor.c interpreter.c input.c patcher.c chainloader.c config-backend-file.c menu-backend.c start.s interrupt.c arm11.c test-framework.c
void chainload_menu();
#endif
+#if defined(UNITTESTS) && UNITTESTS == 1
+void run_test_harness();
+#endif
+
#ifndef REL
#define REL "master"
#endif
{ "Chainload",
"Boot another ARM9 payload file.",
option, 0, chainload_menu, NULL, 0, 0 },
+#endif
+#if defined(UNITTESTS) && UNITTESTS == 1
+ { "Unit tests",
+ "Runs through a number of sanity and regression tests to check for defects.",
+ option, 0, run_test_harness, NULL, 0, 0 },
#endif
{ "Boot Firmware",
"Patches the firmware, and boots it.",
--- /dev/null
+#include <corbconf.h>
+#if defined(UNITTESTS) && UNITTESTS == 1
+
+#include <common.h>
+
+uint32_t total;
+uint32_t pass;
+uint32_t fail;
+uint32_t skip;
+
+void test(int value, const char* name) {
+ fprintf(stderr, "%s: ", name);
+
+ switch(value) {
+ case 0:
+ fprintf(stderr, "pass\n");
+ ++pass;
+ break;
+ case 1:
+ fprintf(stderr, "fail\n");
+ ++fail;
+ break;
+ default:
+ fprintf(stderr, "skip\n");
+ ++skip;
+ break;
+ }
+ ++total;
+}
+
+int assert_null(void* ptr) {
+ if (ptr == NULL)
+ return 0;
+ return 1;
+}
+
+int assert_nonnull(void* ptr) {
+ if (ptr != NULL)
+ return 0;
+ return 1;
+}
+
+int assert_int(int input, int value) {
+ if (input != value)
+ return 1;
+ return 0;
+}
+
+int assert_u32(uint32_t input, uint32_t value) {
+ if (input != value)
+ return 1;
+ return 0;
+}
+
+int assert_array_eq(uint8_t* input, uint8_t* test, uint32_t len) {
+ for (uint32_t i=0; i < len; i++) {
+ if (input[i] != test[i])
+ return 1;
+ }
+ return 0;
+}
+
+// We do NOT want the test harness optimized whatsoever.
+
+void __attribute__((optimize("O0"))) run_test_harness() {
+ total = pass = fail = skip = 0;
+
+ #include "test/basic-sanity.test"
+ #include "test/file.test"
+// #include "test/firmware.test"
+// #include "test/vm.test"
+// #include "test/vm-patch.test"
+
+ fprintf(stderr, "= Tests =\n"
+ "Total: %lu\n"
+ "Pass: %lu\n"
+ "Fail: %lu\n"
+ "Skip: %lu\n",
+ total, pass, fail, skip);
+}
+
+#endif
--- /dev/null
+{
+ uint8_t memory0[8] = "reverse";
+ uint8_t memory1[8] = "forward";
+ uint8_t memory_out[8] = "forward";
+
+ // Test for inequivalence.
+ test(assert_int(!memcmp(memory0, memory1, 8), 0), "Inverted memcmp");
+
+ // Test for equivalence.
+ test(assert_int(memcmp(memory1, memory_out, 8), 0), "memcmp");
+
+ // Test sanity of memcpy.
+ memcpy(memory1, memory0, 8);
+ test(assert_int(memcmp(memory0, memory1, 8), 0), "memcpy");
+}
--- /dev/null
+{
+ FILE* file_handle = NULL;
+ char filename[] = "/lolidontexistwowlol";
+
+ // Test; open nonexistent file for read.
+ file_handle = fopen(filename, "r");
+
+ test(assert_null(file_handle), "Nonexistent file");
+
+ // Test; open nonexistent file for write.
+ file_handle = fopen(filename, "w");
+
+ test(assert_nonnull(file_handle), "Open for writing");
+
+ // Test; write string. Close file, check length, compare equality.
+ char data_str[] = "LOOK AT THIS STRING; It Should be equal.";
+ char data_str2[] = " ";
+
+ test(assert_u32(fwrite(data_str, 1, sizeof(data_str), file_handle), sizeof(data_str)), "fwrite return");
+ fclose(file_handle);
+
+ file_handle = fopen(filename, "r");
+ test(assert_nonnull(file_handle), "Open for read");
+
+ test(assert_u32(fread(data_str2, 1, sizeof(data_str), file_handle), sizeof(data_str)), "fread return");
+
+ test(assert_int(memcmp(data_str, data_str2, sizeof(data_str)), 0), "read data");
+
+ fclose(file_handle);
+
+ // Test removal.
+
+ f_unlink(filename);
+
+ file_handle = fopen(filename, "r");
+ test(assert_null(file_handle), "Remove file");
+}