Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

333 wiersze
9.1 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe
  5. import unittest, json, sys
  6. import xmlrunner
  7. import importlib
  8. from frappe.modules import load_doctype_module, get_module_name
  9. from frappe.utils import cstr
  10. import frappe.utils.scheduler
  11. import cProfile, StringIO, pstats
  12. unittest_runner = unittest.TextTestRunner
  13. def xmlrunner_wrapper(output):
  14. """Convenience wrapper to keep method signature unchanged for XMLTestRunner and TextTestRunner"""
  15. def _runner(*args, **kwargs):
  16. kwargs['output'] = output
  17. return xmlrunner.XMLTestRunner(*args, **kwargs)
  18. return _runner
  19. def main(app=None, module=None, doctype=None, verbose=False, tests=(), force=False, profile=False, junit_xml_output=None):
  20. global unittest_runner
  21. xmloutput_fh = None
  22. if junit_xml_output:
  23. xmloutput_fh = open(junit_xml_output, 'w')
  24. unittest_runner = xmlrunner_wrapper(xmloutput_fh)
  25. else:
  26. unittest_runner = unittest.TextTestRunner
  27. try:
  28. frappe.flags.print_messages = verbose
  29. frappe.flags.in_test = True
  30. if not frappe.db:
  31. frappe.connect()
  32. # if not frappe.conf.get("db_name").startswith("test_"):
  33. # raise Exception, 'db_name must start with "test_"'
  34. # workaround! since there is no separate test db
  35. frappe.clear_cache()
  36. frappe.utils.scheduler.disable_scheduler()
  37. set_test_email_config()
  38. if verbose:
  39. print 'Running "before_tests" hooks'
  40. for fn in frappe.get_hooks("before_tests", app_name=app):
  41. frappe.get_attr(fn)()
  42. if doctype:
  43. ret = run_tests_for_doctype(doctype, verbose, tests, force, profile)
  44. elif module:
  45. ret = run_tests_for_module(module, verbose, tests, profile)
  46. else:
  47. ret = run_all_tests(app, verbose, profile)
  48. frappe.db.commit()
  49. # workaround! since there is no separate test db
  50. frappe.clear_cache()
  51. return ret
  52. finally:
  53. if xmloutput_fh:
  54. xmloutput_fh.flush()
  55. xmloutput_fh.close()
  56. def set_test_email_config():
  57. frappe.conf.update({
  58. "auto_email_id": "test@example.com",
  59. "mail_server": "smtp.example.com",
  60. "mail_login": "test@example.com",
  61. "mail_password": "test",
  62. "admin_password": "admin"
  63. })
  64. def run_all_tests(app=None, verbose=False, profile=False):
  65. import os
  66. apps = [app] if app else frappe.get_installed_apps()
  67. test_suite = unittest.TestSuite()
  68. for app in apps:
  69. for path, folders, files in os.walk(frappe.get_pymodule_path(app)):
  70. for dontwalk in ('locals', '.git', 'public'):
  71. if dontwalk in folders:
  72. folders.remove(dontwalk)
  73. # print path
  74. for filename in files:
  75. filename = cstr(filename)
  76. if filename.startswith("test_") and filename.endswith(".py"):
  77. # print filename[:-3]
  78. _add_test(app, path, filename, verbose, test_suite=test_suite)
  79. if profile:
  80. pr = cProfile.Profile()
  81. pr.enable()
  82. out = unittest_runner(verbosity=1+(verbose and 1 or 0)).run(test_suite)
  83. if profile:
  84. pr.disable()
  85. s = StringIO.StringIO()
  86. ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')
  87. ps.print_stats()
  88. print s.getvalue()
  89. return out
  90. def run_tests_for_doctype(doctype, verbose=False, tests=(), force=False, profile=False):
  91. module = frappe.db.get_value("DocType", doctype, "module")
  92. if not module:
  93. print 'Invalid doctype {0}'.format(doctype)
  94. sys.exit(1)
  95. test_module = get_module_name(doctype, module, "test_")
  96. if force:
  97. for name in frappe.db.sql_list("select name from `tab%s`" % doctype):
  98. frappe.delete_doc(doctype, name, force=True)
  99. make_test_records(doctype, verbose=verbose, force=force)
  100. module = frappe.get_module(test_module)
  101. return _run_unittest(module, verbose=verbose, tests=tests, profile=profile)
  102. def run_tests_for_module(module, verbose=False, tests=(), profile=False):
  103. module = importlib.import_module(module)
  104. if hasattr(module, "test_dependencies"):
  105. for doctype in module.test_dependencies:
  106. make_test_records(doctype, verbose=verbose)
  107. return _run_unittest(module=module, verbose=verbose, tests=tests, profile=profile)
  108. def _run_unittest(module, verbose=False, tests=(), profile=False):
  109. test_suite = unittest.TestSuite()
  110. module_test_cases = unittest.TestLoader().loadTestsFromModule(module)
  111. if tests:
  112. for each in module_test_cases:
  113. for test_case in each.__dict__["_tests"]:
  114. if test_case.__dict__["_testMethodName"] in tests:
  115. test_suite.addTest(test_case)
  116. else:
  117. test_suite.addTest(module_test_cases)
  118. if profile:
  119. pr = cProfile.Profile()
  120. pr.enable()
  121. out = unittest_runner(verbosity=1+(verbose and 1 or 0)).run(test_suite)
  122. if profile:
  123. pr.disable()
  124. s = StringIO.StringIO()
  125. ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')
  126. ps.print_stats()
  127. print s.getvalue()
  128. return out
  129. def _add_test(app, path, filename, verbose, test_suite=None):
  130. import os
  131. if os.path.sep.join(["doctype", "doctype", "boilerplate"]) in path:
  132. # in /doctype/doctype/boilerplate/
  133. return
  134. app_path = frappe.get_pymodule_path(app)
  135. relative_path = os.path.relpath(path, app_path)
  136. if relative_path=='.':
  137. module_name = app
  138. else:
  139. module_name = '{app}.{relative_path}.{module_name}'.format(app=app,
  140. relative_path=relative_path.replace('/', '.'), module_name=filename[:-3])
  141. module = frappe.get_module(module_name)
  142. if getattr(module, "selenium_tests", False) and not frappe.conf.run_selenium_tests:
  143. return
  144. if not test_suite:
  145. test_suite = unittest.TestSuite()
  146. if os.path.basename(os.path.dirname(path))=="doctype":
  147. txt_file = os.path.join(path, filename[5:].replace(".py", ".json"))
  148. with open(txt_file, 'r') as f:
  149. doc = json.loads(f.read())
  150. doctype = doc["name"]
  151. make_test_records(doctype, verbose)
  152. test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module))
  153. def make_test_records(doctype, verbose=0, force=False):
  154. if not frappe.db:
  155. frappe.connect()
  156. for options in get_dependencies(doctype):
  157. if options == "[Select]":
  158. continue
  159. if not options in frappe.local.test_objects:
  160. if options in frappe.local.test_objects:
  161. print "No test records or circular reference for {0}".format(options)
  162. frappe.local.test_objects[options] = []
  163. make_test_records(options, verbose, force)
  164. make_test_records_for_doctype(options, verbose, force)
  165. def get_modules(doctype):
  166. module = frappe.db.get_value("DocType", doctype, "module")
  167. try:
  168. test_module = load_doctype_module(doctype, module, "test_")
  169. if test_module:
  170. reload(test_module)
  171. except ImportError:
  172. test_module = None
  173. return module, test_module
  174. def get_dependencies(doctype):
  175. module, test_module = get_modules(doctype)
  176. meta = frappe.get_meta(doctype)
  177. link_fields = meta.get_link_fields()
  178. for df in meta.get_table_fields():
  179. link_fields.extend(frappe.get_meta(df.options).get_link_fields())
  180. options_list = [df.options for df in link_fields] + [doctype]
  181. if hasattr(test_module, "test_dependencies"):
  182. options_list += test_module.test_dependencies
  183. options_list = list(set(options_list))
  184. if hasattr(test_module, "test_ignore"):
  185. for doctype_name in test_module.test_ignore:
  186. if doctype_name in options_list:
  187. options_list.remove(doctype_name)
  188. return options_list
  189. def make_test_records_for_doctype(doctype, verbose=0, force=False):
  190. module, test_module = get_modules(doctype)
  191. if verbose:
  192. print "Making for " + doctype
  193. if hasattr(test_module, "_make_test_records"):
  194. frappe.local.test_objects[doctype] += test_module._make_test_records(verbose)
  195. elif hasattr(test_module, "test_records"):
  196. frappe.local.test_objects[doctype] += make_test_objects(doctype, test_module.test_records, verbose)
  197. else:
  198. test_records = frappe.get_test_records(doctype)
  199. if test_records:
  200. frappe.local.test_objects[doctype] += make_test_objects(doctype, test_records, verbose)
  201. elif verbose:
  202. print_mandatory_fields(doctype)
  203. def make_test_objects(doctype, test_records, verbose=None):
  204. records = []
  205. # if not frappe.get_meta(doctype).issingle:
  206. # existing = frappe.get_all(doctype, filters={"name":("like", "_T-" + doctype + "-%")})
  207. # if existing:
  208. # return [d.name for d in existing]
  209. #
  210. # existing = frappe.get_all(doctype, filters={"name":("like", "_Test " + doctype + "%")})
  211. # if existing:
  212. # return [d.name for d in existing]
  213. for doc in test_records:
  214. if not doc.get("doctype"):
  215. doc["doctype"] = doctype
  216. d = frappe.copy_doc(doc)
  217. if doc.get('name'):
  218. d.name = doc.get('name')
  219. if frappe.local.test_objects.get(d.doctype):
  220. # do not create test records, if already exists
  221. return []
  222. if d.meta.get_field("naming_series"):
  223. if not d.naming_series:
  224. d.naming_series = "_T-" + d.doctype + "-"
  225. # submit if docstatus is set to 1 for test record
  226. docstatus = d.docstatus
  227. d.docstatus = 0
  228. try:
  229. d.run_method("before_test_insert")
  230. d.insert()
  231. if docstatus == 1:
  232. d.submit()
  233. except frappe.NameError:
  234. pass
  235. except Exception, e:
  236. if d.flags.ignore_these_exceptions_in_test and e.__class__ in d.flags.ignore_these_exceptions_in_test:
  237. pass
  238. else:
  239. raise
  240. records.append(d.name)
  241. frappe.db.commit()
  242. return records
  243. def print_mandatory_fields(doctype):
  244. print "Please setup make_test_records for: " + doctype
  245. print "-" * 60
  246. meta = frappe.get_meta(doctype)
  247. print "Autoname: " + (meta.autoname or "")
  248. print "Mandatory Fields: "
  249. for d in meta.get("fields", {"reqd":1}):
  250. print d.parent + ":" + d.fieldname + " | " + d.fieldtype + " | " + (d.options or "")
  251. print