|
1 # |
|
2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 # All rights reserved. |
|
4 # |
|
5 # This program is free software: you can redistribute it and/or modify |
|
6 # it under the terms of the GNU Lesser General Public License as published by |
|
7 # the Free Software Foundation, either version 3 of the License, or |
|
8 # (at your option) any later version. |
|
9 # |
|
10 # This program is distributed in the hope that it will be useful, |
|
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 # GNU Lesser General Public License for more details. |
|
14 # |
|
15 # You should have received a copy of the GNU Lesser General Public License |
|
16 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
17 |
|
18 # Launch QEMU for SVP with a specified ROM image and core using a COM port for IO |
|
19 # Uses Python's Popen class for process control. |
|
20 # Test output is captured by invoking QEMU in -nographic mode. This redirects serial output to stdout which can then be PIPE'd |
|
21 # using Popen. |
|
22 # Tests which hang(or crash into the the crash debugger) are detected using a trivial 'watchdog'. IWBN to use e.g. 'select' |
|
23 # with a timeout, but this won't work on Windoze, which only supports timeout's on sockets. If the watchdog timeouts out QEMU is killed. |
|
24 # When the test suite runs to (recognizable) completion QEMU is killed. Unfortunately this appears to require an OS specific |
|
25 # solution. NB unrecognized completion will result in the watchdog killing QEMU. |
|
26 # QemuTestRunner collects output into LineTimeInfo objects. These record the time at which each line was received. The time can be used as a crude |
|
27 # measure of how long a test took to execute. |
|
28 # The raw data gathered from running the tests can be retrieved using GetResults. This returns a list of LineTimeInfo objects. |
|
29 |
|
30 import sys |
|
31 mswindows = (sys.platform == "win32") |
|
32 |
|
33 # import the following so we can kill QEMU |
|
34 |
|
35 if mswindows: |
|
36 # import win32api |
|
37 import signal |
|
38 else: |
|
39 import signal |
|
40 |
|
41 import os |
|
42 |
|
43 import time |
|
44 |
|
45 import re |
|
46 |
|
47 import subprocess |
|
48 from subprocess import * |
|
49 |
|
50 from stat import * |
|
51 |
|
52 import watchdog |
|
53 |
|
54 __all__ = ["QemuTestRunner"] |
|
55 |
|
56 class LineTimeInfo(object): |
|
57 def __init__(self, line, atime): |
|
58 self.line = line |
|
59 self.time = atime |
|
60 |
|
61 def GetLine(self): |
|
62 return self.line |
|
63 |
|
64 def GetTime(self): |
|
65 return self.time |
|
66 |
|
67 class QemuTestRunner(object): |
|
68 def __init__(self, qemupath, cpu, rompath, board = 'syborg', endOfTestFn=None, displayp=False, dataFile = None): |
|
69 """Create new QemuTestRunner instance.""" |
|
70 self.qemupath = qemupath |
|
71 self.board = board |
|
72 self.cpu = cpu |
|
73 self.rompath = rompath |
|
74 self.endOfTestFn = endOfTestFn |
|
75 self.displayp = displayp |
|
76 #self.cmd = qemupath + " -M syborg -cpu " + cpu + " -kernel " + rompath + " -nographic" |
|
77 self.cmd = "%s -M %s -cpu %s -kernel %s -nographic" % (qemupath, board, cpu, rompath) |
|
78 self.endOfTestPattern = re.compile('RUNTESTS: Completed test script') |
|
79 self.lineTimeInfo = [] |
|
80 self.id = None |
|
81 self.dataFile = dataFile |
|
82 self.watchdog = watchdog.WatchDog(900, lambda : self.KillSim()) |
|
83 |
|
84 def Run(self): |
|
85 self.lineTimeInfo = [] |
|
86 output = False |
|
87 self.timeStarted = time.gmtime() |
|
88 self.id = time.strftime("%d-%m%--%Y-%H-%M-%S", self.timeStarted) |
|
89 if self.dataFile != None: |
|
90 filename = self.dataFile + "-" + self.GetRunId() + "-data.txt" |
|
91 self.dataFileName = filename |
|
92 output = open(filename, 'wb') |
|
93 p = Popen( self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE ) |
|
94 self.popen = p |
|
95 stdin = p.stdin |
|
96 stdout = p.stdout |
|
97 stop = False |
|
98 |
|
99 if self.displayp: |
|
100 print >> sys.stdout, self.cmd |
|
101 |
|
102 self.watchdog.Start() |
|
103 try: |
|
104 while p.poll() == None and not stop: |
|
105 line = stdout.readline() |
|
106 atime = time.clock() |
|
107 self.watchdog.Reset() |
|
108 if self.displayp and p.returncode == None: |
|
109 print >> sys.stdout , line |
|
110 if output and p.returncode == None: |
|
111 print >> output , line |
|
112 if p.returncode == None: |
|
113 self.lineTimeInfo.append(LineTimeInfo(line, atime)) |
|
114 if self.endOfTestFn != None: |
|
115 stop = self.endOfTestFn(line) |
|
116 else: |
|
117 stop = self.EndOfTestp(line) |
|
118 finally: |
|
119 self.timeEnded = time.gmtime() |
|
120 self.watchdog.Stop() |
|
121 if output: |
|
122 output.close() |
|
123 self.testSuiteFinished = stop; |
|
124 |
|
125 if p.returncode == None: |
|
126 self.KillSim() |
|
127 return True |
|
128 else: |
|
129 return False |
|
130 |
|
131 def GetDataFileName(self): |
|
132 return self.dataFileName |
|
133 |
|
134 def TestSuiteFinishedp(self): |
|
135 return self.testSuiteFinished |
|
136 |
|
137 def EndOfTestp(self, l): |
|
138 return re.match(self.endOfTestPattern,l) != None |
|
139 |
|
140 def KillSim(self): |
|
141 # if mswindows: |
|
142 # win32api.TerminateProcess(int(self.popen._handle), -1) |
|
143 # else: |
|
144 os.kill(slef.popen.pid, signal.SIGKILL) |
|
145 |
|
146 def GetResults(self): |
|
147 return self.lineTimeInfo |
|
148 |
|
149 def GetRomName(self): |
|
150 return self.rompath |
|
151 |
|
152 def GetRunId(self): |
|
153 return self.id |
|
154 |
|
155 def GetSummary(self): |
|
156 simStat = os.stat(self.qemupath) |
|
157 simSummary = "QEMU Executable: %s size: %d creation time: %d\n" % (self.qemupath, simStat[ST_SIZE], simStat[ST_CTIME]) |
|
158 boardSummary ="Board: %s\n" % self.board |
|
159 cpuSummary = "CPU: %s\n" % self.cpu |
|
160 romStat = os.stat(self.rompath) |
|
161 romSummary = "ROM image: %s size: %d creation date: %d\n" % (self.rompath, romStat[ST_SIZE], romStat[ST_CTIME]) |
|
162 timeFormat = "%d-%m%--%Y %H:%M:%S" |
|
163 startTime = "Start time: " + time.strftime(timeFormat, self.timeStarted) + "\n" |
|
164 endTime = "End time: " + time.strftime(timeFormat, self.timeEnded) + "\n" |
|
165 status = "Testsuite did not complete\n" |
|
166 if self.TestSuiteFinishedp(): |
|
167 status = "Testsuite completed\n" |
|
168 return simSummary + boardSummary + cpuSummary + romSummary + startTime + endTime + status |
|
169 |
|
170 def GetReportFileName(self): |
|
171 return self.GetRomName() + "-" + self.GetRunId() + "-results-summary.txt" |
|
172 |
|
173 class PseudoRunner(QemuTestRunner): |
|
174 def __init__(self, input): |
|
175 #self.qemupath = qemupath |
|
176 #self.board = board |
|
177 #self.cpu = cpu |
|
178 #self.rompath = rompath |
|
179 #self.endOfTestFn = endOfTestFn |
|
180 #self.displayp = displayp |
|
181 #self.cmd = qemupath + " -M syborg -cpu " + cpu + " -kernel " + rompath + " -nographic" |
|
182 #self.endOfTestPattern = re.compile('RUNTESTS: Completed test script') |
|
183 self.lineTimeInfo = [] |
|
184 #self.id = None |
|
185 self.dataFile = input |
|
186 for line in open(input): |
|
187 self.lineTimeInfo.append(LineTimeInfo(line, 0)) |
|
188 |
|
189 def GetRomName(self): |
|
190 return "Unknown" |
|
191 |
|
192 def GetRunId(self): |
|
193 return None |
|
194 |
|
195 def GetSummary(self): |
|
196 return "" |
|
197 |
|
198 def GetReportFileName(self): |
|
199 return "Runtest-Summary.txt" |