2012-03-05 77 views
9

我在C#中编写了Tiger编译器,我打算将Tiger代码翻译为IL在我的编译器中编写单元测试(生成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类。我想到了以下几点:

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

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

回答

14

这正是我们在C#编译器团队上测试我们的IL生成器的过程。

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

+0

很高兴知道这一点。那么我会这样做。我只是担心很多正在运行的测试的性能,以及在磁盘上创建,读取和删除文件。 – 2012-03-05 06:16:09

+3

@OscarMederos:如果性能不够好,那么要么(1)分析找出什么是缓慢的,如果可以的话修复它,或者(2)改变你的测试策略,以便在每次签入时运行一些测试,一夜之间跑步,有些跑周末。这样,您就可以快速发现问题并进行彻底的测试。 – 2012-03-05 14:51:02

0

你可以把测试作为做两件事情:

  1. 让你知道,如果输出已经改变
  2. 让你知道如果输出是不正确

确定如果事情已经改变往往大大高于确定的东西是不正确,所以可以比更频繁地运行变化检测测试一个很好的策略不正确检测试验。

在你的情况下,如果你可以快速确定可执行程序没有改变,那么你不需要运行编译器生成的可执行文件,因为已经生成了相同可执行文件的已知好的(或假定的)副本。

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

所以问题是,如果您发现运行由编译器生成的可执行文件的性能问题,并且检测到这些程序输出的更改,则可以选择通过比较可执行文件本身来运行早期检测更改的测试。

1

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

  1. Save the assemblytemp file,我用它做之后将被删除。
  2. 使用PEVerifycheck the assembly;如果出现问题,我将其复制到已知位置以进一步进行错误分析。
  3. 测试装配内容。以我为例,我在一个单独的AppDomain(所以我以后可以撕裂下来)大多loading the assembly dynamically和行使class in there(因此它就像一个自检组件:here's a sample implementation)。

我也给出了一些关于如何在this answer中扩展集成测试的想法。