#!/bin/ksh
#
# Regression test for graphviz
#
# Assume LD_LIBRARY_PATH or equivalent is set.
#
# Environment:
#   TESTDIR = root directory for tests
#   INSTALLROOT = root directory for graphviz 
#   OUTDIR = directory in which to place test output
#   ARCHDIR = pathname for directory of architecture-specific output
#   ARCHTYPE = basename for directory of architecture-specific output
#   BINDIR = directory of executables; overrides INSTALLROOT
#
# If ARCHDIR is defined, architecture-specific files are found there.
# else
#   if ARCHTYPE not set, let ARCHTYPE=(uname -s).(uname -m).(whichcc)
#   use ARCHDIR=TESTDIR/ARCHTYPE
#   If ARCHDIR doesn't exist, use PAIRS mapping to map ARCHTYPE to another name
#   and try again.
#   If this doesn't work, use the share directory
#
# If INSTALLROOT is defined, it is used to find bin and lib directories
# else binaries found on PATH.
# If BINDIR is defined, it is used to find executables.
#
# Test output:
#   OK-BAD   (stable but inconsistent)   no action
#   BAD-OK   (unstable but consistent)   can remove special test file
#   BAD-BAD  (unstable and inconsistent) bug or update special test file
#   OK-OK    (stable and consistent)     cannot happen
#   OK=OK    (stable and consistent)     no action
#   BAD=BAD  (unstable and inconsistent) bug or add special test file
#

  # Canonical machine
MACH=$(uname -s).$(uname -m)
  # Default location of graphviz tree
DFLTROOT=/Users/north/src/graphviz
  # Quiet diff : usually diff -q
QDIFF="diff -q"
  # Root of common libraries and fonts
COMMON=/opt/local
  # Source of tclsh8.4
TCLDIR=/opt/local/bin

TESTDIR=${TESTDIR:=.}
GRAPHDIR=$TESTDIR/graphs    # directory where .gv test files are kept
INSTALLROOT=${INSTALLROOT:-""}            # base for bin and lib directories

DOTTESTS=1             # if non-empty, do dot batch tests
NEATOTESTS=1           # if non-empty, do neato batch tests
TWOPITESTS=1           # if non-empty, do twopi batch tests
CIRCOTESTS=1           # if non-empty, do circo batch tests
BIGBATCHTESTS=         # if non-empty, do big batch tests
LEFTYTESTS=            # if non-empty, do lefty tests
TCLTESTS=              # if non-empty, do tcl tests
USEDFLT=               # if non-empty, use default path
VERBOSE=               # if non-empty, what would be done
CLEANUP=               # if non-empty, remove generated files
UPDATE=                # if non-empty, copy new files into data
SHOWENV=               # if non-empty, exit after printing environment

TFILE1=/tmp/tmp$$_1
TFILE2=/tmp/tmp$$_2

USAGE="Usage: doit [-ncdbltuV?] [+abCDNPBlt] [--help]"
LUSAGE='\040-d  - use built-in default bin for arch\n
 -n  - print tests\n
 -b  - no batch tests\n
 -c  - cleanup\n
 -u  - update\n
 -l  - no lefty tests\n
 -t  - no tcl tests\n
 -V  - print test environment and exit\n
 -?  - usage\n
 +a  - all tests except big batch\n
 +b  - only batch tests\n
 +C  - only circo tests\n
 +D  - only dot tests\n
 +N  - only neato tests\n
 +P  - only twopi tests\n
 +B  - big batch tests\n
 +l  - only lefty tests\n
 +t  - only tcl tests\n
 --help  - long usage'

trap 'rm -f $TFILE1 $TFILE2 errout core; exit' 0 1 2 3 15

while [[ -n $1 ]]
do
  case $1 in
  +a )                     # all tests except big
    LEFTYTESTS=1
    TCLTESTS=1
    ;;
  -b )                     # no batch tests
    DOTTESTS=
    NEATOTESTS=
    TWOPITESTS=
    CIRCOTESTS=
    ;;
  +b )                     # only batch tests
    LEFTYTESTS=
    TCLTESTS=
    ;;
  +B )                     # big batch tests
    BIGBATCHTESTS=1
    ;;
  -c )                     # cleanup
    CLEANUP=1
    ;;
  -V )                     # print test environment only
    SHOWENV=1
    ;;
  +C )                     # only circo tests
    CIRCOTESTS=1
    DOTTESTS=
    LEFTYTESTS=
    TCLTESTS=
    NEATOTESTS=
    TWOPITESTS=
    ;;
  +D )                     # only dot tests
    CIRCOTESTS=
    DOTTESTS=1
    LEFTYTESTS=
    TCLTESTS=
    NEATOTESTS=
    TWOPITESTS=
    ;;
  +N )                     # only neato tests
    CIRCOTESTS=
    LEFTYTESTS=
    TCLTESTS=
    DOTTESTS=
    NEATOTESTS=1
    TWOPITESTS=
    ;;
  +P )                     # only twopi tests
    CIRCOTESTS=
    LEFTYTESTS=
    TCLTESTS=
    DOTTESTS=
    NEATOTESTS=
    TWOPITESTS=1
    ;;
  -d )
    USEDFLT=1
    ;;
  -c )                     # no circo tests
    CIRCOTESTS=
    ;;
  -l )                     # no lefty tests
    LEFTYTESTS=
    ;;
  +l )
    LEFTYTESTS=1           # only lefty tests
    TCLTESTS=
    DOTTESTS=
    NEATOTESTS=
    TWOPITESTS=
    ;;
  -t )                     # no tcl tests
    TCLTESTS=
    ;;
  +t )                     # only tcl tests
    TCLTESTS=1
    LEFTYTESTS=
    DOTTESTS=
    NEATOTESTS=
    TWOPITESTS=
    ;;
  -n )
    VERBOSE=1
    ;;
  -u )                     # update
    UPDATE=1
    ;;
  -\? )
    echo $USAGE
    exit 0
    ;;
  --help )
    echo $USAGE
    echo $LUSAGE
    exit 0
    ;;
  -* )
    echo Unknown flag : $1
    echo $USAGE
    exit 0
    ;;
  esac
  shift
done

COMMONDIR=$TESTDIR/share   # sys-independent directory

OUTDIR=${OUTDIR:-"$TESTDIR/data"}        # output directory
if [[ ! -d $OUTDIR ]]
then
  mkdir -p "$OUTDIR"
  if [[ $? != 0 ]]
  then
    echo "Could not create directory $OUTDIR" >&2
    exit 1
  fi
fi

 # make sure output directory is writable
if [[ ! -w "$OUTDIR" ]]
then
    echo "Output directory $OUTDIR is not writable" >&2
    exit 1
fi

if [[ -n "$USEDFLT" ]]
then
  if [[ -z "$ARCHTYPE" ]]
  then
    case "$ARCH" in
    linux* )
      ARCHTYPE=linux.i386.old
      ;;
    sgi* )
      ARCHTYPE=sgi.mips3
      ;;
    sol* )
      ARCHTYPE=sol6.sun4
      ;;
    esac
  fi
  export GDFONTPATH=$COMMON/share/fonts/ttf
  echo export GDFONTPATH=$GDFONTPATH
  export LD_LIBRARY_PATH=$COMMON/lib:$COMMON/lib/graphviz:$LD_LIBRARY_PATH
fi

# Array of pattern/string mapping one archtype to another
# Current hack uses colon separator because I don't the correct way now. 
PAIRS="IRIX64.IP27.sgi_cc:share sgi.mips3:share SunOS.sun4u*:sol6.sun4 Linux.*:linux.i386"

function mapa {
  for R in $PAIRS
  do
    PAT=${R%%:*}
    VAL=${R##*:}
    if [[ $1 == $PAT ]]
    then
      echo $VAL
      return
    fi
  done
  echo share
}

  # set ARCHTYPE if necessary
if [[ -z "$ARCHDIR" ]]
then
  if [[ -z "$ARCHTYPE" ]]
  then
    ARCHTYPE=$MACH.$($TESTDIR/whichcc)  
    USER_ARCHTYPE=
  else
    USER_ARCHTYPE=$ARCHTYPE
  fi
  ARCHDIR="$TESTDIR/$ARCHTYPE"
  if [[ ! -d $ARCHDIR ]]
  then
    NARCHTYPE=$(mapa $ARCHTYPE)
    ARCHDIR="$TESTDIR/$NARCHTYPE"
    if [[ -z "$NARCHTYPE" || ! -d $ARCHDIR ]]
    then
      if [[ -n "$USER_ARCHTYPE" ]]
      then
        echo "Warning: could not find $USER_ARCHTYPE - using share" >&2
      fi
      ARCHDIR=$COMMONDIR
    fi
  fi
else
 # make sure ARCHDIR exists as directory
  if [[ ! -d "$ARCHDIR" ]]
  then
    echo "Cannot find directory ARCHDIR=$ARCHDIR">&2
    exit 1
  fi
fi

 # where to find the binaries and libraries to test
if [[ -z "$INSTALLROOT" ]]
then
  if [[ -n "$USEDFLT" ]]
  then
    INSTALLROOT=$DFLTROOT
  else
    DP=`which dot`
    INSTALLROOT=${DP%/*/dot}  # remove /bin/dot
  fi
fi

BINDIR=${BINDIR:-$INSTALLROOT/bin}
if [[ ! -x "$BINDIR/dot" ]]
then
  echo "Could not find test programs in $BINDIR" >&2
  exit 1
fi

export TCLLIBPATH=$INSTALLROOT/lib/graphviz/tcl
PATH=$BINDIR:$TCLDIR:$PATH
echo doit: BINDIR=$BINDIR ARCHDIR=$ARCHDIR ARCHTYPE=$ARCHTYPE TCLLIBPATH=$TCLLIBPATH
if [[ -n "$SHOWENV" ]]
then
  exit
fi

 # if need DISPLAY, make sure set
if [[ -n "$LEFTYTESTS" || -n "$TCLTESTS" ]]
then
  if [[ -z "$DISPLAY" ]]
  then
    echo "DISPLAY variable needs to be set" >&2
    exit 1
  fi
  export DISPLAY
fi

# newfile = $1
# oldfile = $2
# assume subscript indicates file type
function strip {
  case $1 in
    *.ps )
      awk -f strps.awk $1 > $TFILE1
      awk -f strps.awk $2 > $TFILE2
      ;;
    *.svg )
      sed '/^<!--/d' < $1 | sed '/-->$/d' > $TFILE1
      sed '/^<!--/d' < $2 | sed '/-->$/d' > $TFILE2
      ;;
    * )
      cp $1  $TFILE1
      cp $2  $TFILE2
      ;;
  esac

}

function gdiff {
	strip $1 $2
	diff $TFILE1 $TFILE2 > /dev/null 2>&1
	return $?
}

function findOld {
	F=$ARCHDIR/$1
	if [[ -f $F ]]
	then
		printf "$F"
	else
		printf "$COMMONDIR/$1"
	fi
}

# Apply test: tst cmd fmt arguments name [suffix]
# Suffix allows multiple test on same .gv file.
function tst {
  CMD=$1
  FMT=$2
  ARGS=$3
  NAME=$4
  OUTF=$NAME$5.$FMT
  NEWFILE=$OUTDIR/$OUTF
  OLDFILE=$(findOld $OUTF)
  if [[ -n "$CLEANUP" ]]
  then
    rm -f $NEWFILE
  elif [[ -n "$UPDATE" ]]
  then
	if [[ ! -s $NEWFILE ]]
	then
		return
	fi
    gdiff $NEWFILE $OLDFILE
    if [[ $? != 0 ]]
    then
      cp $NEWFILE $OLDFILE
    fi
  elif [[ -n "$VERBOSE" ]]
  then
    echo $CMD -T$2 $ARGS $GRAPHDIR/$NAME.gv \> $NEWFILE
  elif [[ -f $GRAPHDIR/$NAME.gv ]]
  then 
    $CMD -T$2 $ARGS $GRAPHDIR/$NAME.gv > $NEWFILE 2> errout
    if [[ $? == 0 && -s $NEWFILE ]]
    then
      if [[ -f $OLDFILE ]]
      then
          # Check stability
        if gdiff $NEWFILE $OLDFILE
        then
          STABLE=OK
        else
          STABLE=BAD
        fi

          # Check consistency
        SHAREFILE=$COMMONDIR/$OUTF
        if [[ $OLDFILE == $SHAREFILE ]]
        then
          CONSISTENT="=$STABLE"
        else
            if gdiff $NEWFILE $SHAREFILE
            then
              CONSISTENT="-OK"
            else
              CONSISTENT="-BAD"
            fi
        fi

        if [[ $STABLE == BAD ]]
        then
          LOGFD=2
        else
          LOGFD=1
        fi
        printf "$OUTF : $STABLE$CONSISTENT " >&$LOGFD
        if [[ $STABLE == BAD && $OUTF == *.gv ]]
        then
          $TESTDIR/dotdiff $NEWFILE $OLDFILE 1 1>&$LOGFD
        else
          echo "" >&$LOGFD
        fi
      else
        echo "Data file $OUTF does not exist - creating; add to CVS" >&2
        mv $NEWFILE $OLDFILE
      fi
    else
      echo "$CMD $OUTF : == Failed ==" >&2
      if [[ -s errout ]]
      then
        cat errout 2>&1
      fi
    fi
  else
    echo "$CMD $OUTF : == Graph $NAME.gv does not exist ==" >&2
  fi
}

if [[ -n "$DOTTESTS" ]]
then
tst dot gv "" abstract  
tst dot gv "" alf  
tst dot gv "" clust1
tst dot gv "" clust2
tst dot gv "" clust3
tst dot gv "" clust4
tst dot gv "" clust5
tst dot gv "" clust
tst dot gv "" crazy  
tst dot gv "" ctext 
tst dot gv "" dfa 
tst dot gv "" fig6 
tst dot gv "" fsm 
tst dot gv "" grammar 
tst dot gv "" hashtable 
#tst dot gv "" honda-tokoro     # abort
tst dot gv "" jcctree 
tst dot gv "" jsort 
tst dot gv "" KW91 
tst dot gv "" Latin1 
tst dot gv "" ldbxtried 
tst dot gv "" longflat 
tst dot gv "" mike 
tst dot gv "" NaN 
tst dot gv "" newarrows 
tst dot gv "" nhg 
tst dot gv "" pgram 
tst dot gv "" pm2way 
tst dot gv "" pmpipe 
tst dot gv "" polypoly 
tst dot gv "" proc3d 
tst dot gv "" record2 
tst dot gv "" records 
tst dot gv "" rowe 
tst dot gv "" shells 
tst dot gv "" states 
tst dot gv "" structs 
tst dot gv "" train11 
tst dot gv "" trapeziumlr 
tst dot gv "" tree 
tst dot gv "" triedds 
tst dot gv "" try 
tst dot gv "" unix2 
tst dot gv "" unix2k 
tst dot gv "" unix 
tst dot gv "" viewfile 
tst dot gv "" world 
tst dot gv "" ER 
tst dot gv "" Heawood 
tst dot gv "" ngk10_4 
tst dot gv "" Petersen 
tst dot gv "" process 
tst dot ps "" crazy  
tst dot ps "" unix 
tst dot ps "" structs 
tst dot ps "" big 
tst dot ps "" biglabel 
tst dot ps "" b3         # unescaped parentheses
tst dot ps "" b7         # core dump
#tst dot png "" b15       # windows core dump
tst dot ps "" b22        # core dump
tst dot gv "" b29       # windows core dump
tst dot ps "" b33        # core dump
tst dot gif "" b34       # trouble with tilde
tst dot gv "" b36       # windows core dump
tst dot gv "" b51       # core dump
tst dot gif "" b56       # trouble with umlaut
tst dot ps "" b57        # core dump
tst dot gif "" b60       # bad text length from non-ascii text
tst dot plain "" b62     # core dump
tst dot ps "" b68        # bide attributes ignored
tst dot ps "" b69        # core dump
tst dot ps "" b71        # core dump
#tst dot ps "" b73       # open core dump
#tst dot ps "" b73a      # open core dump
#tst dot ps "" b76       # open core dump
tst dot jpg "" b79       # unfilled dot arrowhead
tst dot ps "" b80
#tst dot ps "" b80a      # open core dump
tst dot svg "" b81       # core dump
tst dot gv "" b94       # windows core dump
tst dot gv "" b102      # windows core dump
tst dot gv "" b106
#tst dot ps "" b123      # open core dump
tst dot ps "" b135       # core dump
tst dot png "" b145      # assertion fail
#tst dot ps "" b146      # open core dump
tst dot ps "" b155      # windows core dump
tst dot gv "" b491
tst dot plain-ext "" b545
tst dot ps "" layer
tst dot ps "" layer2
tst dot gif "" newarrows
tst dot ps "" newarrows
tst dot ps "" lsunix1
tst dot ps "" lsunix2
tst dot ps "" lsunix3
tst dot gv "" labelroot-ndd
tst dot gv "" labelroot-ndr
tst dot gv "" labelroot-ndl
tst dot gv "" labelroot-ndc
tst dot gv "" labelroot-ntd
tst dot gv "" labelroot-ntr
tst dot gv "" labelroot-ntl
tst dot gv "" labelroot-ntc
tst dot gv "" labelroot-nbd
tst dot gv "" labelroot-nbr
tst dot gv "" labelroot-nbl
tst dot gv "" labelroot-nbc
tst dot gv "" labelroot-fdd
tst dot gv "" labelroot-fdr
tst dot gv "" labelroot-fdl
tst dot gv "" labelroot-fdc
tst dot gv "" labelroot-ftd
tst dot gv "" labelroot-ftr
tst dot gv "" labelroot-ftl
tst dot gv "" labelroot-ftc
tst dot gv "" labelroot-fbd
tst dot gv "" labelroot-fbr
tst dot gv "" labelroot-fbl
tst dot gv "" labelroot-fbc
tst dot gv "" labelclust-ndd
tst dot gv "" labelclust-ndr
tst dot gv "" labelclust-ndl
tst dot gv "" labelclust-ndc
tst dot gv "" labelclust-ntd
tst dot gv "" labelclust-ntr
tst dot gv "" labelclust-ntl
tst dot gv "" labelclust-ntc
tst dot gv "" labelclust-nbd
tst dot gv "" labelclust-nbr
tst dot gv "" labelclust-nbl
tst dot gv "" labelclust-nbc
tst dot gv "" labelclust-fdd
tst dot gv "" labelclust-fdr
tst dot gv "" labelclust-fdl
tst dot gv "" labelclust-fdc
tst dot gv "" labelclust-ftd
tst dot gv "" labelclust-ftr
tst dot gv "" labelclust-ftl
tst dot gv "" labelclust-ftc
tst dot gv "" labelclust-fbd
tst dot gv "" labelclust-fbr
tst dot gv "" labelclust-fbl
tst dot gv "" labelclust-fbc
tst dot ps "" in
tst dot ps "-lgraphs/dfd.ps" ps
tst dot gv "-Grankdir=LR" a 1
tst dot gv "-Grankdir=RL" a 2
tst dot gv "-Grankdir=BT" a 3
tst dot ps "" multi
tst dot gv "" b786
fi

if [[ -n "$NEATOTESTS" ]]
then
tst neato ps "" dpd 
tst neato ps "-Gsplines=true -Goverlap=false" dpd 1
tst neato ps "-Gsize=8,8 -Gsplines=true -Goverlap=scale" dpd 2
tst neato ps "-Nstyle=filled -Goutputorder=edgesfirst" dpd  3
tst neato ps "-Nstyle=filled -Goutputorder=nodesfirst" dpd  4
tst neato ps "-Nshape=box" dpd 5
tst neato ps -x b77      # core dump 
tst neato gv "" b124      # core dump on linux
fi

if [[ -n "$TWOPITESTS" ]]
then
tst twopi ps "" b117     # assertion fail
fi

if [[ -n "$CIRCOTESTS" ]]
then
tst circo ps "" b117 a
tst circo ps "" process
tst circo ps "" p4
tst circo ps "" p3
tst circo ps "" p2
tst circo ps "" p
tst circo ps "" ngk10_4
tst circo ps "" Petersen
tst circo ps "" Heawood
tst circo ps "" ER
tst circo ps "" clover
fi

if [[ -n "$BIGBATCHTESTS" ]]
then
tst dot gv "" b100     # windows core dump
tst dot gv "" b103     # windows core dump
tst dot gv "" b104      # windows core dump
fi

if [[ -n "$LEFTYTESTS" ]]
then
  if [[ -n "$VERBOSE" ]]
  then
    echo dotty $GRAPHDIR/awilliams.gv
    echo lneato $GRAPHDIR/dpd.gv
  else
    dotty $GRAPHDIR/awilliams.gv
    lneato $GRAPHDIR/dpd.gv
  fi
fi

if [[ -n "$TCLTESTS" ]]
then
  if [[ -n "$VERBOSE" ]]
  then
    echo $TESTDIR/doted
  else
    $TESTDIR/doted
  fi
fi
