精准测试工具开发分享

本文介绍了一款使用Python编写的Java代码改动影响范围分析工具,通过javalang库解析文件结构,unidiff库处理gitdiff,以帮助开发者分析两次commit之间的代码影响。工具能追踪从改动到最上层控制器的影响路径并生成树形展示结果。
摘要由CSDN通过智能技术生成

开源地址

GitHub - baikaishuipp/jcci: Java code commit impact, java code change impact analysis,java代码改动影响范围分析工具

技术实现

适用于Java代码改动影响范围分析

代码主要由 python 编写,主要涉及 2 个库:

  • javalang java 文件语法解析库
  • unidiff git diff 信息解析库

通过 javalang 语法解析获取每个 Java 文件的 import class extends implements declarators methods 等信息

通过 unidiff 解析 git diff 信息(diff file, added_line_num, removed_lin_num)

然后根据文件增删的代码行去判断影响了哪些类和方法,不断遍历受影响的类和方法直至找到最上层的 controller 层

通过传入项目 git 地址 分支 两次的 commit id,即可分析出两次 commit id 之间代码改动所带来的影响,并生成树图数据方便展示影响链路。

具体过程

clone项目

参数传入项目地址后,通过python os.system执行git clone命令

执行Diff

参数传入项目分支,两次的git commit id

通过python os.system执行git diff命令

git diff commit_id2..commit_id1 > diff_commit_id2..commit_id1.txt

解析项目文件

通过javalang库分别解析项目目录中所有的类文件,获取类的所有信息

包含文件路径、包名、imports、类名、继承类、实现类、变量、方法和是否是controller

变量

记录变量类型、变量名称、所在行

方法

获取方法名、参数、起始行、结束行、方法体、包含类、包含变量、是否是接口及接口路径等

解析Diff信息

通过unidiff库解析diff_commit_id2..commit_id1.txt结果,获取改动文件和改动行号

from unidiff import PatchSet


with open(diff_file, encoding='UTF-8') as f:
    diff_text = f.read()
patch_set = PatchSet(diff_text)

解析patch

遍历每个patch,获取改动影响范围

大致过程是获取改动类的目录路径和增删行号,然后获取解析结果对应的类,判断增删行号落在哪个变量或方法中间,从而定位受影响的类或方法

def _analyze_diff_patch(patch, head_java_file_analyze_result, base_java_file_analyze_result):
    if '.git' in patch.path or 'src' + sep + 'test' + sep in patch.path or (not patch.path.endswith('.java') and not patch.path.endswith('.xml')):
        return None
    line_num_added, line_content_added, line_num_removed, line_content_removed = _diff_patch_lines(patch)
    java_file_path = patch.path
    methods_impacted = {}
    declarators_impacted = {}
    _diff_patch_impact(methods_impacted, declarators_impacted, head_java_file_analyze_result, java_file_path,
                       line_num_added, 'ADD')
    _diff_patch_impact(methods_impacted, declarators_impacted, base_java_file_analyze_result, java_file_path,
                       line_num_removed, 'DEL')
    diff_result = JavaDiffResult(patch.path, line_num_added, line_content_added, line_num_removed, line_content_removed)
    diff_result.changed_methods = methods_impacted
    diff_result.changed_declarators = declarators_impacted
    return diff_result

接着继续解析改动范围,评估影响范围,不断遍历至结束,就获得了最终的影响范围结果

生成影响范围结果

影响范围结果以树形方式展现,生成树形展现的json

def _gen_treemap_data(diff_results, commit_first, commit_second):
    flare = {'name': commit_second + '..' + commit_first, 'children': []}
    api_list = []
    diff_results.reverse()
    diff_filepath = []
    for diff_result in diff_results:
        if diff_result.filepath in diff_filepath:
            continue
        diff_filepath.append(diff_result.filepath)
        class_name = diff_result.filepath.split('/')[-1].split('.')[0]
        if diff_result.filepath.endswith('.xml'):
            class_name = diff_result.filepath.split('/')[-1]
        flare_children = {'name': class_name, 'children': [], 'collapsed': True}
        flare_children_list = []
        changed_methods = diff_result.changed_methods
        for key in changed_methods.keys():
            if changed_methods[key].get('is_api'):
                flare_children_children = {'name': 'methods.' + key + '(' + str(changed_methods[key]['api_path']) + ')',
                                           'children': [], 'collapsed': True}
                api_list_item = {'name': str(changed_methods[key]['api_path'])}
                if api_list_item not in api_list:
                    api_list.append(api_list_item)
            else:
                flare_children_children = {'name': 'methods.' + key, 'children': [], 'collapsed': True}
            if changed_methods[key].get('impact') is not None:
                for method_impact in changed_methods[key]['impact']:
                    flare_children_sub = {'name': 'impacted.' + method_impact, 'children': [],
                                          'collapsed': True}
                    if flare_children_sub not in flare_children_children['children']:
                        flare_children_children['children'].append(flare_children_sub)
            flare_children_list.append(flare_children_children)
        changed_declarators = diff_result.changed_declarators
        for key in changed_declarators.keys():
            flare_children_children = {'name': 'declarators.' + key, 'children': [], 'collapsed': True}
            if changed_declarators[key].get('impact') is not None:
                for declarator_impact in changed_declarators[key]['impact']:
                    flare_children_sub = {'name': 'impacted.' + declarator_impact, 'children': [],
                                          'collapsed': True}
                    if flare_children_sub not in flare_children_children['children']:
                        flare_children_children['children'].append(flare_children_sub)
            flare_children_list.append(flare_children_children)
        flare_children['children'] = flare_children_list
        flare['children'].append(flare_children)
    flare['children'].reverse()
    flare['children'].insert(0, {'name': 'Impact_Apis', 'children': api_list})
    return flare

展现结果

最终结果如下

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值