#! /bin/bash
# Last edited on 2024-07-05 16:59:53 by stolfi

# Usage: "run_python_test.sh [ -noshow ] {MODULE}..."
# 
# Runs "{TSTDIR}/{MODULE}_TST.py" with the proper {PYTHONPATH}, and
# sends the standard output (if not empty) to
# "{OUTDIR}/{MODULE}_TST.txt"
# 
# The {TSTDIR} will be either "testes" or "tests" in the current
# directory, or the same in "../../tst/main/". The {OUTDIR} will be
# "{TSTDIR}/out" or "{TSTDIR}/saida", whichever exists.

if [[ "/$1" == "/-noshow" ]]; then
  show=0; shift
else
  show=1
fi

modules=( "$@" ); shift  # 
opref="tests/out/${modulo}"
ofile="${opref}.html"

while [[ ( $PWD =~ .*/tests ) || ( $PWD =~ .*/src ) ]]; do cd .. ; done

# Extensions of output files:
outexts=( html txt eps dat gcode gcd png pgm ppm )

PYTHONPATH=".:testes:tests" 
PYTHONPATH="${PYTHONPATH}:..:..:../testes:../tests" 
PYTHONPATH="${PYTHONPATH}:../../tst/main:../../tst/main/testes:../../tst/main/tests" 
PYTHONPATH="${PYTHONPATH}:../../../tst/main:../../../tst/main/testes:../../../tst/main/tests" 
PYTHONPATH="${PYTHONPATH}:/usr/lib/python3.6/site-packages/sos/plugins" 

function find_file() {
  # Looks for {fname} in some standard places; returns {fname} if not found.
  # The {type} should be "f" for regular files, "d" for directories.
  
  type="$1"; shift
  fname="$1"; shift
  
  for dir in . .. ../../tst/main ../../../tst/main ; do
    for tsub in . testes tests ; do 
      cand="${dir}/${tsub}/${fname}"
      if [[ "${type}" == "f" ]]; then
        if [[ -f ${cand} ]]; then echo "${cand}"; exit; fi
      elif [[ "${type}" == "d" ]]; then
        if [[ -d ${cand} ]]; then echo "${cand}"; exit; fi
      else
        echo "** invalid type = \"${type}\"" 1>&2; exit 1
      fi
    done
  done
  echo ${fname}
} 

# Find the output directory:
OUT_dir="`find_file d saida`"
if [[ ! ( -d ${OUT_dir} ) ]]; then
  OUT_dir="`find_file d out`"
fi
if [[ ! ( -d ${OUT_dir} ) ]]; then
  echo "** no directory \"out\" or \"saida\" found" 1>&2; exit 1
fi
echo "output directory \"${OUT_dir}\"" 1>&2

# Remove output files of other runs:
for ext in ${outexts[@]} ; do
  rm -fv ${OUT_dir}/*.${ext}
done

# ( cd tests && if [[ ! ( -r images ) ]]; then ln -s ../images; fi )
# ( cd tests && if [[ ! ( -r Makefile ) ]]; then ln -s ../Makefile; fi )

for module in "${modules[@]}" ; do 
  echo "=== testing module ${module} =============================" 1>&2
  TST_fname="${module}_TST.py"
  TST_file="`find_file f ${TST_fname}`" 
  echo "running \"${TST_file}\"" 1>&2
  if [[ ! ( -s ${TST_file} ) ]]; then 
    echo "** file ${TST_file} not found or empty" 1>&2; exit 1
  fi
  
  DEF_fname="${module}.py"
  DEF_file="`find_file f ${DEF_fname}`"  
  echo "interface \"${DEF_file}\"" 1>&2
  if [[ ! ( -s ${DEF_file} ) ]]; then 
    echo "** file ${DEF_file} not found or empty" 1>&2; exit 1
  fi
  
  # Check for functions that are not being tested:
  funcs_file="${OUT_dir}/.funcs"
  uses_file="${OUT_dir}/.uses"
  missing_file="${OUT_dir}/.missing"
  commented_file="${OUT_dir}/.commented"
  
  egrep -e '^def ' ${DEF_file} \
    | sed \
        -e 's/^def *//g' \
        -e 's/[ ]*[\\(].*$//g' \
    | sort \
    > ${funcs_file}
  find_python_uses.sh ${TST_file} ${module} > ${uses_file}
  bool 1-2 ${funcs_file} ${uses_file} > ${missing_file}
  if [[ -s ${missing_file} ]]; then
    echo "!! warning: these functions are not called explicitly by the test program:" 1>&2
    cat ${missing_file} | sed -e 's:^:  :g' 1>&2 
  fi
  cat ${TST_file} | egrep -e '^[#]+ *test' > ${commented_file}
  if [[ -s ${commented_file} ]]; then
    echo "!! warning: these lines in the test program are commented out:" 1>&2
    cat ${commented_file} | sed -e 's:^:  :g' 1>&2 
  fi
    
  # Remove output files of previous runs:
  OUT_pref="${OUT_dir}/${module}"
  
  # Run the test program, save output:
  OUT_txt="${OUT_pref}.txt" # Text output.
  export PYTHONPATH="${PYTHONPATH}" ; \
    time python3 ${TST_file} > ${OUT_txt}
    exstatus=$?
    echo "status = ${exstatus}" 1>&2  
  
  # Remove empty output files:
  for ext in ${outexts[@]} ; do
    for ff in ${OUT_pref}*.${ext}; do
      if [[ ( -e ${ff} ) && ( ! ( -s ${ff} ) ) ]]; then 
        if [[ "${ff}" != "${OUT_txt}" ]]; then
          echo "!! ${ff} is empty" 1>&2
        fi
        rm -fv ${ff}
      fi
    done
  done

  if [[ ( ${exstatus} -eq 0 ) && ( $show -ne 0 ) ]]; then
    # Show PDF outputs, if any:
    for OUT_pdf in ${OUT_pref}*.pdf; do
      if [[ -s ${OUT_pdf} ]]; then 
        evince ${OUT_pdf}
      fi
    done

    # Plot graphs, if any:
    touch ${OUT_pref}_BOGUS.dat # Damn shell...
    for OUT_dat in ${OUT_pref}*.dat; do
      # Name of the plot is the first underscore-delimited word after module name:
      plotname="${OUT_dat}"
      plotname="${plotname##*/}"
      plotname="${plotname/${module}_/}"
      plotname="${plotname%%.dat}"
      plotname="${plotname%%_*}"
      if [[ "/${plotname}" != "/BOGUS" ]]; then
        gscript="plot_${module}_${plotname}.sh"
        # Name of gnuplot output file:
        OUT_plot_png="${OUT_dat/.dat/.png}"
        if [[ ! ( -s ${OUT_dat} ) ]]; then 
          echo "** data file ${OUT_dat} is empty or missing" 1>&2
        elif [[ ! ( -s ${gscript} ) ]]; then 
          echo "** plot script ${gscript} not found" 1>&2
        else
          ${gscript} ${OUT_dat} > ${OUT_plot_png}
        fi
      fi
    done
  fi
  
  echo "=== done testing ${module} ==========================="
done
