1. 背景

公司这边在代码覆盖率方面,依旧使用的传统手工方式生成,配置和操作比较麻烦,对业务同学也有些门槛。正好 Q4 不算特别忙了,抽出了一些精力来开发了这个代码染色平台,帮助业务同学一站式的解决权限申请、环境配置、报告保存、报告解读、信息汇总、增量分析等方向的问题。为什么叫代码染色平台呢,其实是看到了蚂蚁在 MTSC 上实时代码染色的视频和 PPT 资料,觉着很合适,便顺势当了搬运工,把这个词借了过来。开发过程中,借鉴了论坛中很多相关文章和资料,也和朋友进行过交流,有一次 [ID:zailushang] 还把他的同事拉群一起交流沟通,再次感谢😄。所以也把自己产出的一些东西,在社区和伙伴们分享一下,如果其中的思路或细节能帮到一些人,也是再好不过了。

2.做了哪些事

① 为了做成通用平台,以便跨线团队都能用,系统集成了公司内部的各个平台,如发布平台、CI 平台、统一登录、权限控制等。主要点在于 Jacoco Agent 安装与发布平台结合、代码获取与 CI 平台结合、报告生成与存储平台结合。
② 提供了实时获取 exec 并生成报告,exec 合并,报告存取等基础功能
③ 提供了团队视角的项目、应用、环境的管理,将覆盖率报告与之关系映射,方便灵活切换项目环境查看
④ 提供了基于 git diff 的简单增量功能、基于 AST 的关联分析的高级增量功能。
⑤ 对 Jacoco 原生报告的指标统计进行了筛选展示、中文展示。把 jacoco missed 思维的统计修改成 covered。取消掉了 jacoco 的面包屑,感觉不好用。在 jacoco 项目 index 页、包 index、类 index 页增加了带饼图的统计区域,便于在测试报告中进行汇总。
⑥ 提供指定包或类的排除和包含功能

3. 平台的概要流程

注:下方图 1 展示了系统的概要流程,图 2 为系统报告页的截图, 图 3 为报告改造后的细节。


4. Jacoco Report 的生成调用流示意

对 jacoco 报告生成流程,整理了一份类图形式的简单顺序示意,如有偏差欢迎指正。其中绿色实线表示调用关系,黑色虚线表示继承关系。图示如下:

图片也更登陆https://www.processon.com/view/link/5ffbe5747d9c080e58b94417 进行查看。

觉着不错帮点个赞,//每 5 个获赞能增加 processOn 一个免费文件

我们都知道,jacoco 报告的生成入口,是官方 ReportGenerator.java (https://www.jacoco.org/jacoco/trunk/doc/examples/java/ReportGenerator.java) 类中的 create 方法,然后通过 createReport 中的 visitBundle 调用 HTMLFormatter 类的 visitBundle() 来正式开始报告的处理。再后边的详细调用顺序,可以参考上图。对应调整的代码,会在下方部分详细说。

5. 源码改造的细节

5.1 右侧统计指标项

经过分析原始的 jacoco report 统计项,发现原生的 miss% 不容易被用户接受,大家预期的都是 covered%。另外根据调研,用户对圈复杂度和指令覆盖度的指标不是特别关注。因此这里只做了三件事:
A: 删除 Instructions 及 Cxty 的指标项 B: 将 miss% 修改为 covered% C: 将英文指标修改为中文,效果如下:

源码中改动的位置大致有两处,如下:
① 生成 table 列名方法,org.jacoco.report.html.HTMLFormatter.createTable()。删除了 addMissedTotalColumns() miss 列的添加,仅用 PercentageColumn 展示改为中文的列,不再使用 BarColumn

② 生成 table 数据方法,table 底部的统计项目从 missed of count 改为 covered of count

5.2 删除 jacoco 头部面包屑、Session 链接和底部标签

此处修改的目的在于,我自己在报告左侧做了树形的节点 Tree,点击后可进行页面导航。另外当在源码目录时,常常因为代码行数过多很难翻到面包屑处,进行页面跳转。底部标签内容中没有价值数据,删除后利于页面整洁。
源码中改动的位置大致有 3 处:
① 在 org.jacoco.report.internal.html.page.ReportPage.render() 中删除 attr("onload") 和 content() 外所有代码,如下:

private void body(final HTMLElement body) throws IOException {
   body.attr("onload", getOnload());   
   //final HTMLElement navigation = body.div(Styles.BREADCRUMB);   
   //navigation.attr("id", "breadcrumb");   
   //infoLinks(navigation.span(Styles.INFO));   
   //breadcrumb(navigation, folder);   
   //body.h1().text(getLinkLabel());   
   content(body);   
   //footer(body);
}

注:ReportPage 是除了源码页面外,所有页面的公用生成类,因此修改此类的 body 方法就可以影响到汇总页、包的类列表页、类的方法列表页等
② org.jacoco.report.html.HTMLFormatter.visitBundle() 中删除或注掉 createSessionsPage(page);
③org.jacoco.report.html.HTMLFormatter.visitEnd() 中删除或注掉 sessionsPage.render();

5.3 增加统计区域

这里整洁统计区域,是为了在测试完结,编辑测试报告时,能够方便美观的汇总项目级或包级、类级的覆盖率数据,并能明确的看到应用名称、环境信息、创建时间、统计对象等。然后就有了下图:

因为要修改汇总页、包页、类页,根据上面的源码分析,这个功能也应该加到 ReportPage 上。这里大致涉及到处变化,如下:
① 欲传递项目、应用、环境、唯一时间标识等信息到 ReportPage.body()。需要从生成报告的入口 create() 方法,就传入这些参数。并生成报告的调用链的方法上增加参数传递,可以参考第 4 部分的图示。
org.jacoco.report.html.HTMLFormatter.visitBundle():

org.jacoco.report.internal.html.page.BundlePage.render(),另外和 BundlePage 一样继承于 TablePage 的类的 render() 方法全都需要增加参数传递。

② 在 ReportPage 添加展示的区域 div,在 div 中添加展示的 span、div、及 graph-div 等。这里需要提及的是,需添加元素基本都是通过 HTMLElement.elementXX() 方式创建,创建的位置是由调用方 parent.elementXX 决定的,创建顺序需要特别注意,要处理完一个元素再去 new 下个区域,这个需要自己试验来体会。下方贴出部分关键代码:
ReportPage.body() 方法开始创建区域 div,并逐步填充里边的元素

ReportPage.createTitleArea() 方法创建文字相关的展示元素和计算展示数值

ReportPage.summary() 方法初始化图表的元素和布局


③ 页面引入 echarts js,为图表做数据初始化
ReportPage.render() 方法添加 highcharts js

ReportPage.drawGraph() 方法对 highcharts 继续设置和图表渲染

6. 后续计划

应客户需求,预计在 Q1 实现两个定制功能:
① 增加针对 Pom 依赖包的覆盖率并集统计
② 增加方法执行次数统计,与包、类、方法、行指标并列展示


↙↙↙阅读原文可查看相关链接,并与作者交流