Реализация `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

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