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))
234 if hasattr(expr,
"all"):
238 f
"{escapedUnicode(a)} ({type(a).__name__}) == {escapedUnicode(b)} ({type(b).__name__})",
258 if hasattr(expr,
"any"):
262 f
"{escapedUnicode(a)} ({type(a).__name__}) != {escapedUnicode(b)} ({type(b).__name__})",
282 f
"{escapedUnicode(a)} ({type(a).__name__}) < {escapedUnicode(b)} ({type(b).__name__})",
302 f
"{escapedUnicode(a)} ({type(a).__name__}) > {escapedUnicode(b)} ({type(b).__name__})",
323 f
"{escapedUnicode(a)} ({type(a).__name__}) >= {escapedUnicode(b)} ({type(b).__name__})",
344 f
"{escapedUnicode(a)} ({type(a).__name__}) <= {escapedUnicode(b)} ({type(b).__name__})",
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)
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)
396 msg = f
"Comparing {str(a)} ({type(a).__name__}) < {str(b)} ({type(b).__name__})"
397 return Math_compareFloatLessThan(a, b), msg, comment,
shouldLogOnSuccess(logOnSuccess)
412 msg = f
"Comparing {str(a)} ({type(a).__name__}) > {str(b)} ({type(b).__name__})"
413 return Math_compareFloatLessThan(b, a), msg, comment,
shouldLogOnSuccess(logOnSuccess)
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__})"
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.
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.
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.
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.
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.
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.
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.
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.
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.
shouldLogOnSuccess(logOnSuccess=None)
expectError(function, args=(), kwargs=None, errorRegExp=None, preventReplacingMessageSeverity=False, *, kargs=None)
Call the given function with the given parameters and expect an error.
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.
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.
getFileInDataDirectory(filename)
Returns the absolute filename of a file in the data directory using getDataDirectory().
expectInfo(function, args=(), kwargs=None, infoRegExp=None, allowUnexpectedMessages=True, *, kargs=None)
Call the given function with the given parameters and expect an info message.
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)
Expect that the expression in the context logs an error that matches errorRegExp.
ignoreWarning(function, args=(), kwargs=None, warningRegExp=None, resultInfoDict=None, *, kargs=None)
Call the given function with the given parameters and ignores potential warnings.
createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10)
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.
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=(), kwargs=None, errorRegExp=None, resultInfoDict=None, *, kargs=None)
Call the given function with the given parameters and ignores potential errors.
getExternalDataDirectory(name, subDirectory=None)
Method to return path to the external data directory.
expectWarning(function, args=(), kwargs=None, warningRegExp=None, *, kargs=None)
Call the given function with the given parameters and expect a warning.
hasWarning(warningRegExp=None)
Expect that the expression in the context block logs a warning that matches warningRegExp.
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)
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)