TestCenter Reference
Base.py
Go to the documentation of this file.
1 from builtins import str
2 
3 # Already ported to Python 3
4 # **InsertLicense** code author="Gereon A. Frey"
5 
7 
8 # -- system imports ----------------------------------------------------------------------------{{{-
9 import difflib
10 import linecache
11 import os
12 import re
13 import subprocess
14 import sys
15 import hashlib
16 import traceback
17 # ----------------------------------------------------------------------------------------------}}}-
18 
19 # -- local imports -----------------------------------------------------------------------------{{{-
20 import mevis
21 
22 from TestSupport.TestHelper import TestHelper, UseStackFrameFromCallerForLogging, SetLoggingCallerStackFrame, \
23  SuppressedErrors, SuppressedWarnings, ExpectInfos
24 from TestSupport.FieldValueTests import FieldValueTestCaseSet
25 import collections
26 # ----------------------------------------------------------------------------------------------}}}-
27 
28 gEncodingRegExp = re.compile("^[ \t\v]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)")
29 gEncodingCache = {}
30 
32  global gEncodingCache
33  gEncodingCache = {}
34 
35 def getSourceFileEncoding(filename):
36  encoding = gEncodingCache.get(filename)
37  if encoding is None:
38  encoding = "latin1"
39  try:
40  bom = open(filename, 'rb').read(3)
41  except IOError:
42  bom = None
43  if bom == b"\xef\xbb\xbf":
44  encoding = "utf8"
45  else:
46  line = linecache.getline(filename, 0)
47  m = gEncodingRegExp.match(line)
48  if m == None:
49  line = linecache.getline(filename, 1)
50  m = gEncodingRegExp.match(line)
51  if m is not None:
52  encoding = m.group(1)
53  gEncodingCache[filename] = encoding
54  return encoding
55 
56 def decodeSourceCodeLine(filename, line):
57  encoding = getSourceFileEncoding(filename)
58  try:
59  decodedLine = line.decode(encoding)
60  except UnicodeDecodeError:
61  decodedLine = line.decode("latin1")
62  return decodedLine
63 
64 
65 # -- def getPackageList ------------------------------------------------------------------------{{{-
66 
73  return TestHelper.getInstance().getPackageList()
74 # ----------------------------------------------------------------------------------------------}}}-
75 
76 # -- def getTestCaseContext --------------------------------------------------------------------{{{-
77 
82  return TestHelper.getInstance().getTestCaseContext()
83 # ----------------------------------------------------------------------------------------------}}}-
84 
85 # -- def getModuleName -------------------------------------------------------------------------{{{-
86 
90  return TestHelper.getInstance().getModuleName()
91 # ----------------------------------------------------------------------------------------------}}}-
92 
93 
96  frame = sys._getframe()
97  lastFunction = frame.f_code.co_name
98  while frame:
99  if frame.f_code.co_name == '__callTestFunction':
100  class_ = frame.f_locals['self'].__class__
101  # verify that the class of __callTestFunction is TestCase
102  if hasattr(class_, '_TestCase__callSetUpTestCase'):
103  return lastFunction
104  lastFunction = frame.f_code.co_name
105  frame = frame.f_back
106  return 'Unknown'
107 
108 # -- def MacrosShouldLogOnSuccess --------------------------------------------------------------{{{-
109 
116 def setMacrosShouldLogOnSuccess(logOnSuccess):
117  TestHelper.getInstance().setMacrosLogOnSuccess(logOnSuccess)
118 
119 def shouldLogOnSuccess(logOnSuccess=None):
120  if logOnSuccess is None:
121  return TestHelper.getInstance().getMacrosLogOnSuccess()
122  else:
123  return logOnSuccess
124 
125 
133  def __init__(self, logOnSuccess):
134  self.__logOnSuccess = logOnSuccess
135  def __call__(self, func):
136  def wrapper (*args, **kwds):
137  logOnSuccess = TestHelper.getInstance().getMacrosLogOnSuccess()
139  try:
140  r = func(*args, **kwds)
141  finally:
142  setMacrosShouldLogOnSuccess(logOnSuccess)
143  return r
144  return wrapper
145 # ----------------------------------------------------------------------------------------------}}}-
146 
147 # -- def LoggingDecorator ----------------------------------------------------------------------{{{-
148 # @cond
149 
150 def LoggingDecorator (func):
151  def wrapper (*args, **kwds):
153  expr, msg, comment, logOnSuccess = func(*args, **kwds)
154  stackLine = traceback.extract_stack(TestHelper.getInstance().getCallerStackFrame(), 1)[0]
155  codeLine = stackLine[3]
156  if isinstance(codeLine, bytes):
157  codeLine = decodeSourceCodeLine(stackLine[0], stackLine[3])
158  if not expr:
159  Logging_error("%s: %s%s" % (codeLine, msg, ": %s" % (comment) if comment else ''), type="Compare", depth=1)
160  else:
161  if logOnSuccess:
162  Logging_info("%s: %s%s" % (codeLine, msg, ": %s" % (comment) if comment else ''), type="Compare", depth=1)
163  return expr
164  return wrapper
165 # @endcond
166 # ----------------------------------------------------------------------------------------------}}}-
167 
168 def escapedUnicode(value):
169  """ Returns a unicode representation of value. If value is of type bytes then non-ASCII characters
170  are escaped and a representation of the string is returned.
171  For example, the 8-bit literal b'\x01' becomes 'u"\x01"'.
172  """
173  if isinstance(value, bytes):
174  return repr(str(value))
175  else:
176  return str(value)
177 
178 # -- def compareEqual --------------------------------------------------------------------------{{{-
179 ## Compare the two given values for equality.
180 # If the two values are unequal an error is logged.
181 # @param a The first value.
182 # @param b The second value.
183 # @param comment Optional comment shown if comparison fails.
184 # @param logOnSuccess Print message in case of success.
185 # @return True if values are equal.
186 @LoggingDecorator
187 def compareEqual (a, b, comment="", logOnSuccess=None):
188  expr = a==b
189  if hasattr(expr, 'all'): # looks like a numpy.ndarray
190  expr = expr.all()
191  return expr, u"%s (%s) == %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
192 # ----------------------------------------------------------------------------------------------}}}-
193 
194 # -- def compareNotEqual -----------------------------------------------------------------------{{{-
195 ## Compare the two given values for inequality.
196 # If the two values are equal an error is logged.
197 # @param a The first value.
198 # @param b The second value.
199 # @param comment Optional comment shown if comparison fails.
200 # @param logOnSuccess Print message in case of success.
201 # @return True if the values are unequal.
202 @LoggingDecorator
203 def compareNotEqual (a, b, comment="", logOnSuccess=None):
204  expr = a!=b
205  if hasattr(expr, 'any'): # looks like a numpy.ndarray
206  expr = expr.any()
207  return expr, u"%s (%s) != %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
208 # ----------------------------------------------------------------------------------------------}}}-
209 
210 # -- def compareLessThan -----------------------------------------------------------------------{{{-
211 ## Compare the two given values if the first one is less than the second.
212 # @param a The first value.
213 # @param b The second value.
214 # @param comment Optional comment shown if comparison fails.
215 # @param logOnSuccess Print message in case of success.
216 # @return True if the first value is less than the second.
217 @LoggingDecorator
218 def compareLessThan (a, b, comment="", logOnSuccess=None):
219  return a<b, u"%s (%s) < %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
220 # ----------------------------------------------------------------------------------------------}}}-
221 
222 # -- def compareLessThanOrEqual ----------------------------------------------------------------{{{-
223 ## Compare the two given values if the first one is less than or equal to the
224 # second.
225 # @param a The first value.
226 # @param b The second value.
227 # @param comment Optional comment shown if comparison fails.
228 # @param logOnSuccess Print message in case of success.
229 # @return True if the first value less than or equal to the second.
230 @LoggingDecorator
231 def compareLessThanOrEqual (a, b, comment="", logOnSuccess=None):
232  return a<=b, u"%s (%s) <= %s (%s)" % (escapedUnicode(a), type(a).__name__, escapedUnicode(b), type(b).__name__), comment, shouldLogOnSuccess(logOnSuccess)
233 # ----------------------------------------------------------------------------------------------}}}-
234 
235 # -- def compareFloatEqual ---------------------------------------------------------------------{{{-
236 ## Compare the two given float values if they are epsilon-equal.
237 # @param a The first value.
238 # @param b The second value.
239 # @param comment Comment shown if comparison fails.
240 # @param epsilon Epsilon used for float comparison.
241 # @param logOnSucces Print message in case of success.
242 # @return True if values are equal.
243 @LoggingDecorator
244 def compareFloatEqual (a, b, comment="", epsilon=0.0001, logOnSuccess=None):
245  msg = u"Comparing %s (%s) == %s (%s) with epsilon=%f" % (str(a), type(a).__name__, str(b), type(b).__name__, epsilon)
246  return Math_compareFloatEqual(a, b, epsilon), msg, comment, shouldLogOnSuccess(logOnSuccess)
247 # ----------------------------------------------------------------------------------------------}}}-
248 
249 # -- def compareFloatNotEqual ------------------------------------------------------------------{{{-
250 ## Compare the two given float values if they are epsilon-unequal.
251 # @param a The first value.
252 # @param b The second value.
253 # @param comment Comment shown if comparison fails.
254 # @param epsilon Epsilon used for float comparison.
255 # @param logOnSucces Print message in case of success.
256 # @return True if values are unequal.
257 @LoggingDecorator
258 def compareFloatNotEqual (a, b, comment="", epsilon=0.0001, logOnSuccess=None):
259  msg = u"Comparing %s (%s) != %s (%s) with epsilon=%f" % (str(a), type(a).__name__, str(b), type(b).__name__, epsilon)
260  return not Math_compareFloatEqual(a, b, epsilon), msg, comment, shouldLogOnSuccess(logOnSuccess)
261 # ----------------------------------------------------------------------------------------------}}}-
262 
263 # -- def compareFloatLessThan ------------------------------------------------------------------{{{-
264 ## Compare the two given float values if the first is less than the second.
265 # @param a The first value.
266 # @param b The second value.
267 # @param comment Comment shown if comparison fails.
268 # @param logOnSucces Print message in case of success.
269 # @return True if first value is less than the second.
270 @LoggingDecorator
271 def compareFloatLessThan (a, b, comment="", logOnSuccess=None):
272  msg = u"Comparing %s (%s) < %s (%s)" % (str(a), type(a).__name__, str(b), type(b).__name__)
273  return Math_compareFloatLessThan(a, b), msg, comment, shouldLogOnSuccess(logOnSuccess)
274 # ----------------------------------------------------------------------------------------------}}}-
275 
276 # -- def compareFloatLessThanOrEqual ---------------------------------------------------------{{{-
277 ## Compare the two given float values if the first is less than or equal to the
278 # second.
279 # @param a The first value.
280 # @param b The second value.
281 # @param comment Comment shown if comparison fails.
282 # @param epsilon Epsilon used for float comparison.
283 # @param logOnSucces Print message in case of success.
284 # @return True if first value is less than or equal to the second.
285 @LoggingDecorator
286 def compareFloatLessThanOrEqual (a, b, comment="", epsilon=0.0001, logOnSuccess=None):
287  msg = u"Comparing %s (%s) <= %s (%s) with epsilon=%f" % (str(a), type(a).__name__, str(b), type(b).__name__, epsilon)
288  return Math_compareFloatLessThanOrEqual(a, b, epsilon), msg, comment, shouldLogOnSuccess(logOnSuccess)
289 # ----------------------------------------------------------------------------------------------}}}-
290 
291 # -- def verifyTrue ----------------------------------------------------------------------------{{{-
292 ## If the given expression evaluates to False, an error is logged.
293 # @param expr The expression that must evaluate to True.
294 # @param comment Optional comment shown if expression is not True.
295 # @param logOnSucces Print message in case of success.
296 # @return True if expression evaluates to True.
297 @LoggingDecorator
298 def verifyTrue (expr, comment="", logOnSuccess=None, msg=None):
299  success = bool( expr ) # we must do this before evaluating expr for the msg string generation which may have side effects
300  if msg is None:
301  msg = u"Must evaluate to True: {} ({})".format( escapedUnicode(expr), type(expr).__name__ )
302  return success, msg, comment, shouldLogOnSuccess(logOnSuccess)
303 # ----------------------------------------------------------------------------------------------}}}-
304 
305 # -- def verifyFalse ---------------------------------------------------------------------------{{{-
306 ## If the given expression evaluates to True, an error is logged.
307 # @param expr The expression that must evaluate to False.
308 # @param comment Optional comment shown if expression is True.
309 # @param logOnSucces Print message in case of success.
310 # @return True if expression evaluates to False.
311 @LoggingDecorator
312 def verifyFalse (expr, comment="", logOnSuccess=None, msg=None):
313  success = not bool( expr ) # we must do this before evaluating expr for the msg string generation which may have side effects
314  if msg is None:
315  msg = u"Must evaluate to False: {} ({})".format( expr, type(expr).__name__ )
316  return success, msg, comment, shouldLogOnSuccess(logOnSuccess)
317 # ----------------------------------------------------------------------------------------------}}}-
318 
319 # -- def expectError ---------------------------------------------------------------------------{{{-
320 ## Call the given function with the given parameters and expect an error.
321 # Please note that nesting the expect{Warning, Error} methods will lead to
322 # invalid line numbers in the reports!
323 # @param function The function to be called.
324 # @param args List of parameters for the function.
325 # @param kargs Dictionary of named parameters for the function.
326 # @param errorRegExp Optional RegexObject to control which errors are handled.
327 # (Strings will be assumed to be regular expressions and compiled automatically.)
328 # @return Return value of the called function.
329 @UseStackFrameFromCallerForLogging
330 def expectError (function, args=[], kargs={}, errorRegExp=None):
331  testHelper = TestHelper.getInstance()
332  logHandler = testHelper.getLogHandler()
333  assert not logHandler is None
334 
335  errors = SuppressedErrors(errorRegExp, Logging_error)
336  try:
337  retVal = function(*args, **kargs)
338  finally:
339  errors.handleResult()
340 
341  return retVal
342 # ----------------------------------------------------------------------------------------------}}}-
343 
344 # -- def expectWarning -------------------------------------------------------------------------{{{-
345 ## Call the given function with the given parameters and expect a warning.
346 # Please note that nesting the expect{Warning, Error, Info} methods will lead to
347 # invalid line numbers in the reports!
348 # @param function The function to be called.
349 # @param args List of parameters for the function.
350 # @param kargs Dictionary of named parameters for the function.
351 # @param warningRegExp Optional RegexObject to control which warnings are handled.
352 # (Strings will be assumed to be regular expressions and compiled automatically.)
353 # @return Return value of the called function.
354 @UseStackFrameFromCallerForLogging
355 def expectWarning (function, args=[], kargs={}, warningRegExp=None):
356  testHelper = TestHelper.getInstance()
357  logHandler = testHelper.getLogHandler()
358  assert not logHandler is None
359 
360  warnings = SuppressedWarnings(warningRegExp, Logging_error)
361  try:
362  retVal = function(*args, **kargs)
363  finally:
364  warnings.handleResult()
365 
366  return retVal
367 # ----------------------------------------------------------------------------------------------}}}-
368 
369 # -- def expectWarningAndError ---------------------------------------------------------------------------{{{-
370 ## Call the given function with the given parameters and expect both a warning and an error.
371 # Please note that nesting the expect{Warning, Error, Info} methods will lead to
372 # invalid line numbers in the reports!
373 # @param function The function to be called.
374 # @param args List of parameters for the function.
375 # @param kargs Dictionary of named parameters for the function.
376 # @param errorRegExp Optional RegexObject to control which errors are handled.
377 # @param warningRegExp Optional RegexObject to control which warnings are handled.
378 # @return Return value of the called function.
379 @UseStackFrameFromCallerForLogging
380 def expectWarningAndError (function, args=[], kargs={}, errorRegExp=None, warningRegExp=None):
381  testHelper = TestHelper.getInstance()
382  logHandler = testHelper.getLogHandler()
383  assert not logHandler is None
384 
385  warnings = SuppressedWarnings(warningRegExp, Logging_error)
386  errors = SuppressedErrors(errorRegExp, Logging_error)
387  try:
388  retVal = function(*args, **kargs)
389  finally:
390  errors.handleResult()
391  warnings.handleResult()
392 
393  return retVal
394 
395 # -- def expectInfo ---------------------------------------------------------------------------{{{-
396 ## Call the given function with the given parameters and expect an info message.
397 # Please note that nesting the expect{Warning, Error, Info} methods will lead to
398 # invalid line numbers in the reports!
399 # @param function The function to be called.
400 # @param args List of parameters for the function.
401 # @param kargs Dictionary of named parameters for the function.
402 # @param infoRegExp Optional RegexObject to control which info messages are expected.
403 # @param allowUnexpectedMessages Optional flag to specify if unexpected messages are allowed.
404 # @return Return value of the called function.
405 @UseStackFrameFromCallerForLogging
406 def expectInfo (function, args=[], kargs={}, infoRegExp=None, allowUnexpectedMessages=True):
407  testHelper = TestHelper.getInstance()
408  logHandler = testHelper.getLogHandler()
409  assert not logHandler is None
410 
411  expectInfos = ExpectInfos(infoRegExp, allowUnexpectedMessages, Logging_error)
412  try:
413  retVal = function(*args, **kargs)
414  finally:
415  expectInfos.handleResult()
416 
417  return retVal
418 
419 # ----------------------------------------------------------------------------------------------}}}-
420 
421 # -- def getResultDirectory --------------------------------------------------------------------{{{-
422 ## Getter for the directory used to save results.
423 # All results should be collected in the given directory such that the
424 # generated data will be cleaned up when appropriate.
425 # @return Path to the result directory.
426 def getResultDirectory ():
427  return TestHelper.getInstance().getTestCaseResultDirectory()
428 # ----------------------------------------------------------------------------------------------}}}-
429 
430 # -- def getDataDirectory ----------------------------------------------------------------------{{{-
431 ## Method to return path to the data files.
432 # Sometimes data is required for test case that should not reside inside the
433 # repository. This is where the dataDirectory option in the test case
434 # definition comes into play. This is the value that can be changed using this
435 # tag. The default is $(LOCAL)/Data.
436 # @return The path to the directory where required data lives.
437 def getDataDirectory ():
438  return TestHelper.getInstance().getTestCaseDataDirectory()
439 # ----------------------------------------------------------------------------------------------}}}-
440 
441 # -- def getFileInDataDirectory ----------------------------------------------------------------{{{-
442 ## Returns the absolute filename of a file in the data directory using getDataDirectory().
443 # @return The absolute filename of a file in the data directory.
444 def getFileInDataDirectory (filename):
445  return os.path.join(getDataDirectory(), filename)
446 # ----------------------------------------------------------------------------------------------}}}-
447 
448 # -- def existsFileInDataDirectory -------------------------------------------------------------{{{-
449 ## Checks if the given file exists in the data directory.
450 # @return True if the file exists in the data directory, False otherwise.
451 def existsFileInDataDirectory (filename):
452  return os.path.exists(getFileInDataDirectory(filename))
453 # ----------------------------------------------------------------------------------------------}}}-
454 
455 # -- def getExternalDataDirectory --------------------------------------------------------------{{{-
456 ## Method to return path to the external data directory.
457 # Sometimes test cases require large data that should not be added to the
458 # version control. The external data directories provide a solution for such
459 # situations. If the subDirectory parameter is not given the same folder
460 # structure is used like in the calling test case.
461 # If the data directory does not exist None is returned.
462 # @params name The name of the external data directory (FME or MMS for example).
463 # @params subDirectory The sub directory in the external data store.
464 # @return The path to the directory where required data lives.
465 def getExternalDataDirectory (name, subDirectory=None):
466  testHelper = TestHelper.getInstance()
467  testCaseContext = testHelper.getTestCaseContext()
468  externalDataVariable = 'TESTCENTER_EXTERNAL_DATA_%s' % name
469  externalDataPlaceholder = '$(%s)' % externalDataVariable
470  externalDataBaseDirectory = testCaseContext.expandFilename(externalDataPlaceholder)
471  if externalDataBaseDirectory == externalDataPlaceholder:
472  raise LookupError('Variable %s could not be expanded. Please define it in your .prefs file.' %
473  externalDataVariable)
474  if os.path.isdir(externalDataBaseDirectory):
475  if subDirectory:
476  return os.path.join(externalDataBaseDirectory, subDirectory)
477  else:
478  testCasePath = testCaseContext.localPath()
479  path = mevis.MLABPackageManager.findPackageIdentifierAndRelativePath(testCasePath)[1]
480  return os.path.join(externalDataBaseDirectory, path)
481  raise ValueError('"%s" is not a directory.' % externalDataBaseDirectory)
482 # ----------------------------------------------------------------------------------------------}}}-
483 
484 # -- def pushChangeSet -------------------------------------------------------------------------{{{-
485 ## Push a new ChangeSet to the stack.
486 def pushChangeSet ():
487  return TestHelper.getInstance().pushChangeSet()
488 # ----------------------------------------------------------------------------------------------}}}-
489 
490 # -- def popChangeSet --------------------------------------------------------------------------{{{-
491 ## Pop the last ChangeSet from the stack.
492 # At least two ChangeSets (the global one and the one for the function) must
493 # remain on the stack as they are not in the control of the user.
494 def popChangeSet ():
495  retVal = True
496  if TestHelper.getInstance().getChangeSetStackLength() > 2:
497  retVal = TestHelper.getInstance().popChangeSet()
498  else:
499  retVal = False
500  return retVal
501 # ----------------------------------------------------------------------------------------------}}}-
502 
503 # -- def getHash -------------------------------------------------------------------------------{{{-
504 ## Compute a hash for the content of the given file.
505 # The hash is computed using the ImageHash module for images and using
506 # python's hashlib for other files.
507 # @param filename The file that should be hashed.
508 # @param hash The hash algorithm to use.
509 # @param encoder The encoder to use.
510 # @return The computed hash or None in case of failure.
511 def getHash (filename, hash="SHA1", encoder="Hex"):
512  result = None
513  # Images are handled using the ImageHash module!
514  b, ext = os.path.splitext(filename)
515  if ext.lower() in (".dcm", ".hdr", ".img", ".bmp", ".jpg", ".jpeg", ".png", ".pnm", ".quo", ".raw", ".tif", ".tiff"):
516  result = Image_calculateHashFromImage(filename, hash, encoder)
517  else:
518  try:
519  h = hashlib.new(hash)
520  with open(filename, "rb") as f:
521  h.update(f.read())
522  if encoder == "Hex":
523  result = h.hexdigest()
524  elif encoder == "Base64":
525  result = h.digest()
526  else:
527  Logging_error("Unknown encoder (%s) selected." % (encoder))
528  except Exception as e:
529  Logging_error("Failed to generate hash: %s." % e)
530  return result
531 # ----------------------------------------------------------------------------------------------}}}-
532 
533 # -- def createExtraTestCaseResult ----------------------------------------------------------------{{{-
534 ## Creates an ExtraTestCaseResult object.
535 # The extra test case results enable test cases to provide additional results as a test case.
536 # For example, this is used for web tests, where the web application generates an xml report.
537 # @param testCaseName The name of the test case.
538 # @param package The MeVisLab package of the test case.
539 # @param author The author of the test case.
540 # @param maintainer The current maintainer of the test case.
541 # @return The ExtraTestCaseResult object.
542 def createExtraTestCaseResult(testCaseName, package, author, duration, maintainer=None, file=None, line=None, comment=None, showTestFunctionSortingLiterals=False):
543  return ExtraTestCaseResult(testCaseName, package, author, duration, maintainer, file, line, comment, showTestFunctionSortingLiterals)
544 # ----------------------------------------------------------------------------------------------}}}-
545 
546 
547 # -- def addExtraTestCaseResult ----------------------------------------------------------------{{{-
548 ## Adds an ExtraTestCaseResult object.
549 # See createExtraTestCaseResult().
550 # @param extraTestCaseResult A ExtraTestCaseResult object.
551 def addExtraTestCaseResult(extraTestCaseResult):
552  return TestHelper.getInstance().addExtraTestCaseResult(extraTestCaseResult)
553 # ----------------------------------------------------------------------------------------------}}}-
554 
555 
556 # -- def pushEnvironment -----------------------------------------------------------------------{{{-
557 ## Pushes the current environment dictionary on a stack, so that modifications can be applied
558 # and be popped again later.
559 # See popEnvironment().
560 def pushEnvironment():
561  return TestHelper.getInstance().pushEnvironment()
562 # ----------------------------------------------------------------------------------------------}}}-
563 
564 
565 # -- def popEnvironment -----------------------------------------------------------------------{{{-
566 ## Pops the last pushed environment dictionary from a stack and makes it the current one.
567 # See pushEnvironment().
568 # @return Returns if there was an environment dictionary to pop.
569 def popEnvironment():
570  return TestHelper.getInstance().popEnvironment()
571 # ----------------------------------------------------------------------------------------------}}}-
572 
573 # -- def pushEnvironmentDecorator --------------------------------------------------------------{{{-
574 
575 # Pushes the current environment before the function is called and pops it again after it has finished
576 def pushEnvironmentDecorator (func):
577  def wrapper (*args, **kwds):
578  pushEnvironment()
579  # execute macro
580  try:
581  result = func(*args, **kwds)
582  finally:
583  popEnvironment()
584  return result
585  return wrapper
586 # ----------------------------------------------------------------------------------------------}}}-
587 
588 def EnableHighPrecisionLoggingTimeStampsDecorator(func):
589  def wrapper (*args, **kwds):
590  wereHighPrecisionTimeStampsEnabled = mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(True)
591  try:
592  result = func(*args, **kwds)
593  finally:
594  mevis.MLAB.setHighPrecisionLoggingTimeStampsEnabled(wereHighPrecisionTimeStampsEnabled)
595  return result
596  return wrapper
597 
598 class HtmlDiff(difflib.HtmlDiff):
599 
600  _styles = """
601  table.diff {font-family:monospace;border:medium;margin:0;}
602  .diff_header {background-color:#e0e0e0}
603  td.diff_header {text-align:right}
604  .diff_next {background-color:#c0c0c0}
605  .diff_add {background-color:#aaffaa}
606  .diff_chg {background-color:#ffff77}
607  .diff_sub {background-color:#ffaaaa}
608  tr, td, th {vertical-align:top;}
609  table.diff tbody {
610  white-space: -pre-wrap;
611  white-space: -o-pre-wrap;
612  white-space: -moz-pre-wrap;
613  white-space: pre-wrap;
614  word-wrap: break-word;
615  }"""
616 
617  _table_template = """
618  <table class="diff" id="difflib_chg_%(prefix)s_top"
619  cellspacing="0" cellpadding="1" rules="groups" width="100%%">
620  <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
621  <colgroup></colgroup> <colgroup></colgroup> <colgroup width="50%%"></colgroup>
622  %(header_row)s
623  <tbody>
624 %(data_rows)s </tbody>
625  </table>"""
626 
627  def __init__(self):
628  difflib.HtmlDiff.__init__(self)
629 
630  def _format_line(self,side,flag,linenum,text):
631  try:
632  linenum = '%d' % linenum
633  id = ' id="%s%s"' % (self._prefix[side],linenum)
634  except TypeError:
635  # handle blank lines where linenum is '>' or ''
636  id = ''
637  # replace those things that would get confused with HTML symbols
638  text=text.replace("&","&amp;").replace(">","&gt;").replace("<","&lt;")
639  if len(text) == 0 or text == '\n':
640  text = '<span style="display: block; background-color:#e0e0e0;">' + text + '</span>'
641  return '<td class="diff_header"%s>%s</td><td>%s</td>' \
642  % (id,linenum,text)
643 
644 # ----------------------------------------------------------------------------------------------}}}-
645 
646 # -- def pushEnvironmentDecorator --------------------------------------------------------------{{{-
647 
648 # Creates a HTML diff from the two files
649 # @param fromLines The array containing the lines of the left string.
650 # @param toLines The array containing the lines of the right string.
651 # @param showOnlyContextOfDiff If true, then only the context lines around the diff are shown, otherwise the whole content is shown.
652 # @param numberOfContextLines The number of context line only the diff context is shown.
653 def createHtmlDiff(fromLines, toLines, showOnlyContextOfDiff=False, numberOfContextLines=10):
654  diff = HtmlDiff().make_file(fromLines, toLines, "", "", showOnlyContextOfDiff, numberOfContextLines)
655  return diff
656 
657 # ----------------------------------------------------------------------------------------------}}}-
658 
659 
660 # Decorator for disabling test functions conditionally
661 def disableTestFunctionIf(condition):
662  def MLABTC__disableTestFunctionIf_Decorator(func):
663  disableTestFunction = condition() if isinstance(condition, collections.Callable) else condition
664  if disableTestFunction:
665  module = sys.modules[func.__module__]
666  if not hasattr(module, 'MLABTC_DISABLED_TEST_FUNCTIONS'):
667  setattr(module, 'MLABTC_DISABLED_TEST_FUNCTIONS', [])
668  module.MLABTC_DISABLED_TEST_FUNCTIONS.append(func.__name__)
669  return func
670  return MLABTC__disableTestFunctionIf_Decorator
671 
672 
673 # Returns True if offscreen rendering is supported by the hardware. Otherwise returns False.
674 def hardwareSupportsOffscreenRendering():
675  context = TestHelper.getInstance().getHelperContext()
676  return context.field('oSSOffscreenRender.supported').value
677 
678 
679 # Creates a subprocess.Popen instance.
680 # @param command A list containing the executable and arguments for the process.
681 # @param wait If True (default), then the function waits for the process to finish. Afterwards it checks the
682 # return code, stderr, and stdout according to the values of expectSuccess and expectFailure. Finally,
683 # a tuple (stdout, stderr, returncode) is returned.
684 # If False, then the subprocess.Popen instance is returned after it was created and started.
685 # @param expectSuccess If True (default), then this function expects the process to return 0.
686 # Stdout/stderr is logged as an info message on success, otherwise as error messages.
687 # @param expectFailure If True, then this function expects the process to return non-zero.
688 # Stdout/stderr is logged as an info message on failure, otherwise as error messages.
689 # @param verbose A boolean to indicate if stdout/stderr should be printed via Macros_INFO.
690 # @return A tuple (stdout, stderr, returncode) if wait is True, the subprocess.Popen instance otherwise.
691 def runProcess(command, wait=True, expectSuccess=True, expectFailure=False, verbose=True):
692  Macros_INFO("Running: " + " ".join(command))
693  if wait:
694  p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=os.environ)
695  else:
696  p = subprocess.Popen(command, env=os.environ)
697  if wait:
698  shortCommand = []
699  for c in command:
700  if len(os.path.splitext(c)[1]) > 0:
701  c = os.path.basename(c)
702  shortCommand.append(c)
703  shortCommand = " ".join(shortCommand)
704  stdout, stderr = p.communicate()
705  stdout = stdout.strip()
706  stderr = stderr.strip()
707  stderr = stderr.decode('utf-8','replace')
708  stdout = stdout.decode('utf-8','replace')
709  verifyProcessResult(" ".join(command), shortCommand, p.returncode, stdout, stderr, expectSuccess, expectFailure, verbose)
710  return stdout, stderr, p.returncode
711  else:
712  return p
713 
714 
715 # Helper function for runProcess().
716 # @param command A list containing the executable and arguments for the process.
717 # @param shortCommand A shorter command string to improve readability of log messages.
718 # @param returncode The returncode of the process.
719 # @param stdout The stdout output of the process.
720 # @param stderr The stderr output of the process.
721 # @param expectSuccess If True (default), then this function expects the process to return 0.
722 # Stdout/stderr is logged as an info message on success, otherwise as error messages.
723 # @param expectFailure If True, then this function expects the process to return non-zero.
724 # Stdout/stderr is logged as an info message on failure, otherwise as error messages.
725 # @param verbose A boolean to indicate if stdout/stderr should be printed via Macros_INFO.
726 def verifyProcessResult(command, shortCommand, returncode, stdout, stderr, expectSuccess=True, expectFailure=False, verbose=True):
727  if expectSuccess:
728  if returncode != 0:
729  Macros_ERROR("Command returned non-zero: '" + command + "', return code: " + str(returncode))
730  if len(stderr): Macros_ERROR(stderr)
731  if len(stdout): Macros_ERROR(stdout)
732  else:
733  msg = "Success: " + shortCommand
734  if verbose:
735  if len(stderr): msg += "\n\n" + stderr
736  if len(stdout): msg += "\n\n" + stdout
737  Macros_INFO(msg)
738  elif expectFailure:
739  if returncode == 0:
740  Macros_ERROR("Command returned zero: '" + command + "'")
741  if len(stderr): Macros_ERROR(stderr)
742  if len(stdout): Macros_ERROR(stdout)
743  else:
744  msg = "Expected failure: " + shortCommand
745  if verbose:
746  if len(stderr): msg += "\n\n" + stderr
747  if len(stdout): msg += "\n\n" + stdout
748  Macros_INFO(msg)
749 
750 
751 # Creates and starts a process from the python script using runProcess()
752 # @param wait If True (default), then the function waits for the process to finish. Afterwards it checks the
753 # return code, stderr, and stdout according to the values of expectSuccess and expectFailure. Finally,
754 # a tuple (stdout, stderr, returncode) is returned.
755 # If False, then the subprocess.Popen instance is returned after it was created and started.
756 # @param expectSuccess If True (default), then this function expects the process to return 0.
757 # Stdout/stderr is logged as an info message on success, otherwise as error messages.
758 # @param expectFailure If True, then this function expects the process to return non-zero.
759 # Stdout/stderr is logged as an info message on failure, otherwise as error messages.
760 # @param verbose A boolean to indicate if stdout/stderr should be printed via Macros_INFO.
761 # @return A tuple (stdout, stderr, returncode) if wait is True, the subprocess.Popen instance otherwise.
762 def runPythonScript(script, arguments, wait=True, expectSuccess=True, expectFailure=False, verbose=True):
763  python = mevis.MLABFileManager.getExecutable("MeVisPython")
764  command = [python, "-u", script]
765  command.extend(arguments)
766  return runProcess(command, wait=wait, expectSuccess=expectSuccess, expectFailure=expectFailure, verbose=verbose)
767 
768 
769 from .ExtraTestCaseResult import ExtraTestCaseResult
770 from .Image import calculateHashFromImage as Image_calculateHashFromImage
771 from .Logging import error as Logging_error, warning as Logging_warning, info as Logging_info
772 from .Macros import ERROR as Macros_ERROR, INFO as Macros_INFO
773 from .Math import compareFloatEqual as Math_compareFloatEqual, \
774  compareFloatLessThan as Math_compareFloatLessThan, \
775  compareFloatLessThanOrEqual as Math_compareFloatLessThanOrEqual
def decodeSourceCodeLine(filename, line)
Definition: Base.py:56
def LoggingDecorator(func)
Internal decorator used for creating messages in compare methods.
Definition: Base.py:150
def getPackageList()
Returns the list of available packages.
Definition: Base.py:72
def getSourceFileEncoding(filename)
Definition: Base.py:35
def clearEncodingCache()
Definition: Base.py:31
def getTestFunctionName()
Returns the name of the current test function.
Definition: Base.py:95
def shouldLogOnSuccess(logOnSuccess=None)
Definition: Base.py:119
def getTestCaseContext()
Get the context of the test.
Definition: Base.py:81
def getModuleName()
Return the name of the currently tested module.
Definition: Base.py:89
def setMacrosShouldLogOnSuccess(logOnSuccess)
Globally enables or disables if the ASSERT_*/EXPECT_* macros log an info message on success...
Definition: Base.py:116
Decorator to globally enable or disable if the ASSERT_*/EXPECT_* macros log an info message on succes...
Definition: Base.py:132
def escapedUnicode(value)
Definition: Base.py:168