27from contextlib
import contextmanager
33 UseStackFrameFromCallerForLogging,
34 SetLoggingCallerStackFrame,
44gEncodingRegExp = re.compile(
"^[ \t\v]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)")
54 encoding = gEncodingCache.get(filename)
58 with open(filename,
"rb")
as fh:
62 if bom == b
"\xef\xbb\xbf":
65 line = linecache.getline(filename, 0)
66 m = gEncodingRegExp.match(line)
68 line = linecache.getline(filename, 1)
69 m = gEncodingRegExp.match(line)
72 gEncodingCache[filename] = encoding
79 decodedLine = line.decode(encoding)
80 except UnicodeDecodeError:
81 decodedLine = line.decode(
"latin1")
105 return TestHelper.getInstance().getTestCaseContext()
125 frame = sys._getframe()
126 lastFunction = frame.f_code.co_name
128 if frame.f_code.co_name ==
"__callTestFunction":
129 class_ = frame.f_locals[
"self"].__class__
131 if hasattr(class_,
"_TestCase__callSetUpTestCase"):
133 lastFunction = frame.f_code.co_name
147 TestHelper.getInstance().setMacrosLogOnSuccess(logOnSuccess)
151 if logOnSuccess
is None:
152 return TestHelper.getInstance().getMacrosLogOnSuccess()
169 def wrapper(*args, **kwds):
170 logOnSuccess = TestHelper.getInstance().getMacrosLogOnSuccess()
173 r = func(*args, **kwds)
188 def wrapper(*args, **kwds):
190 expr, msg, comment, logOnSuccess = func(*args, **kwds)
191 stackLine = traceback.extract_stack(TestHelper.getInstance().getCallerStackFrame(), 1)[0]
192 codeLine = stackLine[3]
193 if isinstance(codeLine, bytes):
197 "%s: %s%s" % (codeLine, msg,
": %s" % (comment)
if comment
else ""), type=
"Compare", depth=1
202 "%s: %s%s" % (codeLine, msg,
": %s" % (comment)
if comment
else ""), type=
"Compare", depth=1
214 """Returns a unicode representation of value. If value is of type bytes then non-ASCII characters
215 are escaped and a representation of the string is returned.
216 For example, the 8-bit literal b'\x01' becomes 'u"\x01"'.
218 if isinstance(value, bytes):
219 return repr(str(value))
235 if hasattr(expr,
"all"):
239 f
"{escapedUnicode(a)} ({type(a).__name__}) == {escapedUnicode(b)} ({type(b).__name__})",
259 if hasattr(expr,
"any"):
263 f
"{escapedUnicode(a)} ({type(a).__name__}) != {escapedUnicode(b)} ({type(b).__name__})",
283 f
"{escapedUnicode(a)} ({type(a).__name__}) < {escapedUnicode(b)} ({type(b).__name__})",
303 f
"{escapedUnicode(a)} ({type(a).__name__}) > {escapedUnicode(b)} ({type(b).__name__})",
324 f
"{escapedUnicode(a)} ({type(a).__name__}) >= {escapedUnicode(b)} ({type(b).__name__})",
345 f
"{escapedUnicode(a)} ({type(a).__name__}) <= {escapedUnicode(b)} ({type(b).__name__})",
364 msg = f
"Comparing {str(a)} ({type(a).__name__}) == {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
365 return Math_compareFloatEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
381 msg = f
"Comparing {str(a)} ({type(a).__name__}) != {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
382 return not Math_compareFloatEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
397 msg = f
"Comparing {str(a)} ({type(a).__name__}) < {str(b)} ({type(b).__name__})"
398 return Math_compareFloatLessThan(a, b), msg, comment,
shouldLogOnSuccess(logOnSuccess)
413 msg = f
"Comparing {str(a)} ({type(a).__name__}) > {str(b)} ({type(b).__name__})"
414 return Math_compareFloatLessThan(b, a), msg, comment,
shouldLogOnSuccess(logOnSuccess)
431 msg = f
"Comparing {str(a)} ({type(a).__name__}) <= {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
432 return Math_compareFloatLessThanOrEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
449 msg = f
"Comparing {str(a)} ({type(a).__name__}) >= {str(b)} ({type(b).__name__}) with epsilon={epsilon}"
450 return Math_compareFloatLessThanOrEqual(b, a, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
463def verifyTrue(expr, comment="", logOnSuccess=None, msg=None):
468 msg = f
"Must evaluate to True: {escapedUnicode(expr)} ({type(expr).__name__})"
487 msg = f
"Must evaluate to False: {escapedUnicode(expr)} ({type(expr).__name__})"
507@UseStackFrameFromCallerForLogging
508def expectError(function, args=[], kargs={}, errorRegExp=None, preventReplacingMessageSeverity=False):
509 testHelper = TestHelper.getInstance()
510 logHandler = testHelper.getLogHandler()
511 assert not logHandler
is None
513 errors =
SuppressedErrors(errorRegExp, Logging_error, preventReplacingMessageSeverity)
515 retVal = function(*args, **kargs)
517 errors.handleResult()
535@UseStackFrameFromCallerForLogging
537 testHelper = TestHelper.getInstance()
538 logHandler = testHelper.getLogHandler()
539 assert not logHandler
is None
543 retVal = function(*args, **kargs)
545 warnings.handleResult()
563@UseStackFrameFromCallerForLogging
565 testHelper = TestHelper.getInstance()
566 logHandler = testHelper.getLogHandler()
567 assert not logHandler
is None
572 retVal = function(*args, **kargs)
574 errors.handleResult()
575 warnings.handleResult()
594@UseStackFrameFromCallerForLogging
595def ignoreError(function, args=(), kargs=
None, errorRegExp=
None, resultInfoDict=
None):
596 def ___doNotLog(_msg):
601 testHelper = TestHelper.getInstance()
602 logHandler = testHelper.getLogHandler()
604 assert not logHandler
is None
606 hadIgnoredErrors =
False
607 errors = SuppressedErrors(errorRegExp, logErrorFunc=
None)
609 retVal = function(*args, **kargs)
611 hadIgnoredErrors = errors.handleResult()
612 if isinstance(resultInfoDict, dict):
613 resultInfoDict[
"hadIgnoredErrors"] = hadIgnoredErrors
635@UseStackFrameFromCallerForLogging
636def ignoreWarning(function, args=(), kargs=
None, warningRegExp=
None, resultInfoDict=
None):
637 def ___doNotLog(_msg):
642 testHelper = TestHelper.getInstance()
643 logHandler = testHelper.getLogHandler()
645 assert not logHandler
is None
647 hadIgnoredWarnings =
False
648 warnings = SuppressedWarnings(warningRegExp, logErrorFunc=
None)
650 retVal = function(*args, **kargs)
652 hadIgnoredWarnings = warnings.handleResult()
653 if isinstance(resultInfoDict, dict):
654 resultInfoDict[
"hadIgnoredWarnings"] = hadIgnoredWarnings
676@UseStackFrameFromCallerForLogging
678 testHelper = TestHelper.getInstance()
679 logHandler = testHelper.getLogHandler()
680 assert not logHandler
is None
685 retVal = function(*args, **kargs)
687 hadIgnoredErrors = errors.handleResult()
688 hadIgnoredWarnings = warnings.handleResult()
689 if isinstance(resultInfoDict, dict):
690 resultInfoDict[
"hadIgnoredWarnings"] = hadIgnoredWarnings
691 resultInfoDict[
"hadIgnoredErrors"] = hadIgnoredErrors
709@UseStackFrameFromCallerForLogging
710def expectInfo(function, args=[], kargs={}, infoRegExp=None, allowUnexpectedMessages=True):
711 testHelper = TestHelper.getInstance()
712 logHandler = testHelper.getLogHandler()
713 assert not logHandler
is None
715 expectInfos =
ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
717 retVal = function(*args, **kargs)
719 expectInfos.handleResult()
731@UseStackFrameFromCallerForLogging
733 """Expect that the expression in the context block issues no warning that matches `warningRegExp`.
734 If `warningRegExp` is not given, no warning is expected at all.
744 warnings.handleResult()
748@UseStackFrameFromCallerForLogging
750 """Expect that the expression in the context block logs a warning that matches `warningRegExp`.
751 If `warningRegExp` is not given, any warning is expected.
761 warnings.handleResult()
765@UseStackFrameFromCallerForLogging
767 """Expect that the expression in the context logs an error that matches `errorRegExp`.
768 If `errorRegExp` is not given, any error is expected.
778 errors.handleResult()
782@UseStackFrameFromCallerForLogging
784 """Expect that the expression in the context logs errors and warnings.
785 See `hasError` and `hasWarning` for the arguments.
788 with hasWarningAndError():
796 errors.handleResult()
797 warnings.handleResult()
801@UseStackFrameFromCallerForLogging
802def hasInfo(infoRegExp=None, allowUnexpectedMessages=True):
803 """Expect that the expression in the context logs an info entry that matches `infoRegExp`.
804 If `infoRegExp` is not given, any info message is expected.
805 If `allowUnexpectedMessages` is set to True (the default), messages that do not match `infoRegExp` are ignored,
806 otherwise they are considered a failure.
809 with hasInfo(infoRegExp=".*Unknown module.*"):
812 expectInfos =
ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
816 expectInfos.handleResult()
828 return TestHelper.getInstance().getTestCaseResultDirectory()
842 return TestHelper.getInstance().getTestCaseDataDirectory()
852 return os.path.join(getDataDirectory(), filename)
879 testHelper = TestHelper.getInstance()
880 testCaseContext = testHelper.getTestCaseContext()
881 externalDataVariable =
"TESTCENTER_EXTERNAL_DATA_%s" % name
882 externalDataPlaceholder =
"$(%s)" % externalDataVariable
883 externalDataBaseDirectory = testCaseContext.expandFilename(externalDataPlaceholder)
884 if externalDataBaseDirectory == externalDataPlaceholder:
886 "Variable %s could not be expanded. Please define it in your .prefs file." % externalDataVariable
888 if os.path.isdir(externalDataBaseDirectory):
890 return os.path.join(externalDataBaseDirectory, subDirectory)
892 testCasePath = testCaseContext.localPath()
893 path = mevis.MLABPackageManager.findPackageIdentifierAndRelativePath(testCasePath)[1]
894 return os.path.join(externalDataBaseDirectory, path)
895 raise ValueError(
'"%s" is not a directory.' % externalDataBaseDirectory)
916 if TestHelper.getInstance().getChangeSetStackLength() > 2:
934def getHash(filename, hash="SHA1", encoder="Hex"):
937 b, ext = os.path.splitext(filename)
952 result = Image_calculateHashFromImage(filename, hash, encoder)
955 h = hashlib.new(hash)
956 with open(filename,
"rb")
as f:
959 result = h.hexdigest()
960 elif encoder ==
"Base64":
963 Logging_error(
"Unknown encoder (%s) selected." % (encoder))
964 except Exception
as e:
965 Logging_error(
"Failed to generate hash: %s." % e)
990 showTestFunctionSortingLiterals=False,
993 testCaseName, package, author, duration, maintainer, file, line, comment, showTestFunctionSortingLiterals
1037 def wrapper(*args, **kwds):
1041 result = func(*args, **kwds)
1053 def wrapper(*args, **kwds):
1054 wereHighPrecisionTimeStampsEnabled = mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(
True)
1056 result = func(*args, **kwds)
1058 mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(wereHighPrecisionTimeStampsEnabled)
1067 table.diff {font-family:monospace;border:medium;margin:0;}
1068 .diff_header {background-color:#e0e0e0}
1069 td.diff_header {text-align:right}
1070 .diff_next {background-color:#c0c0c0}
1071 .diff_add {background-color:#aaffaa}
1072 .diff_chg {background-color:#ffff77}
1073 .diff_sub {background-color:#ffaaaa}
1074 tr, td, th {vertical-align:top;}
1076 white-space: -pre-wrap;
1077 white-space: -o-pre-wrap;
1078 white-space: -moz-pre-wrap;
1079 white-space: pre-wrap;
1080 word-wrap: break-word;
1083 _table_template =
"""
1084 <table class="diff" id="difflib_chg_%(prefix)s_top"
1085 cellspacing="0" cellpadding="1" rules="groups" width="100%%">
1086 <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
1087 <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
1090%(data_rows)s </tbody>
1094 difflib.HtmlDiff.__init__(self)
1098 linenum =
"%d" % linenum
1099 id =
' id="%s%s"' % (self._prefix[side], linenum)
1104 text = text.replace(
"&",
"&").replace(
">",
">").replace(
"<",
"<")
1105 if len(text) == 0
or text ==
"\n":
1106 text =
'<span style="display: block; background-color:#e0e0e0;">' + text +
"</span>"
1107 return '<td class="diff_header"%s>%s</td><td>%s</td>' % (id, linenum, text)
1120def createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10):
1121 diff =
HtmlDiff().make_file(fromLines, toLines,
"",
"", showOnlyContextOfDiff, numberOfContextLines)
1130 def MLABTC__disableTestFunctionIf_Decorator(func):
1131 disableTestFunction = condition()
if isinstance(condition, collections.abc.Callable)
else condition
1132 if disableTestFunction:
1133 module = sys.modules[func.__module__]
1134 if not hasattr(module,
"MLABTC_DISABLED_TEST_FUNCTIONS"):
1135 setattr(module,
"MLABTC_DISABLED_TEST_FUNCTIONS", [])
1136 module.MLABTC_DISABLED_TEST_FUNCTIONS.append(func.__name__)
1139 return MLABTC__disableTestFunctionIf_Decorator
1144 context = TestHelper.getInstance().getHelperContext()
1145 return context.field(
"oSSOffscreenRender.supported").value
1161def runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None):
1162 Macros_INFO(
"Running: " +
" ".join(command))
1166 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
1168 p = subprocess.Popen(command, env=env)
1172 if len(os.path.splitext(c)[1]) > 0:
1173 c = os.path.basename(c)
1174 shortCommand.append(c)
1175 shortCommand =
" ".join(shortCommand)
1176 stdout, stderr = p.communicate()
1177 stdout = stdout.strip()
1178 stderr = stderr.strip()
1179 stderr = stderr.decode(
"utf-8",
"replace")
1180 stdout = stdout.decode(
"utf-8",
"replace")
1182 " ".join(command), shortCommand, p.returncode, stdout, stderr, expectSuccess, expectFailure, verbose
1184 return stdout, stderr, p.returncode
1201 command, shortCommand, returncode, stdout, stderr, expectSuccess=True, expectFailure=False, verbose=True
1205 Macros_ERROR(
"Command returned non-zero: '" + command +
"', return code: " + str(returncode))
1207 Macros_ERROR(stderr)
1209 Macros_ERROR(stdout)
1211 msg =
"Success: " + shortCommand
1214 msg +=
"\n\n" + stderr
1216 msg +=
"\n\n" + stdout
1220 Macros_ERROR(
"Command returned zero: '" + command +
"'")
1222 Macros_ERROR(stderr)
1224 Macros_ERROR(stdout)
1226 msg =
"Expected failure: " + shortCommand
1229 msg +=
"\n\n" + stderr
1231 msg +=
"\n\n" + stdout
1247def runPythonScript(script, arguments, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None):
1248 python = mevis.MLABFileManager.getExecutable(
"MeVisPython")
1249 command = [python,
"-u", script]
1250 command.extend(arguments)
1252 command, wait=wait, expectSuccess=expectSuccess, expectFailure=expectFailure, verbose=verbose, env=env
1256from .ExtraTestCaseResult
import ExtraTestCaseResult
1257from .Image
import calculateHashFromImage
as Image_calculateHashFromImage
1258from .Logging
import error
as Logging_error, warning
as Logging_warning, info
as Logging_info
1259from .Macros
import ERROR
as Macros_ERROR, INFO
as Macros_INFO
1261 compareFloatEqual
as Math_compareFloatEqual,
1262 compareFloatLessThan
as Math_compareFloatLessThan,
1263 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)
EnableHighPrecisionLoggingTimeStampsDecorator(func)
getHash(filename, hash="SHA1", encoder="Hex")
Compute a hash for the content of the given file.
compareFloatLessThanOrEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None)
Compare the two given float values if the first is less than or equal to the second.
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.
hasInfo(infoRegExp=None, allowUnexpectedMessages=True)
hasWarningAndError(errorRegExp=None, warningRegExp=None)
getDataDirectory()
Method to return path to the data files.
compareGreaterThan(a, b, comment="", logOnSuccess=None)
Compare the two given values if the first one is greater than the second.
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.
compareNotEqual(a, b, comment="", logOnSuccess=None)
Compare the two given values for inequality.
disableTestFunctionIf(condition)
runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None)
hasNoWarning(warningRegExp=None)
getPackageList()
Returns the list of available packages.
compareFloatEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None)
Compare the two given float values if they are epsilon-equal.
existsFileInDataDirectory(filename)
Checks if the given file exists in the data directory.
ignoreWarning(function, args=(), kargs=None, warningRegExp=None, resultInfoDict=None)
Call the given function with the given parameters and ignores potential warnings.
getModuleName()
Return the name of the currently tested module.
compareLessThanOrEqual(a, b, comment="", logOnSuccess=None)
Compare the two given values if the first one is less than or equal to the second.
expectWarning(function, args=[], kargs={}, warningRegExp=None)
Call the given function with the given parameters and expect a warning.
expectWarningAndError(function, args=[], kargs={}, errorRegExp=None, warningRegExp=None)
Call the given function with the given parameters and expect both a warning and an error.
shouldLogOnSuccess(logOnSuccess=None)
getTestCaseContext()
Get the context of the test.
compareFloatLessThan(a, b, comment="", logOnSuccess=None)
Compare the two given float values if the first is less than the second.
getFileInDataDirectory(filename)
Returns the absolute filename of a file in the data directory using getDataDirectory().
compareEqual(a, b, comment="", logOnSuccess=None)
Compare the two given values for equality.
compareLessThan(a, b, comment="", logOnSuccess=None)
Compare the two given values if the first one is less than the second.
verifyTrue(expr, comment="", logOnSuccess=None, msg=None)
If the given expression evaluates to False, an error is logged.
hasError(errorRegExp=None)
createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10)
getTestFunctionName()
Returns the name of the current test function.
ignoreWarningAndError(function, args=[], kargs={}, errorRegExp=None, warningRegExp=None, resultInfoDict=None)
Call the given function with the given parameters and ignore both a warning and an error.
createExtraTestCaseResult(testCaseName, package, author, duration, maintainer=None, file=None, line=None, comment=None, showTestFunctionSortingLiterals=False)
Creates an ExtraTestCaseResult object.
compareFloatGreaterThan(a, b, comment="", logOnSuccess=None)
Compare the two given float values if the first is greater than the second.
LoggingDecorator(func)
Internal decorator used for creating messages in compare methods.
ignoreError(function, args=(), kargs=None, errorRegExp=None, resultInfoDict=None)
Call the given function with the given parameters and ignores potential errors.
expectError(function, args=[], kargs={}, errorRegExp=None, preventReplacingMessageSeverity=False)
Call the given function with the given parameters and expect an error.
getExternalDataDirectory(name, subDirectory=None)
Method to return path to the external data directory.
hasWarning(warningRegExp=None)
compareFloatNotEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None)
Compare the two given float values if they are epsilon-unequal.
decodeSourceCodeLine(filename, line)
verifyFalse(expr, comment="", logOnSuccess=None, msg=None)
If the given expression evaluates to True, an error is logged.
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...
compareGreaterThanOrEqual(a, b, comment="", logOnSuccess=None)
Compare the two given values if the first one is greater than or equal to the second.
pushEnvironmentDecorator(func)
expectInfo(function, args=[], kargs={}, infoRegExp=None, allowUnexpectedMessages=True)
Call the given function with the given parameters and expect an info message.
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)