• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding=utf-8
3
4#
5# Copyright (c) 2023 Huawei Device Co., Ltd.
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19
20import os
21import sys
22import json
23import shutil
24import subprocess
25import CppHeaderParser
26import get_innerkits_json
27import make_report
28
29filter_file_name_list = [
30    "appexecfwk/libjnikit/include/jni.h",
31]
32FILTER_CLASS_ATTRIBUTE_LIST = ["ACE_EXPORT", "OHOS_NWEB_EXPORT"]
33
34
35def _init_sys_config():
36    sys.localcoverage_path = os.path.join(current_path, "..")
37    sys.path.insert(0, sys.localcoverage_path)
38
39
40def create_coverage_result_outpath(filepath):
41    if os.path.exists(filepath):
42        shutil.rmtree(filepath)
43    os.makedirs(filepath)
44
45
46def get_subsystem_part_list(project_rootpath):
47    subsystme_part_dict = {}
48    subsystem_part_config_filepath = os.path.join(
49        project_rootpath, "out", product_name, "build_configs/infos_for_testfwk.json")
50    if os.path.exists(subsystem_part_config_filepath):
51        try:
52            with open(subsystem_part_config_filepath, 'r') as f:
53                data = json.load(f)
54        except IOError:
55            print("Error for open subsystem config file. ")
56        if not data:
57            print("subsystem_part config file error.")
58        else:
59            subsystme_part_dict = data.get("phone", "").get("subsystem_infos", "")
60        return subsystme_part_dict
61    else:
62        print("subsystem_part_config_filepath not exists.")
63        return {}
64
65
66def load_json_data():
67    json_file_path = os.path.join(CODEPATH, KIT_MODULES_INFO)
68    json_data_dic = {}
69    if os.path.isfile(json_file_path):
70        try:
71            with open(json_file_path, 'r') as f:
72                json_data_dic = json.load(f)
73            if not json_data_dic:
74                print("Loadind file \"%s\" error" % json_file_path)
75                return {}
76        except(IOError, ValueError):
77            print("Error for load_json_data: \"%s\"" % json_file_path)
78    else:
79        print("Info: \"%s\" not exist." % json_file_path)
80    return json_data_dic
81
82
83def get_file_list(find_path, postfix):
84    file_names = os.listdir(find_path)
85    file_list = []
86    if len(file_names) > 0:
87        for fn in file_names:
88            if fn.find(postfix) != -1 and fn[-len(postfix):] == postfix:
89                file_list.append(fn)
90    return file_list
91
92
93def get_file_list_by_postfix(path, postfix, filter_jar=""):
94    file_list = []
95    for dirs in os.walk(path):
96        files = get_file_list(find_path=dirs[0], postfix=postfix)
97        for file_path in files:
98            if "" != file_path and -1 == file_path.find(__file__):
99                pos = file_path.rfind(os.sep)
100                file_name = file_path[pos + 1:]
101                file_path = os.path.join(dirs[0], file_path)
102                if filter_jar != "" and file_name == filter_jar:
103                    print("Skipped %s" % file_path)
104                    continue
105                file_list.append(file_path)
106    return file_list
107
108
109def is_need_to_be_parsed(filepath):
110    for item in filter_file_name_list:
111        if -1 != filepath.find(item):
112            return False
113    return True
114
115
116def get_pubilc_func_list_from_headfile(cxx_header_filepath):
117    pubilc_func_list = []
118    try:
119        cpp_header = CppHeaderParser.CppHeader(cxx_header_filepath)
120        for classname in cpp_header.classes:
121            class_name = classname
122            curr_class = cpp_header.classes[classname]
123            for func in curr_class["methods"]["public"]:
124                func_returntype = func["rtnType"]
125                func_name = func["name"]
126                if class_name in FILTER_CLASS_ATTRIBUTE_LIST:
127                    class_name = func_name
128                if func_returntype.find("KVSTORE_API") != -1:
129                    func_returntype = func_returntype.replace("KVSTORE_API", "").strip()
130                if func_name.isupper():
131                    continue
132                if class_name == func_name:
133                    destructor = func["destructor"]
134                    if destructor:
135                        func_name = f"~{func_name}"
136                    func_returntype = ""
137                debug = func["debug"].replace("KVSTORE_API", "")
138                debug = debug.replace(" ", "")
139                debug = debug.strip("{")
140                if debug.endswith("=delete;"):
141                    continue
142                if debug.endswith("=default;"):
143                    continue
144                if debug.startswith("inline"):
145                    continue
146                if debug.startswith("constexpr"):
147                    continue
148                if debug.startswith("virtual"):
149                    continue
150                template = func["template"]
151                if template:
152                    continue
153                param_type_list = [t["type"] for t in func["parameters"]]
154                pubilc_func_list.append((cxx_header_filepath, class_name,
155                                         func_name, param_type_list, func_returntype))
156        for func in cpp_header.functions:
157            func_returntype = func["rtnType"]
158            func_name = func["name"]
159            if func_returntype.find("KVSTORE_API") != -1:
160                func_returntype = func_returntype.replace("KVSTORE_API", "").strip()
161            if func_name.isupper():
162                continue
163            template = func["template"]
164            if template:
165                continue
166            debug = func["debug"].replace("KVSTORE_API", "")
167            debug = debug.replace(" ", "")
168            debug = debug.strip("{")
169            if debug.startswith("inline"):
170                continue
171            if debug.startswith("constexpr"):
172                continue
173            param_type_list = [t["type"] for t in func["parameters"]]
174            pubilc_func_list.append(
175                (cxx_header_filepath, "", func_name, param_type_list,
176                 func_returntype)
177            )
178    except CppHeaderParser.CppParseError:
179        print("")
180    return pubilc_func_list
181
182
183def get_sdk_interface_func_list(part_name):
184    interface_func_list = []
185    sub_path = load_json_data().get(part_name, "")
186    if sub_path == "":
187        return interface_func_list
188
189    sdk_path = os.path.join(CODEPATH, "out", product_name, sub_path)
190    if os.path.exists(sdk_path):
191        file_list = get_file_list_by_postfix(sdk_path, ".h")
192        for file in file_list:
193            try:
194                if is_need_to_be_parsed(file):
195                    interface_func_list += get_pubilc_func_list_from_headfile(file)
196            except Exception:
197                print("get interface error ", sdk_path)
198    else:
199        print("Error: %s is not exist." % sdk_path)
200    return interface_func_list
201
202
203def get_function_info_string(func_string):
204    function_info = ""
205    cxxfilt_filepath = "/usr/bin/c++filt"
206    if os.path.exists(cxxfilt_filepath):
207        command = ["c++filt", func_string]
208        function_info = subprocess.check_output(command, shell=False)
209    else:
210        print("/usr/bin/c++filt is not exist.")
211    return function_info
212
213
214def get_covered_function_list(part_name):
215    covered_function_list = []
216    file_name = f"{part_name}_strip.info"
217    file_path = os.path.join(SUB_SYSTEM_INFO_PATH, file_name)
218    if os.path.exists(file_path):
219        with open(file_path, "r") as fd:
220            for line in fd:
221                if line.startswith("FNDA:"):
222                    sub_line_string = line[len("FNDA:"):].replace("\n", "").strip()
223                    temp_list = sub_line_string.split(",")
224                    if len(temp_list) == 2 and int(temp_list[0]) != 0:
225                        func_info = get_function_info_string(temp_list[1])
226                        after_func_info = func_info.decode("utf-8")
227                        if "" == after_func_info:
228                            continue
229                        after_func_info = after_func_info.replace("\n", "")
230                        if after_func_info == temp_list[1] and after_func_info.startswith("_"):
231                            continue
232                        covered_function_list.append(after_func_info)
233    else:
234        pass
235    return covered_function_list
236
237
238def get_para_sub_string(content):
239    start_index = -1
240    ended_index = -1
241    parentheses_list_left = []
242    parentheses_list_right = []
243
244    for index in range(len(content)):
245        char = content[index]
246        if "<" == char:
247            if 0 == len(parentheses_list_left):
248                start_index = index
249            parentheses_list_left.append(char)
250            continue
251        if ">" == char:
252            parentheses_list_right.append(char)
253            if len(parentheses_list_left) == len(parentheses_list_right):
254                ended_index = index
255                break
256            continue
257
258    if -1 == start_index:
259        substring = content
260    else:
261        if -1 != ended_index:
262            substring = content[start_index:ended_index + 1]
263        else:
264            substring = content[start_index:]
265
266    return substring
267
268
269def filter_para_sub_string(source):
270    content = source
271    if content != "":
272        while True:
273            pos = content.find("<")
274            if -1 != pos:
275                substring = get_para_sub_string(content[pos:])
276                content = content.replace(substring, "")
277            else:
278                break
279    return content
280
281
282def get_function_para_count(func_info):
283    pos_start = func_info.find("(")
284    pos_end = func_info.rfind(")")
285    content = func_info[pos_start + 1: pos_end]
286    if "" == content:
287        return 0
288    content = filter_para_sub_string(content)
289    para_list = content.split(",")
290    return len(para_list)
291
292
293def get_covered_result_data(public_interface_func_list, covered_func_list):
294    coverage_result_list = []
295    for item in public_interface_func_list:
296        data_list = list(item)
297        class_name = data_list[1]
298        func_name = data_list[2]
299        para_list = data_list[3]
300        return_val = data_list[4]
301        para_string = ""
302        for index in range(len(para_list)):
303            if para_list[index].strip() == "":
304                continue
305            curr_para = para_list[index]
306            para_string += curr_para
307            if index < len(para_list) - 1:
308                para_string += ", "
309        fun_string = f"{return_val}' '{func_name}({para_string.strip().strip(',')})"
310        fun_string = fun_string.strip()
311        fun_string = filter_para_sub_string(fun_string)
312
313        if class_name != "":
314            find_string = f"::{class_name}::{func_name}("
315        else:
316            find_string = func_name
317        func_info_list = []
318        for line in covered_func_list:
319            if -1 != line.find(find_string):
320                func_info_list.append(line)
321        curr_list = [class_name, fun_string]
322        if len(func_info_list) == 0:
323            curr_list.append("N")
324        elif len(func_info_list) == 1:
325            curr_list.append("Y")
326        else:
327            interface_para_count = len(para_list)
328            find_flag = False
329            for funcinfo in func_info_list:
330                if find_string == funcinfo:
331                    curr_list.append("Y")
332                    break
333                para_count = get_function_para_count(funcinfo)
334                if interface_para_count == para_count:
335                    curr_list.append("Y")
336                    find_flag = True
337                    break
338            if not find_flag:
339                curr_list.append("N")
340        coverage_result_list.append(curr_list)
341    return coverage_result_list
342
343
344def get_interface_coverage_result_list(part_name):
345    public_interface_func_list = []
346    try:
347        interface_func_list = get_sdk_interface_func_list(part_name)
348        public_interface_func_list.extend(interface_func_list)
349    except Exception:
350        print("####")
351    covered_func_list = get_covered_function_list(part_name)
352    interface_coverage_result_list = get_covered_result_data(
353        public_interface_func_list, covered_func_list)
354    return interface_coverage_result_list
355
356
357def get_coverage_data(data_list):
358    covered_count = 0
359    total_count = len(data_list)
360    if 0 != total_count:
361        for item in data_list:
362            if "Y" == item[2] or "Recorded" == item[2]:
363                covered_count += 1
364        coverage = str("%.2f" % (covered_count * 100 / total_count)) + "%"
365    else:
366        coverage = "0%"
367    return covered_count, coverage
368
369
370def get_summary_data(interface_data_list):
371    summary_list = []
372    total_count = 0
373    covered_count = 0
374
375    for item in interface_data_list:
376        subsystem_name = item[0]
377        data_list = item[1]
378        if 0 != len(data_list):
379            count, coverage = get_coverage_data(data_list)
380            summary_list.append([subsystem_name, len(data_list), count, coverage])
381            total_count += len(data_list)
382            covered_count += count
383    if 0 != total_count:
384        total_coverage = str("%.2f" % (covered_count * 100 / total_count)) + "%"
385        summary_list.append(["Summary", total_count, covered_count, total_coverage])
386    return summary_list
387
388
389def make_summary_file(summary_list, output_path):
390    report_path = os.path.join(output_path, "coverage_summary_file.xml")
391    try:
392        with open(report_path, "w") as fd:
393            fd.write('<?xml version="1.0" encoding="UTF-8"?>\n')
394            fd.write('<coverage>\n')
395            for item in summary_list:
396                fd.write("    <item subsystem_name=\"%s\" "
397                         "function_count=\"%s\" coverage_value=\"%s\" />\n" % (
398                    item[0], str(item[1]), item[3]))
399            fd.write('</coverage>\n')
400    except(IOError, ValueError):
401        print("Error for make coverage result:",)
402
403
404def make_result_file(interface_data_list, summary_list, output_path, title_name):
405    report_path = os.path.join(output_path, "ohos_interfaceCoverage.html")
406    make_report.create_html_start(report_path)
407    make_report.create_title(report_path, title_name, summary_list)
408    make_report.create_summary(report_path, summary_list)
409    for item in interface_data_list:
410        subsystem_name = item[0]
411        data_list = item[1]
412        if 0 == len(data_list):
413            continue
414        count, coverage = get_coverage_data(data_list)
415        make_report.create_table_test(
416            report_path, subsystem_name, data_list, len(data_list), count)
417    make_report.create_html_ended(report_path)
418
419
420def make_coverage_result_file(interface_data_list, output_path, title_name):
421    summary_list = get_summary_data(interface_data_list)
422    make_summary_file(summary_list, output_path)
423    make_result_file(interface_data_list, summary_list, output_path, title_name)
424
425
426def make_interface_coverage_result(part_list):
427    interface_data_list = []
428    for part_name in part_list:
429        coverage_result_list = get_interface_coverage_result_list(
430            part_name)
431        interface_data_list.append([part_name, coverage_result_list])
432    make_coverage_result_file(interface_data_list, OUTPUT_REPORT_PATH,
433                              "Inner Interface")
434
435
436if __name__ == "__main__":
437    current_path = os.getcwd()
438    CODEPATH = current_path.split("/test/testfwk/developer_test")[0]
439    SUB_SYSTEM_INFO_PATH = os.path.join(
440        CODEPATH, "test/testfwk/developer_test/localCoverage/codeCoverage/results/coverage/reports/cxx")
441    OUTPUT_REPORT_PATH = os.path.join(
442        CODEPATH, "test/testfwk/developer_test/localCoverage/interfaceCoverage/results/coverage/interface_kits"
443    )
444    _init_sys_config()
445    from localCoverage.utils import get_product_name, get_target_cpu
446    product_name = get_product_name(CODEPATH)
447    cpu_type = get_target_cpu(CODEPATH)
448    PATH_INFO_PATH = "out/{}/innerkits/ohos-{}".format(product_name, cpu_type)
449    OUTPUT_JSON_PATH = "out/{}/packages/phone/innerkits/ohos-{}".format(
450        product_name, cpu_type)
451    KIT_MODULES_INFO = "out/{}/packages/phone/innerkits/ohos-{}/kits_modules_info.json".format(
452        product_name, cpu_type)
453
454    part_args = sys.argv[1]
455    part_name_list = part_args.split("testpart=")[1].split(",")
456    get_innerkits_json.gen_parts_info_json(
457        get_innerkits_json.get_parts_list(os.path.join(CODEPATH, PATH_INFO_PATH)),
458        os.path.join(CODEPATH, OUTPUT_JSON_PATH), cpu_type
459    )
460    if len(part_name_list) > 0:
461        make_interface_coverage_result(part_name_list)
462    else:
463        print("subsystem_name not exists!")
464