web-dev-qa-db-fra.com

Implémenter `make check` ou` make test`

Comment puis-je implémenter un framework de test de régression simple avec Make? (J'utilise GNU Make, si cela importe.)

Mon makefile actuel ressemble à ceci (modifié pour plus de simplicité):

OBJS = jscheme.o utility.o model.o read.o eval.o print.o

%.o : %.c jscheme.h
    gcc -c -o $@ $<

jscheme : $(OBJS)
    gcc -o $@ $(OBJS)

.PHONY : clean

clean :
    -rm -f jscheme $(OBJS)

J'aimerais avoir un ensemble de tests de régression, par exemple, expr.in tester une "bonne" expression & unrecognized.in en tester une "mauvaise", avec expr.cmp & unrecognized.cmp étant la sortie attendue pour chacun. Le test manuel ressemblerait à ceci:

$ 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

J'ai pensé ajouter un ensemble de règles au makefile ressemblant à ceci:

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]

Mes questions:
• Que dois-je mettre dans les espaces réservés [quelque chose]?
• Existe-t-il un moyen de remplacer le message de diff par un message disant: "Échec du test expr"?

24
J. C. Salomon

Votre approche originale, comme indiqué dans la question, est la meilleure. Chacun de vos tests se présente sous la forme d'une paire d'entrées et de sorties attendues. Make est tout à fait capable de parcourir ces derniers et d'exécuter les tests; il n'est pas nécessaire d'utiliser une boucle Shell for. En fait, en faisant cela, vous perdez la possibilité d'exécuter vos tests en parallèle et vous créez du travail supplémentaire pour nettoyer les fichiers temporaires (qui ne sont pas nécessaires).

Voici une solution (en utilisant bc comme exemple):

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 $@ failed" && exit 1)

all : test 
    @echo "Success, all tests passed."

La solution répond directement à vos questions d'origine:

  • Les espaces réservés que vous recherchez sont $< Et $(Word 2, $?) correspondant aux prérequis %.test-in Et %.test-cmp Respectivement. Contrairement aux commentaires @reinierpost, les fichiers temporaires ne sont pas nécessaires.
  • Le message diff est masqué et remplacé à l'aide de echo.
  • Le makefile doit être invoqué avec make -k Pour exécuter tous les tests indépendamment du fait qu'un test individuel échoue ou réussisse.
  • make -k all Ne s'exécutera que si tous les tests réussissent.

Nous évitons d'énumérer chaque test manuellement lors de la définition de la variable all-tests En tirant parti de la convention de dénomination des fichiers (*.test-in) Et des fonctions GNU make pour le fichier noms . En bonus, cela signifie que la solution évolue à des dizaines de milliers de tests hors de la boîte, car la longueur des variables est illimité dans GNU make C'est mieux que la solution basée sur Shell qui tombera une fois que vous aurez frappé le système d'exploitation limite de ligne de commande .

17
Richard Padley

Créez un script de testeur qui prend un nom de test et déduit le nom de fichier d'entrée, le nom de fichier de sortie et les données de smaple à partir de cela:

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

Ensuite, dans votre Makefile:

TESTS := expr unrecognised

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

Vous pouvez également essayer d'implémenter quelque chose comme automakeframework de test simple .

12
Jack Kelly

Ce que j'ai fini ressemble à ceci:

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

Il est basé sur l'idée de Jack Kelly, avec le conseil de Jonathan Leffler inclus.

3
J. C. Salomon

Je vais répondre à votre question sur le diff. Tu peux faire:

 diff fichier1 fichier2>/dev/null || écho Test bla bla échoué> & 2 

bien que vous souhaitiez peut-être utiliser cmp au lieu de diff.

Sur une autre note, vous pourriez trouver utile d'aller de l'avant et de franchir le pas et d'utiliser Automake. Votre Makefile.am (dans son intégralité) ressemblera à:

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

et vous obtiendrez gratuitement de nombreuses cibles vraiment sympas, y compris un cadre de test assez complet.

2
William Pursell