save progress
This commit is contained in:
299
.gitea/scripts/test/run-test-category.sh
Normal file
299
.gitea/scripts/test/run-test-category.sh
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/usr/bin/env bash
|
||||
# Test Category Runner
|
||||
# Sprint: CI/CD Enhancement - Script Consolidation
|
||||
#
|
||||
# Purpose: Run tests for a specific category across all test projects
|
||||
# Usage: ./run-test-category.sh <category> [options]
|
||||
#
|
||||
# Options:
|
||||
# --fail-on-empty Fail if no tests are found for the category
|
||||
# --collect-coverage Collect code coverage data
|
||||
# --verbose Show detailed output
|
||||
#
|
||||
# Exit Codes:
|
||||
# 0 - Success (all tests passed or no tests found)
|
||||
# 1 - One or more tests failed
|
||||
# 2 - Invalid usage
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source shared libraries if available
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||
|
||||
if [[ -f "$REPO_ROOT/devops/scripts/lib/logging.sh" ]]; then
|
||||
source "$REPO_ROOT/devops/scripts/lib/logging.sh"
|
||||
else
|
||||
# Minimal logging fallback
|
||||
log_info() { echo "[INFO] $*"; }
|
||||
log_error() { echo "[ERROR] $*" >&2; }
|
||||
log_debug() { [[ -n "${DEBUG:-}" ]] && echo "[DEBUG] $*"; }
|
||||
log_step() { echo "==> $*"; }
|
||||
fi
|
||||
|
||||
if [[ -f "$REPO_ROOT/devops/scripts/lib/exit-codes.sh" ]]; then
|
||||
source "$REPO_ROOT/devops/scripts/lib/exit-codes.sh"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Constants
|
||||
# =============================================================================
|
||||
|
||||
readonly FIND_PATTERN='\( -name "*.Tests.csproj" -o -name "*UnitTests.csproj" -o -name "*SmokeTests.csproj" -o -name "*FixtureTests.csproj" -o -name "*IntegrationTests.csproj" \)'
|
||||
readonly EXCLUDE_PATHS='! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/bin/*" ! -path "*/obj/*"'
|
||||
readonly EXCLUDE_FILES='! -name "StellaOps.TestKit.csproj" ! -name "*Testing.csproj"'
|
||||
|
||||
# =============================================================================
|
||||
# Functions
|
||||
# =============================================================================
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") <category> [options]
|
||||
|
||||
Run tests for a specific test category across all test projects.
|
||||
|
||||
Arguments:
|
||||
category Test category (Unit, Architecture, Contract, Integration,
|
||||
Security, Golden, Performance, Benchmark, AirGap, Chaos,
|
||||
Determinism, Resilience, Observability)
|
||||
|
||||
Options:
|
||||
--fail-on-empty Exit with error if no tests found for the category
|
||||
--collect-coverage Collect XPlat Code Coverage data
|
||||
--verbose Show detailed test output
|
||||
--results-dir DIR Custom results directory (default: ./TestResults/<category>)
|
||||
--help Show this help message
|
||||
|
||||
Environment Variables:
|
||||
DOTNET_VERSION .NET SDK version (default: uses installed version)
|
||||
TZ Timezone (should be UTC for determinism)
|
||||
|
||||
Examples:
|
||||
$(basename "$0") Unit
|
||||
$(basename "$0") Integration --collect-coverage
|
||||
$(basename "$0") Performance --results-dir ./perf-results
|
||||
EOF
|
||||
}
|
||||
|
||||
find_test_projects() {
|
||||
local search_dir="${1:-src}"
|
||||
|
||||
# Use eval to properly expand the find pattern
|
||||
eval "find '$search_dir' $FIND_PATTERN -type f $EXCLUDE_PATHS $EXCLUDE_FILES" | sort
|
||||
}
|
||||
|
||||
sanitize_project_name() {
|
||||
local proj="$1"
|
||||
# Replace slashes with underscores, remove .csproj extension
|
||||
echo "$proj" | sed 's|/|_|g' | sed 's|\.csproj$||'
|
||||
}
|
||||
|
||||
run_tests() {
|
||||
local category="$1"
|
||||
local results_dir="$2"
|
||||
local collect_coverage="$3"
|
||||
local verbose="$4"
|
||||
local fail_on_empty="$5"
|
||||
|
||||
local passed=0
|
||||
local failed=0
|
||||
local skipped=0
|
||||
local no_tests=0
|
||||
|
||||
mkdir -p "$results_dir"
|
||||
|
||||
local projects
|
||||
projects=$(find_test_projects "$REPO_ROOT/src")
|
||||
|
||||
if [[ -z "$projects" ]]; then
|
||||
log_error "No test projects found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local project_count
|
||||
project_count=$(echo "$projects" | grep -c '.csproj' || echo "0")
|
||||
log_info "Found $project_count test projects"
|
||||
|
||||
local category_lower
|
||||
category_lower=$(echo "$category" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
while IFS= read -r proj; do
|
||||
[[ -z "$proj" ]] && continue
|
||||
|
||||
local proj_name
|
||||
proj_name=$(sanitize_project_name "$proj")
|
||||
local trx_name="${proj_name}-${category_lower}.trx"
|
||||
|
||||
# GitHub Actions grouping
|
||||
if [[ -n "${GITHUB_ACTIONS:-}" ]]; then
|
||||
echo "::group::Testing $proj ($category)"
|
||||
else
|
||||
log_step "Testing $proj ($category)"
|
||||
fi
|
||||
|
||||
# Build dotnet test command
|
||||
local cmd="dotnet test \"$proj\""
|
||||
cmd+=" --filter \"Category=$category\""
|
||||
cmd+=" --configuration Release"
|
||||
cmd+=" --logger \"trx;LogFileName=$trx_name\""
|
||||
cmd+=" --results-directory \"$results_dir\""
|
||||
|
||||
if [[ "$collect_coverage" == "true" ]]; then
|
||||
cmd+=" --collect:\"XPlat Code Coverage\""
|
||||
fi
|
||||
|
||||
if [[ "$verbose" == "true" ]]; then
|
||||
cmd+=" --verbosity normal"
|
||||
else
|
||||
cmd+=" --verbosity minimal"
|
||||
fi
|
||||
|
||||
# Execute tests
|
||||
local exit_code=0
|
||||
eval "$cmd" 2>&1 || exit_code=$?
|
||||
|
||||
if [[ $exit_code -eq 0 ]]; then
|
||||
# Check if TRX was created (tests actually ran)
|
||||
if [[ -f "$results_dir/$trx_name" ]]; then
|
||||
((passed++))
|
||||
log_info "PASS: $proj"
|
||||
else
|
||||
((no_tests++))
|
||||
log_debug "SKIP: $proj (no $category tests)"
|
||||
fi
|
||||
else
|
||||
# Check if failure was due to no tests matching the filter
|
||||
if [[ -f "$results_dir/$trx_name" ]]; then
|
||||
((failed++))
|
||||
log_error "FAIL: $proj"
|
||||
else
|
||||
((no_tests++))
|
||||
log_debug "SKIP: $proj (no $category tests or build error)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Close GitHub Actions group
|
||||
if [[ -n "${GITHUB_ACTIONS:-}" ]]; then
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
|
||||
done <<< "$projects"
|
||||
|
||||
# Generate summary
|
||||
log_info ""
|
||||
log_info "=========================================="
|
||||
log_info "$category Test Summary"
|
||||
log_info "=========================================="
|
||||
log_info "Passed: $passed"
|
||||
log_info "Failed: $failed"
|
||||
log_info "No Tests: $no_tests"
|
||||
log_info "Total: $project_count"
|
||||
log_info "=========================================="
|
||||
|
||||
# GitHub Actions summary
|
||||
if [[ -n "${GITHUB_ACTIONS:-}" ]]; then
|
||||
{
|
||||
echo "## $category Test Summary"
|
||||
echo ""
|
||||
echo "| Metric | Count |"
|
||||
echo "|--------|-------|"
|
||||
echo "| Passed | $passed |"
|
||||
echo "| Failed | $failed |"
|
||||
echo "| No Tests | $no_tests |"
|
||||
echo "| Total Projects | $project_count |"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
# Determine exit code
|
||||
if [[ $failed -gt 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$fail_on_empty" == "true" ]] && [[ $passed -eq 0 ]]; then
|
||||
log_error "No tests found for category: $category"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Main
|
||||
# =============================================================================
|
||||
|
||||
main() {
|
||||
local category=""
|
||||
local results_dir=""
|
||||
local collect_coverage="false"
|
||||
local verbose="false"
|
||||
local fail_on_empty="false"
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--fail-on-empty)
|
||||
fail_on_empty="true"
|
||||
shift
|
||||
;;
|
||||
--collect-coverage)
|
||||
collect_coverage="true"
|
||||
shift
|
||||
;;
|
||||
--verbose|-v)
|
||||
verbose="true"
|
||||
shift
|
||||
;;
|
||||
--results-dir)
|
||||
results_dir="$2"
|
||||
shift 2
|
||||
;;
|
||||
-*)
|
||||
log_error "Unknown option: $1"
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$category" ]]; then
|
||||
category="$1"
|
||||
else
|
||||
log_error "Unexpected argument: $1"
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate category
|
||||
if [[ -z "$category" ]]; then
|
||||
log_error "Category is required"
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Validate category name
|
||||
local valid_categories="Unit Architecture Contract Integration Security Golden Performance Benchmark AirGap Chaos Determinism Resilience Observability"
|
||||
if ! echo "$valid_categories" | grep -qw "$category"; then
|
||||
log_error "Invalid category: $category"
|
||||
log_error "Valid categories: $valid_categories"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Set default results directory
|
||||
if [[ -z "$results_dir" ]]; then
|
||||
results_dir="./TestResults/$category"
|
||||
fi
|
||||
|
||||
log_info "Running $category tests..."
|
||||
log_info "Results directory: $results_dir"
|
||||
|
||||
run_tests "$category" "$results_dir" "$collect_coverage" "$verbose" "$fail_on_empty"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user