TestCenter Reference
Master.py
Go to the documentation of this file.
1 
2 # Already ported to Python 3
3 # **InsertLicense** code author="Gereon A. Frey"
4 
6 
7 # -- system imports ----------------------------------------------------------------------------{{{-
8 import copy
9 import datetime
10 import os
11 import sys
12 import time
13 import logging
14 import traceback
15 import subprocess
16 import tempfile
17 import threading
18 
19 from copy import deepcopy
20 
21 import xml.etree.cElementTree as etree
22 
23 try:
24  import TestSupport
25 except:
26  pass
27 
28 # ----------------------------------------------------------------------------------------------}}}-
29 
30 # -- local imports -----------------------------------------------------------------------------{{{-
31 from . import Config
32 from . import IPC
33 from . import Utils
34 
35 from .LogHandler import LogHandler
36 
37 if sys.version_info.major >= 3:
38  unicode = str
39 
40 # ----------------------------------------------------------------------------------------------}}}-
41 
42 class TestCaseTimer():
43  def __init__(self, seconds):
44  self.start = time.time()
45  self.availableSeconds = seconds
46 
48  passedSeconds = time.time() - self.start
49  return self.availableSeconds - passedSeconds
50 
51  def isExpired(self):
52  return self.getSecondsRemaining() <= 0.0
53 
54 
55 # ----------------------------------------------------------------------------------------------}}}-
56 
58  nextSlaveId = 0
59 
60  def __init__(self, config, logfileDir, env, isInsideTestCaseManager):
61  self.__id = SlaveProcess.nextSlaveId
62  SlaveProcess.nextSlaveId += 1
63  self.__wasKilled = False
64  self.__hasStarted = False
65  self.__logFileFD = None
66  self.__logFile = None
67  self.__slaveLogFile = None
68  self.__logger = logging.getLogger("TestCenter")
69  self.__workDir = os.path.join(config.getResultDir(), "work")
70 
71  cmd = self.__getCommandLine(config, logfileDir, isInsideTestCaseManager)
72  if cmd:
73  self.__startProcess(cmd, config, env)
74 
75  def hasStarted(self):
76  return self.__hasStarted
77 
78  def __getCommandLine(self, config, logfileDir, isInsideTestCaseManager):
79  try:
80  cmd = config.getMLABTestCenterCommand(macroName="TestCenter")
81  if cmd:
82  self.__slaveLogFile = os.path.join(logfileDir, "slaveLogfile_%s.log"%(self.getID()))
83  cmd.insert(1,'-logfile')
84  cmd.insert(2, self.__slaveLogFile)
85  if isInsideTestCaseManager:
86  cmd.append('--started-from-test-case-manager')
88  cmd = None
89 
90  if not cmd:
91  self.__logger.error("Failed to start slave as no MeVisLab executable was found. " + \
92  "Please verify your configuration (see TestCase Manager's " + \
93  "Configuration tab or check the file %s)!" % config.getConfigFilePath())
94  return cmd
95 
96  def __startProcess(self, cmd, _, env):
97  self.__logFileFD, self.__logFile = tempfile.mkstemp()
98  self.__logger.debug('Starting slave with command line %s' % ' '.join(cmd))
99  try:
100  preexec_fn = os.setpgrp if Utils.isUnix else None
101  self.__process = subprocess.Popen(cmd, cwd=self.__workDir, preexec_fn=preexec_fn, env=env,
102  stdin=subprocess.PIPE,
103  stdout=self.__logFileFD, stderr=self.__logFileFD)
104  self.__hasStarted = True
105  except OSError:
106  self.__logger.error("Failed to start slave (%s)\n%s" % (cmd, traceback.format_exc()))
107 
108  def getID(self):
109  return self.__id
110 
111  def getPID(self):
112  return self.__process.pid
113 
114  def isRunning(self):
115  return self.getExitCode() is None
116 
117  def getExitCode(self):
118  return self.__process.poll()
119 
120  def kill(self):
121  self.__wasKilled = Utils.kill(self.__process)
122  return self.__wasKilled
123 
124  def wasKilled(self):
125  return self.__wasKilled
126 
127  def __printLogsAfterError(self):
128  try:
129  output = ''
130  if self.__slaveLogFile != None and os.path.exists(self.__slaveLogFile):
131  output = open(self.__slaveLogFile, 'r').read()
132  if os.path.exists(self.__logFile):
133  output += open(self.__logFile, 'r').read()
134  if len(output) == 0: output = 'none'
135  self.logErrorWithPID('Slave output: %s' % output)
136  except IOError as e:
137  self.logErrorWithPID("Could not read log file" % str(e))
138 
139  def finish(self, printLog):
140  try:
141  os.fsync(self.__logFileFD)
142  os.close(self.__logFileFD)
143  except IOError as e:
144  self.logErrorWithPID("Could not close log file: %s" % str(e))
145  if printLog:
146  self.__printLogsAfterError()
147  if os.path.exists(self.__logFile):
148  try:
149  Utils.removeFile(self.__logFile)
150  except IOError as e:
151  self.logErrorWithPID("Could not remove log file: %s" % str(e))
152  else:
153  self.logMessageWithPID("Log file does not exist: %s" % self.__logFile)
154 
155  def logMessageWithPID(self, message):
156  return "%s (PID: %s)" % (message, str(self.getPID()))
157 
158  def logDebugWithPID(self, message):
159  self.__logger.debug(self.logMessageWithPID(message))
160 
161  def logErrorWithPID(self, message):
162  self.__logger.error(self.logMessageWithPID(message))
163 
164 
165 class ProgressTestCase(object):
166  def __init__(self, name, status, errorCount, warningCount, duration):
167  self.duration = duration
168  self.name = name
169  self.status = status
170  self.errorCount = errorCount
171  self.warningCount = warningCount
172 
173 
174 class TestProgress(object):
175  def __init__(self):
176  self.activeTestCase = None
178  self.testCaseCount = 0
179 
180  def reset(self, testCaseCount):
181  self.activeTestCase = None
182  self.finishedTestCases = []
183  self.testCaseCount = testCaseCount
184 
185 
186 # -- Class Master ------------------------------------------------------------------------------{{{-
187 
193 class Master (object):
194  # -- member variables ------------------------------------------------------------------------{{{-
195 
196  __config = None
197 
198  __com = None
199 
200  # Slave instance to use if offline testing is used.
201  __slave = None
202 
203  __slaveProc = None
204 
205 
206  __hangingSlaves = []
207 
208  # The configuration of the current test run.
209  __testCfg = None
210 
211  __xmlRoot = None
212 
213 
214  __logger = None
215 
216 
217  __testCasesToIgnoreList = []
218 
219  # --------------------------------------------------------------------------------------------}}}-
220 
221  # -- def __init__ ----------------------------------------------------------------------------{{{-
222 
230  def __init__ (self, cfgFile=None, verbose=False, slave=None, slaveEnvironment=None,
231  isInsideTestCaseManager=False):
232  self.__logger = logging.getLogger("TestCenter")
233 
234  self.__config = Config.Configuration(cfgFile)
235 
236  self.__com = None if slave is not None else IPC.ComMaster(connectTimeout=self.__config.getIPCConnectionTimeout())
237  self.__slave = slave
238  self.__slaveEnvironment = slaveEnvironment
239 
241  self.__shouldStop = False
242  self.__lock = threading.Lock()
243 
244  self.__testCaseListener = None
245 
246  self.__testedPkgList = []
247 
248  # Clean up existing result directory or create it.
249  resultDir = self.__config.getResultDir()
250  imgDir = os.path.join(resultDir, "images")
251  fileDir = os.path.join(resultDir, "files")
252  workDir = os.path.join(resultDir, "work")
253  self.logfileDir = os.path.join(resultDir, "logfiles")
254 
255  if not os.path.exists(resultDir):
256  os.makedirs(resultDir)
257  # Clean up old file directory.
258  Utils.rmtree(fileDir)
259  os.makedirs(fileDir)
260  # Clean up old image directory.
261  Utils.rmtree(imgDir)
262  os.makedirs(imgDir)
263  # Clean up old work directory.
264  Utils.rmtree(workDir)
265  os.makedirs(workDir)
266  # Clean up old logfile directory.
267  Utils.rmtree(self.logfileDir)
268  os.makedirs(self.logfileDir)
269 
270  self.__isInsideTestCaseManager = isInsideTestCaseManager
271 
272  self.__testCfg = self.__testConfigSkeleton()
273 
274  self.__updateProgressFunc = None
275 
276  # If not running offline start a second MeVisLab instance.
277  if self.__slave is None:
278  if not self.__startSlave():
279  raise Exception("Failed to start slave")
280 
281  # --------------------------------------------------------------------------------------------}}}-
282 
283  def __del__(self):
284  for slaveProc in self.__hangingSlaves:
285  slaveProc.finish(printLog = True)
286  if not slaveProc.kill():
287  self.__logger.error("Finally killing process %s failed." % slaveProc.getPID())
288 
289  # --------------------------------------------------------------------------------------------}}}-
290 
291  def stop(self):
292  self.__lock.acquire()
293  self.__shouldStop = True
294  self.__lock.release()
295 
296  def shouldStop(self):
297  self.__lock.acquire()
298  result = self.__shouldStop
299  self.__lock.release()
300  return result
301 
302  def getTestProgress(self):
303  self.__lock.acquire()
304  result = copy.deepcopy(self.__testProgress)
305  self.__lock.release()
306  return result
307 
308  def resetTestProgress(self, testCaseCount):
309  self.__lock.acquire()
310  self.__testProgress.reset(testCaseCount)
311  self.__lock.release()
312 
313  def updateTestProgress(self, finishedTestCase=None, additionalTestCaseCount=None):
314  progressTestCase = None
315  if finishedTestCase is not None:
316  errorCount = 0
317  warningCount = 0
318  for function in finishedTestCase.findall("Function"):
319  status = int(function.get("status", str(Utils.TEST_STATUS_DISABLED)))
320  if status in (Utils.TEST_STATUS_ERROR, Utils.TEST_STATUS_CRASHED,
321  Utils.TEST_STATUS_FUNCTION_NOT_FOUND, Utils.TEST_STATUS_TIMEOUT):
322  errorCount += 1
323  elif status == Utils.TEST_STATUS_WARNING:
324  warningCount += 1
325  progressTestCase = ProgressTestCase(finishedTestCase.get("name"),
326  int(finishedTestCase.get("status", str(Utils.TEST_STATUS_CANCELLED))),
327  errorCount,
328  warningCount,
329  float(finishedTestCase.get("duration", "0")))
330 
331  self.__lock.acquire()
332  if progressTestCase is not None:
333  self.__testProgress.finishedTestCases.append(progressTestCase)
334  if additionalTestCaseCount is not None:
335  self.__testProgress.testCaseCount += additionalTestCaseCount
336  self.__lock.release()
337 
338  def setUpdateProgressFunc(self, updateProgressFunc):
339  self.__updateProgressFunc = updateProgressFunc
340 
341  def setActiveTestCase(self, testCase):
342  self.__lock.acquire()
343  self.__testProgress.activeTestCase = testCase
344  self.__lock.release()
345 
346  # -- def __testConfigSkeleton() --------------------------------------------------------------{{{-
347 
348  def __testConfigSkeleton(self):
349  return etree.fromstring("""
350  <Configuration>
351  <TestGroups />
352  <TestCases />
353  <TestSuites />
354  <Modules />
355  <Packages>
356  <Tested />
357  <Available />
358  <Ignored />
359  </Packages>
360  <TestOptions />
361  </Configuration>""")
362  # --------------------------------------------------------------------------------------------}}}-
363 
364  def setTestCaseListener(self, listener):
365  self.__testCaseListener = listener
366 
367  # -- def setTestCases ------------------------------------------------------------------------{{{-
368 
370  def setTestCases (self, testCaseList):
371  assert self.__xmlRoot == None
372 
373  xmlNode = self.__testCfg.find("TestCases")
374  for testCaseName in testCaseList:
375  etree.SubElement(xmlNode, "TestCase", name=testCaseName)
376  # --------------------------------------------------------------------------------------------}}}-
377 
378  # -- def setIgnoreTestCases ------------------------------------------------------------------------{{{-
379 
381  def setTestCasesToIgnore (self, testCaseToIgnoreList):
382  self.__testCasesToIgnoreList = testCaseToIgnoreList
383  # --------------------------------------------------------------------------------------------}}}-
384 
385  # -- def setTestGroups -----------------------------------------------------------------------{{{-
386 
388  def setTestGroups (self, testGroupList):
389  assert self.__xmlRoot == None
390 
391  xmlNode = self.__testCfg.find("TestGroups")
392  for testGroupName in testGroupList:
393  etree.SubElement(xmlNode, "TestGroup", name=testGroupName)
394  # --------------------------------------------------------------------------------------------}}}-
395 
396  def setTestSuites (self, testSuiteList):
397  assert self.__xmlRoot == None
398 
399  xmlNode = self.__testCfg.find("TestSuites")
400  for testSuiteName in testSuiteList:
401  etree.SubElement(xmlNode, "TestSuite", name=testSuiteName)
402 
403 
404  # -- def setModules --------------------------------------------------------------------------{{{-
405 
410  def setModules (self, moduleList=[], filterList=[]):
411  assert self.__xmlRoot == None
412 
413  xmlNode = self.__testCfg.find("Modules")
414  for moduleName in moduleList:
415  etree.SubElement(xmlNode, "Module", name=moduleName)
416  for filter in filterList:
417  etree.SubElement(xmlNode, "Filter").text = filter
418  # --------------------------------------------------------------------------------------------}}}-
419 
420  # -- def setPackages -------------------------------------------------------------------------{{{-
421 
430  def setPackages (self, testedPkgList=[], availablePkgList=[], ignoredPkgList=[]):
431  assert self.__xmlRoot == None
432 
433  self.__testedPkgList = testedPkgList
434 
435  xmlNode = self.__testCfg.find("Packages/Tested")
436  for packageID in testedPkgList:
437  etree.SubElement(xmlNode, "Package", name=packageID)
438 
439  xmlNode = self.__testCfg.find("Packages/Available")
440  for packageID in availablePkgList:
441  etree.SubElement(xmlNode, "Package", name=packageID)
442 
443  xmlNode = self.__testCfg.find("Packages/Ignored")
444  for packageID in ignoredPkgList:
445  etree.SubElement(xmlNode, "Package", name=packageID)
446  # --------------------------------------------------------------------------------------------}}}-
447 
448  def __testTypeAndName(self, xmlNode):
449  # Just print anything if we are in verbose mode.
450  testType = xmlNode.get('type')
451  testName = xmlNode.get('name') if testType == 'FunctionalTestCase' else "%s::%s" % (xmlNode.get('name'), xmlNode.get('module'))
452 
453  return testType, testName
454 
455  # -- def __statusMessage ---------------------------------------------------------------------{{{-
456 
460  def __statusMessage (self, xmlNode, status):
461  assert type(status) == int
462  assert xmlNode.tag == 'TestCase'
463 
464  statusMapping = { -1:"Unknown", 4:"Crashed", 3:"Timeout", 0:"Ok", 1:"Warning", 2:"Error", 6:"Cancelled", 7:"Test Function Not Found", 8:"Generic Test Case not applied to Module" }
465  statusStr = statusMapping[status]
466 
467  testType, testName = self.__testTypeAndName(xmlNode)
468  fillIn = "."*(80-len(testType+testName+statusStr)-2)
469  if status != 0 and status != 8:
470  if status == 1:
471  self.__logger.warning("%s::%s%s%s" % (testType, testName, fillIn, statusStr))
472  else:
473  self.__logger.error("%s::%s%s%s" % (testType, testName, fillIn, statusStr))
474  else:
475  self.__logger.debug("%s::%s%s%s" % (testType, testName, fillIn, statusStr))
476 
477  # --------------------------------------------------------------------------------------------}}}-
478 
479  # -- def __testFailed ------------------------------------------------------------------------{{{-
480 
490  def __testFailed (self, xmlNode, comError):
491  assert xmlNode.tag == "TestCase"
492 
493  testType = xmlNode.get('type')
494  if testType == u"GenericTestCase":
495  testName = "%s::%s" % (xmlNode.get('name'), xmlNode.get('module'))
496  else:
497  testName = xmlNode.get('name')
498 
499  # Build a mapping from function names to result nodes.
500  funcDict = {}
501  for funcNode in xmlNode.findall('Function'):
502  funcDict.setdefault(funcNode.get('name'), funcNode)
503 
504  # With the previously gathered error status the reason can be determined.
505  status = 2 # Failed
506  if comError in (41, 42):
507  status = 4 # Slave crashed.
508  elif comError == 21:
509  status = 3 # Slave timed out.
510 
511  # Read the slave's logfile and parse it to get as much results as possible.
512  logHandler = LogHandler(self.__config.getSlaveLogFilePath())
513  logHandler.parseLogFile(testType, testName, status, funcDict)
514 
515  return status
516  # --------------------------------------------------------------------------------------------}}}-
517 
518  # -- def __buildTestAgenda -------------------------------------------------------------------{{{-
519 
522  def __buildTestAgenda (self):
523  resultNode = self.__sendRequest("BuildTestAgenda", self.__testCfg)
524  if resultNode.get('errorCode') != "Ok":
525  self.__logger.critical("Error (%s) in generating the test agenda!\n%s" % (resultNode.get('errorCode'), resultNode.find('Error').text))
526  return False
527  self.__xmlRoot = resultNode.find('TestRun')
528  self.__testCfg = self.__verifyConfiguration(resultNode.find('Configuration'))
529 
530  # Set package list as testing is started afterwards.
531  self.__sendRequest('SetPackageList', self.__testCfg.find('Packages'), 5, restart=True)
532  return True
533  # --------------------------------------------------------------------------------------------}}}-
534 
535  # -- def __buildTestCaseAgenda -------------------------------------------------------------------{{{-
536  def __buildTestCaseAgenda (self, testCaseNode):
537  parameterNode = etree.Element('Parameters')
538  genericTestCase = True # is it possible to determine the testcase type here?
539  if genericTestCase:
540  moduleList = []
541  for module in self.__xmlRoot.findall('Information/Modules/Module'):
542  moduleList.append(module.attrib['name'])
543  parameterNode.attrib['modules'] = ','.join(moduleList)
544  if len(self.__testedPkgList) > 0:
545  parameterNode.attrib['packages'] = ','.join(self.__testedPkgList)
546  else:
547  # test every package
548  parameterNode.attrib['packages'] = '*/*'
549  parameterNode.append(testCaseNode)
550  resultNode = self.__sendRequest("BuildTestCaseAgenda", parameterNode, restart=True)
551  if resultNode.get('errorCode') != "Ok":
552  self.__logger.critical("Error (%s) in generating the test case agenda!\n%s" % (resultNode.get('errorCode'), resultNode.find('Error').text))
553  return None
554 
555  return resultNode
556 
557  # --------------------------------------------------------------------------------------------}}}-
558 
559  # -- def __verifyConfiguration ---------------------------------------------------------------{{{-
560 
567  def __verifyConfiguration (self, testCfg):
568  # Verify package settings.
569  packageRoot = testCfg.find('Packages')
570 
571  tPkgNode = packageRoot.find('Tested')
572  for packageNode in tPkgNode.findall('Package'):
573  if packageNode.get('status') not in ('Ok', 'Added', 'Substituted'):
574  self.__logger.error("Failed to verify package (%s with status '%s')!" % (packageNode.get('name'), packageNode.get('status')))
575 
576  aPkgNode = packageRoot.find('Available')
577  for packageNode in aPkgNode.findall('Package'):
578  if packageNode.get('status') not in ('Ok', 'Added', 'Substituted'):
579  self.__logger.error("Failed to verify package (%s with status '%s')!" % (packageNode.get('name'), packageNode.get('status')))
580  else:
581  if packageNode.get('status') not in ('Substituted'):
582  packageRoot.append(packageNode)
583 
584  packageRoot.remove(tPkgNode)
585  packageRoot.remove(aPkgNode)
586 
587  iPkgNode = packageRoot.find('Ignored')
588  packageRoot.remove(iPkgNode)
589 
590  # Verify the module settings.
591  for moduleNode in testCfg.findall('Modules/Module'):
592  isModuleFiltered = moduleNode.get('isModuleFiltered') == str(True)
593  if not isModuleFiltered and moduleNode.get('status') != str(Utils.TEST_STATUS_OK):
594  self.__logger.error("Failed to verify module (%s with status '%s')" % (moduleNode.get('name'), moduleNode.get('status')))
595  testCfg.find('Modules').clear()
596 
597  # Verify the test case settings.
598  for testCaseNode in testCfg.findall('TestCases/TestCase'):
599  isNotInTestGroups = testCaseNode.get('isNotInTestGroups') == str(True)
600  if not isNotInTestGroups and testCaseNode.get('status') != str(Utils.TEST_STATUS_OK):
601  self.__logger.error("Failed to verify test case (%s with status '%s')" % (testCaseNode.get('name'), testCaseNode.get('status')))
602  testCfg.find('TestCases').clear()
603 
604  return testCfg
605  # --------------------------------------------------------------------------------------------}}}-
606 
607  def __createTimeoutTooHighErrorFunction(self, testCaseInfoNode, testResultNode, timeout):
608  functionName = 'TEST001_VerifyTestCaseTimeout'
609  testFunctions = testCaseInfoNode.find('TestFunctions')
610  testFunction = etree.SubElement(testFunctions, "Function")
611  testFunction.attrib = {'basename': 'VerifyTestCaseTimeout', 'is_disabled': 'False', 'name': functionName}
612  etree.SubElement(testFunction, "Documentation").text = 'Verifies if the TestCase timeout.'
613  timeoutTooHighTestFunction = etree.SubElement(testResultNode, "Function")
614  timeoutTooHighTestFunction.attrib = {'duration': '0', 'name': functionName, 'status': '2', 'is_disabled': 'False', 'try': '1'}
615  events = etree.SubElement(timeoutTooHighTestFunction, 'Events')
616  filename = testCaseInfoNode.find('File').text
617  line = testCaseInfoNode.find('Line').text
618  event = etree.SubElement(events, 'Event')
619  event.attrib = {'file': filename, 'internal': 'true', 'line': line, 'origin': '', 'type': 'Error',
620  'timestamp': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
621  error = 'The TestCase timeout (%ss) exceeds the maximum allowed timeout (%ss)' % (timeout, self.__config.getMaxTestTimeoutSeconds())
622  etree.SubElement(event, 'Message').text = error
623 
624  def __buildListOfTestFunctionsToExecute (self, testCaseInfoNode, testCaseNode, funcList=None):
625  # Build up list of test functions that must be executed.
626  testFuncList = []
627  for funcNode in testCaseInfoNode.findall(".//Function"):
628  funcName = funcNode.get('name')
629  # If user selected to process only specific functions ignore the rest.
630  if funcList and funcName not in funcList and not funcName in Utils.VIRTUAL_TEST_CASE_FUNCTIONS:
631  continue
632 
633  isDisabled = funcNode.get("is_disabled", str(False)) == str(True)
634  if testCaseInfoNode.get('name') in self.__testCasesToIgnoreList:
635  isDisabled = True
636  if isDisabled and funcList:
637  # If a user selected the function, then it should be executed
638  isDisabled = False
639  funcNode.set('is_disabled', str(False))
640  funcResultNode = etree.SubElement(testCaseNode, 'Function', name=funcName, is_disabled=str(isDisabled))
641  testFuncList.append(funcName)
642  return testFuncList
643 
644 
645  # -- def __doTest ----------------------------------------------------------------------------{{{-
646 
652 
653  def __doTest (self, testCaseInfoNode, testResultNode, funcList=None):
654  assert testResultNode.tag == 'TestCase'
655 
656  testCaseNode = deepcopy(testResultNode)
657 
658  # Build up list of test functions that must be executed.
659  testFuncList = self.__buildListOfTestFunctionsToExecute(testCaseInfoNode, testCaseNode, funcList)
660 
661  testCaseName = testCaseInfoNode.get('name')
662  if self.__testCaseListener:
663  self.__testCaseListener.startTestCase(testCaseName, testFuncList)
664 
665  # Gather time till timeout.
666  hasValidTimeout = True
667  timeout = int(testCaseInfoNode.get('timeout'))
668  if timeout == 0:
669  timeout = self.__config.getDefaultTestTimeoutSeconds()
670  if self.__config.getMaxTestTimeoutSeconds():
671  if timeout > self.__config.getMaxTestTimeoutSeconds():
672  self.__createTimeoutTooHighErrorFunction(testCaseInfoNode, testResultNode, timeout)
673  hasValidTimeout = False
674 
675  # Use a bigger timeout if coverage or bullseye are enabled
676  if self.__config.isPythonCoverageEnabled():
677  timeout = timeout * 4.
678  if self.__config.isBullseyeCoverageEnabled():
679  timeout = timeout * 4.
680 
681  if hasValidTimeout:
682  retries, status = self.__executeTestFunctions(testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout)
683  else:
684  retries = 0
685  status = Utils.TEST_STATUS_ERROR
686 
687  # Set status and number of retries for the given test case.
688  testResultNode.set("status", unicode(status))
689  testResultNode.set("retries", unicode(retries))
690  # Print status message.
691  self.__statusMessage(testResultNode, status)
692  if self.__testCaseListener:
693  self.__testCaseListener.endTestCase()
694  # --------------------------------------------------------------------------------------------}}}-
695 
696 
697  def transferNodeFromExecutionToResultXML(self, nodeName, executionNode, testResultNode):
698  coverageFile = executionNode.get(nodeName)
699  if executionNode.get(nodeName) is not None:
700  testResultNode.set(nodeName, coverageFile)
701 
702  def __executeTestFunctions (self, testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout):
703  if testCaseNode.get('status') == str(Utils.TEST_STATUS_DO_NOT_TEST_MODULE):
704  return 0, Utils.TEST_STATUS_DO_NOT_TEST_MODULE
705 
706  retries = 0
707  failedFuncList = []
708  timer = TestCaseTimer(timeout)
709 
710  status = Utils.TEST_STATUS_OK
711  maxFuncStatus = Utils.TEST_STATUS_OK
712  cancelled = False
713  while testFuncList and not timer.isExpired() and not cancelled:
714  # If not set status to timeout and stop execution of remaining test functions.
715 
716  if self.shouldStop():
717  cancelled = True
718  status = Utils.TEST_STATUS_CANCELLED
719  break
720 
721  # if we have a test case listener, we request progress calls from the slave:
722  if self.__testCaseListener:
723  testCaseNode.set("requestProgress", "1")
724 
725  testCaseNode.set("maxErrorMessagesPerTestFunction", str(self.__config.getReportOptions().maxErrorMessagesPerTestFunction))
726  testCaseNode.set("maxInfoMessagesPerTestFunction", str(self.__config.getReportOptions().maxInfoMessagesPerTestFunction))
727 
728  # Initiate the processing of the remaining test functions.
729  executionNode = self.__sendRequest("DoTest", testCaseNode, timeout=timer.getSecondsRemaining(), tries=1)
730  if executionNode.get('errorCode') != "Ok":
731  self.__logger.debug('Restarting due to error in test execution.\n%s' % executionNode.find('Error').text)
732 
733  # Try to determine what was the reason for the test to fail.
734  comError = self.__com.getLastError() if self.__com else 0
735 
736  # Delete slave process.
737  self.stopSlave()
738 
739  # Handle logfile to get gathered results.
740  status = self.__testFailed(testCaseNode, comError)
741  # Restart slave.
742  if not self.__startSlave():
743  self.__logger.debug('Failed to re-start slave.')
744  else:
745  testCaseNode = executionNode.find('TestCase')
746 
747  self.transferNodeFromExecutionToResultXML('pythonCoverage', executionNode, testResultNode)
748  self.transferNodeFromExecutionToResultXML('bullseyeCoverage', executionNode, testResultNode)
749 
750  extraTestCasesResults = testCaseNode.find('ExtraTestCasesResults')
751  if extraTestCasesResults != None:
752  testResultNode.append(deepcopy(extraTestCasesResults))
753 
754  # functions to test.
755  for funcResultNode in testCaseNode.findall('Function'):
756  funcName = funcResultNode.get("name")
757 
758  # If status is not set, the function has not been executed yet.
759  funcStatus = funcResultNode.get("status")
760  if not funcStatus:
761  # Function not processed yet.
762  continue
763  else:
764  funcStatus = int(funcStatus)
765  if maxFuncStatus < funcStatus:
766  maxFuncStatus = funcStatus
767 
768  if funcName not in failedFuncList:
769  funcResultNode.set('try', '1')
770 
771  if funcStatus in (Utils.TEST_STATUS_CRASHED , Utils.TEST_STATUS_TIMEOUT):
772  failedFuncList.append(funcName)
773  retries += 1
774  # Create copy of node as it will be overwritten in the next iteration.
775  testResultNode.append(deepcopy(funcResultNode))
776  funcResultNode.remove(funcResultNode.find("Events"))
777  else:
778  funcResultNode.set('try', '2')
779  failedFuncList.remove(funcName)
780 
781  # Remove all processed but not failed for the first time functions from
782  # the temporary node.
783  if funcName not in failedFuncList:
784  testFuncList.remove(funcName)
785  # Move the processed function to the result node.
786  testResultNode.append(funcResultNode)
787  testCaseNode.remove(funcResultNode)
788  if funcStatus == Utils.TEST_STATUS_CANCELLED or funcStatus == Utils.TEST_STATUS_FUNCTION_NOT_FOUND:
789  cancelled = True
790  break
791 
792  if timer.isExpired():
793  status = Utils.TEST_STATUS_TIMEOUT
794 
795 
796  # The status variable is used for status > 2 to handle situations where:
797  # - all test functions worked but the slave crashed or timed out.
798  # - the test case timed out but had crashes, too (global status should be
799  # timeout).
800  # Otherwise the maximum function status found in the reports is used.
801  status = status if status > 2 else maxFuncStatus
802  return retries, status
803 
804 
805  # -- def __handleExtraTestCaseResults --------------------------------------------------------{{{-
806 
809  def __handleExtraTestCaseResults(self, testCaseInfos, resultNode, testResultNode):
810  node = testResultNode.find("ExtraTestCasesResults")
811  if node != None:
812  testResultNode.remove(node)
813  for node in node.findall("ExtraTestCaseResult"):
814  testCaseInfos.append(node.find("Information/TestCase"))
815  extraTestCaseResult = node.find("Result/TestCase")
816  extraTestCaseResult.set("slave_id", testResultNode.get("slave_id"))
817  resultNode.append(extraTestCaseResult)
818  # --------------------------------------------------------------------------------------------}}}-
819 
820  # -- def shouldMeasurePythonCoverageGlobally -------------------------------------------------{{{-
822  return self.__config.isPythonCoverageEnabled() and self.__config.isGlobalPythonCoverageEnabled()
823  # --------------------------------------------------------------------------------------------}}}-
824 
825  # -- def run ---------------------------------------------------------------------------------{{{-
826 
835  def run(self, funcDict=None, stopSlaveAfterTestRun=True):
836  if not self.__buildTestAgenda():
837  self.__logger.error("Failed to build test agenda.")
838  return None
839 
840  # Start the timer.
841  timeStarted = time.time()
842 
843  testCaseInfos = self.__xmlRoot.find('Information/TestCases')
844 
845  # Fetch counters which control after what number of testCases MLAB
846  # should be restarted.
847  freshCounterValue = self.__config.getRestartInterval()
848  restartCounter = freshCounterValue
849  restartDisabled = (restartCounter == 0)
850 
851  testCaseInfosTestCases = testCaseInfos.findall('TestCase')
852  if self.__testCaseListener:
853  testNames = []
854  for node in testCaseInfosTestCases:
855  testNames.append(node.get('name'))
856  self.__testCaseListener.startTestRun(testNames)
857 
858  self.resetTestProgress(len(testCaseInfosTestCases))
859 
860  failedTestCases = 0
861  finishedTestCases = 0
862  totalTestCases = len(testCaseInfosTestCases)
863 
864  resultNode = self.__xmlRoot.find('Results')
865  cancelled = False
866  for testCaseNode in testCaseInfosTestCases:
867  if cancelled:
868  testCaseInfos.remove(testCaseNode)
869  self.updateTestProgress(finishedTestCase=testCaseNode)
870  continue
871  try:
872  testCaseAgenda = self.__buildTestCaseAgenda(testCaseNode)
873  if testCaseAgenda != None:
874 
875  testCaseInfoNodes = testCaseAgenda.findall('TestCase')
876  assert(len(testCaseInfoNodes) == 1)
877  testCaseInfoNode = testCaseInfoNodes[0]
878 
879  testCaseInfos.remove(testCaseNode)
880  testCaseInfos.append(testCaseInfoNode)
881 
882  testResultNodes = testCaseAgenda.find('Results').findall('TestCase')
883  if len(testResultNodes) > 1:
884  self.updateTestProgress(additionalTestCaseCount=len(testResultNodes)-1)
885  totalTestCases += len(testResultNodes)-1
886  for testResultNode in testResultNodes:
887  if cancelled:
888  break
889  try:
890  testCaseName = testCaseInfoNode.get('name')
891  debugType, debugName = self.__testTypeAndName(testResultNode)
892 
893  preferredRendererNode = testCaseInfoNode.find('preferredRenderer')
894  preferredRenderer = "" if preferredRendererNode is None else preferredRendererNode.text.lower()
895  forceSoftwareRendering = (preferredRenderer == "software")
896  if preferredRenderer == "hardware" and Utils.isMesaForced():
897  self.__logger.warning('preferredRenderer settings "hardware" detected but use of software driver is enforced.')
898 
899  if not restartDisabled or forceSoftwareRendering:
900  shouldRestart = (restartCounter == 0)
901  if shouldRestart or forceSoftwareRendering:
902  self.__logger.info("Restart slave for test case %s::%s" % (debugType, debugName))
903  if not self.__startSlave(forceSoftwareRendering=forceSoftwareRendering):
904  raise Exception('Failed to start slave.')
905 
906  restartCounter = freshCounterValue
907 
908  #decrement counter on each pass
909  restartCounter -= 1
910 
911  self.setActiveTestCase(testCaseName)
912 
913  if self.__updateProgressFunc is not None:
914  self.__updateProgressFunc(testCaseName, failedTestCases, finishedTestCases, totalTestCases)
915 
916  timeFunctionStarted = time.time()
917  funcList = funcDict[testCaseName] if (funcDict and testCaseName in funcDict) else None
918  self.__doTest(testCaseInfoNode, testResultNode, funcList)
919  timeFunctionStopped = time.time()
920  if int(testResultNode.get("status")) == Utils.TEST_STATUS_CANCELLED:
921  cancelled = True
922 
923  if int(testResultNode.get("status")) in (Utils.TEST_STATUS_CRASHED,
924  Utils.TEST_STATUS_ERROR,
925  Utils.TEST_STATUS_FUNCTION_NOT_FOUND,
926  Utils.TEST_STATUS_TIMEOUT):
927  failedTestCases += 1
928  finishedTestCases += 1
929 
930  testResultNode.set('duration', str(timeFunctionStopped-timeFunctionStarted))
931 
932  testResultNode.set('slave_id', '0' if self.__slave else str(self.__slaveProc.getID()))
933 
934  resultNode.append(testResultNode)
935 
936  self.__handleExtraTestCaseResults(testCaseInfos, resultNode, testResultNode)
937 
938  except Exception as e:
939  traceback.print_exc()
940  self.__logger.error("Error executing test case %s::%s\n%s" % (debugType, debugName, e))
941  testResultNode.set("status", unicode(Utils.TEST_STATUS_TIMEOUT))
942  self.updateTestProgress(finishedTestCase=testResultNode)
943  else:
944  self.__logger.error("Failed to build the test agenda for test case %s." % testCaseNode.get('name', 'unknown'))
945  except Exception as e:
946  self.__logger.error("Error executing test case %s" % e)
947 
949  resultNode.set("combinePythonCoverage", "True")
950 
951  # Stop the timer.
952  timeStopped = time.time()
953 
954  self.__xmlRoot.set('timestamp', str(timeStarted))
955  self.__xmlRoot.set('duration', str(timeStopped-timeStarted))
956 
957  if stopSlaveAfterTestRun:
958  self.stopSlave()
959 
960  xml = Utils.saveXMLTree(self.__xmlRoot, self.__config.getResultFile())
961 
962  if self.__testCaseListener:
963  self.__testCaseListener.endTestRun()
964 
965  return xml
966  # --------------------------------------------------------------------------------------------}}}-
967 
968  # -- def reset() -----------------------------------------------------------------------------{{{-
969 
972  def reset(self):
973  self.__xmlRoot = None
974  self.__testCfg = self.__testConfigSkeleton()
975  # --------------------------------------------------------------------------------------------}}}-
976 
977  # -- def getConfig() -------------------------------------------------------------------------{{{-
978  def getConfig (self):
979  return self.__config
980  # --------------------------------------------------------------------------------------------}}}-
981 
982  # -- def __isSlaveRunning -------------------------------------------------------------------------{{{-
983 
984  def __isSlaveRunning (self):
985  return self.__slaveProc and self.__slaveProc.isRunning()
986 
987  def __isSlaveStopped(self):
988  return not self.__isSlaveRunning()
989  # --------------------------------------------------------------------------------------------}}}-
990 
991  def logMessageWithPID(self, message):
992  pidString = str(self.__slaveProc.getPID()) if self.__slaveProc is not None else str(None)
993 
994  return "%s (PID: %s)" % (message, pidString)
995 
996  def logDebugWithPID(self, message):
997  self.__logger.debug(self.logMessageWithPID(message))
998 
999  def logErrorWithPID(self, message):
1000  self.__logger.error(self.logMessageWithPID(message))
1001 
1002  def logMessageWithLastError(self, message):
1003  errorCode, errorMessage = self.__com.getLastErrorWithMessage()
1004  return "%s ([%d] %s)" % (message, errorCode, errorMessage)
1005 
1006  # -- def __startSlave ------------------------------------------------------------------------{{{-
1007 
1011  def __startSlave (self, forceSoftwareRendering=False):
1012  if self.__slave:
1013  return True
1014 
1015  if self.__isSlaveRunning():
1016  if not self.stopSlave():
1017  self.__logger.error("Failed to stop the slave before restart.")
1018 
1019  env = Utils.setForceMesa(self.__slaveEnvironment) if forceSoftwareRendering else self.__slaveEnvironment
1020  slaveProc = SlaveProcess(self.__config, self.logfileDir, env, self.__isInsideTestCaseManager)
1021  if slaveProc.hasStarted():
1022  self.__slaveProc = slaveProc
1023  else:
1024  return False
1025 
1026  return self.__connectToSlave()
1027  # --------------------------------------------------------------------------------------------}}}-
1028 
1029  def __connectToSlave(self):
1030  retries = 0
1031  timerSleep = 1
1032  maxTries = 15
1033  while retries < maxTries and not self.__com.connect(timeout = self.__config.getIPCConnectionTimeout()):
1034  retries += 1
1035  self.logDebugWithPID(self.logMessageWithLastError("Failed to connect to slave in %i. try" % retries))
1036 
1037  if not self.__slaveProc.isRunning():
1038  self.__logger.error("Slave died with exit code %s" % self.__slaveProc.getExitCode())
1039  self.__slaveProc.finish(printLog=True)
1040  return False
1041 
1042  # Use a sleep timer to give the slave process more time to start up.
1043  self.__logger.debug("Sleeping for %s seconds" % (timerSleep))
1044  time.sleep(timerSleep)
1045 
1046  if not self.__com.isConnected():
1047  # quit the mlab that we were not able to talk to
1048  self.logErrorWithPID("Failed to connect to slave within %d seconds." % (maxTries*timerSleep))
1049  if not self.stopSlave():
1050  self.__logger.error("Failed to stop the slave after connection error.")
1051  self.__slaveProc.finish(printLog=True)
1052  return False
1053  else:
1054  self.logDebugWithPID("Connected to slave in %s. try" % (retries+1))
1055 
1056  # Send slave a list of packages that are available. On first run an empty
1057  # string is sent; in the __buildTestAgenda call this list will be
1058  # retrieved. Later the verified list of tests will be sent.
1059  if self.__xmlRoot:
1060  resultNode = self.__sendRequest('SetPackageList', self.__testCfg.find('Packages'), 5)
1061  return resultNode.get('errorCode') == "Ok"
1062 
1063  return True
1064 
1065  # -- def stopSlave ---------------------------------------------------------------------------{{{-
1066  # # Stop the slave process.
1067  # If the slave is running this method tries to stop the slave. This method
1068  # will run until the slave has stopped. After 3 tries on unix a KILL signal
1069  # is sent.
1070  # @return True if the slave was stopped.
1071  def stopSlave (self):
1072  if self.__slave:
1073  #ugly special condition for when a running MeVisLab instance is used
1074  #as slave.
1075  return True
1076 
1077  self.logDebugWithPID("Shutting down slave")
1078 
1079  if not self.__isSlaveStopped():
1080  #try soft shutdown first by requesting clean quit
1081  xmlNode = self.__sendRequest('Quit', timeout=1, tries=1)
1082 
1083  if xmlNode.get('errorCode') == 'Ok':
1084  self.logDebugWithPID("Response received, waiting for shutdown")
1085  # wait up to five seconds for the slave to stop:
1086  i = 0
1087  while not self.__isSlaveStopped() and i < 10:
1088  time.sleep(0.5)
1089  i += 1
1090 
1091  #kindly asking for shutdown was obviously not enough, enforce shutdown
1092  if not self.__isSlaveStopped():
1093  self.logDebugWithPID("No response, killing slave")
1094  self.__slaveProc.kill()
1095 
1096  if self.__isSlaveStopped():
1097  self.__slaveProc.finish(printLog=self.__slaveProc.wasKilled())
1098  else:
1099  self.logErrorWithPID("Failed to kill slave!")
1100  self.__hangingSlaves.append(self.__slaveProc)
1101  self.__slaveProc = None
1102 
1103  return self.__isSlaveStopped()
1104  # --------------------------------------------------------------------------------------------}}}-
1105 
1106  # -- def __sendRequest -----------------------------------------------------------------------{{{-
1107 
1114  def __sendRequest (self, command, parameterNode=None, timeout=None, tries=2, restart=False):
1115  trial = 0
1116 
1117  requestNode = etree.Element('Command', type=command)
1118  requestNode.append(parameterNode if parameterNode != None else etree.Element('Dummy'))
1119 
1120  if self.__slave:
1121  self.logDebugWithPID("Handling '%s' request" % command)
1122  resultNode = self.__slave.runCommand(requestNode)
1123  else:
1124  self.logDebugWithPID("Sending '%s' request to slave" % command)
1125  resultNode = None
1126  # The default timeout is taken from the configuration file.
1127  if not timeout:
1128  timeout = self.__config.getDefaultTestTimeoutSeconds()
1129 
1130  string = None
1131  while ( not string
1132  and (trial < tries)):
1133  timer = TestCaseTimer(timeout)
1134  if self.__com.send(etree.tostring(requestNode), timer.getSecondsRemaining()):
1135  # since we allow receiving intermediate "Progress" result values,
1136  # we loop until we receive something that is not a "Progress" message:
1137  while not string and not timer.isExpired():
1138  string = self.__com.recv(timer.getSecondsRemaining())
1139  if string:
1140  recvNode = Utils.filterXMLString(string)
1141  self.logDebugWithPID("Received node of type '%s'" % recvNode.tag)
1142  if recvNode.tag == "Progress":
1143  shouldStop = False
1144  if self.__testCaseListener:
1145  try:
1146  self.__testCaseListener.startTestCaseFunction(recvNode.get("testType"), recvNode.get("testCase"), recvNode.get("testFunction"))
1148  shouldStop = True
1149  # tell slave if it should stop or go on:
1150  self.__com.send("0" if shouldStop else "1", 5)
1151  string = None
1152  elif recvNode.tag == "Status":
1153  if self.__testCaseListener:
1154  self.__testCaseListener.endTestCaseFunction(int(recvNode.get("status")), 0)
1155  string = None
1156  else:
1157  resultNode = recvNode
1158  else:
1159  break
1160 
1161  if not string:
1162  trial += 1
1163 
1164  if restart and (trial < tries):
1165  self.__logger.info("Restarting due to communication error ...")
1166  self.__startSlave()
1167 
1168  if resultNode == None:
1169  self.logDebugWithPID("Error: Received no result from slave.")
1170  resultNode = etree.Element('Result', errorCode='CommunicationError')
1171  etree.SubElement(resultNode, 'Error').text = "Communication error [%d]: %s" % self.__com.getLastErrorWithMessage()
1172  self.logDebugWithPID("Result error code: %s" % resultNode.get('errorCode'))
1173  content = etree.tostring(resultNode).decode()
1174  self.logDebugWithPID("Result content: %s%s" % (content[:150], " [...]" if len(content)>150 else ""))
1175  return resultNode
1176  # --------------------------------------------------------------------------------------------}}}-
1177 # ----------------------------------------------------------------------------------------------}}}-
list __testCasesToIgnoreList
List of test case names to ignore, can be empty.
Definition: Master.py:217
def shouldMeasurePythonCoverageGlobally(self)
Definition: Master.py:821
def __handleExtraTestCaseResults(self, testCaseInfos, resultNode, testResultNode)
This method looks for the node "ExtraTestCasesResults" to support that a test case can created additi...
Definition: Master.py:809
def setTestCaseListener(self, listener)
Definition: Master.py:364
__com
The communication device to talk to slave.
Definition: Master.py:198
def setActiveTestCase(self, testCase)
Definition: Master.py:341
def __buildTestCaseAgenda(self, testCaseNode)
Definition: Master.py:536
__logger
The logger used for output.
Definition: Master.py:214
def reset(self, testCaseCount)
Definition: Master.py:180
def __init__(self, config, logfileDir, env, isInsideTestCaseManager)
Definition: Master.py:60
def __doTest(self, testCaseInfoNode, testResultNode, funcList=None)
Do the actual test.
Definition: Master.py:653
The connection&#39;s master.
Definition: IPC.py:194
def updateTestProgress(self, finishedTestCase=None, additionalTestCaseCount=None)
Definition: Master.py:313
def warning(message, stack=False, type='', depth=0)
Put a warning to the log.
Definition: Logging.py:90
def resetTestProgress(self, testCaseCount)
Definition: Master.py:308
def __statusMessage(self, xmlNode, status)
This method sets status messages with the results of the given test.
Definition: Master.py:460
def setTestCases(self, testCaseList)
Set the list of test cases to execute.
Definition: Master.py:370
def __testTypeAndName(self, xmlNode)
Definition: Master.py:448
def __buildListOfTestFunctionsToExecute(self, testCaseInfoNode, testCaseNode, funcList=None)
Definition: Master.py:624
def logMessageWithPID(self, message)
Definition: Master.py:155
def __init__(self, cfgFile=None, verbose=False, slave=None, slaveEnvironment=None, isInsideTestCaseManager=False)
The default constructor.
Definition: Master.py:231
def reset(self)
Resets the master to the state after instantiation.
Definition: Master.py:972
def logMessageWithLastError(self, message)
Definition: Master.py:1002
__config
The configuration of the test center.
Definition: Master.py:196
def __getCommandLine(self, config, logfileDir, isInsideTestCaseManager)
Definition: Master.py:78
def setPackages(self, testedPkgList=[], availablePkgList=[], ignoredPkgList=[])
Specify the packages that should be tested and thought available.
Definition: Master.py:430
def __testConfigSkeleton(self)
Returns an empty configuration filled with the required information.
Definition: Master.py:348
def setTestSuites(self, testSuiteList)
Definition: Master.py:396
def logDebugWithPID(self, message)
Definition: Master.py:996
def logMessageWithPID(self, message)
Definition: Master.py:991
def __startSlave(self, forceSoftwareRendering=False)
Start the slave process.
Definition: Master.py:1011
__slaveProc
The slave process.
Definition: Master.py:203
def __testFailed(self, xmlNode, comError)
Handle failed tests.
Definition: Master.py:490
def __createTimeoutTooHighErrorFunction(self, testCaseInfoNode, testResultNode, timeout)
Definition: Master.py:607
def setTestCasesToIgnore(self, testCaseToIgnoreList)
Set the list of test cases to ignore.
Definition: Master.py:381
def error(message, stack=False, type='', depth=0)
Put an error to the log.
Definition: Logging.py:112
def setModules(self, moduleList=[], filterList=[])
Specify the list of modules to test.
Definition: Master.py:410
def setUpdateProgressFunc(self, updateProgressFunc)
Definition: Master.py:338
def logDebugWithPID(self, message)
Definition: Master.py:158
def __verifyConfiguration(self, testCfg)
Verify the settings made by the slave.
Definition: Master.py:567
def __sendRequest(self, command, parameterNode=None, timeout=None, tries=2, restart=False)
Sends a message to the slave and waits for the answer.
Definition: Master.py:1114
def setTestGroups(self, testGroupList)
Set the list of test groups to respect.
Definition: Master.py:388
def __init__(self, name, status, errorCount, warningCount, duration)
Definition: Master.py:166
def run(self, funcDict=None, stopSlaveAfterTestRun=True)
The main method which handles all the request from the master.
Definition: Master.py:835
The coordinator of test execution.
Definition: Master.py:193
def __executeTestFunctions(self, testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout)
Definition: Master.py:702
def transferNodeFromExecutionToResultXML(self, nodeName, executionNode, testResultNode)
Definition: Master.py:697
def __isSlaveRunning(self)
Test whether the slave process is running.
Definition: Master.py:984
def logErrorWithPID(self, message)
Definition: Master.py:999
__xmlRoot
The result tree.
Definition: Master.py:211
def __startProcess(self, cmd, _, env)
Definition: Master.py:96
list __hangingSlaves
Slave processes that could not be killed.
Definition: Master.py:206
def info(message, stack=False, type='', depth=0)
Put an info message to the log.
Definition: Logging.py:69
def logErrorWithPID(self, message)
Definition: Master.py:161
def __buildTestAgenda(self)
Build up the basic XML structure with information what tests have to be run.
Definition: Master.py:522