VirtualBox

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

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

RemoteReporter:addLogFile: Try flush the XML before uploading and hope we'll get the uploaded files in the right place in the test results.

  • 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 61189 2016-05-25 11:54:30Z 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: 61189 $"
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 self.xmlFlush();
746 g_oLock.release();
747 self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'text/plain');
748 g_oLock.acquire();
749 elif sKind.startswith('screenshot/'):
750 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
751 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
752 self.xmlFlush();
753 g_oLock.release();
754 self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'image/png');
755 g_oLock.acquire();
756 else:
757 self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
758 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
759 return fRc;
760
761 def xmlFlush(self, fRetry = False, fForce = False):
762 """
763 Flushes the XML back log. Called with the lock held, may leave it
764 while communicating with the server.
765 """
766 if not self._fXmlFlushing:
767 asXml = self._asXml;
768 self._asXml = [];
769 if len(asXml) > 0 or fForce is True:
770 self._fXmlFlushing = True;
771
772 g_oLock.release();
773 (asXml, fIncErrors) = self._xmlDoFlush(asXml, fRetry = fRetry);
774 g_oLock.acquire();
775
776 if fIncErrors:
777 self.testIncErrors();
778
779 self._fXmlFlushing = False;
780 if asXml is None:
781 self._secTsXmlFlush = utils.timestampSecond();
782 else:
783 self._asXml = asXml + self._asXml;
784 return True;
785
786 self._secTsXmlFlush = utils.timestampSecond();
787 return False;
788
789 def _xmlFlushIfNecessary(self):
790 """Flushes the XML back log if necessary."""
791 tsNow = utils.timestampSecond();
792 cSecs = tsNow - self._secTsXmlFlush;
793 cSecsLast = tsNow - self._secTsXmlLast;
794 self._secTsXmlLast = tsNow;
795
796 # Absolute flush thresholds.
797 if cSecs >= self.kcSecXmlFlushMax:
798 return self.xmlFlush();
799 if len(self._asXml) >= self.kcLinesXmlFlush:
800 return self.xmlFlush();
801
802 # Flush if idle long enough.
803 if cSecs >= self.kcSecXmlFlushMin \
804 and cSecsLast >= self.kcSecXmlFlushIdle:
805 return self.xmlFlush();
806
807 return False;
808
809 def _xmlWrite(self, asText, fIndent = True):
810 """XML output function for the reporter."""
811 self._asXml += asText;
812 self._xmlFlushIfNecessary();
813 _ = fIndent; # No pretty printing, thank you.
814 return None;
815
816 def subXmlStart(self, oFileWrapper):
817 oFileWrapper.sXmlBuffer = '';
818 return None;
819
820 def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
821 oFileWrapper.sXmlBuffer += sRawXml;
822 _ = sCaller;
823 return None;
824
825 def subXmlEnd(self, oFileWrapper):
826 sRawXml = oFileWrapper.sXmlBuffer;
827 ## @todo should validate the document here and maybe auto terminate things. Adding some hints to have the server do
828 # this instead.
829 g_oLock.acquire();
830 self._asXml += [ '<PushHint testdepth="%d"/>' % (len(self.atTests),),
831 sRawXml,
832 '<PopHint testdepth="%d"/>' % (len(self.atTests),),];
833 self._xmlFlushIfNecessary();
834 g_oLock.release();
835 return None;
836
837
838#
839# Helpers
840#
841
842def logXcptWorker(iLevel, fIncErrors, sPrefix="", sText=None, cFrames=1):
843 """
844 Log an exception, optionally with a preceeding message and more than one
845 call frame.
846 """
847 g_oLock.acquire();
848 if fIncErrors:
849 g_oReporter.testIncErrors();
850
851 ## @todo skip all this if iLevel is too high!
852
853 # Try get exception info.
854 sTsPrf = utils.getTimePrefix();
855 try:
856 oType, oValue, oTraceback = sys.exc_info();
857 except:
858 oType = oValue = oTraceback = None;
859 if oType is not None:
860
861 # Try format the info
862 try:
863 rc = 0;
864 sCaller = utils.getCallerName(oTraceback.tb_frame);
865 if sText is not None:
866 rc = g_oReporter.log(iLevel, "%s%s" % (sPrefix, sText), sCaller, sTsPrf);
867 asInfo = [];
868 try:
869 asInfo = asInfo + traceback.format_exception_only(oType, oValue);
870 if cFrames is not None and cFrames <= 1:
871 asInfo = asInfo + traceback.format_tb(oTraceback, 1);
872 else:
873 asInfo.append('Traceback:')
874 asInfo = asInfo + traceback.format_tb(oTraceback, cFrames);
875 asInfo.append('Stack:')
876 asInfo = asInfo + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
877 except:
878 g_oReporter.log(0, '** internal-error: Hit exception #2! %s' % (traceback.format_exc()), sCaller, sTsPrf);
879
880 if len(asInfo) > 0:
881 # Do the logging.
882 for sItem in asInfo:
883 asLines = sItem.splitlines();
884 for sLine in asLines:
885 rc = g_oReporter.log(iLevel, '%s%s' % (sPrefix, sLine), sCaller, sTsPrf);
886
887 else:
888 g_oReporter.log(iLevel, 'No exception info...', sCaller, sTsPrf);
889 rc = -3;
890 except:
891 g_oReporter.log(0, '** internal-error: Hit exception! %s' % (traceback.format_exc()), None, sTsPrf);
892 rc = -2;
893 else:
894 g_oReporter.log(0, '** internal-error: No exception! %s'
895 % (utils.getCallerName(iFrame=3)), utils.getCallerName(iFrame=3), sTsPrf);
896 rc = -1;
897
898 g_oLock.release();
899 return rc;
900
901#
902# The public Classes
903#
904class FileWrapper(object):
905 """ File like class for TXS EXEC and similar. """
906 def __init__(self, sPrefix):
907 self.sPrefix = sPrefix;
908
909 def __del__(self):
910 self.close();
911
912 def close(self):
913 """ file.close """
914 # Nothing to be done.
915 return;
916
917 def read(self, cb):
918 """file.read"""
919 _ = cb;
920 return "";
921
922 def write(self, sText):
923 """file.write"""
924 if isinstance(sText, array.array):
925 try:
926 sText = sText.tostring();
927 except:
928 pass;
929 g_oLock.acquire();
930 try:
931 sTsPrf = utils.getTimePrefix();
932 sCaller = utils.getCallerName();
933 asLines = sText.splitlines();
934 for sLine in asLines:
935 g_oReporter.log(0, '%s: %s' % (self.sPrefix, sLine), sCaller, sTsPrf);
936 except:
937 traceback.print_exc();
938 g_oLock.release();
939 return None;
940
941class FileWrapperTestPipe(object):
942 """ File like class for the test pipe (TXS EXEC and similar). """
943 def __init__(self):
944 self.sPrefix = '';
945 self.fStarted = False;
946 self.fClosed = False;
947 self.sTagBuffer = None;
948
949 def __del__(self):
950 self.close();
951
952 def close(self):
953 """ file.close """
954 if self.fStarted is True and self.fClosed is False:
955 self.fClosed = True;
956 try: g_oReporter.subXmlEnd(self);
957 except:
958 try: traceback.print_exc();
959 except: pass;
960 return True;
961
962 def read(self, cb = None):
963 """file.read"""
964 _ = cb;
965 return "";
966
967 def write(self, sText):
968 """file.write"""
969 # lazy start.
970 if self.fStarted is not True:
971 try:
972 g_oReporter.subXmlStart(self);
973 except:
974 traceback.print_exc();
975 self.fStarted = True;
976
977 if isinstance(sText, array.array):
978 try:
979 sText = sText.tostring();
980 except:
981 pass;
982 try:
983 g_oReporter.subXmlWrite(self, sText, utils.getCallerName());
984 # Parse the supplied text and look for <Failed.../> tags to keep track of the
985 # error counter. This is only a very lazy aproach.
986 sText.strip();
987 idxText = 0;
988 while len(sText) > 0:
989 if self.sTagBuffer is None:
990 # Look for the start of a tag.
991 idxStart = sText[idxText:].find('<');
992 if idxStart != -1:
993 # Look for the end of the tag.
994 idxEnd = sText[idxStart:].find('>');
995
996 # If the end was found inside the current buffer, parse the line,
997 # else we have to save it for later.
998 if idxEnd != -1:
999 idxEnd += idxStart + 1;
1000 self._processXmlElement(sText[idxStart:idxEnd]);
1001 idxText = idxEnd;
1002 else:
1003 self.sTagBuffer = sText[idxStart:];
1004 idxText = len(sText);
1005 else:
1006 idxText = len(sText);
1007 else:
1008 # Search for the end of the tag and parse the whole tag.
1009 idxEnd = sText[idxText:].find('>');
1010 if idxEnd != -1:
1011 idxEnd += idxStart + 1;
1012 self._processXmlElement(self.sTagBuffer + sText[idxText:idxEnd]);
1013 self.sTagBuffer = None;
1014 idxText = idxEnd;
1015 else:
1016 self.sTagBuffer = self.sTagBuffer + sText[idxText:];
1017 idxText = len(sText);
1018
1019 sText = sText[idxText:];
1020 sText = sText.lstrip();
1021 except:
1022 traceback.print_exc();
1023 return None;
1024
1025 def _processXmlElement(self, sElement):
1026 """
1027 Processes a complete XML tag (so far we only search for the Failed to tag
1028 to keep track of the error counter.
1029 """
1030 # Make sure we don't parse any space between < and the element name.
1031 sElement = sElement.strip();
1032
1033 # Find the end of the name
1034 idxEndName = sElement.find(' ');
1035 if idxEndName == -1:
1036 idxEndName = sElement.find('/');
1037 if idxEndName == -1:
1038 idxEndName = sElement.find('>');
1039
1040 if idxEndName != -1:
1041 if sElement[1:idxEndName] == 'Failed':
1042 g_oLock.acquire();
1043 g_oReporter.testIncErrors();
1044 g_oLock.release();
1045 else:
1046 error('_processXmlElement(%s)' % sElement);
1047
1048
1049#
1050# The public APIs.
1051#
1052
1053def log(sText):
1054 """Writes the specfied text to the log."""
1055 g_oLock.acquire();
1056 try:
1057 rc = g_oReporter.log(1, sText, utils.getCallerName(), utils.getTimePrefix());
1058 except:
1059 rc = -1;
1060 g_oLock.release();
1061 return rc;
1062
1063def logXcpt(sText=None, cFrames=1):
1064 """
1065 Log an exception, optionally with a preceeding message and more than one
1066 call frame.
1067 """
1068 return logXcptWorker(1, False, "", sText, cFrames);
1069
1070def log2(sText):
1071 """Log level 2: Writes the specfied text to the log."""
1072 g_oLock.acquire();
1073 try:
1074 rc = g_oReporter.log(2, sText, utils.getCallerName(), utils.getTimePrefix());
1075 except:
1076 rc = -1;
1077 g_oLock.release();
1078 return rc;
1079
1080def log2Xcpt(sText=None, cFrames=1):
1081 """
1082 Log level 2: Log an exception, optionally with a preceeding message and
1083 more than one call frame.
1084 """
1085 return logXcptWorker(2, False, "", sText, cFrames);
1086
1087def maybeErr(fIsError, sText):
1088 """ Maybe error or maybe normal log entry. """
1089 if fIsError is True:
1090 return error(sText);
1091 return log(sText);
1092
1093def maybeErrXcpt(fIsError, sText=None, cFrames=1):
1094 """ Maybe error or maybe normal log exception entry. """
1095 if fIsError is True:
1096 return errorXcpt(sText, cFrames);
1097 return logXcpt(sText, cFrames);
1098
1099def maybeLog(fIsNotError, sText):
1100 """ Maybe error or maybe normal log entry. """
1101 if fIsNotError is not True:
1102 return error(sText);
1103 return log(sText);
1104
1105def maybeLogXcpt(fIsNotError, sText=None, cFrames=1):
1106 """ Maybe error or maybe normal log exception entry. """
1107 if fIsNotError is not True:
1108 return errorXcpt(sText, cFrames);
1109 return logXcpt(sText, cFrames);
1110
1111def error(sText):
1112 """
1113 Writes the specfied error message to the log.
1114
1115 This will add an error to the current test.
1116
1117 Always returns False for the convenience of methods returning boolean
1118 success indicators.
1119 """
1120 g_oLock.acquire();
1121 g_oReporter.testIncErrors();
1122 try:
1123 g_oReporter.log(0, '** error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1124 except:
1125 pass;
1126 g_oLock.release();
1127 return False;
1128
1129def errorXcpt(sText=None, cFrames=1):
1130 """
1131 Log an error caused by an exception. If sText is given, it will preceed
1132 the exception information. cFrames can be used to display more stack.
1133
1134 This will add an error to the current test.
1135
1136 Always returns False for the convenience of methods returning boolean
1137 success indicators.
1138 """
1139 logXcptWorker(0, True, '** error: ', sText, cFrames);
1140 return False;
1141
1142def errorTimeout(sText):
1143 """
1144 Flags the current test as having timed out and writes the specified message to the log.
1145
1146 This will add an error to the current test.
1147
1148 Always returns False for the convenience of methods returning boolean
1149 success indicators.
1150 """
1151 g_oLock.acquire();
1152 g_oReporter.testSetTimedOut();
1153 try:
1154 g_oReporter.log(0, '** timeout-error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1155 except:
1156 pass;
1157 g_oLock.release();
1158 return False;
1159
1160def fatal(sText):
1161 """
1162 Writes a fatal error to the log.
1163
1164 This will add an error to the current test.
1165
1166 Always returns False for the convenience of methods returning boolean
1167 success indicators.
1168 """
1169 g_oLock.acquire();
1170 g_oReporter.testIncErrors();
1171 try:
1172 g_oReporter.log(0, '** fatal error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1173 except:
1174 pass
1175 g_oLock.release();
1176 return False;
1177
1178def fatalXcpt(sText=None, cFrames=1):
1179 """
1180 Log a fatal error caused by an exception. If sText is given, it will
1181 preceed the exception information. cFrames can be used to display more
1182 stack.
1183
1184 This will add an error to the current test.
1185
1186 Always returns False for the convenience of methods returning boolean
1187 success indicators.
1188 """
1189 logXcptWorker(0, True, "** fatal error: ", sText, cFrames);
1190 return False;
1191
1192def addLogFile(sFilename, sKind, sDescription = '', sAltName = None):
1193 """
1194 Adds the specified log file to the report if the file exists.
1195
1196 The sDescription is a free form description of the log file.
1197
1198 The sKind parameter is for adding some machine parsable hint what kind of
1199 log file this really is.
1200
1201 Returns True on success, False on failure (no ENOENT errors are logged).
1202 """
1203 sTsPrf = utils.getTimePrefix();
1204 sCaller = utils.getCallerName();
1205 fRc = False;
1206 if sAltName is None:
1207 sAltName = sFilename;
1208
1209 try:
1210 oSrcFile = utils.openNoInherit(sFilename, 'rb');
1211 except IOError, oXcpt:
1212 if oXcpt.errno != errno.ENOENT:
1213 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1214 else:
1215 logXcpt('addLogFile(%s,%s,%s) IOError' % (sFilename, sDescription, sKind));
1216 except:
1217 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1218 else:
1219 g_oLock.acquire();
1220 fRc = g_oReporter.addLogFile(oSrcFile, sFilename, sAltName, sDescription, sKind, sCaller, sTsPrf);
1221 g_oLock.release();
1222 oSrcFile.close();
1223 return fRc;
1224
1225def isLocal():
1226 """Is this a local reporter?"""
1227 return g_oReporter.isLocal()
1228
1229def incVerbosity():
1230 """Increases the verbosity level."""
1231 return g_oReporter.incVerbosity()
1232
1233def incDebug():
1234 """Increases the debug level."""
1235 return g_oReporter.incDebug()
1236
1237def getErrorCount():
1238 """
1239 Get the current error count for the entire test run.
1240 """
1241 g_oLock.acquire();
1242 cErrors = g_oReporter.cErrors;
1243 g_oLock.release();
1244 return cErrors;
1245
1246
1247#
1248# Test reporting, a bit similar to RTTestI*.
1249#
1250
1251def testStart(sName):
1252 """
1253 Starts a new test (pushes it).
1254 """
1255 g_oLock.acquire();
1256 rc = g_oReporter.testStart(sName, utils.getCallerName());
1257 g_oLock.release();
1258 return rc;
1259
1260def testValue(sName, sValue, sUnit):
1261 """
1262 Reports a benchmark value or something simiarlly useful.
1263 """
1264 g_oLock.acquire();
1265 rc = g_oReporter.testValue(sName, str(sValue), sUnit, utils.getCallerName());
1266 g_oLock.release();
1267 return rc;
1268
1269def testFailure(sDetails):
1270 """
1271 Reports a failure.
1272 We count these calls and testDone will use them to report PASSED or FAILED.
1273
1274 Returns False so that a return False line can be saved.
1275 """
1276 g_oLock.acquire();
1277 g_oReporter.testFailure(sDetails, utils.getCallerName());
1278 g_oLock.release();
1279 return False;
1280
1281def testFailureXcpt(sDetails = ''):
1282 """
1283 Reports a failure with exception.
1284 We count these calls and testDone will use them to report PASSED or FAILED.
1285
1286 Returns False so that a return False line can be saved.
1287 """
1288 # Extract exception info.
1289 try:
1290 oType, oValue, oTraceback = sys.exc_info();
1291 except:
1292 oType = oValue, oTraceback = None;
1293 if oType is not None:
1294 sCaller = utils.getCallerName(oTraceback.tb_frame);
1295 sXcpt = ' '.join(traceback.format_exception_only(oType, oValue));
1296 else:
1297 sCaller = utils.getCallerName();
1298 sXcpt = 'No exception at %s' % (sCaller,);
1299
1300 # Use testFailure to do the work.
1301 g_oLock.acquire();
1302 if sDetails == '':
1303 g_oReporter.testFailure('Exception: %s' % (sXcpt,), sCaller);
1304 else:
1305 g_oReporter.testFailure('%s: %s' % (sDetails, sXcpt), sCaller);
1306 g_oLock.release();
1307 return False;
1308
1309def testDone(fSkipped = False):
1310 """
1311 Completes the current test (pops it), logging PASSED / FAILURE.
1312
1313 Returns a tuple with the name of the test and its error count.
1314 """
1315 g_oLock.acquire();
1316 rc = g_oReporter.testDone(fSkipped, utils.getCallerName());
1317 g_oLock.release();
1318 return rc;
1319
1320def testErrorCount():
1321 """
1322 Gets the error count of the current test.
1323
1324 Returns the number of errors.
1325 """
1326 g_oLock.acquire();
1327 cErrors = g_oReporter.testErrorCount();
1328 g_oLock.release();
1329 return cErrors;
1330
1331def testCleanup():
1332 """
1333 Closes all open tests with a generic error condition.
1334
1335 Returns True if no open tests, False if something had to be closed with failure.
1336 """
1337 g_oLock.acquire();
1338 fRc = g_oReporter.testCleanup(utils.getCallerName());
1339 g_oReporter.xmlFlush(fRetry = False, fForce = True);
1340 g_oLock.release();
1341 return fRc;
1342
1343
1344#
1345# Sub XML stuff.
1346#
1347
1348def addSubXmlFile(sFilename):
1349 """
1350 Adds a sub-xml result file to the party.
1351 """
1352 fRc = False;
1353 try:
1354 oSrcFile = utils.openNoInherit(sFilename, 'r');
1355 except IOError, oXcpt:
1356 if oXcpt.errno != errno.ENOENT:
1357 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1358 except:
1359 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1360 else:
1361 try:
1362 oWrapper = FileWrapperTestPipe()
1363 oWrapper.write(oSrcFile.read());
1364 oWrapper.close();
1365 except:
1366 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1367 oSrcFile.close();
1368
1369 return fRc;
1370
1371
1372#
1373# Other useful debugging tools.
1374#
1375
1376def logAllStacks(cFrames = None):
1377 """
1378 Logs the stacks of all python threads.
1379 """
1380 sTsPrf = utils.getTimePrefix();
1381 sCaller = utils.getCallerName();
1382 g_oLock.acquire();
1383
1384 cThread = 0;
1385 for idThread, oStack in sys._current_frames().items(): # >=2.5, a bit ugly - pylint: disable=W0212
1386 try:
1387 if cThread > 0:
1388 g_oReporter.log(1, '', sCaller, sTsPrf);
1389 g_oReporter.log(1, 'Thread %s (%#x)' % (idThread, idThread), sCaller, sTsPrf);
1390 try:
1391 asInfo = traceback.format_stack(oStack, cFrames);
1392 except:
1393 g_oReporter.log(1, ' Stack formatting failed w/ exception', sCaller, sTsPrf);
1394 else:
1395 for sInfo in asInfo:
1396 asLines = sInfo.splitlines();
1397 for sLine in asLines:
1398 g_oReporter.log(1, sLine, sCaller, sTsPrf);
1399 except:
1400 pass;
1401 cThread += 1;
1402
1403 g_oLock.release();
1404 return None;
1405
1406def checkTestManagerConnection():
1407 """
1408 Checks the connection to the test manager.
1409
1410 Returns True if the connection is fine, False if not, None if not remote
1411 reporter.
1412
1413 Note! This as the sideeffect of flushing XML.
1414 """
1415 g_oLock.acquire();
1416 fRc = g_oReporter.xmlFlush(fRetry = False, fForce = True);
1417 g_oLock.release();
1418 return fRc;
1419
1420def flushall():
1421 """
1422 Flushes all output streams, both standard and logger related.
1423 """
1424 try: sys.stdout.flush();
1425 except: pass;
1426 try: sys.stderr.flush();
1427 except: pass;
1428
1429 # Note! Current no logger specific streams to flush.
1430
1431 return True;
1432
1433
1434#
1435# Module initialization.
1436#
1437
1438def _InitReporterModule():
1439 """
1440 Instantiate the test reporter.
1441 """
1442 global g_oReporter, g_sReporterName
1443
1444 g_sReporterName = os.getenv("TESTBOX_REPORTER", "local");
1445 if g_sReporterName == "local":
1446 g_oReporter = LocalReporter();
1447 elif g_sReporterName == "remote":
1448 g_oReporter = RemoteReporter();
1449 else:
1450 print >> sys.stderr, os.path.basename(__file__) + ": Unknown TESTBOX_REPORTER value: '" + g_sReporterName + "'";
1451 raise Exception("Unknown TESTBOX_REPORTER value '" + g_sReporterName + "'");
1452
1453if __name__ != "checker": # pychecker avoidance.
1454 _InitReporterModule();
1455
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