#!/usr/bin/env bash
# Fake cargo for tools/test-tools.sh, only what tools/cargo-mutants.sh calls:
#   cargo locate-project --workspace --message-format plain
#   cargo test --no-run
#   cargo test -- <filter> [<harness args>] --list      (prints "<n> tests, 0 benchmarks" per test binary)
#   cargo mutants [--list] --in-diff <diff> [-f <file>] [-F <re>]... [--shard k/n] [-o <dir>] [...] [-- <filter> [-- <harness args>]]
# FAKE_CM_TABLE: tab-separated  <mutant name> <outcome under a filtered run> <outcome under the full suite>
#   (caught|missed|timeout|unviable). A run with -F is the full suite; a run with -f and a filter is filtered.
# FAKE_CM_TESTS: tab-separated  <filter> <tests the filter selects>  (default 1)
# FAKE_CM_RED=1|2: the stage-1 or stage-2 run fails its baseline (exit 4, no output).
# FAKE_CM_KILLS: a path a run creates (the watchdog's YGG_KILLED flag appearing mid-job).
# FAKE_CM_LOG: appended one line per run (stage, filter, harness args, open fds from 10 up).
# FAKE_WSROOT: the workspace root relative to the repository root, with a trailing slash ('' = the repository root).
case ${1:-} in
  locate-project) echo "$(git rev-parse --show-toplevel)/${FAKE_WSROOT:-}Cargo.toml"; exit 0 ;;
  test)
    shift; for a; do [ "$a" != --no-run ] || exit 0; done
    [ "${1:-}" = -- ] || { echo "fake cargo test: unsupported arguments" >&2; exit 64; }
    shift; f=$1; shift; h=()
    for a; do [ "$a" = --list ] || h+=("$a"); done
    echo "count filter=$f harness=${h[*]}" >>"${FAKE_CM_LOG:?}"
    n=$(awk -F'\t' -v f="$f" '$1 == f { print $2 }' "${FAKE_CM_TESTS:-/dev/null}")
    printf '%s tests, 0 benchmarks\n0 tests, 0 benchmarks\n' "${n:-1}"; exit 0 ;;
  mutants) shift ;;
  *) echo "fake cargo: unsupported ${1:-}" >&2; exit 127 ;;
esac
list=0; mult=''; mintimeout=''; diff=''; ffile=''; res=(); shard=''; out=''; tail=()
while [ $# -gt 0 ]; do
  case $1 in
    --list) list=1 ;;
    --in-diff) diff=$2; shift ;;
    -f) ffile=$2; shift ;;
    -F) res+=("$2"); shift ;;
    --shard) shard=$2; shift ;;
    -o) out=$2; shift ;;
    --jobs) shift ;;
    --minimum-test-timeout) mintimeout=$2; shift ;;
    --timeout-multiplier) mult=$2; shift ;;
    --) shift; tail=("$@"); break ;;
    *) echo "fake cargo mutants: unknown argument $1" >&2; exit 64 ;;
  esac
  shift
done
[ -n "$diff" ] && [ -f "$diff" ] || { echo "fake cargo mutants: no readable --in-diff" >&2; exit 64; }
# the mutants of the files the diff touches (paths as the diff prints them: relative to the workspace root)
touched=$(sed -n 's|^+++ b/||p' "$diff")
sel=$(awk -F'\t' 'NR == FNR { t[$0] = 1; next } { f = $1; sub(/:.*/, "", f); if (f in t) print $1 "\t" $2 "\t" $3 }' <(printf '%s\n' "$touched") "${FAKE_CM_TABLE:?}")
if [ -n "$ffile" ]; then sel=$(awk -F'\t' -v f="$ffile" '{ n = $1; sub(/:.*/, "", n); if (n == f) print }' <<<"$sel"); fi
if [ "${#res[@]}" -gt 0 ]; then
  keep=$(cut -f1 <<<"$sel" | perl -e '@r = map { qr/$_/ } @ARGV; while (<STDIN>) { chomp; for $r (@r) { if (/$r/) { print "$_\n"; last } } }' "${res[@]}")
  sel=$(awk -F'\t' 'NR == FNR { k[$0] = 1; next } $1 in k' <(printf '%s\n' "$keep") <(printf '%s\n' "$sel"))
fi
if [ -n "$shard" ]; then
  sel=$(awk -F'\t' -v k="${shard%/*}" -v n="${shard#*/}" '(NR - 1) % n == k' <<<"$sel")
fi
if [ "$list" = 1 ]; then cut -f1 <<<"$sel" | awk 'NF'; exit 0; fi
stage=1; [ "${#res[@]}" -eq 0 ] || stage=2
filter=''; harness=()
if [ "${#tail[@]}" -gt 0 ] && [ "${tail[0]}" != -- ]; then filter=${tail[0]}; fi
for i in "${!tail[@]}"; do if [ "${tail[$i]}" = -- ]; then harness=("${tail[@]:$((i + 1))}"); break; fi; done
open=$(perl -MPOSIX -e '$n = 0; for (10 .. 40) { my $d = POSIX::dup($_); if (defined $d) { $n++; POSIX::close($d) } } print $n')
[ -z "${FAKE_CM_KILLS:-}" ] || touch "$FAKE_CM_KILLS"   # a watchdog kill arriving while a run is going
echo "stage=$stage filter=$filter harness=${harness[*]} openfds=$open mult=$mult min=$mintimeout" >>"${FAKE_CM_LOG:?}"
[ "${FAKE_CM_RED:-}" != "$stage" ] || { echo "baseline failed" >&2; exit 4; }
mkdir -p "$out/mutants.out/log"
col=3; [ "$stage" = 2 ] || col=2
for o in caught missed timeout unviable; do
  awk -F'\t' -v c="$col" -v o="$o" '{ x = $c; if (x == o) print $1 }' <<<"$sel" >"$out/mutants.out/$o.txt"
done
# a run that selected no mutant (the awk above printed empty lines): drop them
for o in caught missed timeout unviable; do sed -i '/^$/d' "$out/mutants.out/$o.txt"; done
if [ -s "$out/mutants.out/timeout.txt" ]; then exit 3; elif [ -s "$out/mutants.out/missed.txt" ]; then exit 2; fi
exit 0
