3 # Timur (*T*est*I*ng *M*achinari*U*m for *R*omain)
5 # (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
6 # economic rights: Technische Universität Dresden (Germany)
8 # This file is part of TUD:OS and distributed under the terms of the
9 # GNU General Public License 2.
10 # Please see the COPYING-GPL-2 file for details.
13 import subprocess # launching qemu
14 import tempfile # temporary output
15 import shlex # proper splitting for qemu parameters
17 import filecmp # comparing files
18 import argparse # cmd line arguments
21 globalDirs = [ "/home/doebel/src/tudos/src/build/bin/x86_586/l4f",
22 "/home/doebel/src/tudos/src/build/bin/x86_586/",
23 "/home/doebel/src/tudos/src/kernel/fiasco/build",
24 "/home/doebel/src/tudos/src/l4/conf",
26 globalQemu = "qemu-system-i386"
27 globalFiascoOpt = "-serial_esc -jdb_cmd=JS"
28 globalModules = ["sigma0"]
34 print " Runs all tests defined in subdirectories of <path>"
39 Maintain a set of directories and allow searching those dirs
40 for a given file name.
42 def __init__(self, directories):
43 self.dirs = directories
47 Find the given file name in the set of dirs.
60 Wrapper for a temporary file
63 self.f = tempfile.NamedTemporaryFile()
64 self.name = self.f.name
67 return "file:%s" % self.f.name
70 return self.f.readlines()
75 Class that behaves like a tempfile w.r.t. having a name property.
76 This class is passed to the command line generator when we want a
77 printable command line (e.g., one that you can copy & run from your shell)
87 def waitForTermination(self, test):
88 # monitor outfile for expected final line
92 if len(l) > 1 and l[-1].startswith("Return reboots"):
100 def adaptOutfile(self, test, filename=None):
102 Adapt the test output file to the expected format
104 Some output (e.g. bootstrap's) may differ between runs and
105 we want to ignore this. Therefore, we remove all output from
106 the test that comes before MOE's bootup message.
108 For simplicity, we generate a new file here that contains
109 the reduced output and is then used for comparison with the
113 f1 = file(test.tmpOut.name)
115 f2 = tempfile.NamedTemporaryFile()
117 f2 = file(filename, "w")
119 do_output = False # tracks if a line should go to the new tmpfile
121 for l in f1.readlines():
122 if l.startswith("MOE: cmdline: "):
128 f2.flush() # makes sure that everything is written back before
129 # we compare with the expected output
133 def __init__(self, test):
134 # start the test in the background
135 pid = subprocess.Popen(shlex.split(test.commandLine()),
136 stdout = file("/dev/null"),
137 stderr = file("/dev/null"))
139 self.waitForTermination(test)
141 # SIGTERMinate the qemu process
145 def compare(self, test):
147 Evaluate test results by comparing output with expect file.
150 expectFile = test.fullpath([f for f in test.caseFiles if sre.match(".*\.expect", f)][0])
152 print "No .expect file found in %s. (Generate one with -e.)" % test.root
155 if filecmp.cmp(expectFile, test.tmpOut.name):
158 print "\033[31;1mFAIL\033[0m"
159 print "Output mismatch between %s and %s" % (expectFile, test.tmpOut.name)
160 subprocess.call(["diff", "-rub", expectFile, test.tmpOut.name])
165 Representation of a single test case.
167 def get_modules(self):
169 Extract the list of modules for this setup from the .boot file
170 we stored in self.bootFile.
173 f = file(self.bootFile)
175 "Could not open %s for reading" % self.bootFile
178 lines = [l.strip() for l in f.readlines()]
181 if v[0] == "roottask":
182 self.roottask = v[1:]
183 elif v[0] == "module":
184 self.modules += [v[1]]
187 def commandLine(self):
189 Generate the Qemu command line to launch this setup.
191 cmd = globalQemu + " -kernel " + self.finder.find("bootstrap")
192 cmd += " -append \"bootstrap -modaddr 0x01100000\" -initrd \""
193 cmd += self.fiasco + " " + globalFiascoOpt
195 for m in globalModules:
197 cmd += self.finder.find(m)
199 cmd += "," + self.finder.find(self.roottask[0]) + " "
200 cmd += " ".join(self.roottask[1:])
202 for m in self.modules:
204 cmd += self.finder.find(m)
207 cmd += " -serial %s" % self.tmpOut
213 def fullpath(self, filename):
214 return self.root + "/" + filename
217 def __init__(self, rootDir):
218 print "\033[32mTest case: \033[0m", os.path.basename(rootDir)
220 self.finder = FileFinder([rootDir] + globalDirs)
221 self.caseFiles = os.listdir(rootDir)
223 self.fiasco = self.finder.find("fiasco")
224 self.tmpOut = TempFile()
227 self.bootFile = self.fullpath([f for f in self.caseFiles if sre.match(".*\.boot", f)][0])
229 print "No .boot file found in %s." % rootDir
237 rootDir = args.rootDir
238 if not os.path.isdir(rootDir):
239 raise ValueError("Not a directory: %s" % rootDir)
240 except Exception as e:
241 print "[\033[31m%s\033[0m]\n" % e
246 parser = argparse.ArgumentParser("./timur", epilog="Only one of the options {-c, -e, -t} can be run at a time.")
247 parser.add_argument("-d", "--directory", action="store", dest="rootDir",
248 help="Directory containing test descriptions", required=True)
249 parser.add_argument("-c", "--cmdline", action="store_true", dest="cmdline",
250 help="Print the command line that would be used to launch Qemu")
251 parser.add_argument("-e", "--expect", action="store_true", dest="expect",
252 help="Generate expected output")
253 parser.add_argument("-t", "--test", action="store_true", dest="test",
256 args = parser.parse_args()
259 rootDir = args.rootDir # valid, because we'd have aborted otherwise
262 for f in [d for d in os.listdir(rootDir) if os.path.isdir(rootDir+"/"+d) and d != ".svn" ]:
263 tc = TestCase(rootDir+"/"+f)
264 tc.tmpOut=StdioFile() # for the cmdline it is useful to print to stdio
265 print tc.commandLine()
268 for f in [d for d in os.listdir(rootDir) if os.path.isdir(rootDir+"/"+d) and d != ".svn" ]:
269 tc = TestCase(rootDir+"/"+f)
271 fname = rootDir + "/" + f + "/"
272 fname += "%s.expect" % f
273 tr.adaptOutfile(tc, fname)
277 for f in [d for d in os.listdir(rootDir) if os.path.isdir(rootDir+"/"+d) and d != ".svn" ]:
278 tc = TestCase(rootDir+"/"+f)
284 if __name__ == "__main__":