nsdk_report.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #!/usr/bin/env python3
  2. import os
  3. import sys
  4. import time
  5. import copy
  6. import glob
  7. import serial
  8. import tempfile
  9. import json
  10. import argparse
  11. from prettytable import *
  12. from nsdk_utils import *
  13. def get_expected(config, app, cfg_name):
  14. if isinstance(config, dict) == False:
  15. return None
  16. found_app_expected = find_local_appconfig(app, config.get("expected", dict()))
  17. found_app_expecteds = find_local_appconfig(app, config.get("expecteds", dict()))
  18. if found_app_expected:
  19. app_expected = copy.deepcopy(config.get("expected", dict()).get(found_app_expected))
  20. else:
  21. app_expected = dict()
  22. if found_app_expecteds:
  23. app_cfgexpected = config.get("expecteds", dict()).get(found_app_expecteds).get(cfg_name, dict())
  24. else:
  25. app_cfgexpected = dict()
  26. app_expected.update(app_cfgexpected)
  27. return app_expected
  28. def check_expected(build_status, config, run=False):
  29. if isinstance(build_status, dict) == False or isinstance(config, dict) == False:
  30. return False
  31. ret = True
  32. for app in build_status:
  33. app_allstatus = build_status[app]
  34. for cfgname in app_allstatus:
  35. app_status = app_allstatus[cfgname]["status"]
  36. build_ret = app_status.get("build", False)
  37. run_ret = app_status.get("run", False)
  38. app_cfg_expected = get_expected(config, app, cfgname)
  39. if isinstance(app_cfg_expected, dict):
  40. expected_build_ret = app_cfg_expected.get("build", True)
  41. expected_run_ret = app_cfg_expected.get("run", True)
  42. if expected_build_ret != build_ret:
  43. ret = False
  44. if run:
  45. if expected_run_ret != run_ret:
  46. ret = False
  47. else:
  48. if build_ret == False:
  49. ret = False
  50. if run:
  51. if run_ret == False:
  52. ret = False
  53. return ret
  54. def save_results(appcfg, hwcfg, mergedcfg, result, savedir):
  55. if not (isinstance(savedir, str) and os.path.isdir(savedir)):
  56. return
  57. if isinstance(appcfg, dict):
  58. sfn = os.path.join(savedir, "appcfg.json")
  59. save_json(sfn, appcfg)
  60. if isinstance(hwcfg, dict):
  61. sfn = os.path.join(savedir, "hwcfg.json")
  62. save_json(sfn, hwcfg)
  63. if isinstance(mergedcfg, dict):
  64. sfn = os.path.join(savedir, "mergedcfg.json")
  65. save_json(sfn, mergedcfg)
  66. if isinstance(result, dict):
  67. sfn = os.path.join(savedir, "result.json")
  68. save_json(sfn, result)
  69. pass
  70. def analyze_report(config, result, runapp=False):
  71. apps_status = dict()
  72. passed_apps = dict()
  73. failed_apps = dict()
  74. build_cfgs = dict()
  75. # get build configs used per cfgname
  76. if "build_configs" not in config:
  77. build_cfgs["default"] = config.get("build_config", dict())
  78. else:
  79. glb_buildcfg = config.get("build_config", dict())
  80. sub_configs = config["build_configs"]
  81. for cfgname in sub_configs:
  82. bcfg = copy.deepcopy(glb_buildcfg)
  83. bcfg.update(sub_configs[cfgname])
  84. build_cfgs[cfgname] = bcfg
  85. def check_app_status(status, expected, runapp=False):
  86. app_sts = {"expected": True, "exp_build": True, "exp_run": True, "build": True, "run": True}
  87. for cfgname in status:
  88. app_cfg_expected = get_expected(config, app, cfgname)
  89. expected_build = app_cfg_expected.get("build", True)
  90. expected_run = app_cfg_expected.get("run", True)
  91. real_build = status[cfgname]["status"].get("build", False)
  92. real_run = status[cfgname]["status"].get("run", False)
  93. if expected_build != real_build:
  94. app_sts["exp_build"] = False
  95. if expected_run != real_run:
  96. app_sts["exp_run"] = False
  97. if real_build == False:
  98. app_sts["build"] = False
  99. if real_run == False:
  100. app_sts["run"] = False
  101. if runapp:
  102. app_sts["expected"] = app_sts["exp_build"] and app_sts["exp_run"]
  103. else:
  104. app_sts["expected"] = app_sts["exp_build"]
  105. return app_sts
  106. apps_expected = config.get("expected", dict())
  107. # Get app status compared with expected
  108. for app in result:
  109. app_expected = apps_expected.get(app, dict())
  110. app_status = result[app]
  111. apps_status[app] = check_app_status(app_status, app_expected, runapp)
  112. if apps_status[app]["expected"] == True:
  113. passed_apps[app] = copy.deepcopy(apps_status[app])
  114. else:
  115. failed_apps[app] = copy.deepcopy(apps_status[app])
  116. # Create report_dict
  117. report_dict = {"passed": passed_apps, "failed": failed_apps, "status": apps_status, "configs": build_cfgs}
  118. return report_dict
  119. def generate_build_run_status_md(appresult, logdir, bold_false=True):
  120. if isinstance(appresult, dict) == False:
  121. if bold_false:
  122. return "**False**", "**False**"
  123. else:
  124. return "False", "False"
  125. else:
  126. appblog = appresult["logs"].get("build", None)
  127. apprlog = appresult["logs"].get("run", None)
  128. appbsts = appresult["status"].get("build", False)
  129. apprsts = appresult["status"].get("run", False)
  130. def gen_sts_md(sts, log, bold=True):
  131. if log:
  132. log = os.path.relpath(log, logdir)
  133. if bool(bold) ^ bool((not sts)):
  134. sts_md = "[%s](%s)" % (sts, log)
  135. else:
  136. sts_md = "[**%s**](%s)" % (sts, log)
  137. else:
  138. if bool(bold) ^ bool((not sts)):
  139. sts_md = "%s" % (sts)
  140. else:
  141. sts_md = "**%s**" % (sts)
  142. return sts_md
  143. bsts_md = gen_sts_md(appbsts, appblog, bold_false)
  144. rsts_md = gen_sts_md(apprsts, apprlog, bold_false)
  145. return bsts_md, rsts_md
  146. def generate_report(config, result, rptfile, logdir, runapp=False):
  147. if not(isinstance(config, dict) and isinstance(result, dict) and isinstance(rptfile, str)):
  148. return False
  149. report = analyze_report(config, result, runapp)
  150. with open(rptfile, "w") as rf:
  151. rf.write("# Tested Nuclei SDK Applications/Test Cases\r\n")
  152. if len(report["passed"]) > 0:
  153. rf.write("## Passed\r\n")
  154. x = PrettyTable()
  155. x.set_style(MARKDOWN)
  156. x.field_names = ["App/Test Case", "All as Expected", "Build As Expected", "Run As Expected", "Build Status", "Run Status"]
  157. for app in report["passed"]:
  158. app_sts = report["passed"][app]
  159. x.add_row([app, app_sts["expected"], app_sts["exp_build"], app_sts["exp_run"], \
  160. app_sts["build"], app_sts["run"]])
  161. rf.write(str(x))
  162. if len(report["failed"]) > 0:
  163. rf.write("\r\n## Failed\r\n")
  164. x = PrettyTable()
  165. x.set_style(MARKDOWN)
  166. x.field_names = ["App/Test Case", "All as Expected", "Build As Expected", "Run As Expected", "Build Status", "Run Status"]
  167. for app in report["failed"]:
  168. app_sts = report["failed"][app]
  169. x.add_row([app, app_sts["expected"], app_sts["exp_build"], app_sts["exp_run"], \
  170. app_sts["build"], app_sts["run"]])
  171. rf.write(str(x))
  172. rf.write("\r\n# Build configurations\r\n")
  173. x = PrettyTable()
  174. x.set_style(MARKDOWN)
  175. x.field_names = ["Case Name", "Make Options"]
  176. for cfgname in report["configs"]:
  177. make_options = " ".join([ "%s=%s"%(key, value) for key, value in report["configs"][cfgname].items() ])
  178. x.add_row([cfgname, make_options])
  179. rf.write(str(x))
  180. rf.write("\r\n# Build and run status\r\n")
  181. x = PrettyTable()
  182. x.set_style(MARKDOWN)
  183. x.field_names = ["App/Test Case", "Case Name", "Build Status", "Run Status", "Total", "Text", "Data", "Bss"]
  184. apps_buildsts = result
  185. for app in apps_buildsts:
  186. app_sts = apps_buildsts[app]
  187. for cfgname in app_sts:
  188. size = app_sts[cfgname]["size"]
  189. bsts_md, rsts_md = generate_build_run_status_md(app_sts[cfgname], logdir, True)
  190. x.add_row([app, cfgname, bsts_md, rsts_md, \
  191. size["total"], size["text"], size["data"], size["bss"]])
  192. rf.write(str(x))
  193. x = PrettyTable()
  194. x.set_style(MARKDOWN)
  195. x.field_names = ["App/Test Case", "Case Name", "Expected Build", "Expected Run"]
  196. apps_buildsts = result
  197. with_expect = False
  198. for app in apps_buildsts:
  199. app_sts = apps_buildsts[app]
  200. for cfgname in app_sts:
  201. app_cfg_expected = get_expected(config, app, cfgname)
  202. expected_build = app_cfg_expected.get("build", True)
  203. expected_run = app_cfg_expected.get("run", True)
  204. if expected_build == False or expected_run == False:
  205. with_expect = True
  206. x.add_row([app, cfgname, expected_build, expected_run])
  207. if with_expect:
  208. rf.write("\r\n# Expected Build or Run Failed Cases\r\n")
  209. rf.write(str(x))
  210. def merge_all_config_and_result(logdir):
  211. mergedcfg_files = find_files(logdir, "**/mergedcfg.json", True)
  212. all_mergedcfg = dict()
  213. all_result = dict()
  214. print("Start to merge config and result json files in %s" % (logdir))
  215. for mergedcfg_file in mergedcfg_files:
  216. configfile = mergedcfg_file
  217. resultdir = os.path.dirname(mergedcfg_file)
  218. resultfile = os.path.join(resultdir, "result.json")
  219. if os.path.isfile(resultfile) == True:
  220. print("Merging config json file %s, result json file %s" %(configfile, resultfile))
  221. _, config = load_json(configfile)
  222. _, result = load_json(resultfile)
  223. all_mergedcfg.update(config)
  224. all_result.update(result)
  225. return all_mergedcfg, all_result
  226. def generate_report_for_logs(logdir):
  227. if logdir and os.path.isdir(logdir):
  228. reportfile = os.path.join(logdir, "report.md")
  229. all_mergedcfg, all_result = merge_all_config_and_result(logdir)
  230. config_file = os.path.join(logdir, "allcfg.json")
  231. result_file = os.path.join(logdir, "allrst.json")
  232. print("Save all merged config file to %s" % (config_file))
  233. print("Save all result file to %s" % (result_file))
  234. save_json(config_file, all_mergedcfg)
  235. save_json(result_file, all_result)
  236. print("Save generated report file to %s" % (reportfile))
  237. generate_report(all_mergedcfg, all_result, reportfile, logdir, True)
  238. pass
  239. if __name__ == '__main__':
  240. parser = argparse.ArgumentParser(description="Nuclei SDK Bench report Generate Tools")
  241. parser.add_argument('--logdir', required=True, help="logs directory where saved the report json files")
  242. args = parser.parse_args()
  243. if os.path.isdir(args.logdir) == False:
  244. print("The log directory doesn't exist, please check!")
  245. sys.exit(1)
  246. generate_report_for_logs(args.logdir)