VirtualBox

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

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

ValidationKit: Fix uploading certain logs, the aternative name is not used for logs passed as a string

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