Haskell 中的编译器集成测试
Compiler Integration Tests in Haskell
我打算用 Haskell 为一种非常简单的语言编写一个小玩具编译器,以加强我的 Haskell 技能并享受设计新语言的乐趣。我仍在考虑一些一般性的决定,最大的开放点之一是我将如何进行集成测试。我有以下要求:
- 应该可以创建一个三元组列表(输入、程序、输出),这样我的集成测试 运行 我的编译器编译程序,运行 编译的二进制文件,将输入传递给它并验证 运行 的输出是否等于给定的输出。
- 应该可以在以后扩展集成测试,以测试与程序的更复杂的交互,例如它更改文件或休眠至少 20 秒或类似的东西。
我还有以下可选要求:
- 它应该尽可能地是一个 "end to end",即它应该尽可能将整个编译器视为一个黑盒,并且它不应该访问编译器的内部或类似的东西。
- 所有代码都应该写成Haskell。
- 如果我能免费获得典型的测试框架功能就好了,即无需自己实现。例如。一条绿色 "SUCCESS" 消息或一组描述失败的错误消息。
我试图找到满足我需求的东西,但到目前为止我没有成功。我考虑的备选方案如下:
- shunit 会满足所有条件,除了我想在 Haskell.
中编写代码的条件
- QuickCheck 允许我在 Haskell 中编写所有内容,但据我了解,它似乎最适合仅涉及 Haskell 函数及其结果的测试。所以我需要在编译器中测试函数并放宽我的 "end to end" 要求。
- 我可以编写一个 Haskell 程序,在另一个进程中启动编译器,将输入程序传递给它,然后在另一个进程中启动编译代码,将它传递给 inpit 并检查输出。然而,这将涉及我这边的大量编码,以便实现使用测试框架时免费获得的所有功能。
我还不确定我应该选择哪个选项,我仍然希望我错过了一个好的解决方案。您是否知道如何创建满足我所有要求的集成测试?
我认为 unsafePerformIO
在这里应该很安全,因为程序永远不会与测试 environment/logic 所依赖的任何东西交互,或者换句话说,编译和执行应该是可见的作为在受控、隔离环境中进行测试的纯函数。我认为在这种情况下测试您的编译器确实很有用。 QuickCheck 应该成为黑盒测试的一个选项。但一些更清晰的头脑可能会证明我错了。
所以假设你的编译器是这样的:
type Source = String
compile :: Source -> Program
你的程序执行是
data Report = Report Output TimeTaken OtherStats ...
execute :: Program -> IO Report
您可以非常安全地使用 unsafePerformIO
将其转换为
execute' :: Program -> Report -- or perhaps 'executeUnsafe'
execute' = unsafePerformIO . execute
然后
compileAndExec :: Source -> Report
compileAndExec = compile . execute'
并将其与 QuickCheck 一起使用。
是否 execute
调用子进程、获取实际的二进制文件、执行它等等,或者在内存中解释二进制文件(或字节码),由您决定。
但我建议将字节代码和二进制生成分开:这样您就可以将编译器与 linker/whatnot 分开测试,这更容易,并且在此过程中还可以得到一个解释器。
我打算用 Haskell 为一种非常简单的语言编写一个小玩具编译器,以加强我的 Haskell 技能并享受设计新语言的乐趣。我仍在考虑一些一般性的决定,最大的开放点之一是我将如何进行集成测试。我有以下要求:
- 应该可以创建一个三元组列表(输入、程序、输出),这样我的集成测试 运行 我的编译器编译程序,运行 编译的二进制文件,将输入传递给它并验证 运行 的输出是否等于给定的输出。
- 应该可以在以后扩展集成测试,以测试与程序的更复杂的交互,例如它更改文件或休眠至少 20 秒或类似的东西。
我还有以下可选要求:
- 它应该尽可能地是一个 "end to end",即它应该尽可能将整个编译器视为一个黑盒,并且它不应该访问编译器的内部或类似的东西。
- 所有代码都应该写成Haskell。
- 如果我能免费获得典型的测试框架功能就好了,即无需自己实现。例如。一条绿色 "SUCCESS" 消息或一组描述失败的错误消息。
我试图找到满足我需求的东西,但到目前为止我没有成功。我考虑的备选方案如下:
- shunit 会满足所有条件,除了我想在 Haskell. 中编写代码的条件
- QuickCheck 允许我在 Haskell 中编写所有内容,但据我了解,它似乎最适合仅涉及 Haskell 函数及其结果的测试。所以我需要在编译器中测试函数并放宽我的 "end to end" 要求。
- 我可以编写一个 Haskell 程序,在另一个进程中启动编译器,将输入程序传递给它,然后在另一个进程中启动编译代码,将它传递给 inpit 并检查输出。然而,这将涉及我这边的大量编码,以便实现使用测试框架时免费获得的所有功能。
我还不确定我应该选择哪个选项,我仍然希望我错过了一个好的解决方案。您是否知道如何创建满足我所有要求的集成测试?
我认为 unsafePerformIO
在这里应该很安全,因为程序永远不会与测试 environment/logic 所依赖的任何东西交互,或者换句话说,编译和执行应该是可见的作为在受控、隔离环境中进行测试的纯函数。我认为在这种情况下测试您的编译器确实很有用。 QuickCheck 应该成为黑盒测试的一个选项。但一些更清晰的头脑可能会证明我错了。
所以假设你的编译器是这样的:
type Source = String
compile :: Source -> Program
你的程序执行是
data Report = Report Output TimeTaken OtherStats ...
execute :: Program -> IO Report
您可以非常安全地使用 unsafePerformIO
将其转换为
execute' :: Program -> Report -- or perhaps 'executeUnsafe'
execute' = unsafePerformIO . execute
然后
compileAndExec :: Source -> Report
compileAndExec = compile . execute'
并将其与 QuickCheck 一起使用。
是否 execute
调用子进程、获取实际的二进制文件、执行它等等,或者在内存中解释二进制文件(或字节码),由您决定。
但我建议将字节代码和二进制生成分开:这样您就可以将编译器与 linker/whatnot 分开测试,这更容易,并且在此过程中还可以得到一个解释器。