convert.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #!/usr/bin/env python
  2. # convert.py: This script converts a Boost.Test unit test into an
  3. # equivalent Python unit test.
  4. #
  5. # Copyright (c) 2003-2016, John Wiegley. All rights reserved.
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions are
  9. # met:
  10. #
  11. # - Redistributions of source code must retain the above copyright
  12. # notice, this list of conditions and the following disclaimer.
  13. #
  14. # - Redistributions in binary form must reproduce the above copyright
  15. # notice, this list of conditions and the following disclaimer in the
  16. # documentation and/or other materials provided with the distribution.
  17. #
  18. # - Neither the name of New Artisans LLC nor the names of its
  19. # contributors may be used to endorse or promote products derived from
  20. # this software without specific prior written permission.
  21. #
  22. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  28. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  30. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  32. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. import re
  34. import sys
  35. import os
  36. source = os.path.abspath(sys.argv[1])
  37. base = os.path.splitext(source)[0]
  38. target = os.path.abspath(sys.argv[2])
  39. dirname = os.path.dirname(target)
  40. if not os.path.isdir(dirname):
  41. try: os.makedirs(dirname)
  42. except: pass
  43. fd = open(source, "r")
  44. fo = open(target, "w")
  45. fo.write('''# -*- coding: utf-8 -*-
  46. import unittest
  47. import exceptions
  48. import operator
  49. from ledger import *
  50. from StringIO import *
  51. from datetime import *
  52. internalAmount = Amount.exact
  53. class %sTestCase(unittest.TestCase):
  54. testSession = None''' % os.path.basename(base))
  55. not_for_python = 0
  56. class_name = None
  57. for line in fd.readlines():
  58. if re.match('^#ifndef NOT_FOR_PYTHON', line):
  59. not_for_python += 1
  60. continue
  61. elif not_for_python > 0:
  62. if re.match('^#endif // NOT_FOR_PYTHON', line):
  63. not_for_python -= 1
  64. continue
  65. if re.match('^(using|CPP|[#{}/])', line):
  66. continue
  67. if re.match('^\s+[{}]\s+$', line):
  68. continue
  69. if not re.search('assert', line):
  70. match = re.match('^};', line)
  71. if match:
  72. continue
  73. match = re.match('BOOST_.*_TEST_SUITE', line)
  74. if match:
  75. continue
  76. match = re.match('^struct (.*?) {', line)
  77. if match:
  78. class_name = match.group(1)
  79. continue
  80. if class_name:
  81. match = re.search('(~)?%s\(\) {' % class_name, line)
  82. if match:
  83. if match.group(1):
  84. fo.write(' def tearDown(self):\n')
  85. else:
  86. fo.write(' def setUp(self):\n')
  87. continue
  88. match = re.match('BOOST_AUTO_TEST_CASE\((.+?)\)', line)
  89. if match:
  90. fo.write(' def %s(self):\n' % match.group(1))
  91. continue
  92. match = re.match('void [^:]+::(test[^(]+|setUp|tearDown)\(\)', line)
  93. if match:
  94. fo.write(' def %s(self):\n' % match.group(1))
  95. continue
  96. match = re.search(' ([a-z:_<>]+?)&?\s+([a-z0-9_]+)(\((.+?)\))?;', line)
  97. if match:
  98. if match.group(1) != "std::string":
  99. line = ' %s = %s(%s)\n' % (match.group(2), match.group(1),
  100. match.group(4) or "")
  101. else:
  102. line = ''
  103. match = re.search(' ([a-z:_<>]+?)&?\s+([a-z0-9]+)\s*=\s*([^(]+);', line)
  104. if match:
  105. line = ' %s = %s(%s)\n' % (match.group(2), match.group(1),
  106. match.group(3))
  107. match = re.search(' ([a-z:_<>]+?)\s+([a-z0-9]+)\s*=\s*(.+?)$', line)
  108. if match:
  109. line = ' %s = %s\n' % (match.group(2), match.group(3))
  110. line = re.sub('BOOST_CHECK_NE', 'self.assertNotEqual', line)
  111. line = re.sub('BOOST_CHECK_EQUAL', 'self.assertEqual', line)
  112. line = re.sub('BOOST_CHECK_THROW\(([^,]+), ([^,)]+?)\)',
  113. 'self.assertRaises(\\2, lambda: \\1)', line)
  114. line = re.sub('BOOST_CHECK', 'self.assertTrue', line)
  115. # jww (2010-06-20): Determine this list automatically by scanning
  116. # the class_ lines in src/py_*.cc
  117. line = re.sub('amount_t::precision_t\(([^)]+?)\)', '\\1', line)
  118. line = re.sub('amount_t::', 'Amount.', line)
  119. line = re.sub('Amount\.PARSE_', 'AmountParse.', line)
  120. line = re.sub('commodity_t\(([^)]+?)\)', '\\1', line)
  121. line = re.sub('commodity_t::', 'Commodity.', line)
  122. line = re.sub('balance_t::', 'Balance.', line)
  123. line = re.sub('balance_pair_t::', 'BalancePair.', line)
  124. line = re.sub('value_t::', 'Value.', line)
  125. line = re.sub('amount_t', 'Amount', line)
  126. line = re.sub('commodity_t', 'Commodity', line)
  127. line = re.sub('balance_t', 'Balance', line)
  128. line = re.sub('balance_pair_t', 'BalancePair', line)
  129. line = re.sub('value_t', 'Value', line)
  130. line = re.sub("PARSE_DEFAULT", "ParseFlags.Default", line)
  131. line = re.sub("PARSE_PARTIAL", "ParseFlags.Partial", line)
  132. line = re.sub("PARSE_SINGLE", "ParseFlags.Single", line)
  133. line = re.sub("PARSE_NO_MIGRATE", "ParseFlags.NoMigrate", line)
  134. line = re.sub("PARSE_NO_REDUCE", "ParseFlags.NoReduce", line)
  135. line = re.sub("PARSE_NO_ASSIGN", "ParseFlags.NoAssign", line)
  136. line = re.sub("PARSE_NO_DATES", "ParseFlags.NoDates", line)
  137. line = re.sub("PARSE_OP_CONTEXT", "ParseFlags.OpContext", line)
  138. line = re.sub("PARSE_SOFT_FAIL", "ParseFlags.SoftFail", line)
  139. line = re.sub('ledger::', '', line)
  140. line = re.sub('std::istringstream', 'StringIO', line)
  141. line = re.sub('std::ostringstream', 'StringIO', line)
  142. line = re.sub('set_session_context\(&session\)',
  143. 'self.testSession = session()\n set_session_context(self.testSession)', line)
  144. line = re.sub('set_session_context\(\)',
  145. 'set_session_context()\n self.testSession = None', line)
  146. line = re.sub('([a-z_]+?)_t\b', '\\1', line)
  147. line = re.sub('("[^"]+")', 'u\\1', line)
  148. line = re.sub('std::string\(([^)]+?)\)', '\\1', line)
  149. line = re.sub('string\(([^)]+?)\)', '\\1', line)
  150. line = re.sub('\.print\(([^)]+?)\)', '.print_(\\1)', line)
  151. line = re.sub('true', 'True', line)
  152. line = re.sub('false', 'False', line)
  153. line = re.sub('CURRENT_TIME\(\)', 'datetime.now()', line)
  154. line = re.sub('CURRENT_DATE\(\)', 'date.today()', line)
  155. line = re.sub('commodity\(\)', 'commodity', line)
  156. line = re.sub('precision\(\)', 'precision', line)
  157. line = re.sub('([0-9]+)[FL]', '\\1', line)
  158. line = re.sub('([0-9]+)UL', '\\1L', line)
  159. line = re.sub(';', '', line)
  160. line = re.sub('//', '#', line)
  161. line = re.sub('->', '.', line)
  162. line = re.sub('(\s+|\()(\S+?) \? (.+?) : (.+?)\)',
  163. '\\1\\3 if \\2 else \\4)', line)
  164. line = re.sub('if \((.+?)\)( {)?$', 'if \\1:', line)
  165. line = re.sub('(} )?else( {)?$', 'else:', line)
  166. line = re.sub('amount_error', 'exceptions.ArithmeticError', line)
  167. match = re.match('^ ', line)
  168. if match:
  169. fo.write(' ' + line)
  170. else:
  171. fo.write(line)
  172. fo.write('''
  173. def suite():
  174. return unittest.TestLoader().loadTestsFromTestCase(%sTestCase)
  175. if __name__ == '__main__':
  176. unittest.main()
  177. ''' % os.path.basename(base))
  178. fo.close()
  179. fd.close()