VirtualBox

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

Last change on this file since 53500 was 53500, checked in by vboxsync, 10 years ago

validation kit: log the URL during upload

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.2 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: reporter.py 53500 2014-12-10 16:26:19Z vboxsync $
3# pylint: disable=C0302
4
5"""
6Testdriver reporter module.
7"""
8
9__copyright__ = \
10"""
11Copyright (C) 2010-2014 Oracle Corporation
12
13This file is part of VirtualBox Open Source Edition (OSE), as
14available from http://www.215389.xyz. 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: 53500 $"
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 if sys.version_info[0] >= 3 \
547 or (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
548 if self._oParsedTmUrl.scheme == 'https': # pylint: disable=E1101
549 self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname,
550 timeout = self.kcSecTestManagerRequestTimeout);
551 else:
552 self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname,
553 timeout = self.kcSecTestManagerRequestTimeout);
554 else:
555 if self._oParsedTmUrl.scheme == 'https': # pylint: disable=E1101
556 self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname);
557 else:
558 self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname);
559 self._dHttpHeader = \
560 {
561 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
562 'User-Agent': 'TestDriverReporter/%s.0 (%s, %s)' % (__version__, utils.getHostOs(), utils.getHostArch(),),
563 'Accept': 'text/plain,application/x-www-form-urlencoded',
564 'Accept-Encoding': 'identity',
565 'Cache-Control': 'max-age=0',
566 #'Connection': 'keep-alive',
567 };
568
569 dParams = {
570 constants.tbreq.ALL_PARAM_TESTBOX_UUID: self.sTestBoxUuid,
571 constants.tbreq.ALL_PARAM_TESTBOX_ID: self.idTestBox,
572 constants.tbreq.RESULT_PARAM_TEST_SET_ID: self.idTestSet,
573 };
574 self._sTmServerPath = '/%s/testboxdisp.py?%s' \
575 % ( self._oParsedTmUrl.path.strip('/'), # pylint: disable=E1101
576 urllib.urlencode(dParams), );
577
578 def __del__(self):
579 """Flush pending log messages?"""
580 if len(self._asXml) > 0:
581 self._xmlDoFlush(self._asXml, fRetry = True, fDtor = True);
582
583 def _writeOutput(self, sText):
584 """ Does the actual writing and flushing. """
585 print >> self.oOutput, sText.encode('ascii', 'replace');
586 if self.fFlushEachLine: self.oOutput.flush();
587 return None;
588
589 #
590 # Talking to TM.
591 #
592
593 def _processTmStatusResponse(self, oConn, sOperation, fClose = True):
594 """
595 Processes HTTP reponse from the test manager.
596 Returns True, False or None. None should be retried, the others not.
597 May raise exception on HTTP issue (retry ok).
598 """
599 import httplib;
600 from common import constants;
601
602 # Read the response and (optionally) close the connection.
603 oResponse = oConn.getresponse();
604 try:
605 sRspBody = oResponse.read();
606 except httplib.IncompleteRead, oXcpt:
607 self._writeOutput('%s: %s: Warning: httplib.IncompleteRead: %s [expected %s, got %s]'
608 % (utils.getTimePrefix(), sOperation, oXcpt, oXcpt.expected, len(oXcpt.partial),));
609 sRspBody = oXcpt.partial;
610 if fClose is True:
611 try: oConn.close();
612 except: pass;
613
614 # Check the content type.
615 sContentType = oResponse.getheader('Content-Type');
616 if sContentType is not None and sContentType == 'application/x-www-form-urlencoded; charset=utf-8':
617
618 # Parse the body and check the RESULT parameter.
619 dResponse = self._fnUrlParseQs(sRspBody, strict_parsing = True);
620 sResult = dResponse.get(constants.tbresp.ALL_PARAM_RESULT, None);
621 if isinstance(sResult, list):
622 sResult = sResult[0] if len(sResult) == 1 else '%d results' % (len(sResult),);
623
624 if sResult is not None:
625 if sResult == constants.tbresp.STATUS_ACK:
626 return True;
627 if sResult == constants.tbresp.STATUS_NACK:
628 self._writeOutput('%s: %s: Failed (%s). (dResponse=%s)'
629 % (utils.getTimePrefix(), sOperation, sResult, dResponse,));
630 return False;
631
632 self._writeOutput('%s: %s: Failed - dResponse=%s' % (utils.getTimePrefix(), sOperation, dResponse,));
633 else:
634 self._writeOutput('%s: %s: Unexpected Content-Type: %s' % (utils.getTimePrefix(), sOperation, sContentType,));
635 self._writeOutput('%s: %s: Body: %s' % (utils.getTimePrefix(), sOperation, sRspBody,));
636 return None;
637
638 def _doUploadFile(self, oSrcFile, sSrcFilename, sDescription, sKind, sMime):
639 """ Uploads the given file to the test manager. """
640
641 # Prepare header and url.
642 dHeader = dict(self._dHttpHeader);
643 dHeader['Content-Type'] = 'application/octet-stream';
644 self._writeOutput('%s: _doUploadFile: sHeader=%s' % (utils.getTimePrefix(), dHeader,));
645 oSrcFile.seek(0, 2);
646 self._writeOutput('%s: _doUploadFile: size=%d' % (utils.getTimePrefix(), oSrcFile.tell(),));
647 oSrcFile.seek(0);
648
649 from common import constants;
650 sUrl = self._sTmServerPath + '&' \
651 + self._fnUrlEncode({ constants.tbreq.UPLOAD_PARAM_NAME: os.path.basename(sSrcFilename),
652 constants.tbreq.UPLOAD_PARAM_DESC: sDescription,
653 constants.tbreq.UPLOAD_PARAM_KIND: sKind,
654 constants.tbreq.UPLOAD_PARAM_MIME: sMime,
655 constants.tbreq.ALL_PARAM_ACTION: constants.tbreq.UPLOAD,
656 });
657
658 self._writeOutput('%s: _doUploadFile: url="%s"' % (utils.getTimePrefix(), sUrl));
659
660 # Retry loop.
661 secStart = utils.timestampSecond();
662 while True:
663 try:
664 oConn = self._fnTmConnect();
665 oConn.request('POST', sUrl, oSrcFile.read(), dHeader);
666 fRc = self._processTmStatusResponse(oConn, '_doUploadFile', fClose = True);
667 oConn.close();
668 if fRc is not None:
669 return fRc;
670 except:
671 logXcpt('warning: exception during UPLOAD request');
672
673 if utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
674 self._writeOutput('%s: _doUploadFile: Timed out.' % (utils.getTimePrefix(),));
675 break;
676 try: oSrcFile.seek(0);
677 except:
678 logXcpt();
679 break;
680 self._writeOutput('%s: _doUploadFile: Retrying...' % (utils.getTimePrefix(), ));
681 time.sleep(2);
682
683 return False;
684
685 def _xmlDoFlush(self, asXml, fRetry = False, fDtor = False):
686 """
687 The code that does the actual talking to the server.
688 Used by both xmlFlush and __del__.
689 """
690 secStart = utils.timestampSecond();
691 while True:
692 fRc = None;
693 try:
694 # Post.
695 from common import constants;
696 sPostBody = self._fnUrlEncode({constants.tbreq.XML_RESULT_PARAM_BODY: '\n'.join(asXml),});
697 oConn = self._fnTmConnect();
698 oConn.request('POST',
699 self._sTmServerPath + ('&%s=%s' % (constants.tbreq.ALL_PARAM_ACTION, constants.tbreq.XML_RESULTS)),
700 sPostBody,
701 self._dHttpHeader);
702
703 fRc = self._processTmStatusResponse(oConn, '_xmlDoFlush', fClose = True);
704 if fRc is True:
705 if self.fDebugXml:
706 self._writeOutput('_xmlDoFlush:\n%s' % ('\n'.join(asXml),));
707 return (None, False);
708 if fRc is False:
709 self._writeOutput('_xmlDoFlush: Failed - we should abort the test, really.');
710 return (None, True);
711 except Exception, oXcpt:
712 if not fDtor:
713 logXcpt('warning: exception during XML_RESULTS request');
714 else:
715 self._writeOutput('warning: exception during XML_RESULTS request: %s' % (oXcpt,));
716
717 if fRetry is not True \
718 or utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
719 break;
720 time.sleep(2);
721
722 return (asXml, False);
723
724
725 #
726 # Overridden methods.
727 #
728
729 def isLocal(self):
730 return False;
731
732 def log(self, iLevel, sText, sCaller, sTsPrf):
733 if iLevel <= self.iVerbose:
734 if self.iDebug > 0:
735 sLogText = '%s %30s: %s' % (sTsPrf, sCaller, sText);
736 else:
737 sLogText = '%s %s: %s' % (sTsPrf, self.sName, sText);
738 self._writeOutput(sLogText);
739 return 0;
740
741 def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
742 fRc = True;
743 if sKind in [ 'text', 'log', ] or sKind.startswith('log/'):
744 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
745 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
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 g_oLock.release();
753 self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'image/png');
754 g_oLock.acquire();
755 else:
756 self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
757 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
758 return fRc;
759
760 def xmlFlush(self, fRetry = False, fForce = False):
761 """
762 Flushes the XML back log. Called with the lock held, may leave it
763 while communicating with the server.
764 """
765 if not self._fXmlFlushing:
766 asXml = self._asXml;
767 self._asXml = [];
768 if len(asXml) > 0 or fForce is True:
769 self._fXmlFlushing = True;
770
771 g_oLock.release();
772 (asXml, fIncErrors) = self._xmlDoFlush(asXml, fRetry = fRetry);
773 g_oLock.acquire();
774
775 if fIncErrors:
776 self.testIncErrors();
777
778 self._fXmlFlushing = False;
779 if asXml is None:
780 self._secTsXmlFlush = utils.timestampSecond();
781 else:
782 self._asXml = asXml + self._asXml;
783 return True;
784
785 self._secTsXmlFlush = utils.timestampSecond();
786 return False;
787
788 def _xmlFlushIfNecessary(self):
789 """Flushes the XML back log if necessary."""
790 tsNow = utils.timestampSecond();
791 cSecs = tsNow - self._secTsXmlFlush;
792 cSecsLast = tsNow - self._secTsXmlLast;
793 self._secTsXmlLast = tsNow;
794
795 # Absolute flush thresholds.
796 if cSecs >= self.kcSecXmlFlushMax:
797 return self.xmlFlush();
798 if len(self._asXml) >= self.kcLinesXmlFlush:
799 return self.xmlFlush();
800
801 # Flush if idle long enough.
802 if cSecs >= self.kcSecXmlFlushMin \
803 and cSecsLast >= self.kcSecXmlFlushIdle:
804 return self.xmlFlush();
805
806 return False;
807
808 def _xmlWrite(self, asText, fIndent = True):
809 """XML output function for the reporter."""
810 self._asXml += asText;
811 self._xmlFlushIfNecessary();
812 _ = fIndent; # No pretty printing, thank you.
813 return None;
814
815 def subXmlStart(self, oFileWrapper):
816 oFileWrapper.sXmlBuffer = '';
817 return None;
818
819 def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
820 oFileWrapper.sXmlBuffer += sRawXml;
821 _ = sCaller;
822 return None;
823
824 def subXmlEnd(self, oFileWrapper):
825 sRawXml = oFileWrapper.sXmlBuffer;
826 ## @todo should validate the document here and maybe auto terminate things. Adding some hints to have the server do
827 # this instead.
828 g_oLock.acquire();
829 self._asXml += [ '<PushHint testdepth="%d"/>' % (len(self.atTests),),
830 sRawXml,
831 '<PopHint testdepth="%d"/>' % (len(self.atTests),),];
832 self._xmlFlushIfNecessary();
833 g_oLock.release();
834 return None;
835
836
837#
838# Helpers
839#
840
841def logXcptWorker(iLevel, fIncErrors, sPrefix="", sText=None, cFrames=1):
842 """
843 Log an exception, optionally with a preceeding message and more than one
844 call frame.
845 """
846 g_oLock.acquire();
847 if fIncErrors:
848 g_oReporter.testIncErrors();
849
850 ## @todo skip all this if iLevel is too high!
851
852 # Try get exception info.
853 sTsPrf = utils.getTimePrefix();
854 try:
855 oType, oValue, oTraceback = sys.exc_info();
856 except:
857 oType = oValue = oTraceback = None;
858 if oType is not None:
859
860 # Try format the info
861 try:
862 rc = 0;
863 sCaller = utils.getCallerName(oTraceback.tb_frame);
864 if sText is not None:
865 rc = g_oReporter.log(iLevel, "%s%s" % (sPrefix, sText), sCaller, sTsPrf);
866 asInfo = [];
867 try:
868 asInfo = asInfo + traceback.format_exception_only(oType, oValue);
869 if cFrames is not None and cFrames <= 1:
870 asInfo = asInfo + traceback.format_tb(oTraceback, 1);
871 else:
872 asInfo.append('Traceback:')
873 asInfo = asInfo + traceback.format_tb(oTraceback, cFrames);
874 asInfo.append('Stack:')
875 asInfo = asInfo + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
876 except:
877 g_oReporter.log(0, 'internal-error: Hit exception #2! %s' % (traceback.format_exc()), sCaller, sTsPrf);
878
879 if len(asInfo) > 0:
880 # Do the logging.
881 for sItem in asInfo:
882 asLines = sItem.splitlines();
883 for sLine in asLines:
884 rc = g_oReporter.log(iLevel, '%s%s' % (sPrefix, sLine), sCaller, sTsPrf);
885
886 else:
887 g_oReporter.log(iLevel, 'No exception info...', sCaller, sTsPrf);
888 rc = -3;
889 except:
890 g_oReporter.log(0, 'internal-error: Hit exception! %s' % (traceback.format_exc()), None, sTsPrf);
891 rc = -2;
892 else:
893 g_oReporter.log(0, 'internal-error: No exception! %s'
894 % (utils.getCallerName(iFrame=3)), utils.getCallerName(iFrame=3), sTsPrf);
895 rc = -1;
896
897 g_oLock.release();
898 return rc;
899
900#
901# The public Classes
902#
903class FileWrapper(object):
904 """ File like class for TXS EXEC and similar. """
905 def __init__(self, sPrefix):
906 self.sPrefix = sPrefix;
907
908 def read(self, cb):
909 """file.read"""
910 _ = cb;
911 return "";
912
913 def write(self, sText):
914 """file.write"""
915 if isinstance(sText, array.array):
916 try:
917 sText = sText.tostring();
918 except:
919 pass;
920 g_oLock.acquire();
921 try:
922 sTsPrf = utils.getTimePrefix();
923 sCaller = utils.getCallerName();
924 asLines = sText.splitlines();
925 for sLine in asLines:
926 g_oReporter.log(0, '%s: %s' % (self.sPrefix, sLine), sCaller, sTsPrf);
927 except:
928 traceback.print_exc();
929 g_oLock.release();
930 return None;
931
932class FileWrapperTestPipe(object):
933 """ File like class for the test pipe (TXS EXEC and similar). """
934 def __init__(self):
935 self.sPrefix = '';
936 self.fStarted = False;
937 self.fClosed = False;
938
939 def __del__(self):
940 self.close();
941
942 def close(self):
943 """ file.close """
944 if self.fStarted is True and self.fClosed is False:
945 self.fClosed = True;
946 try: g_oReporter.subXmlEnd(self);
947 except:
948 try: traceback.print_exc();
949 except: pass;
950 return True;
951
952 def read(self, cb = None):
953 """file.read"""
954 _ = cb;
955 return "";
956
957 def write(self, sText):
958 """file.write"""
959 # lazy start.
960 if self.fStarted is not True:
961 try:
962 g_oReporter.subXmlStart(self);
963 except:
964 traceback.print_exc();
965 self.fStarted = True;
966
967 if isinstance(sText, array.array):
968 try:
969 sText = sText.tostring();
970 except:
971 pass;
972 try:
973 g_oReporter.subXmlWrite(self, sText, utils.getCallerName());
974 except:
975 traceback.print_exc();
976 return None;
977
978
979#
980# The public APIs.
981#
982
983def log(sText):
984 """Writes the specfied text to the log."""
985 g_oLock.acquire();
986 try:
987 rc = g_oReporter.log(1, sText, utils.getCallerName(), utils.getTimePrefix());
988 except:
989 rc = -1;
990 g_oLock.release();
991 return rc;
992
993def logXcpt(sText=None, cFrames=1):
994 """
995 Log an exception, optionally with a preceeding message and more than one
996 call frame.
997 """
998 return logXcptWorker(1, False, "", sText, cFrames);
999
1000def log2(sText):
1001 """Log level 2: Writes the specfied text to the log."""
1002 g_oLock.acquire();
1003 try:
1004 rc = g_oReporter.log(2, sText, utils.getCallerName(), utils.getTimePrefix());
1005 except:
1006 rc = -1;
1007 g_oLock.release();
1008 return rc;
1009
1010def log2Xcpt(sText=None, cFrames=1):
1011 """
1012 Log level 2: Log an exception, optionally with a preceeding message and
1013 more than one call frame.
1014 """
1015 return logXcptWorker(2, False, "", sText, cFrames);
1016
1017def maybeErr(fIsError, sText):
1018 """ Maybe error or maybe normal log entry. """
1019 if fIsError is True:
1020 return error(sText);
1021 return log(sText);
1022
1023def maybeErrXcpt(fIsError, sText=None, cFrames=1):
1024 """ Maybe error or maybe normal log exception entry. """
1025 if fIsError is True:
1026 return errorXcpt(sText, cFrames);
1027 return logXcpt(sText, cFrames);
1028
1029def maybeLog(fIsNotError, sText):
1030 """ Maybe error or maybe normal log entry. """
1031 if fIsNotError is not True:
1032 return error(sText);
1033 return log(sText);
1034
1035def maybeLogXcpt(fIsNotError, sText=None, cFrames=1):
1036 """ Maybe error or maybe normal log exception entry. """
1037 if fIsNotError is not True:
1038 return errorXcpt(sText, cFrames);
1039 return logXcpt(sText, cFrames);
1040
1041def error(sText):
1042 """
1043 Writes the specfied error message to the log.
1044
1045 This will add an error to the current test.
1046
1047 Always returns False for the convenience of methods returning boolean
1048 success indicators.
1049 """
1050 g_oLock.acquire();
1051 g_oReporter.testIncErrors();
1052 try:
1053 g_oReporter.log(0, 'error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1054 except:
1055 pass;
1056 g_oLock.release();
1057 return False;
1058
1059def errorXcpt(sText=None, cFrames=1):
1060 """
1061 Log an error caused by an exception. If sText is given, it will preceed
1062 the exception information. cFrames can be used to display more stack.
1063
1064 This will add an error to the current test.
1065
1066 Always returns False for the convenience of methods returning boolean
1067 success indicators.
1068 """
1069 logXcptWorker(0, True, "error: ", sText, cFrames);
1070 return False;
1071
1072def errorTimeout(sText):
1073 """
1074 Flags the current test as having timed out and writes the specified message to the log.
1075
1076 This will add an error to the current test.
1077
1078 Always returns False for the convenience of methods returning boolean
1079 success indicators.
1080 """
1081 g_oLock.acquire();
1082 g_oReporter.testSetTimedOut();
1083 try:
1084 g_oReporter.log(0, 'timeout-error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1085 except:
1086 pass;
1087 g_oLock.release();
1088 return False;
1089
1090def fatal(sText):
1091 """
1092 Writes a fatal error to the log.
1093
1094 This will add an error to the current test.
1095
1096 Always returns False for the convenience of methods returning boolean
1097 success indicators.
1098 """
1099 g_oLock.acquire();
1100 g_oReporter.testIncErrors();
1101 try:
1102 g_oReporter.log(0, 'fatal error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1103 except:
1104 pass
1105 g_oLock.release();
1106 return False;
1107
1108def fatalXcpt(sText=None, cFrames=1):
1109 """
1110 Log a fatal error caused by an exception. If sText is given, it will
1111 preceed the exception information. cFrames can be used to display more
1112 stack.
1113
1114 This will add an error to the current test.
1115
1116 Always returns False for the convenience of methods returning boolean
1117 success indicators.
1118 """
1119 logXcptWorker(1, True, "fatal error: ", sText, cFrames);
1120 return False;
1121
1122def addLogFile(sFilename, sKind, sDescription = '', sAltName = None):
1123 """
1124 Adds the specified log file to the report if the file exists.
1125
1126 The sDescription is a free form description of the log file.
1127
1128 The sKind parameter is for adding some machine parsable hint what kind of
1129 log file this really is.
1130
1131 Returns True on success, False on failure (no ENOENT errors are logged).
1132 """
1133 sTsPrf = utils.getTimePrefix();
1134 sCaller = utils.getCallerName();
1135 fRc = False;
1136 if sAltName is None:
1137 sAltName = sFilename;
1138
1139 try:
1140 oSrcFile = utils.openNoInherit(sFilename, 'rb');
1141 except IOError, oXcpt:
1142 if oXcpt.errno != errno.ENOENT:
1143 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1144 else:
1145 logXcpt('addLogFile(%s,%s,%s) IOError' % (sFilename, sDescription, sKind));
1146 except:
1147 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1148 else:
1149 g_oLock.acquire();
1150 fRc = g_oReporter.addLogFile(oSrcFile, sFilename, sAltName, sDescription, sKind, sCaller, sTsPrf);
1151 g_oLock.release();
1152 oSrcFile.close();
1153 return fRc;
1154
1155def isLocal():
1156 """Is this a local reporter?"""
1157 return g_oReporter.isLocal()
1158
1159def incVerbosity():
1160 """Increases the verbosity level."""
1161 return g_oReporter.incVerbosity()
1162
1163def incDebug():
1164 """Increases the debug level."""
1165 return g_oReporter.incDebug()
1166
1167def getErrorCount():
1168 """
1169 Get the current error count for the entire test run.
1170 """
1171 g_oLock.acquire();
1172 cErrors = g_oReporter.cErrors;
1173 g_oLock.release();
1174 return cErrors;
1175
1176
1177#
1178# Test reporting, a bit similar to RTTestI*.
1179#
1180
1181def testStart(sName):
1182 """
1183 Starts a new test (pushes it).
1184 """
1185 g_oLock.acquire();
1186 rc = g_oReporter.testStart(sName, utils.getCallerName());
1187 g_oLock.release();
1188 return rc;
1189
1190def testValue(sName, sValue, sUnit):
1191 """
1192 Reports a benchmark value or something simiarlly useful.
1193 """
1194 g_oLock.acquire();
1195 rc = g_oReporter.testValue(sName, str(sValue), sUnit, utils.getCallerName());
1196 g_oLock.release();
1197 return rc;
1198
1199def testFailure(sDetails):
1200 """
1201 Reports a failure.
1202 We count these calls and testDone will use them to report PASSED or FAILED.
1203
1204 Returns False so that a return False line can be saved.
1205 """
1206 g_oLock.acquire();
1207 g_oReporter.testFailure(sDetails, utils.getCallerName());
1208 g_oLock.release();
1209 return False;
1210
1211def testFailureXcpt(sDetails = ''):
1212 """
1213 Reports a failure with exception.
1214 We count these calls and testDone will use them to report PASSED or FAILED.
1215
1216 Returns False so that a return False line can be saved.
1217 """
1218 # Extract exception info.
1219 try:
1220 oType, oValue, oTraceback = sys.exc_info();
1221 except:
1222 oType = oValue, oTraceback = None;
1223 if oType is not None:
1224 sCaller = utils.getCallerName(oTraceback.tb_frame);
1225 sXcpt = ' '.join(traceback.format_exception_only(oType, oValue));
1226 else:
1227 sCaller = utils.getCallerName();
1228 sXcpt = 'No exception at %s' % (sCaller,);
1229
1230 # Use testFailure to do the work.
1231 g_oLock.acquire();
1232 if sDetails == '':
1233 g_oReporter.testFailure('Exception: %s' % (sXcpt,), sCaller);
1234 else:
1235 g_oReporter.testFailure('%s: %s' % (sDetails, sXcpt), sCaller);
1236 g_oLock.release();
1237 return False;
1238
1239def testDone(fSkipped = False):
1240 """
1241 Completes the current test (pops it), logging PASSED / FAILURE.
1242
1243 Returns a tuple with the name of the test and its error count.
1244 """
1245 g_oLock.acquire();
1246 rc = g_oReporter.testDone(fSkipped, utils.getCallerName());
1247 g_oLock.release();
1248 return rc;
1249
1250def testErrorCount():
1251 """
1252 Gets the error count of the current test.
1253
1254 Returns the number of errors.
1255 """
1256 g_oLock.acquire();
1257 cErrors = g_oReporter.testErrorCount();
1258 g_oLock.release();
1259 return cErrors;
1260
1261def testCleanup():
1262 """
1263 Closes all open tests with a generic error condition.
1264
1265 Returns True if no open tests, False if something had to be closed with failure.
1266 """
1267 g_oLock.acquire();
1268 fRc = g_oReporter.testCleanup(utils.getCallerName());
1269 g_oReporter.xmlFlush(fRetry = False, fForce = True);
1270 g_oLock.release();
1271 return fRc;
1272
1273
1274#
1275# Sub XML stuff.
1276#
1277
1278def addSubXmlFile(sFilename):
1279 """
1280 Adds a sub-xml result file to the party.
1281 """
1282 fRc = False;
1283 try:
1284 oSrcFile = utils.openNoInherit(sFilename, 'r');
1285 except IOError, oXcpt:
1286 if oXcpt.errno != errno.ENOENT:
1287 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1288 except:
1289 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1290 else:
1291 try:
1292 oWrapper = FileWrapperTestPipe()
1293 oWrapper.write(oSrcFile.read());
1294 oWrapper.close();
1295 except:
1296 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1297 oSrcFile.close();
1298
1299 return fRc;
1300
1301
1302#
1303# Other useful debugging tools.
1304#
1305
1306def logAllStacks(cFrames = None):
1307 """
1308 Logs the stacks of all python threads.
1309 """
1310 sTsPrf = utils.getTimePrefix();
1311 sCaller = utils.getCallerName();
1312 g_oLock.acquire();
1313
1314 cThread = 0;
1315 for idThread, oStack in sys._current_frames().items(): # >=2.5, a bit ugly - pylint: disable=W0212
1316 try:
1317 if cThread > 0:
1318 g_oReporter.log(1, '', sCaller, sTsPrf);
1319 g_oReporter.log(1, 'Thread %s (%#x)' % (idThread, idThread), sCaller, sTsPrf);
1320 try:
1321 asInfo = traceback.format_stack(oStack, cFrames);
1322 except:
1323 g_oReporter.log(1, ' Stack formatting failed w/ exception', sCaller, sTsPrf);
1324 else:
1325 for sInfo in asInfo:
1326 asLines = sInfo.splitlines();
1327 for sLine in asLines:
1328 g_oReporter.log(1, sLine, sCaller, sTsPrf);
1329 except:
1330 pass;
1331 cThread += 1;
1332
1333 g_oLock.release();
1334 return None;
1335
1336def checkTestManagerConnection():
1337 """
1338 Checks the connection to the test manager.
1339
1340 Returns True if the connection is fine, False if not, None if not remote
1341 reporter.
1342
1343 Note! This as the sideeffect of flushing XML.
1344 """
1345 g_oLock.acquire();
1346 fRc = g_oReporter.xmlFlush(fRetry = False, fForce = True);
1347 g_oLock.release();
1348 return fRc;
1349
1350def flushall():
1351 """
1352 Flushes all output streams, both standard and logger related.
1353 """
1354 try: sys.stdout.flush();
1355 except: pass;
1356 try: sys.stderr.flush();
1357 except: pass;
1358
1359 # Note! Current no logger specific streams to flush.
1360
1361 return True;
1362
1363
1364#
1365# Module initialization.
1366#
1367
1368def _InitReporterModule():
1369 """
1370 Instantiate the test reporter.
1371 """
1372 global g_oReporter, g_sReporterName
1373
1374 g_sReporterName = os.getenv("TESTBOX_REPORTER", "local");
1375 if g_sReporterName == "local":
1376 g_oReporter = LocalReporter();
1377 elif g_sReporterName == "remote":
1378 g_oReporter = RemoteReporter();
1379 else:
1380 print >> sys.stderr, os.path.basename(__file__) + ": Unknown TESTBOX_REPORTER value: '" + g_sReporterName + "'";
1381 raise Exception("Unknown TESTBOX_REPORTER value '" + g_sReporterName + "'");
1382
1383if __name__ != "checker": # pychecker avoidance.
1384 _InitReporterModule();
1385
Note: See TracBrowser for help on using the repository browser.

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