Интеграционные тесты компилятора в Haskell
Я планирую написать маленький игрушечный компилятор в Haskell для очень простого языка, чтобы укрепить свои навыки Haskell и получать удовольствие от разработки нового языка. Я все еще думаю о некоторых общих решениях, и одним из самых больших открытых моментов является то, как я буду делать интеграционные тесты. У меня есть следующие требования:
- должно быть возможно создать список троек (вход, программа, выход), чтобы мой интеграционный тест запускал мой компилятор для компиляции программа, запускает скомпилированный двоичный файл, передает ему вход и проверяет, что выход запуска равен заданному выходу.
- должно быть возможно расширить тест integratiom на более позднем этапе, чтобы проверить более сложные взаимодействия с программой, например, что она изменяет файл или спит не менее 20 секунд или что-то в этом роде.
У меня также есть следующие дополнительные требования:
- это должно быть столько же, сколько "конец в конец", насколько это возможно, т. е. он должен рассматривать wholencompiler как черный ящик как можно больше, и он не должен иметь доступа к внутренним частям компилятора или что-то в этом роде.
- весь код должен быть написан на Haskell.
- было бы неплохо, если бы я получил типичную функциональность платформы тестирования бесплатно, т. е. без ее реализации. Е. Г. зеленый "успех" сообщение или набор сообщений об ошибках, характеризующих отказы.
Я пробовал найти что-то, что удовлетворит мои потребности, но пока мне это не удалось. Альтернативы, которые я рассматривал, следующие:
- shunit удовлетворит всем, кроме условия, что я хотел бы написать код в Haskell.
- QuickCheck позволит мне написать все в Haskell, но, как я понимаю, он, по-видимому, в основном подходит для тестов, которые включают только функцию Haskell и ее результат. Поэтому мне нужно будет протестировать функции в компиляторе и расслабьте мое требование "конец в конец".
- я мог бы просто написать программу Haskell, которая запускает компилятор в другом процессе, передает ему входную программу, а затем запускает скомпилированный код в другом процессе, передает его inpit и проверяет вывод. Однако это потребует много кодирования на моей стороне в ordee для реализации всех функций, которые можно получить бесплатно при использовании платформы тестирования.
Я еще не знаю, какой вариант я должен выбрать и я все еще надеюсь что мне не хватает хорошего решения. У вас есть идеи о том, как я могу создать интеграционный тест, который удовлетворяет всем моим требованиям?
1 ответов
Я думаю unsafePerformIO
должно быть вполне безопасно здесь, учитывая, что программы никогда не должны взаимодействовать с чем-либо, от чего зависит среда тестирования/логика, или, другими словами, компиляция и выполнение должны рассматриваться как чистая функция в контексте тестирования в контролируемой, изолированной среде. И я считаю, что действительно полезно протестировать ваш компилятор в таких условиях. И QuickCheck должен стать опцией даже для тестирования blackbox. Но некоторые ясные умы могут доказать, что я ошибочно принимать.
Итак, предполагая, что ваш компилятор smth как:
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
вызывает подпроцесс, получает фактический двоичный файл, выполняет это и т. д. или интерпретирует двоичный (или байт - код) в памяти, зависит от вас.
но я бы рекомендовал разделить байтовый код и двоичную генерацию: таким образом, вы можете протестировать компилятор отдельно от компоновщика/whatnot, что проще, а также получить интерпретатор в процессе.