9

我正在编写一个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类。我想过以下几点:

  1. 生成我要测试的示例程序的代码
  2. 将生成的代码另存为test.exe
  3. 执行text.exe并将输出存储在results
  4. 反对results

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

4

3 回答 3

14

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

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

于 2012-03-05T05:52:03.793 回答
1

在 C# 中创建了一个后编译器,并使用这种方法来测试变异的 CIL:

  1. 将程序集保存临时文件中,完成后将被删除。
  2. 使用PEVerify检查程序集;如果有问题,我会将其复制到已知位置以进行进一步的错误分析。
  3. 测试程序集内容。在我的情况下,我主要是在一个单独的 AppDomain 中动态加载程序集(这样我以后可以将其拆除)并在那里执行一个类(所以它就像一个自检程序集:这是一个示例实现)。

我还在这个答案中给出了一些关于如何扩展集成测试的想法。

于 2012-03-06T18:01:02.137 回答
0

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

  1. 让您知道输出是否已更改
  2. 让您知道输出是否不正确

确定某事是否已更改通常比确定某事是否不正确要快得多,因此与错误检测测试相比,更频繁地运行变更检测测试可能是一个好策略。

在您的情况下,如果您可以快速确定可执行文件自生成相同可执行文件的已知良好(或假定良好)副本以来没有更改,则无需每次都运行编译器生成的可执行文件。

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

所以重点是,如果您在运行编译器生成的可执行文件并检测到这些程序的输出变化时发现性能问题,您可以选择运行测试,通过比较可执行文件本身来检测更早阶段的变化。

于 2012-03-05T15:50:09.430 回答