1#!/bin/ksh
2#
3# Graphviz regression test driver
4#
5# Also relies on strps.awk.
6#
7# TODO:
8#  Report differences with shared version and with new output.
9
10# bsh, linux-ksh filter
11bar=1;echo foo | read bar<<- \!
122
13!
14if ((bar==1))
15then
16  echo "Graphviz test suite requires ksh93"
17  exit 1
18fi
19# csh, ksh88 filter
20#(( 1.5 == 1 )) && { echo "Graphviz test suite requires ksh93"; exit 1; }
21
22TESTFILE=tests.txt     # Test specifications
23GRAPHDIR=graphs        # Directory of input graphs and data
24OUTDIR=ndata           # Directory for test output
25OUTHTML=nhtml          # Directory for html test report
26REFDIR=${REFDIR-""}    # Directory for expected test output
27GENERATE=              # If set, generate test data
28VERBOSE=               # If set, give verbose output
29NOOP=                  # If set, just print list of tests
30DOT=${DOT-$(whence dot)} # should be $(top_builddir)/cmd/dot/dot_static
31DIFFIMG=${DIFFIMG-$(whence diffimg)}
32
33TESTNAME=   # name of test
34GRAPH=      # graph specification
35IDX=
36typeset -i i j SUBTESTCNT
37typeset -i CRASH_CNT=0 DIFF_CNT=0 TOT_CNT=0
38typeset -i LINECNT=0
39typeset -A TESTTYPES
40typeset -a ALG
41typeset -a FMT
42typeset -a FLAGS
43TMPINFILE=tmp$$.gv
44TMPFILE1=tmpnew$$
45TMPFILE2=tmpref$$
46
47# Read single line, storing it in LINE and update count.
48# Return 0 on success.
49function readLine
50{
51  if read -u 3 LINE
52  then
53    (( LINECNT+=1 ))
54    return 0
55  else
56    return 1
57  fi
58}
59
60# Skip blank lines and comments (lines starting with #)
61# Use first real line as the test name
62function skipLines
63{
64  while readLine
65  do
66    if [[ -n $LINE && ${LINE:0:1} != \# ]]
67    then
68      return 0
69    fi
70  done
71  return 1
72}
73
74# Subtests have the form: layout format optional_flags
75# Store the 3 parts in the arrays ALG, FMT, FLAGS.
76# Stop at a blank line
77function readSubtests
78{
79  (( SUBTESTCNT=0 ))
80  while readLine
81  do
82    if [[ -z "$LINE" ]]
83    then
84      return
85    fi
86    if [[ ${LINE:0:1} != \# ]]
87    then
88      echo $LINE | read ALG0 FMT0 FLAGS0
89      ALG[$SUBTESTCNT]=$ALG0
90      FMT[$SUBTESTCNT]=$FMT0
91      FLAGS[$SUBTESTCNT]=$FLAGS0
92      (( SUBTESTCNT+=1 ))
93    fi
94  done
95}
96
97function readTest
98{
99  # read test name
100  if skipLines
101  then
102    TESTNAME=$LINE
103  else
104    return 1
105  fi
106
107  # read input graph
108  if skipLines
109  then
110    GRAPH=$LINE
111  else
112    return 1
113  fi
114
115  readSubtests
116  return 0
117}
118
119# newfile = $1
120# oldfile = $2
121# assume subscript indicates file type
122function strip {
123  case $1 in
124    *.ps )
125      awk -f strps.awk $1 > $TFILE1
126      awk -f strps.awk $2 > $TFILE2
127      ;;
128    *.svg )
129      sed '/^<!--/d' < $1 | sed '/-->$/d' > $TFILE1
130      sed '/^<!--/d' < $2 | sed '/-->$/d' > $TFILE2
131      ;;
132    * )
133      cp $1  $TFILE1
134      cp $2  $TFILE2
135      ;;
136  esac
137
138}
139
140# Compare old and new output and report if different.
141#  Args: testname index fmt
142function doDiff
143{
144   FILE1=$OUTDIR/$OUTFILE
145   FILE2=$REFDIR/$OUTFILE
146   F=${3%%:*}
147   case $F in
148    ps | ps2 )
149      awk -f strps.awk $FILE1 > $TMPFILE1
150      awk -f strps.awk $FILE2 > $TMPFILE2
151      diff -q $TMPFILE2 $TMPFILE1 > /dev/null
152      if [[ $? != 0 ]]
153      then
154          print -u 2 "Test $1:$2 : == Failed == $OUTFILE"
155          (( DIFF_CNT+=1 ))
156      else
157          if [[ -n "$VERBOSE" ]]
158          then
159             print -u 2 "Test $1:$2 : == OK == $OUTFILE"
160          fi
161      fi
162      ;;
163    svg )
164      sed '/^<!--/d' < $FILE1 | sed '/-->$/d' > $TMPFILE1
165      sed '/^<!--/d' < $FILE2 | sed '/-->$/d' > $TMPFILE2
166      diff -q $TMPFILE2 $TMPFILE1 > /dev/null
167      if [[ $? != 0 ]]
168      then
169          print -u 2 "Test $1:$2 : == Failed == $OUTFILE"
170          (( DIFF_CNT+=1 ))
171      else
172          if [[ -n "$VERBOSE" ]]
173          then
174              print -u 2 "Test $1:$2 : == OK == $OUTFILE"
175          fi
176      fi
177      ;;
178    png )
179      $DIFFIMG $FILE2 $FILE1 >$OUTHTML/dif_$OUTFILE
180      if [[ $? != 0 ]]
181      then
182	  echo "<p>" >>$OUTHTML/index.html
183	  cp $FILE2 $OUTHTML/old_$OUTFILE
184	  echo "<img src=\"old_$OUTFILE\" width=\"192\" height=\"192\">" >>$OUTHTML/index.html
185	  cp $FILE1 $OUTHTML/new_$OUTFILE
186	  echo "<img src=\"new_$OUTFILE\" width=\"192\" height=\"192\">" >>$OUTHTML/index.html
187	  echo "<img src=\"dif_$OUTFILE\" width=\"192\" height=\"192\">" >>$OUTHTML/index.html
188          print -u 2 "Test $1:$2 : == Failed == $OUTFILE"
189          (( DIFF_CNT+=1 ))
190      else
191          if [[ -n "$VERBOSE" ]]
192          then
193              print -u 2 "Test $1:$2 : == OK == $OUTFILE"
194          fi
195	  rm $OUTHTML/dif_$OUTFILE
196      fi
197      ;;
198    * )
199      diff -q $FILE2 $FILE1 > /dev/null
200      if [[ $? != 0 ]]
201      then
202          print -u 2 "Test $1:$2 : == Failed == $OUTFILE"
203          (( DIFF_CNT+=1 ))
204      else
205          if [[ -n "$VERBOSE" ]]
206          then
207              print -u 2 "Test $1:$2 : == OK == $OUTFILE"
208          fi
209      fi
210      ;;
211    esac
212}
213
214# Generate output file name given 3 parameters.
215#   testname layout format
216# If format ends in :*, remove this, change the colons to underscores,
217# and append to basename
218# If the last two parameters have been used before, add numeric suffix.
219function genOutname
220{
221  if [[ $3 == *:* ]]
222  then
223    F=${3%%:*}
224    XFMT=${3#$F}
225    XFMT=${XFMT/:/_}
226  else
227    F=$3
228    XFMT=""
229  fi
230
231  IDX="$2$XFMT$F"
232  j=${TESTTYPES[$IDX]}
233  if (( j == 0 ))
234  then
235    TESTTYPES[$IDX]=1
236    J=""
237  else
238    TESTTYPES[$IDX]=$(( j+1 ))
239    J=$j
240  fi
241  OUTFILE="$1_$2$XFMT$J.$F"
242}
243
244# clear out all entries of associated array
245function aunset #name
246{
247	typeset i
248	nameref v=$1
249	for i in ${!v[@]}
250	do	unset v[$i]
251	done
252}
253
254function doTest
255{
256  if (( SUBTESTCNT == 0 ))
257  then
258    return
259  fi
260  case $GRAPH in
261    = )
262      INFILE=$GRAPHDIR/$TESTNAME.gv
263      ;;
264    graph* | digraph* )
265      INFILE=$TMPINFILE
266      echo "$GRAPH" > $INFILE
267      ;;
268    *.gv )
269      INFILE=$GRAPHDIR/$GRAPH
270      ;;
271    * )
272      echo "Unknown graph spec, test $TESTNAME - ignoring"
273      return
274      ;;
275  esac
276
277  for ((i=0;i<SUBTESTCNT;i++))
278  do
279    (( TOT_CNT+=1 ))
280    genOutname $TESTNAME ${ALG[$i]} ${FMT[$i]}
281    OUTPATH=$OUTDIR/$OUTFILE
282    KFLAGS=${ALG[$i]}
283    TFLAGS=${FMT[$i]}
284    test -z "$KFLAGS" || KFLAGS="-K$KFLAGS"
285    test -z "$TFLAGS" || TFLAGS="-T$TFLAGS"
286    testcmd="$DOT $KFLAGS $TFLAGS ${FLAGS[$i]} -o$OUTPATH $INFILE"
287    if [[ -n "$VERBOSE" ]]
288    then
289      print $testcmd
290    fi
291    if [[ $NOOP == 1 ]]
292    then
293      continue
294    fi
295
296    $testcmd 2> errout
297    RVAL=$?
298
299    if [[ -s errout ]]
300    then
301      cat errout
302    fi
303
304    if [[ $RVAL != 0 || ! -s $OUTPATH ]]
305    then
306      (( CRASH_CNT+=1 ))
307      print -u 2 "Test $TESTNAME:$i : == Layout failed =="
308      print -u 2 "  $testcmd"
309    elif [[ $GENERATE == 1 ]]
310    then
311      continue
312    elif [[ -r $REFDIR/$OUTFILE ]]
313    then
314      doDiff $TESTNAME $i ${FMT[$i]}
315    else
316      print -u 2 "Test $TESTNAME:$i : == No file $REFDIR/$OUTFILE for comparison =="
317    fi
318  done
319
320  # clear TESTTYPES
321  aunset TESTTYPES
322#  for W in ${!TESTTYPES[@]}
323#  do
324#    TESTTYPES[$W]=0
325#  done
326}
327
328trap 'rm -f $TMPFILE1 $TMPFILE2 $TMPINFILE errout; exit' 0 1 2 3 15
329
330Usage='rtest [-gvn] [TESTFILE]\n
331 -g : generate test data\n
332 -v : verbose\n
333 -n : print test'
334
335# Set REFDIR
336if [[ ! "$REFDIR" ]]
337then
338  SYSTYPE=$(uname -s)
339  case "$SYSTYPE" in
340  Linux*)
341    REFDIR=linux.x86
342    ;;
343  Darwin*)
344    REFDIR=macosx
345    ;;
346  *)
347    print "Unrecognized system \"$SYSTYPE\""
348    REFDIR=nshare
349    ;;
350  esac
351fi
352
353while getopts :gnv c
354do
355  case $c in
356  n )
357    VERBOSE=1
358    NOOP=1
359    ;;
360  v )
361    VERBOSE=1
362    ;;
363  g )
364    GENERATE=1
365    if [[ ! -d "$REFDIR" ]]
366    then
367		mkdir $REFDIR
368    fi
369    OUTDIR=$REFDIR
370    ;;
371  :)
372    echo $OPTARG requires a value
373    exit 2
374    ;;
375  \? )
376    if [[ "$OPTARG" == '?' ]]
377    then
378      print $Usage
379      exit 0
380    else
381      echo "rtest: unknown flag $OPTARG - ignored"
382    fi
383    ;;
384  esac
385done
386shift $((OPTIND-1))
387
388if [[ $# > 0 ]]
389then
390  if [[ -r $1 ]]
391  then
392    TESTFILE=$1
393  else
394    print -u 2 "Test file $1 does not exist"
395    exit 1
396  fi
397fi
398
399# Check environment and initialize
400
401if [[ $NOOP != 1 ]]
402then
403if [[ ! -d "$REFDIR" ]]
404then
405  print -u 2 "Test data directory $REFDIR does not exist"
406  exit 1
407fi
408
409if [[ ! -d "$OUTDIR" ]]
410then
411  mkdir $OUTDIR
412fi
413
414if [[ ! -d "$OUTHTML" ]]
415then
416  mkdir $OUTHTML
417fi
418rm -f $OUTHTML/*
419
420if [[ ! "$DOT" ]]
421then
422  print -u 2 "Could not find a value for DOT"
423  exit
424fi
425if [[ ! -x $DOT ]]
426then
427  print -u 2 "$DOT program is not executable"
428  exit 1
429fi
430
431if [[ $GENERATE != 1 && ! -x $DIFFIMG ]]
432then
433  print -u 2 "$DIFFIMG program is not executable"
434  exit 1
435fi
436fi
437
438
439exec 3< $TESTFILE
440while readTest
441do
442  doTest
443done
444if [[ $NOOP == 1 ]]
445then
446print -u 2 "No. tests: $TOT_CNT"
447elif [[ $GENERATE == 1 ]]
448then
449print -u 2 "No. tests: $TOT_CNT Layout failures: $CRASH_CNT"
450else
451print -u 2 "No. tests: $TOT_CNT Layout failures: $CRASH_CNT Changes: $DIFF_CNT"
452fi
453(( EXIT_STATUS=CRASH_CNT+DIFF_CNT ))
454exit $EXIT_STATUS
455