231 __testCasesToIgnoreList = []
234 __stackTraceFile =
None
247 def __init__ (self, cfgFile=None, verbose=False, slave=None, slaveEnvironment=None,
248 isInsideTestCaseManager=False):
249 self.
__logger = logging.getLogger(
"TestCenter")
259 self.
__lock = threading.Lock()
266 resultDir = self.
__config.getResultDir()
267 imgDir = os.path.join(resultDir,
"images")
268 fileDir = os.path.join(resultDir,
"files")
269 workDir = os.path.join(resultDir,
"work")
272 if not os.path.exists(resultDir):
273 os.makedirs(resultDir)
275 Utils.rmtree(fileDir)
281 Utils.rmtree(workDir)
287 if Utils.isLinux
and "NO_MEVISLAB_DEBUGGER" not in os.environ:
301 raise Exception(
"Failed to start slave")
307 slaveProc.finish(printLog=
True)
308 if not slaveProc.kill():
309 self.
__logger.error(
"Finally killing process %s failed." % slaveProc.getPID())
346 progressTestCase =
None
347 if finishedTestCase
is not None:
350 for function
in finishedTestCase.findall(
"Function"):
351 status = int(function.get(
"status", str(Utils.TEST_STATUS_DISABLED)))
352 if status
in (Utils.TEST_STATUS_ERROR, Utils.TEST_STATUS_CRASHED,
353 Utils.TEST_STATUS_FUNCTION_NOT_FOUND, Utils.TEST_STATUS_TIMEOUT):
355 elif status == Utils.TEST_STATUS_WARNING:
358 int(finishedTestCase.get(
"status", str(Utils.TEST_STATUS_CANCELLED))),
361 float(finishedTestCase.get(
"duration",
"0")))
364 if progressTestCase
is not None:
366 if additionalTestCaseCount
is not None:
380 def __testConfigSkeleton(self):
381 return etree.fromstring(
"""
405 xmlNode = self.
__testCfg.find(
"TestCases")
406 for testCaseName
in testCaseList:
407 etree.SubElement(xmlNode,
"TestCase", name=testCaseName)
423 xmlNode = self.
__testCfg.find(
"TestGroups")
424 for testGroupName
in testGroupList:
425 etree.SubElement(xmlNode,
"TestGroup", name=testGroupName)
431 xmlNode = self.
__testCfg.find(
"TestSuites")
432 for testSuiteName
in testSuiteList:
433 etree.SubElement(xmlNode,
"TestSuite", name=testSuiteName)
446 for moduleName
in moduleList:
447 etree.SubElement(xmlNode,
"Module", name=moduleName)
448 for filter
in filterList:
449 etree.SubElement(xmlNode,
"Filter").text = filter
462 def setPackages (self, testedPkgList=[], availablePkgList=[], ignoredPkgList=[]):
467 xmlNode = self.
__testCfg.find(
"Packages/Tested")
468 for packageID
in testedPkgList:
469 etree.SubElement(xmlNode,
"Package", name=packageID)
471 xmlNode = self.
__testCfg.find(
"Packages/Available")
472 for packageID
in availablePkgList:
473 etree.SubElement(xmlNode,
"Package", name=packageID)
475 xmlNode = self.
__testCfg.find(
"Packages/Ignored")
476 for packageID
in ignoredPkgList:
477 etree.SubElement(xmlNode,
"Package", name=packageID)
480 def __testTypeAndName(self, xmlNode):
482 testType = xmlNode.get(
'type')
483 testName = xmlNode.get(
'name')
if testType ==
'FunctionalTestCase' else "%s::%s" % (xmlNode.get(
'name'), xmlNode.get(
'module'))
485 return testType, testName
492 def __statusMessage (self, xmlNode, status):
493 assert type(status) == int
494 assert xmlNode.tag ==
'TestCase'
496 statusStr = Utils.getStatusString(status)
498 fillIn =
"."*(80-len(testType+testName+statusStr)-2)
499 msg =
"%s::%s%s%s" % (testType, testName, fillIn, statusStr)
500 if status == Utils.TEST_STATUS_OK
or status == Utils.TEST_STATUS_DO_NOT_TEST_MODULE:
502 elif status == Utils.TEST_STATUS_WARNING:
520 def __testFailed (self, xmlNode, comError):
521 assert xmlNode.tag ==
"TestCase"
523 testType = xmlNode.get(
'type')
524 if testType ==
u"GenericTestCase":
525 testName =
"%s::%s" % (xmlNode.get(
'name'), xmlNode.get(
'module'))
527 testName = xmlNode.get(
'name')
531 for funcNode
in xmlNode.findall(
'Function'):
532 funcDict.setdefault(funcNode.get(
'name'), funcNode)
535 status = Utils.TEST_STATUS_ERROR
536 if comError
in (IPC.Error.EMPTY_MESSAGE, IPC.Error.ON_RECEIVING_2):
537 status = Utils.TEST_STATUS_CRASHED
538 elif comError == IPC.Error.ON_RECEIVING_TIMEOUT:
539 status = Utils.TEST_STATUS_TIMEOUT
543 with open(self.
__stackTraceFile, encoding =
"utf8", errors =
"surrogateescape")
as f:
544 stacktrace = f.read().splitlines()
546 new_lwp_re = re.compile(
r"\[New LWP \d+\]", flags=re.IGNORECASE)
547 stacktrace =[x
for x
in stacktrace
if not new_lwp_re.fullmatch(x)]
549 lastSignalHandlerFrame = -1
550 for i
in range(len(stacktrace)):
551 if "/mlabLinuxSignalHandler.cpp:" in stacktrace[i]:
552 lastSignalHandlerFrame = i
554 if lastSignalHandlerFrame != -1:
555 stacktrace = stacktrace[lastSignalHandlerFrame+1:]
557 stackTraceMsg = (
"MeVisLab crashed with the following stacktrace:\n" +
558 "\n".join(stacktrace))
562 logHandler = LogHandler(self.
__config.getSlaveLogFilePath())
563 logHandler.parseLogFile(testType, testName, status, funcDict, stackTraceMsg=stackTraceMsg)
572 def __buildTestAgenda (self):
574 if resultNode.get(
'errorCode') !=
"Ok":
575 self.
__logger.critical(
"Error (%s) in generating the test agenda!\n%s" % (resultNode.get(
'errorCode'), resultNode.find(
'Error').text))
577 self.
__xmlRoot = resultNode.find(
'TestRun')
586 def __buildTestCaseAgenda (self, testCaseNode):
587 parameterNode = etree.Element(
'Parameters')
588 genericTestCase =
True
591 for module
in self.
__xmlRoot.findall(
'Information/Modules/Module'):
592 moduleList.append(module.attrib[
'name'])
593 parameterNode.attrib[
'modules'] =
','.join(moduleList)
598 parameterNode.attrib[
'packages'] =
'*/*'
599 parameterNode.append(testCaseNode)
600 resultNode = self.
__sendRequest(
"BuildTestCaseAgenda", parameterNode, restart=
True)
601 if resultNode.get(
'errorCode') !=
"Ok":
602 self.
__logger.critical(
"Error (%s) in generating the test case agenda!\n%s" % (resultNode.get(
'errorCode'), resultNode.find(
'Error').text))
617 def __verifyConfiguration (self, testCfg):
619 packageRoot = testCfg.find(
'Packages')
621 tPkgNode = packageRoot.find(
'Tested')
622 for packageNode
in tPkgNode.findall(
'Package'):
623 if packageNode.get(
'status')
not in (
'Ok',
'Added',
'Substituted'):
624 self.
__logger.error(
"Failed to verify package (%s with status '%s')!" % (packageNode.get(
'name'), packageNode.get(
'status')))
626 aPkgNode = packageRoot.find(
'Available')
627 for packageNode
in aPkgNode.findall(
'Package'):
628 if packageNode.get(
'status')
not in (
'Ok',
'Added',
'Substituted'):
629 self.
__logger.error(
"Failed to verify package (%s with status '%s')!" % (packageNode.get(
'name'), packageNode.get(
'status')))
631 if packageNode.get(
'status')
not in (
'Substituted'):
632 packageRoot.append(packageNode)
634 packageRoot.remove(tPkgNode)
635 packageRoot.remove(aPkgNode)
637 iPkgNode = packageRoot.find(
'Ignored')
638 packageRoot.remove(iPkgNode)
641 for moduleNode
in testCfg.findall(
'Modules/Module'):
642 isModuleFiltered = moduleNode.get(
'isModuleFiltered') == str(
True)
643 if not isModuleFiltered
and moduleNode.get(
'status') != str(Utils.TEST_STATUS_OK):
644 self.
__logger.error(
"Failed to verify module (%s with status '%s')" % (moduleNode.get(
'name'), moduleNode.get(
'status')))
645 testCfg.find(
'Modules').clear()
648 for testCaseNode
in testCfg.findall(
'TestCases/TestCase'):
649 isNotInTestGroups = testCaseNode.get(
'isNotInTestGroups') == str(
True)
650 if not isNotInTestGroups
and testCaseNode.get(
'status') != str(Utils.TEST_STATUS_OK):
651 self.
__logger.error(
"Failed to verify test case (%s with status '%s')" % (testCaseNode.get(
'name'), testCaseNode.get(
'status')))
652 testCfg.find(
'TestCases').clear()
657 def __createTimeoutTooHighErrorFunction(self, testCaseInfoNode, testResultNode, timeout):
658 functionName =
'TEST001_VerifyTestCaseTimeout'
659 testFunctions = testCaseInfoNode.find(
'TestFunctions')
660 testFunction = etree.SubElement(testFunctions,
"Function")
661 testFunction.attrib = {
'basename':
'VerifyTestCaseTimeout',
'is_disabled':
'False',
'name': functionName}
662 etree.SubElement(testFunction,
"Documentation").text =
'Verifies if the TestCase timeout.'
663 timeoutTooHighTestFunction = etree.SubElement(testResultNode,
"Function")
664 timeoutTooHighTestFunction.attrib = {
'duration':
'0',
'name': functionName,
'status':
'2',
'is_disabled':
'False',
'try':
'1'}
665 events = etree.SubElement(timeoutTooHighTestFunction,
'Events')
666 filename = testCaseInfoNode.find(
'File').text
667 line = testCaseInfoNode.find(
'Line').text
668 event = etree.SubElement(events,
'Event')
669 event.attrib = {
'file': filename,
'internal':
'true',
'line': line,
'origin':
'',
'type':
'Error',
670 'timestamp': datetime.datetime.now().strftime(
'%Y-%m-%d %H:%M:%S')}
671 error =
'The TestCase timeout (%ss) exceeds the maximum allowed timeout (%ss)' % (timeout, self.
__config.getMaxTestTimeoutSeconds())
672 etree.SubElement(event,
'Message').text = error
674 def __buildListOfTestFunctionsToExecute (self, testCaseInfoNode, testCaseNode, funcList=None):
677 for funcNode
in testCaseInfoNode.findall(
".//Function"):
678 funcName = funcNode.get(
'name')
680 if funcList
and funcName
not in funcList
and not funcName
in Utils.VIRTUAL_TEST_CASE_FUNCTIONS:
683 isDisabled = funcNode.get(
"is_disabled", str(
False)) == str(
True)
686 if isDisabled
and funcList:
689 funcNode.set(
'is_disabled', str(
False))
690 funcResultNode = etree.SubElement(testCaseNode,
'Function', name=funcName, is_disabled=str(isDisabled))
691 testFuncList.append(funcName)
703 def __doTest (self, testCaseInfoNode, testResultNode, funcList=None):
704 assert testResultNode.tag ==
'TestCase'
706 testCaseNode = deepcopy(testResultNode)
711 testCaseName = testCaseInfoNode.get(
'name')
716 hasValidTimeout =
True
717 timeout = int(testCaseInfoNode.get(
'timeout'))
719 timeout = self.
__config.getDefaultTestTimeoutSeconds()
720 if self.
__config.getMaxTestTimeoutSeconds():
721 if timeout > self.
__config.getMaxTestTimeoutSeconds():
723 hasValidTimeout =
False
726 if self.
__config.isPythonCoverageEnabled():
727 timeout = timeout * 4.
728 if self.
__config.isBullseyeCoverageEnabled():
729 timeout = timeout * 4.
732 retries, status = self.
__executeTestFunctions(testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout)
735 status = Utils.TEST_STATUS_ERROR
738 testResultNode.set(
"status",
unicode(status))
739 testResultNode.set(
"retries",
unicode(retries))
748 coverageFile = executionNode.get(nodeName)
749 if executionNode.get(nodeName)
is not None:
750 testResultNode.set(nodeName, coverageFile)
752 def __executeTestFunctions (self, testCaseInfoNode, testResultNode, testCaseNode, testFuncList, timeout):
753 if testCaseNode.get(
'status') == str(Utils.TEST_STATUS_DO_NOT_TEST_MODULE):
754 return 0, Utils.TEST_STATUS_DO_NOT_TEST_MODULE
760 status = Utils.TEST_STATUS_OK
761 maxFuncStatus = Utils.TEST_STATUS_OK
763 while testFuncList
and not timer.isExpired()
and not cancelled:
768 status = Utils.TEST_STATUS_CANCELLED
773 testCaseNode.set(
"requestProgress",
"1")
775 testCaseNode.set(
"maxErrorMessagesPerTestFunction", str(self.
__config.getReportOptions().maxErrorMessagesPerTestFunction))
776 testCaseNode.set(
"maxInfoMessagesPerTestFunction", str(self.
__config.getReportOptions().maxInfoMessagesPerTestFunction))
779 executionNode = self.
__sendRequest(
"DoTest", testCaseNode, timeout=timer, tries=1)
780 if executionNode.get(
'errorCode') !=
"Ok":
781 self.
__logger.debug(
'Restarting due to error in test execution.\n%s' % executionNode.find(
'Error').text)
784 comError = self.
__com.getLastError()
if self.
__com else 0
793 self.
__logger.debug(
'Failed to re-start slave.')
795 testCaseNode = executionNode.find(
'TestCase')
800 extraTestCasesResults = testCaseNode.find(
'ExtraTestCasesResults')
801 if extraTestCasesResults !=
None:
802 testResultNode.append(deepcopy(extraTestCasesResults))
805 for funcResultNode
in testCaseNode.findall(
'Function'):
806 funcName = funcResultNode.get(
"name")
809 funcStatus = funcResultNode.get(
"status")
814 funcStatus = int(funcStatus)
815 if maxFuncStatus < funcStatus:
816 maxFuncStatus = funcStatus
818 if funcName
not in failedFuncList:
819 funcResultNode.set(
'try',
'1')
821 if funcStatus
in (Utils.TEST_STATUS_CRASHED , Utils.TEST_STATUS_TIMEOUT):
822 failedFuncList.append(funcName)
825 testResultNode.append(deepcopy(funcResultNode))
826 funcResultNode.remove(funcResultNode.find(
"Events"))
828 funcResultNode.set(
'try',
'2')
829 failedFuncList.remove(funcName)
833 if funcName
not in failedFuncList:
834 testFuncList.remove(funcName)
836 testResultNode.append(funcResultNode)
837 testCaseNode.remove(funcResultNode)
838 if funcStatus == Utils.TEST_STATUS_CANCELLED
or funcStatus == Utils.TEST_STATUS_FUNCTION_NOT_FOUND:
842 if timer.isExpired():
843 status = Utils.TEST_STATUS_TIMEOUT
851 status = status
if status > Utils.TEST_STATUS_ERROR
else maxFuncStatus
852 return retries, status
859 def __handleExtraTestCaseResults(self, testCaseInfos, resultNode, testResultNode):
860 node = testResultNode.find(
"ExtraTestCasesResults")
862 testResultNode.remove(node)
863 for node
in node.findall(
"ExtraTestCaseResult"):
864 testCaseInfos.append(node.find(
"Information/TestCase"))
865 extraTestCaseResult = node.find(
"Result/TestCase")
866 extraTestCaseResult.set(
"slave_id", testResultNode.get(
"slave_id"))
867 resultNode.append(extraTestCaseResult)
872 return self.
__config.isPythonCoverageEnabled()
and self.
__config.isGlobalPythonCoverageEnabled()
885 def run(self, funcDict=None, stopSlaveAfterTestRun=True):
887 self.
__logger.error(
"Failed to build test agenda.")
891 timeStarted = time.time()
893 testCaseInfos = self.
__xmlRoot.find(
'Information/TestCases')
897 freshCounterValue = self.
__config.getRestartInterval()
898 restartCounter = freshCounterValue
899 restartDisabled = (restartCounter == 0)
901 testCaseInfosTestCases = testCaseInfos.findall(
'TestCase')
904 for node
in testCaseInfosTestCases:
905 testNames.append(node.get(
'name'))
911 finishedTestCases = 0
912 totalTestCases = len(testCaseInfosTestCases)
914 resultNode = self.
__xmlRoot.find(
'Results')
916 for testCaseNode
in testCaseInfosTestCases:
918 testCaseInfos.remove(testCaseNode)
923 if testCaseAgenda !=
None:
925 testCaseInfoNodes = testCaseAgenda.findall(
'TestCase')
926 assert(len(testCaseInfoNodes) == 1)
927 testCaseInfoNode = testCaseInfoNodes[0]
929 testCaseInfos.remove(testCaseNode)
930 testCaseInfos.append(testCaseInfoNode)
932 testResultNodes = testCaseAgenda.find(
'Results').findall(
'TestCase')
933 if len(testResultNodes) > 1:
935 totalTestCases += len(testResultNodes)-1
936 for testResultNode
in testResultNodes:
940 testCaseName = testCaseInfoNode.get(
'name')
943 preferredRendererNode = testCaseInfoNode.find(
'preferredRenderer')
944 preferredRenderer =
"" if preferredRendererNode
is None else preferredRendererNode.text.lower()
945 forceSoftwareRendering = (preferredRenderer ==
"software")
946 if preferredRenderer ==
"hardware" and Utils.isMesaForced():
947 self.
__logger.warning(
'preferredRenderer settings "hardware" detected but use of software driver is enforced.')
949 if not restartDisabled
or forceSoftwareRendering:
950 shouldRestart = (restartCounter == 0)
951 if shouldRestart
or forceSoftwareRendering:
952 self.
__logger.info(
"Restart slave for test case %s::%s" % (debugType, debugName))
953 if not self.
__startSlave(forceSoftwareRendering=forceSoftwareRendering):
954 raise Exception(
'Failed to start slave.')
956 restartCounter = freshCounterValue
966 timeFunctionStarted = time.time()
967 funcList = funcDict[testCaseName]
if (funcDict
and testCaseName
in funcDict)
else None
968 self.
__doTest(testCaseInfoNode, testResultNode, funcList)
969 timeFunctionStopped = time.time()
970 if int(testResultNode.get(
"status")) == Utils.TEST_STATUS_CANCELLED:
973 if int(testResultNode.get(
"status"))
in (Utils.TEST_STATUS_CRASHED,
974 Utils.TEST_STATUS_ERROR,
975 Utils.TEST_STATUS_FUNCTION_NOT_FOUND,
976 Utils.TEST_STATUS_TIMEOUT):
978 finishedTestCases += 1
980 testResultNode.set(
'duration', str(timeFunctionStopped-timeFunctionStarted))
982 testResultNode.set(
'slave_id',
'0' if self.
__slave else str(self.
__slaveProc.getID()))
984 resultNode.append(testResultNode)
988 except Exception
as e:
989 traceback.print_exc()
990 self.
__logger.error(
"Error executing test case %s::%s\n%s" % (debugType, debugName, e))
991 testResultNode.set(
"status",
unicode(Utils.TEST_STATUS_TIMEOUT))
994 self.
__logger.error(
"Failed to build the test agenda for test case %s." % testCaseNode.get(
'name',
'unknown'))
995 except Exception
as e:
996 self.
__logger.error(
"Error executing test case %s" % e)
999 resultNode.set(
"combinePythonCoverage",
"True")
1002 timeStopped = time.time()
1004 self.
__xmlRoot.set(
'timestamp', str(timeStarted))
1005 self.
__xmlRoot.set(
'duration', str(timeStopped-timeStarted))
1007 if stopSlaveAfterTestRun:
1034 def __isSlaveRunning (self):
1037 def __isSlaveStopped(self):
1044 return "%s (PID: %s)" % (message, pidString)
1053 errorCode, errorMessage = self.
__com.getLastErrorWithMessage()
1054 return "%s ([%d] %s)" % (message, errorCode, errorMessage)
1061 def __startSlave (self, forceSoftwareRendering=False):
1067 self.
__logger.error(
"Failed to stop the slave before restart.")
1072 env = Utils.setTestCenterPort(env, self.
__com.getPort())
1074 if slaveProc.hasStarted():
1082 def __connectToSlave(self):
1086 while retries < maxTries
and not self.
__com.connect(timeout = self.
__config.getIPCConnectionTimeout()):
1096 self.
__logger.debug(
"Sleeping for %s seconds" % (timerSleep))
1097 time.sleep(timerSleep)
1099 if not self.
__com.isConnected():
1101 self.
logErrorWithPID(
"Failed to connect to slave within %d seconds." % (maxTries*timerSleep))
1103 self.
__logger.error(
"Failed to stop the slave after connection error.")
1114 return resultNode.get(
'errorCode') ==
"Ok"
1136 if xmlNode.get(
'errorCode') ==
'Ok':
1167 def __sendRequest (self, command, parameterNode=None, timeout=None, tries=2, restart=False):
1170 requestNode = etree.Element(
'Command', type=command)
1171 requestNode.append(parameterNode
if parameterNode !=
None else etree.Element(
'Dummy'))
1175 resultNode = self.
__slave.runCommand(requestNode)
1181 timeout = self.
__config.getDefaultTestTimeoutSeconds()
1185 and (trial < tries)):
1186 timer = timeout
if isinstance(timeout, TestCaseTimer)
else TestCaseTimer(timeout)
1187 if self.
__com.send(etree.tostring(requestNode), timer.getSecondsRemaining()):
1190 while not string
and not timer.isExpired():
1191 string = self.
__com.recv(timer.getSecondsRemaining())
1193 recvNode = Utils.filterXMLString(string)
1195 if recvNode.tag ==
"Progress":
1199 self.
__testCaseListener.startTestCaseFunction(recvNode.get(
"testType"), recvNode.get(
"testCase"), recvNode.get(
"testFunction"))
1203 self.
__com.send(
"0" if shouldStop
else "1", 5)
1205 elif recvNode.tag ==
"Status":
1210 resultNode = recvNode
1213 self.
__logger.warning(
"It seems the test client is crashing. Collecting the stacktrace may take some while.")
1215 timer.extendTimeout(300)
1224 if restart
and (trial < tries):
1225 self.
__logger.info(
"Restarting due to communication error ...")
1228 if resultNode ==
None:
1230 resultNode = etree.Element(
'Result', errorCode=
'CommunicationError')
1231 etree.SubElement(resultNode,
'Error').text =
"Communication error [%d]: %s" % self.
__com.getLastErrorWithMessage()
1232 self.
logDebugWithPID(
"Result error code: %s" % resultNode.get(
'errorCode'))
1233 content = etree.tostring(resultNode).decode()
1234 self.
logDebugWithPID(
"Result content: %s%s" % (content[:150],
" [...]" if len(content)>150
else ""))