Написание модульных тестов в моем компиляторе (который генерирует IL)

Я пишу компилятор Tiger в C# и собираюсь перевести код Tiger в IL.

Реализуя семантическую проверку каждого узла в моем AST, я создал для этого множество модульных тестов. Это довольно просто, потому что мой метод CheckSemantic выглядит так:

public override void CheckSemantics(Scope scope, IList<Error> errors) {
...
}

поэтому, если я хочу написать модульный тест для семантической проверки некоторого узла, все, что мне нужно сделать, это создать AST и вызвать этот метод. Тогда я могу сделать что-то вроде:

Assert.That(errors.Count == 0);

or

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

но мне интересно, есть ли лучший способ сделать это?


person Oscar Mederos    schedule 05.03.2012    source источник


Ответы (3)


Это именно то, что мы делаем в команде компилятора C#, чтобы протестировать наш генератор IL.

Мы также запускаем сгенерированный исполняемый файл через ILDASM и проверяем, что IL создается должным образом, и запускаем его через PEVERIFY, чтобы убедиться, что мы генерируем проверяемый код. (За исключением, конечно, тех случаев, когда мы намеренно генерируем непроверяемый код.)

person Eric Lippert    schedule 05.03.2012
comment
Это хорошо знать. Я тогда так и сделаю. Я просто немного беспокоился о производительности множества запущенных тестов, а также о создании, чтении и удалении файлов на диске. - person Oscar Mederos; 05.03.2012
comment
@OscarMederos: Если производительность недостаточно высока, то либо (1) выполните профилирование, чтобы выяснить, что работает медленно, и исправьте это, если сможете, либо (2) измените свою стратегию тестирования, чтобы некоторые тесты выполнялись при каждой регистрации, некоторые тесты выполнялись на ночь, а некоторые работают в выходные дни. Таким образом, вы получаете хороший баланс между быстрым обнаружением проблем и тщательным тестированием. - person Eric Lippert; 05.03.2012

Я создал пост-компилятор на C#. и я использовал этот подход для тестирования измененного CIL:

  1. Сохраните сборку во временном файле, это будет удалено после того, как я закончу с этим.
  2. Используйте PEVerify, чтобы проверьте сборку ; если есть проблема, я копирую ее в известное место для дальнейшего анализа ошибок.
  3. Протестируйте содержимое сборки. В моем случае я в основном динамическая загрузка сборки в отдельный домен приложения (чтобы я мог удалить ее позже) и выполнение здесь (похоже на самопроверяющуюся сборку: вот пример реализации).

Я также дал несколько идей о том, как масштабировать интеграционные тесты в этот ответ.

person Jordão    schedule 06.03.2012

Вы можете думать о тестировании как о выполнении двух вещей:

  1. сообщая, изменился ли вывод
  2. сообщая вам, если вывод неверен

Определение того, изменилось ли что-то, часто происходит значительно быстрее, чем определение того, что что-то неверно, поэтому может быть хорошей стратегией чаще запускать тесты для обнаружения изменений, чем тесты для обнаружения неправильности.

В вашем случае вам не нужно запускать исполняемые файлы, созданные вашим компилятором, каждый раз, если вы можете быстро определить, что исполняемый файл не изменился с тех пор, как была создана известная исправная (или предполагаемая исправная) копия того же исполняемого файла.

Обычно вам нужно выполнить небольшое количество манипуляций с тестируемым выходом, чтобы устранить ожидаемые различия (например, установить для встроенных дат фиксированное значение), но как только вы это сделаете, тесты для обнаружения изменений будут легко выполняться. напишите, потому что проверка - это в основном сравнение файлов: результат такой же, как последний известный хороший вывод? Да: Пройдено, Нет: Не пройдено.

Итак, дело в том, что если вы видите проблемы с производительностью при запуске исполняемых файлов, созданных вашим компилятором, и обнаружении изменений в выводе этих программ, вы можете запустить тесты, которые обнаруживают изменения на этапе раньше, сравнивая сами исполняемые файлы.

person Ajs    schedule 05.03.2012