VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testdriver/reporter.py@ 65309

最後變更 在這個檔案從65309是 65309,由 vboxsync 提交於 8 年 前

testdriver: append '/abort' to the log lines of scripts doing the 'abort' action.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 55.2 KB
 
1# -*- coding: utf-8 -*-
2# $Id: reporter.py 65309 2017-01-16 09:43:20Z vboxsync $
3# pylint: disable=C0302
4
5"""
6Testdriver reporter module.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2010-2016 Oracle Corporation
12
13This file is part of VirtualBox Open Source Edition (OSE), as
14available from http://www.alldomusa.eu.org. This file is free software;
15you can redistribute it and/or modify it under the terms of the GNU
16General Public License (GPL) as published by the Free Software
17Foundation, in version 2 as it comes in the "COPYING" file of the
18VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20
21The contents of this file may alternatively be used under the terms
22of the Common Development and Distribution License Version 1.0
23(CDDL) only, as it comes in the "COPYING.CDDL" file of the
24VirtualBox OSE distribution, in which case the provisions of the
25CDDL are applicable instead of those of the GPL.
26
27You may elect to license modified versions of this file under the
28terms and conditions of either the GPL or the CDDL or both.
29"""
30__version__ = "$Revision: 65309 $"
31
32
33# Standard Python imports.
34import array
35import datetime
36import errno
37import os
38import os.path
39import sys
40import time
41import threading
42import traceback
43
44# Validation Kit imports.
45from common import utils;
46
47## test reporter instance
48g_oReporter = None; # type: ReporterBase
49g_sReporterName = None;
50g_oLock = threading.Lock();
51
52
53
54class PythonLoggingStream(object):
55 """
56 Python logging => testdriver/reporter.py stream.
57 """
58
59 def write(self, sText):
60 """Writes python log message to our stream."""
61 if g_oReporter != None:
62 sText = sText.rstrip("\r\n");
63 #g_oReporter.log(0, 'python: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
64 return True;
65
66 def flush(self):
67 """Flushes the stream."""
68 return True;
69
70
71class ReporterBase(object):
72 """
73 Base class for the reporters.
74 """
75
76 def __init__(self):
77 self.iVerbose = 1;
78 self.iDebug = 0;
79 self.cErrors = 0;
80 self.fTimedOut = False; # Once set, it trickles all the way up.
81 self.atTests = [];
82 self.sName = os.path.splitext(os.path.basename(sys.argv[0]))[0];
83
84 # Hook into the python logging.
85 import logging;
86 logging.basicConfig(stream = PythonLoggingStream(),
87 level = logging.DEBUG,
88 format = '%(name)-12s %(levelname)-8s %(message)s');
89 #
90 # Introspection and configuration.
91 #
92
93 def isLocal(self):
94 """Is this a local reporter?"""
95 return False;
96
97 def incVerbosity(self):
98 """Increases the verbosity level."""
99 self.iVerbose += 1;
100
101 def incDebug(self):
102 """Increases the debug level."""
103 self.iDebug += 1;
104
105 def appendToProcessName(self, sAppend):
106 """
107 Appends sAppend to the base process name.
108 Returns the new process name.
109 """
110 self.sName = os.path.splitext(os.path.basename(sys.argv[0]))[0] + sAppend;
111 return self.sName;
112
113
114 #
115 # Generic logging.
116 #
117
118 def log(self, iLevel, sText, sCaller, sTsPrf):
119 """
120 Writes the specfied text to the log if iLevel is less or requal
121 to iVerbose.
122 """
123 _ = iLevel; _ = sText; _ = sCaller; _ = sTsPrf;
124 return 0;
125
126 #
127 # XML output from the reporter.
128 #
129
130 def _xmlEscAttr(self, sValue):
131 """Escapes an XML attribute value."""
132 sValue = sValue.replace('&', '&');
133 sValue = sValue.replace('<', '&lt;');
134 sValue = sValue.replace('>', '&gt;');
135 #sValue = sValue.replace('\'', '&apos;');
136 sValue = sValue.replace('"', '&quot;');
137 sValue = sValue.replace('\n', '&#xA');
138 sValue = sValue.replace('\r', '&#xD');
139 return sValue;
140
141 def _xmlWrite(self, asText, fIndent = True):
142 """XML output function for the reporter."""
143 _ = asText; _ = fIndent;
144 return None;
145
146 def xmlFlush(self, fRetry = False, fForce = False):
147 """Flushes XML output if buffered."""
148 _ = fRetry; _ = fForce;
149 return None;
150
151 #
152 # XML output from child.
153 #
154
155 def subXmlStart(self, oFileWrapper):
156 """Called by the file wrapper when the first bytes are written to the test pipe."""
157 _ = oFileWrapper;
158 return None;
159
160 def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
161 """Called by the file wrapper write method for test pipes."""
162 return self.log(0, 'raw xml%s: %s' % (oFileWrapper.sPrefix, sRawXml), sCaller, utils.getTimePrefix());
163
164 def subXmlEnd(self, oFileWrapper):
165 """Called by the file wrapper __del__ method for test pipes."""
166 _ = oFileWrapper;
167 return None;
168
169 #
170 # File output.
171 #
172
173 def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
174 """
175 Adds the file to the report.
176 Returns True on success, False on failure.
177 """
178 _ = oSrcFile; _ = sSrcFilename; _ = sAltName; _ = sDescription; _ = sKind; _ = sCaller; _ = sTsPrf;
179 return True;
180
181 def addLogString(self, sLog, sLogName, sDescription, sKind, sCaller, sTsPrf):
182 """
183 Adds the file to the report.
184 Returns True on success, False on failure.
185 """
186 _ = sLog; _ = sLogName; _ = sDescription; _ = sKind; _ = sCaller; _ = sTsPrf;
187 return True;
188
189 #
190 # Test reporting
191 #
192
193 def _testGetFullName(self):
194 """
195 Mangles the test names in atTest into a single name to make it easier
196 to spot where we are.
197 """
198 sName = '';
199 for t in self.atTests:
200 if sName != '':
201 sName += ', ';
202 sName += t[0];
203 return sName;
204
205 def testIncErrors(self):
206 """Increates the error count."""
207 self.cErrors += 1;
208 return self.cErrors;
209
210 def testSetTimedOut(self):
211 """Sets time out indicator for the current test and increases the error counter."""
212 self.fTimedOut = True;
213 self.cErrors += 1;
214 return None;
215
216 def testStart(self, sName, sCaller):
217 """ Starts a new test, may be nested. """
218 (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
219 self._xmlWrite([ '<Test timestamp="%s" name="%s">' % (sTsIso, self._xmlEscAttr(sName),), ]);
220 self.atTests.append((sName, self.cErrors, self.fTimedOut));
221 self.fTimedOut = False;
222 return self.log(1, ' %-50s: TESTING' % (self._testGetFullName()), sCaller, sTsPrf);
223
224 def testValue(self, sName, sValue, sUnit, sCaller):
225 """ Reports a benchmark value or something simiarlly useful. """
226 (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
227 self._xmlWrite([ '<Value timestamp="%s" name="%s" unit="%s" value="%s"/>'
228 % (sTsIso, self._xmlEscAttr(sName), self._xmlEscAttr(sUnit), self._xmlEscAttr(sValue)), ]);
229 return self.log(0, '** %-48s: %12s %s' % (sName, sValue, sUnit), sCaller, sTsPrf);
230
231 def testFailure(self, sDetails, sCaller):
232 """ Reports a failure. """
233 (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
234 self.cErrors = self.cErrors + 1;
235 self._xmlWrite([ '<FailureDetails timestamp="%s" text="%s"/>' % (sTsIso, self._xmlEscAttr(sDetails),), ]);
236 return self.log(0, sDetails, sCaller, sTsPrf);
237
238 def testDone(self, fSkipped, sCaller):
239 """
240 Marks the current test as DONE, pops it and maks the next test on the
241 stack current.
242 Returns (name, errors).
243 """
244 (sTsPrf, sTsIso) = utils.getTimePrefixAndIsoTimestamp();
245 sFullName = self._testGetFullName();
246
247 # safe pop
248 if len(self.atTests) <= 0:
249 self.log(0, 'testDone on empty test stack!', sCaller, sTsPrf);
250 return ('internal error', 0);
251 fTimedOut = self.fTimedOut;
252 sName, cErrorsStart, self.fTimedOut = self.atTests.pop();
253
254 # log + xml.
255 cErrors = self.cErrors - cErrorsStart;
256 if cErrors == 0:
257 if fSkipped is not True:
258 self._xmlWrite([ ' <Passed timestamp="%s"/>' % (sTsIso,), '</Test>' ],);
259 self.log(1, '** %-50s: PASSED' % (sFullName,), sCaller, sTsPrf);
260 else:
261 self._xmlWrite([ ' <Skipped timestamp="%s"/>' % (sTsIso,), '</Test>' ]);
262 self.log(1, '** %-50s: SKIPPED' % (sFullName,), sCaller, sTsPrf);
263 elif fTimedOut:
264 self._xmlWrite([ ' <TimedOut timestamp="%s" errors="%d"/>' % (sTsIso, cErrors), '</Test>' ]);
265 self.log(0, '** %-50s: TIMED-OUT - %d errors' % (sFullName, cErrors), sCaller, sTsPrf);
266 else:
267 self._xmlWrite([ ' <Failed timestamp="%s" errors="%d"/>' % (sTsIso, cErrors), '</Test>' ]);
268 self.log(0, '** %-50s: FAILED - %d errors' % (sFullName, cErrors), sCaller, sTsPrf);
269
270 # Flush buffers when reaching the last test.
271 if len(self.atTests) == 0:
272 self.xmlFlush(fRetry = True);
273
274 return (sName, cErrors);
275
276 def testErrorCount(self):
277 """
278 Returns the number of errors accumulated by the current test.
279 """
280 cTests = len(self.atTests);
281 if cTests <= 0:
282 return self.cErrors;
283 return self.cErrors - self.atTests[cTests - 1][1];
284
285 def testCleanup(self, sCaller):
286 """
287 Closes all open test as failed.
288 Returns True if no open tests, False if there were open tests.
289 """
290 if len(self.atTests) == 0:
291 return True;
292 for _ in range(len(self.atTests)):
293 self.testFailure('Test not closed by test drver', sCaller)
294 self.testDone(False, sCaller);
295 return False;
296
297 #
298 # Misc.
299 #
300
301 def doPollWork(self, sDebug = None):
302 """
303 Check if any pending stuff expired and needs doing.
304 """
305 _ = sDebug;
306 return None;
307
308
309
310
311class LocalReporter(ReporterBase):
312 """
313 Local reporter instance.
314 """
315
316 def __init__(self):
317 ReporterBase.__init__(self);
318 self.oLogFile = None;
319 self.oXmlFile = None;
320 self.fXmlOk = True;
321 self.iSubXml = 0;
322 self.iOtherFile = 0;
323 self.fnGetIsoTimestamp = utils.getIsoTimestamp; # Hack to get a timestamp in __del__.
324 self.oStdErr = sys.stderr; # Hack for __del__ output.
325
326 #
327 # Figure the main log directory.
328 #
329 try:
330 import user;
331 self.sDefLogDir = os.path.abspath(os.path.join(user.home, "VBoxTestLogs"));
332 except:
333 self.sDefLogDir = os.path.abspath("VBoxTestLogs");
334 try:
335 sLogDir = os.path.abspath(os.environ.get('TESTBOX_REPORTER_LOG_DIR', self.sDefLogDir));
336 if not os.path.isdir(sLogDir):
337 os.makedirs(sLogDir, 0750);
338 except:
339 sLogDir = self.sDefLogDir;
340 if not os.path.isdir(sLogDir):
341 os.makedirs(sLogDir, 0750);
342
343 #
344 # Make a subdirectory for this test run.
345 #
346 sTs = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H-%M-%S.log');
347 self.sLogDir = sLogDir = os.path.join(sLogDir, '%s-%s' % (sTs, self.sName));
348 try:
349 os.makedirs(self.sLogDir, 0750);
350 except:
351 self.sLogDir = '%s-%s' % (self.sLogDir, os.getpid());
352 os.makedirs(self.sLogDir, 0750);
353
354 #
355 # Open the log file and write a header.
356 #
357 sLogName = os.path.join(self.sLogDir, 'testsuite.log');
358 sTsIso = utils.getIsoTimestamp();
359 self.oLogFile = utils.openNoInherit(sLogName, "w");
360 self.oLogFile.write(('Created log file at %s.\nRunning: %s' % (sTsIso, sys.argv)).encode('utf-8'));
361
362 #
363 # Open the xml log file and write the mandatory introduction.
364 #
365 # Note! This is done here and not in the base class because the remote
366 # logger doesn't really need this. It doesn't need the outer
367 # test wrapper either.
368 #
369 sXmlName = os.path.join(self.sLogDir, 'testsuite.xml');
370 self.oXmlFile = utils.openNoInherit(sXmlName, "w");
371 self._xmlWrite([ '<?xml version="1.0" encoding="UTF-8" ?>',
372 '<Test timestamp="%s" name="%s">' % (sTsIso, self._xmlEscAttr(self.sName),), ],
373 fIndent = False);
374
375 def __del__(self):
376 """Ends and completes the log files."""
377 try: sTsIso = self.fnGetIsoTimestamp();
378 except Exception, oXcpt:
379 sTsIso = str(oXcpt);
380
381 if self.oLogFile is not None:
382 try:
383 self.oLogFile.write(('\nThe End %s\n' % (sTsIso,)).encode('utf-8'));
384 self.oLogFile.close();
385 except: pass;
386 self.oLogFile = None;
387
388 if self.oXmlFile is not None:
389 self._closeXml(sTsIso);
390 self.oXmlFile = None;
391
392 def _closeXml(self, sTsIso):
393 """Closes the XML file."""
394 if self.oXmlFile is not None:
395 # pop the test stack
396 while len(self.atTests) > 0:
397 sName, cErrorsStart, self.fTimedOut = self.atTests.pop();
398 self._xmlWrite([ '<End timestamp="%s" errors="%d"/>' % (sTsIso, self.cErrors - cErrorsStart,),
399 '</%s>' % (sName,), ]);
400
401 # The outer one is not on the stack.
402 self._xmlWrite([ ' <End timestamp="%s"/>' % (sTsIso,),
403 '</Test>', ], fIndent = False);
404 try:
405 self.oXmlFile.close();
406 self.oXmlFile = None;
407 except:
408 pass;
409
410 def _xmlWrite(self, asText, fIndent = True):
411 """Writes to the XML file."""
412 for sText in asText:
413 if fIndent:
414 sIndent = ''.ljust((len(self.atTests) + 1) * 2);
415 sText = sIndent + sText;
416 sText += '\n';
417
418 try:
419 self.oXmlFile.write(sText.encode('utf-8'));
420 except:
421 if self.fXmlOk:
422 traceback.print_exc();
423 self.fXmlOk = False;
424 return False;
425 return True;
426
427 #
428 # Overridden methods.
429 #
430
431 def isLocal(self):
432 """Is this a local reporter?"""
433 return True;
434
435 def log(self, iLevel, sText, sCaller, sTsPrf):
436 if iLevel <= self.iVerbose:
437 # format it.
438 if self.iDebug > 0:
439 sLogText = '%s %30s: %s' % (sTsPrf, sCaller, sText);
440 else:
441 sLogText = '%s %s' % (sTsPrf, sText);
442
443 # output it.
444 sAscii = sLogText.encode('ascii', 'replace');
445 if self.iDebug == 0:
446 print >> self.oStdErr, '%s: %s' % (self.sName, sAscii)
447 else:
448 print >> self.oStdErr, '%s' % (sAscii)
449 sLogText += '\n';
450 try:
451 self.oLogFile.write(sLogText.encode('utf-8'));
452 except:
453 pass;
454 return 0;
455
456 def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
457 # Figure the destination filename.
458 iOtherFile = self.iOtherFile;
459 self.iOtherFile += 1;
460 sDstFilename = os.path.join(self.sLogDir, 'other-%d-%s.log' \
461 % (iOtherFile, os.path.splitext(os.path.basename(sSrcFilename))[0]));
462 self.log(0, '** Other log file: %s - %s (%s)' % (sDstFilename, sDescription, sSrcFilename), sCaller, sTsPrf);
463
464 # Open the destination file and copy over the data.
465 fRc = True;
466 try:
467 oDstFile = utils.openNoInherit(sDstFilename, 'w');
468 except Exception, oXcpt:
469 self.log(0, 'error opening %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
470 else:
471 while True:
472 try:
473 abBuf = oSrcFile.read(65536);
474 except Exception, oXcpt:
475 fRc = False;
476 self.log(0, 'error reading %s: %s' % (sSrcFilename, oXcpt), sCaller, sTsPrf);
477 else:
478 try:
479 oDstFile.write(abBuf);
480 except Exception, oXcpt:
481 fRc = False;
482 self.log(0, 'error writing %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
483 else:
484 if len(abBuf) > 0:
485 continue;
486 break;
487 oDstFile.close();
488
489 # Leave a mark in the XML log.
490 self._xmlWrite(['<LogFile timestamp="%s" filename="%s" source="%s" kind="%s" ok="%s">%s</LogFile>\n'
491 % (utils.getIsoTimestamp(), self._xmlEscAttr(os.path.basename(sDstFilename)), self._xmlEscAttr(sSrcFilename), \
492 self._xmlEscAttr(sKind), fRc, self._xmlEscAttr(sDescription))] );
493 _ = sAltName;
494 return fRc;
495
496 def addLogString(self, sLog, sLogName, sDescription, sKind, sCaller, sTsPrf):
497 # Figure the destination filename.
498 iOtherFile = self.iOtherFile;
499 self.iOtherFile += 1;
500 sDstFilename = os.path.join(self.sLogDir, 'other-%d-%s.log' \
501 % (iOtherFile, os.path.splitext(os.path.basename(sLogName))[0]));
502 self.log(0, '** Other log file: %s - %s (%s)' % (sDstFilename, sDescription, sLogName), sCaller, sTsPrf);
503
504 # Open the destination file and copy over the data.
505 fRc = True;
506 try:
507 oDstFile = utils.openNoInherit(sDstFilename, 'w');
508 except Exception, oXcpt:
509 self.log(0, 'error opening %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
510 else:
511 try:
512 oDstFile.write(sLog);
513 except Exception, oXcpt:
514 fRc = False;
515 self.log(0, 'error writing %s: %s' % (sDstFilename, oXcpt), sCaller, sTsPrf);
516
517 oDstFile.close();
518
519 # Leave a mark in the XML log.
520 self._xmlWrite(['<LogFile timestamp="%s" filename="%s" source="%s" kind="%s" ok="%s">%s</LogFile>\n'
521 % (utils.getIsoTimestamp(), self._xmlEscAttr(os.path.basename(sDstFilename)), self._xmlEscAttr(sLogName), \
522 self._xmlEscAttr(sKind), fRc, self._xmlEscAttr(sDescription))] );
523 return fRc;
524
525 def subXmlStart(self, oFileWrapper):
526 # Open a new file and just include it from the main XML.
527 iSubXml = self.iSubXml;
528 self.iSubXml += 1;
529 sSubXmlName = os.path.join(self.sLogDir, 'sub-%d.xml' % (iSubXml,));
530 try:
531 oFileWrapper.oSubXmlFile = utils.openNoInherit(sSubXmlName, "w");
532 except:
533 errorXcpt('open(%s)' % oFileWrapper.oSubXmlName);
534 oFileWrapper.oSubXmlFile = None;
535 else:
536 self._xmlWrite(['<Include timestamp="%s" filename="%s"/>\n'
537 % (utils.getIsoTimestamp(), self._xmlEscAttr(os.path.basename(sSubXmlName)))]);
538 return None;
539
540 def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
541 if oFileWrapper.oSubXmlFile is not None:
542 try:
543 oFileWrapper.oSubXmlFile.write(sRawXml);
544 except:
545 pass;
546 if sCaller is None: pass; # pychecker - NOREF
547 return None;
548
549 def subXmlEnd(self, oFileWrapper):
550 if oFileWrapper.oSubXmlFile is not None:
551 try:
552 oFileWrapper.oSubXmlFile.close();
553 oFileWrapper.oSubXmlFile = None;
554 except:
555 pass;
556 return None;
557
558
559
560class RemoteReporter(ReporterBase):
561 """
562 Reporter that talks to the test manager server.
563 """
564
565
566 ## The XML sync min time (seconds).
567 kcSecXmlFlushMin = 30;
568 ## The XML sync max time (seconds).
569 kcSecXmlFlushMax = 120;
570 ## The XML sync idle time before flushing (seconds).
571 kcSecXmlFlushIdle = 5;
572 ## The XML sync line count threshold.
573 kcLinesXmlFlush = 512;
574
575 ## The retry timeout.
576 kcSecTestManagerRetryTimeout = 120;
577 ## The request timeout.
578 kcSecTestManagerRequestTimeout = 30;
579
580
581 def __init__(self):
582 ReporterBase.__init__(self);
583 self.sTestManagerUrl = os.environ.get('TESTBOX_MANAGER_URL');
584 self.sTestBoxUuid = os.environ.get('TESTBOX_UUID');
585 self.idTestBox = int(os.environ.get('TESTBOX_ID'));
586 self.idTestSet = int(os.environ.get('TESTBOX_TEST_SET_ID'));
587 self._asXml = [];
588 self._secTsXmlFlush = utils.timestampSecond();
589 self._secTsXmlLast = self._secTsXmlFlush;
590 self._fXmlFlushing = False;
591 self.oOutput = sys.stdout; # Hack for __del__ output.
592 self.fFlushEachLine = True;
593 self.fDebugXml = 'TESTDRIVER_REPORTER_DEBUG_XML' in os.environ;
594
595 # Prepare the TM connecting.
596 import urlparse;
597 import httplib;
598 import urllib;
599 from common import constants;
600
601 self._fnUrlEncode = urllib.urlencode;
602 self._fnUrlParseQs = urlparse.parse_qs;
603 self._oParsedTmUrl = urlparse.urlparse(self.sTestManagerUrl);
604
605 if sys.version_info[0] >= 3 \
606 or (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
607 if self._oParsedTmUrl.scheme == 'https': # pylint: disable=E1101
608 self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname,
609 timeout = self.kcSecTestManagerRequestTimeout);
610 else:
611 self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname,
612 timeout = self.kcSecTestManagerRequestTimeout);
613 else:
614 if self._oParsedTmUrl.scheme == 'https': # pylint: disable=E1101
615 self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname);
616 else:
617 self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname);
618 self._dHttpHeader = \
619 {
620 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
621 'User-Agent': 'TestDriverReporter/%s.0 (%s, %s)' % (__version__, utils.getHostOs(), utils.getHostArch(),),
622 'Accept': 'text/plain,application/x-www-form-urlencoded',
623 'Accept-Encoding': 'identity',
624 'Cache-Control': 'max-age=0',
625 #'Connection': 'keep-alive',
626 };
627
628 dParams = {
629 constants.tbreq.ALL_PARAM_TESTBOX_UUID: self.sTestBoxUuid,
630 constants.tbreq.ALL_PARAM_TESTBOX_ID: self.idTestBox,
631 constants.tbreq.RESULT_PARAM_TEST_SET_ID: self.idTestSet,
632 };
633 self._sTmServerPath = '/%s/testboxdisp.py?%s' \
634 % ( self._oParsedTmUrl.path.strip('/'), # pylint: disable=E1101
635 urllib.urlencode(dParams), );
636
637 def __del__(self):
638 """Flush pending log messages?"""
639 if len(self._asXml) > 0:
640 self._xmlDoFlush(self._asXml, fRetry = True, fDtor = True);
641
642 def _writeOutput(self, sText):
643 """ Does the actual writing and flushing. """
644 print >> self.oOutput, sText.encode('ascii', 'replace');
645 if self.fFlushEachLine: self.oOutput.flush();
646 return None;
647
648 #
649 # Talking to TM.
650 #
651
652 def _processTmStatusResponse(self, oConn, sOperation, fClose = True):
653 """
654 Processes HTTP reponse from the test manager.
655 Returns True, False or None. None should be retried, the others not.
656 May raise exception on HTTP issue (retry ok).
657 """
658 import httplib;
659 from common import constants;
660
661 # Read the response and (optionally) close the connection.
662 oResponse = oConn.getresponse();
663 try:
664 sRspBody = oResponse.read();
665 except httplib.IncompleteRead, oXcpt:
666 self._writeOutput('%s: %s: Warning: httplib.IncompleteRead: %s [expected %s, got %s]'
667 % (utils.getTimePrefix(), sOperation, oXcpt, oXcpt.expected, len(oXcpt.partial),));
668 sRspBody = oXcpt.partial;
669 if fClose is True:
670 try: oConn.close();
671 except: pass;
672
673 # Check the content type.
674 sContentType = oResponse.getheader('Content-Type');
675 if sContentType is not None and sContentType == 'application/x-www-form-urlencoded; charset=utf-8':
676
677 # Parse the body and check the RESULT parameter.
678 dResponse = self._fnUrlParseQs(sRspBody, strict_parsing = True);
679 sResult = dResponse.get(constants.tbresp.ALL_PARAM_RESULT, None);
680 if isinstance(sResult, list):
681 sResult = sResult[0] if len(sResult) == 1 else '%d results' % (len(sResult),);
682
683 if sResult is not None:
684 if sResult == constants.tbresp.STATUS_ACK:
685 return True;
686 if sResult == constants.tbresp.STATUS_NACK:
687 self._writeOutput('%s: %s: Failed (%s). (dResponse=%s)'
688 % (utils.getTimePrefix(), sOperation, sResult, dResponse,));
689 return False;
690
691 self._writeOutput('%s: %s: Failed - dResponse=%s' % (utils.getTimePrefix(), sOperation, dResponse,));
692 else:
693 self._writeOutput('%s: %s: Unexpected Content-Type: %s' % (utils.getTimePrefix(), sOperation, sContentType,));
694 self._writeOutput('%s: %s: Body: %s' % (utils.getTimePrefix(), sOperation, sRspBody,));
695 return None;
696
697 def _doUploadFile(self, oSrcFile, sSrcFilename, sDescription, sKind, sMime):
698 """ Uploads the given file to the test manager. """
699
700 # Prepare header and url.
701 dHeader = dict(self._dHttpHeader);
702 dHeader['Content-Type'] = 'application/octet-stream';
703 self._writeOutput('%s: _doUploadFile: sHeader=%s' % (utils.getTimePrefix(), dHeader,));
704 oSrcFile.seek(0, 2);
705 self._writeOutput('%s: _doUploadFile: size=%d' % (utils.getTimePrefix(), oSrcFile.tell(),));
706 oSrcFile.seek(0);
707
708 from common import constants;
709 sUrl = self._sTmServerPath + '&' \
710 + self._fnUrlEncode({ constants.tbreq.UPLOAD_PARAM_NAME: os.path.basename(sSrcFilename),
711 constants.tbreq.UPLOAD_PARAM_DESC: sDescription,
712 constants.tbreq.UPLOAD_PARAM_KIND: sKind,
713 constants.tbreq.UPLOAD_PARAM_MIME: sMime,
714 constants.tbreq.ALL_PARAM_ACTION: constants.tbreq.UPLOAD,
715 });
716
717 # Retry loop.
718 secStart = utils.timestampSecond();
719 while True:
720 try:
721 oConn = self._fnTmConnect();
722 oConn.request('POST', sUrl, oSrcFile.read(), dHeader);
723 fRc = self._processTmStatusResponse(oConn, '_doUploadFile', fClose = True);
724 oConn.close();
725 if fRc is not None:
726 return fRc;
727 except:
728 logXcpt('warning: exception during UPLOAD request');
729
730 if utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
731 self._writeOutput('%s: _doUploadFile: Timed out.' % (utils.getTimePrefix(),));
732 break;
733 try: oSrcFile.seek(0);
734 except:
735 logXcpt();
736 break;
737 self._writeOutput('%s: _doUploadFile: Retrying...' % (utils.getTimePrefix(), ));
738 time.sleep(2);
739
740 return False;
741
742 def _doUploadString(self, sSrc, sSrcName, sDescription, sKind, sMime):
743 """ Uploads the given string as a separate file to the test manager. """
744
745 # Prepare header and url.
746 dHeader = dict(self._dHttpHeader);
747 dHeader['Content-Type'] = 'application/octet-stream';
748 self._writeOutput('%s: _doUploadString: sHeader=%s' % (utils.getTimePrefix(), dHeader,));
749 self._writeOutput('%s: _doUploadString: size=%d' % (utils.getTimePrefix(), sys.getsizeof(sSrc),));
750
751 from common import constants;
752 sUrl = self._sTmServerPath + '&' \
753 + self._fnUrlEncode({ constants.tbreq.UPLOAD_PARAM_NAME: os.path.basename(sSrcName),
754 constants.tbreq.UPLOAD_PARAM_DESC: sDescription,
755 constants.tbreq.UPLOAD_PARAM_KIND: sKind,
756 constants.tbreq.UPLOAD_PARAM_MIME: sMime,
757 constants.tbreq.ALL_PARAM_ACTION: constants.tbreq.UPLOAD,
758 });
759
760 # Retry loop.
761 secStart = utils.timestampSecond();
762 while True:
763 try:
764 oConn = self._fnTmConnect();
765 oConn.request('POST', sUrl, sSrc, dHeader);
766 fRc = self._processTmStatusResponse(oConn, '_doUploadString', fClose = True);
767 oConn.close();
768 if fRc is not None:
769 return fRc;
770 except:
771 logXcpt('warning: exception during UPLOAD request');
772
773 if utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
774 self._writeOutput('%s: _doUploadString: Timed out.' % (utils.getTimePrefix(),));
775 break;
776 self._writeOutput('%s: _doUploadString: Retrying...' % (utils.getTimePrefix(), ));
777 time.sleep(2);
778
779 return False;
780
781 def _xmlDoFlush(self, asXml, fRetry = False, fDtor = False):
782 """
783 The code that does the actual talking to the server.
784 Used by both xmlFlush and __del__.
785 """
786 secStart = utils.timestampSecond();
787 while True:
788 fRc = None;
789 try:
790 # Post.
791 from common import constants;
792 sPostBody = self._fnUrlEncode({constants.tbreq.XML_RESULT_PARAM_BODY: '\n'.join(asXml),});
793 oConn = self._fnTmConnect();
794 oConn.request('POST',
795 self._sTmServerPath + ('&%s=%s' % (constants.tbreq.ALL_PARAM_ACTION, constants.tbreq.XML_RESULTS)),
796 sPostBody,
797 self._dHttpHeader);
798
799 fRc = self._processTmStatusResponse(oConn, '_xmlDoFlush', fClose = True);
800 if fRc is True:
801 if self.fDebugXml:
802 self._writeOutput('_xmlDoFlush:\n%s' % ('\n'.join(asXml),));
803 return (None, False);
804 if fRc is False:
805 self._writeOutput('_xmlDoFlush: Failed - we should abort the test, really.');
806 return (None, True);
807 except Exception, oXcpt:
808 if not fDtor:
809 logXcpt('warning: exception during XML_RESULTS request');
810 else:
811 self._writeOutput('warning: exception during XML_RESULTS request: %s' % (oXcpt,));
812
813 if fRetry is not True \
814 or utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
815 break;
816 time.sleep(2);
817
818 return (asXml, False);
819
820
821 #
822 # Overridden methods.
823 #
824
825 def isLocal(self):
826 return False;
827
828 def log(self, iLevel, sText, sCaller, sTsPrf):
829 if iLevel <= self.iVerbose:
830 if self.iDebug > 0:
831 sLogText = '%s %30s: %s' % (sTsPrf, sCaller, sText);
832 else:
833 sLogText = '%s %s: %s' % (sTsPrf, self.sName, sText);
834 self._writeOutput(sLogText);
835 return 0;
836
837 def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
838 fRc = True;
839 if sKind in [ 'text', 'log', ] or sKind.startswith('log/') or sKind.startswith('info/'):
840 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
841 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
842 self.xmlFlush();
843 g_oLock.release();
844 self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'text/plain');
845 g_oLock.acquire();
846 elif sKind.startswith('screenshot/'):
847 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
848 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
849 self.xmlFlush();
850 g_oLock.release();
851 self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'image/png');
852 g_oLock.acquire();
853 else:
854 self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
855 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
856 return fRc;
857
858 def addLogString(self, sLog, sLogName, sDescription, sKind, sCaller, sTsPrf):
859 fRc = True;
860 if sKind in [ 'text', 'log', ] or sKind.startswith('log/') or sKind.startswith('info/'):
861 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
862 % (sLogName, sKind, sDescription), sCaller, sTsPrf);
863 self.xmlFlush();
864 g_oLock.release();
865 self._doUploadString(sLog, sLogName, sDescription, sKind, 'text/plain');
866 g_oLock.acquire();
867 else:
868 self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
869 % (sLogName, sKind, sDescription), sCaller, sTsPrf);
870 return fRc;
871
872 def xmlFlush(self, fRetry = False, fForce = False):
873 """
874 Flushes the XML back log. Called with the lock held, may leave it
875 while communicating with the server.
876 """
877 if not self._fXmlFlushing:
878 asXml = self._asXml;
879 self._asXml = [];
880 if len(asXml) > 0 or fForce is True:
881 self._fXmlFlushing = True;
882
883 g_oLock.release();
884 (asXml, fIncErrors) = self._xmlDoFlush(asXml, fRetry = fRetry);
885 g_oLock.acquire();
886
887 if fIncErrors:
888 self.testIncErrors();
889
890 self._fXmlFlushing = False;
891 if asXml is None:
892 self._secTsXmlFlush = utils.timestampSecond();
893 else:
894 self._asXml = asXml + self._asXml;
895 return True;
896
897 self._secTsXmlFlush = utils.timestampSecond();
898 return False;
899
900 def _xmlFlushIfNecessary(self, fPolling = False, sDebug = None):
901 """Flushes the XML back log if necessary."""
902 tsNow = utils.timestampSecond();
903 cSecs = tsNow - self._secTsXmlFlush;
904 cSecsLast = tsNow - self._secTsXmlLast;
905 if fPolling is not True:
906 self._secTsXmlLast = tsNow;
907
908 # Absolute flush thresholds.
909 if cSecs >= self.kcSecXmlFlushMax:
910 return self.xmlFlush();
911 if len(self._asXml) >= self.kcLinesXmlFlush:
912 return self.xmlFlush();
913
914 # Flush if idle long enough.
915 if cSecs >= self.kcSecXmlFlushMin \
916 and cSecsLast >= self.kcSecXmlFlushIdle:
917 return self.xmlFlush();
918
919 _ = sDebug;
920 return False;
921
922 def _xmlWrite(self, asText, fIndent = True):
923 """XML output function for the reporter."""
924 self._asXml += asText;
925 self._xmlFlushIfNecessary();
926 _ = fIndent; # No pretty printing, thank you.
927 return None;
928
929 def subXmlStart(self, oFileWrapper):
930 oFileWrapper.sXmlBuffer = '';
931 return None;
932
933 def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
934 oFileWrapper.sXmlBuffer += sRawXml;
935 _ = sCaller;
936 return None;
937
938 def subXmlEnd(self, oFileWrapper):
939 sRawXml = oFileWrapper.sXmlBuffer;
940 ## @todo should validate the document here and maybe auto terminate things. Adding some hints to have the server do
941 # this instead.
942 g_oLock.acquire();
943 self._asXml += [ '<PushHint testdepth="%d"/>' % (len(self.atTests),),
944 sRawXml,
945 '<PopHint testdepth="%d"/>' % (len(self.atTests),),];
946 self._xmlFlushIfNecessary();
947 g_oLock.release();
948 return None;
949
950 def doPollWork(self, sDebug = None):
951 if len(self._asXml) > 0:
952 g_oLock.acquire();
953 self._xmlFlushIfNecessary(fPolling = True, sDebug = sDebug);
954 g_oLock.release();
955 return None;
956
957
958#
959# Helpers
960#
961
962def logXcptWorker(iLevel, fIncErrors, sPrefix="", sText=None, cFrames=1):
963 """
964 Log an exception, optionally with a preceeding message and more than one
965 call frame.
966 """
967 g_oLock.acquire();
968 if fIncErrors:
969 g_oReporter.testIncErrors();
970
971 ## @todo skip all this if iLevel is too high!
972
973 # Try get exception info.
974 sTsPrf = utils.getTimePrefix();
975 try:
976 oType, oValue, oTraceback = sys.exc_info();
977 except:
978 oType = oValue = oTraceback = None;
979 if oType is not None:
980
981 # Try format the info
982 try:
983 rc = 0;
984 sCaller = utils.getCallerName(oTraceback.tb_frame);
985 if sText is not None:
986 rc = g_oReporter.log(iLevel, "%s%s" % (sPrefix, sText), sCaller, sTsPrf);
987 asInfo = [];
988 try:
989 asInfo = asInfo + traceback.format_exception_only(oType, oValue);
990 if cFrames is not None and cFrames <= 1:
991 asInfo = asInfo + traceback.format_tb(oTraceback, 1);
992 else:
993 asInfo.append('Traceback:')
994 asInfo = asInfo + traceback.format_tb(oTraceback, cFrames);
995 asInfo.append('Stack:')
996 asInfo = asInfo + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
997 except:
998 g_oReporter.log(0, '** internal-error: Hit exception #2! %s' % (traceback.format_exc()), sCaller, sTsPrf);
999
1000 if len(asInfo) > 0:
1001 # Do the logging.
1002 for sItem in asInfo:
1003 asLines = sItem.splitlines();
1004 for sLine in asLines:
1005 rc = g_oReporter.log(iLevel, '%s%s' % (sPrefix, sLine), sCaller, sTsPrf);
1006
1007 else:
1008 g_oReporter.log(iLevel, 'No exception info...', sCaller, sTsPrf);
1009 rc = -3;
1010 except:
1011 g_oReporter.log(0, '** internal-error: Hit exception! %s' % (traceback.format_exc()), None, sTsPrf);
1012 rc = -2;
1013 else:
1014 g_oReporter.log(0, '** internal-error: No exception! %s'
1015 % (utils.getCallerName(iFrame=3)), utils.getCallerName(iFrame=3), sTsPrf);
1016 rc = -1;
1017
1018 g_oLock.release();
1019 return rc;
1020
1021#
1022# The public Classes
1023#
1024class FileWrapper(object):
1025 """ File like class for TXS EXEC and similar. """
1026 def __init__(self, sPrefix):
1027 self.sPrefix = sPrefix;
1028
1029 def __del__(self):
1030 self.close();
1031
1032 def close(self):
1033 """ file.close """
1034 # Nothing to be done.
1035 return;
1036
1037 def read(self, cb):
1038 """file.read"""
1039 _ = cb;
1040 return "";
1041
1042 def write(self, sText):
1043 """file.write"""
1044 if isinstance(sText, array.array):
1045 try:
1046 sText = sText.tostring();
1047 except:
1048 pass;
1049 g_oLock.acquire();
1050 try:
1051 sTsPrf = utils.getTimePrefix();
1052 sCaller = utils.getCallerName();
1053 asLines = sText.splitlines();
1054 for sLine in asLines:
1055 g_oReporter.log(0, '%s: %s' % (self.sPrefix, sLine), sCaller, sTsPrf);
1056 except:
1057 traceback.print_exc();
1058 g_oLock.release();
1059 return None;
1060
1061class FileWrapperTestPipe(object):
1062 """ File like class for the test pipe (TXS EXEC and similar). """
1063 def __init__(self):
1064 self.sPrefix = '';
1065 self.fStarted = False;
1066 self.fClosed = False;
1067 self.sTagBuffer = None;
1068
1069 def __del__(self):
1070 self.close();
1071
1072 def close(self):
1073 """ file.close """
1074 if self.fStarted is True and self.fClosed is False:
1075 self.fClosed = True;
1076 try: g_oReporter.subXmlEnd(self);
1077 except:
1078 try: traceback.print_exc();
1079 except: pass;
1080 return True;
1081
1082 def read(self, cb = None):
1083 """file.read"""
1084 _ = cb;
1085 return "";
1086
1087 def write(self, sText):
1088 """file.write"""
1089 # lazy start.
1090 if self.fStarted is not True:
1091 try:
1092 g_oReporter.subXmlStart(self);
1093 except:
1094 traceback.print_exc();
1095 self.fStarted = True;
1096
1097 if isinstance(sText, array.array):
1098 try:
1099 sText = sText.tostring();
1100 except:
1101 pass;
1102 try:
1103 g_oReporter.subXmlWrite(self, sText, utils.getCallerName());
1104 # Parse the supplied text and look for <Failed.../> tags to keep track of the
1105 # error counter. This is only a very lazy aproach.
1106 sText.strip();
1107 idxText = 0;
1108 while len(sText) > 0:
1109 if self.sTagBuffer is None:
1110 # Look for the start of a tag.
1111 idxStart = sText[idxText:].find('<');
1112 if idxStart != -1:
1113 # Look for the end of the tag.
1114 idxEnd = sText[idxStart:].find('>');
1115
1116 # If the end was found inside the current buffer, parse the line,
1117 # else we have to save it for later.
1118 if idxEnd != -1:
1119 idxEnd += idxStart + 1;
1120 self._processXmlElement(sText[idxStart:idxEnd]);
1121 idxText = idxEnd;
1122 else:
1123 self.sTagBuffer = sText[idxStart:];
1124 idxText = len(sText);
1125 else:
1126 idxText = len(sText);
1127 else:
1128 # Search for the end of the tag and parse the whole tag.
1129 idxEnd = sText[idxText:].find('>');
1130 if idxEnd != -1:
1131 idxEnd += idxStart + 1;
1132 self._processXmlElement(self.sTagBuffer + sText[idxText:idxEnd]);
1133 self.sTagBuffer = None;
1134 idxText = idxEnd;
1135 else:
1136 self.sTagBuffer = self.sTagBuffer + sText[idxText:];
1137 idxText = len(sText);
1138
1139 sText = sText[idxText:];
1140 sText = sText.lstrip();
1141 except:
1142 traceback.print_exc();
1143 return None;
1144
1145 def _processXmlElement(self, sElement):
1146 """
1147 Processes a complete XML tag (so far we only search for the Failed to tag
1148 to keep track of the error counter.
1149 """
1150 # Make sure we don't parse any space between < and the element name.
1151 sElement = sElement.strip();
1152
1153 # Find the end of the name
1154 idxEndName = sElement.find(' ');
1155 if idxEndName == -1:
1156 idxEndName = sElement.find('/');
1157 if idxEndName == -1:
1158 idxEndName = sElement.find('>');
1159
1160 if idxEndName != -1:
1161 if sElement[1:idxEndName] == 'Failed':
1162 g_oLock.acquire();
1163 g_oReporter.testIncErrors();
1164 g_oLock.release();
1165 else:
1166 error('_processXmlElement(%s)' % sElement);
1167
1168
1169#
1170# The public APIs.
1171#
1172
1173def log(sText):
1174 """Writes the specfied text to the log."""
1175 g_oLock.acquire();
1176 try:
1177 rc = g_oReporter.log(1, sText, utils.getCallerName(), utils.getTimePrefix());
1178 except:
1179 rc = -1;
1180 g_oLock.release();
1181 return rc;
1182
1183def logXcpt(sText=None, cFrames=1):
1184 """
1185 Log an exception, optionally with a preceeding message and more than one
1186 call frame.
1187 """
1188 return logXcptWorker(1, False, "", sText, cFrames);
1189
1190def log2(sText):
1191 """Log level 2: Writes the specfied text to the log."""
1192 g_oLock.acquire();
1193 try:
1194 rc = g_oReporter.log(2, sText, utils.getCallerName(), utils.getTimePrefix());
1195 except:
1196 rc = -1;
1197 g_oLock.release();
1198 return rc;
1199
1200def log2Xcpt(sText=None, cFrames=1):
1201 """
1202 Log level 2: Log an exception, optionally with a preceeding message and
1203 more than one call frame.
1204 """
1205 return logXcptWorker(2, False, "", sText, cFrames);
1206
1207def maybeErr(fIsError, sText):
1208 """ Maybe error or maybe normal log entry. """
1209 if fIsError is True:
1210 return error(sText);
1211 return log(sText);
1212
1213def maybeErrXcpt(fIsError, sText=None, cFrames=1):
1214 """ Maybe error or maybe normal log exception entry. """
1215 if fIsError is True:
1216 return errorXcpt(sText, cFrames);
1217 return logXcpt(sText, cFrames);
1218
1219def maybeLog(fIsNotError, sText):
1220 """ Maybe error or maybe normal log entry. """
1221 if fIsNotError is not True:
1222 return error(sText);
1223 return log(sText);
1224
1225def maybeLogXcpt(fIsNotError, sText=None, cFrames=1):
1226 """ Maybe error or maybe normal log exception entry. """
1227 if fIsNotError is not True:
1228 return errorXcpt(sText, cFrames);
1229 return logXcpt(sText, cFrames);
1230
1231def error(sText):
1232 """
1233 Writes the specfied error message to the log.
1234
1235 This will add an error to the current test.
1236
1237 Always returns False for the convenience of methods returning boolean
1238 success indicators.
1239 """
1240 g_oLock.acquire();
1241 g_oReporter.testIncErrors();
1242 try:
1243 g_oReporter.log(0, '** error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1244 except:
1245 pass;
1246 g_oLock.release();
1247 return False;
1248
1249def errorXcpt(sText=None, cFrames=1):
1250 """
1251 Log an error caused by an exception. If sText is given, it will preceed
1252 the exception information. cFrames can be used to display more stack.
1253
1254 This will add an error to the current test.
1255
1256 Always returns False for the convenience of methods returning boolean
1257 success indicators.
1258 """
1259 logXcptWorker(0, True, '** error: ', sText, cFrames);
1260 return False;
1261
1262def errorTimeout(sText):
1263 """
1264 Flags the current test as having timed out and writes the specified message to the log.
1265
1266 This will add an error to the current test.
1267
1268 Always returns False for the convenience of methods returning boolean
1269 success indicators.
1270 """
1271 g_oLock.acquire();
1272 g_oReporter.testSetTimedOut();
1273 try:
1274 g_oReporter.log(0, '** timeout-error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1275 except:
1276 pass;
1277 g_oLock.release();
1278 return False;
1279
1280def fatal(sText):
1281 """
1282 Writes a fatal error to the log.
1283
1284 This will add an error to the current test.
1285
1286 Always returns False for the convenience of methods returning boolean
1287 success indicators.
1288 """
1289 g_oLock.acquire();
1290 g_oReporter.testIncErrors();
1291 try:
1292 g_oReporter.log(0, '** fatal error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1293 except:
1294 pass
1295 g_oLock.release();
1296 return False;
1297
1298def fatalXcpt(sText=None, cFrames=1):
1299 """
1300 Log a fatal error caused by an exception. If sText is given, it will
1301 preceed the exception information. cFrames can be used to display more
1302 stack.
1303
1304 This will add an error to the current test.
1305
1306 Always returns False for the convenience of methods returning boolean
1307 success indicators.
1308 """
1309 logXcptWorker(0, True, "** fatal error: ", sText, cFrames);
1310 return False;
1311
1312def addLogFile(sFilename, sKind, sDescription = '', sAltName = None):
1313 """
1314 Adds the specified log file to the report if the file exists.
1315
1316 The sDescription is a free form description of the log file.
1317
1318 The sKind parameter is for adding some machine parsable hint what kind of
1319 log file this really is.
1320
1321 Returns True on success, False on failure (no ENOENT errors are logged).
1322 """
1323 sTsPrf = utils.getTimePrefix();
1324 sCaller = utils.getCallerName();
1325 fRc = False;
1326 if sAltName is None:
1327 sAltName = sFilename;
1328
1329 try:
1330 oSrcFile = utils.openNoInherit(sFilename, 'rb');
1331 except IOError, oXcpt:
1332 if oXcpt.errno != errno.ENOENT:
1333 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1334 else:
1335 logXcpt('addLogFile(%s,%s,%s) IOError' % (sFilename, sDescription, sKind));
1336 except:
1337 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1338 else:
1339 g_oLock.acquire();
1340 fRc = g_oReporter.addLogFile(oSrcFile, sFilename, sAltName, sDescription, sKind, sCaller, sTsPrf);
1341 g_oLock.release();
1342 oSrcFile.close();
1343 return fRc;
1344
1345def addLogString(sLog, sLogName, sKind, sDescription = ''):
1346 """
1347 Adds the specified log string to the report.
1348
1349 The sLog parameter sets the name of the log file.
1350
1351 The sDescription is a free form description of the log file.
1352
1353 The sKind parameter is for adding some machine parsable hint what kind of
1354 log file this really is.
1355
1356 Returns True on success, False on failure (no ENOENT errors are logged).
1357 """
1358 sTsPrf = utils.getTimePrefix();
1359 sCaller = utils.getCallerName();
1360 fRc = False;
1361
1362 g_oLock.acquire();
1363 fRc = g_oReporter.addLogString(sLog, sLogName, sDescription, sKind, sCaller, sTsPrf);
1364 g_oLock.release();
1365 return fRc;
1366
1367def isLocal():
1368 """Is this a local reporter?"""
1369 return g_oReporter.isLocal()
1370
1371def incVerbosity():
1372 """Increases the verbosity level."""
1373 return g_oReporter.incVerbosity()
1374
1375def incDebug():
1376 """Increases the debug level."""
1377 return g_oReporter.incDebug()
1378
1379def appendToProcessName(sAppend):
1380 """
1381 Appends sAppend to the base process name.
1382 Returns the new process name.
1383 """
1384 return g_oReporter.appendToProcessName(sAppend);
1385
1386def getErrorCount():
1387 """
1388 Get the current error count for the entire test run.
1389 """
1390 g_oLock.acquire();
1391 cErrors = g_oReporter.cErrors;
1392 g_oLock.release();
1393 return cErrors;
1394
1395def doPollWork(sDebug = None):
1396 """
1397 This can be called from wait loops and similar to make the reporter call
1398 home with pending XML and such.
1399 """
1400 g_oReporter.doPollWork(sDebug);
1401 return None;
1402
1403
1404#
1405# Test reporting, a bit similar to RTTestI*.
1406#
1407
1408def testStart(sName):
1409 """
1410 Starts a new test (pushes it).
1411 """
1412 g_oLock.acquire();
1413 rc = g_oReporter.testStart(sName, utils.getCallerName());
1414 g_oLock.release();
1415 return rc;
1416
1417def testValue(sName, sValue, sUnit):
1418 """
1419 Reports a benchmark value or something simiarlly useful.
1420 """
1421 g_oLock.acquire();
1422 rc = g_oReporter.testValue(sName, str(sValue), sUnit, utils.getCallerName());
1423 g_oLock.release();
1424 return rc;
1425
1426def testFailure(sDetails):
1427 """
1428 Reports a failure.
1429 We count these calls and testDone will use them to report PASSED or FAILED.
1430
1431 Returns False so that a return False line can be saved.
1432 """
1433 g_oLock.acquire();
1434 g_oReporter.testFailure(sDetails, utils.getCallerName());
1435 g_oLock.release();
1436 return False;
1437
1438def testFailureXcpt(sDetails = ''):
1439 """
1440 Reports a failure with exception.
1441 We count these calls and testDone will use them to report PASSED or FAILED.
1442
1443 Returns False so that a return False line can be saved.
1444 """
1445 # Extract exception info.
1446 try:
1447 oType, oValue, oTraceback = sys.exc_info();
1448 except:
1449 oType = oValue, oTraceback = None;
1450 if oType is not None:
1451 sCaller = utils.getCallerName(oTraceback.tb_frame);
1452 sXcpt = ' '.join(traceback.format_exception_only(oType, oValue));
1453 else:
1454 sCaller = utils.getCallerName();
1455 sXcpt = 'No exception at %s' % (sCaller,);
1456
1457 # Use testFailure to do the work.
1458 g_oLock.acquire();
1459 if sDetails == '':
1460 g_oReporter.testFailure('Exception: %s' % (sXcpt,), sCaller);
1461 else:
1462 g_oReporter.testFailure('%s: %s' % (sDetails, sXcpt), sCaller);
1463 g_oLock.release();
1464 return False;
1465
1466def testDone(fSkipped = False):
1467 """
1468 Completes the current test (pops it), logging PASSED / FAILURE.
1469
1470 Returns a tuple with the name of the test and its error count.
1471 """
1472 g_oLock.acquire();
1473 rc = g_oReporter.testDone(fSkipped, utils.getCallerName());
1474 g_oLock.release();
1475 return rc;
1476
1477def testErrorCount():
1478 """
1479 Gets the error count of the current test.
1480
1481 Returns the number of errors.
1482 """
1483 g_oLock.acquire();
1484 cErrors = g_oReporter.testErrorCount();
1485 g_oLock.release();
1486 return cErrors;
1487
1488def testCleanup():
1489 """
1490 Closes all open tests with a generic error condition.
1491
1492 Returns True if no open tests, False if something had to be closed with failure.
1493 """
1494 g_oLock.acquire();
1495 fRc = g_oReporter.testCleanup(utils.getCallerName());
1496 g_oReporter.xmlFlush(fRetry = False, fForce = True);
1497 g_oLock.release();
1498 return fRc;
1499
1500
1501#
1502# Sub XML stuff.
1503#
1504
1505def addSubXmlFile(sFilename):
1506 """
1507 Adds a sub-xml result file to the party.
1508 """
1509 fRc = False;
1510 try:
1511 oSrcFile = utils.openNoInherit(sFilename, 'r');
1512 except IOError, oXcpt:
1513 if oXcpt.errno != errno.ENOENT:
1514 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1515 except:
1516 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1517 else:
1518 try:
1519 oWrapper = FileWrapperTestPipe()
1520 oWrapper.write(oSrcFile.read());
1521 oWrapper.close();
1522 except:
1523 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1524 oSrcFile.close();
1525
1526 return fRc;
1527
1528
1529#
1530# Other useful debugging tools.
1531#
1532
1533def logAllStacks(cFrames = None):
1534 """
1535 Logs the stacks of all python threads.
1536 """
1537 sTsPrf = utils.getTimePrefix();
1538 sCaller = utils.getCallerName();
1539 g_oLock.acquire();
1540
1541 cThread = 0;
1542 for idThread, oStack in sys._current_frames().items(): # >=2.5, a bit ugly - pylint: disable=W0212
1543 try:
1544 if cThread > 0:
1545 g_oReporter.log(1, '', sCaller, sTsPrf);
1546 g_oReporter.log(1, 'Thread %s (%#x)' % (idThread, idThread), sCaller, sTsPrf);
1547 try:
1548 asInfo = traceback.format_stack(oStack, cFrames);
1549 except:
1550 g_oReporter.log(1, ' Stack formatting failed w/ exception', sCaller, sTsPrf);
1551 else:
1552 for sInfo in asInfo:
1553 asLines = sInfo.splitlines();
1554 for sLine in asLines:
1555 g_oReporter.log(1, sLine, sCaller, sTsPrf);
1556 except:
1557 pass;
1558 cThread += 1;
1559
1560 g_oLock.release();
1561 return None;
1562
1563def checkTestManagerConnection():
1564 """
1565 Checks the connection to the test manager.
1566
1567 Returns True if the connection is fine, False if not, None if not remote
1568 reporter.
1569
1570 Note! This as the sideeffect of flushing XML.
1571 """
1572 g_oLock.acquire();
1573 fRc = g_oReporter.xmlFlush(fRetry = False, fForce = True);
1574 g_oLock.release();
1575 return fRc;
1576
1577def flushall(fSkipXml = False):
1578 """
1579 Flushes all output streams, both standard and logger related.
1580 This may also push data to the remote test manager.
1581 """
1582 try: sys.stdout.flush();
1583 except: pass;
1584 try: sys.stderr.flush();
1585 except: pass;
1586
1587 if fSkipXml is not True:
1588 g_oLock.acquire();
1589 g_oReporter.xmlFlush(fRetry = False);
1590 g_oLock.release();
1591
1592 return True;
1593
1594
1595#
1596# Module initialization.
1597#
1598
1599def _InitReporterModule():
1600 """
1601 Instantiate the test reporter.
1602 """
1603 global g_oReporter, g_sReporterName
1604
1605 g_sReporterName = os.getenv("TESTBOX_REPORTER", "local");
1606 if g_sReporterName == "local":
1607 g_oReporter = LocalReporter();
1608 elif g_sReporterName == "remote":
1609 g_oReporter = RemoteReporter(); # Correct, but still plain stupid. pylint: disable=redefined-variable-type
1610 else:
1611 print >> sys.stderr, os.path.basename(__file__) + ": Unknown TESTBOX_REPORTER value: '" + g_sReporterName + "'";
1612 raise Exception("Unknown TESTBOX_REPORTER value '" + g_sReporterName + "'");
1613
1614if __name__ != "checker": # pychecker avoidance.
1615 _InitReporterModule();
1616
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette