27from contextlib
import contextmanager
33 UseStackFrameFromCallerForLogging,
34 SetLoggingCallerStackFrame,
43gEncodingRegExp = re.compile(
"^[ \t\v]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)")
53 encoding = gEncodingCache.get(filename)
57 with open(filename,
"rb")
as fh:
61 if bom == b
"\xef\xbb\xbf":
64 line = linecache.getline(filename, 0)
65 m = gEncodingRegExp.match(line)
67 line = linecache.getline(filename, 1)
68 m = gEncodingRegExp.match(line)
71 gEncodingCache[filename] = encoding
78 decodedLine = line.decode(encoding)
79 except UnicodeDecodeError:
80 decodedLine = line.decode(
"latin1")
124 frame = sys._getframe()
125 lastFunction = frame.f_code.co_name
127 if frame.f_code.co_name ==
"__callTestFunction":
128 class_ = frame.f_locals[
"self"].__class__
130 if hasattr(class_,
"_TestCase__callSetUpTestCase"):
132 lastFunction = frame.f_code.co_name
146 TestHelper.getInstance().setMacrosLogOnSuccess(logOnSuccess)
150 if logOnSuccess
is None:
151 return TestHelper.getInstance().getMacrosLogOnSuccess()
168 def wrapper(*args, **kwds):
169 logOnSuccess = TestHelper.getInstance().getMacrosLogOnSuccess()
172 r = func(*args, **kwds)
187 def wrapper(*args, **kwds):
189 expr, msg, comment, logOnSuccess = func(*args, **kwds)
190 stackLine = traceback.extract_stack(TestHelper.getInstance().getCallerStackFrame(), 1)[0]
191 codeLine = stackLine[3]
192 if isinstance(codeLine, bytes):
196 "%s: %s%s" % (codeLine, msg,
": %s" % (comment)
if comment
else ""), type=
"Compare", depth=1
201 "%s: %s%s" % (codeLine, msg,
": %s" % (comment)
if comment
else ""), type=
"Compare", depth=1
213 """Returns a unicode representation of value. If value is of type bytes then non-ASCII characters
214 are escaped and a representation of the string is returned.
215 For example, the 8-bit literal b'\x01' becomes 'u"\x01"'.
217 if isinstance(value, bytes):
218 return repr(str(value))
232def compareEqual(a, b, comment="", logOnSuccess=None):
234 if hasattr(expr,
"all"):
238 f
"{escapedUnicode(a)} ({type(a).__name__}) == {escapedUnicode(b)} ({type(b).__name__})",
256def compareNotEqual(a, b, comment="", logOnSuccess=None):
258 if hasattr(expr,
"any"):
262 f
"{escapedUnicode(a)} ({type(a).__name__}) != {escapedUnicode(b)} ({type(b).__name__})",
279def compareLessThan(a, b, comment="", logOnSuccess=None):
282 f
"{escapedUnicode(a)} ({type(a).__name__}) < {escapedUnicode(b)} ({type(b).__name__})",
299def compareGreaterThan(a, b, comment="", logOnSuccess=None):
302 f
"{escapedUnicode(a)} ({type(a).__name__}) > {escapedUnicode(b)} ({type(b).__name__})",
320def compareGreaterThanOrEqual(a, b, comment="", logOnSuccess=None):
323 f
"{escapedUnicode(a)} ({type(a).__name__}) >= {escapedUnicode(b)} ({type(b).__name__})",
341def compareLessThanOrEqual(a, b, comment="", logOnSuccess=None):
344 f
"{escapedUnicode(a)} ({type(a).__name__}) <= {escapedUnicode(b)} ({type(b).__name__})",
362def compareFloatEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None):
363 msg = f
"Comparing {str(a)} ({type(a).__name__}) == {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
364 return Math_compareFloatEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
379def compareFloatNotEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None):
380 msg = f
"Comparing {str(a)} ({type(a).__name__}) != {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
381 return not Math_compareFloatEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
395def compareFloatLessThan(a, b, comment="", logOnSuccess=None):
396 msg = f
"Comparing {str(a)} ({type(a).__name__}) < {str(b)} ({type(b).__name__})"
397 return Math_compareFloatLessThan(a, b), msg, comment,
shouldLogOnSuccess(logOnSuccess)
411def compareFloatGreaterThan(a, b, comment="", logOnSuccess=None):
412 msg = f
"Comparing {str(a)} ({type(a).__name__}) > {str(b)} ({type(b).__name__})"
413 return Math_compareFloatLessThan(b, a), msg, comment,
shouldLogOnSuccess(logOnSuccess)
429def compareFloatLessThanOrEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None):
430 msg = f
"Comparing {str(a)} ({type(a).__name__}) <= {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
431 return Math_compareFloatLessThanOrEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
448 msg = f
"Comparing {str(a)} ({type(a).__name__}) >= {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
449 return Math_compareFloatLessThanOrEqual(b, a, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
462def verifyTrue(expr, comment="", logOnSuccess=None, msg=None):
467 msg = f
"Must evaluate to True: {escapedUnicode(expr)} ({type(expr).__name__})"
481def verifyFalse(expr, comment="", logOnSuccess=None, msg=None):
486 msg = f
"Must evaluate to False: {escapedUnicode(expr)} ({type(expr).__name__})"
506@UseStackFrameFromCallerForLogging
507def expectError(function, args=(), kwargs=
None, errorRegExp=
None, preventReplacingMessageSeverity=
False, *, kargs=
None):
508 testHelper = TestHelper.getInstance()
509 logHandler = testHelper.getLogHandler()
510 assert logHandler
is not None
512 errors =
SuppressedErrors(errorRegExp, Logging_error, preventReplacingMessageSeverity)
514 return function(*args, **(kwargs
or kargs
or {}))
516 errors.handleResult()
532@UseStackFrameFromCallerForLogging
533def expectWarning(function, args=(), kwargs=
None, warningRegExp=
None, *, kargs=
None):
534 testHelper = TestHelper.getInstance()
535 logHandler = testHelper.getLogHandler()
536 assert not logHandler
is None
540 return function(*args, **(kwargs
or kargs
or {}))
542 warnings.handleResult()
558@UseStackFrameFromCallerForLogging
560 testHelper = TestHelper.getInstance()
561 logHandler = testHelper.getLogHandler()
562 assert not logHandler
is None
567 return function(*args, **(kwargs
or kargs
or {}))
569 errors.handleResult()
570 warnings.handleResult()
587@UseStackFrameFromCallerForLogging
588def ignoreError(function, args=(), kwargs=
None, errorRegExp=
None, resultInfoDict=
None, *, kargs=
None):
589 testHelper = TestHelper.getInstance()
590 logHandler = testHelper.getLogHandler()
592 assert not logHandler
is None
596 return function(*args, **(kwargs
or kargs
or {}))
598 hadIgnoredErrors = errors.handleResult()
599 if isinstance(resultInfoDict, dict):
600 resultInfoDict[
"hadIgnoredErrors"] = hadIgnoredErrors
620@UseStackFrameFromCallerForLogging
621def ignoreWarning(function, args=(), kwargs=
None, warningRegExp=
None, resultInfoDict=
None, *, kargs=
None):
622 testHelper = TestHelper.getInstance()
623 logHandler = testHelper.getLogHandler()
625 assert not logHandler
is None
629 return function(*args, **(kwargs
or kargs
or {}))
631 hadIgnoredWarnings = warnings.handleResult()
632 if isinstance(resultInfoDict, dict):
633 resultInfoDict[
"hadIgnoredWarnings"] = hadIgnoredWarnings
653@UseStackFrameFromCallerForLogging
655 function, args=(), kwargs=
None, errorRegExp=
None, warningRegExp=
None, resultInfoDict=
None, *, kargs=
None
657 testHelper = TestHelper.getInstance()
658 logHandler = testHelper.getLogHandler()
659 assert not logHandler
is None
664 return function(*args, **(kwargs
or kargs
or {}))
666 hadIgnoredErrors = errors.handleResult()
667 hadIgnoredWarnings = warnings.handleResult()
668 if isinstance(resultInfoDict, dict):
669 resultInfoDict[
"hadIgnoredWarnings"] = hadIgnoredWarnings
670 resultInfoDict[
"hadIgnoredErrors"] = hadIgnoredErrors
686@UseStackFrameFromCallerForLogging
687def expectInfo(function, args=(), kwargs=
None, infoRegExp=
None, allowUnexpectedMessages=
True, *, kargs=
None):
688 testHelper = TestHelper.getInstance()
689 logHandler = testHelper.getLogHandler()
690 assert not logHandler
is None
692 expectInfos =
ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
694 return function(*args, **(kwargs
or kargs
or {}))
696 expectInfos.handleResult()
706@UseStackFrameFromCallerForLogging
708 """Expect that the expression in the context block issues no warning that matches `warningRegExp`.
709 If `warningRegExp` is not given, no warning is expected at all.
719 warnings.handleResult()
723@UseStackFrameFromCallerForLogging
725 """Expect that the expression in the context block logs a warning that matches `warningRegExp`.
726 If `warningRegExp` is not given, any warning is expected.
736 warnings.handleResult()
740@UseStackFrameFromCallerForLogging
742 """Expect that the expression in the context logs an error that matches `errorRegExp`.
743 If `errorRegExp` is not given, any error is expected.
753 errors.handleResult()
757@UseStackFrameFromCallerForLogging
759 """Expect that the expression in the context logs errors and warnings.
760 See `hasError` and `hasWarning` for the arguments.
763 with hasWarningAndError():
771 errors.handleResult()
772 warnings.handleResult()
776@UseStackFrameFromCallerForLogging
777def hasInfo(infoRegExp=None, allowUnexpectedMessages=True):
778 """Expect that the expression in the context logs an info entry that matches `infoRegExp`.
779 If `infoRegExp` is not given, any info message is expected.
780 If `allowUnexpectedMessages` is set to True (the default), messages that do not match `infoRegExp` are ignored,
781 otherwise they are considered a failure.
784 with hasInfo(infoRegExp=".*Unknown module.*"):
787 expectInfos =
ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
791 expectInfos.handleResult()
803 return TestHelper.getInstance().getTestCaseResultDirectory()
817 return TestHelper.getInstance().getTestCaseDataDirectory()
854 testHelper = TestHelper.getInstance()
855 testCaseContext = testHelper.getTestCaseContext()
856 externalDataVariable =
"TESTCENTER_EXTERNAL_DATA_%s" % name
857 externalDataPlaceholder =
"$(%s)" % externalDataVariable
858 externalDataBaseDirectory = testCaseContext.expandFilename(externalDataPlaceholder)
859 if externalDataBaseDirectory == externalDataPlaceholder:
861 "Variable %s could not be expanded. Please define it in your .prefs file." % externalDataVariable
863 if os.path.isdir(externalDataBaseDirectory):
865 return os.path.join(externalDataBaseDirectory, subDirectory)
867 testCasePath = testCaseContext.localPath()
868 path = mevis.MLABPackageManager.findPackageIdentifierAndRelativePath(testCasePath)[1]
869 return os.path.join(externalDataBaseDirectory, path)
870 raise ValueError(
'"%s" is not a directory.' % externalDataBaseDirectory)
890 if TestHelper.getInstance().getChangeSetStackLength() > 2:
906def getHash(filename, hash="SHA1", encoder="Hex"):
909 b, ext = os.path.splitext(filename)
924 result = Image_calculateHashFromImage(filename, hash, encoder)
927 h = hashlib.new(hash)
928 with open(filename,
"rb")
as f:
931 result = h.hexdigest()
932 elif encoder ==
"Base64":
935 Logging_error(
"Unknown encoder (%s) selected." % (encoder))
936 except Exception
as e:
937 Logging_error(
"Failed to generate hash: %s." % e)
962 showTestFunctionSortingLiterals=False,
965 testCaseName, package, author, duration, maintainer, file, line, comment, showTestFunctionSortingLiterals
1009 def wrapper(*args, **kwds):
1013 result = func(*args, **kwds)
1025 def wrapper(*args, **kwds):
1026 wereHighPrecisionTimeStampsEnabled = mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(
True)
1028 result = func(*args, **kwds)
1030 mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(wereHighPrecisionTimeStampsEnabled)
1039 table.diff {font-family:monospace;border:medium;margin:0;}
1040 .diff_header {background-color:#e0e0e0}
1041 td.diff_header {text-align:right}
1042 .diff_next {background-color:#c0c0c0}
1043 .diff_add {background-color:#aaffaa}
1044 .diff_chg {background-color:#ffff77}
1045 .diff_sub {background-color:#ffaaaa}
1046 tr, td, th {vertical-align:top;}
1048 white-space: -pre-wrap;
1049 white-space: -o-pre-wrap;
1050 white-space: -moz-pre-wrap;
1051 white-space: pre-wrap;
1052 word-wrap: break-word;
1055 _table_template =
"""
1056 <table class="diff" id="difflib_chg_%(prefix)s_top"
1057 cellspacing="0" cellpadding="1" rules="groups" width="100%%">
1058 <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
1059 <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
1062%(data_rows)s </tbody>
1066 difflib.HtmlDiff.__init__(self)
1070 linenum =
"%d" % linenum
1071 id =
' id="%s%s"' % (self._prefix[side], linenum)
1076 text = text.replace(
"&",
"&").replace(
">",
">").replace(
"<",
"<")
1077 if len(text) == 0
or text ==
"\n":
1078 text =
'<span style="display: block; background-color:#e0e0e0;">' + text +
"</span>"
1079 return '<td class="diff_header"%s>%s</td><td>%s</td>' % (id, linenum, text)
1092def createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10):
1093 diff =
HtmlDiff().make_file(fromLines, toLines,
"",
"", showOnlyContextOfDiff, numberOfContextLines)
1102 def MLABTC__disableTestFunctionIf_Decorator(func):
1103 disableTestFunction = condition()
if isinstance(condition, collections.abc.Callable)
else condition
1104 if disableTestFunction:
1105 module = sys.modules[func.__module__]
1106 if not hasattr(module,
"MLABTC_DISABLED_TEST_FUNCTIONS"):
1107 setattr(module,
"MLABTC_DISABLED_TEST_FUNCTIONS", [])
1108 module.MLABTC_DISABLED_TEST_FUNCTIONS.append(func.__name__)
1111 return MLABTC__disableTestFunctionIf_Decorator
1116 context = TestHelper.getInstance().getHelperContext()
1117 return context.field(
"oSSOffscreenRender.supported").value
1133def runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None):
1134 Macros_INFO(
"Running: " +
" ".join(command))
1138 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
1140 p = subprocess.Popen(command, env=env)
1144 if len(os.path.splitext(c)[1]) > 0:
1145 c = os.path.basename(c)
1146 shortCommand.append(c)
1147 shortCommand =
" ".join(shortCommand)
1148 stdout, stderr = p.communicate()
1149 stdout = stdout.strip()
1150 stderr = stderr.strip()
1151 stderr = stderr.decode(
"utf-8",
"replace")
1152 stdout = stdout.decode(
"utf-8",
"replace")
1154 " ".join(command), shortCommand, p.returncode, stdout, stderr, expectSuccess, expectFailure, verbose
1156 return stdout, stderr, p.returncode
1173 command, shortCommand, returncode, stdout, stderr, expectSuccess=True, expectFailure=False, verbose=True
1177 Macros_ERROR(
"Command returned non-zero: '" + command +
"', return code: " + str(returncode))
1179 Macros_ERROR(stderr)
1181 Macros_ERROR(stdout)
1183 msg =
"Success: " + shortCommand
1186 msg +=
"\n\n" + stderr
1188 msg +=
"\n\n" + stdout
1192 Macros_ERROR(
"Command returned zero: '" + command +
"'")
1194 Macros_ERROR(stderr)
1196 Macros_ERROR(stdout)
1198 msg =
"Expected failure: " + shortCommand
1201 msg +=
"\n\n" + stderr
1203 msg +=
"\n\n" + stdout
1219def runPythonScript(script, arguments, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None):
1220 python = mevis.MLABFileManager.getExecutable(
"MeVisPython")
1221 command = [python,
"-u", script]
1222 command.extend(arguments)
1224 command, wait=wait, expectSuccess=expectSuccess, expectFailure=expectFailure, verbose=verbose, env=env
1228from .ExtraTestCaseResult
import ExtraTestCaseResult
1229from .Image
import calculateHashFromImage
as Image_calculateHashFromImage
1230from .Logging
import error
as Logging_error, warning
as Logging_warning, info
as Logging_info
1231from .Macros
import ERROR
as Macros_ERROR, INFO
as Macros_INFO
1233 compareFloatEqual
as Math_compareFloatEqual,
1234 compareFloatLessThan
as Math_compareFloatLessThan,
1235 compareFloatLessThanOrEqual
as Math_compareFloatLessThanOrEqual,
_format_line(self, side, flag, linenum, text)
Decorator to globally enable or disable if the ASSERT_*/EXPECT_* macros log an info message on succes...
__init__(self, logOnSuccess)
Helper object to supress warnings.
expectWarningAndError(function, args=(), kwargs=None, errorRegExp=None, warningRegExp=None, *kargs=None)
Call the given function with the given parameters and expect both a warning and an error.
EnableHighPrecisionLoggingTimeStampsDecorator(func)
verifyProcessResult(command, shortCommand, returncode, stdout, stderr, expectSuccess=True, expectFailure=False, verbose=True)
addExtraTestCaseResult(extraTestCaseResult)
Adds an ExtraTestCaseResult object.
runPythonScript(script, arguments, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None)
compareFloatGreaterThanOrEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None)
Compare the two given float values if the first is greater than or equal to the second.
ignoreWarningAndError(function, args=(), kwargs=None, errorRegExp=None, warningRegExp=None, resultInfoDict=None, *kargs=None)
Call the given function with the given parameters and ignore both a warning and an error.
escapedUnicode(value)
Returns a unicode representation of value.
hasInfo(infoRegExp=None, allowUnexpectedMessages=True)
Expect that the expression in the context logs an info entry that matches infoRegExp.
ignoreError(function, args=(), kwargs=None, errorRegExp=None, resultInfoDict=None, *kargs=None)
Call the given function with the given parameters and ignores potential errors.
hasWarningAndError(errorRegExp=None, warningRegExp=None)
Expect that the expression in the context logs errors and warnings.
getDataDirectory()
Method to return path to the data files.
setMacrosShouldLogOnSuccess(logOnSuccess)
Globally enables or disables if the ASSERT_*/EXPECT_* macros log an info message on success.
getResultDirectory()
Getter for the directory used to save results.
disableTestFunctionIf(condition)
runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None)
hasNoWarning(warningRegExp=None)
Expect that the expression in the context block issues no warning that matches warningRegExp.
ignoreWarning(function, args=(), kwargs=None, warningRegExp=None, resultInfoDict=None, *kargs=None)
Call the given function with the given parameters and ignores potential warnings.
getPackageList()
Returns the list of available packages.
existsFileInDataDirectory(filename)
Checks if the given file exists in the data directory.
expectError(function, args=(), kwargs=None, errorRegExp=None, preventReplacingMessageSeverity=False, *kargs=None)
Call the given function with the given parameters and expect an error.
getModuleName()
Return the name of the currently tested module.
shouldLogOnSuccess(logOnSuccess=None)
getTestCaseContext()
Get the context of the test.
getFileInDataDirectory(filename)
Returns the absolute filename of a file in the data directory using getDataDirectory().
hasError(errorRegExp=None)
Expect that the expression in the context logs an error that matches errorRegExp.
getTestFunctionName()
Returns the name of the current test function.
createExtraTestCaseResult(testCaseName, package, author, duration, maintainer=None, file=None, line=None, comment=None, showTestFunctionSortingLiterals=False)
Creates an ExtraTestCaseResult object.
LoggingDecorator(func)
Internal decorator used for creating messages in compare methods.
expectInfo(function, args=(), kwargs=None, infoRegExp=None, allowUnexpectedMessages=True, *kargs=None)
Call the given function with the given parameters and expect an info message.
getExternalDataDirectory(name, subDirectory=None)
Method to return path to the external data directory.
hasWarning(warningRegExp=None)
Expect that the expression in the context block logs a warning that matches warningRegExp.
expectWarning(function, args=(), kwargs=None, warningRegExp=None, *kargs=None)
Call the given function with the given parameters and expect a warning.
decodeSourceCodeLine(filename, line)
pushChangeSet()
Push a new ChangeSet to the stack.
hardwareSupportsOffscreenRendering()
pushEnvironment()
Pushes the current environment dictionary on a stack, so that modifications can be applied and be pop...
pushEnvironmentDecorator(func)
popEnvironment()
Pops the last pushed environment dictionary from a stack and makes it the current one.
popChangeSet()
Pop the last ChangeSet from the stack.
getSourceFileEncoding(filename)