Add a testing framework.

This is an automated system by which we boot qemu headless and use the
serial line to capture output from a testing application that is started
on bootup, running with the VGA terminal shell. This might be expanded
to boot to the graphical display within VNC and perform more advanced
tests with the Python shim using a VNC module for Python; we'll see.
This commit is contained in:
Kevin Lange 2012-09-02 02:24:25 -07:00
parent 9928a2b4cf
commit bba242dd62
10 changed files with 149 additions and 22 deletions

View File

@ -14,4 +14,5 @@ make || exit 1
popd
# Build the kernel
make system || exit 1 # to build the kernel
# XXX: Attempt to boot the kernel with qemu automatically...
# Boot it up and run some tests
make test || exit 1

View File

@ -58,7 +58,7 @@ install: system
run: system
${EMU} ${EMUARGS} -append "vid=qemu hdd"
kvm: system
${EMU} ${EMUARGS} ${EMUKVM} -append "vid=qemu hdd"
${EMU} ${EMUARGS} ${EMUKVM} -append "optirun vid=qemu hdd"
vga: system
${EMU} ${EMUARGS} -append "vgaterm hdd"
vga-kvm: system
@ -70,6 +70,9 @@ term-kvm: system
run-config: system
util/config-parser | xargs ${EMU}
test: system
python util/run-tests.py 2>/dev/null
utils: ${UTILITIES}
.passed:

View File

@ -39,7 +39,9 @@ extern unsigned int __irq_sem;
extern void * code;
extern void * end;
extern char * boot_arg;
extern char * boot_arg; /* Argument to pass to init */
extern char * boot_arg_extra; /* Extra data to pass to init */
extern void *sbrk(uintptr_t increment);

View File

@ -144,6 +144,7 @@ int main(struct multiboot *mboot, uint32_t mboot_mag, uintptr_t esp) {
char * argv[] = {
"/bin/init",
boot_arg,
boot_arg_extra,
NULL
};
int argc = 0;

View File

@ -63,8 +63,8 @@ parse_args(
} else {
x = atoi(argp[2]);
y = atoi(argp[3]);
kprintf("[video] Requested display resolution is %dx%d\n", x, y);
}
kprintf("--> Requested display resolution is %dx%d\n", x, y);
if (!strcmp(argp[1],"qemu")) {
/* Bochs / Qemu Video Device */
graphics_install_bochs(x,y);
@ -80,6 +80,9 @@ parse_args(
boot_arg = "--single";
} else if (!strcmp(argp[0],"vgaterm")) {
boot_arg = "--vga";
} else if (!strcmp(argp[0],"start")) {
if (argc < 2) { kprintf("start=?\n"); continue; }
boot_arg_extra = argp[1];
}
}
}

View File

@ -22,6 +22,7 @@ void spin_unlock(uint8_t volatile * lock) {
}
char * boot_arg = NULL;
char * boot_arg_extra = NULL;
/*
* memcpy

30
userspace/core-tests.c Normal file
View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdarg.h>
#include <syscall.h>
#define INFO(...) notice("INFO", __VA_ARGS__)
#define WARN(...) notice("WARN", __VA_ARGS__)
#define DONE(...) notice("DONE", __VA_ARGS__)
#define PASS(...) notice("PASS", __VA_ARGS__)
#define FAIL(...) notice("FAIL", __VA_ARGS__)
#define FATAL(...) notice("FATAL", __VA_ARGS__)
void notice(char * type, char * fmt, ...) {
va_list argp;
va_start(argp, fmt);
/* core-tests header */
syscall_print("core-tests : ");
syscall_print(type);
syscall_print(" : ");
/* end core-tests header */
char buffer[1024];
vsnprintf(buffer, 1024, fmt, argp);
syscall_print(buffer);
syscall_print("\n");
}
int main(int argc, char * argv[]) {
INFO("Hello world!");
DONE("Finished tests!");
}

View File

@ -33,12 +33,13 @@ void set_hostname() {
}
}
void start_terminal() {
void start_terminal(char * arg) {
int pid = fork();
if (!pid) {
char * tokens[] = {
"/bin/terminal",
"-F",
arg,
NULL
};
int i = execve(tokens[0], tokens, NULL);
@ -48,12 +49,13 @@ void start_terminal() {
}
}
void start_vga_terminal() {
void start_vga_terminal(char * arg) {
int pid = fork();
if (!pid) {
char * tokens[] = {
"/bin/terminal",
"-Vl",
arg,
NULL
};
int i = execve(tokens[0], tokens, NULL);
@ -78,17 +80,22 @@ void start_compositor() {
}
void main(int argc, char * argv[]) {
int main(int argc, char * argv[]) {
fprintf(stderr, "[init] Hello world.\n");
/* Hostname */
set_hostname();
if (argc > 1 && !strcmp(argv[1],"--single")) {
/* Terminal */
start_terminal();
} else if (argc > 1 && !strcmp(argv[1],"--vga")) {
start_vga_terminal();
} else {
/* Compositor */
start_compositor();
if (argc > 1) {
char * args = NULL;
if (argc > 2) {
args = argv[2];
}
if (!strcmp(argv[1],"--single")) {
start_terminal(args);
return 0;
} else if (!strcmp(argv[1], "--vga")) {
start_vga_terminal(args);
return 0;
}
}
start_compositor();
}

View File

@ -34,6 +34,10 @@
#include "lib/decorations.h"
#include "lib/pthread.h"
#ifndef strtok_r
char *strtok_r(char *s1, const char *s2, char **s3);
#endif
#define FONT_SIZE 13
#define MOUSE_SCALE 6
@ -3152,15 +3156,24 @@ int main(int argc, char ** argv) {
syscall_dup2(ifd, 0);
syscall_dup2(ofd, 1);
syscall_dup2(ofd, 2);
/*
* TODO: Check the public-readable passwd file to select which shell to run
*/
if (_login_shell) {
char * tokens[] = {"/bin/login",NULL};
if (argv[optind] != NULL) {
char * tokens[] = {argv[optind], NULL};
int i = execve(tokens[0], tokens, NULL);
printf("Failed to execute requested startup application `%s`!\n", argv[optind]);
printf("Your system is now unusable, and a restart will not be attempted.\n");
syscall_print("core-tests : FATAL : Could not execute the core-tests binary. This is a fatal error.\n");
} else {
char * tokens[] = {"/bin/esh",NULL};
int i = execve(tokens[0], tokens, NULL);
/*
* TODO: Check the public-readable passwd file to select which shell to run
*/
if (_login_shell) {
char * tokens[] = {"/bin/login",NULL};
int i = execve(tokens[0], tokens, NULL);
} else {
char * tokens[] = {"/bin/esh",NULL};
int i = execve(tokens[0], tokens, NULL);
}
}
exit_application = 1;

66
util/run-tests.py Normal file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env python
import subprocess, sys
q = subprocess.Popen(['qemu', '-kernel', 'toaruos-kernel', '-m', '256',
'-serial', 'stdio', '-vga', 'std', '-hda', 'toaruos-disk.img',
'-vnc', ':1', '-append', 'vgaterm hdd start=/bin/core-tests'],
stdout=subprocess.PIPE)
passes = 0
failures = 0
result = 0
def process_line(line):
global passes, failures
data = line.strip().split(" : ")
if data[1] == "FAIL":
color = "1;31"
text = "fail"
failures += 1
elif data[1] == "WARN":
color = "1;33"
text = "warn"
elif data[1] == "PASS":
color = "1;32"
text = "pass"
passes += 1
elif data[1] == "INFO":
color = "1;34"
text = "info"
elif data[1] == "DONE":
color = "1;36"
text = "Done!"
elif data[1] == "FATAL":
color = "1;37;41"
text = "FATAL ERROR ECOUNTERED"
print "\033[%sm%s\033[0m %s" % (color, text, data[2])
if data[1] == "FATAL":
return 2
elif data[1] == "DONE":
return 1
return 0
def log_line(line):
print >>sys.stderr, line.strip()
while q.poll() == None:
line = q.stdout.readline()
if line:
if line.startswith("core-tests :"):
result = process_line(line)
if result > 0:
q.kill()
try:
subprocess.call(["stty","echo"])
except:
pass
if result == 2:
sys.exit(2)
else:
log_line(line)
print "\033[1mTest completed. \033[1;32m%d passes\033[0m, \033[1;31m%d failures\033[0m." % (passes, failures)
if failures > 0:
sys.exit(1)