Android增量报告生成新方案

   在开发精准测试覆盖率相关的功能时候,对于Android的增量报告,由于担心修改jacoco插件会影响App的打包,所以一直没有修改。在网上查了一下,没有找到合适的方案。只有一个diff-cover开源项目:https://github.com/abmaonline/diff-cover

图片

经过测试,可以生成对比分支的增量报告,可是对于对比版本,就不太适应了。于是就各种调研,最后通过最低层的方案,过滤文件和函数,于是有了第一个版本的增量覆盖率报告生成方案。

一,增量覆盖率报告方案一

第一版本的增量覆盖率报告如下流程:

图片

此方案虽然能生成增量覆盖率报告,但存在如下问题:

1,diff-cover生成的报告只能对比分支,不能对比版本;并且生成的覆盖率报告和jacoco相差很大;

2,生成的报告数据欠缺,diff-cover只有行覆盖率;而对比版本过滤的方案,生成的有行,方法和类,但没有分支数据;

3,操作比较繁琐,要过滤很多数据,如果diff文件较多,生成时间比较长;

4,最终的代码渲染没有办法按增量处理,只能过滤出diff类对应的文件,渲染则是全量的。

二,增量覆盖率报告方案二

     Android jacoco生成增量报告,早期没有找到合适的方案,后面做了一些调研,发现下面有个方案:

1,参考文档

Android 增量代码测试覆盖率工具实践:https://juejin.cn/post/6920029313316159502

2,开源项目

AndJacoco:https://github.com/ttpai/AndJacoco

图片

此方案的核心思想是:

  • 通过指定的对比方式或是分支,或是版本,找到diff的文件及函数列表;

  • 在jacoco进行注入的时候,只对增量代码做注入;

  • 最后根据采集的覆盖率数据,生成报告,即为增量报告。

经过各种尝试,发现这个项目在文件:org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java 中与jacoco github上的有差别::https://github.com/jacoco/jacoco/blob/master/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/KotlinInlineFilter.java

主要是下面这个函数,修改成与github上的一样就可以,可能是这个jacoco项目比较早的原因:

private static int getFirstGeneratedLineNumber(final String sourceFileName,final String smap) {try {final BufferedReader br = new BufferedReader(new StringReader(smap));expectLine(br, "SMAP");// OutputFileNameexpectLine(br, sourceFileName);// DefaultStratumIdexpectLine(br, "Kotlin");// StratumSectionexpectLine(br, "*S Kotlin");// FileSectionexpectLine(br, "*F");final BitSet sourceFileIds = new BitSet();String line;while (!"*L".equals(line = br.readLine())) {// AbsoluteFileNamebr.readLine();final Matcher m = FILE_INFO_PATTERN.matcher(line);if (!m.matches()) {throw new IllegalStateException("Unexpected SMAP line: " + line);}final String fileName = m.group(2);if (fileName.equals(sourceFileName)) {sourceFileIds.set(Integer.parseInt(m.group(1)));}}if (sourceFileIds.isEmpty()) {throw new IllegalStateException("Unexpected SMAP FileSection");}// LineSectionint min = Integer.MAX_VALUE;while (true) {line = br.readLine();if (line.equals("*E") || line.equals("*S KotlinDebug")) {break;}final Matcher m = LINE_INFO_PATTERN.matcher(line);if (!m.matches()) {throw new IllegalStateException("Unexpected SMAP line: " + line);}final int inputStartLine = Integer.parseInt(m.group(1));final int lineFileID = Integer.parseInt(m.group(2).substring(1));final int outputStartLine = Integer.parseInt(m.group(4));if (sourceFileIds.get(lineFileID)&& inputStartLine == outputStartLine) {continue;}min = Math.min(outputStartLine, min);}return min;} catch (final IOException e) {// Must not happen with StringReaderthrow new AssertionError(e);}
}

开源项目的函数检测也有问题,需要改成如下所示:


CodeDiffUtil.java
/******************************************************************************** Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors* This program and the accompanying materials are made available under* the terms of the Eclipse Public License 2.0 which is available at* http://www.eclipse.org/legal/epl-2.0** SPDX-License-Identifier: EPL-2.0** Contributors:*    Marc R. Hoffmann - initial API and implementation********************************************************************************/
package org.jacoco.core.internal.diff;import org.jacoco.core.analysis.CoverageBuilder;
import org.objectweb.asm.Type;import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;/*** @ProjectName: root* @Package: org.jacoco.core.internal.diff* @Description: 差异代码处理类* @Author: duanrui* @CreateDate: 2021/1/12 15:17* @Version: 1.0*           <p>*           Copyright: Copyright (c) 2021*/
public class CodeDiffUtil {private final static String OPERATE_ADD = "ADD";private static String OutputStream;/*** 检测类是否在差异代码中** @param className* @return Boolean*/public static Boolean checkClassIn(String className,List<ClassInfoDto> classInfos) {if (null == classInfos || classInfos.isEmpty() || null == className) {return Boolean.FALSE;}System.out.println("className="+className)// 这里要考虑匿名内部类的问题return classInfos.stream().anyMatch(c -> className.equals(c.getClassFile())|| className.split("\\$")[0].equals(c.getClassFile()));}/*** 检测方法是否在差异代码中** @param className* @param methodName* @return Boolean*/public static Boolean checkMethodIn(String className, String methodName,String desc, List<ClassInfoDto> classInfos) {// 参数校验if (null == classInfos || classInfos.isEmpty() || null == methodName|| null == className) {return Boolean.FALSE;}ClassInfoDto classInfoDto = classInfos.stream().filter(c -> className.equals(c.getClassFile())|| className.split("\\$")[0].equals(c.getClassFile())).findFirst().orElse(null);if (null == classInfoDto) {return Boolean.FALSE;}// 如果是新增类,不用匹配方法,直接运行if (OPERATE_ADD.equals(classInfoDto.getType())) {return Boolean.TRUE;}if (null == classInfoDto.getMethodInfos()|| classInfoDto.getMethodInfos().isEmpty()) {return Boolean.FALSE;}// 匹配了方法,参数也需要校验return classInfoDto.getMethodInfos().stream().anyMatch(m -> {if (methodName.equals(m.getMethodName())) {// System.out.println("className=" + className + ",methodName="// + methodName + ",parmas=" + desc + ",m.getParameters()="// + m.getParameters().toString());return checkParamsIn(m.getParameters(), desc);// lambda表示式匹配} else if (methodName.contains("lambda$")&& methodName.split("\\$")[1].equals(m.getMethodName())) {return Boolean.TRUE;} else {return Boolean.FALSE;}});}/*** 匹配参数* @param params*            格式:String a* @param desc*            转换后格式: java.lang.String* @return*/public static Boolean checkParamsIn(List<String> params, String desc) {// 解析ASM获取的参数Type[] argumentTypes = Type.getArgumentTypes(desc);// 处理一下params,直接使用list有问题String ckparams = "";if (params.size() == 1) {ckparams = params.get(0);ckparams = ckparams.trim();if (ckparams.length() == 0 && argumentTypes.length == 0) {return Boolean.TRUE;} else {String[] diffParams = ckparams.split(",");// 只有参数数量完全相等才做下一次比较,Type格式:I C Ljava/lang/String;if (diffParams.length > 0&& argumentTypes.length == diffParams.length) {for (int i = 0; i < argumentTypes.length; i++) {// 去掉包名只保留最后一位匹配,getClassName格式: int java/lang/StringString[] args = argumentTypes[i].getClassName().split("\\.");String arg = args[args.length - 1];// 如果参数是内部类类型,再截取下if (arg.contains("$")) {arg = arg.split("\\$")[arg.split("\\$").length - 1];}if (!diffParams[i].toLowerCase().contains(arg.toLowerCase())) {return Boolean.FALSE;}}// 只有个数和类型全匹配到才算匹配return Boolean.TRUE;}return Boolean.FALSE;}} else {return Boolean.FALSE;}}
}

3,构建项目

 从https://gitee.com/Dray/jacoco下载项目,修改KotlinInlineFilter.java 文件,如上面所示,执行打包命令:

mvn clean install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true

项目会下载很多依赖的包,耐心等待即可。最后命令执行完成后,发现出错了,如下所示:

图片

相应的jar包打包完成,没有问题。

4,测试修改后的包

    看项目的介绍,需要使用org.jacoco.cli-0.8.7-SNAPSHOT-nodeps.jar,修改成newjacococli.jar以便进行测试。

(1)生成正常的覆盖率报告

执行如下命令:

java -jar ./newjacococli.jar report ./coverage-4.1.100.66230-2023-08-11-17_48_31.ec --classfiles /Users/sxf/Downloads/jacocoincrease/build_classes_12354/61/appstoreRelease/com/yiqixie/kem/im/ui/messages/roomlist/RoomListFragment.class --sourcefiles /Users/sxf/Documents/精准测试/KimCode/kim-android/packages/kim-android/eek-features/module-kim/src/main/java/ --encoding utf-8 --html ./jacoco15

没有指定diff信息,可以生成指定类的全量覆盖率报告:

图片

(2)生成diff报告信息

通过其他的功能,拿到diff文件信息,格式如下所示:

[{"classFile":"com/yiqixie/kem/im/ui/messages/roomlist/RoomListFragment","methodInfos":[{"methodName":"updateFolderTabLayout","parameters":["folderList: List<ConversationFolderUIEntity>"]},{"methodName":"onCreate","parameters":["savedInstanceState: Bundle?"]}],"type":"MODIFY"}
]

将上面的json转换成String,执行如下命令生成增量报告:


java -jar ./newjacococli.jar report ./coverage-4.1.100.66230-2023-08-11-17_48_31.ec --classfiles /Users/sxf/Downloads/jacocoincrease/build_classes_12354/61/appstoreRelease/com/yiqixie/kem/im/ui/messages/roomlist/RoomListFragment.class 
--sourcefiles /Users/sxf/Documents/精准测试/KimCode/kim-android/packages/kim-android/eek-features/module-kim/src/main/java/ --encoding utf-8 --html ./jacoco16 
--diffCode "[{\"classFile\":\"com/yiqixie/kem/im/ui/messages/roomlist/RoomListFragment\",\"methodInfos\":[{\"methodName\":\"updateFolderTabLayout\",\"parameters\":[\"folderList: List<ConversationFolderUIEntity>\"]},{\"methodName\":\"onCreate\",\"parameters\":[\"savedInstanceState: Bundle?\"]}],\"type\":\"MODIFY\"}]"

生成的增量报告结果如下:

图片

渲染结果如下:

图片

通过上面的测试,可以达到想要的效果,现在就需要再修改一下Android agent先找到diff的信息,再去执行新的增量覆盖率的命令。

(3) 如果diff文件过多,则无法使用命令行方式

建议改成以json文件传递。

java -jar ./newjacococli.jar report ./packages/kim-android/app/build/outputs/code-coverage/connected/mergedcoverage.ec  
--classfiles ./build_classes_12397/62/appstoreRelease/com/yiqixie/kem/im/ui/messages/media/MediaFragment.class 
--classfiles ./build_classes_12397/62/appstoreRelease/com/yiqixie/kem/im/ui/messages/media/MediaLayout.class 
--classfiles ./build_classes_12397/62/appstoreRelease/com/yiqixie/kem/im/ui/messages/media/MediaSlideActivity.class 
--sourcefiles ./packages/kim-android/eek-features/module-kim/src/main/java 
--sourcefiles ./packages/kim-android/eek-features/module-kim/src/main/java 
--sourcefiles ./packages/kim-android/eek-features/module-kim/src/main/java 
--encoding utf-8 --html ./diffreportsxf 
--diffCodeFiles ./diff_files.json

5,最新的生成覆盖率报告流程

    通过借助于上面修改后的jacococli.jar包,最新生成增量报告的流程如下所示:

图片

新的生成增量报告的方案做到了如下几点:

  • 统一对比版本和对比分支生成增量报告的逻辑,最终生成jacoco格式的报告;

  • 补全了所有增量报告数据,分支,行,函数和类的覆盖率数据都是准确的;

  • 增量方案渲染代码页覆盖情况,方便测试同学根据增量函数进行排查问题。

  • 无需修改jacoco插件,不影响覆盖率数据的采集。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/731528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Yolov8-pose关键点检测:SPPF创新涨点篇 | SPPELAN:SPP创新结合ELAN ,效果优于SPP、SPPF| YOLOv9

💡💡💡本文独家改进:新颖SPPF创新涨点改进,SPP创新结合ELAN,来自于YOLOv9,助力YOLOv8-pose,将SPPELAN代替原始的SPPF SPPELAN结构图如下: Yolov8-Pose关键点检测专栏介绍:https://blog.csdn.net/m0_63774211/category_12398833.html ✨✨✨手把手教你从数据标…

计算机网络——23网络层导论

网络层导论 网络层服务 在发送主机和接收主机对之间传送段&#xff08;segment&#xff09;在发送端将段封装到数据报中在接收端&#xff0c;将段上交给传输层实体网络层协议存在于每一个主机和路由器路由器检查每一个经过它的IP数据报的头部 网络层的关键功能 网络层功能 …

C#,老鼠迷宫问题的回溯法求解(Rat in a Maze)算法与源代码

1 老鼠迷宫问题 迷宫中的老鼠&#xff0c;作为另一个可以使用回溯解决的示例问题。 迷宫以块的NN二进制矩阵给出&#xff0c;其中源块是最左上方的块&#xff0c;即迷宫[0][0]&#xff0c;目标块是最右下方的块&#xff0c;即迷宫[N-1][N-1]。老鼠从源头开始&#xff0c;必须…

MySQL 篇-快速了解事务、索引

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 事务概述 1.1 事务四大特性(ACID) 2.0 索引概述 2.1 关于 “索引一定要创建在主键上&#xff1f;” 的问题 2.2 索引操作语法 2.3 索引结构 1.0 事务概述 事务是…

JavaWeb实验 JSP内置对象

实验目的 掌握JSP内置对象的使用&#xff1b;内置对象包括out\request\response\cookie等。 实验内容 【1】创建一个JSP网页&#xff0c;文件名为index.jsp&#xff0c;利用request内置对象获取客户端访问ip的功能&#xff0c;屏蔽用户利用局域网实际ip访问Web&#xff0c;但…

技术选型思考:分库分表和分布式DB(TiDB/OceanBase) 的权衡与抉择

在当今数据爆炸的时代&#xff0c;数据库作为存储和管理数据的核心组件&#xff0c;其性能和扩展性成为了企业关注的重点。随着业务的发展和数据量的不断增长&#xff0c;传统的单库单表架构逐渐暴露出性能瓶颈和扩展性限制。为了应对这些挑战&#xff0c;企业常常需要在分库分…

15. C++泛型与符号重载

【泛型编程】 若多组类型不同的数据需要使用相同的代码处理&#xff0c;在C语言中需要编写多组代码分别处理&#xff0c;这样做显然太过繁琐&#xff0c;C增加了虚拟类型&#xff0c;使用虚拟类型可以实现一组代码处理多种类型的数据。 虚拟类型是暂时不确定的数据类型&#…

数据中台:数字中国战略关键技术设施

文章目录 每日一句正能量前言为何要建设数据中台数据中台建设痛点数据中台学习资料聚焦前沿&#xff0c;方法论体系更新与时俱进&#xff0c;紧跟时代热点深入6大行业&#xff0c;提炼实践精华大咖推荐&#xff0c;数字化转型必备案头书购买链接赠书活动 每日一句正能量 人生之…

找不到本地组策略编辑器解决办法

创建记事本写入以下命令 echo offpushd "%~dp0"dir /b %systemroot%\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >gp.txtdir /b %systemroot%\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientTools-…

Django会话

一、Cookie介绍 1.1、背景介绍 HTTP协议有一个特性就是无状态的,是指协议对于交互性场景没有记忆能力 随着动态交互的web应用的出现,HTTP的无状态特性严重阻碍了动态交互应用程序的发展,例如一些购物网站在进行购物时候都会进行了页面跳转/刷新,按照HTTP的无状态协议岂不…

Compose UI 之 MediumLarge TopAppBar

Medium&Large TopAppBar 前面文章介绍了 Small 类型的 TopAppBar&#xff1a;TopAppBar CenterAlignedTopAppBar 。下来介绍 Medium 和 Large 类型的 TopAppBar&#xff1a;MediumTopAppBar LargeTopAppBar 。 MediumTopAppBar 上面介绍了Small 类型的 TopAppBar (TopAp…

实战|环信 Vue2 uniapp Demo重构焕新!经典再升级!

项目背景 当前环信 uni-app vue2 Demo 地址升级版本 Github 地址&#xff08;临时&#xff09; 原版本功能实现方式较混乱&#xff0c;代码逻辑晦涩难懂&#xff0c;不利于开发者参考或复用。此实战项目在确保原项目功能保留的情况下进行完全重写并新增大量功能&#xff0c;以…

【C语言】指针相关知识点

什么是指针&#xff1f; 指针&#xff08;Pointer&#xff09;是编程语言中的一个对象&#xff0c;利用地址&#xff0c;它的值直接指向&#xff08;points to&#xff09;存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元&#xff0c;可以说&#xff0c;地…

Golang搭建grpc环境

简介 OS : Windows 11 Golang 版本: go1.22.0 grpc : 1.2 protobuffer: 1.28代理 没有代理国内环境下载不了库七牛CDN &#xff08;试过可用&#xff09; go env -w GOPROXYhttps://goproxy.cn,direct阿里云代理(运行grpc时下载包出现报错 ): go env -w GOPROXYhttps://mirr…

黑马点评-异步秒杀实现

异步秒杀思路 我们来回顾一下下单流程 当用户发起请求&#xff0c;此时会请求nginx&#xff0c;nginx会访问到tomcat&#xff0c;而tomcat中的程序&#xff0c;会进行串行操作&#xff0c;分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单 4、校验是…

Axure Cloud如何给每个原型配置私有域名

需求 在原型发布之后&#xff0c;自动给原型生成一个独立访问的域名&#xff0c;类似http://u591bi.axshare.bushrose.cn&#xff0c;应该如何配置呢&#xff1f; 准备事项 已备案域名 如何备案&#xff1f;阿里云备案流程 已安装部署Axure Cloud 如何安装部署&#xff0c;请…

WordPress建站入门教程:如何创建菜单和设置前端导航菜单?

前面我们跟大家分享了WordPress如何上传安装WordPress主题&#xff0c;但是启用主题后前端没有看到有导航菜单&#xff0c;这是因为我们还没有创建菜单和设置导航菜单。 JianYue主题导航菜单和右上角菜单 今天boke112百科就继续跟大家分享WordPress站点如何创建菜单和设置前端…

Kafka入门及生产者详解

1. Kafka定义 传统定义&#xff1a;分布式的、基于发布/订阅模式的消息队列&#xff0c;主要用于大数据实时处理领域。发布/订阅模式中&#xff0c;发布者不会直接将消息发送给特定的订阅者&#xff0c;而是将发布的消息分为不同的类别&#xff0c;订阅者只接受感兴趣的消息。…

Linux系统Docker部署DbGate并结合内网穿透实现公网管理本地数据库

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-66GkyG9g7oNq7tl8 {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

基于yolov5的草莓成长期检测系统,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的草莓成长期检测系统&#xff0c;支持图像检测&#xff0c;视频检测和实时摄像检测功能_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的草莓成长期检测系统是在pytorch框架下实现的&#xff0c;成长期分为3类&#xff1a;…