TestReport.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. """
  2. this module generates markdown format test report for throughput test.
  3. The test report contains 2 parts:
  4. 1. throughput with different configs
  5. 2. throughput with RSSI
  6. """
  7. import os
  8. class ThroughputForConfigsReport(object):
  9. THROUGHPUT_TYPES = ["tcp_tx", "tcp_rx", "udp_tx", "udp_rx"]
  10. REPORT_FILE_NAME = "ThroughputForConfigs.md"
  11. def __init__(self, output_path, ap_ssid, throughput_results, sdkconfig_files):
  12. """
  13. :param ap_ssid: the ap we expected to use
  14. :param throughput_results: config with the following type::
  15. {
  16. "config_name": {
  17. "tcp_tx": result,
  18. "tcp_rx": result,
  19. "udp_tx": result,
  20. "udp_rx": result,
  21. },
  22. "config_name2": {},
  23. }
  24. """
  25. self.output_path = output_path
  26. self.ap_ssid = ap_ssid
  27. self.results = throughput_results
  28. self.sdkconfigs = dict()
  29. for config_name in sdkconfig_files:
  30. self.sdkconfigs[config_name] = self._parse_config_file(sdkconfig_files[config_name])
  31. if not os.path.exists(output_path):
  32. os.makedirs(output_path)
  33. self.sort_order = self.sdkconfigs.keys()
  34. self.sort_order.sort()
  35. @staticmethod
  36. def _parse_config_file(config_file_path):
  37. sdkconfig = {}
  38. with open(config_file_path, "r") as f:
  39. for line in f:
  40. if not line.isspace():
  41. if line[0] == "#":
  42. continue
  43. name, value = line.split("=")
  44. value = value.strip("\r\n")
  45. sdkconfig[name] = value if value else "n"
  46. return sdkconfig
  47. def _generate_the_difference_between_configs(self):
  48. """
  49. generate markdown list for different configs::
  50. default: esp-idf default
  51. low:
  52. * `config name 1`: old value -> new value
  53. * `config name 2`: old value -> new value
  54. * ...
  55. ...
  56. """
  57. data = "## Config Definition:\r\n\r\n"
  58. def find_difference(base, new):
  59. _difference = {}
  60. all_configs = set(base.keys())
  61. all_configs.update(set(new.keys()))
  62. for _config in all_configs:
  63. try:
  64. _base_value = base[_config]
  65. except KeyError:
  66. _base_value = "null"
  67. try:
  68. _new_value = new[_config]
  69. except KeyError:
  70. _new_value = "null"
  71. if _base_value != _new_value:
  72. _difference[_config] = "{} -> {}".format(_base_value, _new_value)
  73. return _difference
  74. for i, _config_name in enumerate(self.sort_order):
  75. current_config = self.sdkconfigs[_config_name]
  76. if i > 0:
  77. previous_config_name = self.sort_order[i - 1]
  78. previous_config = self.sdkconfigs[previous_config_name]
  79. else:
  80. previous_config = previous_config_name = None
  81. if previous_config:
  82. # log the difference
  83. difference = find_difference(previous_config, current_config)
  84. data += "* {} (compared to {}):\r\n".format(_config_name, previous_config_name)
  85. for diff_name in difference:
  86. data += " * `{}`: {}\r\n".format(diff_name, difference[diff_name])
  87. return data
  88. def _generate_report_for_one_type(self, throughput_type):
  89. """
  90. generate markdown table with the following format::
  91. | config name | throughput (Mbps) | free heap size (bytes) |
  92. |-------------|-------------------|------------------------|
  93. | default | 32.11 | 147500 |
  94. | low | 32.11 | 147000 |
  95. | medium | 33.22 | 120000 |
  96. | high | 43.11 | 100000 |
  97. | max | 45.22 | 79000 |
  98. """
  99. empty = True
  100. ret = "\r\n### {} {}\r\n\r\n".format(*throughput_type.split("_"))
  101. ret += "| config name | throughput (Mbps) | free heap size (bytes) |\r\n"
  102. ret += "|-------------|-------------------|------------------------|\r\n"
  103. for config in self.sort_order:
  104. try:
  105. result = self.results[config][throughput_type]
  106. throughput = "{:.02f}".format(max(result.throughput_by_att[self.ap_ssid].values()))
  107. heap_size = str(result.heap_size)
  108. # although markdown table will do alignment
  109. # do align here for better text editor presentation
  110. ret += "| {:<12}| {:<18}| {:<23}|\r\n".format(config, throughput, heap_size)
  111. empty = False
  112. except KeyError:
  113. pass
  114. return ret if not empty else ""
  115. def generate_report(self):
  116. data = "# Throughput for different configs\r\n"
  117. data += "\r\nAP: {}\r\n".format(self.ap_ssid)
  118. for throughput_type in self.THROUGHPUT_TYPES:
  119. data += self._generate_report_for_one_type(throughput_type)
  120. data += "\r\n------\r\n"
  121. data += self._generate_the_difference_between_configs()
  122. with open(os.path.join(self.output_path, self.REPORT_FILE_NAME), "w") as f:
  123. f.write(data)
  124. class ThroughputVsRssiReport(object):
  125. REPORT_FILE_NAME = "ThroughputVsRssi.md"
  126. def __init__(self, output_path, throughput_results):
  127. """
  128. :param throughput_results: config with the following type::
  129. {
  130. "tcp_tx": result,
  131. "tcp_rx": result,
  132. "udp_tx": result,
  133. "udp_rx": result,
  134. }
  135. """
  136. self.output_path = output_path
  137. self.raw_data_path = os.path.join(output_path, "raw_data")
  138. self.results = throughput_results
  139. self.throughput_types = self.results.keys()
  140. self.throughput_types.sort()
  141. if not os.path.exists(self.raw_data_path):
  142. os.makedirs(self.raw_data_path)
  143. def _generate_summary(self):
  144. """
  145. generate summary with the following format::
  146. | item | curve analysis | max throughput (Mbps) |
  147. |---------|----------------|-----------------------|
  148. | tcp tx | Success | 32.11 |
  149. | tcp rx | Success | 32.11 |
  150. | udp tx | Success | 45.22 |
  151. | udp rx | Failed | 55.44 |
  152. """
  153. ret = "\r\n### Summary\r\n\r\n"
  154. ret += "| item | curve analysis | max throughput (Mbps) |\r\n"
  155. ret += "|---------|----------------|-----------------------|\r\n"
  156. for _type in self.throughput_types:
  157. result = self.results[_type]
  158. max_throughput = 0.0
  159. curve_analysis = "Failed" if result.error_list else "Success"
  160. for ap_ssid in result.throughput_by_att:
  161. _max_for_ap = max(result.throughput_by_rssi[ap_ssid].values())
  162. if _max_for_ap > max_throughput:
  163. max_throughput = _max_for_ap
  164. max_throughput = "{:.02f}".format(max_throughput)
  165. ret += "| {:<8}| {:<15}| {:<22}|\r\n".format("{}_{}".format(result.proto, result.direction),
  166. curve_analysis, max_throughput)
  167. return ret
  168. def _generate_report_for_one_type(self, result):
  169. """
  170. generate markdown table with the following format::
  171. ### tcp rx
  172. Errors:
  173. * detected error 1
  174. * ...
  175. AP: ap_ssid
  176. ![throughput Vs RSSI](path to figure)
  177. AP: ap_ssid
  178. ![throughput Vs RSSI](path to figure)
  179. """
  180. result.post_analysis()
  181. ret = "\r\n### {} {}\r\n".format(result.proto, result.direction)
  182. if result.error_list:
  183. ret += "\r\nErrors:\r\n\r\n"
  184. for error in result.error_list:
  185. ret += "* " + error + "\r\n"
  186. for ap_ssid in result.throughput_by_rssi:
  187. ret += "\r\nAP: {}\r\n".format(ap_ssid)
  188. # draw figure
  189. file_name = result.draw_throughput_figure(self.raw_data_path, ap_ssid, "rssi")
  190. result.draw_throughput_figure(self.raw_data_path, ap_ssid, "att")
  191. result.draw_rssi_vs_att_figure(self.raw_data_path, ap_ssid)
  192. ret += "\r\n![throughput Vs RSSI]({})\r\n".format(os.path.join("raw_data", file_name))
  193. return ret
  194. def generate_report(self):
  195. data = "# Throughput Vs RSSI\r\n"
  196. data += self._generate_summary()
  197. for _type in self.throughput_types:
  198. data += self._generate_report_for_one_type(self.results[_type])
  199. with open(os.path.join(self.output_path, self.REPORT_FILE_NAME), "w") as f:
  200. f.write(data)