TestCenter Reference
Master.py
Go to the documentation of this file.
2# Copyright 2007, MeVis Medical Solutions AG
3#
4# The user may use this file in accordance with the license agreement provided with
5# the Software or, alternatively, in accordance with the terms contained in a
6# written agreement between the user and MeVis Medical Solutions AG.
7#
8# For further information use the contact form at https://www.mevislab.de/contact
9#
10
13
14# -- system imports ----------------------------------------------------------------------------{{{-
15import copy
16import datetime
17import os
18import re
19import sys
20import time
21import logging
22import traceback
23import subprocess
24import tempfile
25import threading
26
27from copy import deepcopy
28
29import xml.etree.cElementTree as etree
30
31try:
32 import TestSupport
33except:
34 pass
35
36# ----------------------------------------------------------------------------------------------}}}-
37
38# -- local imports -----------------------------------------------------------------------------{{{-
39from . import Config
40from . import IPC
41from . import Utils
42
43from .LogHandler import LogHandler
44
45if sys.version_info.major >= 3:
46 unicode = str
47
48# ----------------------------------------------------------------------------------------------}}}-
49
50
52 def __init__(self, seconds):
53 self.start = time.monotonic()
54 self.availableSeconds = seconds
55
56 def extendTimeout(self, seconds):
57 self.availableSeconds += seconds
58
60 passedSeconds = time.monotonic() - self.start
61 return self.availableSeconds - passedSeconds
62
63 def isExpired(self):
64 return self.getSecondsRemaining() <= 0.0
65
66
67# ----------------------------------------------------------------------------------------------}}}-
68
69
71 nextSlaveId = 0
72
73 def __init__(self, config, logfileDir, env, isInsideTestCaseManager):
74 self.__id = SlaveProcess.nextSlaveId
75 SlaveProcess.nextSlaveId += 1
76 self.__wasKilled = False
77 self.__hasStarted = False
78 self.__logFileFD = None
79 self.__logFile = None
80 self.__slaveLogFile = None
81 self.__logger = logging.getLogger("TestCenter")
82 self.__workDir = os.path.join(config.getResultDir(), "work")
83
84 cmd = self.__getCommandLine(config, logfileDir, isInsideTestCaseManager)
85 if cmd:
86 self.__startProcess(cmd, config, env)
87
88 def hasStarted(self):
89 return self.__hasStarted
90
91 def __getCommandLine(self, config, logfileDir, isInsideTestCaseManager):
92 try:
93 cmd = config.getMLABTestCenterCommand(macroName="TestCenter")
94 if cmd:
95 self.__slaveLogFile = os.path.join(logfileDir, f"slaveLogfile_{self.getID()}.log")
96 cmd.insert(1, "-logfile")
97 cmd.insert(2, self.__slaveLogFile)
98 if isInsideTestCaseManager:
99 cmd.append("--started-from-test-case-manager")
101 cmd = None
102
103 if not cmd:
104 self.__logger.error(
105 "Failed to start slave as no MeVisLab executable was found. "
106 + "Please verify your configuration (see TestCase Manager's "
107 + "Configuration tab or check the file %s)!" % config.getConfigFilePath()
108 )
109 return cmd
110
111 def __startProcess(self, cmd, _, env):
112 self.__logFileFD, self.__logFile = tempfile.mkstemp()
113 self.__logger.debug("Starting slave with command line %s" % " ".join(cmd))
114 try:
115 preexec_fn = os.setpgrp if Utils.isUnix else None
116 self.__process = subprocess.Popen(
117 cmd,
118 cwd=self.__workDir,
119 preexec_fn=preexec_fn,
120 env=env,
121 stdin=subprocess.PIPE,
122 stdout=self.__logFileFD,
123 stderr=self.__logFileFD,
124 )
125 self.__hasStarted = True
126 except OSError:
127 self.__logger.error("Failed to start slave (%s)\n%s" % (cmd, traceback.format_exc()))
128
129 def getID(self):
130 return self.__id
131
132 def getPID(self):
133 return self.__process.pid
134
135 def isRunning(self):
136 return self.getExitCode() is None
137
138 def getExitCode(self):
139 return self.__process.poll()
140
141 def kill(self):
142 self.__wasKilled = Utils.kill(self.__process)
143 return self.__wasKilled
144
145 def wasKilled(self):
146 return self.__wasKilled
147
148 def __printLogsAfterError(self):
149 try:
150 output = ""
151 if self.__slaveLogFile is not None and os.path.exists(self.__slaveLogFile):
152 with open(self.__slaveLogFile, "r", errors="replace") as fh:
153 output = fh.read()
154 if os.path.exists(self.__logFile):
155 with open(self.__logFile, "r", errors="replace") as fh:
156 output += fh.read()
157 if not output:
158 output = "none"
159 self.logErrorWithPID("Slave output: %s" % output)
160 except IOError as e:
161 self.logErrorWithPID(f"Could not read log file: {e}")
162
163 def finish(self, printLog):
164 try:
165 os.fsync(self.__logFileFD)
166 os.close(self.__logFileFD)
167 except IOError as e:
168 self.logErrorWithPID("Could not close log file: %s" % str(e))
169 if printLog:
171 if os.path.exists(self.__logFile):
172 try:
173 Utils.removeFile(self.__logFile)
174 except IOError as e:
175 self.logErrorWithPID("Could not remove log file: %s" % str(e))
176 else:
177 self.logMessageWithPID("Log file does not exist: %s" % self.__logFile)
178
179 def logMessageWithPID(self, message):
180 return "%s (PID: %s)" % (message, str(self.getPID()))
181
182 def logDebugWithPID(self, message):
183 self.__logger.debug(self.logMessageWithPID(message))
184
185 def logErrorWithPID(self, message):
186 self.__logger.error(self.logMessageWithPID(message))
187
188
190 def __init__(self, name, status, errorCount, warningCount, duration):
191 self.duration = duration
192 self.name = name
193 self.status = status
194 self.errorCount = errorCount
195 self.warningCount = warningCount
196
197
199 def __init__(self):
200 self.activeTestCase = None
203
204 def reset(self, testCaseCount):
205 self.activeTestCase = None
206 self.finishedTestCases = []
207 self.testCaseCount = testCaseCount
208
209
210# -- Class Master ------------------------------------------------------------------------------{{{-
211
217class Master:
218 # -- member variables ------------------------------------------------------------------------{{{-
219
220 __config = None
221
222 __com = None
223
224 # Slave instance to use if offline testing is used.
225 __slave = None
226
227 __slaveProc = None
228
229
230 __hangingSlaves = []
231
232 # The configuration of the current test run.
233 __testCfg = None
234
235 __xmlRoot = None
236
237
238 __logger = None
239
240
241 __testCasesToIgnoreList = []
242
243
244 __stackTraceFile = None
245
246 # --------------------------------------------------------------------------------------------}}}-
247
248 # -- def __init__ ----------------------------------------------------------------------------{{{-
249
257 def __init__(self, cfgFile=None, verbose=False, slave=None, slaveEnvironment=None, isInsideTestCaseManager=False):
258 self.__logger = logging.getLogger("TestCenter")
259
260 self.__config = Config.Configuration(cfgFile)
261
262 self.__com = (
263 None if slave is not None else IPC.ComMaster(connectTimeout=self.__config.getIPCConnectionTimeout())
264 )
265 self.__slave = slave
266 self.__slaveEnvironment = slaveEnvironment
267
269 self.__shouldStop = False
270 self.__lock = threading.Lock()
271
272 self.__testCaseListener = None
273
274 self.__testedPkgList = []
275
276 # Clean up existing result directory or create it.
277 resultDir = self.__config.getResultDir()
278 imgDir = os.path.join(resultDir, "images")
279 fileDir = os.path.join(resultDir, "files")
280 workDir = os.path.join(resultDir, "work")
281 self.logfileDir = os.path.join(resultDir, "logfiles")
282
283 if not os.path.exists(resultDir):
284 os.makedirs(resultDir)
285 # Clean up old file directory.
286 Utils.rmtree(fileDir)
287 os.makedirs(fileDir)
288 # Clean up old image directory.
289 Utils.rmtree(imgDir)
290 os.makedirs(imgDir)
291 # Clean up old work directory.
292 Utils.rmtree(workDir)
293 os.makedirs(workDir)
294 # Clean up old logfile directory.
295 Utils.rmtree(self.logfileDir)
296 os.makedirs(self.logfileDir)
297
298 if Utils.isLinux and "NO_MEVISLAB_DEBUGGER" not in os.environ:
299 self.__stackTraceFile = os.path.join(self.logfileDir, "lastCrashStackTrace.txt")
300 self.__slaveEnvironment = Utils.setStackTraceFile(self.__slaveEnvironment, self.__stackTraceFile)
301
302 self.__isInsideTestCaseManager = isInsideTestCaseManager
303
304 self.__testCfg = self.__testConfigSkeleton()
305
306 self.__updateProgressFunc = None
307
308 # If not running offline start a second MeVisLab instance.
309 if self.__slave is None:
310 if not self.__startSlave():
311 raise Exception("Failed to start slave")
312
313 # --------------------------------------------------------------------------------------------}}}-
314
315 def onExit(self):
316 for slaveProc in self.__hangingSlaves:
317 slaveProc.finish(printLog=True)
318 if not slaveProc.kill():
319 self.__logger.error("Finally killing process %s failed." % slaveProc.getPID())
320 self.__hangingSlaves.clear()
321
322 def __enter__(self):
323 return self
324
325 def __exit__(self, *_):
326 self.onExit()
327
328 def __del__(self):
329 self.onExit()
330
331 # --------------------------------------------------------------------------------------------}}}-
332
333 def stop(self):
334 self.__lock.acquire()
335 self.__shouldStop = True
336 self.__lock.release()
337
338 def shouldStop(self):
339 self.__lock.acquire()
340 result = self.__shouldStop
341 self.__lock.release()
342 return result
343
345 self.__lock.acquire()
346 result = copy.deepcopy(self.__testProgress)
347 self.__lock.release()
348 return result
349
350 def resetTestProgress(self, testCaseCount):
351 self.__lock.acquire()
352 self.__testProgress.reset(testCaseCount)
353 self.__lock.release()
354
355 def updateTestProgress(self, finishedTestCase=None, additionalTestCaseCount=None):
356 progressTestCase = None
357 if finishedTestCase is not None:
358 errorCount = 0
359 warningCount = 0
360 for function in finishedTestCase.findall("Function"):
361 status = int(function.get("status", str(Utils.TEST_STATUS_DISABLED)))
362 if status in (
363 Utils.TEST_STATUS_ERROR,
364 Utils.TEST_STATUS_CRASHED,
365 Utils.TEST_STATUS_FUNCTION_NOT_FOUND,
366 Utils.TEST_STATUS_TIMEOUT,
367 ):
368 errorCount += 1
369 elif status == Utils.TEST_STATUS_WARNING:
370 warningCount += 1
371 progressTestCase = ProgressTestCase(
372 finishedTestCase.get("name"),
373 int(finishedTestCase.get("status", str(Utils.TEST_STATUS_CANCELLED))),
374 errorCount,
375 warningCount,
376 float(finishedTestCase.get("duration", "0")),
377 )
378
379 self.__lock.acquire()
380 if progressTestCase is not None:
381 self.__testProgress.finishedTestCases.append(progressTestCase)
382 if additionalTestCaseCount is not None:
383 self.__testProgress.testCaseCount += additionalTestCaseCount
384 self.__lock.release()
385
386 def setUpdateProgressFunc(self, updateProgressFunc):
387 self.__updateProgressFunc = updateProgressFunc
388
389 def setActiveTestCase(self, testCase):
390 self.__lock.acquire()
391 self.__testProgress.activeTestCase = testCase
392 self.__lock.release()
393
394 # -- def __testConfigSkeleton() --------------------------------------------------------------{{{-
395
396 def __testConfigSkeleton(self):
397 return etree.fromstring(
398 """
399 <Configuration>
400 <TestGroups />
401 <TestCases />
402 <TestSuites />
403 <Modules />
404 <Packages>
405 <Tested />
406 <Available />
407 <Ignored />
408 </Packages>
409 <TestOptions />
410 </Configuration>"""
411 )
412
413 # --------------------------------------------------------------------------------------------}}}-
414
415 def setTestCaseListener(self, listener):
416 self.__testCaseListener = listener
417
418 # -- def setTestCases ------------------------------------------------------------------------{{{-
419
421 def setTestCases(self, testCaseList):
422 assert self.__xmlRoot == None
423
424 xmlNode = self.__testCfg.find("TestCases")
425 for testCaseName in testCaseList:
426 etree.SubElement(xmlNode, "TestCase", name=testCaseName)
427
428 # --------------------------------------------------------------------------------------------}}}-
429
430 # -- def setIgnoreTestCases ------------------------------------------------------------------------{{{-
431
433 def setTestCasesToIgnore(self, testCaseToIgnoreList):
434 self.__testCasesToIgnoreList__testCasesToIgnoreList = testCaseToIgnoreList
435
436 # --------------------------------------------------------------------------------------------}}}-
437
438 # -- def setTestGroups -----------------------------------------------------------------------{{{-
439
441 def setTestGroups(self, testGroupList):
442 assert self.__xmlRoot == None
443
444 xmlNode = self.__testCfg.find("TestGroups")
445 for testGroupName in testGroupList:
446 etree.SubElement(xmlNode, "TestGroup", name=testGroupName)
447
448 # --------------------------------------------------------------------------------------------}}}-
449
450 def setTestSuites(self, testSuiteList):
451 assert self.__xmlRoot == None
452
453 xmlNode = self.__testCfg.find("TestSuites")
454 for testSuiteName in testSuiteList:
455 etree.SubElement(xmlNode, "TestSuite", name=testSuiteName)
456
457 # -- def setModules --------------------------------------------------------------------------{{{-
458
463 def setModules(self, moduleList=[], filterList=[]):
464 assert self.__xmlRoot == None
465
466 xmlNode = self.__testCfg.find("Modules")
467 for moduleName in moduleList:
468 etree.SubElement(xmlNode, "Module", name=moduleName)
469 for filter in filterList:
470 etree.SubElement(xmlNode, "Filter").text = filter
471
472 # --------------------------------------------------------------------------------------------}}}-
473
474 # -- def setPackages -------------------------------------------------------------------------{{{-
475
484 def setPackages(self, testedPkgList=[], availablePkgList=[], ignoredPkgList=[]):
485 assert self.__xmlRoot == None
486
487 self.__testedPkgList = testedPkgList
488
489 xmlNode = self.__testCfg.find("Packages/Tested")
490 for packageID in testedPkgList:
491 etree.SubElement(xmlNode, "Package", name=packageID)
492
493 xmlNode = self.__testCfg.find("Packages/Available")
494 for packageID in availablePkgList:
495 etree.SubElement(xmlNode, "Package", name=packageID)
496
497 xmlNode = self.__testCfg.find("Packages/Ignored")
498 for packageID in ignoredPkgList:
499 etree.SubElement(xmlNode, "Package", name=packageID)
500
501 # --------------------------------------------------------------------------------------------}}}-
502
503
504 def __sortNodes(self, nodes):
505 nodeList = []
506 testAgendas = {}
507 for testCaseNode in nodes:
508 testCaseAgenda = self.__buildTestCaseAgenda(testCaseNode)
509 testAgendas[testCaseNode] = testCaseAgenda
510 for prefRendererSetting in ["", "hardware", "software"]:
511 for testCaseNode in nodes:
512 testCaseAgenda = testAgendas[testCaseNode]
513 testCaseInfoNodes = testCaseAgenda.findall("TestCase")
514 testCaseInfoNode = testCaseInfoNodes[0]
515 preferredRendererNode = testCaseInfoNode.find("preferredRenderer")
516 if preferredRendererNode is None and prefRendererSetting == "" or preferredRendererNode is not None and preferredRendererNode.text.lower() == prefRendererSetting:
517 nodeList.append(testCaseNode)
518 assert len(nodes) == len(nodeList)
519 return nodeList, testAgendas
520
521
522 def __testTypeAndName(self, xmlNode):
523 # Just print anything if we are in verbose mode.
524 testType = xmlNode.get("type")
525 testName = (
526 xmlNode.get("name")
527 if testType == "FunctionalTestCase"
528 else "%s::%s" % (xmlNode.get("name"), xmlNode.get("module"))
529 )
530
531 return testType, testName
532
533 # -- def __statusMessage ---------------------------------------------------------------------{{{-
534
538 def __statusMessage(self, xmlNode, status):
539 assert type(status) == int
540 assert xmlNode.tag == "TestCase"
541
542 statusStr = Utils.getStatusString(status)
543 testType, testName = self.__testTypeAndName(xmlNode)
544 fillIn = "." * (80 - len(testType + testName + statusStr) - 2)
545 msg = "%s::%s%s%s" % (testType, testName, fillIn, statusStr)
546 if status == Utils.TEST_STATUS_OK or status == Utils.TEST_STATUS_DO_NOT_TEST_MODULE:
547 self.__logger.debug(msg)
548 elif status == Utils.TEST_STATUS_WARNING:
549 self.__logger.warning(msg)
550 else:
551 self.__logger.error(msg)
552
553 # --------------------------------------------------------------------------------------------}}}-
554
555 # -- def __testFailed ------------------------------------------------------------------------{{{-
556
566 def __testFailed(self, xmlNode, comError):
567 assert xmlNode.tag == "TestCase"
568
569 testType = xmlNode.get("type")
570 if testType == "GenericTestCase":
571 testName = "%s::%s" % (xmlNode.get("name"), xmlNode.get("module"))
572 else:
573 testName = xmlNode.get("name")
574
575 # Build a mapping from function names to result nodes.
576 funcDict = {}
577 for funcNode in xmlNode.findall("Function"):
578 funcDict.setdefault(funcNode.get("name"), funcNode)
579
580 # With the previously gathered error status the reason can be determined.
581 status = Utils.TEST_STATUS_ERROR
582 if comError in (IPC.Error.EMPTY_MESSAGE, IPC.Error.ON_RECEIVING_2):
583 status = Utils.TEST_STATUS_CRASHED
584 elif comError == IPC.Error.ON_RECEIVING_TIMEOUT:
585 status = Utils.TEST_STATUS_TIMEOUT
586
587 stackTraceMsg = None
588 if self.__stackTraceFile and os.path.exists(self.__stackTraceFile):
589 with open(self.__stackTraceFile, encoding="utf8", errors="surrogateescape") as f:
590 stacktrace = f.read().splitlines()
591 # filter out information lines about encountered threads (which there are a lot of):
592 new_lwp_re = re.compile(r"\[New LWP \d+\]", flags=re.IGNORECASE)
593 stacktrace = [x for x in stacktrace if not new_lwp_re.fullmatch(x)]
594 # remove the stack frames from the signal handler itself:
595 lastSignalHandlerFrame = -1
596 for i in range(len(stacktrace)):
597 if "/mlabLinuxSignalHandler.cpp:" in stacktrace[i]:
598 lastSignalHandlerFrame = i
599 # the last one counts, therefore no break here
600 if lastSignalHandlerFrame != -1:
601 stacktrace = stacktrace[lastSignalHandlerFrame + 1 :]
602 if stacktrace:
603 stackTraceMsg = "MeVisLab crashed with the following stacktrace:\n" + "\n".join(stacktrace)
604 os.unlink(self.__stackTraceFile)
605
606 # Read the slave's logfile and parse it to get as much results as possible.
607 logHandler = LogHandler(self.__config.getSlaveLogFilePath())
608 logHandler.parseLogFile(testType, testName, status, funcDict, stackTraceMsg=stackTraceMsg)
609
610 return status
611
612 # --------------------------------------------------------------------------------------------}}}-
613
614 # -- def __buildTestAgenda -------------------------------------------------------------------{{{-
615
618 def __buildTestAgenda(self):
619 resultNode = self.__sendRequest("BuildTestAgenda", self.__testCfg)
620 if resultNode.get("errorCode") != "Ok":
621 self.__logger.critical(
622 "Error (%s) in generating the test agenda!\n%s"
623 % (resultNode.get("errorCode"), resultNode.find("Error").text)
624 )
625 return False
626 self.__xmlRoot = resultNode.find("TestRun")
627 self.__testCfg = self.__verifyConfiguration(resultNode.find("Configuration"))
628
629 # Set package list as testing is started afterwards.
630 self.__sendRequest("SetPackageList", self.__testCfg.find("Packages"), 5, restart=True)
631 return True
632
633 # --------------------------------------------------------------------------------------------}}}-
634
635 # -- def __buildTestCaseAgenda -------------------------------------------------------------------{{{-
636 def __buildTestCaseAgenda(self, testCaseNode):
637 parameterNode = etree.Element("Parameters")
638 genericTestCase = True # is it possible to determine the testcase type here?
639 if genericTestCase:
640 moduleList = []
641 for module in self.__xmlRoot.findall("Information/Modules/Module"):
642 moduleList.append(module.attrib["name"])
643 parameterNode.attrib["modules"] = ",".join(moduleList)
644 if len(self.__testedPkgList) > 0:
645 parameterNode.attrib["packages"] = ",".join(self.__testedPkgList)
646 else:
647 # test every package
648 parameterNode.attrib["packages"] = "*/*"
649 parameterNode.append(testCaseNode)
650 resultNode = self.__sendRequest("BuildTestCaseAgenda", parameterNode, restart=True)
651 if resultNode.get("errorCode") != "Ok":
652 self.__logger.critical(
653 "Error (%s) in generating the test case agenda!\n%s"
654 % (resultNode.get("errorCode"), resultNode.find("Error").text)
655 )
656 return None
657
658 return resultNode
659
660 # --------------------------------------------------------------------------------------------}}}-
661
662 # -- def __verifyConfiguration ---------------------------------------------------------------{{{-
663
670 def __verifyConfiguration(self, testCfg):
671 # Verify package settings.
672 packageRoot = testCfg.find("Packages")
673
674 tPkgNode = packageRoot.find("Tested")
675 for packageNode in tPkgNode.findall("Package"):
676 if packageNode.get("status") not in ("Ok", "Added", "Substituted"):
677 self.__logger.error(
678 "Failed to verify package (%s with status '%s')!"
679 % (packageNode.get("name"), packageNode.get("status"))
680 )
681
682 aPkgNode = packageRoot.find("Available")
683 for packageNode in aPkgNode.findall("Package"):
684 if packageNode.get("status") not in ("Ok", "Added", "Substituted"):
685 self.__logger.error(
686 "Failed to verify package (%s with status '%s')!"
687 % (packageNode.get("name"), packageNode.get("status"))
688 )
689 else:
690 if packageNode.get("status") not in ("Substituted"):
691 packageRoot.append(packageNode)
692
693 packageRoot.remove(tPkgNode)
694 packageRoot.remove(aPkgNode)
695
696 iPkgNode = packageRoot.find("Ignored")
697 packageRoot.remove(iPkgNode)
698
699 # Verify the module settings.
700 for moduleNode in testCfg.findall("Modules/Module"):
701 isModuleFiltered = moduleNode.get("isModuleFiltered") == str(True)
702 if not isModuleFiltered and moduleNode.get("status") != str(Utils.TEST_STATUS_OK):
703 self.__logger.error(
704 "Failed to verify module (%s with status '%s')" % (moduleNode.get("name"), moduleNode.get("status"))
705 )
706 testCfg.find("Modules").clear()
707
708 # Verify the test case settings.
709 for testCaseNode in testCfg.findall("TestCases/TestCase"):
710 isNotInTestGroups = testCaseNode.get("isNotInTestGroups") == str(True)
711 if not isNotInTestGroups and testCaseNode.get("status") != str(Utils.TEST_STATUS_OK):
712 self.__logger.error(
713 "Failed to verify test case (%s with status '%s')"
714 % (testCaseNode.get("name"), testCaseNode.get("status"))
715 )
716 testCfg.find("TestCases").clear()
717
718 return testCfg
719
720 # --------------------------------------------------------------------------------------------}}}-
721
722 def __createTimeoutTooHighErrorFunction(self, testCaseInfoNode, testResultNode, timeout):
723 functionName = "TEST001_VerifyTestCaseTimeout"
724 testFunctions = testCaseInfoNode.find("TestFunctions")
725 testFunction = etree.SubElement(testFunctions, "Function")
726 testFunction.attrib = {"basename": "VerifyTestCaseTimeout", "is_disabled": "False", "name": functionName}
727 etree.SubElement(testFunction, "Documentation").text = "Verifies if the TestCase timeout."
728 timeoutTooHighTestFunction = etree.SubElement(testResultNode, "Function")
729 timeoutTooHighTestFunction.attrib = {
730 "duration": "0",
731 "name": functionName,
732 "status": "2",
733 "is_disabled": "False",
734 "try": "1",
735 }
736 events = etree.SubElement(timeoutTooHighTestFunction, "Events")
737 filename = testCaseInfoNode.find("File").text
738 line = testCaseInfoNode.find("Line").text
739 event = etree.SubElement(events, "Event")
740 event.attrib = {
741 "file": filename,
742 "internal": "true",
743 "line": line,
744 "origin": "",
745 "type": "Error",
746 "timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
747 }
748 error = "The TestCase timeout (%ss) exceeds the maximum allowed timeout (%ss)" % (
749 timeout,
750 self.__config.getMaxTestTimeoutSeconds(),
751 )
752 etree.SubElement(event, "Message").text = error
753
754 def __buildListOfTestFunctionsToExecute(self, testCaseInfoNode, testCaseNode, funcList=None):
755 # Build up list of test functions that must be executed.
756 testFuncList = []
757 for funcNode in testCaseInfoNode.findall(".//Function"):
758 funcName = funcNode.get("name")
759 # If user selected to process only specific functions ignore the rest.
760 if funcList and funcName not in funcList and not funcName in Utils.VIRTUAL_TEST_CASE_FUNCTIONS:
761 continue
762
763 isDisabled = funcNode.get("is_disabled", str(False)) == str(True)
764 if testCaseInfoNode.get("name") in self.__testCasesToIgnoreList__testCasesToIgnoreList:
765 isDisabled = True
766 if isDisabled and funcList:
767 # If a user selected the function, then it should be executed
768 isDisabled = False
769 funcNode.set("is_disabled", str(False))
770 funcResultNode = etree.SubElement(testCaseNode, "Function", name=funcName, is_disabled=str(isDisabled))
771 testFuncList.append(funcName)
772 return testFuncList
773
774 # -- def __doTest ----------------------------------------------------------------------------{{{-
775
781
782 def __doTest(self, testCaseInfoNode, testResultNode, funcList=None):
783 assert testResultNode.tag == "TestCase"
784
785 testCaseNode = deepcopy(testResultNode)
786
787 # Build up list of test functions that must be executed.
788 testFuncList = self.__buildListOfTestFunctionsToExecute(testCaseInfoNode, testCaseNode, funcList)
789
790 testCaseName = testCaseInfoNode.get("name")
791 if self.__testCaseListener:
792 self.__testCaseListener.startTestCase(testCaseName, testFuncList)
793
794 # Gather time till timeout.
795 hasValidTimeout = True
796 timeout = int(testCaseInfoNode.get("timeout"))
797 if timeout == 0:
798 timeout = self.__config.getDefaultTestTimeoutSeconds()
799 if self.__config.getMaxTestTimeoutSeconds():
800 if timeout > self.__config.getMaxTestTimeoutSeconds():
801 self.__createTimeoutTooHighErrorFunction(testCaseInfoNode, testResultNode, timeout)
802 hasValidTimeout = False
803
804 # Use a bigger timeout if coverage or bullseye are enabled
805 if self.__config.isPythonCoverageEnabled():
806 timeout = timeout * 4.0
807 if self.__config.isBullseyeCoverageEnabled():
808 timeout = timeout * 4.0
809
810 if hasValidTimeout:
811 retries, status = self.__executeTestFunctions(
812 testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout
813 )
814 else:
815 retries = 0
816 status = Utils.TEST_STATUS_ERROR
817
818 # Set status and number of retries for the given test case.
819 testResultNode.set("status", unicode(status))
820 testResultNode.set("retries", unicode(retries))
821 # Print status message.
822 self.__statusMessage(testResultNode, status)
823 if self.__testCaseListener:
824 self.__testCaseListener.endTestCase()
825
826 # --------------------------------------------------------------------------------------------}}}-
827
828 def transferNodeFromExecutionToResultXML(self, nodeName, executionNode, testResultNode):
829 coverageFile = executionNode.get(nodeName)
830 if executionNode.get(nodeName) is not None:
831 testResultNode.set(nodeName, coverageFile)
832
833 def __executeTestFunctions(self, testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout):
834 if testCaseNode.get("status") == str(Utils.TEST_STATUS_DO_NOT_TEST_MODULE):
835 return 0, Utils.TEST_STATUS_DO_NOT_TEST_MODULE
836
837 retries = 0
838 failedFuncList = []
839 timer = TestCaseTimer(timeout)
840
841 status = Utils.TEST_STATUS_OK
842 maxFuncStatus = Utils.TEST_STATUS_OK
843 cancelled = False
844 while testFuncList and not timer.isExpired() and not cancelled:
845 # If not set status to timeout and stop execution of remaining test functions.
846
847 if self.shouldStop():
848 cancelled = True
849 status = Utils.TEST_STATUS_CANCELLED
850 break
851
852 # if we have a test case listener, we request progress calls from the slave:
853 if self.__testCaseListener:
854 testCaseNode.set("requestProgress", "1")
855
856 testCaseNode.set(
857 "maxErrorMessagesPerTestFunction", str(self.__config.getReportOptions().maxErrorMessagesPerTestFunction)
858 )
859 testCaseNode.set(
860 "maxInfoMessagesPerTestFunction", str(self.__config.getReportOptions().maxInfoMessagesPerTestFunction)
861 )
862
863 # Initiate the processing of the remaining test functions.
864 executionNode = self.__sendRequest("DoTest", testCaseNode, timeout=timer, tries=1)
865 if executionNode.get("errorCode") != "Ok":
866 self.__logger.debug("Restarting due to error in test execution.\n%s" % executionNode.find("Error").text)
867
868 # Try to determine what was the reason for the test to fail.
869 comError = self.__com.getLastError() if self.__com else 0
870
871 # Delete slave process.
872 self.stopSlave()
873
874 # Handle logfile to get gathered results.
875 status = self.__testFailed(testCaseNode, comError)
876 # Restart slave.
877 if not self.__startSlave():
878 self.__logger.debug("Failed to re-start slave.")
879 else:
880 testCaseNode = executionNode.find("TestCase")
881
882 self.transferNodeFromExecutionToResultXML("pythonCoverage", executionNode, testResultNode)
883 self.transferNodeFromExecutionToResultXML("bullseyeCoverage", executionNode, testResultNode)
884
885 extraTestCasesResults = testCaseNode.find("ExtraTestCasesResults")
886 if extraTestCasesResults != None:
887 testResultNode.append(deepcopy(extraTestCasesResults))
888
889 # functions to test.
890 for funcResultNode in testCaseNode.findall("Function"):
891 funcName = funcResultNode.get("name")
892
893 # If status is not set, the function has not been executed yet.
894 funcStatus = funcResultNode.get("status")
895 if not funcStatus:
896 # Function not processed yet.
897 continue
898 else:
899 funcStatus = int(funcStatus)
900 if maxFuncStatus < funcStatus:
901 maxFuncStatus = funcStatus
902
903 if funcName not in failedFuncList:
904 funcResultNode.set("try", "1")
905
906 if funcStatus in (Utils.TEST_STATUS_CRASHED, Utils.TEST_STATUS_TIMEOUT):
907 failedFuncList.append(funcName)
908 retries += 1
909 # Create copy of node as it will be overwritten in the next iteration.
910 testResultNode.append(deepcopy(funcResultNode))
911 funcResultNode.remove(funcResultNode.find("Events"))
912 else:
913 funcResultNode.set("try", "2")
914 failedFuncList.remove(funcName)
915
916 # Remove all processed but not failed for the first time functions from
917 # the temporary node.
918 if funcName not in failedFuncList:
919 testFuncList.remove(funcName)
920 # Move the processed function to the result node.
921 testResultNode.append(funcResultNode)
922 testCaseNode.remove(funcResultNode)
923 if funcStatus == Utils.TEST_STATUS_CANCELLED or funcStatus == Utils.TEST_STATUS_FUNCTION_NOT_FOUND:
924 cancelled = True
925 break
926
927 if timer.isExpired():
928 status = Utils.TEST_STATUS_TIMEOUT
929
930 # The status variable is used for status > 2 to handle situations where:
931 # - all test functions worked but the slave crashed or timed out.
932 # - the test case timed out but had crashes, too (global status should be
933 # timeout).
934 # Otherwise the maximum function status found in the reports is used.
935 status = status if status > Utils.TEST_STATUS_ERROR else maxFuncStatus
936 return retries, status
937
938 # -- def __handleExtraTestCaseResults --------------------------------------------------------{{{-
939
942 def __handleExtraTestCaseResults(self, testCaseInfos, resultNode, testResultNode):
943 node = testResultNode.find("ExtraTestCasesResults")
944 if node != None:
945 testResultNode.remove(node)
946 for node in node.findall("ExtraTestCaseResult"):
947 testCaseInfos.append(node.find("Information/TestCase"))
948 extraTestCaseResult = node.find("Result/TestCase")
949 extraTestCaseResult.set("slave_id", testResultNode.get("slave_id"))
950 resultNode.append(extraTestCaseResult)
951
952 # --------------------------------------------------------------------------------------------}}}-
953
954 # -- def shouldMeasurePythonCoverageGlobally -------------------------------------------------{{{-
956 return self.__config.isPythonCoverageEnabled() and self.__config.isGlobalPythonCoverageEnabled()
957
958 # --------------------------------------------------------------------------------------------}}}-
959
960 # -- def run ---------------------------------------------------------------------------------{{{-
961
970 def run(self, funcDict=None, stopSlaveAfterTestRun=True):
971 if not self.__buildTestAgenda():
972 self.__logger.error("Failed to build test agenda.")
973 return None
974
975 # Start the timer.
976 timeStarted = time.time()
977
978 testCaseInfos = self.__xmlRoot.find("Information/TestCases")
979
980 # Fetch counters which control after what number of testCases MLAB
981 # should be restarted.
982 freshCounterValue = self.__config.getRestartInterval()
983 restartCounter = freshCounterValue
984 restartDisabled = restartCounter == 0
985
986 testCaseInfosTestCases = testCaseInfos.findall("TestCase")
987 testCaseInfosTestCases, testAgendas = self.__sortNodes(testCaseInfosTestCases)
988
989 if self.__testCaseListener:
990 testNames = []
991 for node in testCaseInfosTestCases:
992 testNames.append(node.get("name"))
993 self.__testCaseListener.startTestRun(testNames)
994
995 self.resetTestProgress(len(testCaseInfosTestCases))
996
997 failedTestCases = 0
998 finishedTestCases = 0
999 totalTestCases = len(testCaseInfosTestCases)
1000
1001 resultNode = self.__xmlRoot.find("Results")
1002 cancelled = False
1003 previousRenderer = ""
1004 for testCaseNode in testCaseInfosTestCases:
1005 if cancelled:
1006 testCaseInfos.remove(testCaseNode)
1007 self.updateTestProgress(finishedTestCase=testCaseNode)
1008 continue
1009 try:
1010 testCaseAgenda = testAgendas[testCaseNode]
1011 if testCaseAgenda != None:
1012
1013 testCaseInfoNodes = testCaseAgenda.findall("TestCase")
1014 assert len(testCaseInfoNodes) == 1
1015
1016 testCaseInfoNode = testCaseInfoNodes[0]
1017
1018 testCaseInfos.remove(testCaseNode)
1019 testCaseInfos.append(testCaseInfoNode)
1020
1021 preferredRendererNode = testCaseInfoNode.find("preferredRenderer")
1022 preferredRenderer = (
1023 "" if preferredRendererNode is None else preferredRendererNode.text.lower()
1024 )
1025 if preferredRenderer == "hardware" and Utils.isMesaForced():
1026 self.__logger.warning(
1027 'preferredRenderer settings "hardware" detected but use of software driver is enforced.'
1028 )
1029 restartNeeded = previousRenderer != preferredRenderer and (preferredRenderer == "software" or previousRenderer == "software")
1030 previousRenderer = preferredRenderer
1031
1032 testResultNodes = testCaseAgenda.find("Results").findall("TestCase")
1033 if len(testResultNodes) > 1:
1034 self.updateTestProgress(additionalTestCaseCount=len(testResultNodes) - 1)
1035 totalTestCases += len(testResultNodes) - 1
1036 for testResultNode in testResultNodes:
1037 if cancelled:
1038 break
1039 try:
1040 testCaseName = testCaseInfoNode.get("name")
1041 debugType, debugName = self.__testTypeAndName(testResultNode)
1042
1043 if not restartDisabled or restartNeeded:
1044 shouldRestart = restartCounter == 0
1045 if shouldRestart or restartNeeded:
1046 self.__logger.info("Restart slave for test case %s::%s" % (debugType, debugName))
1047 if not self.__startSlave(forceSoftwareRendering=(preferredRenderer == "software")):
1048 resultNode.append(testResultNode)
1049 raise Exception("Failed to start slave.")
1050
1051 restartNeeded = False
1052 restartCounter = freshCounterValue
1053
1054 # decrement counter on each pass
1055 restartCounter -= 1
1056
1057 self.setActiveTestCase(testCaseName)
1058
1059 if self.__updateProgressFunc is not None:
1061 testCaseName, failedTestCases, finishedTestCases, totalTestCases
1062 )
1063
1064 timeFunctionStarted = time.time()
1065 funcList = funcDict[testCaseName] if (funcDict and testCaseName in funcDict) else None
1066 self.__doTest(testCaseInfoNode, testResultNode, funcList)
1067 timeFunctionStopped = time.time()
1068 if int(testResultNode.get("status")) == Utils.TEST_STATUS_CANCELLED:
1069 cancelled = True
1070
1071 if int(testResultNode.get("status")) in (
1072 Utils.TEST_STATUS_CRASHED,
1073 Utils.TEST_STATUS_ERROR,
1074 Utils.TEST_STATUS_FUNCTION_NOT_FOUND,
1075 Utils.TEST_STATUS_TIMEOUT,
1076 ):
1077 failedTestCases += 1
1078 finishedTestCases += 1
1079
1080 testResultNode.set("duration", str(timeFunctionStopped - timeFunctionStarted))
1081
1082 testResultNode.set("slave_id", "0" if self.__slave else str(self.__slaveProc.getID()))
1083
1084 resultNode.append(testResultNode)
1085
1086 self.__handleExtraTestCaseResults(testCaseInfos, resultNode, testResultNode)
1087
1088 except Exception as e:
1089 traceback.print_exc()
1090 self.__logger.error("Error executing test case %s::%s\n%s" % (debugType, debugName, e))
1091 testResultNode.set("status", unicode(Utils.TEST_STATUS_ERROR))
1092 self.updateTestProgress(finishedTestCase=testResultNode)
1093 else:
1094 self.__logger.error(
1095 "Failed to build the test agenda for test case %s." % testCaseNode.get("name", "unknown")
1096 )
1097 except Exception as e:
1098 self.__logger.error("Error executing test case %s" % e)
1099
1101 resultNode.set("combinePythonCoverage", "True")
1102
1103 # Stop the timer.
1104 timeStopped = time.time()
1105
1106 self.__xmlRoot.set("timestamp", str(timeStarted))
1107 self.__xmlRoot.set("duration", str(timeStopped - timeStarted))
1108
1109 if stopSlaveAfterTestRun:
1110 self.stopSlave()
1111
1112 xml = Utils.saveXMLTree(self.__xmlRoot, self.__config.getResultFile())
1113
1114 if self.__testCaseListener:
1115 self.__testCaseListener.endTestRun()
1116
1117 return xml
1118
1119 # --------------------------------------------------------------------------------------------}}}-
1120
1121 # -- def reset() -----------------------------------------------------------------------------{{{-
1122
1125 def reset(self):
1126 self.__xmlRoot = None
1127 self.__testCfg = self.__testConfigSkeleton()
1128
1129 # --------------------------------------------------------------------------------------------}}}-
1130
1131 # -- def getConfig() -------------------------------------------------------------------------{{{-
1132 def getConfig(self):
1133 return self.__config
1134
1135 # --------------------------------------------------------------------------------------------}}}-
1136
1137 # -- def __isSlaveRunning -------------------------------------------------------------------------{{{-
1138
1139 def __isSlaveRunning(self):
1140 return self.__slaveProc and self.__slaveProc.isRunning()
1141
1142 def __isSlaveStopped(self):
1143 return not self.__isSlaveRunning()
1144
1145 # --------------------------------------------------------------------------------------------}}}-
1146
1147 def logMessageWithPID(self, message):
1148 pidString = str(self.__slaveProc.getPID()) if self.__slaveProc is not None else str(None)
1149
1150 return "%s (PID: %s)" % (message, pidString)
1151
1152 def logDebugWithPID(self, message):
1153 self.__logger.debug(self.logMessageWithPID(message))
1154
1155 def logErrorWithPID(self, message):
1156 self.__logger.error(self.logMessageWithPID(message))
1157
1158 def logMessageWithLastError(self, message):
1159 errorCode, errorMessage = self.__com.getLastErrorWithMessage()
1160 return "%s ([%d] %s)" % (message, errorCode, errorMessage)
1161
1162 # -- def __startSlave ------------------------------------------------------------------------{{{-
1163
1167 def __startSlave(self, forceSoftwareRendering=False):
1168 if self.__slave:
1169 return True
1170
1171 if self.__isSlaveRunning():
1172 if not self.stopSlave():
1173 self.__logger.error("Failed to stop the slave before restart.")
1174
1175 env = Utils.setForceMesa(self.__slaveEnvironment) if forceSoftwareRendering else self.__slaveEnvironment
1176 if self.__com:
1177 # server port is given through environment variable
1178 env = Utils.setTestCenterPort(env, self.__com.getPort())
1179 slaveProc = SlaveProcess(self.__config, self.logfileDir, env, self.__isInsideTestCaseManager)
1180 if slaveProc.hasStarted():
1181 self.__slaveProc = slaveProc
1182 else:
1183 return False
1184
1185 return self.__connectToSlave()
1186
1187 # --------------------------------------------------------------------------------------------}}}-
1188
1189 def __connectToSlave(self):
1190 retries = 0
1191 timerSleep = 1
1192 maxTries = 15
1193 while retries < maxTries and not self.__com.connect(timeout=self.__config.getIPCConnectionTimeout()):
1194 retries += 1
1195 self.logDebugWithPID(self.logMessageWithLastError("Failed to connect to slave in %i. try" % retries))
1196
1197 if not self.__slaveProc.isRunning():
1198 self.__logger.error("Slave died with exit code %s" % self.__slaveProc.getExitCode())
1199 self.__slaveProc.finish(printLog=True)
1200 return False
1201
1202 # Use a sleep timer to give the slave process more time to start up.
1203 self.__logger.debug("Sleeping for %s seconds" % (timerSleep))
1204 time.sleep(timerSleep)
1205
1206 if not self.__com.isConnected():
1207 # quit the mlab that we were not able to talk to
1208 self.logErrorWithPID("Failed to connect to slave within %d seconds." % (maxTries * timerSleep))
1209 if not self.stopSlave():
1210 self.__logger.error("Failed to stop the slave after connection error.")
1211 self.__slaveProc.finish(printLog=True)
1212 return False
1213 else:
1214 self.logDebugWithPID("Connected to slave in %s. try" % (retries + 1))
1215
1216 # Send slave a list of packages that are available. On first run an empty
1217 # string is sent; in the __buildTestAgenda call this list will be
1218 # retrieved. Later the verified list of tests will be sent.
1219 if self.__xmlRoot:
1220 resultNode = self.__sendRequest("SetPackageList", self.__testCfg.find("Packages"), 5)
1221 return resultNode.get("errorCode") == "Ok"
1222
1223 return True
1224
1225 # -- def stopSlave ---------------------------------------------------------------------------{{{-
1226 # # Stop the slave process.
1227 # If the slave is running this method tries to stop the slave. This method
1228 # will run until the slave has stopped. After 3 tries on unix a KILL signal
1229 # is sent.
1230 # @return True if the slave was stopped.
1231 def stopSlave(self):
1232 if self.__slave:
1233 # ugly special condition for when a running MeVisLab instance is used
1234 # as slave.
1235 return True
1236
1237 self.logDebugWithPID("Shutting down slave")
1238
1239 if not self.__isSlaveStopped():
1240 # try soft shutdown first by requesting clean quit
1241 xmlNode = self.__sendRequest("Quit", timeout=1, tries=1)
1242
1243 if xmlNode.get("errorCode") == "Ok":
1244 self.logDebugWithPID("Response received, waiting for shutdown")
1245 # wait up to five seconds for the slave to stop:
1246 i = 0
1247 while not self.__isSlaveStopped() and i < 10:
1248 time.sleep(0.5)
1249 i += 1
1250
1251 # kindly asking for shutdown was obviously not enough, enforce shutdown
1252 if not self.__isSlaveStopped():
1253 self.logDebugWithPID("No response, killing slave")
1254 self.__slaveProc.kill()
1255
1256 if self.__isSlaveStopped():
1257 self.__slaveProc.finish(printLog=self.__slaveProc.wasKilled())
1258 else:
1259 self.logErrorWithPID("Failed to kill slave!")
1260 self.__hangingSlaves.append(self.__slaveProc)
1261 self.__slaveProc = None
1262
1263 return self.__isSlaveStopped()
1264
1265 # --------------------------------------------------------------------------------------------}}}-
1266
1267 # -- def __sendRequest -----------------------------------------------------------------------{{{-
1268
1275 def __sendRequest(self, command, parameterNode=None, timeout=None, tries=2, restart=False):
1276 trial = 0
1277
1278 requestNode = etree.Element("Command", type=command)
1279 requestNode.append(parameterNode if parameterNode != None else etree.Element("Dummy"))
1280
1281 if self.__slave:
1282 self.logDebugWithPID("Handling '%s' request" % command)
1283 resultNode = self.__slave.runCommand(requestNode)
1284 else:
1285 self.logDebugWithPID("Sending '%s' request to slave" % command)
1286 resultNode = None
1287 # The default timeout is taken from the configuration file.
1288 if not timeout:
1289 timeout = self.__config.getDefaultTestTimeoutSeconds()
1290
1291 string = None
1292 while not string and (trial < tries):
1293 timer = timeout if isinstance(timeout, TestCaseTimer) else TestCaseTimer(timeout)
1294 if self.__com.send(etree.tostring(requestNode), timer.getSecondsRemaining()):
1295 # since we allow receiving intermediate "Progress" result values,
1296 # we loop until we receive something that is not a "Progress" message:
1297 while not string and not timer.isExpired():
1298 string = self.__com.recv(timer.getSecondsRemaining())
1299 if string:
1300 recvNode = Utils.filterXMLString(string)
1301 self.logDebugWithPID("Received node of type '%s'" % recvNode.tag)
1302 if recvNode.tag == "Progress":
1303 shouldStop = False
1304 if self.__testCaseListener:
1305 try:
1306 self.__testCaseListener.startTestCaseFunction(
1307 recvNode.get("testType"),
1308 recvNode.get("testCase"),
1309 recvNode.get("testFunction"),
1310 )
1312 shouldStop = True
1313 # tell slave if it should stop or go on:
1314 self.__com.send("0" if shouldStop else "1", 5)
1315 string = None
1316 elif recvNode.tag == "Status":
1317 if self.__testCaseListener:
1318 self.__testCaseListener.endTestCaseFunction(int(recvNode.get("status")), 0)
1319 string = None
1320 else:
1321 resultNode = recvNode
1322 else:
1323 if self.__stackTraceFile and os.path.exists(self.__stackTraceFile):
1324 self.__logger.warning(
1325 "It seems the test client is crashing. Collecting the stacktrace may take some while."
1326 )
1327 # Add additional grace period to timer
1328 timer.extendTimeout(300)
1329 # wait for slave process to stop
1330 while not self.__isSlaveStopped() and not timer.isExpired():
1331 time.sleep(0.5)
1332 break
1333
1334 if not string:
1335 trial += 1
1336
1337 if restart and (trial < tries):
1338 self.__logger.info("Restarting due to communication error ...")
1339 self.__startSlave()
1340
1341 if resultNode == None:
1342 self.logDebugWithPID("Error: Received no result from slave.")
1343 resultNode = etree.Element("Result", errorCode="CommunicationError")
1344 etree.SubElement(resultNode, "Error").text = (
1345 "Communication error [%d]: %s" % self.__com.getLastErrorWithMessage()
1346 )
1347 self.logDebugWithPID("Result error code: %s" % resultNode.get("errorCode"))
1348 content = etree.tostring(resultNode).decode()
1349 self.logDebugWithPID("Result content: %s%s" % (content[:150], " [...]" if len(content) > 150 else ""))
1350 return resultNode
1351
1352 # --------------------------------------------------------------------------------------------}}}-
1353
1354
1355# ----------------------------------------------------------------------------------------------}}}-
The connection's master.
Definition IPC.py:246
The coordinator of test execution.
Definition Master.py:217
transferNodeFromExecutionToResultXML(self, nodeName, executionNode, testResultNode)
Definition Master.py:828
resetTestProgress(self, testCaseCount)
Definition Master.py:350
__sendRequest(self, command, parameterNode=None, timeout=None, tries=2, restart=False)
Definition Master.py:1275
__doTest(self, testCaseInfoNode, testResultNode, funcList=None)
Definition Master.py:782
__statusMessage(self, xmlNode, status)
Definition Master.py:538
__init__(self, cfgFile=None, verbose=False, slave=None, slaveEnvironment=None, isInsideTestCaseManager=False)
The default constructor.
Definition Master.py:257
__verifyConfiguration(self, testCfg)
Definition Master.py:670
setTestSuites(self, testSuiteList)
Definition Master.py:450
setTestCases(self, testCaseList)
Set the list of test cases to execute.
Definition Master.py:421
logMessageWithPID(self, message)
Definition Master.py:1147
__testTypeAndName(self, xmlNode)
Definition Master.py:522
__executeTestFunctions(self, testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout)
Definition Master.py:833
run(self, funcDict=None, stopSlaveAfterTestRun=True)
The main method which handles all the request from the master.
Definition Master.py:970
setPackages(self, testedPkgList=[], availablePkgList=[], ignoredPkgList=[])
Specify the packages that should be tested and thought available.
Definition Master.py:484
reset(self)
Resets the master to the state after instantiation.
Definition Master.py:1125
logMessageWithLastError(self, message)
Definition Master.py:1158
__createTimeoutTooHighErrorFunction(self, testCaseInfoNode, testResultNode, timeout)
Definition Master.py:722
__buildListOfTestFunctionsToExecute(self, testCaseInfoNode, testCaseNode, funcList=None)
Definition Master.py:754
updateTestProgress(self, finishedTestCase=None, additionalTestCaseCount=None)
Definition Master.py:355
setActiveTestCase(self, testCase)
Definition Master.py:389
setTestGroups(self, testGroupList)
Set the list of test groups to respect.
Definition Master.py:441
setTestCaseListener(self, listener)
Definition Master.py:415
__startSlave(self, forceSoftwareRendering=False)
Definition Master.py:1167
setModules(self, moduleList=[], filterList=[])
Specify the list of modules to test.
Definition Master.py:463
setTestCasesToIgnore(self, testCaseToIgnoreList)
Set the list of test cases to ignore.
Definition Master.py:433
__testFailed(self, xmlNode, comError)
Definition Master.py:566
__buildTestCaseAgenda(self, testCaseNode)
Definition Master.py:636
__handleExtraTestCaseResults(self, testCaseInfos, resultNode, testResultNode)
Definition Master.py:942
setUpdateProgressFunc(self, updateProgressFunc)
Definition Master.py:386
__init__(self, name, status, errorCount, warningCount, duration)
Definition Master.py:190
__getCommandLine(self, config, logfileDir, isInsideTestCaseManager)
Definition Master.py:91
__startProcess(self, cmd, _, env)
Definition Master.py:111
__init__(self, config, logfileDir, env, isInsideTestCaseManager)
Definition Master.py:73