如何测试解释器或编译器?

时间:2009-03-27 23:59:02

标签: testing compiler-construction interpreter brainfuck

我一直在尝试为Brainfuck创建一个解释器,虽然很容易制作和启动并运行,但我的一部分希望能够针对它运行测试。我似乎无法理解可能需要编写多少测试来测试所有可能的指令组合以确保实现正确。

显然,使用Brainfuck,指令集很小,但我不禁想到,随着更多指令的添加,您的测试代码将呈指数级增长。比任何速度的典型测试都要多。

现在,我就像编写编译器和解释器一样可以获得新手,所以我的假设很可能远离基础。

基本上,你甚至从哪里开始测试这样的东西?

6 个答案:

答案 0 :(得分:11)

测试编译器与测试其他类型的应用程序略有不同,因为编译器可以生成程序的不同汇编代码版本,只要它们都做正确的事情。但是,如果您只是测试一个解释器,它与任何其他基于文本的应用程序几乎相同。这是一个以Unix为中心的观点:

  1. 您需要构建回归测试套件。每个测试都应该有
    • 您要解释的源代码,比如test001.bf
    • 您要解释的程序的标准输入,例如test001.0
    • 您期望口译员在标准输出上产生什么,比如test001.1
    • 您希望解释器在标准错误上产生什么,比如test001.2(您关心标准错误,因为您想测试解释器的错误消息)
  2. 您需要一个“运行测试”脚本,其执行类似以下内容的操作

    function fail {
      echo "Unexpected differences on $1:"
      diff $2 $3
      exit 1
    }
    
    for testname
    do
      tmp1=$(tempfile)
      tmp2=$(tempfile)
      brainfuck $testname.bf < $testname.0 > $tmp1 2> $tmp2
      [ cmp -s $testname.1 $tmp1 ] || fail "stdout" $testname.1 $tmp1
      [ cmp -s $testname.2 $tmp2 ] || fail "stderr" $testname.2 $tmp2
    done
    
  3. 你会发现有一个像“

    ”这样的“创建测试”脚本很有帮助
    brainfuck $testname.bf < $testname.0 > $testname.1 2> $testname.2
    

    只有当你完全相信翻译工作适合那种情况时,你才会这样做。

  4. 您将测试套件置于源代码管理之下。

  5. 修饰测试脚本很方便,因此可以省略预期为空的文件。

  6. 任何时候发生任何变化,都会重新运行所有测试。你可能也会通过cron工作整夜重新运行它们。

  7. 最后,您希望添加足够的测试以获得编译器源代码的良好测试覆盖率。覆盖工具的质量差异很大,但GNU Gcov是一个足够的覆盖工具。

  8. 祝你的口译员好运!如果您想看到一个精心设计但没有很好记录的测试基础架构,请查看Quick C-- compilertest2目录。

答案 1 :(得分:2)

我认为测试编译器没有任何'特殊';从某种意义上说,它比测试某些程序更容易,因为编译器有这样一个基本的高级摘要 - 你交给源代码,它会返回(可能)编译代码和(可能)一组诊断消息。

与任何复杂的软件实体一样,会有很多代码路径,但由于它都是面向数据的(文本输入,文本和字节输出),因此可以直接进行测试。

答案 2 :(得分:2)

我写了一篇关于编译器测试的文章,其最初的结论(略微降低了发布)是:重新发明轮子在道德上是错误的。除非你已经知道所有关于预先存在的解决方案并有一个很好的理由忽略它们,你应该从查看已经存在的工具开始。最容易开始的地方是Gnu C Torture,但要记住它是基于Deja Gnu的,我们应该说,它有问题。 (我甚至花了六次尝试让维护者在邮件列表上允许{Helloer示例critical bug report。)

我会毫不客气地建议您将以下内容作为调查工具的起始位置:

  1. Software: Practice and Experience April 2007。 (Payware,公众无法使用--- http://pobox.com/~flash/Practical_Testing_of_C99.pdf免费预印。

  2. http://en.wikipedia.org/wiki/Compiler_correctness#Testing(主要由我撰写。)

  3. Compiler testing bibliography(请告诉我我错过的任何更新。)

答案 3 :(得分:1)

在brainfuck的情况下,我认为应该使用brainfuck脚本进行测试。不过我会测试以下内容:

1:是否所有单元都初始化为0

2:当数据指针当前指向第一个单元格时递减数据指针会发生什么?它包裹吗?它是否指向无效的内存?

3:当数据指针指向最后一个单元格时增加数据指针会发生什么?它包裹吗?它是否指向无效内存

4:输出功能是否正确

5:输入功能是否正确

6:[]的东西是否正常工作

7:当你将一个字节递增超过255次,它是否正确地换行为0,或者它被错误地视为整数或其他值时会发生什么。

也可以进行更多测试,但这可能是我开始的地方。几年前我写了一个BF编译器,还有一些额外的测试。特别是我通过在块中包含大量代码来大量测试[]内容,因为我的代码生成器的早期版本存在问题(在x86上使用jxx我遇到问题,当块生成超过128个字节左右时代码,导致x86 asm无效。

答案 4 :(得分:-1)

您可以使用一些已编写的应用进行测试。

答案 5 :(得分:-3)

秘诀是:

  • 分开关注
  • 遵守得墨忒耳法则
  • 注入您的依赖项

嗯,很难测试的软件是开发人员像1985年那样编写它的标志。很抱歉这样说,但是利用我在这里介绍的三个原则,甚至行编号BASIC也可以进行单元测试(可以将依赖注入BASIC,因为你可以做“转变量”。