#!/bin/sh

# ps tests
#
# Assumptions:
# - PS_BIN points to the newly built ps
# - TEST_HELPER points to tests/spin_helper.c compiled into out/tests/spin_helper

# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'

fail() {
    printf "${RED}FAIL: %s${NC}\n" "$1" >&2
    exit 1
}

pass() {
    printf "${GREEN}PASS: %s${NC}\n" "$1"
}

# Build a ps-specific helper in shared out/ roots to avoid name collisions
# with other test suites (e.g. pkill also has a spin_helper).
HELPER_DIR="$(dirname "$0")"
OUT_DIR="$(dirname "${PS_BIN}")"
HELPER_BIN="${OUT_DIR}/ps_spin_helper"
HELPER_SRC="${HELPER_DIR}/spin_helper.c"
CC_BIN="${CC:-cc}"

if [ ! -x "${HELPER_BIN}" ] || [ "${HELPER_SRC}" -nt "${HELPER_BIN}" ]; then
    "${CC_BIN}" -O2 "${HELPER_SRC}" -o "${HELPER_BIN}" || fail "failed to compile spin_helper"
fi

# 1. Basic sanity: run without arguments
# Should output at least headers and some processes.
${PS_BIN} > /tmp/ps_out || fail "ps failed to run"
grep -q "PID" /tmp/ps_out || fail "ps output lacks PID header"
grep -q "COMMAND" /tmp/ps_out || fail "ps output lacks COMMAND header"
pass "Basic sanity (no args)"

# 2. Process selection: -p
# Start a helper, then find it with ps.
${HELPER_BIN} test-ps-select &
HELPER_PID=$!
sleep 0.1
${PS_BIN} -p ${HELPER_PID} > /tmp/ps_p_out || fail "ps -p failed"
grep -q "${HELPER_PID}" /tmp/ps_p_out || fail "ps -p did not find helper pid ${HELPER_PID}"
kill ${HELPER_PID}
pass "Process selection (-p pid)"

# 3. Format selection: -j
${HELPER_BIN} test-ps-fmt-j &
HELPER_PID=$!
sleep 0.1
${PS_BIN} -j -p ${HELPER_PID} > /tmp/ps_j_out || fail "ps -j failed"
grep -q "USER" /tmp/ps_j_out || fail "ps -j output lacks USER header"
grep -q "PPID" /tmp/ps_j_out || fail "ps -j output lacks PPID header"
grep -q "SID" /tmp/ps_j_out || fail "ps -j output lacks SID header"
kill ${HELPER_PID}
pass "Format selection (-j)"

# 4. Format selection: -u
${HELPER_BIN} test-ps-fmt-u &
HELPER_PID=$!
sleep 0.1
${PS_BIN} -u -p ${HELPER_PID} > /tmp/ps_u_out || fail "ps -u failed"
grep -q "%CPU" /tmp/ps_u_out || fail "ps -u output lacks %CPU header"
grep -q "%MEM" /tmp/ps_u_out || fail "ps -u output lacks %MEM header"
grep -q "VSZ" /tmp/ps_u_out || fail "ps -u output lacks VSZ header"
grep -q "RSS" /tmp/ps_u_out || fail "ps -u output lacks RSS header"
kill ${HELPER_PID}
pass "Format selection (-u)"

# 5. Format selection: -o (custom)
${HELPER_BIN} test-ps-fmt-o &
HELPER_PID=$!
sleep 0.1
${PS_BIN} -o pid,comm,nice -p ${HELPER_PID} > /tmp/ps_o_out || fail "ps -o failed"
grep -q "PID" /tmp/ps_o_out || fail "ps -o output lacks PID header"
grep -q "COMMAND" /tmp/ps_o_out || fail "ps -o output lacks COMMAND header"
grep -q "NI" /tmp/ps_o_out || fail "ps -o output lacks NI header"
# First line is header, second line should contain the PID.
grep -q "${HELPER_PID}" /tmp/ps_o_out || fail "ps -o did not find helper pid"
kill ${HELPER_PID}
pass "Format selection (-o pid,comm,nice)"

# 6. Sorting: -m (by memory) and -r (by cpu)
${PS_BIN} -m -A > /dev/null || fail "ps -m -A failed"
${PS_BIN} -r -A > /dev/null || fail "ps -r -A failed"
pass "Sorting (-m, -r)"

# 7. Negative test: invalid pid
${PS_BIN} -p 999999 > /tmp/ps_neg_out 2>&1
# FreeBSD ps should just print header and exits 1 if no matches found?
# Actually, standard behavior varies. Let's check status.
EXIT_CODE=$?
if [ ${EXIT_CODE} -ne 1 ]; then
    # In some versions, no match means exit 1. In others, exit 0.
    # FreeBSD ps.c: main() calls exit(1) if nkept == 0.
    fail "ps -p invalid_pid should exit 1 (got ${EXIT_CODE})"
fi
pass "Negative test (invalid pid)"

# 8. Negative test: invalid flag
${PS_BIN} -Y > /dev/null 2>&1
if [ $? -eq 0 ]; then
    fail "ps -Y should fail"
fi
pass "Negative test (invalid flag)"

# 9. Width test: -w
${HELPER_BIN} test-ps-width-very-long-name-to-check-truncation &
HELPER_PID=$!
sleep 0.1
# Record output without -w (should truncate or at least be limited)
${PS_BIN} -u -p ${HELPER_PID} > /tmp/ps_w1
# Record output with -ww (no limit)
${PS_BIN} -u -ww -p ${HELPER_PID} > /tmp/ps_w2
# Compare -w2 should be longer or equal to -w1 in terms of line length
LEN1=$(awk '{ print length }' /tmp/ps_w1 | sort -nr | head -1)
LEN2=$(awk '{ print length }' /tmp/ps_w2 | sort -nr | head -1)
if [ "${LEN2}" -lt "${LEN1}" ]; then
    fail "ps -ww output shorter than normal output"
fi
kill ${HELPER_PID}
pass "Width control (-w, -ww)"

# 10. Headers control: -h
# Start 30 helpers safely.
I=1
while [ ${I} -le 30 ]; do
    ${HELPER_BIN} test-ps-h-${I} &
    H_PIDS="${H_PIDS} $!"
    I=$((I+1))
done
sleep 0.1
# Run ps -h -A (repeat headers every 22 lines or so)
${PS_BIN} -h -A | grep "PID" | wc -l > /tmp/ps_h_count
COUNT=$(cat /tmp/ps_h_count)
if [ "${COUNT}" -lt 2 ]; then
    fail "ps -h should repeat headers (got ${COUNT} headers for 30+ processes)"
fi
kill ${H_PIDS}
pass "Header repetition (-h)"

# Clean up
rm -f /tmp/ps_*
printf "${GREEN}All tests passed!${NC}\n"
exit 0
