72 """The class controlling a MeVisLab slave instance.
73 The slave is controlled by a master using socket based IPC to send messages.
76 def __init__(self, cfgFile, context, offline=False):
77 """Initializes the slave process.
79 @param cfgFile The configuration file used for this test run.
80 @param context The context used for testing.
81 @param offline Specifies whether the tests are run in same MeVisLab instance as master.
97 resultDir = self.
_config.getResultDir()
98 if not os.path.isdir(resultDir):
99 os.makedirs(resultDir)
107 if not mevis.MLABTestCaseDatabase.areTestCasesLoaded():
108 mevis.MLABTestCaseDatabase.loadTestCases()
111 TestHelper.getInstance(context=self.
__ctx, resultDir=self.
_config.getResultDir())
120 """Load the given test case.
121 If the test case could not be loaded "None" is returned.
123 @param testCaseNode XML representation containing information on the test case to load.
124 @return The loaded test case.
126 testType = testCaseNode.get(
"type")
127 moduleName = testCaseNode.get(
"module")
if testType ==
"GenericTestCase" else None
128 return self.
__loadTestCase(testCaseNode.get(
"name"), testType, moduleName)
131 def __loadTestCase(testCaseName, testCaseType, moduleName=None):
132 """Internal loading mechanism.
134 @param testCaseName Name of the test case to be loaded.
135 @param testCaseType Type of the test case to be loaded.
136 @param moduleName Name of module to be tested (in case of generic test cases.)
137 @return The loaded test case. None if loading failed.
142 case
"GenericTestCase":
144 case
"FunctionalTestCase":
146 case
"CodeTestTestCase":
147 return CodeTestTestCase(testCaseName)
148 except Exception
as e:
149 mevis.MLAB.logError(
"Error loading test case %s: %s" % (testCaseName, e))
151 raise RuntimeError(
"Unsupported test case type %s" % testCaseType)
154 """Set the test case that is used for building the agenda/function list.
155 Used in the TestCaseManager module to make local testing possible.
157 @param testCase The test case object.
162 """Returns the test case that has been run.
163 Used in the TestCaseManager to keep test case after execution.
164 @return The test case object.
169 """Clears the loaded test case."""
172 def __doTest(self, testCaseNode, resultNode):
173 """Run the test received from the master.
174 The results (returned as an XML node) are appended to the resultNode.
176 @param testCaseNode The test case description in XML.
177 @param resultNode The result node returned (call by reference!).
181 maxErrorMessagesPerTestFunction = int(testCaseNode.get(
"maxErrorMessagesPerTestFunction"))
183 maxErrorMessagesPerTestFunction = 10000
185 maxInfoMessagesPerTestFunction = int(testCaseNode.get(
"maxInfoMessagesPerTestFunction"))
187 maxInfoMessagesPerTestFunction = 10000
196 pythonCoverage = CodeCoverage.PythonCoverage(config=self.
_config, testCaseNode=testCaseNode)
197 except CodeCoverage.CoverageException
as e:
198 pythonCoverage =
None
199 mevis.MLAB.logError(e)
202 bullseyeCoverage = CodeCoverage.BullseyeCoverage(config=self.
_config, testCaseNode=testCaseNode)
203 except CodeCoverage.CoverageException
as e:
204 bullseyeCoverage =
None
205 mevis.MLAB.logError(e)
212 if not testCaseNode.get(
"status") == Utils.TEST_STATUS_DO_NOT_TEST_MODULE:
213 if pythonCoverage
and pythonCoverage.isRunning():
216 resultNode.set(
"pythonCoverage", pythonCoverage.getCoverageBinaryFilePath())
218 if bullseyeCoverage
and bullseyeCoverage.isRunning():
221 resultNode.set(
"bullseyeCoverage", bullseyeCoverage.getCoverageBinaryFilePath())
224 logHandler.setErrorLimit(maxErrorMessagesPerTestFunction)
225 logHandler.setInfoLimit(maxInfoMessagesPerTestFunction)
226 TestHelper.getInstance().setLogHandler(logHandler)
231 TestHelper.getInstance().unsetLogHandler()
234 resultNode.set(
"errorCode",
"Ok")
236 resultNode.append(testCaseNode)
238 resultNode.set(
"errorCode",
"TestCaseError")
239 etree.SubElement(resultNode,
"Error").text =
"Unknown test case (%s)." % (testCaseNode.get(
"name"))
242 pythonCoverage.stop()
246 bullseyeCoverage.stop()
247 except CodeCoverage.CoverageException
as e:
248 mevis.MLAB.logError(e)
250 def __buildTestAgenda(self, testCfg, resultNode):
251 """Build a test agenda for the given request.
252 The agenda contains information which tests should be executed. The
253 information is contained in the returned XML structure.
255 @param testCfg The parameterization of the current test run.
256 @param resultNode The resulting agenda.
259 def ___addPackageInfoNode(rootNode):
260 packageInfoNode = etree.SubElement(rootNode,
"PackageSortingInReport")
262 "PackagesByNumberOfDependencies",
",".join(PackageSorter.byNumberOfDependencies.packages)
265 "PackageGroupsByNumberOfDependencies",
",".join(PackageSorter.byNumberOfDependencies.packageGroups)
268 testRunNode = etree.Element(
"TestRun")
272 "Packages": {
"Tested": [],
"Available": []},
282 for testGroupNode
in testCfg.findall(
"TestGroups/TestGroup"):
283 self.
__testCfg[
"TestGroups"].append(testGroupNode.get(
"name"))
286 for testSuiteNode
in testCfg.findall(
"TestSuites/TestSuite"):
287 testSuite = testSuiteNode.get(
"name")
288 testCaseNames = mevis.MLABTestCaseDatabase.allTestCaseSuites().get(testSuite, {}).get(
"testCases", [])
289 testCasesNode = testCfg.find(
"TestCases")
290 for name
in testCaseNames:
291 etree.SubElement(testCasesNode,
"TestCase", {
"name": name})
294 testRunNode.set(
"platform", mevis.MLAB.systemId())
295 testRunNode.set(
"compiler", mevis.MLAB.compilerInfo())
297 testRunNode.set(
"builddate", mevis.MLAB.dateOfBuildJob().date().toString(
"yyyy-MM-dd"))
298 testRunNode.set(
"version", mevis.MLAB.priv().variable(
"Version",
False))
301 infoNode = etree.SubElement(testRunNode,
"Information")
302 testResultNode = etree.SubElement(testRunNode,
"Results")
303 etree.SubElement(infoNode,
"MissingTestCases")
304 etree.SubElement(infoNode,
"TestCases")
305 etree.SubElement(infoNode,
"Modules")
306 ___addPackageInfoNode(infoNode)
314 for testCaseNode
in infoNode.findall(
"./TestCases/TestCase"):
315 if testCaseNode.get(
"type") ==
"GenericTestCase":
321 resultNode.set(
"errorCode",
"Ok")
322 resultNode.append(testRunNode)
323 resultNode.append(testCfg)
327 def __buildTestCaseAgenda(self, requestNode, resultNode):
329 parameterNode = requestNode.find(
"Parameters")
330 packagesToTest = parameterNode.get(
"packages",
"").split(
",")
331 modulesToTest = parameterNode.get(
"modules",
"").split(
",")
332 testCaseName = parameterNode.find(
"TestCase").get(
"name")
333 testResultsNode = etree.SubElement(resultNode,
"Results")
335 logHandler = LogHandler(
None)
336 logHandler.setErrorLimit(100)
337 logHandler.setInfoLimit(1000)
338 logHandler.allowNonFileBasedMessageLog()
339 TestHelper.getInstance().setLogHandler(logHandler)
342 testCaseName, testResultsNode, packagesToTest, modulesToTest
344 testCaseAgenda = testCase.getTestCaseNode()
345 testCaseNode = testResultsNode.find(
"TestCase")
347 if testCaseNode
is not None and testCaseNode.get(
"status",
"") == str(Utils.TEST_STATUS_DO_NOT_TEST_MODULE):
348 functionListNode = testCaseAgenda.find(
"TestFunctions")
349 functionNodes = functionListNode.findall(
"Function")
350 for functionNode
in functionNodes:
351 functionListNode.remove(functionNode)
352 resultNode.append(testCaseAgenda)
354 etree.SubElement(resultNode,
"Error").text =
"Failed to load the test case."
355 errorMessages = etree.SubElement(resultNode,
"Events")
356 for timeStamp, type, info
in logHandler.getCachedMessageList():
357 errorNode = etree.SubElement(errorMessages,
"Event")
358 errorNode.set(
"timestamp", timeStamp)
359 errorNode.set(
"type", type)
360 etree.SubElement(errorNode,
"Message").text = info
362 TestHelper.getInstance().unsetLogHandler()
365 resultNode.set(
"errorCode", status)
367 resultNode.set(
"errorCode",
"Failed")
368 etree.SubElement(resultNode,
"Error").text = traceback.format_exc()
370 def __verifyTestCaseList(self, testCfg, testRunNode):
371 """Verify the availability of all the given tests.
372 If no tests are given all available tests are returned.
374 @param testCfg The test run parameterization.
375 @param testRunNode The test run agenda.
378 availTestCaseList = []
379 for package
in self.
__testCfg[
"Packages"][
"Tested"]:
380 availTestCaseList.extend(mevis.MLABTestCaseDatabase.allTestCasesForPackage(package))
382 availTestCaseList.extend(getCodeTestsByPackage().get(package, {}).keys())
385 testCaseListNode = testCfg.find(
"TestCases")
386 if len(testCaseListNode.findall(
"TestCase")) == 0:
387 for testCaseName
in availTestCaseList:
388 etree.SubElement(testCaseListNode,
"TestCase", name=testCaseName)
391 testInfoNode = testRunNode.find(
"Information/TestCases")
392 testInfoNodeMissing = testRunNode.find(
"Information/MissingTestCases")
394 for testCaseNode
in testCfg.findall(
"TestCases/TestCase"):
395 testCaseName = testCaseNode.get(
"name")
396 if testCaseName
in availTestCaseList:
401 "TestCase %s not in tested groups (%s)."
402 % (testCaseName,
", ".join(self.
__testCfg[
"TestGroups"]))
404 testCaseNode.set(
"isNotInTestGroups", str(
True))
406 testCaseNode.set(
"type", infoDict[
"type"])
407 testInfoNode.append(testCaseNode)
408 testCaseNode.set(
"status", str(Utils.TEST_STATUS_OK))
410 mevis.MLAB.logError(
"test case not found %s %s" % (testCaseName, availTestCaseList))
411 testInfoNodeMissing.append(testCaseNode)
412 testCaseNode.set(
"status", str(Utils.TEST_STATUS_LOAD_ERROR))
414 def __loadAndVerifyTestCase(self, testCaseName, testResultNode, packagesToTest, modulesToTest):
415 """Verify Test Case and add information on it."""
416 resolvedPackagesToTest = []
417 for package
in packagesToTest:
418 pGroup, pName = package.split(
"/")
419 if pGroup ==
"*" and pName ==
"*":
420 for package
in mevis.MLABPackageManager.packages():
421 resolvedPackagesToTest.append(package.packageIdentifier())
423 for package
in mevis.MLABPackageManager.packages():
424 if package.packageGroup() == pGroup:
425 resolvedPackagesToTest.append(package.packageIdentifier())
427 resolvedPackagesToTest.append(package)
429 testCaseName, testResultNode, resolvedPackagesToTest, modulesToTest
431 return testCase, status
434 def __getTestCaseInfo(testCaseName):
435 infoDict = mevis.MLABTestCaseDatabase.testCaseInfo(testCaseName)
437 codeTest = getCodeTest(testCaseName)
438 if codeTest
is not None:
439 infoDict = codeTest.getInfoDict()
443 def __verifyTestCase(self, testCaseName, testResultNode, packagesToTest, modulesToTest):
444 """Verify Test Case and add information on it.
446 @param testCaseName The name of the test case to be verified.
447 @param testResultNode The node to append verified test cases.
448 @return The XML info node and a status string.
453 testType = infoDict[
"type"]
458 mevis.MLAB.log(
"Trying to load test case %s" % testCaseName)
461 mevis.MLAB.log(
"Loading complete")
464 return None,
"Failed"
467 if testType ==
"GenericTestCase":
468 modulesToTest, modulesNotToTest = testCase.getModulesToTest(modulesToTest, packagesToTest)
469 for moduleName
in modulesToTest:
470 etree.SubElement(testResultNode,
"TestCase", name=testCaseName, type=testType, module=moduleName)
471 for moduleName
in modulesNotToTest:
478 status=str(Utils.TEST_STATUS_DO_NOT_TEST_MODULE),
481 etree.SubElement(testResultNode,
"TestCase", name=testCaseName, type=testType)
483 return testCase, (
"Ok" if testCase.isValid()
else "FAILED")
486 def __verifyTestGroups(infoDict, testGroupList):
487 """Verify whether test case should be executed.
488 TestGroups can be used to specify groups of modules that should be tested.
489 There is one special group: if 'automatic' is given modules of the 'manual' group are not executed.
490 Tests in the testGroup 'disabled' are never executed.
492 @param testGroupList The list of test groups that should be tested.
493 @return Whether the test case should be executed.
495 testCaseGroupList = infoDict[
"testGroups"].lower()
if "testGroups" in infoDict
else ""
496 testCaseGroupList = testCaseGroupList.split()
498 if "disabled" in testCaseGroupList
or (
"needspublicsdk" in testCaseGroupList
and not _isPublicSDK):
500 testGroupList = [tg.lower()
for tg
in testGroupList]
502 if "automatic" in testGroupList:
503 if "manual" in testCaseGroupList:
506 for testGroup
in testGroupList:
507 if testGroup
in testCaseGroupList:
512 def __verifyPackages(self, testCfg):
513 """Build up a list of packages and verify existence of given ones.
514 This method is given an XML node containing the names of all the packages
515 that should be searched for TestCases and those that should be available.
516 The wildcard "*" can be used to select either all available packages
517 ("*/*") or all packages of a selected group ("Standard/*").
519 @param testCfg The test run parameterization.
521 packageRoot = testCfg.find(
"Packages")
525 tPkgNode = packageRoot.find(
"Tested")
526 aPkgNode = packageRoot.find(
"Available")
527 iPkgNode = packageRoot.find(
"Ignored")
530 if len(iPkgNode) > 0:
532 ignoredPackageIDs = set()
533 for ignoredPackage
in iPkgNode:
534 ignoredPackageIDs.add(ignoredPackage.get(
"name"))
540 for packageNode
in tPkgNode.findall(
"Package"):
541 if packageNode.get(
"status")
in (
"Ok",
"Added"):
542 tPkgSet.add(packageNode.get(
"name"))
545 for packageNode
in aPkgNode.findall(
"Package"):
546 if packageNode.get(
"status")
in (
"Ok",
"Added"):
547 aPkgSet.add(packageNode.get(
"name"))
550 self.
__testCfg[
"Packages"][
"Tested"] = list(tPkgSet)
551 self.
__testCfg[
"Packages"][
"Available"] = list(tPkgSet.union(aPkgSet))
554 for item
in tPkgSet.difference(aPkgSet):
555 etree.SubElement(aPkgNode,
"Package", name=item, status=
"Ok")
558 def __verifyPackageListNode(packageListNode, ignoredPackageIDs=None):
559 """Substitute wildcards and verify package nodes.
561 @param packageListNode Node with package sub-nodes that contain information
565 if len(packageListNode) == 0:
566 etree.SubElement(packageListNode,
"Package", name=
"*/*")
568 for packageNode
in packageListNode:
569 packageID = packageNode.get(
"name")
571 pGroup, pName = packageID.split(
"/")
573 packageNode.set(
"status",
"Invalid")
576 ignoredPackageIDs = ignoredPackageIDs
or []
580 for package
in mevis.MLABPackageManager.packages():
581 packageID = package.packageIdentifier()
582 if not packageID
in ignoredPackageIDs:
583 etree.SubElement(packageListNode,
"Package", name=packageID, status=
"Added")
585 packageNode.set(
"status",
"Substituted")
587 packageNode.set(
"status",
"Failed")
589 packageNode.set(
"status",
"Substituted")
590 for package
in mevis.MLABPackageManager.packagesByGroup(pGroup):
591 packageID = package.packageIdentifier()
592 if not packageID
in ignoredPackageIDs:
593 etree.SubElement(packageListNode,
"Package", name=packageID, status=
"Added")
597 if mevis.MLABPackageManager.packageByIdentifier(packageID):
598 packageNode.set(
"status",
"Ok")
600 packageNode.set(
"status",
"Failed")
602 def __collectModulesToTest(self, testCfg):
603 """Get a list of all modules to test.
604 The list of all available modules is filtered using the settings the user made.
605 Those can either be a specific list of tests or a set of filters to apply on the list of modules.
607 @param testCfg The test run parameterization.
609 modulesNode = testCfg.find(
"Modules")
613 for filterNode
in modulesNode.findall(
"Filter"):
614 key, valueList = filterNode.text.split(
":")
616 for value
in valueList.split(
","):
617 filterDict[key].append(value)
620 availableModuleList = []
621 for packageName
in self.
__testCfg[
"Packages"][
"Tested"]:
622 availableModuleList.extend(mevis.MLAB.allModulesForPackageIdentifier(packageName))
625 if len(modulesNode.findall(
"Module")) == 0:
626 for moduleName
in availableModuleList:
627 etree.SubElement(modulesNode,
"Module", name=moduleName)
630 for moduleNode
in modulesNode.findall(
"Module"):
631 moduleName = moduleNode.get(
"name")
632 moduleInfo = mevis.MLAB.moduleInfo(moduleName)
634 if moduleInfo
and moduleName
in availableModuleList:
638 if not f
in moduleInfo
or not [x
for x
in filterDict[f]
if re.search(x, moduleInfo[f])]:
642 self.
__testCfg[
"Modules"].append(moduleName)
643 moduleNode.set(
"status", str(Utils.TEST_STATUS_OK))
645 moduleNode.set(
"isModuleFiltered", str(
True))
647 modulesNode.set(
"status", str(Utils.TEST_STATUS_ERROR))
649 def __buildModuleInfo(self, modulesInfoNode):
650 for moduleName
in self.
__testCfg[
"Modules"]:
651 moduleInfo = mevis.MLAB.moduleInfo(moduleName)
652 newModuleNode = etree.SubElement(modulesInfoNode,
"Module", name=moduleName)
653 for key
in moduleInfo:
654 value = moduleInfo[key]
655 etree.SubElement(newModuleNode, key, type=value.__class__.__name__).text = str(value)
658 def __setPackageList(xmlNode, resultNode):
659 """Method to set the list of available packages given in the setting.
661 @param xmlNode An XML node containing 'Package' sub-nodes.
662 @param resultNode The result of the request.
665 for item
in xmlNode.findall(
"Package"):
666 packageList.append(item.get(
"name"))
667 TestHelper.getInstance().setPackageList(packageList)
668 resultNode.set(
"errorCode",
"Ok")
671 """Run a command given via the requestNode.
673 @param requestNode The request that contains the command and its parameters.
674 @return An XML node containing the results.
676 resultNode = etree.Element(
"Result")
677 if not TestHelper.getInstance().getGlobalContext():
679 mevis.MLAB.logWarning(
"Test case manager has been closed - aborting test execution")
681 command = requestNode.get(
"type")
682 mevis.MLAB.log(
"Running command '%s'" % command)
685 if command ==
"Quit":
686 resultNode.set(
"errorCode",
"Ok")
688 elif command ==
"DoTest":
689 testCaseNode = requestNode.find(
"TestCase")
690 if self.
__com and testCaseNode.get(
"requestProgress") ==
"1":
693 self.
__doTest(testCaseNode, resultNode)
695 if self.
__com and testCaseNode.get(
"requestProgress") ==
"1":
698 elif command ==
"SetPackageList":
700 elif command ==
"BuildTestAgenda":
702 elif command ==
"BuildTestCaseAgenda":
705 raise Exception(
"Unknown command: %s" % (command))
707 resultNode.set(
"errorCode",
"Exception")
708 etree.SubElement(resultNode,
"Error").text = traceback.format_exc()
709 mevis.MLAB.log(traceback.format_exc())
712 content = etree.tostring(resultNode).decode()
713 mevis.MLAB.log(
"Result content: %s%s" % (content[:150],
" [...]" if len(content) > 150
else ""))
717 """The main method which handles all the request from the master."""
720 TestHelper.getInstance().runTestInSecureMode()
722 request = self.
__com.recv(20)
723 if request
in (
"",
None):
724 mevis.MLAB.logError(self.
__com.getLastErrorWithMessage()[1])
727 requestNode = etree.fromstring(request)
730 if not self.
__com.send(etree.tostring(resultNode), 5):
731 mevis.MLAB.logError(
"Couldn't send result to request %s" % (request))
733 TestHelper.getInstance().runTestInCurrentMlabInstance()
734 self.
__com.disconnect()