init: os161-bas2-2.02
This commit is contained in:
12
testscripts/Makefile
Normal file
12
testscripts/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# Makefile for src/testscripts (test driver tools)
|
||||
#
|
||||
|
||||
TOP=..
|
||||
.include "$(TOP)/mk/os161.config.mk"
|
||||
|
||||
SCRIPTDIR=/testscripts
|
||||
EXECSCRIPTS=test.py
|
||||
NONEXECSCRIPTS=runtest.py
|
||||
|
||||
.include "$(TOP)/mk/os161.script.mk"
|
||||
202
testscripts/runtest.py
Normal file
202
testscripts/runtest.py
Normal file
@@ -0,0 +1,202 @@
|
||||
#
|
||||
# Usage:
|
||||
# import runtest
|
||||
# runtest.run(testcommands, outputfile,
|
||||
# menuprompt=None, default "OS/161 kernel [? for menu]: "
|
||||
# shellprompt=None, default "OS/161$ "
|
||||
# conf=None, default is sys161 default behavior
|
||||
# ram=None, default is per sys161 config
|
||||
# cpus=None, default is per sys161 config
|
||||
# doom=None, default is no doom counter
|
||||
# progress=30, default is 30 seconds
|
||||
# timeout=300, default is 300 seconds
|
||||
# kernel=None) default is "kernel"
|
||||
#
|
||||
# Returns None on success or a (string) message if something apparently
|
||||
# went wrong in the middle. (XXX: should it throw exceptions instead?)
|
||||
#
|
||||
# * The testcommands argument is a string containing a list of commands
|
||||
# separated by semicolons. These can be either kernel menu commands
|
||||
# or shell commands; the command 's' is recognized for switching from
|
||||
# the menu to the shell and 'exit' for switching back to the menu.
|
||||
# (This affects waiting for prompts - running the shell via 'p' or
|
||||
# crashing out of the shell will confuse things.)
|
||||
#
|
||||
# The command 'q' from the menu is also recognized as causing a
|
||||
# shutdown. This will be done automatically after everything else if
|
||||
# not issued explicitly.
|
||||
#
|
||||
# The following commands are interpreted as macros:
|
||||
# DOMOUNT expands to "mount sfs lhd1:; cd lhd1:"
|
||||
# DOUNMOUNT expands to "cd /; unmount lhd1:"
|
||||
# WAIT sleeps 3 seconds and just presses return
|
||||
#
|
||||
# * The outputfile argument should be a python file (e.g. sys.stdout)
|
||||
# and receives a copy of the System/161 output.
|
||||
#
|
||||
# * The menuprompt and shellprompt arguments can be used to change the
|
||||
# menu and shell prompt strings looked for. For the moment these can
|
||||
# only be fixed strings, not regular expressions. (This is probably
|
||||
# easy to improve, but I ran into some mysterious problems when I
|
||||
# tried, so YMMV.) By default if you pass None prompt strings matching
|
||||
# what OS/161 issues by default are used.
|
||||
#
|
||||
# * The conf argument can be used to supply an alternate sys161.conf
|
||||
# file. If None is given (the default), sys161 will use its default
|
||||
# config file.
|
||||
#
|
||||
# * The ram and cpus arguments can be used to override the RAM size
|
||||
# and number-of-cpus settings in the sys161 config file. The number of
|
||||
# cpus must be an integer, but any RAM size specification understood
|
||||
# by sys161 can be used. Note: this feature requires System/161 2.0.5
|
||||
# or higher.
|
||||
#
|
||||
# * The doom argument can be used to set the doom counter. If None is
|
||||
# given (the default) the doom counter is not engaged.
|
||||
#
|
||||
# * The progress and timeout arguments can be used to set the timeouts
|
||||
# for System/161 progress monitoring and pexpect-level global timeout,
|
||||
# respectively. The defaults (somewhat arbitraily chosen) are 30 and
|
||||
# 300 seconds. Passing progress=None disables progress monitoring; this
|
||||
# is necessary for nontrivial tests that run within the kernel, as
|
||||
# progress monitoring measures userland progress. Passing timeout=None
|
||||
# probably either disables the global timeout or makes pexpect crash;
|
||||
# I haven't tested it. I don't recommend trying: it is your defense
|
||||
# against test runs hanging forever.
|
||||
#
|
||||
# Note that no-debugger unattended mode (sys161 -X) is always used.
|
||||
# The purpose of this script is specifically to support unattended
|
||||
# test runs...
|
||||
#
|
||||
# Depends on pexpect, which you may need to install specifically
|
||||
# depending on your OS.
|
||||
#
|
||||
|
||||
import time
|
||||
import pexpect
|
||||
|
||||
#
|
||||
# Macro commands
|
||||
#
|
||||
macros = {
|
||||
"MOUNT" : ["mount sfs lhd1:", "cd lhd1:"],
|
||||
"UNMOUNT" : ["cd /", "unmount lhd1:"],
|
||||
# "WAIT" special-cased below
|
||||
}
|
||||
|
||||
#
|
||||
# Wait for a prompt; returns True if we got it, False if we need to
|
||||
# bail.
|
||||
#
|
||||
def getprompt(proc, prompt):
|
||||
which = proc.expect_exact([
|
||||
prompt,
|
||||
"panic: ", # panic message
|
||||
"sys161: No progress in ", # sys161 deadman print
|
||||
"sys161: Elapsed ", # sys161 shutdown print
|
||||
pexpect.EOF,
|
||||
pexpect.TIMEOUT
|
||||
])
|
||||
if which == 0:
|
||||
# got the prompt
|
||||
return None
|
||||
if which == 1:
|
||||
proc.expect_exact([pexpect.EOF, pexpect.TIMEOUT])
|
||||
return "panic"
|
||||
if which == 2:
|
||||
proc.expect_exact([pexpect.EOF, pexpect.TIMEOUT])
|
||||
return "progress timeout"
|
||||
if which == 3:
|
||||
proc.expect_exact([pexpect.EOF, pexpect.TIMEOUT])
|
||||
return "unexpected shutdown"
|
||||
if which == 4:
|
||||
return "unexpected end of input"
|
||||
if which == 5:
|
||||
return "top-level timeout"
|
||||
return "runtest: Internal error: pexpect returned out-of-range result"
|
||||
# end getprompt
|
||||
|
||||
#
|
||||
# main test function
|
||||
#
|
||||
def run(testcommands, outputfile,
|
||||
menuprompt=None, shellprompt=None,
|
||||
conf=None, ram=None, cpus=None,
|
||||
doom=None,
|
||||
progress=30, timeout=300,
|
||||
kernel=None):
|
||||
if menuprompt is None:
|
||||
menuprompt = "OS/161 kernel [? for menu]: "
|
||||
if shellprompt is None:
|
||||
shellprompt = "OS/161$ "
|
||||
if kernel is None:
|
||||
kernel = "kernel"
|
||||
|
||||
args = ["-X"]
|
||||
if conf is not None:
|
||||
args.append("-c")
|
||||
args.append(conf)
|
||||
if cpus is not None:
|
||||
args.append("-C")
|
||||
args.append("31:cpus=%d" % cpus)
|
||||
if doom is not None:
|
||||
args.append("-D")
|
||||
args.append("%d" % doom)
|
||||
if progress is not None:
|
||||
args.append("-Z")
|
||||
args.append("%d" % progress)
|
||||
if ram is not None:
|
||||
args.append("-C")
|
||||
args.append("31:ramsize=%s" % ram)
|
||||
args.append(kernel)
|
||||
|
||||
proc = pexpect.spawn("sys161", args, timeout=timeout,
|
||||
ignore_sighup=False)
|
||||
proc.logfile_read = outputfile
|
||||
|
||||
commands = testcommands.split(";")
|
||||
commands = [macros[c] if c in macros else [c] for c in commands]
|
||||
# Apparently list flatten() is unpythonic...
|
||||
commands = [c for sublist in commands for c in sublist]
|
||||
|
||||
prompts = { True: shellprompt, False: menuprompt }
|
||||
inshell = False
|
||||
quit = False
|
||||
for cmd in commands:
|
||||
msg = getprompt(proc, prompts[inshell])
|
||||
if msg is not None:
|
||||
return msg
|
||||
if cmd == "WAIT":
|
||||
time.sleep(3)
|
||||
cmd = ""
|
||||
proc.send("%s\r" % cmd)
|
||||
if not inshell and cmd == "q":
|
||||
quit = True
|
||||
if not inshell and cmd == "s":
|
||||
inshell = True
|
||||
if inshell and cmd == "exit":
|
||||
inshell = False
|
||||
if not quit:
|
||||
if inshell:
|
||||
msg = getprompt(proc, prompts[inshell])
|
||||
if msg is not None:
|
||||
return msg
|
||||
proc.send("exit\r")
|
||||
inshell = False
|
||||
msg = getprompt(proc, prompts[inshell])
|
||||
if msg is not None:
|
||||
return msg
|
||||
proc.send("q\r")
|
||||
quit = True
|
||||
|
||||
proc.expect_exact([pexpect.EOF, pexpect.TIMEOUT])
|
||||
|
||||
# Apparently if you call pexpect.wait() you must have
|
||||
# explicitly read all the input, or it hangs; and the process
|
||||
# can't be already dead, or it crashes. Therefore it appears
|
||||
# to be entirely useless. I hope not calling it doesn't cause
|
||||
# zombies to accumulate.
|
||||
#proc.wait()
|
||||
|
||||
return None
|
||||
# end run
|
||||
113
testscripts/test.py
Executable file
113
testscripts/test.py
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/usr/pkg/bin/python2.7
|
||||
# test.py - run some test material
|
||||
# usage: auto/test.py [options] test-commands
|
||||
# options:
|
||||
# --menuprompt=STR Change menu prompt string
|
||||
# --shellprompt=STR Change shell prompt string
|
||||
# --conf=sys161.conf Use alternate sys161 config
|
||||
# --ram=N Force RAM size (default from sys161 config)
|
||||
# --cpus=N Force number of cpus (default from sys161 config)
|
||||
# --doom=N Set doom counter to N (default none)
|
||||
# --progress=N Progress monitoring with N-second timeout (default 30)
|
||||
# --no-progress Disable progress monitoring
|
||||
# --timeout=N Global timeout, in seconds (default 300)
|
||||
# --kernel=KERNEL Choose kernel to run (default "kernel")
|
||||
#
|
||||
#
|
||||
# This is a directly executable wrapper around runtest.py. You can use
|
||||
# it from the host OS shell to run more or less arbitrary scripts, or
|
||||
# you can write your own Python scripts using runtest.py directly.
|
||||
#
|
||||
# See the top of runtest.py for an explanation of the arguments.
|
||||
#
|
||||
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
import runtest
|
||||
|
||||
############################################################
|
||||
# global settings
|
||||
|
||||
g_doom = None
|
||||
g_conf = None
|
||||
g_cpus = None
|
||||
g_kernel = None
|
||||
g_menuprompt = None
|
||||
g_progress = 30
|
||||
g_ram = None
|
||||
g_shellprompt = None
|
||||
g_timeout = 300
|
||||
|
||||
############################################################
|
||||
# main
|
||||
|
||||
def getargs():
|
||||
global g_menuprompt
|
||||
global g_shellprompt
|
||||
global g_ram
|
||||
global g_conf
|
||||
global g_cpus
|
||||
global g_doom
|
||||
global g_progress
|
||||
global g_timeout
|
||||
global g_kernel
|
||||
|
||||
# XXX is there no better scheme for this?
|
||||
p = OptionParser()
|
||||
|
||||
# grr -h is hardwired
|
||||
p.add_option("-c", "--conf", dest="conf")
|
||||
p.add_option("-D", "--doom", dest="doom")
|
||||
p.add_option("-j", "--cpus", dest="cpus")
|
||||
p.add_option("-k", "--kernel", dest="kernel")
|
||||
p.add_option("-m", "--menuprompt", dest="menuprompt")
|
||||
p.add_option("-r", "--ram", dest="ram")
|
||||
p.add_option("-s", "--shellprompt", dest="shellprompt")
|
||||
p.add_option("-t", "--timeout", dest="timeout")
|
||||
p.add_option("-z", "--no-progress", dest="no_progress")
|
||||
p.add_option("-Z", "--progress", dest="progress")
|
||||
|
||||
(options, args) = p.parse_args()
|
||||
if options.menuprompt is not None:
|
||||
g_menuprompt = options.menuprompt
|
||||
if options.shellprompt is not None:
|
||||
g_shellprompt = options.shellprompt
|
||||
if options.conf is not None:
|
||||
g_conf = options.conf
|
||||
if options.ram is not None:
|
||||
g_ram = options.ram
|
||||
if options.cpus is not None:
|
||||
g_cpus = int(options.cpus)
|
||||
if options.doom is not None:
|
||||
g_doom = int(options.doom)
|
||||
if options.progress is not None:
|
||||
g_progress = int(options.progress)
|
||||
if options.no_progress is not None:
|
||||
g_progress = None
|
||||
if options.timeout is not None:
|
||||
g_timeout = int(options.timeout)
|
||||
if options.kernel is not None:
|
||||
g_kernel = options.conf
|
||||
|
||||
if len(args) != 1:
|
||||
sys.stderr.write("Usage: test.py [options] test-commands\n")
|
||||
exit(1)
|
||||
return args[0]
|
||||
# end getargs
|
||||
|
||||
testcommands = getargs()
|
||||
msg = runtest.run(testcommands,
|
||||
sys.stdout,
|
||||
menuprompt=g_menuprompt,
|
||||
shellprompt=g_shellprompt,
|
||||
conf=g_conf,
|
||||
ram=g_ram,
|
||||
cpus=g_cpus,
|
||||
doom=g_doom,
|
||||
progress=g_progress,
|
||||
timeout=g_timeout,
|
||||
kernel=g_kernel)
|
||||
if msg is not None:
|
||||
sys.stderr.write("test.py: test commands aborted with %s\n" % msg)
|
||||
exit(0)
|
||||
Reference in New Issue
Block a user