Lua 杂谈系列,就以代码覆盖率测试的 luacov 开头吧
简介
说到 lua 的覆盖率测试,我们一般都会想到用luacov做代码覆盖率测试
在干货|使用 luacov 统计 lua 代码覆盖率一文中,介绍了基本的 luacov 用法,但是缺少对 luacov 深入挖掘的相关内容。并且同时,原生的 luacov 提供了一套简洁的覆盖率测试实现以及报告输出形式,但是在实际许多场景中,采用原生 luacov 还是远远满足不了需求的
因此,本文旨在通过分析 luacov 的实现,帮助希望了解 lua 代码覆盖率测试或是使用、二次开发 luacov 的同学尽快上手
获取代码覆盖率数据
luacov 获取代码覆盖率数据,得益于 lua 自带的 debug 库。我们从 luacov 的主类 runner 中,可以一探究竟
1 | -- 初始化runner |
lua 的debug.sethook([thread], hook, mask)
函数可以使得我们的 lua 脚本在运行过程中,遇到特定的条件(mask)时执行相应的函数(hook)。当 mask 为'l'
时,表示 lua 脚本已经执行到了新的一行。因此,为了统计覆盖率,只需要在我们 hook'l'
事件的函数中,寻找执行的文件和行号就好了
hook 函数
在 luacov.runner 中,定义的 debug hook 为:
1 | runner.debug_hook = require(cluacov_ok and "cluacov.hook" or "luacov.hook").new(runner) |
因此我们可以以 luacov.hook 模块为例观察具体实现:
1 | function hook.new(runner) |
可以看到整一个 hook 中最有价值的部分还是local name = debug.getinfo(level, "S").source
。lua 原生的 debug.getinfo 相较于 c api 的性能差,因此建议实际需求使用中引入cluacov的 hook 模块作为 hook 函数
总结
lua 覆盖率信息的收集,总体无非如我们在 luacov 所看到的:在'l'
事件的 hook 函数中获取文件名与相应行数,然后保证每一个 lua 线程(协程)都能打上 hook。
luacov 实现总体而言也并不复杂,优化空间非常多,比如 save_stats()可以修改为 socket、websocket 一类实时传送数据,从而避免原生 luacov 设置 step 过小时导致报告文件 io 频繁,造成数据丢失。当然,有了网络传输,原配的很多参数都不需要了。
再深入一点,代码文件翻译成机器码,毕竟是状态机使然。如果细心观察 luacov 覆盖率的结果的话,会发现有很多该 hit 的行会 hit 不到。这些种种,就留待后续发掘啦~