13

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

显然,使用 Brainfuck,指令集很小,但我不禁认为,随着更多指令的添加,您的测试代码会成倍增长。无论如何,比您的典型测试更重要。

现在,在编写编译器和解释器方面,我几乎是新手,所以我的假设很可能是错误的。

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

4

6 回答 6

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是一个足够的覆盖工具。

祝你的口译员好运!如果您想查看精心制作但文档不完善的测试基础架构,请查看Quick C-compilertest2的目录。

于 2009-03-28T00:55:38.437 回答
2

我不认为测试编译器有什么特别之处。从某种意义上说,它几乎比测试某些程序更容易,因为编译器有这样一个基本的高级摘要——你提交源代码,它会给你返回(可能)编译的代码和(可能)一组诊断消息。

像任何复杂的软件实体一样,会有许多代码路径,但由于它们都是面向数据的(文本输入、文本和字节输出),因此编写测试很简单。

于 2009-03-28T00:12:17.497 回答
2

我写了一篇关于编译器测试的文章,其最初的结论(为了发表而稍微淡化了)是: 重新发明轮子在道德上是错误的。 除非您已经了解所有现有解决方案并且有充分的理由忽略它们,否则您应该从查看已经存在的工具开始。最容易开始的地方是Gnu C Torture,但请记住,它基于 Deja Gnu,容我们说,它有问题。(我花了六次尝试才让维护人员允许将有关 Hello World 示例 的严重错误报告放到邮件列表中。)

我会不谦虚地建议您将以下内容作为工具进行调查的起点:

  1. 软件:实践与经验2007 年 4 月。(付费软件,不向公众提供——http://pobox.com/~flash/Practical_Testing_of_C99.pdf 上的免费预印本

  2. http://en.wikipedia.org/wiki/Compiler_correctness#Testing(大部分由我编写。)

  3. 编译器测试参考书目(请让我知道我错过的任何更新。)

于 2009-04-09T18:54:56.620 回答
1

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

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

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

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

4:输出功能是否正确

5:输入功能是否正确

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

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

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

于 2009-03-28T00:21:25.960 回答
-1

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

于 2009-03-28T00:11:48.403 回答
-3

秘诀是:

  • 分离关注点
  • 遵守得墨忒耳定律
  • 注入你的依赖

好吧,难以测试的软件表明开发人员像 1985 年那样编写它。很抱歉,但是利用我在这里介绍的三个原则,即使是行号 BASIC 也是可单元测试的(可以将依赖项注入BASIC,因为你可以做“goto variable”。

于 2010-07-08T05:52:46.497 回答