• 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
19import os
20import re
21import time
22import json
23import stat
24import fcntl
25import platform
26import linecache
27import traceback
28from multiprocessing import Pool
29from lxml import html
30from selectolax.parser import HTMLParser
31
32FLAGS_WRITE = os.O_WRONLY | os.O_CREAT | os.O_EXCL
33FLAGS_ADD = os.O_WRONLY | os.O_APPEND | os.O_CREAT
34MODES = stat.S_IWUSR | stat.S_IRUSR
35
36
37class CoverageReportPath:
38    def __init__(self, report_path):
39        self.report_path = report_path
40
41    def gcov_file_generator(self):
42        """
43        gcov文件生成器
44        """
45        for root_path, _, files in os.walk(self.report_path):
46            for file in files:
47                if not file.endswith(".gcov.html"):
48                    continue
49                file_path = os.path.join(root_path, file)
50                yield file_path
51
52    def modify_report_style(self):
53        """
54        修改覆盖率报告css样式
55        """
56        css_file_path = ""
57        for root_path, _, files in os.walk(self.report_path):
58            for file in files:
59                if file == "gcov.css":
60                    css_file_path = os.path.join(root_path, file)
61        text = """
62/* update */
63span.linenumupdate, span.branchexecupdate, span.branchnoexecupdate,
64span.linecovupdate, span.linenocovupdate, span.branchcovupdate,
65span.branchnocovupdate
66{
67  background-color:#BBBBBB;
68}"""
69        if os.path.exists(css_file_path):
70            with os.fdopen(os.open(css_file_path, FLAGS_ADD, MODES), 'a+') as file:
71                file.write(text)
72
73    def get_statistic_path(self, gcov_file_path: str):
74        """
75        根据gcov.html路径和统计等级获取统计报告路径
76        """
77        file_name_list = ["index.html", "index-sort-b.html",
78                          "index-sort-f.html", "index-sort-l.html"]
79        index_path_list = [
80            os.path.normcase(os.path.join(os.path.dirname(gcov_file_path), file_name))
81            for file_name in file_name_list
82        ]
83        out_path_list = [
84            os.path.normcase(os.path.join(self.report_path, file_name))
85            for file_name in file_name_list
86        ]
87        index_path_list.extend(out_path_list)
88        return index_path_list
89
90
91class KeywordRegistration:
92    def __init__(self, report_path):
93        self.report_path = report_path
94        self.keyword_file_path = os.path.normcase(
95            os.path.join(os.path.dirname(__file__), "keyword.json"))
96
97    def get_keyword_info(self):
98        """
99        获取报备关键字信息
100        """
101        try:
102            with open(self.keyword_file_path, "r") as file:
103                keyword_dict = json.load(file)
104            keyword_list = keyword_dict.get("KEYWORD")
105            return keyword_list
106        except (FileNotFoundError, AttributeError, FileExistsError):
107            print(f"获取报备过滤关键字报错")
108            return []
109
110    @staticmethod
111    def get_coverage_content(file_path):
112        """
113        获取覆盖率文件内容
114        """
115        if not os.path.exists(file_path):
116            return ""
117        with open(file_path, "r", encoding="utf-8") as file:
118            content = file.read()
119        return content
120
121    @staticmethod
122    def get_tag(content, line):
123        start_index = content.find(f'<a name="{line}"')
124        end_index = content.find(f'<a name="{int(line) + 1}"')
125        tag = content[start_index:end_index - 1]
126        return tag
127
128    @staticmethod
129    def get_source_code(tag):
130        etree = html.etree
131        parser = etree.HTMLParser(encoding="utf-8")
132        parser_obj = etree.HTML(tag, parser=parser)
133        source_code = parser_obj.xpath("//span[@class='lineNoCov' or "
134                                       "@class='lineCov' or "
135                                       "@class='linecovupdate' or"
136                                       "@class='linenocovupdate']/text()")
137        if not source_code:
138            index = tag.find(":")
139            source_code = tag[index + 1:].split("</a>")[0]
140
141        if source_code:
142            source_code = "".join(source_code)
143        return source_code
144
145    @staticmethod
146    def update_tag(tag: str):
147        replace_item_list = ["lineNum", "branchNoCov", "branchCov", "lineCov",
148                             "branchNoExec", "lineNoCov", "branchExec"]
149
150        for item in replace_item_list:
151            if item in tag:
152                replace_item = (item + "Update").lower()
153                tag = tag.replace(item, replace_item)
154        return tag
155
156    def get_coverage_lines_by_branch(self, file_path, content=None):
157        """
158        获取覆盖率报告中的所有的if分支行号
159        """
160        if not content:
161            content = self.get_coverage_content(file_path)
162        if not content:
163            return []
164        _, file_name = os.path.split(file_path)
165        branch_line_list = re.findall(
166            r'''<a name="(\d+).*?Branch.*? if \(|
167            <a name="(\d+).*?Branch.*? if\(''', content)
168
169        if branch_line_list:
170            branch_line_list = [int(line_tuple[0]) if line_tuple[0] else
171                                int(line_tuple[1]) for line_tuple in branch_line_list]
172            return branch_line_list
173        else:
174            return []
175
176    def get_coverage_lines_by_keyword(self, keyword, content):
177        """
178        获取关键字在覆盖率报告中的行号
179        """
180        if "&" in keyword:
181            keyword = keyword.replace("&", "")
182        origin_keyword = keyword
183        if keyword.endswith("("):
184            keyword = keyword.replace("(", "")
185            keyword_line_list = re.findall(
186                rf'<a name="(\d+).*?[ .;!:()]{keyword}[ .;!:()&]', content)
187        else:
188            keyword_line_list = re.findall(
189                rf'<a name="(\d+).*?[ .;!:()]{keyword}[ .;!:)&]', content)
190
191        if keyword_line_list:
192            keyword_line_list = [int(line) for line in keyword_line_list]
193            keyword_branch_list = []
194            get_tag = self.get_tag
195            keyword_branch_list_append = keyword_branch_list.append
196            keyword_line_list_remove = keyword_line_list.remove
197            for line in keyword_line_list:
198                tag_html = get_tag(content, line)
199                if origin_keyword not in tag_html:
200                    keyword_branch_list_append(line)
201                if 'title="Branch' not in tag_html:
202                    continue
203                if "switch (" in tag_html or "switch(" in tag_html:
204                    keyword_branch_list_append(line)
205                elif "while (" in tag_html or "while(" in tag_html:
206                    keyword_branch_list_append(line)
207                elif "for (" in tag_html or "for(" in tag_html:
208                    keyword_branch_list_append(line)
209                elif "do {" in tag_html or "do{" in tag_html:
210                    keyword_branch_list_append(line)
211            for key in keyword_branch_list:
212                keyword_line_list_remove(key)
213        return keyword_line_list
214
215    def code_body_judge(self, line, content):
216        """
217        全量报备按行获取覆盖率报告源码
218        """
219        func_body_tag_list = []
220        update_func_body_tag_list = []
221        func_body_tag_list_append = func_body_tag_list.append
222        update_func_body_tag_list_append = update_func_body_tag_list.append
223        get_tag = self.get_tag
224        update_tag = self.update_tag
225        tag = get_tag(content, line)
226        if "{" not in tag:
227            return func_body_tag_list, update_func_body_tag_list
228
229        braces_sub = 1
230        if "{" not in tag:
231            braces_sub = 0
232        next_line = int(line) + 1
233        branch_braces_sub = count = 0
234        sub_brace_start_status = sub_brace_end_status = False
235        while braces_sub:
236            count += 1
237            if count > 200:
238                return [], []
239            next_tag = get_tag(content, str(next_line))
240            func_body_tag_list_append(next_tag)
241            if "{" in next_tag:
242                if branch_braces_sub:
243                    branch_braces_sub += 1
244                braces_sub += 1
245            if "}" in next_tag:
246                if branch_braces_sub:
247                    branch_braces_sub -= 1
248                braces_sub -= 1
249            if 'title="Branch' in next_tag:
250                branch_braces_sub += 1
251                sub_brace_start_status = True
252            if not branch_braces_sub and \
253                    (sub_brace_start_status == sub_brace_end_status):
254                next_tag = update_tag(next_tag)
255            if not branch_braces_sub and sub_brace_start_status:
256                sub_brace_end_status = True
257            update_func_body_tag_list_append(next_tag)
258            next_line += 1
259        return func_body_tag_list, update_func_body_tag_list
260
261    def get_line_funcname(self, branch_line, content):
262        """
263        获取行号源码所在函数的函数名
264        """
265        function_name = ""
266        previous_line = int(branch_line)
267        loop_count = 0
268        get_tag = self.get_tag
269        try:
270            while not function_name:
271                loop_count += 1
272                if loop_count > 200:
273                    return ""
274                previous_line -= 1
275                tag = get_tag(content, previous_line)
276                tag_text = HTMLParser(tag).text()
277                if tag_text[39:].strip().startswith("{"):
278                    left_parentheses_count = right_parentheses_count = 0
279                    child_count = 0
280                    while previous_line:
281                        child_count += 1
282                        if child_count > 200:
283                            return ""
284                        previous_line -= 1
285                        html_text = get_tag(content, previous_line)
286                        source_code = HTMLParser(html_text).text()[39:].strip()
287
288                        left_parentheses_count += source_code.count("(")
289                        right_parentheses_count += source_code.count(")")
290                        if not left_parentheses_count == right_parentheses_count:
291                            continue
292                        if " operator" in source_code:
293                            class_name_list = re.findall(r'\((.*?)\)', source_code)
294                            if class_name_list:
295                                function_name_str = class_name_list[0]
296                                if not function_name_str:
297                                    return ""
298
299                                function_name = function_name_str.split()[-1]
300                                return function_name
301                        function_name_list = re.findall(r' (.*?)\(', source_code)
302                        if function_name_list:
303                            function_name_str = function_name_list[0]
304                            if not function_name_str:
305                                return ""
306                            function_name = function_name_str.split()[-1]
307                            return function_name
308            return ""
309        except (OSError, IndexError, TypeError) as error:
310            print(f"覆盖率报告{branch_line}行获取函数名报错, error:{error}",
311                  traceback.format_exc())
312            return ""
313
314    @staticmethod
315    def get_branch_line_list(keyword_line: int, branch_line_list: list):
316        """
317        获取大于关键字行号的所有分支行号
318        """
319        if keyword_line in branch_line_list:
320            index = branch_line_list.index(keyword_line)
321            branch_line_list = branch_line_list[index:]
322        else:
323            for line in branch_line_list:
324                if line > keyword_line:
325                    index = branch_line_list.index(line)
326                    branch_line_list = branch_line_list[index:]
327                    break
328        return branch_line_list
329
330    @staticmethod
331    def get_keyword_judge_char(keyword, keyword_source_code):
332        """
333        获取关键字替代字符
334        """
335        if "&" in keyword:
336            keyword = keyword.replace("&", "<")
337
338        keyword_index = keyword_source_code.find(keyword)
339        if keyword_index == -1:
340            return ""
341
342        try:
343            keyword_code = keyword_source_code[:keyword_index + len(keyword)]
344            if " = " in keyword_code:
345                judge_key = keyword_code.split(" = ")[0].split()[-1]
346            else:
347                bracket_index = keyword_code.find("(")
348                bracket_code = keyword_code[:bracket_index]
349                judge_key = bracket_code.split()[-1]
350            return judge_key
351        except (IndexError, ValueError):
352            print("获取关键字替代字符失败")
353            return ""
354
355    @staticmethod
356    def get_branch_data_by_tag(tag_html: str, symbol_status=None):
357        """
358        根据前端标签获取分支数据
359        """
360        if symbol_status:
361            key = r"#+\-*"
362        else:
363            key = r"#+\-"
364        branch_line_list = re.findall(rf"> ([{key}]) </span>", tag_html)
365
366        return branch_line_list
367
368    @staticmethod
369    def get_judge_condition_index(judge_key: str, source_code: str):
370        """
371        获取判断条件索引
372        """
373        keyword_index_list = []
374        keyword_index_list_append = keyword_index_list.append
375
376        condition_str_list = re.split(rf"\|\||&&", source_code)
377        for index, code_str in enumerate(condition_str_list):
378            if judge_key in code_str:
379                keyword_index_list_append(index)
380        return keyword_index_list, condition_str_list
381
382    @staticmethod
383    def update_source_code_tag(html_tag: str):
384        replace_item_list = ["lineNum", "lineCov", "lineNoCov"]
385
386        for item in replace_item_list:
387            if item in html_tag:
388                replace_item = (item + "Update").lower()
389                html_tag = html_tag.replace(item, replace_item)
390
391        return html_tag
392
393    def get_break_line_tag(self, content, origin_branch_html, branch_line):
394        """
395        获取分支换行的判断条件源码tag
396        """
397        get_tag = self.get_tag
398        left_brace_exist = False
399        line_break = loop_count = 0
400        while not left_brace_exist:
401            if loop_count > 10:
402                break
403            line_break += 1
404            origin_branch_html = os.path.join(origin_branch_html, "\n")
405            next_line_tag = get_tag(content, branch_line + line_break)
406            if "{" in next_line_tag:
407                left_brace_exist = True
408            origin_branch_html += next_line_tag
409            loop_count += 1
410        return origin_branch_html
411
412    @staticmethod
413    def modify_tag_style(tag, rate):
414        """
415        修改标签样式
416        """
417        if 75 <= rate < 90:
418            tag = tag.replace("headerCovTableEntryLo", "headerCovTableEntryMed")
419            tag = tag.replace("coverPerLo", "coverPerMed")
420            tag = tag.replace("coverNumLo", "coverNumMed")
421        elif rate >= 90:
422            tag = tag.replace("headerCovTableEntryLo", "headerCovTableEntryHi")
423            tag = tag.replace("headerCovTableEntryMed", "headerCovTableEntryHi")
424            tag = tag.replace("coverPerLo", "coverPerHi")
425            tag = tag.replace("coverNumLo", "coverNumHi")
426            tag = tag.replace("coverPerMed", "coverPerHi")
427            tag = tag.replace("coverNumMed", "coverNumHi")
428        return tag
429
430    def update_coverage_ratio_tag(self, file_path):
431        """
432        修改覆盖率比率数据
433        """
434        with open(file_path, "r", encoding="utf-8") as coverage_file:
435            content = coverage_file.read()
436
437        covered_nums = len(re.findall(r"> ([+]) </span>", content))
438        branch_total = len(re.findall(r"> ([+\-#]) </span>", content))
439
440        line = 0
441        start_line = 1
442        content_list = content.splitlines()
443        for line_text in content_list:
444            if '<td class="headerItem">Branches:</td>' in line_text:
445                line = start_line
446                break
447            start_line += 1
448        linecache.clearcache()
449        coverage_tag = linecache.getline(file_path, line)
450        covered_tag = linecache.getline(file_path, line + 1)
451        coverage_total_tag = linecache.getline(file_path, line + 2)
452        try:
453            origin_hit = int(re.findall(r"(\d+)", covered_tag)[0])
454            origin_total = int(re.findall(r"(\d+)", coverage_total_tag)[0])
455            coverage_rate_tag = linecache.getline(file_path, line + 3)
456            replace_tag = coverage_tag + covered_tag + coverage_total_tag + \
457                          coverage_rate_tag
458            if origin_total - branch_total == 0 and origin_hit - covered_nums == 0:
459                return
460            if int(branch_total) == 0:
461                coverage = 0.0
462            else:
463                coverage = round(int(covered_nums) / int(branch_total) * 100, 1)
464
465            origin_rate = re.findall(r"(\d+.\d+)", coverage_rate_tag)[0]
466            update_tag = coverage_tag + covered_tag.replace(
467                str(origin_hit), str(covered_nums)) + \
468                         coverage_total_tag.replace(str(origin_total),
469                                                    str(branch_total)) + \
470                         coverage_rate_tag.replace(origin_rate, str(coverage))
471
472            update_tag = self.modify_tag_style(update_tag, coverage)
473            if replace_tag in content:
474                content = content.replace(replace_tag, update_tag)
475
476            os.remove(file_path)
477            with os.fdopen(os.open(file_path, FLAGS_WRITE, MODES), 'w') as file:
478                file.write(content)
479
480            # 修改数据统计页面
481            hit_shield_num = origin_hit - covered_nums
482            total_shield_num = origin_total - branch_total
483            self.update_statistic(file_path, hit_shield_num, total_shield_num)
484        except (IndexError, TypeError, FileNotFoundError):
485            print("修改分支数据失败")
486
487    def update_statistic(self, file_path, hit_shield_num, total_shield_num):
488        """
489        修改覆盖率分支数据
490        """
491        index_path_list = CoverageReportPath(self.report_path).get_statistic_path(file_path)
492        for index_path in index_path_list:
493            if not os.path.exists(index_path):
494                continue
495
496            with open(index_path, "r", encoding="utf-8") as coverage_file:
497                fcntl.flock(coverage_file.fileno(), fcntl.LOCK_EX)
498                content = coverage_file.read()
499
500            file_dir, file_name = os.path.split(file_path)
501            if os.path.normcase(file_dir) not in os.path.normcase(index_path):
502                if platform.system() == "Windows":
503                    judge_item = file_dir.replace(self.report_path + "\\", "")
504                    judge_item = ">" + judge_item.replace("\\", "/") + "</a>"
505                else:
506                    judge_item = file_dir.replace(self.report_path + "/", "")
507                    judge_item = ">" + judge_item + "</a>"
508            else:
509                judge_item = ">" + file_name.replace(".gcov.html", "")
510
511            start_line = 1
512            line = tag_line = 0
513            content_list = content.splitlines()
514            tmp = '<td class="headerItem">Branches:</td>'
515            for line_text in content_list:
516                if not line and tmp in line_text:
517                    line = start_line
518                if judge_item in line_text:
519                    tag_line = start_line
520                    break
521                start_line += 1
522            try:
523                # total statistic modify
524                linecache.clearcache()
525                coverage_head_tag = linecache.getline(index_path, line)
526                origin_hit_tag = linecache.getline(index_path, line + 1)
527                origin_hit_num = int(re.findall(r"(\d+)", origin_hit_tag)[0])
528                origin_total_tag = linecache.getline(index_path, line + 2)
529                origin_total_num = int(re.findall(r"(\d+)", origin_total_tag)[0])
530                origin_rate_tag = linecache.getline(index_path, line + 3)
531                origin_rate = re.findall(r"(\d+.\d+)", origin_rate_tag)[0]
532                replace_tag = coverage_head_tag + origin_hit_tag + \
533                              origin_total_tag + origin_rate_tag
534
535                # file branch modify
536                no_change_file_tag = ""
537                for i in range(8):
538                    no_change_file_tag += linecache.getline(index_path, tag_line + i)
539                file_rate_tag = linecache.getline(index_path, tag_line + 8)
540                file_covered_tag = linecache.getline(index_path, tag_line + 9)
541                file_tag = no_change_file_tag + file_rate_tag + file_covered_tag
542                origin_file_covered_num, origin_file_total_num = map(
543                    int, re.findall(r"(\d+)", file_covered_tag))
544                if origin_file_total_num:
545                    origin_file_rate = re.findall(r"(\d+.\d+)", file_rate_tag)[0]
546                else:
547                    # 不存在覆盖率
548                    origin_file_rate = "-"
549                head_hit_num = origin_hit_num - hit_shield_num
550                head_total_num = origin_total_num - total_shield_num
551                if head_total_num == 0:
552                    rate = 0.0
553                else:
554                    rate = round(head_hit_num / head_total_num * 100, 1)
555                file_hit_num = origin_file_covered_num - hit_shield_num
556                file_total_num = origin_file_total_num - total_shield_num
557                if file_total_num == 0:
558                    file_rate = 0.0
559                else:
560                    file_rate = round(file_hit_num / file_total_num * 100, 1)
561                update_tag = coverage_head_tag + origin_hit_tag.replace(
562                    str(origin_hit_num), str(head_hit_num)) + \
563                             origin_total_tag.replace(str(origin_total_num),
564                                                      str(head_total_num)) + \
565                             origin_rate_tag.replace(origin_rate, str(rate))
566
567                update_tag = self.modify_tag_style(update_tag, rate)
568
569                update_branch_tag = file_rate_tag.replace(
570                    origin_file_rate, str(file_rate)) + \
571                                    file_covered_tag.replace(
572                                        str(origin_file_covered_num) + " / " +
573                                        str(origin_file_total_num), str(file_hit_num) + " / " +
574                                        str(file_total_num))
575
576                update_branch_tag = self.modify_tag_style(update_branch_tag, file_rate)
577                update_branch_tag = no_change_file_tag + update_branch_tag
578                if replace_tag in content:
579                    content = content.replace(replace_tag, update_tag)
580                if file_tag in content:
581                    content = content.replace(file_tag, update_branch_tag)
582                os.remove(index_path)
583                with os.fdopen(os.open(index_path, FLAGS_WRITE, MODES), 'w') as file:
584                    file.write(content)
585                    fcntl.flock(file.fileno(), fcntl.LOCK_UN)
586                time.sleep(1)
587            except (IndexError, TypeError, FileNotFoundError):
588                print("修改分支统计数据出错")
589
590    def _check_if_branch_line(self, judge_key, sub_branch_line_list,
591                              key_line, content, function_name):
592        """
593        确定if分支行号
594        """
595        if_branch_line = None
596        for branch_line in sub_branch_line_list:
597            if branch_line == key_line:
598                if_branch_line = key_line
599                break
600            # 获取分支行所在函数名
601            branch_function_name = self.get_line_funcname(
602                branch_line, content)
603            # 关键字范围只在关键字所在函数之内
604            branch_line_tag = self.get_tag(content, branch_line)
605            try:
606                if "{" not in branch_line_tag:
607                    branch_line_tag = self.get_break_line_tag(
608                        content, branch_line_tag, branch_line)
609                branch_line_source_code = self.get_source_code(
610                    branch_line_tag)
611                if function_name == branch_function_name:
612                    if judge_key in branch_line_source_code:
613                        if_branch_line = branch_line
614                        break
615                else:
616                    break
617            except (ValueError, KeyError):
618                print("获取关键字if分支行报错", traceback.format_exc())
619
620        return if_branch_line
621
622    @staticmethod
623    def _branch_replace(branch):
624        """
625        分支符号替换
626        """
627        if branch == "#":
628            branch = "> # <"
629        elif branch == "+":
630            branch = "> + <"
631        elif branch == "*":
632            branch = "> * <"
633        else:
634            branch = "> - <"
635        return branch
636
637    @staticmethod
638    def _single_condition_modify_html(branch_html, branch_list):
639        """
640        单条件修改代码块html
641        """
642        line_item = '<span class="lineNum">         </span>'
643        line_feed_index = branch_html.find(line_item)
644        if line_feed_index == -1:
645            if "+" in branch_list:
646                update_branch_tag = branch_html.replace("> - <", ">   <")
647                update_branch_tag = update_branch_tag.replace("> # <", ">   <")
648            else:
649                try:
650                    first_branch = branch_list[0]
651                    first_branch = "> " + first_branch + " <"
652                    first_branch_index = branch_html.find(first_branch)
653                    branch_tag = branch_html[:first_branch_index + 5]
654                    update_branch_tag = branch_html[first_branch_index + 5:]
655                    update_branch_tag = update_branch_tag.replace("> - <", ">   <")
656                    update_branch_tag = update_branch_tag.replace("> # <", ">   <")
657                    update_branch_tag = branch_tag + update_branch_tag
658                except ValueError:
659                    return ""
660        else:
661            line_feed_index = branch_html.find(line_item)
662            update_branch_tag = branch_html[:line_feed_index + len(line_item) + 1]
663            if "-" not in branch_list and "+" not in branch_list:
664                del_count = update_branch_tag.count("> # <")
665                update_branch_tag = update_branch_tag.replace("> # <", ">   <", del_count - 1)
666            else:
667                update_branch_tag = update_branch_tag.replace("> - <", ">   <")
668                update_branch_tag = update_branch_tag.replace("> # <", ">   <")
669            branch_tag = branch_html[line_feed_index + len(line_item) + 1:]
670            line_feed_index = branch_tag.find(line_item)
671            if line_feed_index == -1:
672                branch_tag = branch_tag.replace("> - <", ">   <")
673                branch_tag = branch_tag.replace("> # <", ">   <")
674                update_branch_tag += branch_tag
675            else:
676                loop_count = 0
677                while line_feed_index + 1:
678                    loop_count += 1
679                    if loop_count > 200:
680                        continue
681                    try:
682                        update_branch_tag += branch_tag[:line_feed_index + len(line_item) + 1]
683                        update_branch_tag = update_branch_tag.replace("> - <", ">   <")
684                        update_branch_tag = update_branch_tag.replace("> # <", ">   <")
685                        branch_tag = branch_tag[line_feed_index + len(line_item) + 1:]
686                        line_feed_index = branch_tag.find(line_item)
687                    except ValueError:
688                        return ""
689
690                branch_tag = branch_tag.replace("> - <", ">   <")
691                update_branch_tag = update_branch_tag.replace("> # <", ">   <")
692                update_branch_tag += branch_tag
693        return update_branch_tag
694
695    def _multi_condition_modify_html(self, branch_html, branch_length,
696                                     condition_str_list, judge_index_list):
697        """
698        多条件修改代码块html
699        """
700        line_item = '<span class="lineNum">         </span>'
701        if branch_length % len(condition_str_list):
702            line_feed_index = branch_html.find(line_item)
703            update_branch_tag = branch_html[:line_feed_index]
704            update_branch_tag = update_branch_tag.replace("> - <", ">   <")
705            branch_html = branch_html[line_feed_index:]
706            loop_count = 0
707            while line_feed_index + 1:
708                loop_count += 1
709                if loop_count > 200:
710                    continue
711                line_feed_index = branch_html.count(line_item)
712                if line_feed_index > 1:
713                    try:
714                        line_feed_length = len(line_item)
715                        branch_tag_before = branch_html[:line_feed_length]
716                        branch_html = branch_html[line_feed_length:]
717                        line_feed_index = branch_html.find(line_item)
718                        branch_tag_after = branch_html[:line_feed_index]
719                        branch_tag_after = branch_tag_after.replace("> - <", ">   <")
720                        branch_tag = branch_tag_before + branch_tag_after
721                    except ValueError:
722                        return ""
723                else:
724                    branch_tag = branch_html
725                    branch_tag = branch_tag.replace("> - <", ">   <")
726                    # 不再换行,索引为-1
727                    line_feed_index = -1
728
729                update_branch_tag += branch_tag
730                if line_feed_index == -1:
731                    branch_html = ""
732                else:
733                    branch_html = branch_html[line_feed_index:]
734            if branch_html != "":
735                branch_html = branch_html.replace("> - <", ">   <")
736                update_branch_tag += branch_html
737        else:
738            branch_list = self.get_branch_data_by_tag(branch_html, True)
739            update_branch_tag = ""
740            end_count = -1
741            try:
742                for index in judge_index_list:
743                    branch_data = branch_list[:(index + 1) * 2]
744                    # 要修改的分支数据列表长度
745                    branch_data_length = len(branch_data)
746                    change_status = False
747                    for count, branch in enumerate(branch_data, 1):
748                        if count <= end_count:
749                            continue
750
751                        end_count = count
752                        branch = self._branch_replace(branch)
753                        end_index = branch_html.find(branch)
754                        branch_tag = branch_html[:end_index + 5]
755                        if branch_data_length - count in [0, 1]:
756                            if change_status:
757                                continue
758                            if branch == "> # <":
759                                change_status = True
760                                branch_tag = branch_tag.replace("> # <", "> * <")
761                            elif branch == "> - <":
762                                change_status = True
763                                branch_tag = branch_tag.replace("> - <", "> * <")
764                        update_branch_tag += branch_tag
765                        branch_html = branch_html[end_index + 5:]
766            except (ValueError, TypeError):
767                return ""
768            update_branch_tag += branch_html
769        return update_branch_tag
770
771    def judge_branch_exists(self, file_path):
772        """
773        判断报告是否存在分支
774        """
775        content = self.get_coverage_content(file_path)
776        branch_total = len(re.findall(r"> ([+\-#]) </span>", content))
777        if branch_total:
778            return True
779        else:
780            return False
781
782    def modify_branch(self, file_path, keyword_list):
783        """
784        html报告分支屏蔽修改
785        """
786        branch_line_list = self.get_coverage_lines_by_branch(file_path)
787        if not branch_line_list:
788            return
789
790        branch_line_list.sort()
791        no_change_content = self.get_coverage_content(file_path)
792        for keyword in keyword_list:
793            content = self.get_coverage_content(file_path)
794            keyword_line_list = self.get_coverage_lines_by_keyword(keyword, content)
795            if not keyword_line_list:
796                continue
797            for key_line in keyword_line_list:
798                key_html = self.get_tag(content, key_line)
799                if not key_html or "LOG" in key_html:
800                    continue
801
802                function_name = self.get_line_funcname(key_line, content)
803                sub_branch_line_list = self.get_branch_line_list(
804                    key_line, branch_line_list)
805                keyword_source_code = self.get_source_code(key_html)
806                if not all([function_name, keyword_source_code,
807                            sub_branch_line_list]):
808                    continue
809
810                judge_key = keyword
811                if 'title="Branch' not in key_html:
812                    judge_key = self.get_keyword_judge_char(
813                        keyword, keyword_source_code)
814                if "*" in judge_key:
815                    judge_key = judge_key.replace("*", "")
816                if "&" in judge_key:
817                    judge_key = judge_key.replace("&", "")
818                if_branch_line = self._check_if_branch_line(
819                    judge_key, sub_branch_line_list, key_line,
820                    content, function_name)
821
822                if not if_branch_line:
823                    continue
824
825                origin_branch_html = self.get_tag(content, if_branch_line)
826                branch_html = origin_branch_html
827                if "{" not in origin_branch_html:
828                    origin_branch_html = self.get_break_line_tag(
829                        content, origin_branch_html, if_branch_line)
830                    branch_html = origin_branch_html
831                top_source_code = self.get_source_code(branch_html)
832                no_change_branch_html = self.get_tag(
833                    no_change_content, if_branch_line)
834                branch_list = self.get_branch_data_by_tag(no_change_branch_html)
835                if not branch_list:
836                    continue
837                if "#" not in branch_list and "-" not in branch_list:
838                    continue
839                branch_tag_data = self.get_branch_data_by_tag(branch_html, True)
840                if len(branch_tag_data) <= 1:
841                    continue
842                if len(branch_list) == 2:
843                    if "-" in branch_list:
844                        update_branch_tag = branch_html.replace("> - <", ">   <")
845                    else:
846                        update_branch_tag = branch_html.replace("> # <", ">   <", 1)
847                else:
848                    branch_length = len(branch_list)
849                    judge_index_list, condition_str_list = self.\
850                        get_judge_condition_index(judge_key, top_source_code)
851                    if not len(judge_index_list):
852                        continue
853                    if len(condition_str_list) == 1:
854                        update_branch_tag = self._single_condition_modify_html(
855                            branch_html, branch_list)
856                    else:
857                        update_branch_tag = self._multi_condition_modify_html(
858                            branch_html, branch_length, condition_str_list,
859                            judge_index_list)
860                    if not update_branch_tag:
861                        return
862
863                update_branch_tag = self.update_source_code_tag(
864                    update_branch_tag)
865                content = content.replace(origin_branch_html, update_branch_tag)
866                source_code_tag_list, update_source_code_tag_list = self.\
867                    code_body_judge(int(if_branch_line), content)
868                if source_code_tag_list:
869                    source_code_tag_html = "\n".join(source_code_tag_list)
870                    update_source_code_tag_html = "\n".join(
871                        update_source_code_tag_list)
872                    content = content.replace(
873                        source_code_tag_html, update_source_code_tag_html)
874            os.remove(file_path)
875            with os.fdopen(os.open(file_path, FLAGS_WRITE, MODES), 'w') as new_html:
876                new_html.write(content)
877
878        content = self.get_coverage_content(file_path)
879        content = content.replace('> * </span>', '>   </span>')
880        os.remove(file_path)
881
882        with os.fdopen(os.open(file_path, FLAGS_WRITE, MODES), 'w') as new_html:
883            new_html.write(content)
884
885    def keyword_registration(self, file_path, keyword_list):
886        """
887        报备
888        """
889        self.modify_branch(file_path, keyword_list)
890
891        # 修改覆盖率数据
892        self.update_coverage_ratio_tag(file_path)
893        return True
894
895    def multiprocessing_registration(self):
896        """
897        多进程关键字报备
898        """
899        keyword_list = self.get_keyword_info()
900        if not keyword_list:
901            return
902
903        # 创建报告路径生成器
904        report_instance = CoverageReportPath(self.report_path)
905        # 修改css文件
906        report_instance.modify_report_style()
907        # 创建报告路径生成器
908        report_generator = report_instance.gcov_file_generator()
909        # 获取关键字
910        keyword_registration = self.keyword_registration
911
912        pool = Pool(10)
913        apply_async = pool.apply_async
914        while True:
915            file_path = next(report_generator, None)
916            if not file_path:
917                break
918
919            # 关键字报备
920            apply_async(keyword_registration, args=(file_path, keyword_list))
921
922        pool.close()
923        pool.join()
924
925
926def main(report_path):
927    print("*" * 50, "报备开始", "*" * 50)
928    manager = KeywordRegistration(report_path)
929    start_time = time.time()
930    manager.multiprocessing_registration()
931    end_time = time.time()
932    times = round(end_time - start_time, 3)
933    print("*" * 50, "报备结束", "*" * 50, "耗时:", times)
934
935
936if __name__ == '__main__':
937    current_path = os.getcwd()
938    home_path = current_path.split("/test/testfwk/developer_test")[0]
939    developer_path = os.path.join(home_path, "test/testfwk/developer_test")
940    html_path = os.path.join(developer_path, "localCoverage/codeCoverage/results/coverage/reports/cxx/html")
941    main(html_path)
942