2011-02-07 9 views
16

¿Cómo puedo implementar un marco de prueba de regresión simple con Make? (Estoy usando GNU Make, si lo que importa.)Implementando `make check` o` make test`

Mi makefile actual se ve algo como esto (editado por simplicidad):

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) 

me gustaría tener un conjunto de pruebas de regresión, p.ej, expr.in probando una expresión "buena" & unrecognized.in probando una "mala", con expr.cmp & unrecognized.cmp siendo el resultado esperado para cada una. Las pruebas manuales se vería así:

$ 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 

pensé para añadir un conjunto de reglas para el makefile buscando algo como esto:

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] 

Mis preguntas:
• ¿Qué debo poner en el [ algo] marcadores de posición?
• ¿Hay alguna manera de reemplazar el mensaje de diff con un mensaje que dice "Falló la prueba expr"?

+0

¿De dónde provienen todos los tempfiles? ¿Qué hay de malo en – reinierpost

+0

@reinierpost? Si tiene una mejor forma de hacer estas comparaciones, de todos modos publique una respuesta que los incluya; eso es exactamente el tipo de ayuda que estoy pidiendo. –

+0

Lo siento, pensé que había cancelado esa pregunta. Necesitas tempfiles. – reinierpost

Respuesta

8

Su enfoque original, como se indica en la pregunta, es el mejor. Cada una de tus pruebas tiene la forma de un par de entradas y salidas esperadas. Make es bastante capaz de iterar a través de estos y ejecutar las pruebas; no es necesario utilizar un ciclo de shell for. De hecho, al hacer esto, está perdiendo la oportunidad de ejecutar sus pruebas en paralelo y crear trabajo adicional para usted mismo para limpiar los archivos temporales (que no son necesarios).

he aquí una solución (utilizando bc como ejemplo):

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

La solución se dirige directamente a sus preguntas originales:

  • Los marcadores de posición que usted está buscando son $< y $(word 2, $?) correspondientes a la requisitos previos %.test-in y %.test-cmp respectivamente. Contrario al comentario @reinierpost, los archivos temporales no son necesarios.
  • El mensaje de diferencia está oculto y reemplazado usando echo.
  • El archivo MAKE se debe invocar con make -k para ejecutar todas las pruebas, independientemente de si una prueba individual falla o tiene éxito.
  • make -k all solo se ejecutará si todas las pruebas tienen éxito.

Evitamos la enumeración de cada prueba manualmente la hora de definir la variable all-tests mediante el aprovechamiento de la convención de nomenclatura de archivos (*.test-in) y el GNU make functions for file names. Como extra, esto significa que la solución se amplía a decenas de miles de pruebas de fábrica, ya que la longitud de las variables es unlimited en GNU make. Esto es mejor que la solución basada en shell que caerá una vez que llegue al sistema operativo command line limit.

+0

¿Podría incluir un enlace a la entrada manual pertinente de GNU Make en <[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

Con gusto - hecho! –

+1

¡Gracias, aprendí bastantes cosas sacando el manual de fabricación de GNU y trabajando con este ejemplo! – dpritch

9

hacer un script corredor de prueba que toma un nombre de la prueba y deduce el nombre del archivo de entrada, nombre de archivo de salida y datos Smaple de que:

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

Luego, en su Makefile:

TESTS := expr unrecognised 

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

Usted podría También intente implementar algo como automake 's simple test framework.

+1

Me gusta la idea del bucle de shell sobre mi plan original de objetivos separados. El script de shell no es una mala idea, pero creo que incorporaré todo en el archivo MAKE. –

+1

También estoy _realmente_ no buscando abrir toda la lata de gusanos autotools. –

+1

@jcsalomon: No quise decir usar las autotools, sino estudiar cómo se comporta el sistema de prueba. Dejaré esto aquí en caso de que cambies de opinión: http://www.lrde.epita.fr/~adl/autotools.html. Recuerde escapar de los signos '$' en su 'Makefile'. Me atrapó un par de veces. –

2

Voy a abordar solo su pregunta acerca de diff. Puede hacerlo:

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

aunque es posible que desee utilizar cmp en lugar de diff.

En otra nota, puede que le resulte útil seguir adelante y tomar el y utilizar automake. Su Makefile.am (en su totalidad) se verá así:

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

e obtendrá una gran cantidad de objetivos muy agradable de forma gratuita, incluyendo un marco de pruebas bastante con todas las funciones.

+0

Me había olvidado de esa función de shell; ¡Gracias! –

2

Lo que terminé con el siguiente aspecto:

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 

Se basa en la idea de Jack Kelly con la punta de Jonathan Leffler incluido.

+0

Querrás que haya un '|| salga 1' en algún lugar allí; de lo contrario, simplemente ignoras todas las fallas de prueba. – l0b0

+0

@ l0b0, tampoco quiero rescatarme después de la primera falla. Te diré algo: se te ocurre una forma limpia de 'salir 1' si falla alguna de las pruebas, pero después de que se ejecuten todas las pruebas, y cambiaré mi respuesta aceptada a esa. –

+0

'para ...; hacer una prueba || error = 1; hecho; salir $ error'? – l0b0