Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

360 lignes
10 KiB

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