3 # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Dan McMahill
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of version 2 of the GNU General Public License as
7 # published by the Free Software Foundation
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
17 # All rights reserved.
19 # This code was derived from code written by Dan McMahill as part of the
20 # latex-mk testsuite. The original code was covered by a BSD license
21 # but the copyright holder (Dan McMahill) has re-released the code under
24 magic_test_skip
=${PCB_MAGIC_TEST_SKIP:-no}
26 if test "x${magic_test_skip}" = "xyes" ; then
29 The environment variable PCB_MAGIC_TEST_SKIP is set to yes.
30 This causes the testsuite to skip all tests and report no errors.
31 This is used for certain debugging *only*. The primary use is to
32 allow testing of the 'distcheck' target without including the effects
33 of the testsuite. The reason this is useful is that due to minor differences
34 in library versions and perhaps roundoff in different CPU's, the testsuite
35 may falsely report failures on some systems. These reported failures
36 prevent using 'distcheck' for verifying the rest of the build system.
37 These comments only apply to the tests which try to compare image files
50 $0 -- Run pcb regression tests
53 $0 [-d | --debug] [-g | --golden dir] [-r|--regen] [testname1 [testname2[ ...]]]
57 The $0 script is used both for running the pcb regression testsuite
58 as well as maintaining it. The way the test suite works is a number
59 of different layouts are exported using the various export HID's.
61 The resulting output files are examined in various ways to make sure
62 they are correct. The exact details of how they are compared varies.
63 For example, the PNG outputs are compared using tools from the ImageMagick
64 suite while the ASCII centroid and bill of materials files are normalized
65 with awk and then compared with the standard diff utility. The normalization
66 removes things like a comment line which contains the creation date.
70 -d | --debug Enables extra debug output
72 -g | --golden <dir> : Specifies that <dir> should be used for the
75 -r | --regen : Specifies that the results should be taken as new
76 reference files instead of comparing them to the
81 - The GUI interface is not checked via the regression testsuite.
87 echo "----------------------------------------------------------------------"
90 ##########################################################################
97 if test $do_debug = yes ; then
104 ##########################################################################
106 # command line processing
127 # set the 'golden' directory.
133 # regenerate the 'golden' output files. Use this with caution.
134 # In particular, all differences should be noted and understood.
140 echo "unknown option: $1"
151 if test "X$regen" = "Xyes" && test $# -ne 1; then
152 echo "Please regenerate only one test at a time."
153 echo "This limitation is a safety measure."
157 ##########################################################################
159 # set up various tools
167 top_srcdir
=${top_srcdir:-.}
169 # The pcb wrapper script we want to test
171 # by default we will be running it from $(srcdir)/runs/testname
172 # so we need to look 3 levels up and then down to src
173 PCB
=${PCB:-../../../src/pcbtest.sh}
175 # The gerbv executible
176 GERBV
=${GERBV:-gerbv}
177 GERBV_DEFAULT_FLAGS
=${GERBV_DEFAULT_FLAGS:---export=png --window=640x480}
179 # various ImageMagick tools
180 IM_ANIMATE
=${IM_ANIMATE:-animate}
181 IM_COMPARE
=${IM_COMPARE:-compare}
182 IM_COMPOSITE
=${IM_COMPOSITE:-composite}
183 IM_CONVERT
=${IM_CONVERT:-convert}
184 IM_DISPLAY
=${IM_DISPLAY:-display}
185 IM_MONTAGE
=${IM_MONTAGE:-montage}
188 INDIR
=${INDIR:-${srcdir}/inputs}
190 REFDIR
=${REFDIR:-${srcdir}/golden}
195 # the list of tests to run
196 TESTLIST
=${srcdir}/tests.list
198 if test "X$regen" = "Xyes" ; then
202 # create output directory
203 if test ! -d $OUTDIR ; then
205 if test $?
-ne 0 ; then
206 echo "Failed to create output directory ${OUTDIR}"
211 if test -z "$all_tests" ; then
212 if test ! -f ${TESTLIST} ; then
213 echo "ERROR: ($0) Test list $TESTLIST does not exist"
216 all_tests
=`${AWK} 'BEGIN{FS="|"} /^#/{next} {print $1}' ${TESTLIST} | sed 's; ;;g'`
219 if test -z "${all_tests}" ; then
220 echo "$0: No tests specified"
225 # fail/pass/total counts
231 ##########################################################################
240 top_srcdir ${top_srcdir}
252 GERBV_DEFAULT_FLAGS ${GERBV_DEFAULT_FLAGS}
256 IM_ANIMATE ${IM_ANIMATE}
257 IM_COMPARE ${IM_COMPARE}
258 IM_COMPOSITE ${IM_COMPOSITE}
259 IM_CONVERT ${IM_CONVERT}
260 IM_DISPLAY ${IM_DISPLAY}
261 IM_MONTAGE ${IM_MONTAGE}
265 tmpd
=/tmp
/pcb_tests.$$
266 mkdir
-p -m 0700 $tmpd
268 if test $rc -ne 0 ; then
269 echo "$0: ERROR: could not create $tmpd"
273 ##########################################################################
275 # utility functions for comparison
279 # compare_check "test_name" "file1" "file2"
281 # Makes sure that file1 and file2 both exist. If not, mark the current
282 # test as skipped and give an error message
289 if test ! -f "$f1" ; then
290 echo "$0: ${fn}(): $f1 does not exist"
295 if test ! -f "$f2" ; then
296 echo "$0: ${fn}(): $f2 does not exist"
303 ##########################################################################
305 # ASCII file comparison routines
309 # run_diff "file1" "file2"
315 if test $?
-ne 0 ; then return 1 ; fi
319 ##########################################################################
321 # BOM comparison routines
324 # used to remove things like creation date from BOM files
329 /^# Date:/ {print "# Date: today"; next}
330 /^# Author:/ {print "# Author: PCB"; next}
335 # top level function to compare BOM output
339 compare_check
"compare_bom" "$f1" "$f2" ||
return 1
341 # an example BOM file is:
343 # # PcbBOM Version 1.0
344 # # Date: Wed Jun 17 14:41:43 2009 UTC
345 # # Author: Dan McMahill
346 # # Title: Basic BOM/XY Test - PCB BOM
347 # # Quantity, Description, Value, RefDes
348 # # --------------------------------------------
349 # 8,"Standard SMT resistor, capacitor etc","RESC3216N",R90_TOP R180_TOP R270_TOP R0_TOP R270_BOT R180_BOT R90_BOT R0_BOT
350 # 8,"Dual in-line package, narrow (300 mil)","DIP8",UDIP90_TOP UDIP180_TOP UDIP270_TOP UDIP0_TOP UDIP270_BOT UDIP180_BOT UDIP90_BOT UDIP0_BOT
351 # 8,"Small outline package, narrow (150mil)","SO8",USO90_TOP USO180_TOP USO270_TOP USO0_TOP USO270_BOT USO180_BOT USO90_BOT USO0_BOT
353 # For comparison, we need to ignore changes in the Date and Author lines.
354 local cf1
=${tmpd}/`basename $f1`-ref
355 local cf2
=${tmpd}/`basename $f2`-out
357 normalize_bom
$f1 $cf1
358 normalize_bom
$f2 $cf2
359 run_diff
$cf1 $cf2 || test_failed
=yes
362 ##########################################################################
364 # X-Y (centroid) comparison routines
367 # used to remove things like creation date from BOM files
372 /^# Date:/ {print "# Date: today"; next}
373 /^# Author:/ {print "# Author: PCB"; next}
381 compare_check
"compare_xy" "$f1" "$f2" ||
return 1
383 local cf1
=${tmpd}/`basename $f1`-ref
384 local cf2
=${tmpd}/`basename $f2`-out
385 normalize_xy
"$f1" "$cf1"
386 normalize_xy
"$f2" "$cf2"
387 run_diff
"$cf1" "$cf2" || test_failed
=yes
390 ##########################################################################
392 # GCODE comparison routines
395 # used to remove things like creation date from gcode files
399 # matches string such as '( Tue Mar 9 17:45:43 2010 )'
400 $AWK --posix '!/^\( *[A-Z][a-z]{2} [A-Z][a-z]{2} [0123 ][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{4} *\)$/' \
407 compare_check
"compare_gcode" "$f1" "$f2" ||
return 1
409 # For comparison, we need to ignore changes in the Date and Author lines.
410 local cf1
=${tmpd}/`basename $f1`-ref
411 local cf2
=${tmpd}/`basename $f2`-out
413 normalize_gcode
$f1 $cf1
414 normalize_gcode
$f2 $cf2
416 run_diff
"$cf1" "$cf2" || test_failed
=yes
419 ##########################################################################
421 # RS274-X and Excellon comparison
427 compare_check
"compare_rs274x" "$f1" "$f2" ||
return 1
429 # use gerbv to export our reference RS274-X file and our generated
430 # RS274-X file to png. Then we'll use ImageMagick to see
431 # if there are any differences in the resulting files
435 png1
=${pngdir}/${nb}-ref.png
436 png2
=${pngdir}/${nb}-out.png
438 debug
"${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png1} ${f1}"
439 ${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png1} ${f1}
441 debug
"${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png2} ${f2}"
442 ${GERBV} ${GERBV_DEFAULT_FLAGS} --output=${png2} ${f2}
444 compare_image
${png1} ${png2}
452 ##########################################################################
454 # PostScript comparison
460 compare_check
"compare_ps" "$f1" "$f2" ||
return 1
462 # PostScript output is difficult to compare, because the last page
463 # ( = fab page) contains a date stamp written not as text, but drawn
464 # with lines. For now we only check wether the file contains valid
465 # PostScript and wether the page count matches.
467 echo "%!" > ${TEMP_FILE}
468 echo "currentpagedevice /PageCount get" >> ${TEMP_FILE}
469 echo " == flush" >> ${TEMP_FILE}
471 ORG_COUNT
=`gs -q -dNODISPLAY -dBATCH -dNOPAUSE "$f1" ${TEMP_FILE}`
472 NEW_COUNT
=`gs -q -dNODISPLAY -dBATCH -dNOPAUSE "$f2" ${TEMP_FILE}`
477 if test ${RESULT} -ne 0; then
478 echo "Invalid PostScript generated."
480 else if test "${ORG_COUNT}" != "${NEW_COUNT}"; then
481 echo -n "Page count of generated PostScript is ${NEW_COUNT} "
482 echo "instead of expected ${OLD_COUNT}."
486 unset ORG_COUNT NEW_COUNT RESULT
489 ##########################################################################
491 # GIF/JPEG/PNG comparison routines
497 compare_check
"compare_image" "$f1" "$f2" ||
return 1
499 # now see if the image files are the same
500 debug
"${IM_COMPARE} -metric MAE ${f1} ${f2} null:"
501 same
=`${IM_COMPARE} -metric MAE ${f1} ${f2} null: 2>&1 | \
502 ${AWK} '{if($1 == 0){print "yes"} else {print "no"}}'`
503 debug
"compare_image(): same = $same"
505 if test "$same" != yes ; then
507 echo "FAILED: See ${errdir}"
509 ${IM_COMPARE} ${f1} ${f2} ${errdir}/compare.png
510 ${IM_COMPOSITE} ${f1} ${f2} -compose difference ${errdir}/composite.png
511 ${IM_CONVERT} ${f1} ${f2} -compose difference -composite -colorspace gray ${errdir}/gray.png
512 cat > ${errdir}/animate.sh
<< EOF
514 ${IM_CONVERT} -label "%f" ${f1} ${f2} miff:- | \
515 ${IM_MONTAGE} - -geometry +0+0 -tile 1x1 miff:- | \
516 ${IM_ANIMATE} -delay 0.5 -loop 0 -
518 chmod a
+x
${errdir}/animate.sh
522 ##########################################################################
524 # PCB (layout) comparison routines
530 compare_check
"compare_pcb" "$f1" "$f2" ||
return 1
532 run_diff
"$f1" "$f2" || test_failed
=yes
535 ##########################################################################
537 # The main program loop
540 for t
in $all_tests ; do
546 ######################################################################
548 # extract the details for the test
551 pcb_flags
="${PCB_DEFAULT_FLAGS}"
553 rundir
="${OUTDIR}/${t}"
554 refdir
="${REFDIR}/${t}"
555 errdir
=${rundir}/mismatch
557 # test_name | layout file(s) | [export hid name] | [optional arguments to pcb] | [mismatch]
559 name
=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $1}'`
560 files
=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $2}'`
561 hid
=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {gsub(/[ \t]*/, ""); print $3}'`
562 args
=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $4}'`
563 mismatch
=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {if($5 == "mismatch"){print "yes"}else{print "no"}}'`
564 out_files
=`grep "^[ \t]*${t}[ \t]*|" $TESTLIST | $AWK 'BEGIN{FS="|"} {print $6}'`
566 # strip whitespace from single file names
567 while test "X${files# }" != "X${files#}" ; do
570 while test "X${files% }" != "X${files%}" ; do
574 if test "X${name}" = "X" ; then
575 echo "ERROR: Specified test ${t} does not appear to exist"
576 skip
=`expr $skip + 1`
580 if test "X${args}" != "X" ; then
584 if test "X${hid}" = "Xgerber" ; then
585 pcb_flags
="--fab-author Fab_Author ${pcb_flags}"
588 if test "X${hid}" = "Xaction" ; then
589 script_file
=${files%.pcb}.
script
590 pcb_flags
="--action-script ../../${INDIR}/${script_file}"
592 # hidden here, still in effect for all HID tests
593 pcb_flags
="-x ${hid} ${pcb_flags}"
596 ######################################################################
598 # Set up the run directory
601 test -d ${rundir} && rm -fr ${rundir}
603 if test $?
-ne 0 ; then
604 echo "$0: Could not create run directory ${rundir}"
605 skip
=`expr $skip + 1`
610 ######################################################################
612 # check to see if the files we need exist and copy them to the run
619 if test ! -f ${INDIR}/${f} ; then
620 echo "ERROR: File $f specified as part of the $t test does not exist"
623 path_files
="${path_files} ${INDIR}/${f}"
624 cp "${INDIR}/${f}" "${rundir}"
625 chmod u
+w
"${rundir}/${f}"
629 if test "$missing_files" = "yes" ; then
630 echo "${t} had missing input files. Skipping test"
631 skip
=`expr $skip + 1`
635 ######################################################################
640 echo "(cd ${rundir} && ${PCB} ${pcb_flags} ${files})"
641 (cd ${rundir} && ${PCB} ${pcb_flags} ${files})
644 if test $pcb_rc -ne 0 ; then
645 echo "${PCB} returned ${pcb_rc}. This is a failure."
646 fail
=`expr $fail + 1`
650 ######################################################################
655 if test "X$regen" = "Xyes" ; then
656 echo "Regenerated ${t}"
658 # compare the result to our reference file
661 for f
in $out_files ; do
662 debug
"processing $f"
663 # break apart type:fn into the type and file name
664 type=`echo $f | sed 's;:.*;;g'`
665 fn
=`echo $f | sed 's;.*:;;g'`
670 compare_bom
${refdir}/${fn} ${rundir}/${fn}
674 compare_xy
${refdir}/${fn} ${rundir}/${fn}
679 compare_gcode
${refdir}/${fn} ${rundir}/${fn}
684 compare_cnc
${refdir}/${fn} ${rundir}/${fn}
688 compare_rs274x
${refdir}/${fn} ${rundir}/${fn}
693 compare_ps
${refdir}/${fn} ${rundir}/${fn}
698 compare_image
${refdir}/${fn} ${rundir}/${fn}
702 compare_image
${refdir}/${fn} ${rundir}/${fn}
706 compare_image
${refdir}/${fn} ${rundir}/${fn}
711 compare_pcb
${refdir}/${fn} ${rundir}/${fn}
716 echo "internal error: $type is not a known file type"
723 if test "X${hid}" != "Xaction" ; then
724 # clean up the input files we'd copied over
730 if test $test_failed = yes ; then
732 fail
=`expr $fail + 1`
733 elif test $test_skipped = yes ; then
735 skip
=`expr $skip + 1`
738 pass
=`expr $pass + 1`
746 echo "Passed $pass, failed $fail, skipped $skip out of $tot tests."
749 if test $pass -ne $tot ; then