VirtualBox

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

Last change on this file since 60864 was 60864, checked in by vboxsync, 9 years ago

ValidationKit/reporter: grr

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

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