Lit and FileCheck

Lit And FileCheck

LitFileCheck 是 LLVM 测试中常用的工具, 尽管二者功能上是完全独立的,但是搭配起来使用会显得更加方便.

Lit

Lit总的来说仅仅是一个 test-launcher, 它的主要功能就是发现测试,执行测试,收集结果.

“发现测试”主要依赖于lit.cfg(或lit.site.cfg)文件标记来实现, 当一个目录下包含这个文件时,那么这个目录及其子目录都会被lit视作是搜索测试文件的目录.

“执行测试”则相对简单, 每个”lit测试文件”都应该是文本文件,这些文本文件中 RUN: 将被视作标记,这个标记之后的内容就是执行测试的shell指令,测试指令返回0则认为测试通过, 例如 RUN: echo jojo | grep jojo.

具体来说,RUN:对应的测试指令在执行之前还会被lit执行一次 substitute, 以更新待执行的指令, 常见的有%s被替换为所在文件的路径,%t被替换成和当前文件对应的临时文件路径(可以手动用%t0,%t1来对应多个路径)

从习惯上说, RUN:一般被写在源文件中, 所以常常会被comment起来, 避免和源码内容冲突, 下面的python例子描述了典型的RUN:指示用法.

# file.py

# RUN: python3 %s | grep Foo
print("Foo")

“收集结果”: 自然是一个test-launcher应该做的事.

FileCheck

FileCheck的功能像是增强的grep, 从功能上说, 它的作用是检查stdin内的文本是否符合用户设定的Pattern

FileCheck的Match Pattern是用文本文件来描述的, 基于诸如 CHECK: , CHECK-NOT:这样的Directive实现. Pattern可以相当复杂, 但是总的来说是按照先后顺序进行描述的. 例如,假如文件中有两行分别包含了CHECK: FOOCHECK: BAR, 那么stdin中就必须先出现”FOO”, 再出现”BAR”, 则FileCheck才能通过.

FileCheck的Directive也同样一般会写在源文件中. 此时, 这些Directive写在哪里并不重要, 重要的仅仅是先后顺序. 例如, 有的人习惯把所有的CHECK:写在文件开头, 而有的人则习惯把CHECK:写在刚好能打印这一内容的地方, 这只是风格的问题. 下面的python3 file1.py | FileCheck file1.pypython3 file2.py | FileCheck file2.py 并没有本质区别

# file1.py

# CHECK: Foo
print("Foo")
# CHECK: Bar
print("Bar")
# file2.py

#### FileCheck Directives #### 
# CHECK: Foo
# CHECK: Bar
#### FileCheck Directives ####

print("Foo")
print("Bar")

Combination

综上, Lit 和 FileCheck 搭配起来的简单例子如下, 该文件包含了三个lit测试.
1. 标准的FileCheck测试, 检测stdin中按顺序出现了Foo和Bar
2. 按自定的 FOO_ONLY_CHECK 进行检测, 只检测 stdin 出现了 Foo
3. 按自定的 BAR_ONLY_CHECK 进行检测, 只检测stdin出现了 Bar

# sample.py
# RUN: python3 %s | FileCheck %s
# RUN: python3 %s | FileCheck %s --check-prefix=FOO_ONLY_CHECK
# RUN: python3 %s | FileCheck %s --check-prefix=BAR_ONLY_CHECK

# CHECK: Foo
# FOO_ONLY_CHECK: Foo
print("Foo")

# CHECK: Bar
# BAR_ONLY_CHECK: Bar
print("Bar")

最后, 对于C++这样的编译语言, 可执行文件和源码文件是分开的, 由于Lit和FileCheck的Directive只能是文本, 所以一般使用类似于RUN: compile.sh && ${build_dir}/build/main | FileCheck %s的指令来描述测试. 由于 build_dir 和 src_dir 往往不同,这可能会涉及到一些文件查找路径的问题.

参考资料

  1. FileCheck的文档基本谈到了所有的用法.
  2. LIT的CLI Manual仅仅说了CLI的用法, 在CLI中调用lit时可以用上. LLVM Testing Infrastructure Guide则仅描述了LLVM内的测试方法,几乎没有什么用.
  3. 尽管 Lit 的 CLI 可以参考manual使用, 但如果要亲自基于lit进行测试, 最主要的问题是如何写lit.cfg, 而目前并没有公开的文档来说明lit.cfg的规则, 可以参考的主要是一篇博客using-llvm-lit-out-of-tree,以及官方的测试用例lit.cfglit.site.cfg. 简而言之,下面的lit.cfg,对于一个naive的python项目应该是足够了.
# lit.cfg
import lit.formats

config.name = "MyTest"
config.test_format = lit.formats.ShTest(True)
config.suffixes = ['.py']
  1. FileCheck 会随着 llvm 安装包分发,但也有第三方的Python实现. Lit则自身就是一个标准的Python项目,可以直接用pip安装