|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Script: run-unit-tests.sh |
| 4 | +# Purpose: Run CodeQL unit tests for generated queries |
| 5 | +# Usage: ./run-unit-tests.sh [OPTIONS] |
| 6 | + |
| 7 | +set -euo pipefail |
| 8 | + |
| 9 | +# Color codes for output |
| 10 | +readonly RED='\033[0;31m' |
| 11 | +readonly GREEN='\033[0;32m' |
| 12 | +readonly YELLOW='\033[1;33m' |
| 13 | +readonly BLUE='\033[0;34m' |
| 14 | +readonly NC='\033[0m' # No Color |
| 15 | + |
| 16 | +# Logging functions |
| 17 | +log_info() { |
| 18 | + echo -e "${BLUE}[INFO]${NC} $1" >&2 |
| 19 | +} |
| 20 | + |
| 21 | +log_success() { |
| 22 | + echo -e "${GREEN}[SUCCESS]${NC} $1" >&2 |
| 23 | +} |
| 24 | + |
| 25 | +log_error() { |
| 26 | + echo -e "${RED}[ERROR]${NC} $1" >&2 |
| 27 | +} |
| 28 | + |
| 29 | +log_warning() { |
| 30 | + echo -e "${YELLOW}[WARNING]${NC} $1" >&2 |
| 31 | +} |
| 32 | + |
| 33 | +# Configuration - Centralized path variables |
| 34 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 35 | +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" |
| 36 | +QLT_PROJECT_DIR="$REPO_ROOT/src/CodeQLToolkit.Core" |
| 37 | +QLT_PROJECT_FILE="$QLT_PROJECT_DIR/CodeQLToolkit.Core.csproj" |
| 38 | + |
| 39 | +# Default configuration |
| 40 | +TEST_DIR="" |
| 41 | +LANGUAGES="" |
| 42 | +VERBOSE=false |
| 43 | +HELP=false |
| 44 | + |
| 45 | +# Usage information |
| 46 | +show_help() { |
| 47 | + cat << EOF |
| 48 | +Usage: $0 [OPTIONS] |
| 49 | +
|
| 50 | +Run CodeQL unit tests for generated queries in a test directory. |
| 51 | +
|
| 52 | +OPTIONS: |
| 53 | + -d, --test-dir DIR Test directory containing language subdirectories |
| 54 | + -l, --languages LANGS Comma-separated list of languages to test (e.g., "python,java") |
| 55 | + -v, --verbose Enable verbose output |
| 56 | + -h, --help Show this help message |
| 57 | +
|
| 58 | +EXAMPLES: |
| 59 | + $0 --test-dir /tmp/qlt-test --languages python,java |
| 60 | + $0 -d /tmp/qlt-test -l python -v |
| 61 | +
|
| 62 | +DESCRIPTION: |
| 63 | + This script runs CodeQL unit tests for queries generated by the CodeQL Toolkit. |
| 64 | + It expects a directory structure like: |
| 65 | +
|
| 66 | + TEST_DIR/ |
| 67 | + ├── python/ |
| 68 | + │ └── testpack-python/ |
| 69 | + │ └── test/ |
| 70 | + └── java/ |
| 71 | + └── testpack-java/ |
| 72 | + └── test/ |
| 73 | +
|
| 74 | + The script will: |
| 75 | + 1. Execute unit tests for each language |
| 76 | + 2. Validate test results |
| 77 | + 3. Generate a summary report |
| 78 | +
|
| 79 | +EOF |
| 80 | +} |
| 81 | + |
| 82 | +# Parse command line arguments |
| 83 | +parse_args() { |
| 84 | + while [[ $# -gt 0 ]]; do |
| 85 | + case $1 in |
| 86 | + -d|--test-dir) |
| 87 | + TEST_DIR="$2" |
| 88 | + shift 2 |
| 89 | + ;; |
| 90 | + -l|--languages) |
| 91 | + LANGUAGES="$2" |
| 92 | + shift 2 |
| 93 | + ;; |
| 94 | + -v|--verbose) |
| 95 | + VERBOSE=true |
| 96 | + shift |
| 97 | + ;; |
| 98 | + -h|--help) |
| 99 | + HELP=true |
| 100 | + shift |
| 101 | + ;; |
| 102 | + *) |
| 103 | + log_error "Unknown option: $1" |
| 104 | + show_help |
| 105 | + exit 1 |
| 106 | + ;; |
| 107 | + esac |
| 108 | + done |
| 109 | +} |
| 110 | + |
| 111 | +# Validate required parameters |
| 112 | +validate_params() { |
| 113 | + if [[ "$HELP" == "true" ]]; then |
| 114 | + show_help |
| 115 | + exit 0 |
| 116 | + fi |
| 117 | + |
| 118 | + if [[ -z "$TEST_DIR" ]]; then |
| 119 | + log_error "Test directory is required. Use --test-dir option." |
| 120 | + show_help |
| 121 | + exit 1 |
| 122 | + fi |
| 123 | + |
| 124 | + if [[ ! -d "$TEST_DIR" ]]; then |
| 125 | + log_error "Test directory does not exist: $TEST_DIR" |
| 126 | + exit 1 |
| 127 | + fi |
| 128 | + |
| 129 | + if [[ -z "$LANGUAGES" ]]; then |
| 130 | + log_error "Languages are required. Use --languages option." |
| 131 | + show_help |
| 132 | + exit 1 |
| 133 | + fi |
| 134 | +} |
| 135 | + |
| 136 | +# Execute unit tests for a language |
| 137 | +run_unit_tests() { |
| 138 | + local language="$1" |
| 139 | + local work_dir="$2" |
| 140 | + |
| 141 | + log_info "Running unit tests for $language..." |
| 142 | + |
| 143 | + # Get environment info |
| 144 | + local os_name=$(uname -s) |
| 145 | + if [[ "$os_name" == "Darwin" ]]; then |
| 146 | + os_name="macOS" |
| 147 | + fi |
| 148 | + |
| 149 | + log_info " → Environment: $os_name, CodeQL CLI: Startup... |
| 150 | +unknown, Stdlib: codeql-cli_vStartup... |
| 151 | +unknown" |
| 152 | + |
| 153 | + # Find the QLT project using centralized variables |
| 154 | + if [[ ! -f "$QLT_PROJECT_FILE" ]]; then |
| 155 | + log_error "Could not find QLT project at: $QLT_PROJECT_FILE" |
| 156 | + return 1 |
| 157 | + fi |
| 158 | + |
| 159 | + # Change to the work directory since that's where QLT expects to be run from |
| 160 | + local old_pwd=$(pwd) |
| 161 | + cd "$work_dir" || return 1 |
| 162 | + |
| 163 | + # Create test results directory for the language |
| 164 | + local test_results_dir="${work_dir}/${language}/test-results" |
| 165 | + mkdir -p "$test_results_dir" |
| 166 | + |
| 167 | + # Execute tests using dotnet run |
| 168 | + local test_output |
| 169 | + if ! test_output=$(dotnet run --project "$QLT_PROJECT_FILE" -- test run execute-unit-tests \ |
| 170 | + --language "$language" \ |
| 171 | + --num-threads 2 \ |
| 172 | + --work-dir "$test_results_dir" \ |
| 173 | + --runner-os "$os_name" \ |
| 174 | + --automation-type actions 2>&1); then |
| 175 | + log_error "Unit test execution failed for $language" |
| 176 | + if [[ "$VERBOSE" == "true" ]]; then |
| 177 | + log_error "$test_output" |
| 178 | + fi |
| 179 | + cd "$old_pwd" |
| 180 | + return 1 |
| 181 | + fi |
| 182 | + |
| 183 | + cd "$old_pwd" |
| 184 | + |
| 185 | + if [[ "$VERBOSE" == "true" ]]; then |
| 186 | + log_info "Test execution output for $language:" |
| 187 | + echo "$test_output" |
| 188 | + fi |
| 189 | + |
| 190 | + log_success "Unit tests completed for $language" |
| 191 | + return 0 |
| 192 | +} |
| 193 | + |
| 194 | +# Validate unit test results |
| 195 | +validate_test_results() { |
| 196 | + local language="$1" |
| 197 | + local work_dir="$2" |
| 198 | + |
| 199 | + log_info "Validating unit test results for $language..." |
| 200 | + |
| 201 | + local results_dir="${work_dir}/${language}/test-results" |
| 202 | + if [[ ! -d "$results_dir" ]]; then |
| 203 | + log_error "Test results directory not found: $results_dir" |
| 204 | + return 1 |
| 205 | + fi |
| 206 | + |
| 207 | + # Find the QLT project using centralized variables |
| 208 | + if [[ ! -f "$QLT_PROJECT_FILE" ]]; then |
| 209 | + log_error "Could not find QLT project at: $QLT_PROJECT_FILE" |
| 210 | + return 1 |
| 211 | + fi |
| 212 | + |
| 213 | + # Change to the work directory since that's where QLT expects to be run from |
| 214 | + local old_pwd=$(pwd) |
| 215 | + cd "$work_dir" || return 1 |
| 216 | + |
| 217 | + local validation_output |
| 218 | + if ! validation_output=$(dotnet run --project "$QLT_PROJECT_FILE" -- test run validate-unit-tests --pretty-print --results-directory "$results_dir" 2>&1); then |
| 219 | + log_error "Test result validation failed" |
| 220 | + if [[ "$VERBOSE" == "true" ]]; then |
| 221 | + log_error "$validation_output" |
| 222 | + fi |
| 223 | + cd "$old_pwd" |
| 224 | + return 1 |
| 225 | + fi |
| 226 | + |
| 227 | + cd "$old_pwd" |
| 228 | + |
| 229 | + if [[ "$VERBOSE" == "true" ]]; then |
| 230 | + log_info "Validation output:" |
| 231 | + echo "$validation_output" |
| 232 | + fi |
| 233 | + |
| 234 | + log_success "Test result validation passed" |
| 235 | + return 0 |
| 236 | +} |
| 237 | + |
| 238 | +# Main execution function |
| 239 | +main() { |
| 240 | + parse_args "$@" |
| 241 | + validate_params |
| 242 | + |
| 243 | + log_info "Starting unit test execution..." |
| 244 | + log_info "Test directory: $TEST_DIR" |
| 245 | + log_info "Languages: $LANGUAGES" |
| 246 | + |
| 247 | + # Convert comma-separated languages to array |
| 248 | + IFS=',' read -ra LANG_ARRAY <<< "$LANGUAGES" |
| 249 | + |
| 250 | + local passed=0 |
| 251 | + local failed=0 |
| 252 | + local failed_languages=() |
| 253 | + |
| 254 | + # Process each language |
| 255 | + for language in "${LANG_ARRAY[@]}"; do |
| 256 | + language=$(echo "$language" | xargs) # trim whitespace |
| 257 | + |
| 258 | + log_info "Processing language: $language" |
| 259 | + |
| 260 | + # Check if language directory exists |
| 261 | + local lang_dir="$TEST_DIR/$language" |
| 262 | + if [[ ! -d "$lang_dir" ]]; then |
| 263 | + log_warning "Language directory not found: $lang_dir, skipping..." |
| 264 | + continue |
| 265 | + fi |
| 266 | + |
| 267 | + # Run tests for this language |
| 268 | + local language_failed=false |
| 269 | + if ! run_unit_tests "$language" "$TEST_DIR"; then |
| 270 | + log_error "Unit test execution failed for $language" |
| 271 | + language_failed=true |
| 272 | + fi |
| 273 | + |
| 274 | + # Update counters |
| 275 | + if [[ "$language_failed" == "true" ]]; then |
| 276 | + ((failed++)) |
| 277 | + failed_languages+=("$language") |
| 278 | + log_error "Language $language validation failed" |
| 279 | + else |
| 280 | + ((passed++)) |
| 281 | + log_success "Language $language validation complete" |
| 282 | + fi |
| 283 | + |
| 284 | + # Validate all test results if any tests were run |
| 285 | + if [[ $((passed + failed)) -gt 0 ]]; then |
| 286 | + if ! validate_test_results "$language" "$TEST_DIR"; then |
| 287 | + log_error "Unit test validation failed" |
| 288 | + ((failed++)) |
| 289 | + else |
| 290 | + log_success "Unit test validation passed" |
| 291 | + fi |
| 292 | + fi |
| 293 | + done |
| 294 | + |
| 295 | + # Print summary |
| 296 | + echo |
| 297 | + log_info "=== Unit Test Summary ===" |
| 298 | + log_success "Unit Tests Passed: $passed" |
| 299 | + if [[ $failed -gt 0 ]]; then |
| 300 | + log_error "Unit Tests Failed: $failed" |
| 301 | + if [[ ${#failed_languages[@]} -gt 0 ]]; then |
| 302 | + log_error "Unit Tests Failed for Languages: ${failed_languages[*]}" |
| 303 | + fi |
| 304 | + else |
| 305 | + log_success "No Unit Tests Failed" |
| 306 | + fi |
| 307 | + |
| 308 | + if [[ $failed -eq 0 ]]; then |
| 309 | + log_success "All unit tests passed! 🎉" |
| 310 | + exit 0 |
| 311 | + else |
| 312 | + log_error "Some unit tests failed. 😞" |
| 313 | + exit 1 |
| 314 | + fi |
| 315 | +} |
| 316 | + |
| 317 | +# Run main function with all arguments |
| 318 | +main "$@" |
0 commit comments