#!/usr/bin/env bash
# scripts/focus/NAME.tsv, the table of `scripts/ci.sh affected --focus` (one file for each regression test, and _hot.tsv): for each regression test, the functions of src/ it executes, measured with gcov.
#
#   scripts/focus-gen.sh [TEST...]    PostgreSQL 18 (SIFT_TMP, SIFT_PORT as scripts/pgtest.sh); no names: every test of test/sql and the hot list is made again
#
# A coverage build in a private copy of the tracked sources, one cluster, the tests one after the other with the counters reset before each. What the
# library executes on load (CREATE EXTENSION) is left out, and so is every function that more than HOT_PCT (10) percent of the tests execute: a change
# there can break any of them, `affected --focus` runs the tests of the area for it. With names, only their rows are replaced (a new test registers with
# `scripts/focus-gen.sh NAME`; scripts/ci.sh --selftest fails on a test without a row); the hot list stays as it is.
((BASH_VERSINFO[0] >= 4)) || { echo 'needs bash 4+ (brew install bash)' >&2; exit 1; }
set -euo pipefail
REPO=$(cd "$(dirname "$0")/.." && pwd)
source "$REPO/scripts/source-snapshot.sh"
TESTS=("$@")
(( ${#TESTS[@]} )) || mapfile -t TESTS < <(cd "$REPO/test/sql" && ls ./*.sql | sed 's|^\./||; s|\.sql$||')
WORK=${SIFT_TMP:-/tmp/sift-focus-$$}/focus
rm -rf "$WORK" && mkdir -p "$WORK/tree" "$WORK/cov"
copy_git_sources "$REPO" "$WORK/tree"
cd "$WORK/tree"
export COPT=--coverage SHLIB_LINK=--coverage SIFT_TMP=$WORK/cluster SIFT_ROOT=$WORK/root SIFT_SUDO=
unset SANITIZE SIFT_INITDB_TEMPLATE INITDB_TEMPLATE
trap 'scripts/pgtest.sh stop >/dev/null 2>&1 || true; [[ -n ${KEEP:-} ]] || rm -rf "$WORK"' EXIT
scripts/pgtest.sh install >"$WORK/build.log" 2>&1 || { tail -n 20 "$WORK/build.log" >&2; exit 1; }
scripts/pgtest.sh start >/dev/null

# the functions run since the counters were reset, as JSON on stdout: {"src/agg/cte/hook.c": ["cte_floor", ...]}
executed() {
	local f
	f=$(find src -name '*.gcda')
	[[ -n $f ]] || { echo '{}'; return; }
	# shellcheck disable=SC2086
	gcov --json-format --stdout $f 2>/dev/null | python3 -I -c '
import json, re, sys
out, dec, s, i = {}, json.JSONDecoder(), sys.stdin.read(), 0
while True:
    while i < len(s) and s[i].isspace(): i += 1
    if i >= len(s): break
    o, i = dec.raw_decode(s, i)
    for f in o.get("files", []):
        if not f["file"].startswith("src/"): continue
        for fn in f.get("functions", []):
            if fn.get("execution_count", 0) > 0:
                out.setdefault(f["file"], set()).add(re.sub(r"(\.(isra|constprop|part|cold|lto_priv)(\.[0-9]+)?)+$", "", fn["name"]))
json.dump({k: sorted(v) for k, v in out.items()}, sys.stdout)'
}
reset() { find src -name '*.gcda' -delete; }

reset
scripts/pgtest.sh psql -c 'CREATE EXTENSION pg_sift' -c 'DROP EXTENSION pg_sift' >/dev/null
executed >"$WORK/load.json"
for t in "${TESTS[@]}"; do
	reset
	nice scripts/pgtest.sh regress "$t" >/dev/null 2>&1 || echo "focus-gen: $t failed (its rows are what ran)" >&2
	executed >"$WORK/cov/$t.json"
done

OLD=$REPO/scripts/focus ALL=$(( ${#TESTS[@]} == $(ls "$REPO/test/sql"/*.sql | wc -l) )) HOT_PCT=${HOT_PCT:-10} python3 -I - "$WORK" "${TESTS[@]}" <<'PY'
import json, os, sys
work, tests = sys.argv[1], sys.argv[2:]
load = {(f, g) for f, gs in json.load(open(work + "/load.json")).items() for g in gs}
rows, hot = {}, set()
old = os.environ["OLD"]
os.makedirs(old, exist_ok=True)
for name in os.listdir(old):
    for line in open(old + "/" + name):
        k, _, v = line.rstrip("\n").partition("\t")
        if k == "=hot": hot = set(v.split())
        else: rows[k] = set(v.split())
new = {}
for t in tests:
    new[t] = {f + ":" + g for f, gs in json.load(open("%s/cov/%s.json" % (work, t))).items() for g in gs if (f, g) not in load}
if os.environ["ALL"] == "1":
    rows = {}
    count = {}
    for fs in new.values():
        for x in fs: count[x] = count.get(x, 0) + 1
    hot = {x for x, c in count.items() if c > len(new) * int(os.environ["HOT_PCT"]) / 100}
for t, fs in new.items(): rows[t] = fs - hot
# a row is TEST<TAB>FILE:FUNCTION...: the functions of src/ the test executes (gcov) without the ones that more than 10 percent of the tests execute (the =hot row, _hot.tsv)
for name in os.listdir(old): os.remove(old + "/" + name)
with open(old + "/_hot.tsv", "w") as out: out.write("=hot\t" + " ".join(sorted(hot)) + "\n")
for t in rows:
    with open(old + "/" + t + ".tsv", "w") as out: out.write(t + "\t" + " ".join(sorted(rows[t])) + "\n")
PY
echo "focus-gen: ${#TESTS[@]} tests, scripts/focus/ written"
