Viewing: smoke_test.exp
📄 smoke_test.exp (Read Only) ⬅ To go back
#!/usr/bin/expect -f
#
# AdrOS Automated Smoke Test via QEMU serial console
#
# Usage: expect tests/smoke_test.exp [smp_count] [timeout_sec]
#   smp_count  : number of CPUs (default: 4)
#   timeout_sec: max seconds to wait for full boot (default: 30)
#
# Exit codes:
#   0 = all checks passed
#   1 = test failure (missing expected output or PANIC detected)
#   2 = timeout (boot did not complete in time)

set smp [lindex $argv 0]
if {$smp eq ""} { set smp 4 }

set timeout_sec [lindex $argv 1]
if {$timeout_sec eq ""} { set timeout_sec 60 }

set iso "adros-x86.iso"
set disk "disk.img"
set serial_log "serial.log"

# Ensure disk image exists
if {![file exists $disk]} {
    exec dd if=/dev/zero of=$disk bs=1M count=4 2>/dev/null
}

# Remove old serial log
file delete -force $serial_log

# Start QEMU in background, serial to file
set qemu_pid [exec qemu-system-i386 \
    -smp $smp -boot d -cdrom $iso -m 128M -display none \
    -drive file=$disk,if=ide,format=raw \
    -nic user,model=e1000 \
    -serial file:$serial_log -monitor none \
    -no-reboot -no-shutdown &]

# Wait for QEMU to start writing
after 1000

# ---- Test definitions ----
# Each test is {description pattern}
set tests {
    {"Heap init"            "\\[HEAP\\] 8MB Buddy Allocator Ready."}
    {"PCI enumeration"      "\\[PCI\\] Enumerated"}
    {"ATA DMA init"         "\\[ATA-DMA\\] Ch0 initialized"}
    {"ATA DMA mode"         "\\[ATA\\] Channel 0: DMA mode."}
    {"SMP CPUs active"      "CPU\\(s\\) active."}
    {"User ring3 entry"     "\\[USER\\] enter ring3"}
    {"init.elf hello"       "\\[test\\] hello from init.elf"}
    {"open/read/close"      "\\[test\\] open/read/close OK"}
    {"overlay copy-up"      "\\[test\\] overlay copy-up OK"}
    {"lseek/stat/fstat"     "\\[test\\] lseek/stat/fstat OK"}
    {"dup2 restore"         "\\[test\\] dup2 restore tty OK"}
    {"kill SIGKILL"         "\\[test\\] kill\\(SIGKILL\\) OK"}
    {"poll pipe"            "\\[test\\] poll\\(pipe\\) OK"}
    {"select pipe"          "\\[test\\] select\\(pipe\\) OK"}
    {"ioctl tty"            "\\[test\\] ioctl\\(/dev/tty\\) OK"}
    {"job control"          "\\[test\\] job control \\(SIGTTIN/SIGTTOU\\) OK"}
    {"poll /dev/null"       "\\[test\\] poll\\(/dev/null\\) OK"}
    {"pty bidirectional"    "\\[test\\] pty OK"}
    {"setsid/setpgid"       "\\[test\\] setsid/setpgid/getpgrp OK"}
    {"sigaction SIGUSR1"    "\\[test\\] sigaction/kill\\(SIGUSR1\\) OK"}
    {"sigreturn"            "\\[test\\] sigreturn OK"}
    {"tmpfs/mount"          "\\[test\\] tmpfs/mount OK"}
    {"dev null"             "\\[test\\] /dev/null OK"}
    {"persist counter"      "\\[test\\] /persist/counter="}
    {"dev tty write"        "\\[test\\] /dev/tty write OK"}
    {"diskfs test"          "\\[test\\] /disk/test prev="}
    {"diskfs mkdir/unlink"  "\\[test\\] diskfs mkdir/unlink OK"}
    {"diskfs getdents"      "\\[test\\] diskfs getdents OK"}
    {"isatty"               "\\[test\\] isatty OK"}
    {"O_NONBLOCK"           "\\[test\\] O_NONBLOCK OK"}
    {"pipe2/dup3"           "\\[test\\] pipe2/dup3 OK"}
    {"chdir/getcwd"         "\\[test\\] chdir/getcwd OK"}
    {"*at syscalls"         "\\[test\\] \\*at OK"}
    {"rename/rmdir"         "\\[test\\] rename/rmdir OK"}
    {"getdents multi-fs"    "\\[test\\] getdents multi-fs OK"}
    {"brk heap"             "\\[test\\] brk OK"}
    {"mmap/munmap"          "\\[test\\] mmap/munmap OK"}
    {"clock_gettime"        "\\[test\\] clock_gettime OK"}
    {"dev zero"             "\\[test\\] /dev/zero OK"}
    {"dev random"           "\\[test\\] /dev/random OK"}
    {"procfs"               "\\[test\\] procfs OK"}
    {"pread/pwrite"         "\\[test\\] pread/pwrite OK"}
    {"ftruncate"            "\\[test\\] ftruncate OK"}
    {"symlink/readlink"     "\\[test\\] symlink/readlink OK"}
    {"access"               "\\[test\\] access OK"}
    {"sigprocmask"          "\\[test\\] sigprocmask/sigpending OK"}
    {"alarm SIGALRM"        "\\[test\\] alarm/SIGALRM OK"}
    {"shmget/shmat"         "\\[test\\] shmget/shmat/shmdt OK"}
    {"O_APPEND"             "\\[test\\] O_APPEND OK"}
    {"umask"                "\\[test\\] umask OK"}
    {"pipe capacity"        "\\[test\\] pipe capacity OK"}
    {"waitid"               "\\[test\\] waitid OK"}
    {"setitimer/getitimer"  "\\[test\\] setitimer/getitimer OK"}
    {"select regfile"       "\\[test\\] select regfile OK"}
    {"poll regfile"         "\\[test\\] poll regfile OK"}
    {"hard link"            "\\[test\\] hard link OK"}
    {"epoll"                "\\[test\\] epoll OK"}
    {"epollet"              "\\[test\\] epollet OK"}
    {"inotify"              "\\[test\\] inotify OK"}
    {"aio"                  "\\[test\\] aio OK"}
    {"nanosleep"            "\\[test\\] nanosleep OK"}
    {"CLOCK_REALTIME"       "\\[test\\] CLOCK_REALTIME OK"}
    {"dev urandom"          "\\[test\\] /dev/urandom OK"}
    {"proc cmdline"         "\\[test\\] /proc/cmdline OK"}
    {"CoW fork"             "\\[test\\] CoW fork OK"}
    {"readv/writev"         "\\[test\\] readv/writev OK"}
    {"fsync"                "\\[test\\] fsync OK"}
    {"truncate path"        "\\[test\\] truncate OK"}
    {"getuid/getgid"        "\\[test\\] getuid/getgid OK"}
    {"chmod"                "\\[test\\] chmod OK"}
    {"flock"                "\\[test\\] flock OK"}
    {"times"                "\\[test\\] times OK"}
    {"gettid"               "\\[test\\] gettid OK"}
    {"posix_spawn"          "\\[test\\] posix_spawn OK"}
    {"clock_ns precision"   "\\[test\\] clock_ns precision OK"}
    {"getppid"              "\\[test\\] getppid OK"}
    {"waitpid WNOHANG"      "\\[test\\] waitpid WNOHANG OK"}
    {"SIGSEGV handler"      "\\[test\\] SIGSEGV OK"}
    {"waitpid 100 children" "\\[test\\] waitpid OK \\(100 children"}
    {"lazy PLT"             "\\[test\\] lazy PLT OK"}
    {"PLT cached"           "\\[test\\] PLT cached OK"}
    {"PING network"         "\\[PING\\] .*received.*network OK"}
    {"echo execve"          "\\[echo\\] hello from echo"}
    {"setuid/setgid"        "\\[test\\] setuid/setgid OK"}
    {"fcntl F_GETFL/SETFL"  "\\[test\\] fcntl F_GETFL/F_SETFL OK"}
    {"fcntl FD_CLOEXEC"     "\\[test\\] fcntl FD_CLOEXEC OK"}
    {"sigsuspend"           "\\[test\\] sigsuspend OK"}
    {"orphan reparent"      "\\[test\\] orphan reparent OK"}
    {"proc PID cmdline"    "\\[test\\] /proc/PID/cmdline OK"}
    {"proc PID status"     "\\[test\\] /proc/PID/status OK"}
    {"dev console"         "\\[test\\] /dev/console OK"}
    {"multi-pty"           "\\[test\\] multi-pty OK"}
    {"dup standalone"      "\\[test\\] dup OK"}
    {"pipe EOF"            "\\[test\\] pipe EOF OK"}
    {"readdir /proc"       "\\[test\\] readdir /proc OK"}
    {"readdir /bin"        "\\[test\\] readdir /bin OK"}
    {"gettimeofday"        "\\[test\\] gettimeofday OK"}
    {"mprotect"            "\\[test\\] mprotect OK"}
    {"madvise"             "\\[test\\] madvise OK"}
    {"getrlimit/setrlimit" "\\[test\\] getrlimit/setrlimit OK"}
    {"uname"               "\\[test\\] uname OK"}
    {"SMP parallel fork"   "\\[test\\] SMP parallel fork OK"}
    {"LZ4 Frame decomp"    "\\[INITRD\\] LZ4"}
}

# ---- Poll serial.log for results ----
set start_time [clock seconds]
set passed 0
set failed 0
set panic 0
set total [llength $tests]

# Track which tests have passed
for {set i 0} {$i < $total} {incr i} {
    set test_passed($i) 0
}

proc check_log {} {
    global serial_log
    if {[file exists $serial_log]} {
        set fd [open $serial_log r]
        set content [read $fd]
        close $fd
        return $content
    }
    return ""
}

# Poll loop
while {1} {
    set elapsed [expr {[clock seconds] - $start_time}]
    if {$elapsed > $timeout_sec} {
        break
    }

    set log [check_log]

    # Check for PANIC
    if {[regexp {KERNEL PANIC} $log]} {
        set panic 1
        break
    }

    # Check for HEAP OOM
    if {[regexp {\[HEAP\] OOM} $log]} {
        set panic 1
        break
    }

    # Check each test
    set all_done 1
    for {set i 0} {$i < $total} {incr i} {
        if {$test_passed($i)} continue
        set pattern [lindex [lindex $tests $i] 1]
        if {[regexp $pattern $log]} {
            set test_passed($i) 1
        } else {
            set all_done 0
        }
    }

    if {$all_done} {
        break
    }

    after 1000
}

# Kill QEMU
catch {exec pkill -f "qemu-system-i386.*$iso" 2>/dev/null}
after 500

# ---- Report results ----
puts ""
puts "========================================="
puts "  AdrOS Smoke Test Results (SMP=$smp)"
puts "========================================="

if {$panic} {
    set log [check_log]
    puts ""
    puts "  *** KERNEL PANIC DETECTED ***"
    if {[regexp {Exception Number: (\d+)} $log _ exc]} {
        puts "  Exception: $exc"
    }
    if {[regexp {EIP: (0x[0-9A-Fa-f]+)} $log _ eip]} {
        puts "  EIP: $eip"
    }
    if {[regexp {PAGE FAULT at address: (0x[0-9A-Fa-f]+)} $log _ addr]} {
        puts "  Fault address: $addr"
    }
    puts ""
}

for {set i 0} {$i < $total} {incr i} {
    set desc [lindex [lindex $tests $i] 0]
    if {$test_passed($i)} {
        puts "  PASS  $desc"
        incr passed
    } else {
        puts "  FAIL  $desc"
        incr failed
    }
}

set elapsed [expr {[clock seconds] - $start_time}]
puts ""
puts "  $passed/$total passed, $failed failed ($elapsed sec)"

if {$panic} {
    puts "  RESULT: FAIL (PANIC)"
    exit 1
}

if {$failed > 0} {
    set elapsed_val [expr {[clock seconds] - $start_time}]
    if {$elapsed_val >= $timeout_sec} {
        puts "  RESULT: FAIL (TIMEOUT after ${timeout_sec}s)"
        exit 2
    }
    puts "  RESULT: FAIL"
    exit 1
}

puts "  RESULT: PASS"
exit 0