2011-02-07 16 views
16

Jak mogę zaimplementować proste ramy testowe regresji za pomocą Make? (Używam GNU make, jeśli to ma znaczenie).Implementacja `make check` lub` make test`

Mój obecny makefile wygląda mniej więcej tak (wydanej dla uproszczenia):

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) 

chciałbym mieć zestaw testów regresji, na przykład, expr.in testowania „dobre” wyraz & unrecognized.in Testowanie „zły” jeden z expr.cmp & unrecognized.cmp jest oczekiwany wynik dla każdego. Testy instrukcja będzie wyglądać następująco:

$ 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 

myślałem, aby dodać zestaw reguł do makefile szuka coś takiego:

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] 

Moje pytania:
• Co mogę umieścić w [ coś] placeholders?
• Czy istnieje sposób na zastąpienie wiadomości od diff z komunikatem "Test expr nie powiodło się"?

+0

Skąd pochodzą wszystkie pliki tymczasowe? Co jest nie tak z – reinierpost

+0

@reinierpost: Jeśli masz lepszy sposób na dokonywanie porównań, to z całą pewnością opublikuj odpowiedź, która je zawiera - to jest dokładnie taka pomoc, o jaką pytam. –

+0

Przepraszam, myślałem, że anulowałem to pytanie. Potrzebujesz plików tymczasowych. – reinierpost

Odpowiedz

8

Twoje oryginalne podejście, jak określono w pytaniu, jest najlepsze. Każdy z twoich testów ma formę oczekiwanych wejść i wyjść. Make jest całkiem zdolny do iteracji i przeprowadzania testów; nie ma potrzeby korzystania z pętli powłoki for. W rzeczywistości, tracąc możliwość równoległego uruchamiania testów, tworzysz dodatkową pracę dla siebie, aby posprzątać pliki tymczasowe (które nie są potrzebne).

Oto rozwiązanie (stosując bc jako przykład):

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." 

Rozwiązanie bezpośrednio kieruje swoje oryginalne pytania:

  • Symbole zastępcze których szukasz są $< i $(word 2, $?) odpowiednie do wymagania wstępne: odpowiednio: %.test-in i %.test-cmp. W przeciwieństwie do plików tymczasowych komentarzy @reinierpost nie są potrzebne.
  • Wiadomość różnicowa jest ukryta i zastąpiona przy użyciu echo.
  • Plik makefile należy wywołać za pomocą make -k, aby uruchomić wszystkie testy, niezależnie od tego, czy pojedynczy test zakończy się niepowodzeniem, czy się uda.
  • make -k all będzie działać, tylko jeśli wszystkie testy zakończą się pomyślnie.

Unikamy wyliczanie każdy test ręcznie przy definiowaniu zmiennej all-tests wykorzystując konwencję nazewnictwa plików (*.test-in) i GNU make functions for file names. Jako bonus oznacza to, że rozwiązanie skaluje się do dziesiątek tysięcy testów po wyjęciu z pudełka, ponieważ długość zmiennych to unlimited w GNU make. Jest to lepsze rozwiązanie, niż rozwiązanie oparte na powłoce, które zostanie upuszczone po uruchomieniu systemu operacyjnego command line limit.

+0

Czy możesz dołączyć link do odpowiedniego GNU Dokonaj ręcznego wpisu na <[https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html] (https: //www.gnu .org/software/make/manual/html_node/File-Name-Functions.html)>? –

+0

Z przyjemnością - gotowe! –

+1

Dzięki, nauczyłem się kilku rzeczy, wyciągając podręcznik GNU make i pracując nad tym przykładem! – dpritch

9

Wykonaj skrypt testowy biegacz, która przyjmuje nazwę testu i wywodzi nazwę pliku wejściowego, wyjściowego pliku i dane smaple od tego:

#!/bin/bash 
set -e 
jscheme < $1.in > $1.out 2>&1 
diff -q $1.out $1.cmp 

Następnie w Makefile:

TESTS := expr unrecognised 

.PHONY: test 
test: 
    for test in $(TESTS); do bash test-runner.sh $$test || exit 1; done 

Mogłabyś spróbuj też wykonać coś takiego, jak automake 's simple test framework.

+1

Podoba mi się pomysł na pętlę powłoki na mój pierwotny plan oddzielnych celów. Skrypt powłoki nie jest złym pomysłem, ale myślę, że włączę całość do pliku Makefile. –

+1

Poza tym nie zamierzam otwierać całej puszki autotools robaków. –

+1

@jcsalomon: Nie chciałem używać autotools, ale raczej badać zachowanie systemu testowego. Zostawię to tutaj na wypadek, gdybyś zmieniła zdanie: http://www.lrde.epita.fr/~adl/autotools.html. Pamiętaj o unikaniu znaków '$' w twoim 'Makefile'. Kilka razy mnie złapało. –

2

Podam tylko twoje pytanie dotyczące diff. Możesz zrobić:

 
diff file1 file2 > /dev/null || echo Test blah blah failed >&2 

chociaż możesz chcieć użyć cmp zamiast diff.

Inna sprawa może okazać się pomocna, aby wykonać krok po kroku i zająć się automake. Twój Makefile.am (w całości) będzie wyglądać następująco:

 
bin_PROGRAMS = jscheme 
jscheme_SOURCES = jscheme.c utility.c model.c read.c eval.c print.c jscheme.h 
TESTS = test-script 

a dostaniesz mnóstwo naprawdę ładne celów za darmo, w tym całkiem pełni funkcjonalny ramach testu.

+0

Zapomniałem o tej funkcji powłoki; dzięki! –

2

Co skończyło się wygląda następująco:

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 

Jest on oparty na idei Jack Kelly, z końcówka Jonathana Lefflera włączone.

+0

Będziesz chciał, żeby było '|| zjazd 1' gdzieś tam; w przeciwnym razie po prostu ignorujesz wszystkie niepowodzenia testów. – l0b0

+0

@ l0b0, Nie chcę też ratować się po pierwszej porażce. Powiem ci coś: wymyślisz czysty sposób na "wyjście 1", jeśli którykolwiek z testów się nie powiedzie - ale po wykonaniu wszystkich testów, zmienię moją akceptowaną odpowiedź na tę. –

+0

'dla ...; uruchom test || błąd = 1; Gotowe; exit $ error'? – l0b0