#!/bin/sh -

OBJDIR='/tmp/jobo';export OBJDIR # MANAGED!!

: ${SHELL:=/bin/sh};export SHELL

: ${JOBNO:=1}
: ${JOBWAIT:=2}
: ${JOBMON:=y}

export \
   OBJDIR SHELL JOBNO JOBWAIT JOBMON

LC_ALL=C LANG=C TZ=UTC
export LC_ALL LANG TZ

if [ -z "${MAILX__CC_TEST_RUNNING}" ]; then
      CHECK=1 RUN_TEST= MAILX=
   export CHECK RUN_TEST MAILX
fi

awk='/usr/bin/awk';export awk
cat='/bin/cat';export cat
grep='/usr/bin/grep';export grep
mkdir='/bin/mkdir';export mkdir
rm='/bin/rm';export rm

if [ -z "${MAILX__CC_TEST_RUNNING}" ]; then
   MAILX__CC_TEST_RUNNING=y
   export MAILX__CC_TEST_RUNNING
rm -rf $OBJDIR
mkdir -p $OBJDIR
   exec "${SHELL}" "${0}" "${@}"
fi
cd $OBJDIR || exit 100

DEVELDIFF= DUMPERR=
TESTS_PERFORMED=0 TESTS_OK=0 TESTS_FAILED=0 TESTS_SKIPPED=0
JOBS=0 JOBLIST= JOBDESC= JOBREAPER= JOBSYNC=
SUBSECOND_SLEEP=
   ( sleep .1 ) >/dev/null 2>&1 && SUBSECOND_SLEEP=y

   TESTS_NET_TEST=
   [ "${OPT_NET_TEST}" = 1 ] && [ -x ./net-test ] && TESTS_NET_TEST=1
   export TESTS_NET_TEST

COLOR_ERR_ON= COLOR_ERR_OFF=  COLOR_DBGERR_ON= COLOR_DBGERR_OFF=
COLOR_WARN_ON= COLOR_WARN_OFF=
COLOR_OK_ON= COLOR_OK_OFF=
ESTAT=0
TEST_NAME=

trap "
   jobreaper_stop
" EXIT
trap "exit 1" HUP INT QUIT TERM
trap 'echo TTIN' TTIN
trap 'echo TTOU' TTOU
trap 'echo TSTP' TSTP
trap : CHLD

jobreaper_start() {
   printf 'Starting job reaper (timeout of %s seconds)\n' ${JOBWAIT}

   i=
   trap 'i=1' USR1 # "reaper (actually a notify timer only) is up"
   (
      parent=${$}
      sleeper= int=0 hot=0
      trap '' EXIT HUP QUIT CHLD
      trap 'exit 0' INT
      trap '
         [ -n "${sleeper}" ] && kill -TERM ${sleeper} >/dev/null 2>&1
         int=1 hot=1
      ' USR1
      trap '
         [ -n "${sleeper}" ] && kill -TERM ${sleeper} >/dev/null 2>&1
         int=1 hot=0
      ' USR2
      trap '
         [ -n "${sleeper}" ] && kill -TERM ${sleeper} >/dev/null 2>&1
         echo "Stopping job reaper"
         exit 0
      ' TERM

      # traps are setup, notify parent that we are up and running
      kill -USR1 ${parent} >/dev/null 2>&1

      while :; do
         int=0
         sleep ${JOBWAIT} &
         sleeper=${!}
         wait ${!}
         sleeper=
         [ "${int}${hot}" = 01 ] && kill -USR1 ${parent} >/dev/null 2>&1
      done
   ) </dev/null & #>/dev/null 2>&1 &
   JOBREAPER=${!}

   j=
   if [ ${?} -eq 0 ]; then
      while :; do
         if [ -n "${i}" ]; then
            trap '' USR1
            return
         fi
         printf '..%s waiting for job reaper to come up\n' "${j}"
         j=' still'
         sleep 1 &
         wait ${!}
      done
   fi

   JOBREAPER=
   printf '%s!! Cannot start the wild job reaper!%s\n' \
      "${COLOR_ERR_ON}" "${COLOR_ERR_OFF}"
}

jobreaper_stop() {
   [ -n "${JOBREAPER}" ] && kill -TERM ${JOBREAPER} >/dev/null 2>&1
   JOBREAPER=
   if [ ${JOBS} -gt 0 ]; then
      echo 'Cleaning up running jobs'
      jtimeout
      wait ${JOBLIST}
      JOBLIST=
   fi
}

jspawn() {
   if [ ${JOBNO} -gt 1 ]; then
      # We are spawning multiple jobs..
      [ ${JOBS} -eq 0 ] && printf '...'
      JOBS=`add ${JOBS} 1`
      printf ' [%s=%s]' ${JOBS} "${1}"
   else
      JOBS=1
      # Assume problems exist, do not let user keep hanging on terminal
      if [ -n "${RUN_TEST}" ]; then
         printf '... [%s]\n' "${1}"
      fi
   fi

   [ -n "${JOBMON}" ] && set -m >/dev/null 2>&1
   (  # Place the job in its own directory to ease file management
      trap '' EXIT HUP INT QUIT TERM USR1 USR2
      ${mkdir} t.${JOBS}.d && cd t.${JOBS}.d &&
         eval t_${1} ${JOBS} ${1} &&
         ${rm} -f ../t.${JOBS}.id
   ) > t.${JOBS}.io </dev/null & # 2>&1 </dev/null &
   i=${!}
   [ -n "${JOBMON}" ] && set +m >/dev/null 2>&1
   JOBLIST="${JOBLIST} ${i}"
   printf '%s\n%s\n' ${i} ${1} > t.${JOBS}.id

   # ..until we should sync or reach the maximum concurrent number
   [ ${JOBS} -lt ${JOBNO} ] && return

   jsync 1
}

jsync() {
   if [ ${JOBS} -eq 0 ]; then
      [ -n "${TEST_ANY}" ] && printf '\n'
      TEST_ANY=
      return
   fi
   [ -z "${JOBSYNC}" ] && [ ${#} -eq 0 ] && return

   [ ${JOBNO} -ne 1 ] && printf ' .. waiting\n'

   if [ -n "${JOBREAPER}" ]; then
      timeout= alldone=

      trap 'echo TIMEOUT IN PARENT;timeout=1' USR1
      kill -USR1 ${JOBREAPER} >/dev/null 2>&1

      loops=0
      while [ -z "${timeout}" ]; do
         alldone=1
         i=0
         while [ ${i} -lt ${JOBS} ]; do
            i=`add ${i} 1`
            [ -f t.${i}.id ] || continue
            alldone=
            break
         done
         [ -n "${alldone}" ] && break

         if [ -z "${SUBSECOND_SLEEP}" ]; then
            loops=`add ${loops} 1`
            [ ${loops} -lt 111 ] && continue
            sleep 1 &
         else
            sleep .5 &
         fi
echo >&2 PREWAIT
         wait ${!}
      done

      kill -USR2 ${JOBREAPER} >/dev/null 2>&1
      trap '' USR1

      [ -n "${timeout}" ] && jtimeout
   fi

   # Now collect the zombies
   wait ${JOBLIST}
   JOBLIST=

   # Update global counters
   i=0
   while [ ${i} -lt ${JOBS} ]; do
      i=`add ${i} 1`

      if [ -f t.${i}.id ]; then
         { read pid; read desc; } < t.${i}.id
         desc=${desc#${desc%%[! ]*}}
         desc=${desc%${desc##*[! ]}}
         [ -s t.${i}.io ] && printf >&2 '\n'
         printf >&2 '%s!! Timeout: reaped job %s [%s]%s\n' \
            "${COLOR_ERR_ON}" ${i} "${desc}" "${COLOR_ERR_OFF}"
         TESTS_FAILED=`add ${TESTS_FAILED} 1`
      elif [ -s t.${i}.result ]; then
         read es tp to tf ts < t.${i}.result
         TESTS_PERFORMED=`add ${TESTS_PERFORMED} ${tp}`
         TESTS_OK=`add ${TESTS_OK} ${to}`
         TESTS_FAILED=`add ${TESTS_FAILED} ${tf}`
         TESTS_SKIPPED=`add ${TESTS_SKIPPED} ${ts}`
         [ "${es}" != 0 ] && ESTAT=${es}
      else
         TESTS_FAILED=`add ${TESTS_FAILED} 1`
         ESTAT=1
      fi
   done

   JOBS=0
}

jtimeout() {
   i=0
   while [ ${i} -lt ${JOBS} ]; do
      i=`add ${i} 1`
      if [ -f t.${i}.id ] &&
            read pid < t.${i}.id >/dev/null 2>&1 &&
            kill -0 ${pid} >/dev/null 2>&1; then
         j=${pid}
         [ -n "${JOBMON}" ] && j=-${j}
         kill -KILL ${j} >/dev/null 2>&1
      else
         ${rm} -f t.${i}.id
      fi
   done
}

t_prolog() {
   shift

   ESTAT=0 TESTS_PERFORMED=0 TESTS_OK=0 TESTS_FAILED=0 TESTS_SKIPPED=0 \
      TEST_NAME=${1} TEST_ANY=

   printf '%s[%s]%s\n' "" "${TEST_NAME}" ""
}

t_epilog() {
   [ -n "${TEST_ANY}" ] && printf '\n'

   printf '%s %s %s %s %s\n' \
      ${ESTAT} \
         ${TESTS_PERFORMED} ${TESTS_OK} ${TESTS_FAILED} ${TESTS_SKIPPED} \
      > ../t.${1}.result
}

t_echo() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s' "${*}"
   TEST_ANY=1
}

t_echook() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s%s:ok%s' "${COLOR_OK_ON}" "${*}" "${COLOR_OK_OFF}"
   TEST_ANY=1
}

t_echoerr() {
   ESTAT=1
   t_echo0err "${@}"
}

t_echo0err() {
   [ -n "${TEST_ANY}" ] && __i__="\n" || __i__=
   printf "${__i__}"'%sERROR: %s%s\n' \
      "${COLOR_ERR_ON}" "${*}" "${COLOR_ERR_OFF}"
   TEST_ANY=
}

t_echowarn() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s%s%s' "${COLOR_WARN_ON}" "${*}" "${COLOR_WARN_OFF}"
   TEST_ANY=1
}

t_echoskip() {
   [ -n "${TEST_ANY}" ] && __i__=' ' || __i__=
   printf "${__i__}"'%s%s[skip]%s' \
      "${COLOR_WARN_ON}" "${*}" "${COLOR_WARN_OFF}"
   TEST_ANY=1
   TESTS_SKIPPED=`add ${TESTS_SKIPPED} 1`
}

check() {
   restat=${?} tid=${1} eestat=${2} f=${3} s=${4} optmode=${5}

echo >&2 CHECK $TID 0
   TESTS_PERFORMED=`add ${TESTS_PERFORMED} 1`

echo >&2 CHECK $TID 10
   if [ -n "${CHECK}${RUN_TEST}" ]; then
      x="t.${TEST_NAME}-${tid}"
echo >&2 CHECK $TID 11
      if :; then
         y=test-out
echo >&2 CHECK $TID 20
echo >&2 CHECK $TID 21
         if [ -n "${y}" ]; then
echo >&2 CHECK $TID 22
            if : ; then
echo >&2 CHECK $TID 22.5
# NOTE: REMOVE THIS (or use true(1)) AND WORKS directly and via bmake (mksh)
# (env -i regardless, "false" also is not it, whatever)
env -i ls -latr /NONEXISTENT
echo >&2 CHECK $SHELL $TID 22.8
# NOTE: monitor should be "on" here
set -o >&2
echo >&2 CHECK $TID 23
            fi
echo >&2 CHECK $TID 30
         fi
echo >&2 CHECK $TID 35
      fi
   fi
echo >&2 CHECK $TID 100
}

check_ex0() {
   # $1=test name [$2=status]
   __qm__=${?}
   [ ${#} -gt 1 ] && __qm__=${2}

   TESTS_PERFORMED=`add ${TESTS_PERFORMED} 1`

   if [ ${__qm__} -ne 0 ]; then
      ESTAT=1
      t_echoerr "${1}: unexpected non-0 exit status: ${__qm__}"
      TESTS_FAILED=`add ${TESTS_FAILED} 1`
   else
      t_echook "${1}"
      TESTS_OK=`add ${TESTS_OK} 1`
   fi
}

check_exn0() {
   # $1=test name [$2=status]
   __qm__=${?}
   [ ${#} -gt 1 ] && __qm__=${2}
   [ ${#} -gt 2 ] && __expect__=${3} || __expect__=

   TESTS_PERFORMED=`add ${TESTS_PERFORMED} 1`

   if [ ${__qm__} -eq 0 ]; then
      ESTAT=1
      t_echoerr "${1}: unexpected 0 exit status: ${__qm__}"
      TESTS_FAILED=`add ${TESTS_FAILED} 1`
   elif [ -n "${__expect__}" ] && [ ${__expect__} -ne ${__qm__} ]; then
      ESTAT=1
      t_echoerr "${1}: unexpected exit status: ${__qm__} != ${__expected__}"
      TESTS_FAILED=`add ${TESTS_FAILED} 1`
   else
      t_echook "${1}"
      TESTS_OK=`add ${TESTS_OK} 1`
   fi
}

color_init() {
   :
}

if ( [ "$((1 + 1))" = 2 ] ) >/dev/null 2>&1; then
   add() {
      echo "$((${1} + ${2}))"
   }
else
   add() {
      ${awk} 'BEGIN{print '${1}' + '${2}'}'
   }
fi

if ( [ "$((2 % 3))" = 2 ] ) >/dev/null 2>&1; then
   modulo() {
      echo "$((${1} % ${2}))"
   }
else
   modulo() {
      ${awk} 'BEGIN{print '${1}' % '${2}'}'
   }
fi

have_feat() {
   ( "${RAWMAILX}" ${ARGS} -X'echo $features' -Xx |
      ${grep} +${1}, ) >/dev/null 2>&1
}

t_X_errexit() {
   t_prolog "${@}"

   </dev/null ${MAILX} ${ARGS} -Snomemdebug \
         -X'echo one' -X' echos nono ' -X'echo two' \
      > ./t1 2>&1
echo >&2 PRE T1 CHEK
   check 1 0 ./t1 '2700500141 51'
echo >&2 AFTER T1 CHEK
t_epilog "$@"
return
}

ssec=$SECONDS
      jobreaper_start
   jspawn X_errexit
jsync 1
      jobreaper_stop
esec=$SECONDS

exit ${ESTAT}
# s-sh-mode
