在我的编译器中编写单元测试(生成IL)

时间:2012-03-05 05:20:06

标签: c# unit-testing compiler-construction compilation tiger

我正在Tiger编写C#编译器,我将把Tiger代码翻译成IL

在我的AST中实现每个节点的语义检查时,我为此创建了许多单元测试。这很简单,因为我的CheckSemantic方法如下所示:

public override void CheckSemantics(Scope scope, IList<Error> errors) {
...
}

所以,如果我想为某个节点的语义检查编写一些单元测试,我所要做的就是构建一个AST,然后调用该方法。然后我可以做类似的事情:

Assert.That(errors.Count == 0);

Assert.That(errors.Count == 1);
Assert.That(errors[0] is UnexpectedTypeError);
Assert.That(scope.ExistsType("some_declared_type"));

但是我现在正在开始代码生成,而且我不知道在为该阶段编写单元测试时可能是一个好的做法。

我正在使用ILGenerator课程。我考虑过以下几点:

  • 生成我要测试的示例程序的代码
  • 保存该可执行文件
  • 执行该文件,并将输出存储在文件
  • 断言该文件

但我想知道是否有更好的方法呢?

3 个答案:

答案 0 :(得分:14)

这正是我们在C#编译器团队中为测试我们的IL生成器所做的。

我们还通过ILDASM运行生成的可执行文件,并验证IL是否按预期生成,并通过PEVERIFY运行它以确保我们生成可验证的代码。 (当然,除非我们故意生成无法验证的代码。)

答案 1 :(得分:1)

我创建了一个post-compiler in C#,我使用这种方法来测试变异的CIL:

    {li> Save the assemblytemp file中,在我完成后将被删除。
  1. 使用PEVerifycheck the assembly;如果有问题,我将其复制到已知位置以进行进一步的错误分析。
  2. 测试装配内容。在我的情况下,我大多数loading the assembly dynamically在一个单独的AppDomain中(所以我可以将其拆除)并执行class in there(所以它就像一个自我检查程序集:here's a sample implementation)。
  3. 我还提供了一些关于如何在this answer中扩展集成测试的想法。

答案 2 :(得分:0)

您可以将测试视为做两件事:

  1. 告知您输出是否已更改
  2. 告知您输出是否不正确
  3. 确定某些内容是否发生变化通常比确定某些内容是否错误要快得多,因此,与错误检测测试相比,更频繁地运行变更检测测试是一种很好的策略。

    在您的情况下,如果您可以快速确定可执行文件未发生更改,则不需要每次运行编译器生成的可执行文件,因为生成了相同可执行文件的已知良好(或假设良好)副本。 / p>

    您通常需要对正在测试的输出进行少量操作以消除预期的差异(例如将嵌入日期设置为固定值),但是一旦完成,就会进行更改检测测试很容易写,因为验证基本上是文件比较:输出是否与上一次已知的良好输出相同?是的:通过,否:失败。

    因此,如果您发现运行编译器生成的可执行文件并检测这些程序输出的更改时出现性能问题,您可以选择运行通过比较可执行文件本身来检测更早阶段更改的测试。 / p>