241 __testCasesToIgnoreList = []
244 __stackTraceFile =
None
257 def __init__(self, cfgFile=None, verbose=False, slave=None, slaveEnvironment=None, isInsideTestCaseManager=False):
258 self.
__logger = logging.getLogger(
"TestCenter")
270 self.
__lock = threading.Lock()
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")
283 if not os.path.exists(resultDir):
284 os.makedirs(resultDir)
286 Utils.rmtree(fileDir)
292 Utils.rmtree(workDir)
298 if Utils.isLinux
and "NO_MEVISLAB_DEBUGGER" not in os.environ:
311 raise Exception(
"Failed to start slave")
317 slaveProc.finish(printLog=
True)
318 if not slaveProc.kill():
319 self.
__logger.error(
"Finally killing process %s failed." % slaveProc.getPID())
356 progressTestCase =
None
357 if finishedTestCase
is not None:
360 for function
in finishedTestCase.findall(
"Function"):
361 status = int(function.get(
"status", str(Utils.TEST_STATUS_DISABLED)))
363 Utils.TEST_STATUS_ERROR,
364 Utils.TEST_STATUS_CRASHED,
365 Utils.TEST_STATUS_FUNCTION_NOT_FOUND,
366 Utils.TEST_STATUS_TIMEOUT,
369 elif status == Utils.TEST_STATUS_WARNING:
372 finishedTestCase.get(
"name"),
373 int(finishedTestCase.get(
"status", str(Utils.TEST_STATUS_CANCELLED))),
376 float(finishedTestCase.get(
"duration",
"0")),
380 if progressTestCase
is not None:
382 if additionalTestCaseCount
is not None:
396 def __testConfigSkeleton(self):
397 return etree.fromstring(
424 xmlNode = self.
__testCfg.find(
"TestCases")
425 for testCaseName
in testCaseList:
426 etree.SubElement(xmlNode,
"TestCase", name=testCaseName)
444 xmlNode = self.
__testCfg.find(
"TestGroups")
445 for testGroupName
in testGroupList:
446 etree.SubElement(xmlNode,
"TestGroup", name=testGroupName)
453 xmlNode = self.
__testCfg.find(
"TestSuites")
454 for testSuiteName
in testSuiteList:
455 etree.SubElement(xmlNode,
"TestSuite", name=testSuiteName)
467 for moduleName
in moduleList:
468 etree.SubElement(xmlNode,
"Module", name=moduleName)
469 for filter
in filterList:
470 etree.SubElement(xmlNode,
"Filter").text = filter
484 def setPackages(self, testedPkgList=[], availablePkgList=[], ignoredPkgList=[]):
489 xmlNode = self.
__testCfg.find(
"Packages/Tested")
490 for packageID
in testedPkgList:
491 etree.SubElement(xmlNode,
"Package", name=packageID)
493 xmlNode = self.
__testCfg.find(
"Packages/Available")
494 for packageID
in availablePkgList:
495 etree.SubElement(xmlNode,
"Package", name=packageID)
497 xmlNode = self.
__testCfg.find(
"Packages/Ignored")
498 for packageID
in ignoredPkgList:
499 etree.SubElement(xmlNode,
"Package", name=packageID)
504 def __sortNodes(self, nodes):
507 for testCaseNode
in nodes:
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
522 def __testTypeAndName(self, xmlNode):
524 testType = xmlNode.get(
"type")
527 if testType ==
"FunctionalTestCase"
528 else "%s::%s" % (xmlNode.get(
"name"), xmlNode.get(
"module"))
531 return testType, testName
538 def __statusMessage(self, xmlNode, status):
539 assert type(status) == int
540 assert xmlNode.tag ==
"TestCase"
542 statusStr = Utils.getStatusString(status)
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:
548 elif status == Utils.TEST_STATUS_WARNING:
566 def __testFailed(self, xmlNode, comError):
567 assert xmlNode.tag ==
"TestCase"
569 testType = xmlNode.get(
"type")
570 if testType ==
"GenericTestCase":
571 testName =
"%s::%s" % (xmlNode.get(
"name"), xmlNode.get(
"module"))
573 testName = xmlNode.get(
"name")
577 for funcNode
in xmlNode.findall(
"Function"):
578 funcDict.setdefault(funcNode.get(
"name"), funcNode)
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
589 with open(self.
__stackTraceFile, encoding=
"utf8", errors=
"surrogateescape")
as f:
590 stacktrace = f.read().splitlines()
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)]
595 lastSignalHandlerFrame = -1
596 for i
in range(len(stacktrace)):
597 if "/mlabLinuxSignalHandler.cpp:" in stacktrace[i]:
598 lastSignalHandlerFrame = i
600 if lastSignalHandlerFrame != -1:
601 stacktrace = stacktrace[lastSignalHandlerFrame + 1 :]
603 stackTraceMsg =
"MeVisLab crashed with the following stacktrace:\n" +
"\n".join(stacktrace)
607 logHandler = LogHandler(self.
__config.getSlaveLogFilePath())
608 logHandler.parseLogFile(testType, testName, status, funcDict, stackTraceMsg=stackTraceMsg)
618 def __buildTestAgenda(self):
620 if resultNode.get(
"errorCode") !=
"Ok":
622 "Error (%s) in generating the test agenda!\n%s"
623 % (resultNode.get(
"errorCode"), resultNode.find(
"Error").text)
626 self.
__xmlRoot = resultNode.find(
"TestRun")
636 def __buildTestCaseAgenda(self, testCaseNode):
637 parameterNode = etree.Element(
"Parameters")
638 genericTestCase =
True
641 for module
in self.
__xmlRoot.findall(
"Information/Modules/Module"):
642 moduleList.append(module.attrib[
"name"])
643 parameterNode.attrib[
"modules"] =
",".join(moduleList)
648 parameterNode.attrib[
"packages"] =
"*/*"
649 parameterNode.append(testCaseNode)
650 resultNode = self.
__sendRequest(
"BuildTestCaseAgenda", parameterNode, restart=
True)
651 if resultNode.get(
"errorCode") !=
"Ok":
653 "Error (%s) in generating the test case agenda!\n%s"
654 % (resultNode.get(
"errorCode"), resultNode.find(
"Error").text)
670 def __verifyConfiguration(self, testCfg):
672 packageRoot = testCfg.find(
"Packages")
674 tPkgNode = packageRoot.find(
"Tested")
675 for packageNode
in tPkgNode.findall(
"Package"):
676 if packageNode.get(
"status")
not in (
"Ok",
"Added",
"Substituted"):
678 "Failed to verify package (%s with status '%s')!"
679 % (packageNode.get(
"name"), packageNode.get(
"status"))
682 aPkgNode = packageRoot.find(
"Available")
683 for packageNode
in aPkgNode.findall(
"Package"):
684 if packageNode.get(
"status")
not in (
"Ok",
"Added",
"Substituted"):
686 "Failed to verify package (%s with status '%s')!"
687 % (packageNode.get(
"name"), packageNode.get(
"status"))
690 if packageNode.get(
"status")
not in (
"Substituted"):
691 packageRoot.append(packageNode)
693 packageRoot.remove(tPkgNode)
694 packageRoot.remove(aPkgNode)
696 iPkgNode = packageRoot.find(
"Ignored")
697 packageRoot.remove(iPkgNode)
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):
704 "Failed to verify module (%s with status '%s')" % (moduleNode.get(
"name"), moduleNode.get(
"status"))
706 testCfg.find(
"Modules").clear()
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):
713 "Failed to verify test case (%s with status '%s')"
714 % (testCaseNode.get(
"name"), testCaseNode.get(
"status"))
716 testCfg.find(
"TestCases").clear()
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 = {
731 "name": functionName,
733 "is_disabled":
"False",
736 events = etree.SubElement(timeoutTooHighTestFunction,
"Events")
737 filename = testCaseInfoNode.find(
"File").text
738 line = testCaseInfoNode.find(
"Line").text
739 event = etree.SubElement(events,
"Event")
746 "timestamp": datetime.datetime.now().strftime(
"%Y-%m-%d %H:%M:%S"),
748 error =
"The TestCase timeout (%ss) exceeds the maximum allowed timeout (%ss)" % (
750 self.
__config.getMaxTestTimeoutSeconds(),
752 etree.SubElement(event,
"Message").text = error
754 def __buildListOfTestFunctionsToExecute(self, testCaseInfoNode, testCaseNode, funcList=None):
757 for funcNode
in testCaseInfoNode.findall(
".//Function"):
758 funcName = funcNode.get(
"name")
760 if funcList
and funcName
not in funcList
and not funcName
in Utils.VIRTUAL_TEST_CASE_FUNCTIONS:
763 isDisabled = funcNode.get(
"is_disabled", str(
False)) == str(
True)
766 if isDisabled
and funcList:
769 funcNode.set(
"is_disabled", str(
False))
770 funcResultNode = etree.SubElement(testCaseNode,
"Function", name=funcName, is_disabled=str(isDisabled))
771 testFuncList.append(funcName)
782 def __doTest(self, testCaseInfoNode, testResultNode, funcList=None):
783 assert testResultNode.tag ==
"TestCase"
785 testCaseNode = deepcopy(testResultNode)
790 testCaseName = testCaseInfoNode.get(
"name")
795 hasValidTimeout =
True
796 timeout = int(testCaseInfoNode.get(
"timeout"))
798 timeout = self.
__config.getDefaultTestTimeoutSeconds()
799 if self.
__config.getMaxTestTimeoutSeconds():
800 if timeout > self.
__config.getMaxTestTimeoutSeconds():
802 hasValidTimeout =
False
805 if self.
__config.isPythonCoverageEnabled():
806 timeout = timeout * 4.0
807 if self.
__config.isBullseyeCoverageEnabled():
808 timeout = timeout * 4.0
812 testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout
816 status = Utils.TEST_STATUS_ERROR
819 testResultNode.set(
"status",
unicode(status))
820 testResultNode.set(
"retries",
unicode(retries))
829 coverageFile = executionNode.get(nodeName)
830 if executionNode.get(nodeName)
is not None:
831 testResultNode.set(nodeName, coverageFile)
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
841 status = Utils.TEST_STATUS_OK
842 maxFuncStatus = Utils.TEST_STATUS_OK
844 while testFuncList
and not timer.isExpired()
and not cancelled:
849 status = Utils.TEST_STATUS_CANCELLED
854 testCaseNode.set(
"requestProgress",
"1")
857 "maxErrorMessagesPerTestFunction", str(self.
__config.getReportOptions().maxErrorMessagesPerTestFunction)
860 "maxInfoMessagesPerTestFunction", str(self.
__config.getReportOptions().maxInfoMessagesPerTestFunction)
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)
869 comError = self.
__com.getLastError()
if self.
__com else 0
878 self.
__logger.debug(
"Failed to re-start slave.")
880 testCaseNode = executionNode.find(
"TestCase")
885 extraTestCasesResults = testCaseNode.find(
"ExtraTestCasesResults")
886 if extraTestCasesResults !=
None:
887 testResultNode.append(deepcopy(extraTestCasesResults))
890 for funcResultNode
in testCaseNode.findall(
"Function"):
891 funcName = funcResultNode.get(
"name")
894 funcStatus = funcResultNode.get(
"status")
899 funcStatus = int(funcStatus)
900 if maxFuncStatus < funcStatus:
901 maxFuncStatus = funcStatus
903 if funcName
not in failedFuncList:
904 funcResultNode.set(
"try",
"1")
906 if funcStatus
in (Utils.TEST_STATUS_CRASHED, Utils.TEST_STATUS_TIMEOUT):
907 failedFuncList.append(funcName)
910 testResultNode.append(deepcopy(funcResultNode))
911 funcResultNode.remove(funcResultNode.find(
"Events"))
913 funcResultNode.set(
"try",
"2")
914 failedFuncList.remove(funcName)
918 if funcName
not in failedFuncList:
919 testFuncList.remove(funcName)
921 testResultNode.append(funcResultNode)
922 testCaseNode.remove(funcResultNode)
923 if funcStatus == Utils.TEST_STATUS_CANCELLED
or funcStatus == Utils.TEST_STATUS_FUNCTION_NOT_FOUND:
927 if timer.isExpired():
928 status = Utils.TEST_STATUS_TIMEOUT
935 status = status
if status > Utils.TEST_STATUS_ERROR
else maxFuncStatus
936 return retries, status
942 def __handleExtraTestCaseResults(self, testCaseInfos, resultNode, testResultNode):
943 node = testResultNode.find(
"ExtraTestCasesResults")
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)
956 return self.
__config.isPythonCoverageEnabled()
and self.
__config.isGlobalPythonCoverageEnabled()
970 def run(self, funcDict=None, stopSlaveAfterTestRun=True):
972 self.
__logger.error(
"Failed to build test agenda.")
976 timeStarted = time.time()
978 testCaseInfos = self.
__xmlRoot.find(
"Information/TestCases")
982 freshCounterValue = self.
__config.getRestartInterval()
983 restartCounter = freshCounterValue
984 restartDisabled = restartCounter == 0
986 testCaseInfosTestCases = testCaseInfos.findall(
"TestCase")
987 testCaseInfosTestCases, testAgendas = self.
__sortNodes(testCaseInfosTestCases)
991 for node
in testCaseInfosTestCases:
992 testNames.append(node.get(
"name"))
998 finishedTestCases = 0
999 totalTestCases = len(testCaseInfosTestCases)
1001 resultNode = self.
__xmlRoot.find(
"Results")
1003 previousRenderer =
""
1004 for testCaseNode
in testCaseInfosTestCases:
1006 testCaseInfos.remove(testCaseNode)
1010 testCaseAgenda = testAgendas[testCaseNode]
1011 if testCaseAgenda !=
None:
1013 testCaseInfoNodes = testCaseAgenda.findall(
"TestCase")
1014 assert len(testCaseInfoNodes) == 1
1016 testCaseInfoNode = testCaseInfoNodes[0]
1018 testCaseInfos.remove(testCaseNode)
1019 testCaseInfos.append(testCaseInfoNode)
1021 preferredRendererNode = testCaseInfoNode.find(
"preferredRenderer")
1022 preferredRenderer = (
1023 "" if preferredRendererNode
is None else preferredRendererNode.text.lower()
1025 if preferredRenderer ==
"hardware" and Utils.isMesaForced():
1027 'preferredRenderer settings "hardware" detected but use of software driver is enforced.'
1029 restartNeeded = previousRenderer != preferredRenderer
and (preferredRenderer ==
"software" or previousRenderer ==
"software")
1030 previousRenderer = preferredRenderer
1032 testResultNodes = testCaseAgenda.find(
"Results").findall(
"TestCase")
1033 if len(testResultNodes) > 1:
1035 totalTestCases += len(testResultNodes) - 1
1036 for testResultNode
in testResultNodes:
1040 testCaseName = testCaseInfoNode.get(
"name")
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.")
1051 restartNeeded =
False
1052 restartCounter = freshCounterValue
1061 testCaseName, failedTestCases, finishedTestCases, totalTestCases
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:
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,
1077 failedTestCases += 1
1078 finishedTestCases += 1
1080 testResultNode.set(
"duration", str(timeFunctionStopped - timeFunctionStarted))
1082 testResultNode.set(
"slave_id",
"0" if self.
__slave else str(self.
__slaveProc.getID()))
1084 resultNode.append(testResultNode)
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))
1095 "Failed to build the test agenda for test case %s." % testCaseNode.get(
"name",
"unknown")
1097 except Exception
as e:
1098 self.
__logger.error(
"Error executing test case %s" % e)
1101 resultNode.set(
"combinePythonCoverage",
"True")
1104 timeStopped = time.time()
1106 self.
__xmlRoot.set(
"timestamp", str(timeStarted))
1107 self.
__xmlRoot.set(
"duration", str(timeStopped - timeStarted))
1109 if stopSlaveAfterTestRun:
1139 def __isSlaveRunning(self):
1142 def __isSlaveStopped(self):
1150 return "%s (PID: %s)" % (message, pidString)
1159 errorCode, errorMessage = self.
__com.getLastErrorWithMessage()
1160 return "%s ([%d] %s)" % (message, errorCode, errorMessage)
1167 def __startSlave(self, forceSoftwareRendering=False):
1173 self.
__logger.error(
"Failed to stop the slave before restart.")
1178 env = Utils.setTestCenterPort(env, self.
__com.getPort())
1180 if slaveProc.hasStarted():
1189 def __connectToSlave(self):
1193 while retries < maxTries
and not self.
__com.connect(timeout=self.
__config.getIPCConnectionTimeout()):
1203 self.
__logger.debug(
"Sleeping for %s seconds" % (timerSleep))
1204 time.sleep(timerSleep)
1206 if not self.
__com.isConnected():
1208 self.
logErrorWithPID(
"Failed to connect to slave within %d seconds." % (maxTries * timerSleep))
1210 self.
__logger.error(
"Failed to stop the slave after connection error.")
1221 return resultNode.get(
"errorCode") ==
"Ok"
1243 if xmlNode.get(
"errorCode") ==
"Ok":
1275 def __sendRequest(self, command, parameterNode=None, timeout=None, tries=2, restart=False):
1278 requestNode = etree.Element(
"Command", type=command)
1279 requestNode.append(parameterNode
if parameterNode !=
None else etree.Element(
"Dummy"))
1283 resultNode = self.
__slave.runCommand(requestNode)
1289 timeout = self.
__config.getDefaultTestTimeoutSeconds()
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()):
1297 while not string
and not timer.isExpired():
1298 string = self.
__com.recv(timer.getSecondsRemaining())
1300 recvNode = Utils.filterXMLString(string)
1302 if recvNode.tag ==
"Progress":
1307 recvNode.get(
"testType"),
1308 recvNode.get(
"testCase"),
1309 recvNode.get(
"testFunction"),
1314 self.
__com.send(
"0" if shouldStop
else "1", 5)
1316 elif recvNode.tag ==
"Status":
1321 resultNode = recvNode
1325 "It seems the test client is crashing. Collecting the stacktrace may take some while."
1328 timer.extendTimeout(300)
1337 if restart
and (trial < tries):
1338 self.
__logger.info(
"Restarting due to communication error ...")
1341 if resultNode ==
None:
1343 resultNode = etree.Element(
"Result", errorCode=
"CommunicationError")
1344 etree.SubElement(resultNode,
"Error").text = (
1345 "Communication error [%d]: %s" % self.
__com.getLastErrorWithMessage()
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 ""))