Реализация `make check` или` make test`
Как я могу реализовать простую структуру регрессионных тестов с помощью Make? (Я использую GNU Make, если это имеет значение.)
Мой текущий make файл выглядит примерно так (отредактирован для простоты):
OBJS = jscheme.o utility.o model.o read.o eval.o print.o
%.o : %.c jscheme.h
gcc -c -o [email protected] $<
jscheme : $(OBJS)
gcc -o [email protected] $(OBJS)
.PHONY : clean
clean :
-rm -f jscheme $(OBJS)
Id нравится иметь набор регрессионных тестов, например, expr.in
тестирование "хорошего" выражения и unrecognized.in
тестирование "плохого", причем expr.cmp
и unrecognized.cmp
являются ожидаемым результатом для каждого. Ручное тестирование будет выглядеть следующим образом:
$ jscheme < expr.in > expr.out 2>&1
$ jscheme < unrecognized.in > unrecognized.out 2>&1
$ diff -q expr.out expr.cmp # identical
$ diff -q unrecognized.out unrecognized.cmp
Files unrecognized.out and unrecognized.cmp differ
Я думал добавить набор правил в make файл, выглядящий примерно так:
TESTS = expr.test unrecognized.test
.PHONY test $(TESTS)
test : $(TESTS)
%.test : jscheme %.in %.cmp
jscheme < [something.in] > [something.out] 2>&1
diff -q [something.out] [something.cmp]
Мои вопросы:
• Что я помещаю в [что-то] заполнители?
• Есть ли способ заменить сообщение из diff
сообщением "Test expr
failed"?
Ответы
Ответ 1
Ваш оригинальный подход, как указано в вопросе, является лучшим. Каждый из ваших тестов представлен в виде пары ожидаемых входов и выходов. Make вполне способен выполнять итерации и запускать тесты; нет необходимости использовать оболочку for
цикла. Фактически, делая это, вы теряете возможность выполнять свои тесты параллельно и создаете для себя дополнительную работу для очистки временных файлов (которые не нужны).
Вот решение (на примере bc):
SHELL := /bin/bash
all-tests := $(addsuffix .test, $(basename $(wildcard *.test-in)))
.PHONY : test all %.test
BC := /usr/bin/bc
test : $(all-tests)
%.test : %.test-in %.test-cmp $(BC)
@$(BC) <$< 2>&1 | diff -q $(word 2, $?) - >/dev/null || \
(echo "Test [email protected] failed" && exit 1)
all : test
@echo "Success, all tests passed."
Решение напрямую решает ваши оригинальные вопросы:
- Заполнители, которые вы ищете, это
$<
и $(word 2, $?)
Соответствующие предварительным условиям %.test-in
и %.test-cmp
соответственно. В отличие от комментариев @reinierpost временные файлы не нужны. - Сообщение diff скрыто и заменено с помощью
echo
. - Makefile должен вызываться с
make -k
для запуска всех тестов независимо от того, был ли отдельный тест провален или успешен. -
make -k all
будет работать только в том случае, если все тесты пройдут успешно.
Мы избегаем перечисления каждого теста вручную при определении переменной all-tests
путем использования соглашения об именовании файлов (*.test-in
) и функций GNU make для имен файлов. В качестве бонуса это означает, что решение масштабируется до десятков тысяч тестов из коробки, так как длина переменных в GNU make не ограничена. Это лучше, чем решение на основе оболочки, которое упадет, когда вы достигнете предела командной строки операционной системы.
Ответ 2
Сделайте тестовый бегун script, который берет имя теста и вводит имя входного файла, выводит имя файла и извлекает данные из этого:
#!/bin/bash
set -e
jscheme < $1.in > $1.out 2>&1
diff -q $1.out $1.cmp
Затем в Makefile
:
TESTS := expr unrecognised
.PHONY: test
test:
for test in $(TESTS); do bash test-runner.sh $$test || exit 1; done
Вы также можете попробовать реализовать что-то вроде automake
простая тестовая среда.
Ответ 3
В результате я выгляжу так:
TESTS = whitespace list boolean character \
literal fixnum string symbol quote
.PHONY: clean test
test: $(JSCHEME)
for t in $(TESTS); do \
$(JSCHEME) < test/$$t.ss > test/$$t.out 2>&1; \
diff test/$$t.out test/$$t.cmp > /dev/null || \
echo Test $$t failed >&2; \
done
Его основали на идее Джека Келлиса, включая совет Джонатана Леффлера.
Ответ 4
Я рассмотрю только ваш вопрос о diff. Вы можете сделать:
diff file1 file2 > /dev/null || echo Test blah blah failed >&2
хотя вы можете использовать cmp вместо diff.
В другой заметке вам может показаться полезным продолжить
окунуться и использовать automake. Ваш Makefile.am(полностью)
будет выглядеть следующим образом:
bin_PROGRAMS = jscheme
jscheme_SOURCES = jscheme.c utility.c model.c read.c eval.c print.c jscheme.h
TESTS = test-script
и вы получите множество действительно хороших целей бесплатно, включая довольно полнофункциональную тестовую среду.