#!/bin/bash

# Copyright 2010 Google Inc.
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ------------------------------------------------------------------------

if [[ "$0" == */* ]] ; then
  cd "${0%/*}/"
fi

source ./setpaths

failures=""

function fail() {
  failures="$failures $base.szl"
}

# The MODES variable specifies which set of execution flags to pass to ./update
#
# The following modes are supported:
# -    szl default: run nonative, optimizer enabled (shell makes "" problematic)
# -N   run native, optimizer enabled
# -U   run nonative, optimizer disabled
# -NU  run native, optimizer disabled
# -C   run nonative, optimizer enabled, test cloning
# -NC  run native, optimizer enabled, test cloning
# -UC  run nonative, optimizer disabled, test cloning
# -NUC run native, optimizer disabled, test cloning
#
# Modes for multiple runs can be specified via a comma-separated list.
#
# If no MODE is specified, we run 2 optimized out of 4 possible options
# because szl set --optimize_sawzall_code to true by default.
# Running all of the options takes too long and likely to be unnecessary.
# It is astronomically unlikely that turning on optimization will fix a bug
# that is present if you do not optimize.

# Other options
#
# -e   do not stop at the first failed test
# -i   ignore differences in line numbers


MODES=-N,-
EXIT=exit
IGNORE_LINE_NUMBERS=false

SPECIFIED_MODES=""

while true; do
  case "$1" in
    '')
      break;  # end of args
      ;;
    -e)
      EXIT=fail
      ;;
    -f)
      shift
      FILES=$1
      ;;
    -i)
      shift
      IGNORE_LINE_NUMBERS=true
      ;;
    -m)
      shift
      MODES=$1
      SPECIFIED_MODES=$1
      if [[ -z $MODES || $MODES == "-e" ]]; then
        echo >&2 "Missing mode(s) in [-m <mode1>,...,<modeN>]"
        echo >&2 "<mode> is one of: -, -N, -U, -NU, -C, -NC, -UC, -NUC"
        exit 1
      fi
      ;;
    *)
      echo >&2 "Usage: regress [-e] [-m <mode1>,...,<modeN>]"
      echo >&2 "       where <mode> is one of: -, -N, -U, -NU, -C, -NC, -UC, -NUC"
      exit 1
      ;;
  esac
  shift
done

echo RUN "$SZL"
MYPID=$$

# these are modified for uniqueness when used

out="$SZL_TMP"/$USER-sawzall-out
err="$SZL_TMP"/$USER-sawzall-err

# remove any existing temps and intermediates

function cleanup {
  # created by update
  rm -f $out-$MYPID-* $err-$MYPID-*
}
cleanup


# Determine if we are using a 32-bit or 64-bit binary.
# (During the build SZL is a script, so we use need SZLEXEC instead.)

file_info="$(file -L "$SZLEXEC")"
if [[ "$file_info" == *"ELF 64-bit LSB"* ]]; then
  platform=64
elif [[ "$file_info" == *"ELF 32-bit LSB"* ]]; then
  platform=32
elif [[ "$file_info" == *"i386"* ]]; then
  platform=32
elif [[ "$file_info" == *"x86_64"* ]]; then
  platform=32
else
  echo "regress: Unrecognized platform" >&2
  echo "Result of file -L: $(file -L "$SZLEXEC")" >&2
  echo "SZL = $SZL"
  echo "SZLEXEC = $SZLEXEC"
  exit 1
fi

# iterate over all *.szl files in alphabetically named subdirectories.
# also do any subdirectory named on the command line.

if [ -n "$1" ]; then
  files=$(ls [a-z]*/*.szl $1/*.szl)
elif [ -n "$FILES" ]; then
  files="$FILES"
else
  files=$(ls [a-z]*/*.szl)
fi


# compare actual results with expected results

if ! $IGNORE_LINE_NUMBERS ; then
  function filecmp  { cmp -s "$1" "$2"; }
  function filediff { diff   "$1" "$2"; }
else
  function stripln {
    sed -e 's/\.szl:[0-9]*/.szl:NN/g' -e 's/\.proto:[0-9]*/.proto:NN/g' "$1"
  }
  function filecmp  { cmp -s <(stripln "$1") <(stripln "$2"); }
  function filediff { diff   <(stripln "$1") <(stripln "$2"); }
fi


function compare {
  myerr=$err-$MYPID-$1
  myout=$out-$MYPID-$1
  if ! filecmp $myerr $base.err; then
    echo regress: stderr for $i $1
    filediff $base.err $myerr
    cleanup
    $EXIT 1
  fi
  if ! filecmp $myout $base.out; then
    echo regress: stdout for $i $1
    filediff $base.out $myout
    cleanup
    $EXIT 1
  fi
}

if [[ -z $SPECIFIED_MODES ]]; then
  echo running in default modes $MODES
fi

for m in $(echo $MODES|sed 's/,/ /g'); do
  for i in $files; do
    base=$(echo $i | sed 's/\.szl.*//')
    if [[ $base == *-32 && $platform == 64 ]]; then
      echo regress: skipping $i
      continue
    elif [[ $base == *-64 && $platform == 32 ]]; then
      echo regress: skipping $i
      continue
    fi
    if [[ "$SZL_USES_MEMORY_LIMIT" != 1 ]] ; then
      if [[ $base == base/garbagecollection* ]] ; then
        echo regress: skipping $i
        continue
      fi
      if [[ $base == fixed_crashes/crash7 ]] ; then
        echo regress: skipping $i
        continue
      fi
      if [[ $base == intrinsics/resourcestats-* ]] ; then
        echo regress: skipping $i
        continue
      fi
    fi
    echo doing $i $m
    # run test
    ./update $m -R -X $MYPID-$m $i
    compare $m
  done
done


# clean up

cleanup


# done

if [ -n "$failures" ]; then
  echo Failed tests:
  echo $failures | tr ' ' '\n' | sort -u | sed 's/^/    /'
  echo "*** FAILED ***"
  exit 1
else
  echo "*** PASSED ***"
  exit 0
fi
