PageRenderTime 82ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/cxxtest/python/python3/cxxtest/cxxtest_parser.py

https://github.com/michaelkook/GraphLab-2
Python | 242 lines | 201 code | 13 blank | 28 comment | 15 complexity | f587019e0cdefc4df0a7a584a8632c0b MD5 | raw file
Possible License(s): ISC, Apache-2.0
  1. #-------------------------------------------------------------------------
  2. # CxxTest: A lightweight C++ unit testing library.
  3. # Copyright (c) 2008 Sandia Corporation.
  4. # This software is distributed under the LGPL License v2.1
  5. # For more information, see the COPYING file in the top CxxTest directory.
  6. # Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  7. # the U.S. Government retains certain rights in this software.
  8. #-------------------------------------------------------------------------
  9. import codecs
  10. import re
  11. #import sys
  12. #import getopt
  13. #import glob
  14. from cxxtest.cxxtest_misc import abort
  15. # Global variables
  16. suites = []
  17. suite = None
  18. inBlock = 0
  19. options=None
  20. def scanInputFiles(files, _options):
  21. '''Scan all input files for test suites'''
  22. global options
  23. options=_options
  24. for file in files:
  25. scanInputFile(file)
  26. global suites
  27. if len(suites) is 0 and not options.root:
  28. abort( 'No tests defined' )
  29. return [options,suites]
  30. lineCont_re = re.compile('(.*)\\\s*$')
  31. def scanInputFile(fileName):
  32. '''Scan single input file for test suites'''
  33. # mode 'rb' is problematic in python3 - byte arrays don't behave the same as
  34. # strings.
  35. # As far as the choice of the default encoding: utf-8 chews through
  36. # everything that the previous ascii codec could, plus most of new code.
  37. # TODO: figure out how to do this properly - like autodetect encoding from
  38. # file header.
  39. file = codecs.open(fileName, mode='r', encoding='utf-8')
  40. prev = ""
  41. lineNo = 0
  42. contNo = 0
  43. while 1:
  44. line = file.readline()
  45. if not line:
  46. break
  47. lineNo += 1
  48. m = lineCont_re.match(line)
  49. if m:
  50. prev += m.group(1) + " "
  51. contNo += 1
  52. else:
  53. scanInputLine( fileName, lineNo - contNo, prev + line )
  54. contNo = 0
  55. prev = ""
  56. if contNo:
  57. scanInputLine( fileName, lineNo - contNo, prev + line )
  58. closeSuite()
  59. file.close()
  60. def scanInputLine( fileName, lineNo, line ):
  61. '''Scan single input line for interesting stuff'''
  62. scanLineForExceptionHandling( line )
  63. scanLineForStandardLibrary( line )
  64. scanLineForSuiteStart( fileName, lineNo, line )
  65. global suite
  66. if suite:
  67. scanLineInsideSuite( suite, lineNo, line )
  68. def scanLineInsideSuite( suite, lineNo, line ):
  69. '''Analyze line which is part of a suite'''
  70. global inBlock
  71. if lineBelongsToSuite( suite, lineNo, line ):
  72. scanLineForTest( suite, lineNo, line )
  73. scanLineForCreate( suite, lineNo, line )
  74. scanLineForDestroy( suite, lineNo, line )
  75. def lineBelongsToSuite( suite, lineNo, line ):
  76. '''Returns whether current line is part of the current suite.
  77. This can be false when we are in a generated suite outside of CXXTEST_CODE() blocks
  78. If the suite is generated, adds the line to the list of lines'''
  79. if not suite['generated']:
  80. return 1
  81. global inBlock
  82. if not inBlock:
  83. inBlock = lineStartsBlock( line )
  84. if inBlock:
  85. inBlock = addLineToBlock( suite, lineNo, line )
  86. return inBlock
  87. std_re = re.compile( r"\b(std\s*::|CXXTEST_STD|using\s+namespace\s+std\b|^\s*\#\s*include\s+<[a-z0-9]+>)" )
  88. def scanLineForStandardLibrary( line ):
  89. '''Check if current line uses standard library'''
  90. global options
  91. if not options.haveStandardLibrary and std_re.search(line):
  92. if not options.noStandardLibrary:
  93. options.haveStandardLibrary = 1
  94. exception_re = re.compile( r"\b(throw|try|catch|TSM?_ASSERT_THROWS[A-Z_]*)\b" )
  95. def scanLineForExceptionHandling( line ):
  96. '''Check if current line uses exception handling'''
  97. global options
  98. if not options.haveExceptionHandling and exception_re.search(line):
  99. if not options.noExceptionHandling:
  100. options.haveExceptionHandling = 1
  101. classdef = '(?:::\s*)?(?:\w+\s*::\s*)*\w+'
  102. baseclassdef = '(?:public|private|protected)\s+%s' % (classdef,)
  103. general_suite = r"\bclass\s+(%s)\s*:(?:\s*%s\s*,)*\s*public\s+" \
  104. % (classdef, baseclassdef,)
  105. testsuite = '(?:(?:::)?\s*CxxTest\s*::\s*)?TestSuite'
  106. suites_re = { re.compile( general_suite + testsuite ) : None }
  107. generatedSuite_re = re.compile( r'\bCXXTEST_SUITE\s*\(\s*(\w*)\s*\)' )
  108. def scanLineForSuiteStart( fileName, lineNo, line ):
  109. '''Check if current line starts a new test suite'''
  110. for i in list(suites_re.items()):
  111. m = i[0].search( line )
  112. if m:
  113. suite = startSuite( m.group(1), fileName, lineNo, 0 )
  114. if i[1] is not None:
  115. for test in i[1]['tests']:
  116. addTest(suite, test['name'], test['line'])
  117. break
  118. m = generatedSuite_re.search( line )
  119. if m:
  120. sys.stdout.write( "%s:%s: Warning: Inline test suites are deprecated.\n" % (fileName, lineNo) )
  121. startSuite( m.group(1), fileName, lineNo, 1 )
  122. def startSuite( name, file, line, generated ):
  123. '''Start scanning a new suite'''
  124. global suite
  125. closeSuite()
  126. object_name = name.replace(':',"_")
  127. suite = { 'name' : name,
  128. 'file' : file,
  129. 'cfile' : cstr(file),
  130. 'line' : line,
  131. 'generated' : generated,
  132. 'object' : 'suite_%s' % object_name,
  133. 'dobject' : 'suiteDescription_%s' % object_name,
  134. 'tlist' : 'Tests_%s' % object_name,
  135. 'tests' : [],
  136. 'lines' : [] }
  137. suites_re[re.compile( general_suite + name )] = suite
  138. return suite
  139. def lineStartsBlock( line ):
  140. '''Check if current line starts a new CXXTEST_CODE() block'''
  141. return re.search( r'\bCXXTEST_CODE\s*\(', line ) is not None
  142. test_re = re.compile( r'^([^/]|/[^/])*\bvoid\s+([Tt]est\w+)\s*\(\s*(void)?\s*\)' )
  143. def scanLineForTest( suite, lineNo, line ):
  144. '''Check if current line starts a test'''
  145. m = test_re.search( line )
  146. if m:
  147. addTest( suite, m.group(2), lineNo )
  148. def addTest( suite, name, line ):
  149. '''Add a test function to the current suite'''
  150. test = { 'name' : name,
  151. 'suite' : suite,
  152. 'class' : 'TestDescription_%s_%s' % (suite['object'], name),
  153. 'object' : 'testDescription_%s_%s' % (suite['object'], name),
  154. 'line' : line,
  155. }
  156. suite['tests'].append( test )
  157. def addLineToBlock( suite, lineNo, line ):
  158. '''Append the line to the current CXXTEST_CODE() block'''
  159. line = fixBlockLine( suite, lineNo, line )
  160. line = re.sub( r'^.*\{\{', '', line )
  161. e = re.search( r'\}\}', line )
  162. if e:
  163. line = line[:e.start()]
  164. suite['lines'].append( line )
  165. return e is None
  166. def fixBlockLine( suite, lineNo, line):
  167. '''Change all [E]TS_ macros used in a line to _[E]TS_ macros with the correct file/line'''
  168. return re.sub( r'\b(E?TSM?_(ASSERT[A-Z_]*|FAIL))\s*\(',
  169. r'_\1(%s,%s,' % (suite['cfile'], lineNo),
  170. line, 0 )
  171. create_re = re.compile( r'\bstatic\s+\w+\s*\*\s*createSuite\s*\(\s*(void)?\s*\)' )
  172. def scanLineForCreate( suite, lineNo, line ):
  173. '''Check if current line defines a createSuite() function'''
  174. if create_re.search( line ):
  175. addSuiteCreateDestroy( suite, 'create', lineNo )
  176. destroy_re = re.compile( r'\bstatic\s+void\s+destroySuite\s*\(\s*\w+\s*\*\s*\w*\s*\)' )
  177. def scanLineForDestroy( suite, lineNo, line ):
  178. '''Check if current line defines a destroySuite() function'''
  179. if destroy_re.search( line ):
  180. addSuiteCreateDestroy( suite, 'destroy', lineNo )
  181. def cstr( s ):
  182. '''Convert a string to its C representation'''
  183. return '"' + s.replace( '\\', '\\\\' ) + '"'
  184. def addSuiteCreateDestroy( suite, which, line ):
  185. '''Add createSuite()/destroySuite() to current suite'''
  186. if which in suite:
  187. abort( '%s:%s: %sSuite() already declared' % ( suite['file'], str(line), which ) )
  188. suite[which] = line
  189. def closeSuite():
  190. '''Close current suite and add it to the list if valid'''
  191. global suite
  192. if suite is not None:
  193. if len(suite['tests']) is not 0:
  194. verifySuite(suite)
  195. rememberSuite(suite)
  196. suite = None
  197. def verifySuite(suite):
  198. '''Verify current suite is legal'''
  199. if 'create' in suite and 'destroy' not in suite:
  200. abort( '%s:%s: Suite %s has createSuite() but no destroySuite()' %
  201. (suite['file'], suite['create'], suite['name']) )
  202. elif 'destroy' in suite and 'create' not in suite:
  203. abort( '%s:%s: Suite %s has destroySuite() but no createSuite()' %
  204. (suite['file'], suite['destroy'], suite['name']) )
  205. def rememberSuite(suite):
  206. '''Add current suite to list'''
  207. global suites
  208. suites.append( suite )