Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
cea63ef
parrallel tests
LahkLeKey May 27, 2026
155f22d
Merge pull request #1 from LahkLeKey/feat/parallel-test-suite
LahkLeKey May 27, 2026
9bf891c
fix(test): keep xdist wrapper-only and harden collection
LahkLeKey May 28, 2026
9449e24
fix(test): forward runtime passthrough and validate cursor
LahkLeKey May 30, 2026
e317e4e
fix(test): harden chunk sizing and resume behavior
LahkLeKey Jun 3, 2026
e72b5b4
fix(test): use -q instead of -qq for pytest collect-only to preserve …
LahkLeKey Jun 3, 2026
2caac75
(copilot-commit-suggestion) Potential fix for pull request finding
LahkLeKey Jun 4, 2026
2227434
chore: harden fast-test cursor handling
LahkLeKey Jun 4, 2026
dcd1475
chore: harden fast-test portability
LahkLeKey Jun 4, 2026
533e7a7
chore: harden fast-test collection
LahkLeKey Jun 4, 2026
91e873a
chore: tune fast-test output
LahkLeKey Jun 4, 2026
e55ca41
chore: harden fast-test locking
LahkLeKey Jun 4, 2026
4a73eef
chore: stream fast-test chunks
LahkLeKey Jun 4, 2026
aeb476e
chore: harden fast-test fd handling
LahkLeKey Jun 4, 2026
500c843
chore: fix fast-test help and lock
LahkLeKey Jun 4, 2026
f711540
chore: tighten fast-test locks
LahkLeKey Jun 4, 2026
f5e0a01
chore: clarify fast-test resume
LahkLeKey Jun 4, 2026
ba75dab
chore: route fast-test logs to stderr
LahkLeKey Jun 4, 2026
ec06344
chore: harden fast-test argv sizing
LahkLeKey Jun 4, 2026
abb8cb5
chore: align test wrapper naming
LahkLeKey Jun 4, 2026
48e53e1
chore: harden fast-test tempfiles
LahkLeKey Jun 4, 2026
575547b
chore: harden fast-test cursor path
LahkLeKey Jun 5, 2026
955d0e5
chore: harden pytest cache handling
LahkLeKey Jun 5, 2026
c10ae0d
chore: relax fast-test collection filter
LahkLeKey Jun 5, 2026
92abcdc
chore: tighten fast-test path guards
LahkLeKey Jun 5, 2026
6da8eb1
chore: force xdist and quiet collect
LahkLeKey Jun 5, 2026
9df0085
chore: tighten fast-test xdist args
LahkLeKey Jun 5, 2026
e4a30f0
chore: harden cursor temp handling
LahkLeKey Jun 5, 2026
b0641b9
chore: tighten fast-test temp and lock
LahkLeKey Jun 5, 2026
c86a6d4
chore: speed up arg sizing
LahkLeKey Jun 5, 2026
12a55ea
chore: harden cursor temp creation
LahkLeKey Jun 5, 2026
6e4bae2
chore: fix help and cursor guard
LahkLeKey Jun 5, 2026
a00beb4
chore: harden cursor cleanup
LahkLeKey Jun 5, 2026
15f2a06
chore: harden resume cursor access
LahkLeKey Jun 5, 2026
ef12855
chore: harden cursor read/write
LahkLeKey Jun 5, 2026
c9de4be
chore: harden lock file handling
LahkLeKey Jun 5, 2026
16e34a0
chore: fix lock pid scope
LahkLeKey Jun 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ packages = ["src/specify_cli"]
test = [
"pytest>=7.0",
"pytest-cov>=4.0",
"pytest-xdist>=3.5",
]

[tool.pytest.ini_options]
Expand Down
125 changes: 125 additions & 0 deletions scripts/dev/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env bash
Comment thread
LahkLeKey marked this conversation as resolved.
# Spec Kit local test wrapper — chunked FIFO dispatch over pytest-xdist.
#
# Design (matches the chunked-task / bounded-memory pattern):
# * Collect node ids once: `pytest --collect-only -q`.
# * Split the collection into fixed-size chunks (default 200 nodes).
# * Dispatch chunks sequentially as a FIFO queue; inside each chunk,
# pytest-xdist's `--dist=load` hands tests out one at a time to
# workers as they finish — natural FIFO progression with bounded
# in-flight memory.
# * Persist the cursor (next chunk index) to
# `.pytest_cache/fast-test-cursor` after every successful chunk so
# `--resume` continues exactly where a crash left off.
# * `--reset` clears the cursor; `--bench` reports wall-time only.
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
#
# Usage:
# scripts/dev/test.sh # full suite, chunked
# scripts/dev/test.sh --chunk-size 100
# scripts/dev/test.sh --resume # continue from cursor
# scripts/dev/test.sh --reset # clear cursor
# scripts/dev/test.sh --bench # time only, no -v
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
# scripts/dev/test.sh -- tests/test_merge.py # pass-through to pytest

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
CURSOR_FILE="$REPO_ROOT/.pytest_cache/fast-test-cursor"
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated

CHUNK_SIZE=200
RESUME=0
RESET=0
BENCH=0
PASSTHROUGH=()
RUNTIME_PASSTHROUGH=()

while (( $# )); do
case "$1" in
--chunk-size) CHUNK_SIZE="$2"; shift 2 ;;
Comment thread
mnriem marked this conversation as resolved.
Outdated
--resume) RESUME=1; shift ;;
--reset) RESET=1; shift ;;
--bench) BENCH=1; shift ;;
--) shift; PASSTHROUGH+=("$@"); break ;;
-h|--help) sed -n '2,22p' "$0"; exit 0 ;;
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
*) PASSTHROUGH+=("$1"); shift ;;
esac
done
Comment thread
LahkLeKey marked this conversation as resolved.

# Collection and execution do not share every pytest flag. Keep runtime
# passthrough aligned with user intent while stripping collect-only toggles.
for arg in "${PASSTHROUGH[@]}"; do
case "$arg" in
--collect-only|--co) ;;
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
*) RUNTIME_PASSTHROUGH+=("$arg") ;;
esac
done

if (( RESET )); then
rm -f "$CURSOR_FILE"
echo "[fast-test] cursor cleared"
exit 0
fi
Comment thread
LahkLeKey marked this conversation as resolved.

cd "$REPO_ROOT"
mkdir -p "$(dirname "$CURSOR_FILE")"
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated

# 1. Collect node ids.
echo "[fast-test] collecting tests ..."
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
COLLECT_ERR="$(mktemp)"
COLLECT_OUT="$(mktemp)"
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
if ! uv run pytest --collect-only -qq "${PASSTHROUGH[@]}" >"$COLLECT_OUT" 2>"$COLLECT_ERR"; then
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
echo "[fast-test] test collection failed" >&2
[[ -s "$COLLECT_ERR" ]] && { echo "--- collection stderr ---"; cat "$COLLECT_ERR"; } >&2
rm -f "$COLLECT_ERR" "$COLLECT_OUT"
exit 1
fi
mapfile -t NODES < <(grep -E '::' "$COLLECT_OUT" || true)
Comment thread
mnriem marked this conversation as resolved.
Outdated
rm -f "$COLLECT_ERR" "$COLLECT_OUT"
TOTAL="${#NODES[@]}"
if (( TOTAL == 0 )); then
echo "[fast-test] no tests collected" >&2
exit 1
fi

# 2. Determine starting cursor.
START=0
if (( RESUME )) && [[ -f "$CURSOR_FILE" ]]; then
RAW_START="$(tr -d '[:space:]' < "$CURSOR_FILE")"
if [[ "$RAW_START" =~ ^[0-9]+$ ]]; then
START="$RAW_START"
(( START > TOTAL )) && START="$TOTAL"
echo "[fast-test] resuming from next test index: $START"
else
Comment thread
LahkLeKey marked this conversation as resolved.
echo "[fast-test] cursor file is invalid ('$RAW_START'); starting from 0" >&2
fi
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
fi
Comment thread
LahkLeKey marked this conversation as resolved.
Comment thread
LahkLeKey marked this conversation as resolved.
Comment thread
LahkLeKey marked this conversation as resolved.

CHUNKS=$(( (TOTAL - START + CHUNK_SIZE - 1) / CHUNK_SIZE ))
echo "[fast-test] $TOTAL tests · chunk=$CHUNK_SIZE · $CHUNKS chunk(s) queued · workers=auto"
Comment thread
mnriem marked this conversation as resolved.
Outdated
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated

# 3. FIFO dispatch.
T_START=$(date +%s)
i="$START"
Comment thread
LahkLeKey marked this conversation as resolved.
chunk_idx=0
while (( i < TOTAL )); do
end=$(( i + CHUNK_SIZE ))
(( end > TOTAL )) && end="$TOTAL"
chunk_idx=$(( chunk_idx + 1 ))
echo "[fast-test] chunk $chunk_idx/$CHUNKS tests $((i+1))..$end"
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated

PYTEST_FLAGS=(-n auto --dist=load)
(( BENCH )) && PYTEST_FLAGS+=(-q) || PYTEST_FLAGS+=(--no-header -q)
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated

if ! uv run pytest "${PYTEST_FLAGS[@]}" "${RUNTIME_PASSTHROUGH[@]}" "${NODES[@]:i:CHUNK_SIZE}"; then
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
echo "[fast-test] chunk failed — cursor preserved at next test index $i (use --resume to retry)"
echo "$i" > "$CURSOR_FILE"
exit 1
fi

i="$end"
echo "$i" > "$CURSOR_FILE"
done
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated

T_END=$(date +%s)
rm -f "$CURSOR_FILE"
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
echo "[fast-test] all $TOTAL tests passed in $((T_END - T_START))s"
Comment thread
LahkLeKey marked this conversation as resolved.
Outdated
Loading