VirtualBox

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

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

ValidationKit: Follow up to previous fix

  • 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 61360 2016-06-01 07:51:59Z 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: 61360 $"
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, sDescription, sKind, sCaller, sTsPrf):
173 """
174 Adds the file to the report.
175 Returns True on success, False on failure.
176 """
177 _ = sLog; _ = sLogName; _ = 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, 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 return fRc;
503
504 def subXmlStart(self, oFileWrapper):
505 # Open a new file and just include it from the main XML.
506 iSubXml = self.iSubXml;
507 self.iSubXml += 1;
508 sSubXmlName = os.path.join(self.sLogDir, 'sub-%d.xml' % (iSubXml,));
509 try:
510 oFileWrapper.oSubXmlFile = utils.openNoInherit(sSubXmlName, "w");
511 except:
512 errorXcpt('open(%s)' % oFileWrapper.oSubXmlName);
513 oFileWrapper.oSubXmlFile = None;
514 else:
515 self._xmlWrite(['<Include timestamp="%s" filename="%s"/>\n'
516 % (utils.getIsoTimestamp(), self._xmlEscAttr(os.path.basename(sSubXmlName)))]);
517 return None;
518
519 def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
520 if oFileWrapper.oSubXmlFile is not None:
521 try:
522 oFileWrapper.oSubXmlFile.write(sRawXml);
523 except:
524 pass;
525 if sCaller is None: pass; # pychecker - NOREF
526 return None;
527
528 def subXmlEnd(self, oFileWrapper):
529 if oFileWrapper.oSubXmlFile is not None:
530 try:
531 oFileWrapper.oSubXmlFile.close();
532 oFileWrapper.oSubXmlFile = None;
533 except:
534 pass;
535 return None;
536
537
538
539class RemoteReporter(ReporterBase):
540 """
541 Reporter that talks to the test manager server.
542 """
543
544
545 ## The XML sync min time (seconds).
546 kcSecXmlFlushMin = 30;
547 ## The XML sync max time (seconds).
548 kcSecXmlFlushMax = 120;
549 ## The XML sync idle time before flushing (seconds).
550 kcSecXmlFlushIdle = 5;
551 ## The XML sync line count threshold.
552 kcLinesXmlFlush = 512;
553
554 ## The retry timeout.
555 kcSecTestManagerRetryTimeout = 120;
556 ## The request timeout.
557 kcSecTestManagerRequestTimeout = 30;
558
559
560 def __init__(self):
561 ReporterBase.__init__(self);
562 self.sTestManagerUrl = os.environ.get('TESTBOX_MANAGER_URL');
563 self.sTestBoxUuid = os.environ.get('TESTBOX_UUID');
564 self.idTestBox = int(os.environ.get('TESTBOX_ID'));
565 self.idTestSet = int(os.environ.get('TESTBOX_TEST_SET_ID'));
566 self._asXml = [];
567 self._secTsXmlFlush = utils.timestampSecond();
568 self._secTsXmlLast = self._secTsXmlFlush;
569 self._fXmlFlushing = False;
570 self.oOutput = sys.stdout; # Hack for __del__ output.
571 self.fFlushEachLine = True;
572 self.fDebugXml = 'TESTDRIVER_REPORTER_DEBUG_XML' in os.environ;
573
574 # Prepare the TM connecting.
575 import urlparse;
576 import httplib;
577 import urllib;
578 from common import constants;
579
580 self._fnUrlEncode = urllib.urlencode;
581 self._fnUrlParseQs = urlparse.parse_qs;
582 self._oParsedTmUrl = urlparse.urlparse(self.sTestManagerUrl);
583
584 if sys.version_info[0] >= 3 \
585 or (sys.version_info[0] == 2 and sys.version_info[1] >= 6):
586 if self._oParsedTmUrl.scheme == 'https': # pylint: disable=E1101
587 self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname,
588 timeout = self.kcSecTestManagerRequestTimeout);
589 else:
590 self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname,
591 timeout = self.kcSecTestManagerRequestTimeout);
592 else:
593 if self._oParsedTmUrl.scheme == 'https': # pylint: disable=E1101
594 self._fnTmConnect = lambda: httplib.HTTPSConnection(self._oParsedTmUrl.hostname);
595 else:
596 self._fnTmConnect = lambda: httplib.HTTPConnection( self._oParsedTmUrl.hostname);
597 self._dHttpHeader = \
598 {
599 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
600 'User-Agent': 'TestDriverReporter/%s.0 (%s, %s)' % (__version__, utils.getHostOs(), utils.getHostArch(),),
601 'Accept': 'text/plain,application/x-www-form-urlencoded',
602 'Accept-Encoding': 'identity',
603 'Cache-Control': 'max-age=0',
604 #'Connection': 'keep-alive',
605 };
606
607 dParams = {
608 constants.tbreq.ALL_PARAM_TESTBOX_UUID: self.sTestBoxUuid,
609 constants.tbreq.ALL_PARAM_TESTBOX_ID: self.idTestBox,
610 constants.tbreq.RESULT_PARAM_TEST_SET_ID: self.idTestSet,
611 };
612 self._sTmServerPath = '/%s/testboxdisp.py?%s' \
613 % ( self._oParsedTmUrl.path.strip('/'), # pylint: disable=E1101
614 urllib.urlencode(dParams), );
615
616 def __del__(self):
617 """Flush pending log messages?"""
618 if len(self._asXml) > 0:
619 self._xmlDoFlush(self._asXml, fRetry = True, fDtor = True);
620
621 def _writeOutput(self, sText):
622 """ Does the actual writing and flushing. """
623 print >> self.oOutput, sText.encode('ascii', 'replace');
624 if self.fFlushEachLine: self.oOutput.flush();
625 return None;
626
627 #
628 # Talking to TM.
629 #
630
631 def _processTmStatusResponse(self, oConn, sOperation, fClose = True):
632 """
633 Processes HTTP reponse from the test manager.
634 Returns True, False or None. None should be retried, the others not.
635 May raise exception on HTTP issue (retry ok).
636 """
637 import httplib;
638 from common import constants;
639
640 # Read the response and (optionally) close the connection.
641 oResponse = oConn.getresponse();
642 try:
643 sRspBody = oResponse.read();
644 except httplib.IncompleteRead, oXcpt:
645 self._writeOutput('%s: %s: Warning: httplib.IncompleteRead: %s [expected %s, got %s]'
646 % (utils.getTimePrefix(), sOperation, oXcpt, oXcpt.expected, len(oXcpt.partial),));
647 sRspBody = oXcpt.partial;
648 if fClose is True:
649 try: oConn.close();
650 except: pass;
651
652 # Check the content type.
653 sContentType = oResponse.getheader('Content-Type');
654 if sContentType is not None and sContentType == 'application/x-www-form-urlencoded; charset=utf-8':
655
656 # Parse the body and check the RESULT parameter.
657 dResponse = self._fnUrlParseQs(sRspBody, strict_parsing = True);
658 sResult = dResponse.get(constants.tbresp.ALL_PARAM_RESULT, None);
659 if isinstance(sResult, list):
660 sResult = sResult[0] if len(sResult) == 1 else '%d results' % (len(sResult),);
661
662 if sResult is not None:
663 if sResult == constants.tbresp.STATUS_ACK:
664 return True;
665 if sResult == constants.tbresp.STATUS_NACK:
666 self._writeOutput('%s: %s: Failed (%s). (dResponse=%s)'
667 % (utils.getTimePrefix(), sOperation, sResult, dResponse,));
668 return False;
669
670 self._writeOutput('%s: %s: Failed - dResponse=%s' % (utils.getTimePrefix(), sOperation, dResponse,));
671 else:
672 self._writeOutput('%s: %s: Unexpected Content-Type: %s' % (utils.getTimePrefix(), sOperation, sContentType,));
673 self._writeOutput('%s: %s: Body: %s' % (utils.getTimePrefix(), sOperation, sRspBody,));
674 return None;
675
676 def _doUploadFile(self, oSrcFile, sSrcFilename, sDescription, sKind, sMime):
677 """ Uploads the given file to the test manager. """
678
679 # Prepare header and url.
680 dHeader = dict(self._dHttpHeader);
681 dHeader['Content-Type'] = 'application/octet-stream';
682 self._writeOutput('%s: _doUploadFile: sHeader=%s' % (utils.getTimePrefix(), dHeader,));
683 oSrcFile.seek(0, 2);
684 self._writeOutput('%s: _doUploadFile: size=%d' % (utils.getTimePrefix(), oSrcFile.tell(),));
685 oSrcFile.seek(0);
686
687 from common import constants;
688 sUrl = self._sTmServerPath + '&' \
689 + self._fnUrlEncode({ constants.tbreq.UPLOAD_PARAM_NAME: os.path.basename(sSrcFilename),
690 constants.tbreq.UPLOAD_PARAM_DESC: sDescription,
691 constants.tbreq.UPLOAD_PARAM_KIND: sKind,
692 constants.tbreq.UPLOAD_PARAM_MIME: sMime,
693 constants.tbreq.ALL_PARAM_ACTION: constants.tbreq.UPLOAD,
694 });
695
696 # Retry loop.
697 secStart = utils.timestampSecond();
698 while True:
699 try:
700 oConn = self._fnTmConnect();
701 oConn.request('POST', sUrl, oSrcFile.read(), dHeader);
702 fRc = self._processTmStatusResponse(oConn, '_doUploadFile', fClose = True);
703 oConn.close();
704 if fRc is not None:
705 return fRc;
706 except:
707 logXcpt('warning: exception during UPLOAD request');
708
709 if utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
710 self._writeOutput('%s: _doUploadFile: Timed out.' % (utils.getTimePrefix(),));
711 break;
712 try: oSrcFile.seek(0);
713 except:
714 logXcpt();
715 break;
716 self._writeOutput('%s: _doUploadFile: Retrying...' % (utils.getTimePrefix(), ));
717 time.sleep(2);
718
719 return False;
720
721 def _doUploadString(self, sSrc, sSrcName, sDescription, sKind, sMime):
722 """ Uploads the given string as a separate file to the test manager. """
723
724 # Prepare header and url.
725 dHeader = dict(self._dHttpHeader);
726 dHeader['Content-Type'] = 'application/octet-stream';
727 self._writeOutput('%s: _doUploadString: sHeader=%s' % (utils.getTimePrefix(), dHeader,));
728 self._writeOutput('%s: _doUploadString: size=%d' % (utils.getTimePrefix(), sys.getsizeof(sSrc),));
729
730 from common import constants;
731 sUrl = self._sTmServerPath + '&' \
732 + self._fnUrlEncode({ constants.tbreq.UPLOAD_PARAM_NAME: os.path.basename(sSrcName),
733 constants.tbreq.UPLOAD_PARAM_DESC: sDescription,
734 constants.tbreq.UPLOAD_PARAM_KIND: sKind,
735 constants.tbreq.UPLOAD_PARAM_MIME: sMime,
736 constants.tbreq.ALL_PARAM_ACTION: constants.tbreq.UPLOAD,
737 });
738
739 # Retry loop.
740 secStart = utils.timestampSecond();
741 while True:
742 try:
743 oConn = self._fnTmConnect();
744 oConn.request('POST', sUrl, sSrc, dHeader);
745 fRc = self._processTmStatusResponse(oConn, '_doUploadString', fClose = True);
746 oConn.close();
747 if fRc is not None:
748 return fRc;
749 except:
750 logXcpt('warning: exception during UPLOAD request');
751
752 if utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
753 self._writeOutput('%s: _doUploadString: Timed out.' % (utils.getTimePrefix(),));
754 break;
755 self._writeOutput('%s: _doUploadString: Retrying...' % (utils.getTimePrefix(), ));
756 time.sleep(2);
757
758 return False;
759
760 def _xmlDoFlush(self, asXml, fRetry = False, fDtor = False):
761 """
762 The code that does the actual talking to the server.
763 Used by both xmlFlush and __del__.
764 """
765 secStart = utils.timestampSecond();
766 while True:
767 fRc = None;
768 try:
769 # Post.
770 from common import constants;
771 sPostBody = self._fnUrlEncode({constants.tbreq.XML_RESULT_PARAM_BODY: '\n'.join(asXml),});
772 oConn = self._fnTmConnect();
773 oConn.request('POST',
774 self._sTmServerPath + ('&%s=%s' % (constants.tbreq.ALL_PARAM_ACTION, constants.tbreq.XML_RESULTS)),
775 sPostBody,
776 self._dHttpHeader);
777
778 fRc = self._processTmStatusResponse(oConn, '_xmlDoFlush', fClose = True);
779 if fRc is True:
780 if self.fDebugXml:
781 self._writeOutput('_xmlDoFlush:\n%s' % ('\n'.join(asXml),));
782 return (None, False);
783 if fRc is False:
784 self._writeOutput('_xmlDoFlush: Failed - we should abort the test, really.');
785 return (None, True);
786 except Exception, oXcpt:
787 if not fDtor:
788 logXcpt('warning: exception during XML_RESULTS request');
789 else:
790 self._writeOutput('warning: exception during XML_RESULTS request: %s' % (oXcpt,));
791
792 if fRetry is not True \
793 or utils.timestampSecond() - secStart >= self.kcSecTestManagerRetryTimeout:
794 break;
795 time.sleep(2);
796
797 return (asXml, False);
798
799
800 #
801 # Overridden methods.
802 #
803
804 def isLocal(self):
805 return False;
806
807 def log(self, iLevel, sText, sCaller, sTsPrf):
808 if iLevel <= self.iVerbose:
809 if self.iDebug > 0:
810 sLogText = '%s %30s: %s' % (sTsPrf, sCaller, sText);
811 else:
812 sLogText = '%s %s: %s' % (sTsPrf, self.sName, sText);
813 self._writeOutput(sLogText);
814 return 0;
815
816 def addLogFile(self, oSrcFile, sSrcFilename, sAltName, sDescription, sKind, sCaller, sTsPrf):
817 fRc = True;
818 if sKind in [ 'text', 'log', ] or sKind.startswith('log/'):
819 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
820 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
821 self.xmlFlush();
822 g_oLock.release();
823 self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'text/plain');
824 g_oLock.acquire();
825 elif sKind.startswith('screenshot/'):
826 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
827 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
828 self.xmlFlush();
829 g_oLock.release();
830 self._doUploadFile(oSrcFile, sAltName, sDescription, sKind, 'image/png');
831 g_oLock.acquire();
832 else:
833 self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
834 % (sSrcFilename, sKind, sDescription), sCaller, sTsPrf);
835 return fRc;
836
837 def addLogString(self, sLog, sLogName, sDescription, sKind, sCaller, sTsPrf):
838 fRc = True;
839 if sKind in [ 'text', 'log', ] or sKind.startswith('log/'):
840 self.log(0, '*** Uploading "%s" - KIND: "%s" - DESC: "%s" ***'
841 % (sLogName, sKind, sDescription), sCaller, sTsPrf);
842 self.xmlFlush();
843 g_oLock.release();
844 self._doUploadString(sLog, sLogName, sDescription, sKind, 'text/plain');
845 g_oLock.acquire();
846 else:
847 self.log(0, '*** UNKNOWN FILE "%s" - KIND "%s" - DESC "%s" ***'
848 % (sLogName, sKind, sDescription), sCaller, sTsPrf);
849 return fRc;
850
851 def xmlFlush(self, fRetry = False, fForce = False):
852 """
853 Flushes the XML back log. Called with the lock held, may leave it
854 while communicating with the server.
855 """
856 if not self._fXmlFlushing:
857 asXml = self._asXml;
858 self._asXml = [];
859 if len(asXml) > 0 or fForce is True:
860 self._fXmlFlushing = True;
861
862 g_oLock.release();
863 (asXml, fIncErrors) = self._xmlDoFlush(asXml, fRetry = fRetry);
864 g_oLock.acquire();
865
866 if fIncErrors:
867 self.testIncErrors();
868
869 self._fXmlFlushing = False;
870 if asXml is None:
871 self._secTsXmlFlush = utils.timestampSecond();
872 else:
873 self._asXml = asXml + self._asXml;
874 return True;
875
876 self._secTsXmlFlush = utils.timestampSecond();
877 return False;
878
879 def _xmlFlushIfNecessary(self):
880 """Flushes the XML back log if necessary."""
881 tsNow = utils.timestampSecond();
882 cSecs = tsNow - self._secTsXmlFlush;
883 cSecsLast = tsNow - self._secTsXmlLast;
884 self._secTsXmlLast = tsNow;
885
886 # Absolute flush thresholds.
887 if cSecs >= self.kcSecXmlFlushMax:
888 return self.xmlFlush();
889 if len(self._asXml) >= self.kcLinesXmlFlush:
890 return self.xmlFlush();
891
892 # Flush if idle long enough.
893 if cSecs >= self.kcSecXmlFlushMin \
894 and cSecsLast >= self.kcSecXmlFlushIdle:
895 return self.xmlFlush();
896
897 return False;
898
899 def _xmlWrite(self, asText, fIndent = True):
900 """XML output function for the reporter."""
901 self._asXml += asText;
902 self._xmlFlushIfNecessary();
903 _ = fIndent; # No pretty printing, thank you.
904 return None;
905
906 def subXmlStart(self, oFileWrapper):
907 oFileWrapper.sXmlBuffer = '';
908 return None;
909
910 def subXmlWrite(self, oFileWrapper, sRawXml, sCaller):
911 oFileWrapper.sXmlBuffer += sRawXml;
912 _ = sCaller;
913 return None;
914
915 def subXmlEnd(self, oFileWrapper):
916 sRawXml = oFileWrapper.sXmlBuffer;
917 ## @todo should validate the document here and maybe auto terminate things. Adding some hints to have the server do
918 # this instead.
919 g_oLock.acquire();
920 self._asXml += [ '<PushHint testdepth="%d"/>' % (len(self.atTests),),
921 sRawXml,
922 '<PopHint testdepth="%d"/>' % (len(self.atTests),),];
923 self._xmlFlushIfNecessary();
924 g_oLock.release();
925 return None;
926
927
928#
929# Helpers
930#
931
932def logXcptWorker(iLevel, fIncErrors, sPrefix="", sText=None, cFrames=1):
933 """
934 Log an exception, optionally with a preceeding message and more than one
935 call frame.
936 """
937 g_oLock.acquire();
938 if fIncErrors:
939 g_oReporter.testIncErrors();
940
941 ## @todo skip all this if iLevel is too high!
942
943 # Try get exception info.
944 sTsPrf = utils.getTimePrefix();
945 try:
946 oType, oValue, oTraceback = sys.exc_info();
947 except:
948 oType = oValue = oTraceback = None;
949 if oType is not None:
950
951 # Try format the info
952 try:
953 rc = 0;
954 sCaller = utils.getCallerName(oTraceback.tb_frame);
955 if sText is not None:
956 rc = g_oReporter.log(iLevel, "%s%s" % (sPrefix, sText), sCaller, sTsPrf);
957 asInfo = [];
958 try:
959 asInfo = asInfo + traceback.format_exception_only(oType, oValue);
960 if cFrames is not None and cFrames <= 1:
961 asInfo = asInfo + traceback.format_tb(oTraceback, 1);
962 else:
963 asInfo.append('Traceback:')
964 asInfo = asInfo + traceback.format_tb(oTraceback, cFrames);
965 asInfo.append('Stack:')
966 asInfo = asInfo + traceback.format_stack(oTraceback.tb_frame.f_back, cFrames);
967 except:
968 g_oReporter.log(0, '** internal-error: Hit exception #2! %s' % (traceback.format_exc()), sCaller, sTsPrf);
969
970 if len(asInfo) > 0:
971 # Do the logging.
972 for sItem in asInfo:
973 asLines = sItem.splitlines();
974 for sLine in asLines:
975 rc = g_oReporter.log(iLevel, '%s%s' % (sPrefix, sLine), sCaller, sTsPrf);
976
977 else:
978 g_oReporter.log(iLevel, 'No exception info...', sCaller, sTsPrf);
979 rc = -3;
980 except:
981 g_oReporter.log(0, '** internal-error: Hit exception! %s' % (traceback.format_exc()), None, sTsPrf);
982 rc = -2;
983 else:
984 g_oReporter.log(0, '** internal-error: No exception! %s'
985 % (utils.getCallerName(iFrame=3)), utils.getCallerName(iFrame=3), sTsPrf);
986 rc = -1;
987
988 g_oLock.release();
989 return rc;
990
991#
992# The public Classes
993#
994class FileWrapper(object):
995 """ File like class for TXS EXEC and similar. """
996 def __init__(self, sPrefix):
997 self.sPrefix = sPrefix;
998
999 def __del__(self):
1000 self.close();
1001
1002 def close(self):
1003 """ file.close """
1004 # Nothing to be done.
1005 return;
1006
1007 def read(self, cb):
1008 """file.read"""
1009 _ = cb;
1010 return "";
1011
1012 def write(self, sText):
1013 """file.write"""
1014 if isinstance(sText, array.array):
1015 try:
1016 sText = sText.tostring();
1017 except:
1018 pass;
1019 g_oLock.acquire();
1020 try:
1021 sTsPrf = utils.getTimePrefix();
1022 sCaller = utils.getCallerName();
1023 asLines = sText.splitlines();
1024 for sLine in asLines:
1025 g_oReporter.log(0, '%s: %s' % (self.sPrefix, sLine), sCaller, sTsPrf);
1026 except:
1027 traceback.print_exc();
1028 g_oLock.release();
1029 return None;
1030
1031class FileWrapperTestPipe(object):
1032 """ File like class for the test pipe (TXS EXEC and similar). """
1033 def __init__(self):
1034 self.sPrefix = '';
1035 self.fStarted = False;
1036 self.fClosed = False;
1037 self.sTagBuffer = None;
1038
1039 def __del__(self):
1040 self.close();
1041
1042 def close(self):
1043 """ file.close """
1044 if self.fStarted is True and self.fClosed is False:
1045 self.fClosed = True;
1046 try: g_oReporter.subXmlEnd(self);
1047 except:
1048 try: traceback.print_exc();
1049 except: pass;
1050 return True;
1051
1052 def read(self, cb = None):
1053 """file.read"""
1054 _ = cb;
1055 return "";
1056
1057 def write(self, sText):
1058 """file.write"""
1059 # lazy start.
1060 if self.fStarted is not True:
1061 try:
1062 g_oReporter.subXmlStart(self);
1063 except:
1064 traceback.print_exc();
1065 self.fStarted = True;
1066
1067 if isinstance(sText, array.array):
1068 try:
1069 sText = sText.tostring();
1070 except:
1071 pass;
1072 try:
1073 g_oReporter.subXmlWrite(self, sText, utils.getCallerName());
1074 # Parse the supplied text and look for <Failed.../> tags to keep track of the
1075 # error counter. This is only a very lazy aproach.
1076 sText.strip();
1077 idxText = 0;
1078 while len(sText) > 0:
1079 if self.sTagBuffer is None:
1080 # Look for the start of a tag.
1081 idxStart = sText[idxText:].find('<');
1082 if idxStart != -1:
1083 # Look for the end of the tag.
1084 idxEnd = sText[idxStart:].find('>');
1085
1086 # If the end was found inside the current buffer, parse the line,
1087 # else we have to save it for later.
1088 if idxEnd != -1:
1089 idxEnd += idxStart + 1;
1090 self._processXmlElement(sText[idxStart:idxEnd]);
1091 idxText = idxEnd;
1092 else:
1093 self.sTagBuffer = sText[idxStart:];
1094 idxText = len(sText);
1095 else:
1096 idxText = len(sText);
1097 else:
1098 # Search for the end of the tag and parse the whole tag.
1099 idxEnd = sText[idxText:].find('>');
1100 if idxEnd != -1:
1101 idxEnd += idxStart + 1;
1102 self._processXmlElement(self.sTagBuffer + sText[idxText:idxEnd]);
1103 self.sTagBuffer = None;
1104 idxText = idxEnd;
1105 else:
1106 self.sTagBuffer = self.sTagBuffer + sText[idxText:];
1107 idxText = len(sText);
1108
1109 sText = sText[idxText:];
1110 sText = sText.lstrip();
1111 except:
1112 traceback.print_exc();
1113 return None;
1114
1115 def _processXmlElement(self, sElement):
1116 """
1117 Processes a complete XML tag (so far we only search for the Failed to tag
1118 to keep track of the error counter.
1119 """
1120 # Make sure we don't parse any space between < and the element name.
1121 sElement = sElement.strip();
1122
1123 # Find the end of the name
1124 idxEndName = sElement.find(' ');
1125 if idxEndName == -1:
1126 idxEndName = sElement.find('/');
1127 if idxEndName == -1:
1128 idxEndName = sElement.find('>');
1129
1130 if idxEndName != -1:
1131 if sElement[1:idxEndName] == 'Failed':
1132 g_oLock.acquire();
1133 g_oReporter.testIncErrors();
1134 g_oLock.release();
1135 else:
1136 error('_processXmlElement(%s)' % sElement);
1137
1138
1139#
1140# The public APIs.
1141#
1142
1143def log(sText):
1144 """Writes the specfied text to the log."""
1145 g_oLock.acquire();
1146 try:
1147 rc = g_oReporter.log(1, sText, utils.getCallerName(), utils.getTimePrefix());
1148 except:
1149 rc = -1;
1150 g_oLock.release();
1151 return rc;
1152
1153def logXcpt(sText=None, cFrames=1):
1154 """
1155 Log an exception, optionally with a preceeding message and more than one
1156 call frame.
1157 """
1158 return logXcptWorker(1, False, "", sText, cFrames);
1159
1160def log2(sText):
1161 """Log level 2: Writes the specfied text to the log."""
1162 g_oLock.acquire();
1163 try:
1164 rc = g_oReporter.log(2, sText, utils.getCallerName(), utils.getTimePrefix());
1165 except:
1166 rc = -1;
1167 g_oLock.release();
1168 return rc;
1169
1170def log2Xcpt(sText=None, cFrames=1):
1171 """
1172 Log level 2: Log an exception, optionally with a preceeding message and
1173 more than one call frame.
1174 """
1175 return logXcptWorker(2, False, "", sText, cFrames);
1176
1177def maybeErr(fIsError, sText):
1178 """ Maybe error or maybe normal log entry. """
1179 if fIsError is True:
1180 return error(sText);
1181 return log(sText);
1182
1183def maybeErrXcpt(fIsError, sText=None, cFrames=1):
1184 """ Maybe error or maybe normal log exception entry. """
1185 if fIsError is True:
1186 return errorXcpt(sText, cFrames);
1187 return logXcpt(sText, cFrames);
1188
1189def maybeLog(fIsNotError, sText):
1190 """ Maybe error or maybe normal log entry. """
1191 if fIsNotError is not True:
1192 return error(sText);
1193 return log(sText);
1194
1195def maybeLogXcpt(fIsNotError, sText=None, cFrames=1):
1196 """ Maybe error or maybe normal log exception entry. """
1197 if fIsNotError is not True:
1198 return errorXcpt(sText, cFrames);
1199 return logXcpt(sText, cFrames);
1200
1201def error(sText):
1202 """
1203 Writes the specfied error message to the log.
1204
1205 This will add an error to the current test.
1206
1207 Always returns False for the convenience of methods returning boolean
1208 success indicators.
1209 """
1210 g_oLock.acquire();
1211 g_oReporter.testIncErrors();
1212 try:
1213 g_oReporter.log(0, '** error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1214 except:
1215 pass;
1216 g_oLock.release();
1217 return False;
1218
1219def errorXcpt(sText=None, cFrames=1):
1220 """
1221 Log an error caused by an exception. If sText is given, it will preceed
1222 the exception information. cFrames can be used to display more stack.
1223
1224 This will add an error to the current test.
1225
1226 Always returns False for the convenience of methods returning boolean
1227 success indicators.
1228 """
1229 logXcptWorker(0, True, '** error: ', sText, cFrames);
1230 return False;
1231
1232def errorTimeout(sText):
1233 """
1234 Flags the current test as having timed out and writes the specified message to the log.
1235
1236 This will add an error to the current test.
1237
1238 Always returns False for the convenience of methods returning boolean
1239 success indicators.
1240 """
1241 g_oLock.acquire();
1242 g_oReporter.testSetTimedOut();
1243 try:
1244 g_oReporter.log(0, '** timeout-error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1245 except:
1246 pass;
1247 g_oLock.release();
1248 return False;
1249
1250def fatal(sText):
1251 """
1252 Writes a fatal error to the log.
1253
1254 This will add an error to the current test.
1255
1256 Always returns False for the convenience of methods returning boolean
1257 success indicators.
1258 """
1259 g_oLock.acquire();
1260 g_oReporter.testIncErrors();
1261 try:
1262 g_oReporter.log(0, '** fatal error: %s' % (sText), utils.getCallerName(), utils.getTimePrefix());
1263 except:
1264 pass
1265 g_oLock.release();
1266 return False;
1267
1268def fatalXcpt(sText=None, cFrames=1):
1269 """
1270 Log a fatal error caused by an exception. If sText is given, it will
1271 preceed the exception information. cFrames can be used to display more
1272 stack.
1273
1274 This will add an error to the current test.
1275
1276 Always returns False for the convenience of methods returning boolean
1277 success indicators.
1278 """
1279 logXcptWorker(0, True, "** fatal error: ", sText, cFrames);
1280 return False;
1281
1282def addLogFile(sFilename, sKind, sDescription = '', sAltName = None):
1283 """
1284 Adds the specified log file to the report if the file exists.
1285
1286 The sDescription is a free form description of the log file.
1287
1288 The sKind parameter is for adding some machine parsable hint what kind of
1289 log file this really is.
1290
1291 Returns True on success, False on failure (no ENOENT errors are logged).
1292 """
1293 sTsPrf = utils.getTimePrefix();
1294 sCaller = utils.getCallerName();
1295 fRc = False;
1296 if sAltName is None:
1297 sAltName = sFilename;
1298
1299 try:
1300 oSrcFile = utils.openNoInherit(sFilename, 'rb');
1301 except IOError, oXcpt:
1302 if oXcpt.errno != errno.ENOENT:
1303 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1304 else:
1305 logXcpt('addLogFile(%s,%s,%s) IOError' % (sFilename, sDescription, sKind));
1306 except:
1307 logXcpt('addLogFile(%s,%s,%s)' % (sFilename, sDescription, sKind));
1308 else:
1309 g_oLock.acquire();
1310 fRc = g_oReporter.addLogFile(oSrcFile, sFilename, sAltName, sDescription, sKind, sCaller, sTsPrf);
1311 g_oLock.release();
1312 oSrcFile.close();
1313 return fRc;
1314
1315def addLogString(sLog, sLogName, sKind, sDescription = ''):
1316 """
1317 Adds the specified log string to the report.
1318
1319 The sLog parameter sets the name of the log file.
1320
1321 The sDescription is a free form description of the log file.
1322
1323 The sKind parameter is for adding some machine parsable hint what kind of
1324 log file this really is.
1325
1326 Returns True on success, False on failure (no ENOENT errors are logged).
1327 """
1328 sTsPrf = utils.getTimePrefix();
1329 sCaller = utils.getCallerName();
1330 fRc = False;
1331
1332 g_oLock.acquire();
1333 fRc = g_oReporter.addLogString(sLog, sLogName, sDescription, sKind, sCaller, sTsPrf);
1334 g_oLock.release();
1335 return fRc;
1336
1337def isLocal():
1338 """Is this a local reporter?"""
1339 return g_oReporter.isLocal()
1340
1341def incVerbosity():
1342 """Increases the verbosity level."""
1343 return g_oReporter.incVerbosity()
1344
1345def incDebug():
1346 """Increases the debug level."""
1347 return g_oReporter.incDebug()
1348
1349def getErrorCount():
1350 """
1351 Get the current error count for the entire test run.
1352 """
1353 g_oLock.acquire();
1354 cErrors = g_oReporter.cErrors;
1355 g_oLock.release();
1356 return cErrors;
1357
1358
1359#
1360# Test reporting, a bit similar to RTTestI*.
1361#
1362
1363def testStart(sName):
1364 """
1365 Starts a new test (pushes it).
1366 """
1367 g_oLock.acquire();
1368 rc = g_oReporter.testStart(sName, utils.getCallerName());
1369 g_oLock.release();
1370 return rc;
1371
1372def testValue(sName, sValue, sUnit):
1373 """
1374 Reports a benchmark value or something simiarlly useful.
1375 """
1376 g_oLock.acquire();
1377 rc = g_oReporter.testValue(sName, str(sValue), sUnit, utils.getCallerName());
1378 g_oLock.release();
1379 return rc;
1380
1381def testFailure(sDetails):
1382 """
1383 Reports a failure.
1384 We count these calls and testDone will use them to report PASSED or FAILED.
1385
1386 Returns False so that a return False line can be saved.
1387 """
1388 g_oLock.acquire();
1389 g_oReporter.testFailure(sDetails, utils.getCallerName());
1390 g_oLock.release();
1391 return False;
1392
1393def testFailureXcpt(sDetails = ''):
1394 """
1395 Reports a failure with exception.
1396 We count these calls and testDone will use them to report PASSED or FAILED.
1397
1398 Returns False so that a return False line can be saved.
1399 """
1400 # Extract exception info.
1401 try:
1402 oType, oValue, oTraceback = sys.exc_info();
1403 except:
1404 oType = oValue, oTraceback = None;
1405 if oType is not None:
1406 sCaller = utils.getCallerName(oTraceback.tb_frame);
1407 sXcpt = ' '.join(traceback.format_exception_only(oType, oValue));
1408 else:
1409 sCaller = utils.getCallerName();
1410 sXcpt = 'No exception at %s' % (sCaller,);
1411
1412 # Use testFailure to do the work.
1413 g_oLock.acquire();
1414 if sDetails == '':
1415 g_oReporter.testFailure('Exception: %s' % (sXcpt,), sCaller);
1416 else:
1417 g_oReporter.testFailure('%s: %s' % (sDetails, sXcpt), sCaller);
1418 g_oLock.release();
1419 return False;
1420
1421def testDone(fSkipped = False):
1422 """
1423 Completes the current test (pops it), logging PASSED / FAILURE.
1424
1425 Returns a tuple with the name of the test and its error count.
1426 """
1427 g_oLock.acquire();
1428 rc = g_oReporter.testDone(fSkipped, utils.getCallerName());
1429 g_oLock.release();
1430 return rc;
1431
1432def testErrorCount():
1433 """
1434 Gets the error count of the current test.
1435
1436 Returns the number of errors.
1437 """
1438 g_oLock.acquire();
1439 cErrors = g_oReporter.testErrorCount();
1440 g_oLock.release();
1441 return cErrors;
1442
1443def testCleanup():
1444 """
1445 Closes all open tests with a generic error condition.
1446
1447 Returns True if no open tests, False if something had to be closed with failure.
1448 """
1449 g_oLock.acquire();
1450 fRc = g_oReporter.testCleanup(utils.getCallerName());
1451 g_oReporter.xmlFlush(fRetry = False, fForce = True);
1452 g_oLock.release();
1453 return fRc;
1454
1455
1456#
1457# Sub XML stuff.
1458#
1459
1460def addSubXmlFile(sFilename):
1461 """
1462 Adds a sub-xml result file to the party.
1463 """
1464 fRc = False;
1465 try:
1466 oSrcFile = utils.openNoInherit(sFilename, 'r');
1467 except IOError, oXcpt:
1468 if oXcpt.errno != errno.ENOENT:
1469 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1470 except:
1471 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1472 else:
1473 try:
1474 oWrapper = FileWrapperTestPipe()
1475 oWrapper.write(oSrcFile.read());
1476 oWrapper.close();
1477 except:
1478 logXcpt('addSubXmlFile(%s)' % (sFilename,));
1479 oSrcFile.close();
1480
1481 return fRc;
1482
1483
1484#
1485# Other useful debugging tools.
1486#
1487
1488def logAllStacks(cFrames = None):
1489 """
1490 Logs the stacks of all python threads.
1491 """
1492 sTsPrf = utils.getTimePrefix();
1493 sCaller = utils.getCallerName();
1494 g_oLock.acquire();
1495
1496 cThread = 0;
1497 for idThread, oStack in sys._current_frames().items(): # >=2.5, a bit ugly - pylint: disable=W0212
1498 try:
1499 if cThread > 0:
1500 g_oReporter.log(1, '', sCaller, sTsPrf);
1501 g_oReporter.log(1, 'Thread %s (%#x)' % (idThread, idThread), sCaller, sTsPrf);
1502 try:
1503 asInfo = traceback.format_stack(oStack, cFrames);
1504 except:
1505 g_oReporter.log(1, ' Stack formatting failed w/ exception', sCaller, sTsPrf);
1506 else:
1507 for sInfo in asInfo:
1508 asLines = sInfo.splitlines();
1509 for sLine in asLines:
1510 g_oReporter.log(1, sLine, sCaller, sTsPrf);
1511 except:
1512 pass;
1513 cThread += 1;
1514
1515 g_oLock.release();
1516 return None;
1517
1518def checkTestManagerConnection():
1519 """
1520 Checks the connection to the test manager.
1521
1522 Returns True if the connection is fine, False if not, None if not remote
1523 reporter.
1524
1525 Note! This as the sideeffect of flushing XML.
1526 """
1527 g_oLock.acquire();
1528 fRc = g_oReporter.xmlFlush(fRetry = False, fForce = True);
1529 g_oLock.release();
1530 return fRc;
1531
1532def flushall():
1533 """
1534 Flushes all output streams, both standard and logger related.
1535 """
1536 try: sys.stdout.flush();
1537 except: pass;
1538 try: sys.stderr.flush();
1539 except: pass;
1540
1541 # Note! Current no logger specific streams to flush.
1542
1543 return True;
1544
1545
1546#
1547# Module initialization.
1548#
1549
1550def _InitReporterModule():
1551 """
1552 Instantiate the test reporter.
1553 """
1554 global g_oReporter, g_sReporterName
1555
1556 g_sReporterName = os.getenv("TESTBOX_REPORTER", "local");
1557 if g_sReporterName == "local":
1558 g_oReporter = LocalReporter();
1559 elif g_sReporterName == "remote":
1560 g_oReporter = RemoteReporter();
1561 else:
1562 print >> sys.stderr, os.path.basename(__file__) + ": Unknown TESTBOX_REPORTER value: '" + g_sReporterName + "'";
1563 raise Exception("Unknown TESTBOX_REPORTER value '" + g_sReporterName + "'");
1564
1565if __name__ != "checker": # pychecker avoidance.
1566 _InitReporterModule();
1567
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