TestCenter Reference
Base.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#
10
11
13
14# -- system imports ----------------------------------------------------------------------------{{{-
15import difflib
16import linecache
17import os
18import re
19import subprocess
20import sys
21import hashlib
22import traceback
23# ----------------------------------------------------------------------------------------------}}}-
24
25# -- local imports -----------------------------------------------------------------------------{{{-
26from contextlib import contextmanager
27
28import mevis
29
30from TestSupport.TestHelper import TestHelper, UseStackFrameFromCallerForLogging, SetLoggingCallerStackFrame, \
31 SuppressedErrors, SuppressedWarnings, ExpectInfos
32from TestSupport.FieldValueTests import FieldValueTestCaseSet
33import collections
34# ----------------------------------------------------------------------------------------------}}}-
35
36gEncodingRegExp = re.compile("^[ \t\v]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)")
37gEncodingCache = {}
38
40 global gEncodingCache
41 gEncodingCache = {}
42
44 encoding = gEncodingCache.get(filename)
45 if encoding is None:
46 encoding = "latin1"
47 try:
48 with open(filename, 'rb') as fh:
49 bom = fh.read(3)
50 except IOError:
51 bom = None
52 if bom == b"\xef\xbb\xbf":
53 encoding = "utf8"
54 else:
55 line = linecache.getline(filename, 0)
56 m = gEncodingRegExp.match(line)
57 if m == None:
58 line = linecache.getline(filename, 1)
59 m = gEncodingRegExp.match(line)
60 if m is not None:
61 encoding = m.group(1)
62 gEncodingCache[filename] = encoding
63 return encoding
64
65def decodeSourceCodeLine(filename, line):
66 encoding = getSourceFileEncoding(filename)
67 try:
68 decodedLine = line.decode(encoding)
69 except UnicodeDecodeError:
70 decodedLine = line.decode("latin1")
71 return decodedLine
72
73
74# -- def getPackageList ------------------------------------------------------------------------{{{-
75
82 return TestHelper.getInstance().getPackageList()
83# ----------------------------------------------------------------------------------------------}}}-
84
85# -- def getTestCaseContext --------------------------------------------------------------------{{{-
86
91 return TestHelper.getInstance().getTestCaseContext()
92# ----------------------------------------------------------------------------------------------}}}-
93
94# -- def getModuleName -------------------------------------------------------------------------{{{-
95
99 return TestHelper.getInstance().getModuleName()
100# ----------------------------------------------------------------------------------------------}}}-
101
102
105 frame = sys._getframe()
106 lastFunction = frame.f_code.co_name
107 while frame:
108 if frame.f_code.co_name == '__callTestFunction':
109 class_ = frame.f_locals['self'].__class__
110 # verify that the class of __callTestFunction is TestCase
111 if hasattr(class_, '_TestCase__callSetUpTestCase'):
112 return lastFunction
113 lastFunction = frame.f_code.co_name
114 frame = frame.f_back
115 return 'Unknown'
116
117# -- def MacrosShouldLogOnSuccess --------------------------------------------------------------{{{-
118
126 TestHelper.getInstance().setMacrosLogOnSuccess(logOnSuccess)
127
128def shouldLogOnSuccess(logOnSuccess=None):
129 if logOnSuccess is None:
130 return TestHelper.getInstance().getMacrosLogOnSuccess()
131 else:
132 return logOnSuccess
133
134
142 def __init__(self, logOnSuccess):
143 self.__logOnSuccess = logOnSuccess
144 def __call__(self, func):
145 def wrapper (*args, **kwds):
146 logOnSuccess = TestHelper.getInstance().getMacrosLogOnSuccess()
148 try:
149 r = func(*args, **kwds)
150 finally:
151 setMacrosShouldLogOnSuccess(logOnSuccess)
152 return r
153 return wrapper
154# ----------------------------------------------------------------------------------------------}}}-
155
156# -- def LoggingDecorator ----------------------------------------------------------------------{{{-
157# @cond
158
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):
166 codeLine = decodeSourceCodeLine(stackLine[0], stackLine[3])
167 if not expr:
168 Logging_error("%s: %s%s" % (codeLine, msg, ": %s" % (comment) if comment else ''), type="Compare", depth=1)
169 else:
170 if logOnSuccess:
171 Logging_info("%s: %s%s" % (codeLine, msg, ": %s" % (comment) if comment else ''), type="Compare", depth=1)
172 return expr
173 return wrapper
174# @endcond
175# ----------------------------------------------------------------------------------------------}}}-
176
177def escapedUnicode(value):
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"'.
181 """
182 if isinstance(value, bytes):
183 return repr(str(value))
184 else:
185 return str(value)
186
187# -- def compareEqual --------------------------------------------------------------------------{{{-
188
195@LoggingDecorator
196def compareEqual (a, b, comment="", logOnSuccess=None):
197 expr = a==b
198 if hasattr(expr, 'all'): # looks like a numpy.ndarray
199 expr = expr.all()
200 return expr, u"%s (%s) == %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
201# ----------------------------------------------------------------------------------------------}}}-
202
203# -- def compareNotEqual -----------------------------------------------------------------------{{{-
204
211@LoggingDecorator
212def compareNotEqual (a, b, comment="", logOnSuccess=None):
213 expr = a!=b
214 if hasattr(expr, 'any'): # looks like a numpy.ndarray
215 expr = expr.any()
216 return expr, u"%s (%s) != %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
217# ----------------------------------------------------------------------------------------------}}}-
218
219# -- def compareLessThan -----------------------------------------------------------------------{{{-
220
226@LoggingDecorator
227def compareLessThan (a, b, comment="", logOnSuccess=None):
228 return a<b, u"%s (%s) < %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
229# ----------------------------------------------------------------------------------------------}}}-
230
231# -- def compareLessThanOrEqual ----------------------------------------------------------------{{{-
232
239@LoggingDecorator
240def compareLessThanOrEqual (a, b, comment="", logOnSuccess=None):
241 return a<=b, u"%s (%s) <= %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
242# ----------------------------------------------------------------------------------------------}}}-
243
244# -- def compareFloatEqual ---------------------------------------------------------------------{{{-
245
252@LoggingDecorator
253def compareFloatEqual (a, b, comment="", epsilon=0.0001, logOnSuccess=None):
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)
256# ----------------------------------------------------------------------------------------------}}}-
257
258# -- def compareFloatNotEqual ------------------------------------------------------------------{{{-
259
266@LoggingDecorator
267def compareFloatNotEqual (a, b, comment="", epsilon=0.0001, logOnSuccess=None):
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)
270# ----------------------------------------------------------------------------------------------}}}-
271
272# -- def compareFloatLessThan ------------------------------------------------------------------{{{-
273
279@LoggingDecorator
280def compareFloatLessThan (a, b, comment="", logOnSuccess=None):
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)
283# ----------------------------------------------------------------------------------------------}}}-
284
285# -- def compareFloatLessThanOrEqual ---------------------------------------------------------{{{-
286
294@LoggingDecorator
295def compareFloatLessThanOrEqual (a, b, comment="", epsilon=0.0001, logOnSuccess=None):
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)
298# ----------------------------------------------------------------------------------------------}}}-
299
300# -- def verifyTrue ----------------------------------------------------------------------------{{{-
301
306@LoggingDecorator
307def verifyTrue (expr, comment="", logOnSuccess=None, msg=None):
308 success = bool( expr ) # we must do this before evaluating expr for the msg string generation which may have side effects
309 if msg is None:
310 msg = u"Must evaluate to True: {} ({})".format( escapedUnicode(expr), type(expr).__name__ )
311 return success, msg, comment, shouldLogOnSuccess(logOnSuccess)
312# ----------------------------------------------------------------------------------------------}}}-
313
314# -- def verifyFalse ---------------------------------------------------------------------------{{{-
315
320@LoggingDecorator
321def verifyFalse (expr, comment="", logOnSuccess=None, msg=None):
322 success = not bool( expr ) # we must do this before evaluating expr for the msg string generation which may have side effects
323 if msg is None:
324 msg = u"Must evaluate to False: {} ({})".format( expr, type(expr).__name__ )
325 return success, msg, comment, shouldLogOnSuccess(logOnSuccess)
326# ----------------------------------------------------------------------------------------------}}}-
327
328# -- def expectError ---------------------------------------------------------------------------{{{-
329
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
346
347 errors = SuppressedErrors(errorRegExp, Logging_error, preventReplacingMessageSeverity)
348 try:
349 retVal = function(*args, **kargs)
350 finally:
351 errors.handleResult()
352
353 return retVal
354# ----------------------------------------------------------------------------------------------}}}-
355
356# -- def expectWarning -------------------------------------------------------------------------{{{-
357
366@UseStackFrameFromCallerForLogging
367def expectWarning (function, args=[], kargs={}, warningRegExp=None):
368 testHelper = TestHelper.getInstance()
369 logHandler = testHelper.getLogHandler()
370 assert not logHandler is None
371
372 warnings = SuppressedWarnings(warningRegExp, Logging_error)
373 try:
374 retVal = function(*args, **kargs)
375 finally:
376 warnings.handleResult()
377
378 return retVal
379# ----------------------------------------------------------------------------------------------}}}-
380
381# -- def expectWarningAndError ---------------------------------------------------------------------------{{{-
382
391@UseStackFrameFromCallerForLogging
392def expectWarningAndError (function, args=[], kargs={}, errorRegExp=None, warningRegExp=None):
393 testHelper = TestHelper.getInstance()
394 logHandler = testHelper.getLogHandler()
395 assert not logHandler is None
396
397 warnings = SuppressedWarnings(warningRegExp, Logging_error)
398 errors = SuppressedErrors(errorRegExp, Logging_error)
399 try:
400 retVal = function(*args, **kargs)
401 finally:
402 errors.handleResult()
403 warnings.handleResult()
404
405 return retVal
406
407# -- def ignoreError ---------------------------------------------------------------------------{{{-
408
421@UseStackFrameFromCallerForLogging
422def ignoreError (function, args=(), kargs=None, errorRegExp=None, resultInfoDict=None):
423 def ___doNotLog( _msg ): pass
424 #
425 kargs = kargs or {}
426 testHelper = TestHelper.getInstance()
427 logHandler = testHelper.getLogHandler()
428
429 assert not logHandler is None
430
431 hadIgnoredErrors = False
432 errors = SuppressedErrors( errorRegExp, logErrorFunc=None )
433 try:
434 retVal = function(*args, **kargs)
435 finally:
436 hadIgnoredErrors = errors.handleResult()
437 if isinstance( resultInfoDict, dict ):
438 resultInfoDict[ "hadIgnoredErrors" ] = hadIgnoredErrors
439
440 return retVal
441# ----------------------------------------------------------------------------------------------}}}-
442
443
444# -- def ignoreWarning ---------------------------------------------------------------------------{{{-
445
458@UseStackFrameFromCallerForLogging
459def ignoreWarning (function, args=(), kargs=None, warningRegExp=None, resultInfoDict=None ):
460 def ___doNotLog( _msg ): pass
461 #
462 kargs = kargs or {}
463 testHelper = TestHelper.getInstance()
464 logHandler = testHelper.getLogHandler()
465
466 assert not logHandler is None
467
468 hadIgnoredWarnings = False
469 warnings = SuppressedWarnings( warningRegExp, logErrorFunc=None )
470 try:
471 retVal = function(*args, **kargs)
472 finally:
473 hadIgnoredWarnings = warnings.handleResult()
474 if isinstance( resultInfoDict, dict ):
475 resultInfoDict[ "hadIgnoredWarnings" ] = hadIgnoredWarnings
476
477 return retVal
478# ----------------------------------------------------------------------------------------------}}}-
479
480# -- def ignoreWarningAndError ---------------------------------------------------------------------------{{{-
481
494@UseStackFrameFromCallerForLogging
495def ignoreWarningAndError (function, args=[], kargs={}, errorRegExp=None, warningRegExp=None, resultInfoDict=None):
496 testHelper = TestHelper.getInstance()
497 logHandler = testHelper.getLogHandler()
498 assert not logHandler is None
499
500 warnings = SuppressedWarnings(warningRegExp, logErrorFunc=None)
501 errors = SuppressedErrors(errorRegExp, logErrorFunc=None)
502 try:
503 retVal = function(*args, **kargs)
504 finally:
505 hadIgnoredErrors = errors.handleResult()
506 hadIgnoredWarnings = warnings.handleResult()
507 if isinstance( resultInfoDict, dict ):
508 resultInfoDict[ "hadIgnoredWarnings" ] = hadIgnoredWarnings
509 resultInfoDict[ "hadIgnoredErrors" ] = hadIgnoredErrors
510
511 return retVal
512# ----------------------------------------------------------------------------------------------}}}-
513
514
515# -- def expectInfo ---------------------------------------------------------------------------{{{-
516
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
530
531 expectInfos = ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
532 try:
533 retVal = function(*args, **kargs)
534 finally:
535 expectInfos.handleResult()
536
537 return retVal
538# ----------------------------------------------------------------------------------------------}}}-
539
540
541# Helpers for expected errors or warnings that can be used with a context manager
542
543
544@contextmanager
545@UseStackFrameFromCallerForLogging
546def hasNoWarning(warningRegExp=None):
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.
549
550 Usage:
551 with hasNoWarning():
552 doStuff()
553 """
554 warnings = SuppressedWarnings(warningRegExp, expectWarning=False)
555 try:
556 yield
557 finally:
558 warnings.handleResult()
559
560
561@contextmanager
562@UseStackFrameFromCallerForLogging
563def hasWarning(warningRegExp=None):
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.
566
567 Usage:
568 with hasWarning():
569 doStuff()
570 """
571 warnings = SuppressedWarnings(warningRegExp, expectWarning=True)
572 try:
573 yield
574 finally:
575 warnings.handleResult()
576
577
578@contextmanager
579@UseStackFrameFromCallerForLogging
580def hasError(errorRegExp=None):
581 """Expect that the expression in the context logs an error that matches `errorRegExp`.
582 If `errorRegExp` is not given, any error is expected.
583
584 Usage:
585 with hasError():
586 doStuff()
587 """
588 errors = SuppressedErrors(errorRegExp, Logging_error)
589 try:
590 yield
591 finally:
592 errors.handleResult()
593
594
595@contextmanager
596@UseStackFrameFromCallerForLogging
597def hasWarningAndError(errorRegExp=None, warningRegExp=None):
598 """Expect that the expression in the context logs errors and warnings.
599 See `hasError` and `hasWarning` for the arguments.
600
601 Usage:
602 with hasWarningAndError():
603 doStuff()
604 """
605 warnings = SuppressedWarnings(warningRegExp, Logging_error)
606 errors = SuppressedErrors(errorRegExp, Logging_error)
607 try:
608 yield
609 finally:
610 errors.handleResult()
611 warnings.handleResult()
612
613
614@contextmanager
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.
621
622 Usage:
623 with hasInfo(infoRegExp=".*Unknown module.*"):
624 doStuff()
625 """
626 expectInfos = ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
627 try:
628 yield
629 finally:
630 expectInfos.handleResult()
631
632# ----------------------------------------------------------------------------------------------}}}-
633
634# -- def getResultDirectory --------------------------------------------------------------------{{{-
635
640 return TestHelper.getInstance().getTestCaseResultDirectory()
641# ----------------------------------------------------------------------------------------------}}}-
642
643# -- def getDataDirectory ----------------------------------------------------------------------{{{-
644
651 return TestHelper.getInstance().getTestCaseDataDirectory()
652# ----------------------------------------------------------------------------------------------}}}-
653
654# -- def getFileInDataDirectory ----------------------------------------------------------------{{{-
655
658 return os.path.join(getDataDirectory(), filename)
659# ----------------------------------------------------------------------------------------------}}}-
660
661# -- def existsFileInDataDirectory -------------------------------------------------------------{{{-
662
665 return os.path.exists(getFileInDataDirectory(filename))
666# ----------------------------------------------------------------------------------------------}}}-
667
668# -- def getExternalDataDirectory --------------------------------------------------------------{{{-
669
678def getExternalDataDirectory (name, subDirectory=None):
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):
688 if subDirectory:
689 return os.path.join(externalDataBaseDirectory, subDirectory)
690 else:
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)
695# ----------------------------------------------------------------------------------------------}}}-
696
697# -- def pushChangeSet -------------------------------------------------------------------------{{{-
698
700 return TestHelper.getInstance().pushChangeSet()
701# ----------------------------------------------------------------------------------------------}}}-
702
703# -- def popChangeSet --------------------------------------------------------------------------{{{-
704
708 retVal = True
709 if TestHelper.getInstance().getChangeSetStackLength() > 2:
710 retVal = TestHelper.getInstance().popChangeSet()
711 else:
712 retVal = False
713 return retVal
714# ----------------------------------------------------------------------------------------------}}}-
715
716# -- def getHash -------------------------------------------------------------------------------{{{-
717
724def getHash (filename, hash="SHA1", encoder="Hex"):
725 result = None
726 # Images are handled using the ImageHash module!
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)
730 else:
731 try:
732 h = hashlib.new(hash)
733 with open(filename, "rb") as f:
734 h.update(f.read())
735 if encoder == "Hex":
736 result = h.hexdigest()
737 elif encoder == "Base64":
738 result = h.digest()
739 else:
740 Logging_error("Unknown encoder (%s) selected." % (encoder))
741 except Exception as e:
742 Logging_error("Failed to generate hash: %s." % e)
743 return result
744# ----------------------------------------------------------------------------------------------}}}-
745
746# -- def createExtraTestCaseResult ----------------------------------------------------------------{{{-
747
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)
757# ----------------------------------------------------------------------------------------------}}}-
758
759
760# -- def addExtraTestCaseResult ----------------------------------------------------------------{{{-
761
764def addExtraTestCaseResult(extraTestCaseResult):
765 return TestHelper.getInstance().addExtraTestCaseResult(extraTestCaseResult)
766# ----------------------------------------------------------------------------------------------}}}-
767
768
769# -- def pushEnvironment -----------------------------------------------------------------------{{{-
770
774 return TestHelper.getInstance().pushEnvironment()
775# ----------------------------------------------------------------------------------------------}}}-
776
777
778# -- def popEnvironment -----------------------------------------------------------------------{{{-
779
783 return TestHelper.getInstance().popEnvironment()
784# ----------------------------------------------------------------------------------------------}}}-
785
786# -- def pushEnvironmentDecorator --------------------------------------------------------------{{{-
787
788# Pushes the current environment before the function is called and pops it again after it has finished
790 def wrapper (*args, **kwds):
792 # execute macro
793 try:
794 result = func(*args, **kwds)
795 finally:
797 return result
798 return wrapper
799# ----------------------------------------------------------------------------------------------}}}-
800
802 def wrapper (*args, **kwds):
803 wereHighPrecisionTimeStampsEnabled = mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(True)
804 try:
805 result = func(*args, **kwds)
806 finally:
807 mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(wereHighPrecisionTimeStampsEnabled)
808 return result
809 return wrapper
810
811class HtmlDiff(difflib.HtmlDiff):
812
813 _styles = """
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;}
822 table.diff tbody {
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;
828 }"""
829
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>
835 %(header_row)s
836 <tbody>
837%(data_rows)s </tbody>
838 </table>"""
839
840 def __init__(self):
841 difflib.HtmlDiff.__init__(self)
842
843 def _format_line(self,side,flag,linenum,text):
844 try:
845 linenum = '%d' % linenum
846 id = ' id="%s%s"' % (self._prefix[side],linenum)
847 except TypeError:
848 # handle blank lines where linenum is '>' or ''
849 id = ''
850 # replace those things that would get confused with HTML symbols
851 text=text.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")
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>' \
855 % (id,linenum,text)
856
857# ----------------------------------------------------------------------------------------------}}}-
858
859# -- def pushEnvironmentDecorator --------------------------------------------------------------{{{-
860
861# Creates a HTML diff from the two files
862# @param fromLines The array containing the lines of the left string.
863# @param toLines The array containing the lines of the right string.
864# @param showOnlyContextOfDiff If true, then only the context lines around the diff are shown, otherwise the whole content is shown.
865# @param numberOfContextLines The number of context line only the diff context is shown.
866def createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10):
867 diff = HtmlDiff().make_file(fromLines, toLines, "", "", showOnlyContextOfDiff, numberOfContextLines)
868 return diff
869
870# ----------------------------------------------------------------------------------------------}}}-
871
872
873# Decorator for disabling test functions conditionally
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__)
882 return func
883 return MLABTC__disableTestFunctionIf_Decorator
884
885
886# Returns True if offscreen rendering is supported by the hardware. Otherwise returns False.
888 context = TestHelper.getInstance().getHelperContext()
889 return context.field('oSSOffscreenRender.supported').value
890
891
892# Creates a subprocess.Popen instance.
893# @param command A list containing the executable and arguments for the process.
894# @param wait If True (default), then the function waits for the process to finish. Afterwards it checks the
895# return code, stderr, and stdout according to the values of expectSuccess and expectFailure. Finally,
896# a tuple (stdout, stderr, returncode) is returned.
897# If False, then the subprocess.Popen instance is returned after it was created and started.
898# @param expectSuccess If True (default), then this function expects the process to return 0.
899# Stdout/stderr is logged as an info message on success, otherwise as error messages.
900# @param expectFailure If True, then this function expects the process to return non-zero.
901# Stdout/stderr is logged as an info message on failure, otherwise as error messages.
902# @param verbose A boolean to indicate if stdout/stderr should be printed via Macros_INFO.
903# @param env A dictionary with environment variables. Default is os.environ.
904# @return A tuple (stdout, stderr, returncode) if wait is True, the subprocess.Popen instance otherwise.
905def runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None):
906 Macros_INFO("Running: " + " ".join(command))
907 if env is None:
908 env = os.environ
909 if wait:
910 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
911 else:
912 p = subprocess.Popen(command, env=env)
913 if wait:
914 shortCommand = []
915 for c in command:
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
927 else:
928 return p
929
930
931# Helper function for runProcess().
932# @param command A list containing the executable and arguments for the process.
933# @param shortCommand A shorter command string to improve readability of log messages.
934# @param returncode The returncode of the process.
935# @param stdout The stdout output of the process.
936# @param stderr The stderr output of the process.
937# @param expectSuccess If True (default), then this function expects the process to return 0.
938# Stdout/stderr is logged as an info message on success, otherwise as error messages.
939# @param expectFailure If True, then this function expects the process to return non-zero.
940# Stdout/stderr is logged as an info message on failure, otherwise as error messages.
941# @param verbose A boolean to indicate if stdout/stderr should be printed via Macros_INFO.
942def verifyProcessResult(command, shortCommand, returncode, stdout, stderr, expectSuccess=True, expectFailure=False, verbose=True):
943 if expectSuccess:
944 if returncode != 0:
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)
948 else:
949 msg = "Success: " + shortCommand
950 if verbose:
951 if len(stderr): msg += "\n\n" + stderr
952 if len(stdout): msg += "\n\n" + stdout
953 Macros_INFO(msg)
954 elif expectFailure:
955 if returncode == 0:
956 Macros_ERROR("Command returned zero: '" + command + "'")
957 if len(stderr): Macros_ERROR(stderr)
958 if len(stdout): Macros_ERROR(stdout)
959 else:
960 msg = "Expected failure: " + shortCommand
961 if verbose:
962 if len(stderr): msg += "\n\n" + stderr
963 if len(stdout): msg += "\n\n" + stdout
964 Macros_INFO(msg)
965
966
967# Creates and starts a process from the python script using runProcess()
968# @param wait If True (default), then the function waits for the process to finish. Afterwards it checks the
969# return code, stderr, and stdout according to the values of expectSuccess and expectFailure. Finally,
970# a tuple (stdout, stderr, returncode) is returned.
971# If False, then the subprocess.Popen instance is returned after it was created and started.
972# @param expectSuccess If True (default), then this function expects the process to return 0.
973# Stdout/stderr is logged as an info message on success, otherwise as error messages.
974# @param expectFailure If True, then this function expects the process to return non-zero.
975# Stdout/stderr is logged as an info message on failure, otherwise as error messages.
976# @param verbose A boolean to indicate if stdout/stderr should be printed via Macros_INFO.
977# @param env A dictionary with environment variables. Default is os.environ.
978# @return A tuple (stdout, stderr, returncode) if wait is True, the subprocess.Popen instance otherwise.
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)
984
985
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)
Definition Base.py:843
Decorator to globally enable or disable if the ASSERT_*/EXPECT_* macros log an info message on succes...
Definition Base.py:141
EnableHighPrecisionLoggingTimeStampsDecorator(func)
Definition Base.py:801
getHash(filename, hash="SHA1", encoder="Hex")
Compute a hash for the content of the given file.
Definition Base.py:724
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.
Definition Base.py:295
verifyProcessResult(command, shortCommand, returncode, stdout, stderr, expectSuccess=True, expectFailure=False, verbose=True)
Definition Base.py:942
addExtraTestCaseResult(extraTestCaseResult)
Adds an ExtraTestCaseResult object.
Definition Base.py:764
runPythonScript(script, arguments, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None)
Definition Base.py:979
escapedUnicode(value)
Definition Base.py:177
hasInfo(infoRegExp=None, allowUnexpectedMessages=True)
Definition Base.py:616
hasWarningAndError(errorRegExp=None, warningRegExp=None)
Definition Base.py:597
getDataDirectory()
Method to return path to the data files.
Definition Base.py:650
setMacrosShouldLogOnSuccess(logOnSuccess)
Globally enables or disables if the ASSERT_*/EXPECT_* macros log an info message on success.
Definition Base.py:125
getResultDirectory()
Getter for the directory used to save results.
Definition Base.py:639
compareNotEqual(a, b, comment="", logOnSuccess=None)
Compare the two given values for inequality.
Definition Base.py:212
disableTestFunctionIf(condition)
Definition Base.py:874
runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True, env=None)
Definition Base.py:905
hasNoWarning(warningRegExp=None)
Definition Base.py:546
getPackageList()
Returns the list of available packages.
Definition Base.py:81
compareFloatEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None)
Compare the two given float values if they are epsilon-equal.
Definition Base.py:253
existsFileInDataDirectory(filename)
Checks if the given file exists in the data directory.
Definition Base.py:664
ignoreWarning(function, args=(), kargs=None, warningRegExp=None, resultInfoDict=None)
Call the given function with the given parameters and ignores potential warnings.
Definition Base.py:459
getModuleName()
Return the name of the currently tested module.
Definition Base.py:98
compareLessThanOrEqual(a, b, comment="", logOnSuccess=None)
Compare the two given values if the first one is less than or equal to the second.
Definition Base.py:240
expectWarning(function, args=[], kargs={}, warningRegExp=None)
Call the given function with the given parameters and expect a warning.
Definition Base.py:367
expectWarningAndError(function, args=[], kargs={}, errorRegExp=None, warningRegExp=None)
Call the given function with the given parameters and expect both a warning and an error.
Definition Base.py:392
shouldLogOnSuccess(logOnSuccess=None)
Definition Base.py:128
getTestCaseContext()
Get the context of the test.
Definition Base.py:90
compareFloatLessThan(a, b, comment="", logOnSuccess=None)
Compare the two given float values if the first is less than the second.
Definition Base.py:280
getFileInDataDirectory(filename)
Returns the absolute filename of a file in the data directory using getDataDirectory().
Definition Base.py:657
compareEqual(a, b, comment="", logOnSuccess=None)
Compare the two given values for equality.
Definition Base.py:196
compareLessThan(a, b, comment="", logOnSuccess=None)
Compare the two given values if the first one is less than the second.
Definition Base.py:227
verifyTrue(expr, comment="", logOnSuccess=None, msg=None)
If the given expression evaluates to False, an error is logged.
Definition Base.py:307
hasError(errorRegExp=None)
Definition Base.py:580
createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10)
Definition Base.py:866
getTestFunctionName()
Returns the name of the current test function.
Definition Base.py:104
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.
Definition Base.py:495
createExtraTestCaseResult(testCaseName, package, author, duration, maintainer=None, file=None, line=None, comment=None, showTestFunctionSortingLiterals=False)
Creates an ExtraTestCaseResult object.
Definition Base.py:755
LoggingDecorator(func)
Internal decorator used for creating messages in compare methods.
Definition Base.py:159
ignoreError(function, args=(), kargs=None, errorRegExp=None, resultInfoDict=None)
Call the given function with the given parameters and ignores potential errors.
Definition Base.py:422
expectError(function, args=[], kargs={}, errorRegExp=None, preventReplacingMessageSeverity=False)
Call the given function with the given parameters and expect an error.
Definition Base.py:342
getExternalDataDirectory(name, subDirectory=None)
Method to return path to the external data directory.
Definition Base.py:678
hasWarning(warningRegExp=None)
Definition Base.py:563
compareFloatNotEqual(a, b, comment="", epsilon=0.0001, logOnSuccess=None)
Compare the two given float values if they are epsilon-unequal.
Definition Base.py:267
decodeSourceCodeLine(filename, line)
Definition Base.py:65
verifyFalse(expr, comment="", logOnSuccess=None, msg=None)
If the given expression evaluates to True, an error is logged.
Definition Base.py:321
pushChangeSet()
Push a new ChangeSet to the stack.
Definition Base.py:699
hardwareSupportsOffscreenRendering()
Definition Base.py:887
pushEnvironment()
Pushes the current environment dictionary on a stack, so that modifications can be applied and be pop...
Definition Base.py:773
clearEncodingCache()
Definition Base.py:39
pushEnvironmentDecorator(func)
Definition Base.py:789
expectInfo(function, args=[], kargs={}, infoRegExp=None, allowUnexpectedMessages=True)
Call the given function with the given parameters and expect an info message.
Definition Base.py:526
popEnvironment()
Pops the last pushed environment dictionary from a stack and makes it the current one.
Definition Base.py:782
popChangeSet()
Pop the last ChangeSet from the stack.
Definition Base.py:707
getSourceFileEncoding(filename)
Definition Base.py:43