openubmc-bingo 0.5.240__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of openubmc-bingo might be problematic. Click here for more details.

Files changed (242) hide show
  1. bmcgo/__init__.py +12 -0
  2. bmcgo/bmcgo.py +22 -0
  3. bmcgo/bmcgo_config.py +176 -0
  4. bmcgo/cli/__init__.py +10 -0
  5. bmcgo/cli/cli.py +584 -0
  6. bmcgo/codegen/__init__.py +14 -0
  7. bmcgo/codegen/c/__init__.py +9 -0
  8. bmcgo/codegen/c/annotation.py +52 -0
  9. bmcgo/codegen/c/argument.py +42 -0
  10. bmcgo/codegen/c/codegen.py +153 -0
  11. bmcgo/codegen/c/comment.py +22 -0
  12. bmcgo/codegen/c/ctype_defination.py +353 -0
  13. bmcgo/codegen/c/helper.py +87 -0
  14. bmcgo/codegen/c/interface.py +63 -0
  15. bmcgo/codegen/c/method.py +82 -0
  16. bmcgo/codegen/c/property.py +180 -0
  17. bmcgo/codegen/c/renderer.py +21 -0
  18. bmcgo/codegen/c/signal.py +64 -0
  19. bmcgo/codegen/c/template/client.c.mako +145 -0
  20. bmcgo/codegen/c/template/client.h.mako +36 -0
  21. bmcgo/codegen/c/template/interface.c.mako +0 -0
  22. bmcgo/codegen/c/template/interface.introspect.xml.mako +99 -0
  23. bmcgo/codegen/c/template/micro_component.c.mako +32 -0
  24. bmcgo/codegen/c/template/public.c.mako +228 -0
  25. bmcgo/codegen/c/template/public.h.mako +128 -0
  26. bmcgo/codegen/c/template/server.c.mako +104 -0
  27. bmcgo/codegen/c/template/server.h.mako +36 -0
  28. bmcgo/codegen/lua/.lua-format +7 -0
  29. bmcgo/codegen/lua/Makefile +101 -0
  30. bmcgo/codegen/lua/__init__.py +9 -0
  31. bmcgo/codegen/lua/codegen.py +171 -0
  32. bmcgo/codegen/lua/proto/Makefile +87 -0
  33. bmcgo/codegen/lua/proto/ipmi_types.proto +17 -0
  34. bmcgo/codegen/lua/proto/types.proto +52 -0
  35. bmcgo/codegen/lua/script/check_intfs.py +161 -0
  36. bmcgo/codegen/lua/script/dto/__init__.py +11 -0
  37. bmcgo/codegen/lua/script/dto/exception.py +53 -0
  38. bmcgo/codegen/lua/script/dto/kepler_abstract.py +47 -0
  39. bmcgo/codegen/lua/script/dto/options.py +33 -0
  40. bmcgo/codegen/lua/script/dto/print_simple.py +19 -0
  41. bmcgo/codegen/lua/script/dto/redfish_api.py +241 -0
  42. bmcgo/codegen/lua/script/dto/url_route.py +195 -0
  43. bmcgo/codegen/lua/script/gen_db_json.py +444 -0
  44. bmcgo/codegen/lua/script/gen_depends.py +89 -0
  45. bmcgo/codegen/lua/script/gen_entry.py +263 -0
  46. bmcgo/codegen/lua/script/gen_feature_json.py +156 -0
  47. bmcgo/codegen/lua/script/gen_historical_local_db_json.py +88 -0
  48. bmcgo/codegen/lua/script/gen_intf_json.py +261 -0
  49. bmcgo/codegen/lua/script/gen_intf_rpc_json.py +575 -0
  50. bmcgo/codegen/lua/script/gen_ipmi_json.py +485 -0
  51. bmcgo/codegen/lua/script/gen_mdb_json.py +117 -0
  52. bmcgo/codegen/lua/script/gen_rpc_msg_json.py +487 -0
  53. bmcgo/codegen/lua/script/gen_schema.py +302 -0
  54. bmcgo/codegen/lua/script/ipmi_types_pb2.py +135 -0
  55. bmcgo/codegen/lua/script/loader/__init__.py +11 -0
  56. bmcgo/codegen/lua/script/loader/file_utils.py +33 -0
  57. bmcgo/codegen/lua/script/loader/kepler_abstract_collect.py +79 -0
  58. bmcgo/codegen/lua/script/loader/kepler_abstract_loader.py +47 -0
  59. bmcgo/codegen/lua/script/loader/redfish_loader.py +127 -0
  60. bmcgo/codegen/lua/script/lua_format.py +62 -0
  61. bmcgo/codegen/lua/script/mds_util.py +385 -0
  62. bmcgo/codegen/lua/script/merge_model.py +330 -0
  63. bmcgo/codegen/lua/script/merge_proto_algo.py +85 -0
  64. bmcgo/codegen/lua/script/proto_loader.py +47 -0
  65. bmcgo/codegen/lua/script/proto_plugin.py +140 -0
  66. bmcgo/codegen/lua/script/redfish_source_tree.py +118 -0
  67. bmcgo/codegen/lua/script/render_utils/__init__.py +38 -0
  68. bmcgo/codegen/lua/script/render_utils/base.py +25 -0
  69. bmcgo/codegen/lua/script/render_utils/client_lua.py +98 -0
  70. bmcgo/codegen/lua/script/render_utils/controller_lua.py +71 -0
  71. bmcgo/codegen/lua/script/render_utils/db_lua.py +224 -0
  72. bmcgo/codegen/lua/script/render_utils/error_lua.py +185 -0
  73. bmcgo/codegen/lua/script/render_utils/factory.py +52 -0
  74. bmcgo/codegen/lua/script/render_utils/ipmi_lua.py +159 -0
  75. bmcgo/codegen/lua/script/render_utils/ipmi_message_lua.py +24 -0
  76. bmcgo/codegen/lua/script/render_utils/mdb_lua.py +177 -0
  77. bmcgo/codegen/lua/script/render_utils/mdb_register.py +215 -0
  78. bmcgo/codegen/lua/script/render_utils/message_lua.py +26 -0
  79. bmcgo/codegen/lua/script/render_utils/messages_lua.py +156 -0
  80. bmcgo/codegen/lua/script/render_utils/model_lua.py +485 -0
  81. bmcgo/codegen/lua/script/render_utils/old_model_lua.py +429 -0
  82. bmcgo/codegen/lua/script/render_utils/plugin_lua.py +38 -0
  83. bmcgo/codegen/lua/script/render_utils/redfish_proto.py +86 -0
  84. bmcgo/codegen/lua/script/render_utils/request_lua.py +76 -0
  85. bmcgo/codegen/lua/script/render_utils/service_lua.py +130 -0
  86. bmcgo/codegen/lua/script/render_utils/utils_message_lua.py +125 -0
  87. bmcgo/codegen/lua/script/render_utils/validate_lua.py +221 -0
  88. bmcgo/codegen/lua/script/sep_ipmi_message_cmds.py +217 -0
  89. bmcgo/codegen/lua/script/template.py +166 -0
  90. bmcgo/codegen/lua/script/types_pb2.py +516 -0
  91. bmcgo/codegen/lua/script/utils.py +663 -0
  92. bmcgo/codegen/lua/script/validate.py +80 -0
  93. bmcgo/codegen/lua/script/yaml_to_json.py +73 -0
  94. bmcgo/codegen/lua/templates/Makefile +114 -0
  95. bmcgo/codegen/lua/templates/apps/Makefile +261 -0
  96. bmcgo/codegen/lua/templates/apps/Makefile.mdb.mk +64 -0
  97. bmcgo/codegen/lua/templates/apps/app.lua.mako +19 -0
  98. bmcgo/codegen/lua/templates/apps/class.lua.mako +35 -0
  99. bmcgo/codegen/lua/templates/apps/client.lua.mako +429 -0
  100. bmcgo/codegen/lua/templates/apps/controller.lua.mako +276 -0
  101. bmcgo/codegen/lua/templates/apps/datas.lua.mako +8 -0
  102. bmcgo/codegen/lua/templates/apps/db.lua.mako +89 -0
  103. bmcgo/codegen/lua/templates/apps/entry.lua.mako +128 -0
  104. bmcgo/codegen/lua/templates/apps/feature.lua.mako +37 -0
  105. bmcgo/codegen/lua/templates/apps/generate_route.mako +25 -0
  106. bmcgo/codegen/lua/templates/apps/impl_feature.lua.mako +72 -0
  107. bmcgo/codegen/lua/templates/apps/ipmi.lua.mako +97 -0
  108. bmcgo/codegen/lua/templates/apps/ipmi_cmd.lua.mako +18 -0
  109. bmcgo/codegen/lua/templates/apps/ipmi_message.lua.mako +36 -0
  110. bmcgo/codegen/lua/templates/apps/local_db.lua.mako +263 -0
  111. bmcgo/codegen/lua/templates/apps/main.lua.mako +25 -0
  112. bmcgo/codegen/lua/templates/apps/mc.lua.mako +77 -0
  113. bmcgo/codegen/lua/templates/apps/mdb.lua.mako +45 -0
  114. bmcgo/codegen/lua/templates/apps/mdb_interface.lua.mako +73 -0
  115. bmcgo/codegen/lua/templates/apps/message.lua.mako +38 -0
  116. bmcgo/codegen/lua/templates/apps/model.lua.mako +239 -0
  117. bmcgo/codegen/lua/templates/apps/orm_classes.lua.mako +16 -0
  118. bmcgo/codegen/lua/templates/apps/plugin.lua.mako +8 -0
  119. bmcgo/codegen/lua/templates/apps/redfish.proto.mako +47 -0
  120. bmcgo/codegen/lua/templates/apps/service.lua.mako +440 -0
  121. bmcgo/codegen/lua/templates/apps/signal_listen.lua.mako +19 -0
  122. bmcgo/codegen/lua/templates/apps/utils/default_intf.lua.mako +41 -0
  123. bmcgo/codegen/lua/templates/apps/utils/enum.mako +10 -0
  124. bmcgo/codegen/lua/templates/apps/utils/imports.mako +13 -0
  125. bmcgo/codegen/lua/templates/apps/utils/mdb_intf.lua.mako +25 -0
  126. bmcgo/codegen/lua/templates/apps/utils/mdb_obj.lua.mako +23 -0
  127. bmcgo/codegen/lua/templates/apps/utils/message.mako +160 -0
  128. bmcgo/codegen/lua/templates/apps/utils/request.lua.mako +59 -0
  129. bmcgo/codegen/lua/templates/apps/utils/validate.mako +83 -0
  130. bmcgo/codegen/lua/templates/errors.lua.mako +36 -0
  131. bmcgo/codegen/lua/templates/messages.lua.mako +32 -0
  132. bmcgo/codegen/lua/templates/new_app/.clang-format.mako +170 -0
  133. bmcgo/codegen/lua/templates/new_app/.gitignore.mako +26 -0
  134. bmcgo/codegen/lua/templates/new_app/CHANGELOG.md.mako +0 -0
  135. bmcgo/codegen/lua/templates/new_app/CMakeLists.txt.mako +29 -0
  136. bmcgo/codegen/lua/templates/new_app/Makefile.mako +25 -0
  137. bmcgo/codegen/lua/templates/new_app/README.md.mako +0 -0
  138. bmcgo/codegen/lua/templates/new_app/conanfile.py.mako +7 -0
  139. bmcgo/codegen/lua/templates/new_app/config.cfg.mako +6 -0
  140. bmcgo/codegen/lua/templates/new_app/mds/model.json.mako +3 -0
  141. bmcgo/codegen/lua/templates/new_app/mds/service.json.mako +21 -0
  142. bmcgo/codegen/lua/templates/new_app/permissions.ini.mako +16 -0
  143. bmcgo/codegen/lua/templates/new_app/src/lualib/${project_name}_app.lua.mako +16 -0
  144. bmcgo/codegen/lua/templates/new_app/src/service/main.lua.mako +25 -0
  145. bmcgo/codegen/lua/templates/new_app/test/integration/test_${project_name}.conf.mako +9 -0
  146. bmcgo/codegen/lua/templates/new_app/test/integration/test_${project_name}.lua.mako +47 -0
  147. bmcgo/codegen/lua/templates/new_app/test/unit/test.lua.mako +23 -0
  148. bmcgo/codegen/lua/templates/new_app/user_conf/rootfs/etc/systemd/system/${project_name}.service.mako +18 -0
  149. bmcgo/codegen/lua/templates/new_app/user_conf/rootfs/etc/systemd/system/multi-user.target.wants/${project_name}.service.link +1 -0
  150. bmcgo/component/__init__.py +10 -0
  151. bmcgo/component/analysis/analysis.py +183 -0
  152. bmcgo/component/analysis/build_deps.py +165 -0
  153. bmcgo/component/analysis/data_deps.py +333 -0
  154. bmcgo/component/analysis/dep-rules.json +912 -0
  155. bmcgo/component/analysis/dep_node.py +110 -0
  156. bmcgo/component/analysis/intf_deps.py +163 -0
  157. bmcgo/component/analysis/intf_validation.py +254 -0
  158. bmcgo/component/analysis/rule.py +211 -0
  159. bmcgo/component/analysis/smc_dfx_whitelist.json +11 -0
  160. bmcgo/component/analysis/sr_validation.py +391 -0
  161. bmcgo/component/build.py +222 -0
  162. bmcgo/component/component_dt_version_parse.py +348 -0
  163. bmcgo/component/component_helper.py +114 -0
  164. bmcgo/component/coverage/__init__.py +11 -0
  165. bmcgo/component/coverage/c_incremental_cov_report.template +53 -0
  166. bmcgo/component/coverage/incremental_cov.py +464 -0
  167. bmcgo/component/deploy.py +110 -0
  168. bmcgo/component/gen.py +169 -0
  169. bmcgo/component/package_info.py +236 -0
  170. bmcgo/component/template/conanbase.py.mako +278 -0
  171. bmcgo/component/template/conanfile.deploy.py.mako +40 -0
  172. bmcgo/component/test.py +947 -0
  173. bmcgo/errors.py +119 -0
  174. bmcgo/frame.py +217 -0
  175. bmcgo/functional/__init__.py +10 -0
  176. bmcgo/functional/analysis.py +96 -0
  177. bmcgo/functional/bmc_studio_action.py +98 -0
  178. bmcgo/functional/check.py +185 -0
  179. bmcgo/functional/conan_index_build.py +251 -0
  180. bmcgo/functional/config.py +332 -0
  181. bmcgo/functional/csr_build.py +724 -0
  182. bmcgo/functional/deploy.py +263 -0
  183. bmcgo/functional/diff.py +235 -0
  184. bmcgo/functional/fetch.py +235 -0
  185. bmcgo/functional/full_component.py +391 -0
  186. bmcgo/functional/maintain.py +381 -0
  187. bmcgo/functional/new.py +166 -0
  188. bmcgo/functional/schema_valid.py +111 -0
  189. bmcgo/functional/simple_sign.py +104 -0
  190. bmcgo/functional/upgrade.py +78 -0
  191. bmcgo/ipmigen/__init__.py +13 -0
  192. bmcgo/ipmigen/ctype_defination.py +82 -0
  193. bmcgo/ipmigen/ipmigen.py +309 -0
  194. bmcgo/ipmigen/template/cmd.c.mako +366 -0
  195. bmcgo/ipmigen/template/ipmi.c.mako +25 -0
  196. bmcgo/ipmigen/template/ipmi.h.mako +51 -0
  197. bmcgo/logger.py +176 -0
  198. bmcgo/misc.py +117 -0
  199. bmcgo/target/app.yml +17 -0
  200. bmcgo/target/install_sdk.yml +15 -0
  201. bmcgo/target/personal.yml +53 -0
  202. bmcgo/target/publish.yml +45 -0
  203. bmcgo/tasks/__init__.py +11 -0
  204. bmcgo/tasks/download_buildtools_hm.py +124 -0
  205. bmcgo/tasks/misc.py +15 -0
  206. bmcgo/tasks/task.py +354 -0
  207. bmcgo/tasks/task_build_conan.py +714 -0
  208. bmcgo/tasks/task_build_rootfs_img.py +595 -0
  209. bmcgo/tasks/task_buildgppbin.py +88 -0
  210. bmcgo/tasks/task_buildhpm_ext4.py +82 -0
  211. bmcgo/tasks/task_create_interface_config.py +122 -0
  212. bmcgo/tasks/task_download_buildtools.py +99 -0
  213. bmcgo/tasks/task_download_dependency.py +72 -0
  214. bmcgo/tasks/task_hpm_envir_prepare.py +112 -0
  215. bmcgo/tasks/task_packet_to_supporte.py +87 -0
  216. bmcgo/tasks/task_prepare.py +105 -0
  217. bmcgo/tasks/task_sign_and_pack_hpm.py +42 -0
  218. bmcgo/utils/__init__.py +10 -0
  219. bmcgo/utils/buffer.py +128 -0
  220. bmcgo/utils/combine_json_schemas.py +170 -0
  221. bmcgo/utils/component_post.py +54 -0
  222. bmcgo/utils/component_version_check.py +86 -0
  223. bmcgo/utils/config.py +1067 -0
  224. bmcgo/utils/fetch_component_code.py +232 -0
  225. bmcgo/utils/install_manager.py +61 -0
  226. bmcgo/utils/installations/__init__.py +10 -0
  227. bmcgo/utils/installations/base_installer.py +70 -0
  228. bmcgo/utils/installations/install_consts.py +30 -0
  229. bmcgo/utils/installations/install_plans/bingo.yml +11 -0
  230. bmcgo/utils/installations/install_workflow.py +50 -0
  231. bmcgo/utils/installations/installers/apt_installer.py +177 -0
  232. bmcgo/utils/installations/installers/pip_installer.py +46 -0
  233. bmcgo/utils/installations/version_util.py +100 -0
  234. bmcgo/utils/mapping_config_patch.py +443 -0
  235. bmcgo/utils/perf_analysis.py +114 -0
  236. bmcgo/utils/tools.py +704 -0
  237. bmcgo/worker.py +417 -0
  238. openubmc_bingo-0.5.240.dist-info/METADATA +30 -0
  239. openubmc_bingo-0.5.240.dist-info/RECORD +242 -0
  240. openubmc_bingo-0.5.240.dist-info/WHEEL +5 -0
  241. openubmc_bingo-0.5.240.dist-info/entry_points.txt +2 -0
  242. openubmc_bingo-0.5.240.dist-info/top_level.txt +1 -0
@@ -0,0 +1,947 @@
1
+ #!/usr/bin/python3
2
+ # coding: utf-8
3
+ # Copyright (c) 2024 Huawei Technologies Co., Ltd.
4
+ # openUBMC is licensed under Mulan PSL v2.
5
+ # You can use this software according to the terms and conditions of the Mulan PSL v2.
6
+ # You may obtain a copy of Mulan PSL v2 at:
7
+ # http://license.coscl.org.cn/MulanPSL2
8
+ # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9
+ # EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10
+ # MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11
+ # See the Mulan PSL v2 for more details.
12
+ import argparse
13
+ import datetime
14
+ import json
15
+ import os
16
+ import re
17
+ import subprocess
18
+ import stat
19
+ import textwrap
20
+ import xml.etree.ElementTree as ET
21
+ import shutil
22
+ from pathlib import Path
23
+
24
+ from bmcgo import misc
25
+ from bmcgo.logger import Logger
26
+ from bmcgo.component.deploy import DeployComp
27
+ from bmcgo.component.build import BuildComp
28
+ from bmcgo.component.package_info import InfoComp
29
+ from bmcgo.component.gen import GenComp
30
+ from bmcgo.codegen.c.helper import Helper
31
+ from bmcgo.component.component_dt_version_parse import ComponentDtVersionParse
32
+ from bmcgo.component.coverage.incremental_cov import IncrementalCov
33
+ from bmcgo.bmcgo_config import BmcgoConfig
34
+ from bmcgo.utils.tools import Tools
35
+ from bmcgo.errors import BmcGoException
36
+
37
+ log = Logger("comp_test")
38
+ cwd = os.getcwd()
39
+ cwd_script = os.path.split(os.path.realpath(__file__))[0]
40
+ TOTAL_TIME = "totaltime"
41
+ HIT_LINES = "hitlines"
42
+
43
+
44
+ class TestFailedError(OSError):
45
+ """测试失败"""
46
+
47
+
48
+ class TestComp():
49
+ def __init__(self, bconfig: BmcgoConfig, args=None):
50
+ self.bconfig = bconfig
51
+ self.folder = bconfig.component.folder
52
+ self.tools = Tools()
53
+ os.chdir(self.folder)
54
+ # 路径定义
55
+ self._path_define()
56
+
57
+ parser = self.arg_parser(True)
58
+ # 参数检查
59
+ dt_parser = ComponentDtVersionParse(parser=parser, args=args, serv_file=self.temp_service_json)
60
+ dt_parser.chose_dt_mode()
61
+ self.args, self.build_args = parser.parse_known_args(args)
62
+ if self.args.enable_luajit:
63
+ self.build_args.append("-jit")
64
+ self.build_args.append("-bt")
65
+ self.build_args.append("dt")
66
+ self.build_args.append("-r")
67
+ self.build_args.append(self.args.remote)
68
+ if self.args.coverage:
69
+ self.build_args.append("-cov")
70
+ if self.args.asan:
71
+ self.build_args.append("-as")
72
+
73
+ self.info = InfoComp(self.build_args, self.temp_service_json)
74
+ self.unit_test = self.args.unit_test
75
+ self.integration_test = self.args.integration_test
76
+ self.test_filter = self.args.test_filter
77
+ self.app = self.args.app
78
+ self.asan = self.args.asan
79
+ self.coverage = self.args.coverage # 根据入参配置是否显示覆盖率未
80
+ self.without_build = self.args.without_build
81
+ self.coverage_exclude = self.args.coverage_exclude
82
+ self.ut_output = None
83
+ self.it_output = None
84
+ self.fuzz_output = None
85
+ self.dt_result = {}
86
+ # 存在test_package目录的组件由conan拉起DT测试
87
+ self.test_by_conan = os.path.isdir("test_package")
88
+ # 文件名称定义
89
+ self.origin_cov = "luacov.stats.out"
90
+ self.cov_filter = "luacov.stats.filter"
91
+ self.cov_report = "luacov.report.html"
92
+ # dt-fuzz定义
93
+ self.fuzz_test = self.args.fuzz_test
94
+ self.fuzz_gen = self.args.fuzz_gen
95
+ self.fuzz_count = self.args.fuzz_count
96
+ self.dtframe = os.path.join(self.temp_path, "dtframeforlua")
97
+
98
+ # 构建阶段检查
99
+ self.remote_pkg_list = []
100
+ # prerelease默认为空
101
+
102
+ self.srv_file = os.path.join(cwd, "mds", "service.json")
103
+
104
+ # 获取包名,默认取deps目录下的第一个目录名
105
+ self.package_id = ""
106
+ log.info("===>>>包名: %s", self.info.package)
107
+
108
+ self.current_app = self.info.name
109
+ # 初始化DT字典
110
+ self.init_dt_result_dict()
111
+
112
+ # 构建和部署对象
113
+ self.build = BuildComp(bconfig, self.build_args, service_json=self.temp_service_json)
114
+ self.deploy = DeployComp(bconfig, self.build.info)
115
+ self.current_app = self.build.info.name
116
+
117
+ @staticmethod
118
+ def arg_parser(add_help=False):
119
+ pkg_parser = InfoComp.arg_parser()
120
+ parser = argparse.ArgumentParser(description="Test component", parents=[pkg_parser], add_help=add_help)
121
+ parser.add_argument("-ut", "--unit_test", help="Enable unit test", action=misc.STORE_TRUE)
122
+ parser.add_argument("-it", "--integration_test", help="Enable integration test", action=misc.STORE_TRUE)
123
+ parser.add_argument("-ft", "--fuzz_test", help="Enable fuzz test", action=misc.STORE_TRUE)
124
+ parser.add_argument("-fg", "--fuzz_gen", help="Generate fuzz case", action=misc.STORE_TRUE)
125
+ parser.add_argument("-cnt", "--fuzz_count", help="Fuzz count", required=False, type=int, default=100000)
126
+ parser.add_argument("-f", "--test_filter", help="Run unit test with a filter", required=False, default='.')
127
+ parser.add_argument("-a", "--app", help="App in hica", required=False, default='all')
128
+ parser.add_argument("-wb", "--without_build", help="测试时不重新构建", action=misc.STORE_TRUE)
129
+ parser.add_argument("--coverage_exclude", help="Specify coverage exclude file path of whitelist",
130
+ required=False, default="")
131
+ return parser
132
+
133
+ @staticmethod
134
+ def is_method_covered(next_line):
135
+ pattern = re.compile("data-hits")
136
+ match = re.search(pattern, next_line)
137
+ if match is not None:
138
+ return True
139
+ else:
140
+ return False
141
+
142
+ @staticmethod
143
+ def find_method(line_str):
144
+ pattern = re.compile(":(Impl[\w]+)\(.*</span>")
145
+ match = re.search(pattern, line_str)
146
+ if match is not None:
147
+ return match.group(1)
148
+ else:
149
+ return None
150
+
151
+ @staticmethod
152
+ def search_test_config(test_path):
153
+ config_list = []
154
+ # 遍历路径, 搜索.conf文件
155
+ for path, _, files in os.walk(test_path):
156
+ for file in files:
157
+ if ".conf" in file:
158
+ config_file = os.path.join(path, file)
159
+ config_list.append(config_file)
160
+ return config_list
161
+
162
+ @staticmethod
163
+ def is_common_dep(dep, dt_common_deps):
164
+ for dt_dep in dt_common_deps:
165
+ if dt_dep.split("/")[0] in dep:
166
+ return True
167
+ return False
168
+
169
+ @staticmethod
170
+ def reslove_gcov(html_report):
171
+ if not os.path.exists(html_report):
172
+ log.warning("覆盖率报告 %s 不存在, 未运行到C/C++代码, 或执行测试时未使能覆盖率功能", html_report)
173
+ return (0, 0, 0)
174
+ with open(html_report, "r", encoding="utf-8", errors="replace") as fp:
175
+ html_content = fp.read()
176
+ cov_pattern = re.compile(r'<td class="headerCovTableEntryLo">([\d.]+).*%</td>')
177
+ coverage_match = re.search(cov_pattern, html_content)
178
+ coverage = coverage_match.group(1) if coverage_match else 0
179
+
180
+ # 匹配total和hits
181
+ pattern = r'<td class="headerCovTableEntry">(\d+)</td>'
182
+ matches = re.findall(pattern, html_content)
183
+ if len(matches) >= 2:
184
+ first_number = int(matches[0])
185
+ second_number = int(matches[1])
186
+ # 较大的数字是 total_lines
187
+ total_lines = max(first_number, second_number)
188
+ # 较小的数字是 hit_lines
189
+ hit_lines = min(first_number, second_number)
190
+ else:
191
+ total_lines = 0
192
+ hit_lines = 0
193
+ missed_lines = total_lines - hit_lines
194
+ return (f"{coverage}%", hit_lines, missed_lines)
195
+
196
+
197
+ @staticmethod
198
+ def _clear_result(file):
199
+ if not os.path.isfile(file):
200
+ return
201
+ fp = os.fdopen(os.open(file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
202
+ stat.S_IWUSR | stat.S_IRUSR), 'w')
203
+ fp.close()
204
+
205
+ @staticmethod
206
+ def _get_c_test_results(output_xmls):
207
+ results = []
208
+ total = 0
209
+ failed = 0
210
+ duration = 0
211
+ status_dict = {"success": "ok", "failed": "not ok"}
212
+ ljust_width = 7
213
+ for output_xml in output_xmls:
214
+ tree = ET.parse(output_xml)
215
+ for testcase_element in tree.iter(tag="testcase"):
216
+ status_element = testcase_element.find("status")
217
+ if status_element is None:
218
+ continue
219
+ duration_element = testcase_element.find("duration")
220
+ name = testcase_element.get("path")
221
+ result = status_element.get("result")
222
+ duration += float(0 if duration_element is None else duration_element.text)
223
+ total += 1
224
+ test_result = str(status_dict.get(result, "")).ljust(ljust_width)
225
+ results.append(f"{test_result} {str(total).ljust(ljust_width)} {name}")
226
+ failed += 1 if result == "failed" else 0
227
+ error_element = testcase_element.find("error")
228
+ if error_element is not None:
229
+ error = error_element.text
230
+ results.append(f"# {error}")
231
+ if results:
232
+ summary = f"# Ran {total} tests in {duration:.3f} seconds, {total - failed} successes, {failed} failures"
233
+ results.append(summary)
234
+
235
+ return results
236
+
237
+ def get_excluded_files_on_key(self, lang):
238
+ if not self.coverage_exclude:
239
+ return []
240
+
241
+ component_name = self.current_app
242
+ cov_exclude_path = os.path.realpath(os.path.join(cwd, self.coverage_exclude))
243
+ try:
244
+ with open(cov_exclude_path, 'r') as file:
245
+ excluded_data = json.load(file)
246
+ except FileNotFoundError as exc:
247
+ raise BmcGoException(f"File not found: {cov_exclude_path}") from exc
248
+ except json.JSONDecodeError:
249
+ log.info(f"Failed to decode JSON from file: {cov_exclude_path}")
250
+ return []
251
+
252
+ # 基于语言筛选白名单文件
253
+ excluded_files = []
254
+ if component_name in excluded_data:
255
+ excluded_files.extend(excluded_data[component_name][lang])
256
+
257
+ return excluded_files
258
+
259
+ def write_commponent_deps(self, deps, dt_common_deps, mf_file, uc_code, prefix):
260
+ # 获取组件自身配置的依赖列表
261
+ for dep in deps:
262
+ conan = dep.get(misc.CONAN)
263
+ if conan is None:
264
+ log.error("依赖格式错误, 获取到: %s", dep)
265
+ elif not self.is_common_dep(conan, dt_common_deps):
266
+ # 写入组件配置的非DT公共依赖
267
+ if "@" not in conan:
268
+ write_dep = prefix + '"' + conan + uc_code + '"' + "\n"
269
+ else:
270
+ write_dep = prefix + '"' + conan + '"' + "\n"
271
+ mf_file.write(write_dep)
272
+
273
+ def build_and_deploy(self):
274
+ # 构建被测组件
275
+ self.build.run()
276
+ os.chdir(self.folder)
277
+ # 部署被测组件及其依赖到temp/rootfs目录
278
+ self.deploy.run()
279
+ os.chdir(self.folder)
280
+
281
+ def coverage_config(self, project_root, cov_path, app):
282
+ log.info("配置覆盖率报告选项")
283
+ if app is not None:
284
+ app_path = "apps/" + app + "/"
285
+ else:
286
+ app_path = ""
287
+
288
+ # 获取需要屏蔽的.lua文件
289
+ lua_exclusion = self.get_excluded_files_on_key("LUA_EXCLUDED")
290
+ lua_x = []
291
+ # luacov规则,移除.lua拓展名
292
+ for file in lua_exclusion:
293
+ if file.endswith('.lua'):
294
+ file = file[:-4]
295
+ lua_x.append(file)
296
+
297
+ # 默认屏蔽test和temp目录下的文件
298
+ lua_exclusions = [f'"{app_path}{file}"' for file in lua_x]
299
+ exclusions = [f'"{project_root}test/.*"', f'"{project_root}temp/.*"'] + lua_exclusions
300
+
301
+ # 用textwrap格式化配置信息
302
+ config_str = textwrap.dedent(f"""
303
+ return {{
304
+ include = {{"{app_path}src/.*"}},
305
+ exclude = {{{', '.join(exclusions)}}},
306
+ statsfile = "{cov_path}/{self.origin_cov}",
307
+ coverage_filter = "{cov_path}/{self.cov_filter}",
308
+ reporter = "html",
309
+ reportfile = "{cov_path}/{self.cov_report}",
310
+ includeuntestedfiles = {{"{project_root}/src"}}
311
+ }}
312
+ """).strip()
313
+
314
+ if not os.path.exists(cov_path):
315
+ os.makedirs(cov_path)
316
+ # 重写默认的defaults.lua文件
317
+ with os.fdopen(os.open(self.cov_config, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
318
+ stat.S_IWUSR | stat.S_IRUSR), 'w') as file:
319
+ file.write(config_str)
320
+
321
+ def prepare_dlclose(self):
322
+ stub_code = textwrap.dedent('''\
323
+ #include <stdio.h>
324
+ int dlclose(void *handle)
325
+ {
326
+ return 0;
327
+ }
328
+ ''')
329
+
330
+ os.makedirs(self.asan_path, exist_ok=True)
331
+ source_file = os.path.join(self.asan_path, "dlclose.c")
332
+ # 创建打桩文件并编译为so
333
+ flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
334
+ mode = stat.S_IRUSR | stat.S_IWUSR
335
+ with os.fdopen(os.open(source_file, flags, mode), 'w') as f:
336
+ f.write(stub_code)
337
+ cmd = ["gcc", source_file, "-shared", "-fPIC", "-o", self.dlclose_path]
338
+ subprocess.run(cmd)
339
+
340
+ def check_asan_log(self):
341
+ files = os.listdir(self.asan_path)
342
+ asanlog_exist = False
343
+ for f in files:
344
+ if "asan.log" in f:
345
+ asanlog_exist = True
346
+ asan_log = os.path.join(self.asan_path, f)
347
+ # 备份日志到coverage路径下,便于看板或门禁下载
348
+ shutil.copy(asan_log, self.cov_path)
349
+ log.warning("地址消毒检测到内存问题, 日志保存到 %s", asan_log)
350
+ if asanlog_exist:
351
+ raise OSError("检测到内存问题, 请检查地址消毒日志!")
352
+
353
+ def clear_asan_log(self):
354
+ files = os.listdir(self.asan_path)
355
+ for f in files:
356
+ if "asan.log" in f:
357
+ os.remove(os.path.join(self.asan_path, f))
358
+
359
+ def set_additional_env(self, test_env, project_root, project_name):
360
+ test_env["PROJECT_DIR"] = project_root
361
+ test_env["PROJECT_NAME"] = project_name
362
+ if self.fuzz_test:
363
+ test_env["LD_PRELOAD"] = self.preload_option
364
+ test_env["ASAN_OPTIONS"] = "detect_leaks=0"
365
+ if self.coverage:
366
+ test_env["LUA_PATH"] = self.luacov_env
367
+ test_env["LUA_CPATH"] = self.luafilesystem
368
+ if self.asan:
369
+ self.prepare_dlclose()
370
+ self.clear_asan_log()
371
+ test_env["LD_PRELOAD"] = self.preload_option
372
+ test_env["ASAN_OPTIONS"] = "halt_on_error=0:detect_leaks=1:log_path={}".format(self.asan_log)
373
+
374
+ def set_unit_test_cmd(self, test_entry):
375
+ if self.coverage:
376
+ ut_cmd = [self.lua_bin, "-lluacov", test_entry, "-v", "-o", "TAP", "-p", self.test_filter]
377
+ else:
378
+ ut_cmd = [self.lua_bin, test_entry, "-v", "-o", "TAP", "-p", self.test_filter]
379
+ return ut_cmd
380
+
381
+ def add_luacov_to_config(self):
382
+ log.info("添加 luacov 路径到 %s", self.test_config)
383
+ with open(self.test_config, "r") as file:
384
+ search_str = ' self:add_lua_path(self.apps_root .. "?/init.lua")\n'
385
+ insert_str = ' self:add_lua_path(self.bmc_root .. "lualib/luacov/?.lua")\n'
386
+ lines = file.readlines()
387
+ if insert_str in lines:
388
+ return
389
+ insert_index = lines.index(search_str)
390
+ lines.insert(insert_index + 1, insert_str)
391
+ write_str = "".join(lines)
392
+ with os.fdopen(os.open(self.test_config, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
393
+ stat.S_IWUSR | stat.S_IRUSR), 'w') as file:
394
+ file.write(write_str)
395
+
396
+ def add_luacov_to_preloader(self):
397
+ preloader_dir = os.path.dirname(self.preloader)
398
+ preloader_wrapper = "app_preloader_wrapper.lua"
399
+ preloader_wrapper_path = os.path.join(preloader_dir, preloader_wrapper)
400
+ log.info("添加 luacov 依赖到 %s", preloader_wrapper_path)
401
+ with os.fdopen(os.open(preloader_wrapper_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
402
+ stat.S_IWUSR | stat.S_IRUSR), 'w') as file:
403
+ preloader_path = Path(self.preloader)
404
+ preloader_name = preloader_path.stem
405
+ preloader_module_name = os.path.basename(preloader_path.parent)
406
+ # luacov 需要在最前面先加载
407
+ file.write("require 'luacov'\n")
408
+ file.write(f"require '{preloader_module_name}.{preloader_name}'\n")
409
+
410
+ preloader = os.path.basename(self.preloader)
411
+ log.info(f"{self.test_config}加载{preloader}替换为{preloader_wrapper}")
412
+ self.tools.run_command(f"sed -i 's#{preloader}#{preloader_wrapper}#g' {self.test_config}")
413
+
414
+ def run_cmd_and_save_result(self, cmd, env, savefile):
415
+ # 保存当前所在工作目录信息
416
+ where = os.getcwd()
417
+ if os.path.exists("apps"):
418
+ app_path = os.path.join(self.folder, "apps", self.current_app)
419
+ output_dir = os.path.join(self.cov_path, self.current_app, "output")
420
+ # 切换到apps目录下对应的app路径
421
+ os.chdir(app_path)
422
+ else:
423
+ output_dir = os.path.join(self.cov_path, "output")
424
+ if not os.path.exists(output_dir):
425
+ os.makedirs(output_dir)
426
+ try:
427
+ file = os.fdopen(os.open(savefile, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
428
+ stat.S_IWUSR | stat.S_IRUSR), 'w')
429
+ tail_p = subprocess.Popen(["/usr/bin/tail", "-f", savefile])
430
+ process = subprocess.run(cmd, env=env, stdout=file, universal_newlines=True,
431
+ encoding="utf-8", errors="replace")
432
+ finally:
433
+ tail_p.kill()
434
+ file.close()
435
+ if process.returncode != 0:
436
+ raise subprocess.CalledProcessError(process.returncode, process.args)
437
+ # 返回到之前工作目录(主要用于hica场景,独立仓不受影响)
438
+ os.chdir(where)
439
+
440
+ def init_dt_result_dict(self):
441
+ self.dt_result[self.current_app] = {}
442
+ self.dt_result[self.current_app][HIT_LINES] = 0
443
+ self.dt_result[self.current_app]["missedlines"] = 0
444
+ self.dt_result[self.current_app]["linecoverage"] = ""
445
+ self.dt_result[self.current_app]["tests"] = 0
446
+ self.dt_result[self.current_app]["failures"] = 0
447
+ self.dt_result[self.current_app]["successes"] = 0
448
+ self.dt_result[self.current_app][TOTAL_TIME] = 0
449
+ self.dt_result[self.current_app]["uttime"] = 0.0
450
+ self.dt_result[self.current_app]["ittime"] = 0
451
+ self.dt_result[self.current_app]["lua_hitlines"] = 0
452
+ self.dt_result[self.current_app]["lua_missedlines"] = 0
453
+ self.dt_result[self.current_app]["lua_linecoverage"] = ""
454
+ self.dt_result[self.current_app]["gcov_hitlines"] = 0
455
+ self.dt_result[self.current_app]["gcov_missedlines"] = 0
456
+ self.dt_result[self.current_app]["gcov_linecoverage"] = ""
457
+ self.dt_result[self.current_app]["method_coverage"] = ""
458
+ self.dt_result[self.current_app]["total_method_count"] = 0
459
+ self.dt_result[self.current_app]["covered_method"] = []
460
+ self.dt_result[self.current_app]["covered_method_count"] = 0
461
+ self.dt_result[self.current_app]["uncovered_method"] = []
462
+ self.dt_result[self.current_app]["uncovered_method_count"] = 0
463
+
464
+ def parse_ut_result(self):
465
+ with open(self.ut_output) as file:
466
+ pattern = re.compile("# Ran (.*) tests in (.*) seconds, (.*) success(?:es)?, (.*) failures")
467
+ lines = file.readlines()
468
+ match = re.search(pattern, lines[-1])
469
+ if match is not None:
470
+ self.dt_result[self.current_app]["tests"] = int(match.group(1))
471
+ self.dt_result[self.current_app]["uttime"] = float(match.group(2))
472
+ self.dt_result[self.current_app]["successes"] = int(match.group(3))
473
+ self.dt_result[self.current_app]["failures"] = int(match.group(4))
474
+ else:
475
+ log.info("分析单元测试结果失败!")
476
+
477
+ def save_dt_result(self, destdir):
478
+ dt_result = os.path.join(destdir, "dt_result.json")
479
+ if self.app != "all" and os.path.exists("apps"):
480
+ # hica单个app运行时进行结果追加
481
+ if os.path.exists(dt_result):
482
+ file = open(dt_result, "r")
483
+ current_json = json.load(file)
484
+ file.close()
485
+ else:
486
+ current_json = {}
487
+ current_json.update(self.dt_result)
488
+ json_str = json.dumps(current_json, indent=4)
489
+ else:
490
+ # 独立自仓或hica运行所有app时一次写入
491
+ json_str = json.dumps(self.dt_result, indent=4)
492
+
493
+ with os.fdopen(os.open(dt_result, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
494
+ stat.S_IWUSR | stat.S_IRUSR), 'w') as file:
495
+ file.write(json_str)
496
+ log.info("Dt 报告结果输出到 %s", dt_result)
497
+
498
+ def start_test_by_type(self, ut_cmd, config_list, test_env):
499
+ self.init_dt_result_dict()
500
+ cov_path = self.cov_path if not os.path.exists("apps") else os.path.join(self.cov_path, self.current_app)
501
+ output_dir = os.path.join(cov_path, "output")
502
+ self.ut_output = os.path.join(output_dir, "ut_output.txt")
503
+ self.it_output = os.path.join(output_dir, "it_output.txt")
504
+ self.fuzz_output = os.path.join(output_dir, "fuzz_output.txt")
505
+
506
+ # 根据测试类型启动DT测试
507
+ if self.unit_test and not self.integration_test:
508
+ self._clear_result(self.ut_output)
509
+ self.run_cmd_and_save_result(ut_cmd, test_env, self.ut_output)
510
+ self.parse_ut_result()
511
+ self.dt_result[self.current_app][TOTAL_TIME] = self.dt_result[self.current_app]["uttime"]
512
+ elif self.integration_test and not self.unit_test:
513
+ self._clear_result(self.ut_output)
514
+ # 集成测试存在多个conf文件场景
515
+ start = datetime.datetime.utcnow()
516
+ for config in config_list:
517
+ log.info("集成测试配置: %s", config)
518
+ self.run_cmd_and_save_result([self.skynet_path, config], test_env, self.it_output)
519
+ end = datetime.datetime.utcnow()
520
+ it_time = (end - start).seconds
521
+ self.dt_result[self.current_app]["ittime"] = it_time
522
+ self.dt_result[self.current_app][TOTAL_TIME] = it_time
523
+ elif self.integration_test and self.unit_test:
524
+ # 先运行单元测试
525
+ self._clear_result(self.ut_output)
526
+ self.run_cmd_and_save_result(ut_cmd, test_env, self.ut_output)
527
+ self.parse_ut_result()
528
+ # 再运行集成测试
529
+ self._clear_result(self.it_output)
530
+ log.info("================ 集成测试开始 ================")
531
+ start = datetime.datetime.utcnow()
532
+ for config in config_list:
533
+ log.info("集成测试配置: %s", config)
534
+ self.run_cmd_and_save_result([self.skynet_path, config], test_env, self.it_output)
535
+ end = datetime.datetime.utcnow()
536
+ it_time = (end - start).seconds
537
+ self.dt_result[self.current_app]["ittime"] = it_time
538
+ self.dt_result[self.current_app][TOTAL_TIME] = it_time + \
539
+ self.dt_result[self.current_app]["uttime"]
540
+ elif self.fuzz_test:
541
+ self._clear_result(self.fuzz_output)
542
+ fuzz_path = os.path.join(self.folder, "test/fuzz")
543
+ # Fuzz场景只有一个conf文件
544
+ fuzz_config = self.search_test_config(fuzz_path)[0]
545
+ fuzz_cmd = [self.skynet_path, fuzz_config]
546
+ self.run_cmd_and_save_result(fuzz_cmd, test_env, self.fuzz_output)
547
+
548
+ def get_method_coverage(self, lines, index, covered_method, uncovered_method):
549
+ method = self.find_method(lines[index])
550
+ if method is None:
551
+ return
552
+ if self.is_method_covered(lines[index + 1]):
553
+ covered_method.append(method)
554
+ else:
555
+ uncovered_method.append(method)
556
+
557
+ def get_lua_coverage(self, lines, index):
558
+ # 通过行偏移获取总体覆盖率数据
559
+ coverage = lines[index + 7]
560
+ hits = lines[index + 9]
561
+ missed = lines[index + 10]
562
+ pattern = re.compile("<strong>(.*)</strong>")
563
+ match = re.search(pattern, coverage)
564
+ coverage = match.group(1) if match is not None else None
565
+ match = re.search(pattern, hits)
566
+ hits = match.group(1) if match is not None else None
567
+ match = re.search(pattern, missed)
568
+ missed = match.group(1) if match is not None else None
569
+ self.dt_result[self.current_app]["lua_linecoverage"] = coverage
570
+ self.dt_result[self.current_app]["lua_hitlines"] += int(hits)
571
+ self.dt_result[self.current_app]["lua_missedlines"] += int(missed)
572
+
573
+ def reslove_luacov(self, html_report):
574
+ if not os.path.exists(html_report):
575
+ log.error("覆盖率报告 %s 不存在, 执行测试时, 请使能覆盖率功能", html_report)
576
+ return
577
+ covered_method = []
578
+ uncovered_method = []
579
+ with open(html_report, "r", encoding="utf-8", errors="replace") as file:
580
+ lines = file.readlines()
581
+ line_index = 0
582
+ for line in lines:
583
+ if '<main>' in line:
584
+ self.get_lua_coverage(lines, line_index)
585
+ self.get_method_coverage(lines, line_index, covered_method, uncovered_method)
586
+ line_index += 1
587
+ # 汇总method覆盖率信息
588
+ method_count = len(covered_method) + len(uncovered_method)
589
+ if method_count != 0:
590
+ self.dt_result[self.current_app]["method_coverage"] = '%.1d' % (len(covered_method) / method_count)
591
+ self.dt_result[self.current_app]["total_method_count"] = len(covered_method) + len(uncovered_method)
592
+ self.dt_result[self.current_app]["covered_method"] = covered_method
593
+ self.dt_result[self.current_app]["covered_method_count"] = len(covered_method)
594
+ self.dt_result[self.current_app]["uncovered_method"] = uncovered_method
595
+ self.dt_result[self.current_app]["uncovered_method_count"] = len(uncovered_method)
596
+
597
+ def generate_luacov(self, test_env):
598
+ subprocess.run([self.lua_bin, self.luacov_bin], env=test_env)
599
+ self.reslove_luacov(os.path.join(self.cov_path, self.cov_report))
600
+
601
+ def save_package_info(self):
602
+ # 保存package信息,多次构建情况下用于确定唯一的包名
603
+ package_path = os.path.join(self.temp_path, "package_info")
604
+ log.info("保存包信息到 %s", package_path)
605
+ with os.fdopen(os.open(package_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
606
+ stat.S_IWUSR | stat.S_IRUSR), 'w') as file:
607
+ file.write(self.info.package)
608
+
609
+ def generate_gcov(self):
610
+ if not self.test_by_conan:
611
+ if not os.path.exists("src/lualib-src"):
612
+ return
613
+ package_path = os.path.join(self.temp_path, "package_info")
614
+ with open(package_path, "r") as file:
615
+ lines = file.readlines()
616
+ package_path = lines[0].replace("@", "/")
617
+ else:
618
+ package_path = self.info.package.replace("@", "/")
619
+ base_dir = "%s/.conan/data/%s" % (os.path.expanduser('~'), package_path)
620
+ gcov_path = os.path.join(self.cov_path, "gcov")
621
+ if not os.path.exists(gcov_path):
622
+ os.makedirs(gcov_path)
623
+ info_file = os.path.join(gcov_path, "gcov.info")
624
+ self.dt_result[self.current_app]["gcov_linecoverage"] = 0
625
+ self.dt_result[self.current_app]["gcov_hitlines"] += 0
626
+ self.dt_result[self.current_app]["gcov_missedlines"] += 0
627
+ # 生成gcov覆盖率数据
628
+ cmd = [
629
+ "lcov",
630
+ "--base-directory",
631
+ base_dir,
632
+ "--directory",
633
+ base_dir,
634
+ "--capture",
635
+ "--output-file",
636
+ info_file,
637
+ "--ignore-errors",
638
+ "unused",
639
+ ]
640
+ # exclude files
641
+ exclude = [
642
+ "--exclude=*include*",
643
+ "--exclude=*test*",
644
+ "--exclude=*.h",
645
+ "--exclude=*/service/cli/*",
646
+ "--exclude=*/gen/interface/*"
647
+ ]
648
+
649
+ # 获取需要屏蔽的.c/cpp文件
650
+ c_exclusion = self.get_excluded_files_on_key("C_EXCLUDED")
651
+ for file in c_exclusion:
652
+ exclude.append(f"--exclude=*{file}*")
653
+ cmd.extend(exclude)
654
+
655
+ ret = Helper.run(cmd, check=False)
656
+ if ret != 0:
657
+ return
658
+ # 生成html覆盖率报告
659
+ cmd = ["genhtml", "--output-directory", gcov_path, info_file]
660
+ Helper.run(cmd, check=False)
661
+ if ret != 0:
662
+ return
663
+ # resolv coverage
664
+ ret = self.reslove_gcov(os.path.join(self.cov_path, "gcov/index.html"))
665
+ if ret is not None:
666
+ self.dt_result[self.current_app]["gcov_linecoverage"] = ret[0]
667
+ self.dt_result[self.current_app]["gcov_hitlines"] += int(ret[1])
668
+ self.dt_result[self.current_app]["gcov_missedlines"] += int(ret[2])
669
+
670
+ def combine_coverage(self):
671
+ # 将luacov和gcov数据汇总
672
+ self.dt_result[self.current_app][HIT_LINES] = self.dt_result[self.current_app]["lua_hitlines"] + \
673
+ self.dt_result[self.current_app]["gcov_hitlines"]
674
+ self.dt_result[self.current_app]["missedlines"] = self.dt_result[self.current_app]["lua_missedlines"] + \
675
+ self.dt_result[self.current_app]["gcov_missedlines"]
676
+ total_lines = self.dt_result[self.current_app][HIT_LINES] + self.dt_result[self.current_app]["missedlines"]
677
+ if total_lines != 0:
678
+ self.dt_result[self.current_app]["linecoverage"] = "{:.1%}".format(
679
+ self.dt_result[self.current_app][HIT_LINES] / total_lines)
680
+
681
+ def run_independent_test(self, test_env):
682
+ log.info("================ 测试 %s 开始 ================", self.current_app)
683
+ # 配置额外的环境变量
684
+ self.set_additional_env(test_env, self.folder, self.current_app)
685
+ test_entry = os.path.join(self.folder, "test/unit/test.lua")
686
+ itegration_path = os.path.join(self.folder, "test/integration")
687
+ # 获取集成测试配置文件列表
688
+ config_list = self.search_test_config(itegration_path)
689
+ # 设置覆盖率配置文件
690
+ if self.coverage:
691
+ self.coverage_config(self.folder, self.cov_path, None)
692
+ # 设置unit_test运行命令
693
+ ut_cmd = self.set_unit_test_cmd(test_entry)
694
+ # 集成测试场景修改config.cfg和app_preloader.lua
695
+ if (self.integration_test or self.fuzz_test) and self.coverage:
696
+ # config.cfg中增加luacov路径到LUA_PATH
697
+ self.add_luacov_to_config()
698
+ # app_preloader.lua中增加require 'luacov'
699
+ self.add_luacov_to_preloader()
700
+ # 启动DT测试
701
+ self.start_test_by_type(ut_cmd, config_list, test_env)
702
+ # 生成覆盖率报告
703
+ if self.coverage:
704
+ # 生成lua覆盖率报告
705
+ self.generate_luacov(test_env)
706
+ self.generate_gcov()
707
+ self.combine_coverage()
708
+ log.info("行覆盖率: %s, 命中: %s 行, 未命中: %s 行",
709
+ self.dt_result[self.current_app]["linecoverage"],
710
+ self.dt_result[self.current_app][HIT_LINES],
711
+ self.dt_result[self.current_app]["missedlines"])
712
+ log.info("覆盖率报告 %s 保存到 %s", self.cov_report, self.cov_path)
713
+ # 保存结果供jenkins工程解析
714
+ self.save_dt_result(self.cov_path)
715
+ # 检测是否存在asan日志
716
+ if self.asan:
717
+ self.check_asan_log()
718
+
719
+ def run_hica_test(self, test_env):
720
+ # 获取app列表
721
+ app_list = []
722
+ if self.app == "all":
723
+ dirs = os.listdir("apps")
724
+ app_list.extend(dirs)
725
+ else:
726
+ app_list.append(self.app)
727
+
728
+ # 集成测试场景修改config.cfg和app_preloader.lua
729
+ if self.integration_test and self.coverage:
730
+ # config.cfg中增加luacov路径到LUA_PATH
731
+ self.add_luacov_to_config()
732
+ # app_preloader.lua中增加require 'luacov'
733
+ self.add_luacov_to_preloader()
734
+
735
+ hica_cov = self.cov_path
736
+ for app in app_list:
737
+ self.current_app = app
738
+ banner = "单元测试: %s" % (app) if self.unit_test else "集成测试: %s" % (app)
739
+ log.info("================ %s 开始 ================", banner)
740
+ # 适配hica仓的app路径
741
+ project_root = os.path.join(self.folder, "apps/" + app)
742
+ app_cov_path = os.path.join(self.cov_path, app)
743
+ test_entry = os.path.join(project_root, "test/unit/test.lua")
744
+ itegration_path = os.path.join(project_root, "test/integration")
745
+ # 获取集成测试配置文件列表
746
+ config_list = self.search_test_config(itegration_path)
747
+ # 配置额外的环境变量
748
+ self.set_additional_env(test_env, project_root, self.current_app)
749
+ # 设置覆盖率配置文件
750
+ if self.coverage:
751
+ self.coverage_config(project_root, app_cov_path, app)
752
+ # 设置unit_test运行命令
753
+ ut_cmd = self.set_unit_test_cmd(test_entry)
754
+ # 启动DT测试
755
+ self.start_test_by_type(ut_cmd, config_list, test_env)
756
+ # 生成覆盖率报告
757
+ if self.coverage:
758
+ subprocess.run([self.lua_bin, self.luacov_bin], env=test_env)
759
+ ret = self.reslove_luacov(os.path.join(app_cov_path, self.cov_report))
760
+ if ret is not None:
761
+ self.dt_result[self.current_app]["linecoverage"] = ret[0]
762
+ self.dt_result[self.current_app][HIT_LINES] = int(ret[1])
763
+ self.dt_result[self.current_app]["missedlines"] = int(ret[2])
764
+ log.info("行覆盖率: %s, 命中: %s 行, 未命中: %s 行", ret[0], ret[1], ret[2])
765
+ log.info("覆盖率报告 %s 保存到 %s", self.cov_report, app_cov_path)
766
+ # 保存结果供jenkins工程解析
767
+ self.save_dt_result(hica_cov)
768
+ # 检测是否存在asan日志
769
+ if self.asan:
770
+ self.check_asan_log()
771
+
772
+ def generate_fuzz(self):
773
+ dtframe_gen = os.path.join(self.dtframe, "auto_gen_script/fuzz_build.py")
774
+ ipmi_source = os.path.join(self.folder, "mds", "ipmi.json")
775
+ ipmi_template = os.path.join(self.dtframe, "templates/ipmi.mako")
776
+ mdb_template = os.path.join(self.dtframe, "templates/mdb_interface.mako")
777
+ model_source = os.path.join(self.folder, "mds", "model.json")
778
+ mdb_interface_root = os.path.join(self.folder, "temp/opt/bmc/apps/mdb_interface")
779
+ fuzz_path = os.path.join(self.folder, "test/fuzz")
780
+ os.makedirs(fuzz_path, exist_ok=True)
781
+ os.chdir(fuzz_path)
782
+ Helper.run(["/usr/bin/env", "python3", dtframe_gen, "-s", ipmi_source, "-t",
783
+ ipmi_template, "-c", str(self.fuzz_count)])
784
+ Helper.run(["/usr/bin/env", "python3", dtframe_gen, "-s", model_source, "-t",
785
+ mdb_template, "-mp", mdb_interface_root, "-c", str(self.fuzz_count)])
786
+
787
+ def run_test(self):
788
+ log.info("================ 开发者测试开始 ================")
789
+ # 判断temp是否存在
790
+ if not os.path.exists(self.temp_path):
791
+ log.error("%s 不存在, 请先构建一次此工程", self.temp_path)
792
+ return
793
+ # 公共环境变量
794
+ test_env = {
795
+ "ROOT_DIR": self.temp_path,
796
+ "LD_LIBRARY_PATH": self.test_lib,
797
+ "CONFIG_FILE": self.test_config
798
+ }
799
+ # 适配hica仓
800
+ if os.path.exists("apps"):
801
+ self.run_hica_test(test_env)
802
+ else:
803
+ self.run_independent_test(test_env)
804
+
805
+ def print_build_menu(self):
806
+ log.info("================================ 构建菜单 ================================")
807
+ ljust_width = 30
808
+ log.info("%s %s", "单元测试(ut):".ljust(ljust_width), str(self.unit_test))
809
+ log.info("%s %s", "集成测试(it):".ljust(ljust_width), str(self.integration_test))
810
+ log.info("%s %s", "覆盖率:".ljust(ljust_width), str(self.coverage))
811
+ log.info("%s %s", "地址消毒:".ljust(ljust_width), str(self.asan))
812
+ log.info("============================================================================")
813
+
814
+ def test_c_component(self):
815
+ # 调用测试命令
816
+ try:
817
+ test_package_build_dir = os.path.join(self.folder, "test_package", "build")
818
+ if os.path.isdir(test_package_build_dir):
819
+ shutil.rmtree(test_package_build_dir)
820
+ self.build.test()
821
+ except OSError as e:
822
+ raise TestFailedError("执行测试任务失败, 退出") from e
823
+ finally:
824
+ self._save_c_test_result()
825
+ if self.ut_output:
826
+ self.parse_ut_result()
827
+
828
+ # C框架由构建时测试,仅需要统计覆盖率
829
+ if self.coverage:
830
+ self.generate_gcov()
831
+ self.combine_coverage()
832
+ result = self.dt_result[self.current_app]
833
+ log.info("覆盖率: %s, 命中: %s 行, 未命中: %s 行",
834
+ result["linecoverage"], result[HIT_LINES], result["missedlines"])
835
+ self.save_dt_result(self.cov_path)
836
+ log.info("覆盖率报告 %s 保存到 %s", self.cov_report, self.cov_path)
837
+
838
+ def check_folder(self):
839
+ test_package_folder = os.path.join(self.folder, "test_package")
840
+ test_folder = os.path.join(self.folder, "test")
841
+ if os.path.isdir(test_folder) or os.path.isdir(test_package_folder):
842
+ return True
843
+ return False
844
+
845
+ def incremental_cov(self):
846
+ temp_path = os.path.join(self.folder, "temp", "coverage")
847
+ cov_exclude_path = self.coverage_exclude
848
+ if self.coverage_exclude:
849
+ cov_exclude_path = os.path.realpath(os.path.join(cwd, self.coverage_exclude))
850
+ total_coverage, c_coverage, lua_coverage = IncrementalCov(
851
+ "HEAD~..HEAD", self.current_app, temp_path, 0.8, cov_exclude_path).calculate_cov()
852
+ log.info("增量总覆盖:%s, c代码覆盖: %s, lua代码覆盖: %s", total_coverage, c_coverage, lua_coverage)
853
+ log.info("增量覆盖率报告保存到 dt_result.json")
854
+
855
+ def run(self):
856
+ self.print_build_menu()
857
+ if not self.check_folder():
858
+ log.warning("当前组件不存在开发者测试的test文件夹或test_package文件夹,开发者测试终止!")
859
+ return
860
+
861
+ # -gen代码自动生成逻辑
862
+ if self.fuzz_gen:
863
+ self.generate_fuzz()
864
+ return
865
+ # 构建完后单独运行DT
866
+ has_dt_type = self.unit_test or self.integration_test or self.fuzz_test
867
+ if not has_dt_type:
868
+ self.unit_test = True
869
+
870
+ # 构建被测组件
871
+ if not self.without_build:
872
+ self.build_and_deploy()
873
+
874
+ ret = 0
875
+ try:
876
+ # 有test_package的由conan启动测试
877
+ if self.test_by_conan:
878
+ if self.info.language == "c":
879
+ args = ["-s", self.temp_service_json]
880
+ gen = GenComp(args)
881
+ gen.run()
882
+ self.test_c_component()
883
+ if self.coverage:
884
+ # 调用增量模块
885
+ self.incremental_cov()
886
+ self.print_build_menu()
887
+ else:
888
+ self.save_package_info()
889
+ self.run_test()
890
+ if self.coverage:
891
+ # 调用增量模块
892
+ self.incremental_cov()
893
+ self.print_build_menu()
894
+ except Exception as exp:
895
+ log.warning("===>>>> exception: %s", str(exp))
896
+ ret = -1
897
+ if ret == 0:
898
+ log.info("===>>>> 测试: %s 完成", self.info.package)
899
+ else:
900
+ log.warning("===>>>> 测试: %s 失败", self.info.package)
901
+
902
+ def _path_define(self):
903
+ self.temp_path = os.path.join(self.folder, "temp")
904
+ os.makedirs(self.temp_path, exist_ok=True)
905
+ self.temp_service_json = os.path.join(self.temp_path, "service.json")
906
+ self.tools.copy("mds/service.json", self.temp_service_json)
907
+
908
+ self.cov_path = os.path.join(self.folder, "temp", "coverage")
909
+ self.luacov_path = os.path.join(self.temp_path, "opt/bmc/lualib/luacov")
910
+ self.cov_config = os.path.join(self.luacov_path, "luacov/defaults.lua")
911
+ self.test_lib = ';'.join([os.path.join(self.temp_path, v) for v in ["lib", 'lib64', 'usr/lib', "usr/lib64"]])
912
+ self.test_config = os.path.join(self.temp_path, "opt/bmc/libmc/lualib/test_common/test_config.cfg")
913
+ self.preloader = os.path.join(self.temp_path, "opt/bmc/libmc/lualib/test_common/app_preloader.lua")
914
+ self.lua_bin = os.path.join(self.temp_path, "opt/bmc/skynet/lua")
915
+ self.skynet_path = os.path.join(self.temp_path, "opt/bmc/skynet/skynet")
916
+ self.luacov_bin = os.path.join(self.luacov_path, "bin/luacov")
917
+ self.luacov_env = os.path.join(self.luacov_path, "?.lua")
918
+ self.luafilesystem = os.path.join(self.temp_path, "opt/bmc/luaclib/?.so")
919
+ # WSL环境ASAN库路径(只用于DT场景)
920
+ self.libasan_path = "/usr/lib/x86_64-linux-gnu/libasan.so.5"
921
+ # 地址消毒相关文件存储路径
922
+ self.asan_path = os.path.join(self.temp_path, "asan")
923
+ self.asan_log = os.path.join(self.asan_path, "asan.log")
924
+ # 打桩的dlcose库路径
925
+ self.dlclose_path = os.path.join(self.asan_path, "dlclose.so")
926
+ self.preload_option = "{} {}".format(self.libasan_path, self.dlclose_path)
927
+
928
+ def _save_c_test_result(self):
929
+ test_package_build_dir = os.path.join(self.folder, "test_package", "build")
930
+ test_build = os.path.join(test_package_build_dir, os.listdir(test_package_build_dir)[0])
931
+ outputs = []
932
+ for dir_or_file in os.listdir(test_build):
933
+ file_path = os.path.join(test_build, dir_or_file)
934
+ if os.path.isfile(file_path) and re.match(r"gtester.*.xml", dir_or_file):
935
+ outputs.append(file_path)
936
+
937
+ results = self._get_c_test_results(outputs)
938
+ if not results:
939
+ return
940
+
941
+ self.ut_output = os.path.join(self.cov_path, "output", "ut_output.txt")
942
+ os.makedirs(os.path.dirname(self.ut_output), exist_ok=True)
943
+ self._clear_result(self.ut_output)
944
+ with os.fdopen(os.open(self.ut_output, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
945
+ stat.S_IWUSR | stat.S_IRUSR), 'w') as fp:
946
+ for line in results:
947
+ fp.write(f"{line}\n")