feat: add runner conversion scripts and strengthen cutover automation

This commit is contained in:
S
2026-03-04 13:32:06 -06:00
parent e624885bb9
commit c2087d5087
43 changed files with 6995 additions and 42 deletions

View File

@@ -0,0 +1,239 @@
#!/usr/bin/env bash
# test-test-quality-gate.sh — Integration-style tests for validate-test-quality.sh.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
PASS_COUNT=0
FAIL_COUNT=0
declare -a TMP_REPOS=()
log() {
echo "[test-quality-test] $*"
}
pass() {
PASS_COUNT=$((PASS_COUNT + 1))
log "PASS: $*"
}
fail() {
FAIL_COUNT=$((FAIL_COUNT + 1))
log "FAIL: $*"
}
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Missing required command: $1"
exit 1
fi
}
run_expect_success() {
local label="$1"
shift
if "$@" >/tmp/test-quality-gate.out 2>&1; then
pass "$label"
else
fail "$label"
cat /tmp/test-quality-gate.out
fi
}
run_expect_failure() {
local label="$1"
shift
if "$@" >/tmp/test-quality-gate.out 2>&1; then
fail "$label (expected failure but command succeeded)"
cat /tmp/test-quality-gate.out
else
pass "$label"
fi
}
create_fixture_repo() {
local repo
repo="$(mktemp -d)"
mkdir -p "$repo/scripts" \
"$repo/audit" \
"$repo/androidApp/src/androidTest/kotlin/example" \
"$repo/iosApp/iosAppUITests"
cp "$PROJECT_ROOT/scripts/validate-test-quality.sh" "$repo/scripts/"
chmod +x "$repo/scripts/validate-test-quality.sh"
cat > "$repo/androidApp/src/androidTest/kotlin/example/ExampleUiTest.kt" <<'EOF'
package example
import org.junit.Test
class ExampleUiTest {
@Test
fun usesAntiPatternsForFixture() {
Thread.sleep(5)
try {
// fixture-only
} catch (e: AssertionError) {
// fixture-only
}
}
}
EOF
cat > "$repo/iosApp/iosAppUITests/ExampleUiTests.swift" <<'EOF'
import XCTest
final class ExampleUiTests: XCTestCase {
func testFixtureUsesAntiPatterns() {
sleep(1)
if XCUIApplication().buttons["Example"].exists {
XCTAssertTrue(true)
}
}
}
EOF
cat > "$repo/audit/test-quality-baseline.json" <<'EOF'
{
"version": 1,
"generated_at": "2026-02-20T16:00:00Z",
"metrics": [
{
"id": "android_thread_sleep_calls",
"description": "Android Thread.sleep",
"mode": "rg",
"root": "androidApp/src/androidTest",
"glob": "*.kt",
"pattern": "Thread\\.sleep\\(",
"baseline": 1,
"allowed_growth": 0
},
{
"id": "android_assertionerror_catches",
"description": "Android AssertionError catches",
"mode": "rg",
"root": "androidApp/src/androidTest",
"glob": "*.kt",
"pattern": "catch \\([^\\)]*AssertionError",
"baseline": 1,
"allowed_growth": 0
},
{
"id": "ios_sleep_calls",
"description": "iOS sleep calls",
"mode": "rg",
"root": "iosApp/iosAppUITests",
"glob": "*.swift",
"pattern": "\\bsleep\\(",
"baseline": 1,
"allowed_growth": 0
},
{
"id": "ios_conditional_exists_guards_in_test_bodies",
"description": "iOS conditional exists checks in test bodies",
"mode": "swift_test_body_pattern",
"root": "iosApp/iosAppUITests",
"glob": "*.swift",
"pattern": "if[[:space:]]+[^\\n]*\\.exists",
"baseline": 1,
"allowed_growth": 0
},
{
"id": "ios_noop_assert_true",
"description": "iOS no-op assertTrue(true) in test bodies",
"mode": "swift_test_body_pattern",
"root": "iosApp/iosAppUITests",
"glob": "*.swift",
"pattern": "XCTAssertTrue\\(true\\)",
"baseline": 1,
"allowed_growth": 0
},
{
"id": "ios_empty_test_bodies",
"description": "iOS empty or comment-only test bodies",
"mode": "rg_multiline",
"root": "iosApp/iosAppUITests",
"glob": "*.swift",
"pattern": "(?s)func\\s+test[[:alnum:]_]+\\s*\\([^)]*\\)\\s*(?:throws\\s*)?\\{\\s*(?:(?://[^\\n]*\\n)\\s*)*\\}",
"baseline": 0,
"allowed_growth": 0
},
{
"id": "ios_placeholder_test_markers",
"description": "iOS placeholder markers in test bodies",
"mode": "swift_test_body_pattern",
"root": "iosApp/iosAppUITests",
"glob": "*.swift",
"pattern": "(TODO|FIXME|placeholder|no-op)",
"baseline": 0,
"allowed_growth": 0
}
]
}
EOF
TMP_REPOS+=("$repo")
echo "$repo"
}
test_baseline_pass() {
local repo
repo="$(create_fixture_repo)"
run_expect_success "validate-test-quality passes when metrics match baseline" \
bash -lc "cd '$repo' && scripts/validate-test-quality.sh"
}
test_growth_fails() {
local repo
repo="$(create_fixture_repo)"
echo "Thread.sleep(10)" >> "$repo/androidApp/src/androidTest/kotlin/example/ExampleUiTest.kt"
run_expect_failure "validate-test-quality fails when metric grows past threshold" \
bash -lc "cd '$repo' && scripts/validate-test-quality.sh"
}
test_allowed_growth_passes() {
local repo
repo="$(create_fixture_repo)"
local tmp
tmp="$(mktemp)"
jq '(.metrics[] | select(.id == "ios_sleep_calls") | .allowed_growth) = 1' \
"$repo/audit/test-quality-baseline.json" > "$tmp"
mv "$tmp" "$repo/audit/test-quality-baseline.json"
echo "sleep(1)" >> "$repo/iosApp/iosAppUITests/ExampleUiTests.swift"
run_expect_success "validate-test-quality honors allowed_growth threshold" \
bash -lc "cd '$repo' && scripts/validate-test-quality.sh"
}
main() {
require_cmd jq
require_cmd rg
require_cmd awk
test_baseline_pass
test_growth_fails
test_allowed_growth_passes
log "Summary: pass=$PASS_COUNT fail=$FAIL_COUNT"
if [[ "$FAIL_COUNT" -gt 0 ]]; then
exit 1
fi
return 0
}
cleanup() {
local repo
for repo in "${TMP_REPOS[@]:-}"; do
[[ -d "$repo" ]] && rm -rf "$repo"
done
rm -f /tmp/test-quality-gate.out
return 0
}
trap cleanup EXIT
main "$@"