Интеграционные тесты компилятора в 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, что проще, а также получить интерпретатор в процессе.