26from contextlib
import contextmanager
30from TestSupport.TestHelper import TestHelper, UseStackFrameFromCallerForLogging, SetLoggingCallerStackFrame, \
31 SuppressedErrors, SuppressedWarnings, ExpectInfos
36gEncodingRegExp = re.compile(
"^[ \t\v]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)")
44 encoding = gEncodingCache.get(filename)
48 with open(filename,
'rb')
as fh:
52 if bom == b
"\xef\xbb\xbf":
55 line = linecache.getline(filename, 0)
56 m = gEncodingRegExp.match(line)
58 line = linecache.getline(filename, 1)
59 m = gEncodingRegExp.match(line)
62 gEncodingCache[filename] = encoding
68 decodedLine = line.decode(encoding)
69 except UnicodeDecodeError:
70 decodedLine = line.decode(
"latin1")
91 return TestHelper.getInstance().getTestCaseContext()
105 frame = sys._getframe()
106 lastFunction = frame.f_code.co_name
108 if frame.f_code.co_name ==
'__callTestFunction':
109 class_ = frame.f_locals[
'self'].__class__
111 if hasattr(class_,
'_TestCase__callSetUpTestCase'):
113 lastFunction = frame.f_code.co_name
126 TestHelper.getInstance().setMacrosLogOnSuccess(logOnSuccess)
129 if logOnSuccess
is None:
130 return TestHelper.getInstance().getMacrosLogOnSuccess()
145 def wrapper (*args, **kwds):
146 logOnSuccess = TestHelper.getInstance().getMacrosLogOnSuccess()
149 r = func(*args, **kwds)
160 def wrapper (*args, **kwds):
162 expr, msg, comment, logOnSuccess = func(*args, **kwds)
163 stackLine = traceback.extract_stack(TestHelper.getInstance().getCallerStackFrame(), 1)[0]
164 codeLine = stackLine[3]
165 if isinstance(codeLine, bytes):
168 Logging_error(
"%s: %s%s" % (codeLine, msg,
": %s" % (comment)
if comment
else ''), type=
"Compare", depth=1)
171 Logging_info(
"%s: %s%s" % (codeLine, msg,
": %s" % (comment)
if comment
else ''), type=
"Compare", depth=1)
178 """ Returns a unicode representation of value. If value is of type bytes then non-ASCII characters
179 are escaped and a representation of the string is returned.
180 For example, the 8-bit literal b'\x01' becomes 'u"\x01"'.
182 if isinstance(value, bytes):
183 return repr(str(value))
198 if hasattr(expr,
'all'):
214 if hasattr(expr,
'any'):
254 msg =
u"Comparing %s (%s) == %s (%s) with epsilon=%f" % (str(a), type(a).__name__, str(b), type(b).__name__, epsilon)
255 return Math_compareFloatEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
268 msg =
u"Comparing %s (%s) != %s (%s) with epsilon=%f" % (str(a), type(a).__name__, str(b), type(b).__name__, epsilon)
269 return not Math_compareFloatEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
281 msg =
u"Comparing %s (%s) < %s (%s)" % (str(a), type(a).__name__, str(b), type(b).__name__)
282 return Math_compareFloatLessThan(a, b), msg, comment,
shouldLogOnSuccess(logOnSuccess)
296 msg =
u"Comparing %s (%s) <= %s (%s) with epsilon=%f" % (str(a), type(a).__name__, str(b), type(b).__name__, epsilon)
297 return Math_compareFloatLessThanOrEqual(a, b, epsilon), msg, comment,
shouldLogOnSuccess(logOnSuccess)
307def verifyTrue (expr, comment="", logOnSuccess=None, msg=None):
308 success = bool( expr )
310 msg =
u"Must evaluate to True: {} ({})".format(
escapedUnicode(expr), type(expr).__name__ )
322 success =
not bool( expr )
324 msg =
u"Must evaluate to False: {} ({})".format( expr, type(expr).__name__ )
341@UseStackFrameFromCallerForLogging
342def expectError (function, args=[], kargs={}, errorRegExp=None, preventReplacingMessageSeverity=False):
343 testHelper = TestHelper.getInstance()
344 logHandler = testHelper.getLogHandler()
345 assert not logHandler
is None
347 errors =
SuppressedErrors(errorRegExp, Logging_error, preventReplacingMessageSeverity)
349 retVal = function(*args, **kargs)
351 errors.handleResult()
366@UseStackFrameFromCallerForLogging
368 testHelper = TestHelper.getInstance()
369 logHandler = testHelper.getLogHandler()
370 assert not logHandler
is None
374 retVal = function(*args, **kargs)
376 warnings.handleResult()
391@UseStackFrameFromCallerForLogging
393 testHelper = TestHelper.getInstance()
394 logHandler = testHelper.getLogHandler()
395 assert not logHandler
is None
400 retVal = function(*args, **kargs)
402 errors.handleResult()
403 warnings.handleResult()
421@UseStackFrameFromCallerForLogging
422def ignoreError (function, args=(), kargs=
None, errorRegExp=
None, resultInfoDict=
None):
423 def ___doNotLog( _msg ):
pass
426 testHelper = TestHelper.getInstance()
427 logHandler = testHelper.getLogHandler()
429 assert not logHandler
is None
431 hadIgnoredErrors =
False
434 retVal = function(*args, **kargs)
436 hadIgnoredErrors = errors.handleResult()
437 if isinstance( resultInfoDict, dict ):
438 resultInfoDict[
"hadIgnoredErrors" ] = hadIgnoredErrors
458@UseStackFrameFromCallerForLogging
459def ignoreWarning (function, args=(), kargs=
None, warningRegExp=
None, resultInfoDict=
None ):
460 def ___doNotLog( _msg ):
pass
463 testHelper = TestHelper.getInstance()
464 logHandler = testHelper.getLogHandler()
466 assert not logHandler
is None
468 hadIgnoredWarnings =
False
471 retVal = function(*args, **kargs)
473 hadIgnoredWarnings = warnings.handleResult()
474 if isinstance( resultInfoDict, dict ):
475 resultInfoDict[
"hadIgnoredWarnings" ] = hadIgnoredWarnings
494@UseStackFrameFromCallerForLogging
496 testHelper = TestHelper.getInstance()
497 logHandler = testHelper.getLogHandler()
498 assert not logHandler
is None
503 retVal = function(*args, **kargs)
505 hadIgnoredErrors = errors.handleResult()
506 hadIgnoredWarnings = warnings.handleResult()
507 if isinstance( resultInfoDict, dict ):
508 resultInfoDict[
"hadIgnoredWarnings" ] = hadIgnoredWarnings
509 resultInfoDict[
"hadIgnoredErrors" ] = hadIgnoredErrors
525@UseStackFrameFromCallerForLogging
526def expectInfo (function, args=[], kargs={}, infoRegExp=None, allowUnexpectedMessages=True):
527 testHelper = TestHelper.getInstance()
528 logHandler = testHelper.getLogHandler()
529 assert not logHandler
is None
531 expectInfos =
ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
533 retVal = function(*args, **kargs)
535 expectInfos.handleResult()
545@UseStackFrameFromCallerForLogging
547 """Expect that the expression in the context block issues no warning that matches `warningRegExp`.
548 If `warningRegExp` is not given, no warning is expected at all.
558 warnings.handleResult()
562@UseStackFrameFromCallerForLogging
564 """Expect that the expression in the context block logs a warning that matches `warningRegExp`.
565 If `warningRegExp` is not given, any warning is expected.
575 warnings.handleResult()
579@UseStackFrameFromCallerForLogging
581 """Expect that the expression in the context logs an error that matches `errorRegExp`.
582 If `errorRegExp` is not given, any error is expected.
592 errors.handleResult()
596@UseStackFrameFromCallerForLogging
598 """Expect that the expression in the context logs errors and warnings.
599 See `hasError` and `hasWarning` for the arguments.
602 with hasWarningAndError():
610 errors.handleResult()
611 warnings.handleResult()
615@UseStackFrameFromCallerForLogging
616def hasInfo(infoRegExp=None, allowUnexpectedMessages=True):
617 """Expect that the expression in the context logs an info entry that matches `infoRegExp`.
618 If `infoRegExp` is not given, any info message is expected.
619 If `allowUnexpectedMessages` is set to True (the default), messages that do not match `infoRegExp` are ignored,
620 otherwise they are considered a failure.
623 with hasInfo(infoRegExp=".*Unknown module.*"):
626 expectInfos =
ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
630 expectInfos.handleResult()
640 return TestHelper.getInstance().getTestCaseResultDirectory()
651 return TestHelper.getInstance().getTestCaseDataDirectory()
658 return os.path.join(getDataDirectory(), filename)
679 testHelper = TestHelper.getInstance()
680 testCaseContext = testHelper.getTestCaseContext()
681 externalDataVariable =
'TESTCENTER_EXTERNAL_DATA_%s' % name
682 externalDataPlaceholder =
'$(%s)' % externalDataVariable
683 externalDataBaseDirectory = testCaseContext.expandFilename(externalDataPlaceholder)
684 if externalDataBaseDirectory == externalDataPlaceholder:
685 raise LookupError(
'Variable %s could not be expanded. Please define it in your .prefs file.' %
686 externalDataVariable)
687 if os.path.isdir(externalDataBaseDirectory):
689 return os.path.join(externalDataBaseDirectory, subDirectory)
691 testCasePath = testCaseContext.localPath()
692 path = mevis.MLABPackageManager.findPackageIdentifierAndRelativePath(testCasePath)[1]
693 return os.path.join(externalDataBaseDirectory, path)
694 raise ValueError(
'"%s" is not a directory.' % externalDataBaseDirectory)
709 if TestHelper.getInstance().getChangeSetStackLength() > 2:
724def getHash (filename, hash="SHA1", encoder="Hex"):
727 b, ext = os.path.splitext(filename)
728 if ext.lower()
in (
".dcm",
".hdr",
".img",
".bmp",
".jpg",
".jpeg",
".png",
".pnm",
".quo",
".raw",
".tif",
".tiff"):
729 result = Image_calculateHashFromImage(filename, hash, encoder)
732 h = hashlib.new(hash)
733 with open(filename,
"rb")
as f:
736 result = h.hexdigest()
737 elif encoder ==
"Base64":
740 Logging_error(
"Unknown encoder (%s) selected." % (encoder))
741 except Exception
as e:
742 Logging_error(
"Failed to generate hash: %s." % e)
755def createExtraTestCaseResult(testCaseName, package, author, duration, maintainer=None, file=None, line=None, comment=None, showTestFunctionSortingLiterals=False):
756 return ExtraTestCaseResult(testCaseName, package, author, duration, maintainer, file, line, comment, showTestFunctionSortingLiterals)
790 def wrapper (*args, **kwds):
794 result = func(*args, **kwds)
802 def wrapper (*args, **kwds):
803 wereHighPrecisionTimeStampsEnabled = mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(
True)
805 result = func(*args, **kwds)
807 mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(wereHighPrecisionTimeStampsEnabled)
814 table.diff {font-family:monospace;border:medium;margin:0;}
815 .diff_header {background-color:#e0e0e0}
816 td.diff_header {text-align:right}
817 .diff_next {background-color:#c0c0c0}
818 .diff_add {background-color:#aaffaa}
819 .diff_chg {background-color:#ffff77}
820 .diff_sub {background-color:#ffaaaa}
821 tr, td, th {vertical-align:top;}
823 white-space: -pre-wrap;
824 white-space: -o-pre-wrap;
825 white-space: -moz-pre-wrap;
826 white-space: pre-wrap;
827 word-wrap: break-word;
830 _table_template =
"""
831 <table class="diff" id="difflib_chg_%(prefix)s_top"
832 cellspacing="0" cellpadding="1" rules="groups" width="100%%">
833 <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
834 <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
837%(data_rows)s </tbody>
841 difflib.HtmlDiff.__init__(self)
845 linenum =
'%d' % linenum
846 id =
' id="%s%s"' % (self._prefix[side],linenum)
851 text=text.replace(
"&",
"&").replace(
">",
">").replace(
"<",
"<")
852 if len(text) == 0
or text ==
'\n':
853 text =
'<span style="display: block; background-color:#e0e0e0;">' + text +
'</span>'
854 return '<td class="diff_header"%s>%s</td><td>%s</td>' \
866def createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10):
867 diff =
HtmlDiff().make_file(fromLines, toLines,
"",
"", showOnlyContextOfDiff, numberOfContextLines)
875 def MLABTC__disableTestFunctionIf_Decorator(func):
876 disableTestFunction = condition()
if isinstance(condition, collections.abc.Callable)
else condition
877 if disableTestFunction:
878 module = sys.modules[func.__module__]
879 if not hasattr(module,
'MLABTC_DISABLED_TEST_FUNCTIONS'):
880 setattr(module,
'MLABTC_DISABLED_TEST_FUNCTIONS', [])
881 module.MLABTC_DISABLED_TEST_FUNCTIONS.append(func.__name__)
883 return MLABTC__disableTestFunctionIf_Decorator
888 context = TestHelper.getInstance().getHelperContext()
889 return context.field(
'oSSOffscreenRender.supported').value
905def runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None):
906 Macros_INFO(
"Running: " +
" ".join(command))
910 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
912 p = subprocess.Popen(command, env=env)
916 if len(os.path.splitext(c)[1]) > 0:
917 c = os.path.basename(c)
918 shortCommand.append(c)
919 shortCommand =
" ".join(shortCommand)
920 stdout, stderr = p.communicate()
921 stdout = stdout.strip()
922 stderr = stderr.strip()
923 stderr = stderr.decode(
'utf-8',
'replace')
924 stdout = stdout.decode(
'utf-8',
'replace')
925 verifyProcessResult(
" ".join(command), shortCommand, p.returncode, stdout, stderr, expectSuccess, expectFailure, verbose)
926 return stdout, stderr, p.returncode
942def verifyProcessResult(command, shortCommand, returncode, stdout, stderr, expectSuccess=True, expectFailure=False, verbose=True):
945 Macros_ERROR(
"Command returned non-zero: '" + command +
"', return code: " + str(returncode))
946 if len(stderr): Macros_ERROR(stderr)
947 if len(stdout): Macros_ERROR(stdout)
949 msg =
"Success: " + shortCommand
951 if len(stderr): msg +=
"\n\n" + stderr
952 if len(stdout): msg +=
"\n\n" + stdout
956 Macros_ERROR(
"Command returned zero: '" + command +
"'")
957 if len(stderr): Macros_ERROR(stderr)
958 if len(stdout): Macros_ERROR(stdout)
960 msg =
"Expected failure: " + shortCommand
962 if len(stderr): msg +=
"\n\n" + stderr
963 if len(stdout): msg +=
"\n\n" + stdout
979def runPythonScript(script, arguments, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None):
980 python = mevis.MLABFileManager.getExecutable(
"MeVisPython")
981 command = [python,
"-u", script]
982 command.extend(arguments)
983 return runProcess(command, wait=wait, expectSuccess=expectSuccess, expectFailure=expectFailure, verbose=verbose, env=env)
986from .ExtraTestCaseResult
import ExtraTestCaseResult
987from .Image
import calculateHashFromImage
as Image_calculateHashFromImage
988from .Logging
import error
as Logging_error, warning
as Logging_warning, info
as Logging_info
989from .Macros
import ERROR
as Macros_ERROR, INFO
as Macros_INFO
990from .Math
import compareFloatEqual
as Math_compareFloatEqual, \
991 compareFloatLessThan
as Math_compareFloatLessThan, \
992 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)
hasInfo(infoRegExp=None, allowUnexpectedMessages=True)
hasWarningAndError(errorRegExp=None, warningRegExp=None)
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.
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.
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...
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)