TestCenter Reference
TestCase.py
Go to the documentation of this file.
2# Copyright 2009, 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#
10import contextlib
11
14
15# -- system imports ----------------------------------------------------------------------------{{{-
16import inspect
17import os
18import re
19import sys
20import time
21import traceback
22import unittest
23import asyncio
24from PythonQt import QtCore
25
26import xml.etree.cElementTree as etree
27
28# ----------------------------------------------------------------------------------------------}}}-
29
30# -- local imports -----------------------------------------------------------------------------{{{-
31import mevis
32from TestSupport.FieldValueTests import FieldValueTestCaseSet
33
34from TestSupport.TestHelper import TestHelper, CancelTestException
35from . import Utils
38
39# ----------------------------------------------------------------------------------------------}}}-
40
41# -- local imports -----------------------------------------------------------------------------{{{-
42MLABpriv = mevis.MLAB.priv()
43# ----------------------------------------------------------------------------------------------}}}-
44
45
47 """Prints the exception that occurred during a test function."""
48 exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
49 tb = exceptionTraceback.tb_next
50 traceback.print_tb(tb, limit=None, file=sys.stderr)
51 lines = traceback.format_exception_only(exceptionType, exceptionValue)
52 for line in lines:
53 sys.stderr.write(line)
54
55
57 def __init__(self, funcNode, funcName, logHandler, isLastTestFunction):
58 self.funcNode = funcNode
59 self.funcName = funcName
60 self.logHandler = logHandler
61 self.isLastTestFunction = isLastTestFunction
62 self.started = None
63 self.cancelled = False
64
65
67 def __init__(self, testHelper):
68 self.hasWarnings = False
69 self.hasErrors = False
70 self._testHelper = testHelper
71
72 @property
73 def logHandler(self):
74 return self._testHelper.getLogHandler() if self._testHelper else None
75
76 @contextlib.contextmanager
77 def collect(self):
78 try:
79 self.startLogCollect()
80 yield
81 finally:
82 self.stopLogCollect()
83
84 def startLogCollect(self):
85 if self.logHandler:
86 MLABpriv.aboutToLogMessage.connect(self.logHandler.aboutToLogMessage)
87 MLABpriv.clearLogState()
88 MLABpriv.startLogCollect()
89
90 def stopLogCollect(self):
91 if self.logHandler:
92 MLABpriv.aboutToLogMessage.disconnect(self.logHandler.aboutToLogMessage)
93 self.hasWarnings = self.hasWarnings or MLABpriv.hadLogWarnings()
94 self.hasErrors = self.hasErrors or MLABpriv.hadLogErrors()
95 MLABpriv.clearLogState()
96 MLABpriv.stopLogCollect()
97
98 def clearState(self):
99 self.hasWarnings = False
100 self.hasErrors = False
101
102
104 """Allows collecting log messages and logging them later.
105 Keeps the logging state during log collection."""
106
107 def __init__(self, testHelper=None):
108 # the testHelper is set to None in the load phase, because the log handler is not available yet,
109 # and we don't need it as we won't use test functions like "hasErrors" here
110 super().__init__(testHelper)
111 self.messageCache = []
112 self.log = ""
113
115 if self.logHandler:
116 MLABpriv.loggingMessage.connect(
117 lambda msg: self.logHandler.appendMessage(msg, messageCache=self.messageCache)
118 )
119 super().startLogCollect()
120
121 def stopLogCollect(self):
122 super().stopLogCollect()
123 if self.logHandler:
124 MLABpriv.loggingMessage.disconnect()
125 self.log = MLABpriv.collectedLog()
126 MLABpriv.clearLogCollect()
127
128 def logMessages(self, logHandler):
129 if self.logHandler:
130 logHandler.flushMessageCache(self.messageCache)
131 self.messageCache = []
132 else:
133 for log in self.log.split("<br>"):
134 if log:
135 logHandler.appendMessage(log)
136 self.log = ""
137
138
140 """Allows logging messages via a given log handler and records the logging state."""
141
143 MLABpriv.loggingMessage.connect(self.logHandler.appendMessage)
144 super().startLogCollect()
145
146 def stopLogCollect(self):
147 super().stopLogCollect()
148 MLABpriv.loggingMessage.disconnect(self.logHandler.appendMessage)
149
150
151class UnitTestResult(unittest.TestResult):
152
153 # deactivate stdout/stderr redirection, the output is captured by the TestCenter anyway
154 def _setupStdout(self):
155 pass
156
157 def _restoreStdout(self):
158 pass
159
160 def logResult(self, successMsg):
161 if self.wasSuccessful() and successMsg:
162 mevis.MLAB.log(successMsg)
163 if self.errors:
164 for error in self.errors:
165 mevis.MLAB.logError(error[1])
166 if self.failures:
167 for failure in self.failures:
168 mevis.MLAB.logError(failure[1])
169
170
172 """The superclass for the different test case categories."""
173
174 def __init__(self, testCaseName, isNewlyCreated=False):
175 """Initialize the test case.
176
177 @param testCaseName The name of the test case.
178 @param isNewlyCreated Indicates if the test case is newly created to suppress the warning
179 that no test functions exist.
180 """
181 self._ctx = None # testcase context
182 self._testCaseName = testCaseName
183 self._testHelper = TestHelper.getInstance()
184 self.__testCaseListener = None
185
186 self.__functionDict = {}
187
188
189 self.__dummyTestSuite = None
190
191
192 self.__previousTestClass = None
193 # XML node for extra test case results
194 self.__extraTestCasesResults = None
195 # The current result node.
196 self.__currentResultNode = None
197
198 self.__hasSetUpError = False
199 self.__setUpTestCaseCalled = False
200
201 # we have to collect the logs in the setup phase to
202 # avoid mixing load and setup phase logs
205 # during tests and teardown phase log messages can be directly sent to the log
207
208 with self.__loadLogCollector.collect():
209 infoDict = self._getTestCaseInfo(testCaseName)
210 associatedModules = infoDict.get("associatedModules", None)
211 if associatedModules is not None:
212 deprecatedModules = 0
213 for associatedModule in associatedModules:
214 moduleInfo = mevis.MLAB.moduleInfo(associatedModule)
215 if "deprecated" in moduleInfo.get("group", "").lower():
216 deprecatedModules += 1
217 if deprecatedModules == len(associatedModules):
218 MLABpriv.ignoreDeprecationWarnings(associatedModules)
219
220 # Get the basic test case information and store them in the XML node.
221 self.__xmlRoot = etree.Element("TestCase", name=self._testCaseName, type=self.getType())
222 self.__xmlRoot.set("timeout", infoDict.get("timeout", "0"))
223 self.__perFunctionResultDirectory = mevis.MLAB.valueIsTrue(infoDict.get("perFunctionResultDirectory", "1"))
224
225 etree.SubElement(self.__xmlRoot, "Package").text = infoDict["package"]
226 etree.SubElement(self.__xmlRoot, "Author").text = infoDict["author"]
227 etree.SubElement(self.__xmlRoot, "Maintainer").text = infoDict.get("maintainer", "")
228 etree.SubElement(self.__xmlRoot, "Comment").text = infoDict.get("comment", "")
229 etree.SubElement(self.__xmlRoot, "File").text = infoDict["file"]
230 etree.SubElement(self.__xmlRoot, "Line").text = str(infoDict["lineno"])
231 showTestFunctionSortLiterals = "0"
232 if "showTestFunctionSortingLiterals" in infoDict:
233 shouldShowLiterals = mevis.MLAB.valueIsTrue(infoDict["showTestFunctionSortingLiterals"])
234 showTestFunctionSortLiterals = "1" if shouldShowLiterals else "0"
235 etree.SubElement(self.__xmlRoot, "showTestFunctionSortingLiterals").text = showTestFunctionSortLiterals
236 if "preferredRenderer" in infoDict:
237 etree.SubElement(self.__xmlRoot, "preferredRenderer").text = infoDict["preferredRenderer"]
238
239 # Set the data directory. If not specified use default value.
240 if "dataDirectory" in infoDict:
241 self._dataDirectory = infoDict["dataDirectory"]
242 else:
243 self._dataDirectory = os.path.join(os.path.dirname(infoDict["file"]), "Data")
244
245 if not (infoDict and "scriptFile" in infoDict):
246 mevis.MLAB.logError(f"Missing scriptFile tag in TestCase {testCaseName}.")
247 else:
248 try:
249 self._loadScriptFile(infoDict["scriptFile"])
250 # we need to activate the TestCase, because building of the virtual functions will call into
251 # TestHelper functions...
252 self.activate()
253
254 self._buildVirtualFunctions(isNewlyCreated)
255
256 self.deactivate()
257
258 etree.SubElement(self.__xmlRoot, "Documentation").text = self._ctx.scriptVariable("__doc__")
259 except Exception as e:
260 mevis.MLAB.logError(f"Failed to load script file {infoDict['scriptFile']}. No tests created.")
261 raise
262
263 if "setUpTestCase" in self._ctx.callableFunctions():
264 # create a virtual test function to show the setup messages
266 "SetUpTestCase",
267 Utils.SETUP_TEST_CASE_FUNCTION,
268 "Collects messages while the test case is set up",
269 prepend=True,
270 )
271
272 if "tearDownTestCase" in self._ctx.callableFunctions():
273 # create a virtual test function to show messages from the test case tear down
275 "TearDownTestCase",
276 Utils.TEARDOWN_TEST_CASE_FUNCTION,
277 "Collects messages during the teardown phase of the test case",
278 prepend=False,
279 )
280
281 # create a virtual test function to show unload errors
283 "UnloadTestCase",
284 Utils.VIRTUAL_UNLOAD_TEST_CASE_TEST_FUNCTION,
285 "Collects errors while the test case is unloaded",
286 prepend=False,
287 )
288
289 MLABpriv.ignoreDeprecationWarnings()
290 self.__valid = not self.__loadLogCollector.hasErrors
291 if not self.__valid or self.__loadLogCollector.hasWarnings:
292 # create a virtual test function to show errors and warnings during test case load
294 "LoadTestCase",
295 Utils.VIRTUAL_LOAD_TEST_CASE_TEST_FUNCTION,
296 "Collects errors while the test case is loaded",
297 prepend=True,
298 )
299
300 def __createVirtualTestFunction(self, basename, name, documentation, prepend):
301 functionsNode = self.__xmlRoot.find("TestFunctions")
302 if functionsNode is None:
303 functionsNode = etree.SubElement(self.__xmlRoot, "TestFunctions")
304 functionXMLNode = functionsNode.makeelement(
305 "Function", {"basename": basename, "name": name, "is_disabled": "False"}
306 )
307 if prepend:
308 functionsNode.insert(0, functionXMLNode)
309 else:
310 functionsNode.append(functionXMLNode)
311 etree.SubElement(functionXMLNode, "Documentation").text = documentation
312
313 def _getTestCaseInfo(self, testCaseName):
314 return mevis.MLABTestCaseDatabase.testCaseInfo(testCaseName)
315
316 def __del__(self):
317 self.destroyContext()
318
319 def destroyContext(self):
320 if self._ctx:
321 try:
322 self._ctx.remove()
323 finally:
324 self._ctx = None
325
326 def activate(self):
327 """Registers the test case at the TestHelper. Does not support recursive calls."""
328 # Tell the TestHelper about us, but don't set a reference
329 # to self to the TestHelper to avoid that it reference counts us
330 self._testHelper.setMacrosLogOnSuccess(True)
331 self._testHelper.setTestCaseContext(self._ctx)
332 self._testHelper.setTestCaseName(self._testCaseName)
333 self._testHelper.setTestCaseDataDirectory(self._dataDirectory)
334 self._testHelper.pushChangeSet()
335
336 # --------------------------------------------------------------------------------------------}}}-
337
338 def deactivate(self):
339 """Resets the test helper. Does not support recursive calls."""
340 self._testHelper.setTestCaseContext(None)
341 self._testHelper.setTestCaseName("")
342 self._testHelper.setTestCaseDataDirectory("")
343 self._testHelper.popChangeSet()
344 self._testHelper.setMacrosLogOnSuccess(True)
345
346 # --------------------------------------------------------------------------------------------}}}-
347
348 def setTestCaseListener(self, testCaseListener):
349 self.__testCaseListener = testCaseListener
350
351 # -- def addExtraTestCaseResult ------------------------------------------------------------------{{{-
352 def addExtraTestCaseResult(self, extraTestCaseResult):
353 assert self.__currentResultNode is not None
354 assert self.__currentResultNode != self.__xmlRoot
355 if self.__extraTestCasesResults is None:
356 self.__extraTestCasesResults = etree.SubElement(self.__currentResultNode, "ExtraTestCasesResults")
357 self.__extraTestCasesResults.append(extraTestCaseResult.toXML())
358
359 def _getTestFunction(self, funcName):
360 return self._ctx.scriptVariable(funcName)
361
362 def __callTestFunction(self, funcName):
363 """Call the test function with the given name.
364
365 @param funcName The name of the function to execute.
366 """
367 if Utils.isInternalTestCaseFunction(funcName):
368 return
369
370 if not funcName in self.__functionDict:
371 raise Exception("Unknown function!")
372
373 changeSetLength = self._testHelper.pushChangeSet()
374
375 callInfo = self.__functionDict[funcName]
376
377 try:
378 if callInfo:
379 if type(callInfo) in (list, tuple):
380 testFunction, testArguments = callInfo
381 callWithKeywordArguments = False
382 if len(testArguments) == 1 and isinstance(testArguments[0], dict):
383 signature = inspect.signature(testFunction)
384 try:
385 # try to bind the test arguments as keyword arguments to the test function
386 signature.bind(**testArguments[0])
387 callWithKeywordArguments = True
388 except TypeError:
389 pass
390 if callWithKeywordArguments:
391 result = testFunction(**testArguments[0])
392 else:
393 result = testFunction(*testArguments)
394 else:
395 result = self._getTestFunction(callInfo)()
396 else:
397 result = self._getTestFunction(funcName)()
398
399 # if the result is a coroutine, we schedule it and process events until it
400 # has finished... This allows test functions to use the async keyword.
401 if asyncio.iscoroutine(result):
402 self.futureDone = False
403
404 def futureDone(*args):
405 self.futureDone = True
406
407 future = asyncio.ensure_future(result)
408 future.add_done_callback(futureDone)
409 while not self.futureDone:
410 QtCore.QCoreApplication.processEvents()
411 future.result()
412
413 if TestSupport.Logging.gStopped:
414 raise CancelTestException
415
416 except CancelTestException:
417 self._testHelper.clearChangeSet()
418 raise
419 except TestSupport.Macros.AssertException:
420 # an assertion has failed and we should stop execution,
421 # but this is an expected exception, so we don't print it.
422 pass
423 except: # pylint: disable=W0702
424 self._testHelper.clearChangeSet()
425 mevis.MLAB.logError(f"Exception occurred in {funcName}:")
427
428 # Clean up the environment.
429 self._testHelper.popChangeSet()
430 while self._testHelper.getChangeSetStackLength() > changeSetLength:
431 mevis.MLAB.logError("You should pop your ChangeSets by yourself!")
432 self._testHelper.popChangeSet()
433
434 def _loadScriptFile(self, filename):
435 """Load the test cases script file.
436
437 @param filename Name of the script file to be loaded.
438 """
439 self._ctx = self._testHelper.getGlobalContext().addModule(filename)
440 if self._ctx:
441 # set dynamic property so that MeVisLab and MATE know that this is a TestCase
442 self._ctx.setProperty("IsTestCase", True)
443 self._ctx.setProperty("TestCaseName", self._testCaseName)
444
445 def callFieldValueTestCase(self, filename, testcase):
446 """Call the given field-value test case from the given field-value test case set.
447 This method is required as the FIELDVALUETEST test methods don't have a
448 valid counterpart in the script file itself.
449
450 @param filename The field-value test case set.
451 @param testcase The test case to be executed.
452 """
453 fieldValueTestCaseSet = FieldValueTestCaseSet(self._ctx)
454 if not fieldValueTestCaseSet.load(filename):
455 mevis.MLAB.logError(f"Failed ot load field-value test ({filename}).")
456 else:
457 testCase = fieldValueTestCaseSet.get(testcase)
458 testCase.applyParameterization(TestHelper.getInstance().getChangeSet(), verbose=True)
459 if not testCase.verifyExpectedResults(verbose=True):
460 mevis.MLAB.logError("Failed to verify expected results.")
461
462 def __createNormalTestNode(self, virtualFunctionName, functionName, functionBaseName, functionDict, isDisabled):
463 """Create the XML node for a normal test method.
464
465 @param functionName The real name of the function.
466 @param functionBaseName The base name (without the TEST_).
467 @param functionDict The dictionary used to prevent duplicated function names.
468 """
469 retVal = True
470 if not functionBaseName in functionDict["names"]:
471 xmlNode = self.__getFunctionInfo(
472 functionBaseName, functionName, isDisabled=isDisabled, sourceName=functionName
473 )
474 functionDict["names"].append(functionBaseName)
475 functionDict["nodes"][virtualFunctionName] = xmlNode
476 else:
477 mevis.MLAB.logError(f"Function with name {functionBaseName} already defined!")
478 retVal = False
479 return retVal
480
481 def _callGroupFunction(self, groupFunctionName):
482 return self._ctx.call(groupFunctionName, [])
483
484 def __createTestGroupNode(self, groupFunctionName, orderString, groupName, functionDict, isDisabled):
485 """Create the XML information for an item in a test group.
486
487 @param groupFunctionName The name of the group function.
488 @param orderString The string used to determine the order of test functions.
489 @param groupName The name of the group.
490 @param functionDict The dictionary used to prevent duplicated function names.
491 """
492 if groupName in functionDict["names"]:
493 mevis.MLAB.logError(f"A test group with name {groupName} already exists. No duplicate names allowed.")
494 return
495
496 # Get the field-value test case set file.
497 functionList = self._callGroupFunction(groupFunctionName)
498 if not functionList:
499 mevis.MLAB.logError(f"Failed to build virtual functions for the {groupFunctionName} test.")
500 return
501
502 if type(functionList) not in (list, tuple):
503 mevis.MLAB.logError(
504 f"The test group function ({groupFunctionName}) must return a list or tuple with function objects!"
505 )
506 return
507
508 virtualGroupName = f"TEST{orderString}_{groupName}"
509 groupXMLNode = etree.Element(
510 "GroupNode", name=virtualGroupName, basename=groupName, type="TestGroup", sourceName=groupFunctionName
511 )
512 etree.SubElement(groupXMLNode, "Documentation").text = self._ctx.scriptVariable(f"{groupFunctionName}.__doc__")
513
514 nameToFunctionMap = {}
515 for function in functionList:
516 # allow function names as string
517 if isinstance(function, str):
518 function = self._ctx.scriptVariable(function)
519 nameToFunctionMap[function.__name__] = function
520
521 for functionName in sorted(nameToFunctionMap.keys()):
522 function = nameToFunctionMap[functionName]
523
524 functionNodeName = functionName
525 if functionNodeName.startswith("DISABLED_"):
526 functionNodeName = functionNodeName[len("DISABLED_") :]
527
528 if (
529 functionNodeName not in functionDict["nodes"]
530 or functionDict["nodes"][functionNodeName].tag != "Function"
531 ):
532 mevis.MLAB.logError(
533 f"Failed to compile function list. Given function {functionNodeName} not a test function "
534 f"or function already part of a test function group."
535 )
536 return False
537
538 # Update function info, i.e. move the function node to the group node. The
539 # dictionary with the virtual function names must be updated too.
540 virtualFunctionName = (
541 f"{virtualGroupName}_"
542 f"{functionNodeName[4:] if not functionNodeName.startswith('DISABLED_') else functionNodeName[13:]}"
543 )
544
545 functionNode = functionDict["nodes"][functionNodeName]
546 functionNode.set("name", virtualFunctionName)
547 # mark function as disabled if the whole group is disabled; otherwise, keep the current state of the function
548 # this way explicitly disabled functions stay disabled
549 if isDisabled:
550 functionNode.set("is_disabled", str(isDisabled))
551
552 # Remove function from dictionary of nodes. It will be part of the group!
553 del functionDict["nodes"][functionNodeName]
554
555 # Remove old virtual function
556 self.__functionDict.pop(functionName)
557 self.__functionDict[virtualFunctionName] = functionName
558
559 groupXMLNode.append(functionNode)
560
561 functionDict["names"].append(groupName)
562 functionDict["nodes"][virtualGroupName] = groupXMLNode
563 return True
564
565 def __createFieldValueTestNode(self, virtualFunctionBaseName, functionName, groupName, functionDict, isDisabled):
566 """Create the XML information for the given field-value test.
567
568 @param virtualFunctionBaseName The base name of the virtual function.
569 @param functionName The real name of the function.
570 @param groupName The name of the group.
571 @param functionDict The dictionary used to prevent duplicated function names.
572 """
573 # Find all the field-value test case functions, expand them and add the
574 # single items to the virtual function list.
575 if groupName in functionDict["names"]:
576 mevis.MLAB.logError(f"A test with name {groupName} already exists. No duplicate names allowed.")
577 return
578
579 # Get the field-value test case set file.
580 self._testHelper.pushChangeSet()
581 retVal = self._ctx.call(functionName, [])
582 self._testHelper.popChangeSet()
583
584 filename, testCaseList = retVal if type(retVal) in (list, tuple) else (retVal, None)
585
586 groupXMLNode = etree.Element(
587 "GroupNode",
588 name=virtualFunctionBaseName,
589 basename=groupName,
590 type="FieldValueTest",
591 sourceName=functionName,
592 )
593 etree.SubElement(groupXMLNode, "Documentation").text = self.getDocumentation(functionName)
594
595 # Keep in mind that duplicate names aren't allowed in field-value test case
596 # sets either.
597 functionDict["names"].append(groupName)
598 functionDict["nodes"][virtualFunctionBaseName] = groupXMLNode
599
600 fvtcs = FieldValueTestCaseSet(self._ctx)
601 if not fvtcs.load(filename):
602 mevis.MLAB.logError(f"Failed to load the field-value test case set ({filename})!")
603 return
604
605 if not testCaseList:
606 for testcase in sorted(fvtcs.getList()):
607 virtualFunctionName = f"{virtualFunctionBaseName}_{testcase}"
608 groupXMLNode.append(
610 testcase,
611 virtualFunctionName,
613 isDisabled=isDisabled,
614 sourceName=functionName,
615 )
616 )
617 else:
618 availableTestCaseList = fvtcs.getList()
619 for testcase in sorted(testCaseList):
620 if testcase in availableTestCaseList:
621 virtualFunctionName = f"{virtualFunctionBaseName}_{testcase}"
622 groupXMLNode.append(
624 testcase,
625 virtualFunctionName,
626 (self.callFieldValueTestCasecallFieldValueTestCase, (filename, testcase)),
627 isDisabled=isDisabled,
628 sourceName=functionName,
629 )
630 )
631 else:
632 mevis.MLAB.logError(f"The field-value test case {testcase} is unknown!")
633
634 # --------------------------------------------------------------------------------------------}}}-
635
636 # -- def __createIterativeTestNode -----------------------------------------------------------{{{-
637
642 def __createIterativeTestNode(self, virtualFunctionBaseName, functionName, groupName, functionDict, isDisabled):
643 # Find all iterative test functions, expand them and add the single items
644 # to the virtual function list.
645 if groupName in functionDict["names"]:
646 mevis.MLAB.logError(f"A test with name {groupName} already exists. No duplicate names allowed.")
647 return
648
649 # Get the iterations and the actual function to call.
650 self._testHelper.pushChangeSet()
651 retVal = self._ctx.call(functionName, [])
652 self._testHelper.popChangeSet()
653
654 try:
655 iterator, function = retVal
656 except: # pylint: disable=W0702
657 mevis.MLAB.logError(
658 f"The iterative test function ({functionName}) must return a list or dictionary"
659 f" with parameters and a function to call!"
660 )
661 return
662
663 groupXMLNode = etree.Element(
664 "GroupNode", name=virtualFunctionBaseName, basename=groupName, type="IterativeTest", sourceName=functionName
665 )
666 etree.SubElement(groupXMLNode, "Documentation").text = self.getDocumentation(functionName)
667
668 # Duplicate item nodes are not possible by design (either an index is used
669 # or a dictionary).
670 functionDict["names"].append(groupName)
671 functionDict["nodes"][virtualFunctionBaseName] = groupXMLNode
672 regExpFunctionNames = re.compile(r"^[.\w]+$")
673
674 # Create virtual functions for given iterative test.
675 if isinstance(iterator, (list, tuple)):
676 ctr = 0
677 # Let the index used to identify the function be zero padded.
678 justificationWidth = len(str(len(iterator) - 1))
679 for item in iterator:
680 # The zero padded identifier.
681 virtualFunctionId = str(ctr).rjust(justificationWidth, "0")
682 args = [item] if (type(item) not in (list, tuple)) else item
683 virtualFunctionName = f"{virtualFunctionBaseName}_{virtualFunctionId}"
684 if regExpFunctionNames.match(virtualFunctionName):
685 groupXMLNode.append(
687 virtualFunctionId,
688 virtualFunctionName,
689 (function, args),
690 isDisabled=isDisabled,
691 sourceName=functionName,
692 )
693 )
694 else:
695 mevis.MLAB.logError(
696 f"The iterative test function ({functionName}) defines non-alphanumeric virtual "
697 f"function name {virtualFunctionName}!"
698 )
699 ctr += 1
700 elif isinstance(iterator, dict):
701 for item in sorted(iterator.keys()):
702 args = [iterator[item]] if (type(iterator[item]) not in (list, tuple)) else iterator[item]
703 virtualFunctionName = f"{virtualFunctionBaseName}_{item}"
704 if regExpFunctionNames.match(virtualFunctionName):
705 groupXMLNode.append(
707 str(item),
708 virtualFunctionName,
709 (function, args),
710 isDisabled=isDisabled,
711 sourceName=functionName,
712 )
713 )
714 else:
715 mevis.MLAB.logError(
716 f"The iterative test function ({functionName}) defines non-alphanumeric virtual "
717 f"function name {virtualFunctionName}!"
718 )
719 else:
720 mevis.MLAB.logError(
721 f"The iterative test function ({functionName}) must return a list or dictionary with parameters!"
722 )
723
724 # --------------------------------------------------------------------------------------------}}}-
725
726 # -- def __createUnitTestWrapperNode -----------------------------------------------------------{{{-
727
728 def __createUnitTestWrapperNode(self, virtualFunctionBaseName, functionName, groupName, functionDict, isDisabled):
729 """Create the XML information for the given unit test wrapper, which creates a test function for
730 every unit test function, similar to an iterative test.
731
732 @param virtualFunctionBaseName The base name of the virtual function.
733 @param functionName The real name of the function.
734 @param groupName The name of the group.
735 @param functionDict The dictionary used to prevent duplicated function names.
736 """
737
738 # Find all unit test wrapper functions, expand them and add the single items
739 # to the virtual function list.
740 if groupName in functionDict["names"]:
741 mevis.MLAB.logError(f"A test with name {groupName} already exists. No duplicate names allowed.")
742 return
743
744 # Get the unit test and the actual function to call.
745 self._testHelper.pushChangeSet()
746 suite = self._ctx.call(functionName, [])
747 self._testHelper.popChangeSet()
748
749 if not isinstance(suite, unittest.TestSuite):
750 mevis.MLAB.logError(f"The unit test wrapper function ({functionName}) must return a unittest.TestSuite!")
751 return
752
753 groupXMLNode = etree.Element(
754 "GroupNode",
755 name=virtualFunctionBaseName,
756 basename=groupName,
757 type="UnitTestWrapper",
758 sourceName=functionName,
759 )
760 etree.SubElement(groupXMLNode, "Documentation").text = self.getDocumentation(functionName)
761
762 # Duplicate item nodes are not possible by design (either an index is used
763 # or a dictionary).
764 functionDict["names"].append(groupName)
765 functionDict["nodes"][virtualFunctionBaseName] = groupXMLNode
766
767 def _isSkipped(test):
768 isClassSkipped = getattr(test.__class__, "__unittest_skip__", False)
769 testMethod = getattr(test, test._testMethodName) # pylint: disable=W0212
770 isMethodSkipped = getattr(testMethod, "__unittest_skip__", False)
771 return isClassSkipped or isMethodSkipped
772
773 # Create virtual functions for given unit test wrapper
774 def _unpackSuite(suite):
775 allTests = []
776 for test in suite._tests: # pylint: disable=W0212
777 if isinstance(test, unittest.TestSuite):
778 allTests.extend(_unpackSuite(test))
779 else:
780 if not _isSkipped(test):
781 allTests.append(test)
782 return allTests
783
784 allTests = _unpackSuite(suite)
785
786 def _makeName(test):
787 testCaseName = test.__class__.__name__
788 testFunctionName = test._testMethodName # pylint: disable=W0212
789 return "{0}_{1}".format(testCaseName, testFunctionName)
790
791 allTests.sort(key=_makeName)
792 testNames = list(map(_makeName, allTests))
793
794 # It could be smarter to integrate this more deeply into the callTestFunction part.
795 def _runUnitTestFunction(testFunction):
796 testResult = UnitTestResult()
797 testResult._previousTestClass = self.__previousTestClass
798 if self.__dummyTestSuite is None:
799 self.__dummyTestSuite = unittest.TestSuite()
800 # handle setup of class and module fixtures (copied from unittest.TestSuite):
801 self.__dummyTestSuite._tearDownPreviousClass(testFunction, testResult)
802 self.__dummyTestSuite._handleModuleFixture(testFunction, testResult)
803 self.__dummyTestSuite._handleClassSetUp(testFunction, testResult)
804 self.__previousTestClass = testFunction.__class__
805 # run test:
806 testFunction.run(testResult)
807 testResult.logResult("Test was successful")
808
809 for testName, testFunction in zip(testNames, allTests):
810 testMethod = getattr(testFunction, testFunction._testMethodName)
811 funcXMLNode = self.__getFunctionInfo(
812 testName,
813 "{}_{}".format(virtualFunctionBaseName, testName),
814 (_runUnitTestFunction, [testFunction]),
815 isDisabled=isDisabled,
816 )
817 if hasattr(testMethod, "__func__"): # testMethod is really a method
818 funcXMLNode.attrib["sourceFilename"] = testMethod.__func__.__code__.co_filename
819 funcXMLNode.attrib["sourceLine"] = str(testMethod.__func__.__code__.co_firstlineno)
820 elif hasattr(testMethod, "__code__"): # testMethod is actually a function
821 funcXMLNode.attrib["sourceFilename"] = testMethod.__code__.co_filename
822 funcXMLNode.attrib["sourceLine"] = str(testMethod.__code__.co_firstlineno)
823 else: # None or unsupported type - should we raise here?
824 funcXMLNode.attrib["sourceFilename"] = ""
825 funcXMLNode.attrib["sourceLine"] = "0"
826 groupXMLNode.append(funcXMLNode)
827
829 return self._ctx.callableFunctions()
830
831 def _buildVirtualFunctions(self, isNewlyCreated):
832 """Build an XML node with the virtual functions.
833 The result is an XML node that contains information on all virtual functions
834 including the information on hierarchy i.e. which nodes are aggregated in a group.
835 Returns a Boolean value stating success.
836 """
837
838 # A list containing the found function names (to prevent duplicates).
839 functionInformationDict = {"names": [], "nodes": {}}
840
841 regExpDict = {
842 "TestFunction": re.compile(r"^(DISABLED_)?TEST(\d*)_(\w+)$"),
843 "TestGroup": re.compile(r"^(DISABLED_)?GROUP(\d*)_(\w+)$"),
844 "FieldValueTest": re.compile(r"^(DISABLED_)?FIELDVALUETEST(\d*)_(\w+)$"),
845 "IterativeTest": re.compile(r"^(DISABLED_)?ITERATIVETEST(\d*)_(\w+)$"),
846 "UnitTestWrapper": re.compile(r"^(DISABLED_)?UNITTEST(\d*)_(\w+)$"),
847 }
848
849 # The result node.
850 functionsNode = etree.SubElement(self.__xmlRoot, "TestFunctions")
851
852 # Check the optional list of disabled test functions (it is defined by the disabledTestFunction() decorator)
853 disabledFunctions = self._ctx.scriptVariable("MLABTC_DISABLED_TEST_FUNCTIONS") or []
854
855 # The dictionary of all test functions sorted by type.
856 functionDict = {
857 "TestFunction": [],
858 "TestGroup": [],
859 "FieldValueTest": [],
860 "IterativeTest": [],
861 "UnitTestWrapper": [],
862 }
863 for functionName in self._callableFunctions():
864 for prefix, functionList in (
865 ("TEST", functionDict["TestFunction"]),
866 ("GROUP", functionDict["TestGroup"]),
867 ("FIELDVALUETEST", functionDict["FieldValueTest"]),
868 ("ITERATIVETEST", functionDict["IterativeTest"]),
869 ("UNITTEST", functionDict["UnitTestWrapper"]),
870 ):
871 if functionName.startswith(prefix) or functionName.startswith("DISABLED_" + prefix):
872 functionList.append(functionName)
873
874 for functionName in sorted(functionDict["TestFunction"]):
875 mObj = regExpDict["TestFunction"].match(functionName)
876 if not mObj:
877 mevis.MLAB.logError(f"Name of test function invalid ({functionName})")
878 else:
879 disabledString, orderString, functionBaseName = mObj.groups()
880 isDisabled = (disabledString is not None) or (functionName in disabledFunctions)
881 fname = f"TEST{orderString}_{functionBaseName}"
883 fname, functionName, functionBaseName, functionInformationDict, isDisabled=isDisabled
884 )
885
886 for functionName in sorted(functionDict["TestGroup"]):
887 mObj = regExpDict["TestGroup"].match(functionName)
888 if not mObj:
889 mevis.MLAB.logError(f"Name of test group invalid ({functionName})")
890 else:
891 disabledString, orderString, groupName = mObj.groups()
892 isDisabled = (disabledString is not None) or (functionName in disabledFunctions)
894 functionName, orderString, groupName, functionInformationDict, isDisabled=isDisabled
895 )
896
897 for functionName in sorted(functionDict["FieldValueTest"]):
898 mObj = regExpDict["FieldValueTest"].match(functionName)
899 if not mObj:
900 mevis.MLAB.logError(f"Name of field value test invalid ({functionName})")
901 else:
902 disabledString, orderString, groupName = mObj.groups()
903 isDisabled = (disabledString is not None) or (functionName in disabledFunctions)
904 fname = f"TEST{orderString}_{groupName}"
906 fname, functionName, groupName, functionInformationDict, isDisabled=isDisabled
907 )
908
909 for functionName in sorted(functionDict["IterativeTest"]):
910 mObj = regExpDict["IterativeTest"].match(functionName)
911 if not mObj:
912 mevis.MLAB.logError(f"Name of iterative test invalid ({functionName})")
913 else:
914 disabledString, orderString, groupName = mObj.groups()
915 isDisabled = (disabledString is not None) or (functionName in disabledFunctions)
916 fname = f"TEST{orderString}_{groupName}"
918 fname, functionName, groupName, functionInformationDict, isDisabled=isDisabled
919 )
920
921 for functionName in sorted(functionDict["UnitTestWrapper"]):
922 mObj = regExpDict["UnitTestWrapper"].match(functionName)
923 if not mObj:
924 mevis.MLAB.logError(f"Name of unit test wrapper invalid ({functionName})")
925 else:
926 disabledString, orderString, groupName = mObj.groups()
927 isDisabled = (disabledString is not None) or (functionName in disabledFunctions)
928 fname = f"TEST{orderString}_{groupName}"
930 fname, functionName, groupName, functionInformationDict, isDisabled=isDisabled
931 )
932
933 # Append nodes in correct order. Essential for other methods to work
934 # properly.
935 keys = sorted(functionInformationDict["nodes"].keys())
936 if len(keys) > 0:
937 for functionName in keys:
938 functionsNode.append(functionInformationDict["nodes"][functionName])
939 elif not isNewlyCreated:
940 mevis.MLAB.logError(f"No test functions found in TestCase {self._testCaseName}!")
941
942 def __getFunctionInfo(self, functionBaseName, functionName, calledFunction=None, isDisabled=False, sourceName=""):
943 """Internal method to add a function to the required data-structures.
944
945 @param functionBaseName The virtual function name.
946 @param functionName The real name of the function.
947 @param calledFunction Information on the function called.
948 """
949 self.__functionDict.setdefault(functionName, calledFunction)
950 functionXMLNode = etree.Element(
951 "Function", basename=functionBaseName, name=functionName, sourceName=sourceName, is_disabled=str(isDisabled)
952 )
953 documentation = self.getDocumentation(functionName)
954 etree.SubElement(functionXMLNode, "Documentation").text = documentation if documentation else ""
955 return functionXMLNode
956
957 def isValid(self):
958 """Return True if the loaded test case is syntactically valid and should be executable."""
959 return self.__valid
960
961 def hasNetwork(self):
962 """Return True if the test case has a network attached."""
963 return os.path.exists(self.networkFileName())
964
966 """Returns the absolute file name of the attached network or an empty string if no network is attached."""
967 return self._ctx.network().filename()
968
969 def openFiles(self):
970 """Open the files associated with the test case."""
971 typeName = self.getName()
972 for fileName in self._ctx.getScriptSourceFiles():
973 MLABpriv.openRelatedFile(typeName, fileName)
974
975 def openNetwork(self):
976 """Open the internal network of the test case."""
977 if self._ctx:
978 MLABpriv.showIDE()
979 self._ctx.showInternalNetwork()
980
981 def getType(self):
982 """Return the type of this test case (generic or functional)."""
983 raise NotImplementedError(f"getType() is not implemented in {self.__class__.__name__}")
984
985 def getModule(self):
986 """Return the module representing this test case, or None."""
987 return self._ctx
988
989 def getName(self):
990 """Return the name of the test case."""
991 return self._testCaseName
992
994 """Get the list of test functions in this test."""
995 return sorted(self.__functionDict.keys())
996
997 def getPackage(self):
998 """Return the identifier of the package the test case is in."""
999 return self.__xmlRoot.find("Package").text
1000
1002 """Return the XML node with the test case's information."""
1003 return self.__xmlRoot
1004
1005 def __callSetUp(self):
1006 """Call the setup function of the test if available."""
1007 if "setUp" in self._ctx.callableFunctions():
1008 with self.__testLogger.collect():
1009 self._ctx.call("setUp", [])
1010 mevis.MLAB.log(Utils.lineSeparator())
1011
1012 def __callSetUpTestCase(self):
1013 """Call the setup function of the test case if available."""
1014 if "setUpTestCase" in self._ctx.callableFunctions() and not self.__setUpTestCaseCalled:
1015 self.__setUpTestCaseCalled = True
1016 with self.__setupLogCollector.collect():
1017 self._ctx.call("setUpTestCase", [])
1018
1019 def __callTearDown(self):
1020 """Call the teardown function of the test if available."""
1021 # do not call tearDown after a failed setUp
1022 if self.__hasSetUpError:
1023 self.__hasSetUpError = False
1024 # in the virtual unload testcase the context is None
1025 elif self._ctx is not None and "tearDown" in self._ctx.callableFunctions():
1026 with self.__testLogger.collect():
1027 mevis.MLAB.log(Utils.lineSeparator())
1028 self._ctx.call("tearDown", [])
1029
1030 def __callTearDownTestCase(self):
1031 """Call the teardown function of the test case if available."""
1032 if self._ctx is not None and "tearDownTestCase" in self._ctx.callableFunctions():
1033 with self.__testLogger.collect():
1034 self._ctx.call("tearDownTestCase", [])
1035
1036 def __unloadTestCase(self):
1037 with self.__testLogger.collect():
1038 if self.__dummyTestSuite:
1039 # unittest support: clean up after running unittest.TestCases
1040 testResult = UnitTestResult()
1041 testResult._previousTestClass = self.__previousTestClass
1042 self.__dummyTestSuite._tearDownPreviousClass(None, testResult)
1043 self.__dummyTestSuite._handleModuleTearDown(testResult)
1044 testResult.logResult("")
1045 self.__previousTestClass = None
1046 self.__dummyTestSuite = None
1047 if TestSupport.Logging.gStopped:
1048 raise CancelTestException
1049 else:
1050 mevis.MLAB.log("Destroying test case context...")
1051 self.destroyContext()
1052
1053 def run(self, resultNode: etree.Element):
1054 """Run the test case and store results in the given xml data structure.
1055
1056 @param resultNode The node taking the results (call by reference).
1057 """
1058
1059 self.activate()
1060
1062
1063 logHandler = self._testHelper.getLogHandler()
1064
1065 availTestFunctions = self.getListOfTestFuncs()
1066
1067 # Build dict mapping test function names to xml nodes.
1068 funcList = resultNode.findall(".//Function")
1069 length = len(funcList)
1070
1071 self.__currentResultNode = resultNode
1072
1073 # Iterate over all test functions (sorted by name) that should be tested.
1074 # The iteration with an index is required to call setUpTestCase and
1075 # tearDownTestCase properly.
1076 for i, funcNode in enumerate(funcList):
1077 funcName = funcNode.get("name")
1078
1079 isLastTestFunction = i == length - 1
1080 data = RunTestFunctionData(funcNode, funcName, logHandler, isLastTestFunction)
1081
1082 if not self.__prepareRunTestFunction(data, availTestFunctions):
1083 continue
1084
1085 self.__runTestFunction(data)
1086 self.__finishRunTestFunction(data)
1087 if data.cancelled:
1088 # Do NOT call __tearDownTestCase() when 'Stop on error' flag is set and an error occurred.
1089 if not (TestSupport.Logging.gStopOnFirstError and logHandler.numErrors() > 0):
1091 self.__unloadTestCase()
1092 break
1093
1094 self.deactivate()
1095
1096 def __prepareRunTestFunction(self, data, availTestFunctions):
1098 funcNameForResultDir = data.funcName
1099 else:
1100 funcNameForResultDir = None
1101 self._testHelper.setTestCaseResultDirectory(self.getName().replace("::", os.sep), funcNameForResultDir)
1102
1103 shouldRunTestFunction = True
1104
1105 # Verify that a function with the given name exists.
1106 if data.funcName not in availTestFunctions and not Utils.isInternalTestCaseFunction(data.funcName):
1107 mevis.MLAB.logError(f"Function {data.funcName} does not exist in test case {self.getName()}!")
1108 status = Utils.TEST_STATUS_FUNCTION_NOT_FOUND
1109 data.funcNode.set("status", str(status))
1110 shouldRunTestFunction = False
1111 return shouldRunTestFunction
1112
1113 def __runTestFunction(self, data):
1114 data.started = time.time()
1115
1116 try:
1117 data.logHandler.startTestFunction(self.getType(), self.getName(), data.funcName)
1118 if self.__testCaseListener:
1119 self.__testCaseListener.startTestCaseFunction(self.getType(), self.getName(), data.funcName)
1120
1121 MLABpriv.clearLogState()
1122
1124
1125 if data.funcName == Utils.VIRTUAL_LOAD_TEST_CASE_TEST_FUNCTION:
1126 self.__loadLogCollector.logMessages(data.logHandler)
1127 elif data.funcName == Utils.SETUP_TEST_CASE_FUNCTION:
1128 self.__setupLogCollector.logMessages(data.logHandler)
1129 if data.funcName == Utils.TEARDOWN_TEST_CASE_FUNCTION:
1131 elif data.funcName == Utils.VIRTUAL_UNLOAD_TEST_CASE_TEST_FUNCTION:
1132 self.__unloadTestCase()
1133 if TestSupport.Logging.gStopped:
1134 raise CancelTestException
1135 except CancelTestException:
1136 data.cancelled = True
1137
1138 def __finishRunTestFunction(self, data):
1139 for extraTestCaseResult in self._testHelper.takeExtraTestCaseResults():
1140 self.addExtraTestCaseResult(extraTestCaseResult)
1141
1142 if not Utils.isInternalTestCaseFunction(data.funcName):
1143 self.__callTearDown()
1144
1145 match data.funcName:
1146 case Utils.VIRTUAL_LOAD_TEST_CASE_TEST_FUNCTION:
1147 currentLogger = self.__loadLogCollector
1148 case Utils.SETUP_TEST_CASE_FUNCTION:
1149 currentLogger = self.__setupLogCollector
1150 case _:
1151 currentLogger = self.__testLogger
1152
1153 # process events so that any delayed messages are also part of the test function
1154 mevis.MLAB.processEvents()
1155 # force messages from other threads to be printed inside of this test function execution
1156 MLABpriv.flushPendingMessages()
1157
1158 duration = time.time() - data.started
1159
1160 # Gather status and additional information.
1161 if currentLogger.hasErrors:
1162 status = Utils.TEST_STATUS_ERROR
1163 elif currentLogger.hasWarnings:
1164 status = Utils.TEST_STATUS_WARNING
1165 else:
1166 status = Utils.TEST_STATUS_OK
1167
1168 self.__testLogger.clearState()
1169 data.logHandler.stopTestFunction(status, duration)
1170 if self.__testCaseListener:
1171 self.__testCaseListener.endTestCaseFunction(status, duration)
1172
1173 if data.cancelled:
1174 status = Utils.TEST_STATUS_CANCELLED
1175 data.funcNode.set("status", str(status))
1176 data.funcNode.set("duration", str(duration))
1177 data.funcNode.append(data.logHandler.getMessages())
1178
1179 def __setupTestCaseAndRunTestFunction(self, data):
1180 if data.funcNode.get("is_disabled", str(False)) == str(False):
1181 self.__callSetUpTestCase()
1182 if data.funcName == Utils.SETUP_TEST_CASE_FUNCTION and TestSupport.Logging.gStopped:
1183 raise CancelTestException
1184 if not Utils.isInternalTestCaseFunction(data.funcName):
1185 self.__callSetUp()
1186 self.__hasSetUpError = MLABpriv.hadLogErrors()
1187 with self.__testLogger.collect():
1188 if self.__hasSetUpError:
1189 mevis.MLAB.log("Skipping test after error in setUp()")
1190 else:
1191 mevis.MLAB.log(f"Calling test function {data.funcName}()")
1192 self.__callTestFunction(data.funcName)
1193 else:
1194 mevis.MLAB.log(f"Skipping disabled test function {data.funcName}()")
1195
1196 def getDocumentation(self, functionName):
1197 """Get the documentation for the given test function.
1198 The found string will be modified such that all trailing whitespace will be removed.
1199
1200 @param functionName The function to gather documentation for.
1201 @return The function's documentation.
1202 """
1203 docu = self._ctx.scriptVariable(f"{functionName}.__doc__")
1204 testDocu = docu if docu else ""
1205 return re.sub(r"(?m)^\s+", "", testDocu)
Allows collecting log messages and logging them later.
Definition TestCase.py:103
__init__(self, funcNode, funcName, logHandler, isLastTestFunction)
Definition TestCase.py:57
The superclass for the different test case categories.
Definition TestCase.py:171
_callGroupFunction(self, groupFunctionName)
Definition TestCase.py:481
run(self, etree.Element resultNode)
Run the test case and store results in the given xml data structure.
Definition TestCase.py:1053
callFieldValueTestCase(self, filename, testcase)
Call the given field-value test case from the given field-value test case set.
Definition TestCase.py:445
_loadScriptFile(self, filename)
Load the test cases script file.
Definition TestCase.py:434
getName(self)
Return the name of the test case.
Definition TestCase.py:989
getPackage(self)
Return the identifier of the package the test case is in.
Definition TestCase.py:997
isValid(self)
Return True if the loaded test case is syntactically valid and should be executable.
Definition TestCase.py:957
getType(self)
Return the type of this test case (generic or functional).
Definition TestCase.py:981
activate(self)
Registers the test case at the TestHelper.
Definition TestCase.py:326
__createTestGroupNode(self, groupFunctionName, orderString, groupName, functionDict, isDisabled)
Definition TestCase.py:484
openNetwork(self)
Open the internal network of the test case.
Definition TestCase.py:975
__createVirtualTestFunction(self, basename, name, documentation, prepend)
Definition TestCase.py:300
__createUnitTestWrapperNode(self, virtualFunctionBaseName, functionName, groupName, functionDict, isDisabled)
Definition TestCase.py:728
getListOfTestFuncs(self)
Get the list of test functions in this test.
Definition TestCase.py:993
getModule(self)
Return the module representing this test case, or None.
Definition TestCase.py:985
__getFunctionInfo(self, functionBaseName, functionName, calledFunction=None, isDisabled=False, sourceName="")
Definition TestCase.py:942
getDocumentation(self, functionName)
Get the documentation for the given test function.
Definition TestCase.py:1196
getTestCaseNode(self)
Return the XML node with the test case's information.
Definition TestCase.py:1001
__createFieldValueTestNode(self, virtualFunctionBaseName, functionName, groupName, functionDict, isDisabled)
Definition TestCase.py:565
deactivate(self)
Resets the test helper.
Definition TestCase.py:338
openFiles(self)
Open the files associated with the test case.
Definition TestCase.py:969
_getTestCaseInfo(self, testCaseName)
Definition TestCase.py:313
__init__(self, testCaseName, isNewlyCreated=False)
Initialize the test case.
Definition TestCase.py:174
__createNormalTestNode(self, virtualFunctionName, functionName, functionBaseName, functionDict, isDisabled)
Definition TestCase.py:462
setTestCaseListener(self, testCaseListener)
Definition TestCase.py:348
addExtraTestCaseResult(self, extraTestCaseResult)
Definition TestCase.py:352
networkFileName(self)
Returns the absolute file name of the attached network or an empty string if no network is attached.
Definition TestCase.py:965
hasNetwork(self)
Return True if the test case has a network attached.
Definition TestCase.py:961
__createIterativeTestNode(self, virtualFunctionBaseName, functionName, groupName, functionDict, isDisabled)
Definition TestCase.py:642
_buildVirtualFunctions(self, isNewlyCreated)
Build an XML node with the virtual functions.
Definition TestCase.py:831
__prepareRunTestFunction(self, data, availTestFunctions)
Definition TestCase.py:1096
Allows logging messages via a given log handler and records the logging state.
Definition TestCase.py:139
A class collecting a set of field-value test cases.
printTestFunctionException()
Prints the exception that occurred during a test function.
Definition TestCase.py:46
Package to provide logging functions.
Definition Logging.py:1
setStopped(stopped)
Definition Logging.py:35
Adds GoogleTest like methods.
Definition Macros.py:1