关于测试用例缩减的一切:你不知道但应该问的问题
想象一下,在几乎无需人工干预的情况下,减少测试软件所需的代码量和时间,同时提高测试效果并使调试任务更容易。这听起来好得令人难以置信,但我们将解释测试用例缩减如何实现所有这些(甚至更多)。
了解缩减的工作原理有助于故障排除,并更容易找出高效的工作流程和优化测试的最佳工具。我们将解释为什么测试用例缩减是安全工程师需要理解的一个特别重要的主题,并看看DeepState的最先进缩减器。
测试用例缩减对人类的意义
测试用例缩减最常见的目的是将一个复杂的失败测试用例(针对已确认的bug)转换为更容易理解的版本,以便于调试。但您可能还想忽略低优先级的bug!拥有这些bug的简化版本有助于快速识别未来相同不重要问题的重复提交。对于未确认的bug,缩减测试用例可能更为关键,因为在简化之前,您通常无法判断一个bug是否是重复的(甚至是否是一个bug)。您可能会发现问题出在您的规范、测试生成工具或操作系统上。
如果没有一个可以轻松理解并简洁描述的缩减测试用例,很难说您有一个bug。您可能有证据表明软件系统行为异常,但当测试用例足够复杂时,“一个bug”可能是一个可疑的概念。一个复杂的测试用例几乎类似于一个数学证明,只显示具有某种属性的整数必须存在;如果您想做一些需要实际数字的事情,您的证明需要是建设性的。
假设您有一个非常大的、随机生成的HTML文件,会导致Chrome崩溃。您可能发现了一个重要的bug;或者您只是做了一些导致Chrome以预期方式耗尽内存的事情。如果您唯一的结论是“当我在Chrome中加载这个巨大文件时,它停止运行”,您实际上并不了解多少。在这种情况下,您可能想应用测试用例缩减器,而不是花时间查看核心文件和附加调试器。如果它将文件缩减到单页HTML,或者更好的是,缩减到像单个SELECT标签这样小的东西,您就有了理解发生了什么的捷径。
测试用例缩减在网络安全中有直接应用,特别是当模糊器用于从随机输入生成的崩溃中构建漏洞利用时。输入越简单,构建漏洞利用或意识到bug无法被利用就越容易。AFL和libFuzzer提供内置的有限测试用例缩减,但有时您需要更多。现代测试用例缩减工具可以简化这种分析,如果您想生成复杂的API调用序列以在TLS、SQLite或LevelDB等库中找到漏洞,它们可能是必不可少的。这个概念也扩展到模糊测试智能合约,这就是为什么Trail of Bits的智能合约模糊器Echidna包含一个测试用例缩减器。
测试用例缩减对机器的意义
测试用例缩减不仅仅使测试更容易人类阅读;它对核心网络安全任务如模糊测试、符号执行和bug分类也有用。缩减器可以:
- 减少执行时间。这在为大型复杂程序运行巨大回归套件时很重要。用单个SELECT标签测试Chrome比用多GB文件测试更高效。在慢速执行环境(如Android模拟器)中尤其有帮助。
- 提高基于变异的模糊器的性能。当模糊器使用现有测试生成新测试用例时,如果测试用例不包含大量不相关的垃圾,它们有更高的机会探索有趣的路径。这就是为什么AFL和libFuzzer喜欢只对每个覆盖元素模糊最小和运行最快的输入。
- 更轻松地解决符号执行约束。如果涉及较少无趣的执行,生成的约束更容易解决。更多细节可以在Zhang等人的论文中找到。
- 避免不稳定测试。有时通过、有时失败的测试,在不更改测试代码的情况下,被称为“不稳定测试”,它们是谷歌规模测试软件中最关键的问题之一。测试用例缩减是最近提出的自动修复一些不稳定测试算法的核心部分。
- 去重模糊测试bug。当测试用例被缩减时,自动“模糊器驯服”——在模糊器产生的大量大多重复的测试用例中找到实际bug集——更有效。如果移除不相关的部分,相同bug的测试用例更相似。
- 提高故障定位工具性能。故障定位的一个核心问题是失败测试用例执行大量非故障代码。当失败的测试运行较少的非bug代码时,找出坏代码隐藏的位置更容易。缩减测试用例减少了执行的