OBP-API/run_all_tests.sh

1017 lines
40 KiB
Bash
Raw Normal View History

2025-12-21 10:30:01 +00:00
#!/bin/bash
################################################################################
# OBP-API Test Runner Script
#
2025-12-21 10:59:41 +00:00
# What it does:
# 1. Changes terminal to blue background with "Tests Running" in title
# 2. Runs: mvn clean test
# 3. Shows all test output in real-time
# 4. Updates title bar with: phase, time elapsed, pass/fail counts
# 5. Saves detailed log and summary to test-results/
# 6. Restores terminal to normal when done
2025-12-21 10:30:01 +00:00
#
2025-12-22 21:28:03 +00:00
# Usage:
# ./run_all_tests.sh - Run full test suite
# ./run_all_tests.sh --summary-only - Regenerate summary from existing log
# ./run_all_tests.sh --timeout=60 - Run with 60 minute timeout
2025-12-21 10:30:01 +00:00
################################################################################
# Don't use set -e globally - it causes issues with grep returning 1 when no match
# Instead, we handle errors explicitly where needed
2025-12-21 10:30:01 +00:00
2025-12-22 21:28:03 +00:00
################################################################################
# PARSE COMMAND LINE ARGUMENTS
################################################################################
SUMMARY_ONLY=false
TIMEOUT_MINUTES=0 # 0 means no timeout
for arg in "$@"; do
case $arg in
--summary-only)
SUMMARY_ONLY=true
;;
--timeout=*)
TIMEOUT_MINUTES="${arg#*=}"
;;
esac
done
2025-12-22 21:28:03 +00:00
2025-12-21 10:59:41 +00:00
################################################################################
# TERMINAL STYLING FUNCTIONS
################################################################################
# Set terminal to "test mode" - different colors for different phases
2025-12-21 10:59:41 +00:00
set_terminal_style() {
local phase="${1:-Running}"
# Set different background colors for different phases
case "$phase" in
"Starting")
echo -ne "\033]11;#4a4a4a\007" # Dark gray background
echo -ne "\033]10;#ffffff\007" # White text
;;
"Building")
echo -ne "\033]11;#ff6b35\007" # Orange background
echo -ne "\033]10;#ffffff\007" # White text
;;
"Testing")
echo -ne "\033]11;#001f3f\007" # Dark blue background
echo -ne "\033]10;#ffffff\007" # White text
;;
"Complete")
echo -ne "\033]11;#2ecc40\007" # Green background
echo -ne "\033]10;#ffffff\007" # White text
;;
*)
echo -ne "\033]11;#001f3f\007" # Default blue background
echo -ne "\033]10;#ffffff\007" # White text
;;
esac
# Set window title
echo -ne "\033]0;OBP-API Tests ${phase}...\007"
# Print header bar with phase-specific styling
2025-12-21 11:58:44 +00:00
printf "\033[44m\033[1;37m%-$(tput cols)s\r OBP-API TEST RUNNER ACTIVE - ${phase} \n%-$(tput cols)s\033[0m\n" " " " "
2025-12-21 10:59:41 +00:00
}
# Update title bar with progress: "Testing: DynamicEntityTest - Scenario name [5m 23s]"
2025-12-21 10:59:41 +00:00
update_terminal_title() {
local phase="$1" # Starting, Building, Testing, Complete
local elapsed="${2:-}" # Time elapsed (e.g. "5m 23s")
2025-12-21 11:58:44 +00:00
local counts="${3:-}" # Module counts (e.g. "obp-commons:+38 obp-api:+245")
local suite="${4:-}" # Current test suite name
local scenario="${5:-}" # Current scenario name
2025-12-21 10:59:41 +00:00
local title="OBP-API ${phase}"
2025-12-21 11:58:44 +00:00
[ -n "$suite" ] && title="${title}: ${suite}"
[ -n "$scenario" ] && title="${title} - ${scenario}"
2025-12-21 11:58:44 +00:00
title="${title}..."
2025-12-21 10:59:41 +00:00
[ -n "$elapsed" ] && title="${title} [${elapsed}]"
2025-12-21 11:58:44 +00:00
[ -n "$counts" ] && title="${title} ${counts}"
2025-12-21 10:59:41 +00:00
echo -ne "\033]0;${title}\007"
}
# Restore terminal to normal (black background, default title)
restore_terminal_style() {
echo -ne "\033]0;Terminal\007\033]11;#000000\007\033]10;#ffffff\007\033[0m"
}
# Cleanup function: stop monitor, restore terminal, remove flag files
cleanup_on_exit() {
# Stop background monitor if running
if [ -n "${MONITOR_PID:-}" ]; then
kill $MONITOR_PID 2>/dev/null || true
wait $MONITOR_PID 2>/dev/null || true
fi
# Remove monitor flag file
rm -f "${LOG_DIR}/monitor.flag" 2>/dev/null || true
# Restore terminal
restore_terminal_style
}
# Always cleanup on exit (Ctrl+C, errors, or normal completion)
trap cleanup_on_exit EXIT INT TERM
2025-12-21 10:59:41 +00:00
################################################################################
# CONFIGURATION
################################################################################
2025-12-21 10:30:01 +00:00
LOG_DIR="test-results"
2025-12-21 11:58:44 +00:00
DETAIL_LOG="${LOG_DIR}/last_run.log" # Full Maven output
SUMMARY_LOG="${LOG_DIR}/last_run_summary.log" # Summary only
FAILED_TESTS_FILE="${LOG_DIR}/failed_tests.txt" # Failed test list for run_specific_tests.sh
2025-12-21 10:30:01 +00:00
# Phase timing variables (stored in temporary file)
PHASE_START_TIME=0
2025-12-21 10:30:01 +00:00
mkdir -p "${LOG_DIR}"
# Function to get current time in milliseconds
get_time_ms() {
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
python3 -c "import time; print(int(time.time() * 1000))"
else
# Linux
date +%s%3N
fi
}
# Function to record phase timing
record_phase_time() {
local phase="$1"
local current_time=$(get_time_ms)
local timing_file="${LOG_DIR}/phase_timing.tmp"
case "$phase" in
"starting")
echo "PHASE_START_TIME=$current_time" > "$timing_file"
;;
"building")
if [ -f "$timing_file" ]; then
local phase_start=$(grep "PHASE_START_TIME=" "$timing_file" | cut -d= -f2)
if [ "$phase_start" -gt 0 ]; then
local starting_time=$((current_time - phase_start))
echo "STARTING_TIME=$starting_time" >> "$timing_file"
fi
fi
echo "PHASE_START_TIME=$current_time" >> "$timing_file"
;;
"testing")
if [ -f "$timing_file" ]; then
local phase_start=$(grep "PHASE_START_TIME=" "$timing_file" | tail -1 | cut -d= -f2)
if [ "$phase_start" -gt 0 ]; then
local building_time=$((current_time - phase_start))
echo "BUILDING_TIME=$building_time" >> "$timing_file"
fi
fi
echo "PHASE_START_TIME=$current_time" >> "$timing_file"
;;
"complete")
if [ -f "$timing_file" ]; then
local phase_start=$(grep "PHASE_START_TIME=" "$timing_file" | tail -1 | cut -d= -f2)
if [ "$phase_start" -gt 0 ]; then
local testing_time=$((current_time - phase_start))
echo "TESTING_TIME=$testing_time" >> "$timing_file"
fi
fi
echo "PHASE_START_TIME=$current_time" >> "$timing_file"
;;
"end")
if [ -f "$timing_file" ]; then
local phase_start=$(grep "PHASE_START_TIME=" "$timing_file" | tail -1 | cut -d= -f2)
if [ "$phase_start" -gt 0 ]; then
local complete_time=$((current_time - phase_start))
echo "COMPLETE_TIME=$complete_time" >> "$timing_file"
fi
fi
;;
esac
}
2025-12-22 21:28:03 +00:00
# If summary-only mode, skip to summary generation
if [ "$SUMMARY_ONLY" = true ]; then
if [ ! -f "${DETAIL_LOG}" ]; then
echo "ERROR: No log file found at ${DETAIL_LOG}"
echo "Please run tests first without --summary-only flag"
exit 1
fi
echo "Regenerating summary from existing log: ${DETAIL_LOG}"
# Skip cleanup and jump to summary generation
START_TIME=0
END_TIME=0
DURATION=0
DURATION_MIN=0
DURATION_SEC=0
else
# Delete old log files and stale flag files from previous run
echo "Cleaning up old files..."
if [ -f "${DETAIL_LOG}" ]; then
rm -f "${DETAIL_LOG}"
echo " - Removed old detail log"
fi
if [ -f "${SUMMARY_LOG}" ]; then
rm -f "${SUMMARY_LOG}"
echo " - Removed old summary log"
fi
if [ -f "${LOG_DIR}/monitor.flag" ]; then
rm -f "${LOG_DIR}/monitor.flag"
echo " - Removed stale monitor flag"
fi
2025-12-22 21:28:03 +00:00
if [ -f "${LOG_DIR}/warning_analysis.tmp" ]; then
rm -f "${LOG_DIR}/warning_analysis.tmp"
echo " - Removed stale warning analysis"
fi
if [ -f "${LOG_DIR}/recent_lines.tmp" ]; then
rm -f "${LOG_DIR}/recent_lines.tmp"
echo " - Removed stale temp file"
fi
if [ -f "${LOG_DIR}/phase_timing.tmp" ]; then
rm -f "${LOG_DIR}/phase_timing.tmp"
echo " - Removed stale timing file"
fi
2025-12-22 21:28:03 +00:00
fi # End of if [ "$SUMMARY_ONLY" = true ]
2025-12-21 11:58:44 +00:00
2025-12-21 10:59:41 +00:00
################################################################################
# HELPER FUNCTIONS
################################################################################
# Log message to terminal and both log files
2025-12-21 10:30:01 +00:00
log_message() {
2025-12-22 16:24:25 +00:00
echo "$1"
echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >> "${SUMMARY_LOG}"
echo "$1" >> "${DETAIL_LOG}"
2025-12-21 10:30:01 +00:00
}
2025-12-21 10:59:41 +00:00
# Print section header
2025-12-21 10:30:01 +00:00
print_header() {
echo ""
echo "================================================================================"
2025-12-21 10:59:41 +00:00
echo "$1"
2025-12-21 10:30:01 +00:00
echo "================================================================================"
echo ""
}
2025-12-21 11:58:44 +00:00
# Analyze warnings and return top contributors
analyze_warnings() {
local log_file="$1"
local temp_file="${LOG_DIR}/warning_analysis.tmp"
# Extract and categorize warnings from last 5000 lines (for performance)
# This gives good coverage without scanning entire multi-MB log file
tail -n 5000 "${log_file}" 2>/dev/null | grep -i "warning" | \
2025-12-21 11:58:44 +00:00
# Normalize patterns to group similar warnings
sed -E 's/line [0-9]+/line XXX/g' | \
sed -E 's/[0-9]+ warnings?/N warnings/g' | \
sed -E 's/\[WARNING\] .*(src|test)\/[^ ]+/[WARNING] <source-file>/g' | \
sed -E 's/version [0-9]+\.[0-9]+(\.[0-9]+)?/version X.X/g' | \
# Extract the core warning message
sed -E 's/^.*\[WARNING\] *//' | \
sort | uniq -c | sort -rn > "${temp_file}"
# Return the temp file path for further processing
echo "${temp_file}"
}
# Format and display top warning factors
display_warning_factors() {
local analysis_file="$1"
local max_display="${2:-10}"
if [ ! -f "${analysis_file}" ] || [ ! -s "${analysis_file}" ]; then
2025-12-22 16:24:25 +00:00
log_message " No detailed warning analysis available"
2025-12-21 11:58:44 +00:00
return
fi
local total_warning_types=$(wc -l < "${analysis_file}")
local displayed=0
2025-12-22 16:24:25 +00:00
log_message "Top Warning Factors:"
2025-12-21 11:58:44 +00:00
log_message "-------------------"
while IFS= read -r line && [ $displayed -lt $max_display ]; do
# Extract count and message
local count=$(echo "$line" | awk '{print $1}')
local message=$(echo "$line" | sed -E 's/^[[:space:]]*[0-9]+[[:space:]]*//')
# Truncate long messages
if [ ${#message} -gt 80 ]; then
message="${message:0:77}..."
fi
# Format with count prominence
2025-12-22 16:24:25 +00:00
printf " %4d x %s\n" "$count" "$message" | tee -a "${SUMMARY_LOG}" > /dev/tty
2025-12-21 11:58:44 +00:00
displayed=$((displayed + 1))
done < "${analysis_file}"
if [ $total_warning_types -gt $max_display ]; then
local remaining=$((total_warning_types - max_display))
log_message " ... and ${remaining} more warning type(s)"
fi
# Clean up temp file
rm -f "${analysis_file}"
}
2025-12-22 21:28:03 +00:00
################################################################################
# GENERATE SUMMARY FUNCTION (DRY)
################################################################################
generate_summary() {
local detail_log="$1"
local summary_log="$2"
local start_time="${3:-0}"
local end_time="${4:-0}"
# Calculate duration
local duration=$((end_time - start_time))
local duration_min=$((duration / 60))
local duration_sec=$((duration % 60))
# If no timing info (summary-only mode), extract from log
if [ $duration -eq 0 ] && grep -q "Total time:" "$detail_log"; then
local time_str=$(grep "Total time:" "$detail_log" | tail -1)
duration_min=$(echo "$time_str" | sed 's/.*: //' | sed 's/ min.*//' | grep -o '[0-9]*' | head -1)
[ -z "$duration_min" ] && duration_min="0"
duration_sec=$(echo "$time_str" | sed 's/.* min //' | sed 's/\..*//' | grep -o '[0-9]*' | head -1)
[ -z "$duration_sec" ] && duration_sec="0"
2025-12-22 21:28:03 +00:00
fi
print_header "Test Results Summary"
# Extract test statistics from ScalaTest output (with UNKNOWN fallback if extraction fails)
# ScalaTest outputs across multiple lines:
# Run completed in X seconds.
# Total number of tests run: N
# Suites: completed M, aborted 0
# Tests: succeeded N, failed 0, canceled 0, ignored 0, pending 0
# All tests passed.
# We need to sum stats from ALL test runs (multiple modules: obp-commons, obp-api, etc.)
# Sum up all "Total number of tests run" values (macOS compatible - no grep -P)
TOTAL_TESTS=$(grep "Total number of tests run:" "${detail_log}" 2>/dev/null | sed 's/.*Total number of tests run: //' | awk '{sum+=$1} END {print sum}' || echo "0")
[ -z "$TOTAL_TESTS" ] || [ "$TOTAL_TESTS" = "0" ] && TOTAL_TESTS="UNKNOWN"
# Sum up all succeeded from "Tests: succeeded N, ..." lines
SUCCEEDED=$(grep "Tests: succeeded" "${detail_log}" 2>/dev/null | sed 's/.*succeeded //' | sed 's/,.*//' | awk '{sum+=$1} END {print sum}' || echo "0")
[ -z "$SUCCEEDED" ] && SUCCEEDED="UNKNOWN"
# Sum up all failed from "Tests: ... failed N, ..." lines
FAILED=$(grep "Tests:.*failed" "${detail_log}" 2>/dev/null | sed 's/.*failed //' | sed 's/,.*//' | awk '{sum+=$1} END {print sum}' || echo "0")
[ -z "$FAILED" ] && FAILED="0"
# Sum up all ignored from "Tests: ... ignored N, ..." lines
IGNORED=$(grep "Tests:.*ignored" "${detail_log}" 2>/dev/null | sed 's/.*ignored //' | sed 's/,.*//' | awk '{sum+=$1} END {print sum}' || echo "0")
[ -z "$IGNORED" ] && IGNORED="0"
# Sum up errors (if any)
ERRORS=$(grep "errors" "${detail_log}" 2>/dev/null | grep -v "ERROR" | sed 's/.*errors //' | sed 's/[^0-9].*//' | awk '{sum+=$1} END {print sum}' || echo "0")
[ -z "$ERRORS" ] && ERRORS="0"
# Calculate total including ignored (like IntelliJ does)
if [ "$TOTAL_TESTS" != "UNKNOWN" ] && [ "$IGNORED" != "0" ]; then
TOTAL_WITH_IGNORED=$((TOTAL_TESTS + IGNORED))
2025-12-22 21:28:03 +00:00
else
TOTAL_WITH_IGNORED="$TOTAL_TESTS"
2025-12-22 21:28:03 +00:00
fi
WARNINGS=$(grep -c "WARNING" "${detail_log}" 2>/dev/null || echo "0")
2025-12-22 21:28:03 +00:00
# Determine build status
if grep -q "BUILD SUCCESS" "${detail_log}"; then
BUILD_STATUS="SUCCESS"
BUILD_COLOR=""
elif grep -q "BUILD FAILURE" "${detail_log}"; then
BUILD_STATUS="FAILURE"
BUILD_COLOR=""
else
BUILD_STATUS="UNKNOWN"
BUILD_COLOR=""
fi
# Print summary
log_message "Test Run Summary"
log_message "================"
# Extract Maven timestamps and calculate Terminal timestamps
local maven_start_timestamp=""
local maven_end_timestamp=""
local terminal_start_timestamp=""
local terminal_end_timestamp=$(date)
if [ "$start_time" -gt 0 ] && [ "$end_time" -gt 0 ]; then
# Use actual terminal start/end times if available
terminal_start_timestamp=$(date -r "$start_time" 2>/dev/null || date -d "@$start_time" 2>/dev/null || echo "Unknown")
terminal_end_timestamp=$(date -r "$end_time" 2>/dev/null || date -d "@$end_time" 2>/dev/null || echo "Unknown")
else
# Calculate terminal start time by subtracting duration from current time
if [ "$duration_min" -gt 0 -o "$duration_sec" -gt 0 ]; then
local total_seconds=$((duration_min * 60 + duration_sec))
local approx_start_epoch=$(($(date "+%s") - total_seconds))
terminal_start_timestamp=$(date -r "$approx_start_epoch" 2>/dev/null || echo "Approx. ${duration_min}m ${duration_sec}s ago")
else
terminal_start_timestamp="Unknown"
fi
fi
# Extract Maven timestamps from log
maven_end_timestamp=$(grep "Finished at:" "${detail_log}" | tail -1 | sed 's/.*Finished at: //' | sed 's/T/ /' | sed 's/+.*//' || echo "Unknown")
# Calculate Maven start time from Maven's "Total time" if available
local maven_total_time=$(grep "Total time:" "${detail_log}" | tail -1 | sed 's/.*Total time: *//' | sed 's/ .*//' || echo "")
if [ -n "$maven_total_time" ] && [ "$maven_end_timestamp" != "Unknown" ]; then
# Parse Maven duration (e.g., "02:06" for "02:06 min" or "43.653" for "43.653 s")
local maven_seconds=0
if echo "$maven_total_time" | grep -q ":"; then
# Format like "02:06" (minutes:seconds)
local maven_min=$(echo "$maven_total_time" | sed 's/:.*//')
local maven_sec=$(echo "$maven_total_time" | sed 's/.*://')
# Remove leading zeros to avoid octal interpretation
maven_min=$(echo "$maven_min" | sed 's/^0*//' | sed 's/^$/0/')
maven_sec=$(echo "$maven_sec" | sed 's/^0*//' | sed 's/^$/0/')
maven_seconds=$((maven_min * 60 + maven_sec))
else
# Format like "43.653" (seconds)
maven_seconds=$(echo "$maven_total_time" | sed 's/\..*//')
fi
# Calculate Maven start time
if [ "$maven_seconds" -gt 0 ]; then
local maven_end_epoch=$(date -j -f "%Y-%m-%d %H:%M:%S" "$maven_end_timestamp" "+%s" 2>/dev/null || echo "0")
if [ "$maven_end_epoch" -gt 0 ]; then
local maven_start_epoch=$((maven_end_epoch - maven_seconds))
maven_start_timestamp=$(date -r "$maven_start_epoch" 2>/dev/null || echo "Unknown")
else
maven_start_timestamp="Unknown"
fi
else
maven_start_timestamp="Unknown"
fi
else
maven_start_timestamp="Unknown"
fi
# Format Maven end timestamp nicely
if [ "$maven_end_timestamp" != "Unknown" ]; then
maven_end_timestamp=$(date -j -f "%Y-%m-%d %H:%M:%S" "$maven_end_timestamp" "+%a %b %d %H:%M:%S %Z %Y" 2>/dev/null || echo "$maven_end_timestamp")
fi
# Display both timelines
log_message "Terminal Timeline:"
log_message " Started: ${terminal_start_timestamp}"
log_message " Completed: ${terminal_end_timestamp}"
log_message " Duration: ${duration_min}m ${duration_sec}s"
log_message ""
log_message "Maven Timeline:"
log_message " Started: ${maven_start_timestamp}"
log_message " Completed: ${maven_end_timestamp}"
if [ -n "$maven_total_time" ]; then
local maven_duration_display=$(grep "Total time:" "${detail_log}" | tail -1 | sed 's/.*Total time: *//' || echo "Unknown")
log_message " Duration: ${maven_duration_display}"
fi
log_message ""
2025-12-22 21:28:03 +00:00
log_message "Build Status: ${BUILD_STATUS}"
log_message ""
# Phase timing breakdown (if available)
local timing_file="${LOG_DIR}/phase_timing.tmp"
if [ -f "$timing_file" ]; then
# Read timing values from file
local start_ms=$(grep "STARTING_TIME=" "$timing_file" | cut -d= -f2 2>/dev/null || echo "0")
local build_ms=$(grep "BUILDING_TIME=" "$timing_file" | cut -d= -f2 2>/dev/null || echo "0")
local test_ms=$(grep "TESTING_TIME=" "$timing_file" | cut -d= -f2 2>/dev/null || echo "0")
local complete_ms=$(grep "COMPLETE_TIME=" "$timing_file" | cut -d= -f2 2>/dev/null || echo "0")
# Ensure we have numeric values (default to 0 if empty)
[ -z "$start_ms" ] && start_ms=0
[ -z "$build_ms" ] && build_ms=0
[ -z "$test_ms" ] && test_ms=0
[ -z "$complete_ms" ] && complete_ms=0
# Clean up timing file
rm -f "$timing_file"
if [ "$start_ms" -gt 0 ] 2>/dev/null || [ "$build_ms" -gt 0 ] 2>/dev/null || [ "$test_ms" -gt 0 ] 2>/dev/null || [ "$complete_ms" -gt 0 ] 2>/dev/null; then
log_message "Phase Timing Breakdown:"
if [ "$start_ms" -gt 0 ] 2>/dev/null; then
log_message " Starting: ${start_ms}ms ($(printf "%.2f" $(echo "scale=2; $start_ms/1000" | bc))s)"
fi
if [ "$build_ms" -gt 0 ] 2>/dev/null; then
log_message " Building: ${build_ms}ms ($(printf "%.2f" $(echo "scale=2; $build_ms/1000" | bc))s)"
fi
if [ "$test_ms" -gt 0 ] 2>/dev/null; then
log_message " Testing: ${test_ms}ms ($(printf "%.2f" $(echo "scale=2; $test_ms/1000" | bc))s)"
fi
if [ "$complete_ms" -gt 0 ] 2>/dev/null; then
log_message " Complete: ${complete_ms}ms ($(printf "%.2f" $(echo "scale=2; $complete_ms/1000" | bc))s)"
fi
# Calculate percentages
local total_phase_time=$((start_ms + build_ms + test_ms + complete_ms))
if [ "$total_phase_time" -gt 0 ]; then
log_message ""
log_message "Phase Distribution:"
if [ "$start_ms" -gt 0 ] 2>/dev/null; then
local starting_pct=$(echo "scale=1; $start_ms * 100 / $total_phase_time" | bc)
log_message " Starting: ${starting_pct}%"
fi
if [ "$build_ms" -gt 0 ] 2>/dev/null; then
local building_pct=$(echo "scale=1; $build_ms * 100 / $total_phase_time" | bc)
log_message " Building: ${building_pct}%"
fi
if [ "$test_ms" -gt 0 ] 2>/dev/null; then
local testing_pct=$(echo "scale=1; $test_ms * 100 / $total_phase_time" | bc)
log_message " Testing: ${testing_pct}%"
fi
if [ "$complete_ms" -gt 0 ] 2>/dev/null; then
local complete_pct=$(echo "scale=1; $complete_ms * 100 / $total_phase_time" | bc)
log_message " Complete: ${complete_pct}%"
fi
fi
log_message ""
fi
fi
2025-12-22 21:28:03 +00:00
log_message "Test Statistics:"
log_message " Total: ${TOTAL_WITH_IGNORED} (${TOTAL_TESTS} run + ${IGNORED} ignored)"
2025-12-22 21:28:03 +00:00
log_message " Succeeded: ${SUCCEEDED}"
log_message " Failed: ${FAILED}"
log_message " Ignored: ${IGNORED}"
2025-12-22 21:28:03 +00:00
log_message " Errors: ${ERRORS}"
log_message " Warnings: ${WARNINGS}"
log_message ""
# Analyze and display warning factors if warnings exist
if [ "${WARNINGS}" != "0" ] && [ "${WARNINGS}" != "UNKNOWN" ]; then
warning_analysis=$(analyze_warnings "${detail_log}")
display_warning_factors "${warning_analysis}" 10
log_message ""
fi
# Show failed tests if any (only actual test failures, not application ERROR logs)
if [ "${FAILED}" != "0" ] && [ "${FAILED}" != "UNKNOWN" ]; then
log_message "Failed Tests:"
# Look for ScalaTest failure markers, not application ERROR logs
grep -E "\*\*\* FAILED \*\*\*|\*\*\* RUN ABORTED \*\*\*" "${detail_log}" | head -50 >> "${summary_log}"
log_message ""
# Extract failed test class names and save to file for run_specific_tests.sh
# Look backwards from "*** FAILED ***" to find the test class name
# ScalaTest prints: "TestClassName:" before scenarios
> "${FAILED_TESTS_FILE}" # Clear/create file
echo "# Failed test classes from last run" >> "${FAILED_TESTS_FILE}"
echo "# Auto-generated by run_all_tests.sh - you can edit this file manually" >> "${FAILED_TESTS_FILE}"
echo "#" >> "${FAILED_TESTS_FILE}"
echo "# Format: One test class per line with full package path" >> "${FAILED_TESTS_FILE}"
echo "# Example: code.api.v6_0_0.RateLimitsTest" >> "${FAILED_TESTS_FILE}"
echo "#" >> "${FAILED_TESTS_FILE}"
echo "# Usage: ./run_specific_tests.sh will read this file and run only these tests" >> "${FAILED_TESTS_FILE}"
echo "#" >> "${FAILED_TESTS_FILE}"
echo "# Lines starting with # are ignored (comments)" >> "${FAILED_TESTS_FILE}"
echo "" >> "${FAILED_TESTS_FILE}"
# Extract test class names from failures
grep -B 20 "\*\*\* FAILED \*\*\*" "${detail_log}" | \
grep -E "^[A-Z][a-zA-Z0-9_]+:" | sed 's/:$//' | \
sort -u | \
while read test_class; do
# Try to find package by searching for the class in test files
package=$(find obp-api/src/test/scala -name "${test_class}.scala" | \
sed 's|obp-api/src/test/scala/||' | \
sed 's|/|.|g' | \
sed 's|.scala$||' | \
head -1)
if [ -n "$package" ]; then
echo "$package" >> "${FAILED_TESTS_FILE}"
fi
done
log_message "Failed test classes saved to: ${FAILED_TESTS_FILE}"
log_message ""
2025-12-22 21:28:03 +00:00
elif [ "${ERRORS}" != "0" ] && [ "${ERRORS}" != "UNKNOWN" ]; then
log_message "Test Errors:"
grep -E "\*\*\* FAILED \*\*\*|\*\*\* RUN ABORTED \*\*\*" "${detail_log}" | head -50 >> "${summary_log}"
log_message ""
fi
# Final result
print_header "Test Run Complete"
if [ "${BUILD_STATUS}" = "SUCCESS" ] && [ "${FAILED}" = "0" ] && [ "${ERRORS}" = "0" ]; then
log_message "[PASS] All tests passed!"
return 0
else
log_message "[FAIL] Tests failed"
return 1
fi
}
################################################################################
# SUMMARY-ONLY MODE
################################################################################
if [ "$SUMMARY_ONLY" = true ]; then
# Just regenerate the summary and exit
rm -f "${SUMMARY_LOG}"
if generate_summary "${DETAIL_LOG}" "${SUMMARY_LOG}" 0 0; then
log_message ""
log_message "Summary regenerated:"
log_message " ${SUMMARY_LOG}"
exit 0
else
exit 1
fi
fi
2025-12-21 10:59:41 +00:00
################################################################################
# START TEST RUN
################################################################################
# Record starting phase
record_phase_time "starting"
2025-12-21 10:59:41 +00:00
set_terminal_style "Starting"
2025-12-21 10:30:01 +00:00
# Start the test run
print_header "OBP-API Test Suite"
2025-12-22 16:24:25 +00:00
log_message "Starting test run at $(date)"
2025-12-21 10:30:01 +00:00
log_message "Detail log: ${DETAIL_LOG}"
log_message "Summary log: ${SUMMARY_LOG}"
echo ""
# Set Maven options for tests
# The --add-opens flags tell Java 17 to allow Kryo serialization library to access
# the internal java.lang.invoke and java.lang modules, which fixes the InaccessibleObjectException
export MAVEN_OPTS="-Xss128m -Xms3G -Xmx6G -XX:MaxMetaspaceSize=2G --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED"
2025-12-22 16:24:25 +00:00
log_message "Maven Options: ${MAVEN_OPTS}"
2025-12-21 10:30:01 +00:00
echo ""
2025-12-21 10:59:41 +00:00
# Ensure test properties file exists
2025-12-21 10:30:01 +00:00
PROPS_FILE="obp-api/src/main/resources/props/test.default.props"
2025-12-21 10:59:41 +00:00
PROPS_TEMPLATE="${PROPS_FILE}.template"
2025-12-21 10:30:01 +00:00
if [ -f "${PROPS_FILE}" ]; then
2025-12-22 16:24:25 +00:00
log_message "[OK] Found test.default.props"
2025-12-21 10:30:01 +00:00
else
2025-12-22 16:24:25 +00:00
log_message "[WARNING] test.default.props not found - creating from template"
2025-12-21 10:30:01 +00:00
if [ -f "${PROPS_TEMPLATE}" ]; then
cp "${PROPS_TEMPLATE}" "${PROPS_FILE}"
2025-12-22 16:24:25 +00:00
log_message "[OK] Created test.default.props"
2025-12-21 10:30:01 +00:00
else
2025-12-22 16:24:25 +00:00
log_message "ERROR: ${PROPS_TEMPLATE} not found!"
2025-12-21 10:30:01 +00:00
exit 1
fi
fi
2025-12-22 17:28:41 +00:00
################################################################################
# CHECK AND CLEANUP TEST SERVER PORTS
# Port 8018 is used by the embedded Jetty test server (configured in test.default.props)
################################################################################
print_header "Checking Test Server Ports"
# Default test port (can be overridden)
TEST_PORT=8018
MAX_PORT_ATTEMPTS=5
log_message "Checking if test server port ${TEST_PORT} is available..."
# Function to find an available port
find_available_port() {
local port=$1
local max_attempts=$2
local attempt=0
while [ $attempt -lt $max_attempts ]; do
if ! lsof -i :$port >/dev/null 2>&1; then
echo $port
return 0
fi
port=$((port + 1))
attempt=$((attempt + 1))
done
echo ""
return 1
}
# Check if port is in use
if lsof -i :${TEST_PORT} >/dev/null 2>&1; then
log_message "[WARNING] Port ${TEST_PORT} is in use - attempting to kill process"
PORT_PID=$(lsof -t -i :${TEST_PORT} 2>/dev/null || true)
2025-12-22 17:28:41 +00:00
if [ -n "$PORT_PID" ]; then
kill -9 $PORT_PID 2>/dev/null || true
sleep 2
# Verify port is now free
if lsof -i :${TEST_PORT} >/dev/null 2>&1; then
log_message "[WARNING] Could not free port ${TEST_PORT}, searching for alternative..."
NEW_PORT=$(find_available_port $((TEST_PORT + 1)) $MAX_PORT_ATTEMPTS)
if [ -n "$NEW_PORT" ]; then
log_message "[OK] Found available port: ${NEW_PORT}"
# Update test.default.props with new port
if [ -f "${PROPS_FILE}" ]; then
sed -i.bak "s/hostname=127.0.0.1:${TEST_PORT}/hostname=127.0.0.1:${NEW_PORT}/" "${PROPS_FILE}" 2>/dev/null || \
sed -i '' "s/hostname=127.0.0.1:${TEST_PORT}/hostname=127.0.0.1:${NEW_PORT}/" "${PROPS_FILE}"
log_message "[OK] Updated test.default.props to use port ${NEW_PORT}"
TEST_PORT=$NEW_PORT
fi
else
log_message "[ERROR] No available ports found in range ${TEST_PORT}-$((TEST_PORT + MAX_PORT_ATTEMPTS))"
exit 1
fi
else
log_message "[OK] Killed process $PORT_PID, port ${TEST_PORT} is now available"
fi
2025-12-22 17:28:41 +00:00
fi
else
log_message "[OK] Port ${TEST_PORT} is available"
2025-12-22 17:28:41 +00:00
fi
# Also check for any stale Java test processes
STALE_TEST_PROCS=$(ps aux | grep -E "TestServer|ScalaTest.*obp-api" | grep -v grep | awk '{print $2}' 2>/dev/null || true)
2025-12-22 17:28:41 +00:00
if [ -n "$STALE_TEST_PROCS" ]; then
log_message "[WARNING] Found stale test processes - cleaning up"
echo "$STALE_TEST_PROCS" | xargs kill -9 2>/dev/null || true
sleep 2
log_message "[OK] Cleaned up stale test processes"
else
log_message "[OK] No stale test processes found"
fi
log_message ""
################################################################################
# CLEAN METRICS DATABASE
################################################################################
print_header "Cleaning Metrics Database"
2025-12-22 16:24:25 +00:00
log_message "Checking for test database files..."
# Only delete specific test database files to prevent accidental data loss
# The test configuration uses test_only_lift_proto.db as the database filename
TEST_DB_PATTERNS=(
"./test_only_lift_proto.db"
"./test_only_lift_proto.db.mv.db"
"./test_only_lift_proto.db.trace.db"
"./obp-api/test_only_lift_proto.db"
"./obp-api/test_only_lift_proto.db.mv.db"
"./obp-api/test_only_lift_proto.db.trace.db"
)
FOUND_FILES=false
for dbfile in "${TEST_DB_PATTERNS[@]}"; do
if [ -f "$dbfile" ]; then
FOUND_FILES=true
rm -f "$dbfile"
2025-12-22 16:24:25 +00:00
log_message " [OK] Deleted: $dbfile"
fi
done
if [ "$FOUND_FILES" = false ]; then
2025-12-22 16:24:25 +00:00
log_message "No old test database files found"
fi
log_message ""
2025-12-21 10:59:41 +00:00
################################################################################
# RUN TESTS
################################################################################
2025-12-21 10:30:01 +00:00
print_header "Running Tests"
2025-12-22 16:24:25 +00:00
log_message "Executing: mvn clean test"
2025-12-21 10:30:01 +00:00
echo ""
START_TIME=$(date +%s)
2025-12-21 11:58:44 +00:00
export START_TIME
# Create flag file to signal background process to stop
MONITOR_FLAG="${LOG_DIR}/monitor.flag"
touch "${MONITOR_FLAG}"
2025-12-21 10:30:01 +00:00
# Optional timeout handling
MAVEN_PID=""
if [ "$TIMEOUT_MINUTES" -gt 0 ] 2>/dev/null; then
log_message "[INFO] Test timeout set to ${TIMEOUT_MINUTES} minutes"
TIMEOUT_SECONDS=$((TIMEOUT_MINUTES * 60))
fi
2025-12-21 10:59:41 +00:00
# Background process: Monitor log file and update title bar with progress
(
2025-12-21 11:58:44 +00:00
# Wait for log file to be created and have Maven output
while [ ! -f "${DETAIL_LOG}" ] || [ ! -s "${DETAIL_LOG}" ]; do
sleep 1
done
2025-12-21 10:59:41 +00:00
phase="Building"
in_building=false
2025-12-21 10:59:41 +00:00
in_testing=false
timing_file="${LOG_DIR}/phase_timing.tmp"
2025-12-21 10:59:41 +00:00
2025-12-21 11:58:44 +00:00
# Keep monitoring until flag file is removed
while [ -f "${MONITOR_FLAG}" ]; do
# Use tail to look at recent lines only (last 500 lines for performance)
recent_lines=$(tail -n 500 "${DETAIL_LOG}" 2>/dev/null || true)
2025-12-21 11:58:44 +00:00
# Switch to "Building" phase when Maven starts compiling
if ! $in_building && echo "$recent_lines" | grep -q -E 'Compiling|Building.*Open Bank Project' 2>/dev/null; then
phase="Building"
in_building=true
# Record building phase and update terminal (inline to avoid subshell issues)
current_time=$(python3 -c "import time; print(int(time.time() * 1000))" 2>/dev/null || date +%s000)
if [ -f "$timing_file" ]; then
phase_start=$(grep "PHASE_START_TIME=" "$timing_file" 2>/dev/null | tail -1 | cut -d= -f2 || echo "0")
[ -n "$phase_start" ] && [ "$phase_start" -gt 0 ] 2>/dev/null && echo "STARTING_TIME=$((current_time - phase_start))" >> "$timing_file"
fi
echo "PHASE_START_TIME=$current_time" >> "$timing_file"
echo -ne "\033]11;#ff6b35\007\033]10;#ffffff\007" # Orange background
fi
2025-12-21 11:58:44 +00:00
# Switch to "Testing" phase when tests start
if ! $in_testing && echo "$recent_lines" | grep -q "Run starting" 2>/dev/null; then
2025-12-21 11:58:44 +00:00
phase="Testing"
in_testing=true
# Record testing phase
current_time=$(python3 -c "import time; print(int(time.time() * 1000))" 2>/dev/null || date +%s000)
if [ -f "$timing_file" ]; then
phase_start=$(grep "PHASE_START_TIME=" "$timing_file" 2>/dev/null | tail -1 | cut -d= -f2 || echo "0")
[ -n "$phase_start" ] && [ "$phase_start" -gt 0 ] 2>/dev/null && echo "BUILDING_TIME=$((current_time - phase_start))" >> "$timing_file"
fi
echo "PHASE_START_TIME=$current_time" >> "$timing_file"
echo -ne "\033]11;#001f3f\007\033]10;#ffffff\007" # Blue background
2025-12-21 11:58:44 +00:00
fi
2025-12-21 10:59:41 +00:00
# Extract current running test suite and scenario from recent lines
suite=""
scenario=""
if $in_testing; then
suite=$(echo "$recent_lines" | grep -E "Test:" 2>/dev/null | tail -1 | sed 's/\x1b\[[0-9;]*m//g' | sed 's/:$//' | tr -d '\n\r' || true)
scenario=$(echo "$recent_lines" | grep -i "scenario:" 2>/dev/null | tail -1 | sed 's/\x1b\[[0-9;]*m//g' | sed 's/^[[:space:]]*-*[[:space:]]*//' | sed -E 's/^[Ss]cenario:[[:space:]]*//' | tr -d '\n\r' || true)
[ -n "$scenario" ] && [ ${#scenario} -gt 50 ] && scenario="${scenario:0:47}..."
2025-12-21 10:59:41 +00:00
fi
# Calculate elapsed time
duration=$(($(date +%s) - START_TIME))
minutes=$((duration / 60))
seconds=$((duration % 60))
elapsed=$(printf "%dm %ds" $minutes $seconds)
# Update title
title="OBP-API ${phase}"
[ -n "$suite" ] && title="${title}: ${suite}"
[ -n "$scenario" ] && title="${title} - ${scenario}"
title="${title}... [${elapsed}]"
echo -ne "\033]0;${title}\007"
2025-12-21 10:59:41 +00:00
sleep 5
done
) &
MONITOR_PID=$!
# Run Maven with optional timeout
if [ "$TIMEOUT_MINUTES" -gt 0 ] 2>/dev/null; then
# Run Maven in background and monitor for timeout
mvn clean test 2>&1 | tee "${DETAIL_LOG}" &
MAVEN_PID=$!
elapsed=0
while kill -0 $MAVEN_PID 2>/dev/null; do
sleep 10
elapsed=$((elapsed + 10))
if [ $elapsed -ge $TIMEOUT_SECONDS ]; then
log_message ""
log_message "[TIMEOUT] Test execution exceeded ${TIMEOUT_MINUTES} minutes - terminating"
kill -9 $MAVEN_PID 2>/dev/null || true
# Also kill any child Java processes
pkill -9 -P $MAVEN_PID 2>/dev/null || true
TEST_RESULT="TIMEOUT"
break
fi
done
if [ "$TEST_RESULT" != "TIMEOUT" ]; then
wait $MAVEN_PID
if [ $? -eq 0 ]; then
TEST_RESULT="SUCCESS"
else
TEST_RESULT="FAILURE"
fi
fi
2025-12-21 10:30:01 +00:00
else
# Run Maven normally (all output goes to terminal AND log file)
if mvn clean test 2>&1 | tee "${DETAIL_LOG}"; then
TEST_RESULT="SUCCESS"
else
TEST_RESULT="FAILURE"
fi
2025-12-21 10:30:01 +00:00
fi
################################################################################
# GENERATE HTML REPORT
################################################################################
print_header "Generating HTML Report"
log_message "Running: mvn surefire-report:report-only -DskipTests"
# Generate HTML report from surefire XML files (without re-running tests)
if mvn surefire-report:report-only -DskipTests 2>&1 | tee -a "${DETAIL_LOG}"; then
log_message "[OK] HTML report generated"
# Copy HTML reports to test-results directory for easy access
HTML_REPORT_DIR="${LOG_DIR}/html-reports"
mkdir -p "${HTML_REPORT_DIR}"
# Copy reports from both modules
if [ -f "obp-api/target/surefire-reports/surefire.html" ]; then
cp "obp-api/target/surefire-reports/surefire.html" "${HTML_REPORT_DIR}/obp-api-report.html"
# Also copy CSS, JS, images for proper rendering
cp -r "obp-api/target/surefire-reports/css" "${HTML_REPORT_DIR}/" 2>/dev/null || true
cp -r "obp-api/target/surefire-reports/js" "${HTML_REPORT_DIR}/" 2>/dev/null || true
cp -r "obp-api/target/surefire-reports/images" "${HTML_REPORT_DIR}/" 2>/dev/null || true
cp -r "obp-api/target/surefire-reports/fonts" "${HTML_REPORT_DIR}/" 2>/dev/null || true
cp -r "obp-api/target/surefire-reports/img" "${HTML_REPORT_DIR}/" 2>/dev/null || true
log_message " - obp-api report: ${HTML_REPORT_DIR}/obp-api-report.html"
fi
if [ -f "obp-commons/target/surefire-reports/surefire.html" ]; then
cp "obp-commons/target/surefire-reports/surefire.html" "${HTML_REPORT_DIR}/obp-commons-report.html"
log_message " - obp-commons report: ${HTML_REPORT_DIR}/obp-commons-report.html"
fi
# Also check for site reports location (alternative naming)
if [ -f "obp-api/target/site/surefire-report.html" ]; then
cp "obp-api/target/site/surefire-report.html" "${HTML_REPORT_DIR}/obp-api-report.html"
log_message " - obp-api report: ${HTML_REPORT_DIR}/obp-api-report.html"
fi
if [ -f "obp-commons/target/site/surefire-report.html" ]; then
cp "obp-commons/target/site/surefire-report.html" "${HTML_REPORT_DIR}/obp-commons-report.html"
log_message " - obp-commons report: ${HTML_REPORT_DIR}/obp-commons-report.html"
fi
else
log_message "[WARNING] Failed to generate HTML report"
fi
log_message ""
2025-12-21 11:58:44 +00:00
# Stop background monitor by removing flag file
rm -f "${MONITOR_FLAG}"
sleep 1
kill $MONITOR_PID 2>/dev/null || true
wait $MONITOR_PID 2>/dev/null || true
2025-12-21 10:59:41 +00:00
2025-12-21 10:30:01 +00:00
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
DURATION_MIN=$((DURATION / 60))
DURATION_SEC=$((DURATION % 60))
# Update title with final results (no suite/scenario name for Complete phase)
2025-12-21 10:59:41 +00:00
FINAL_ELAPSED=$(printf "%dm %ds" $DURATION_MIN $DURATION_SEC)
2025-12-21 11:58:44 +00:00
# Build final counts with module context
FINAL_COMMONS=$(sed -n '/Building Open Bank Project Commons/,/Building Open Bank Project API/{/Tests: succeeded/p;}' "${DETAIL_LOG}" 2>/dev/null | sed 's/.*succeeded //' | sed 's/,.*//' | head -1)
FINAL_API=$(sed -n '/Building Open Bank Project API/,/OBP Http4s Runner/{/Tests: succeeded/p;}' "${DETAIL_LOG}" 2>/dev/null | sed 's/.*succeeded //' | sed 's/,.*//' | tail -1)
2025-12-21 11:58:44 +00:00
FINAL_COUNTS=""
[ -n "$FINAL_COMMONS" ] && FINAL_COUNTS="commons:+${FINAL_COMMONS}"
[ -n "$FINAL_API" ] && FINAL_COUNTS="${FINAL_COUNTS:+${FINAL_COUNTS} }api:+${FINAL_API}"
# Record complete phase start and change to green for completion phase
record_phase_time "complete"
set_terminal_style "Complete"
update_terminal_title "Complete" "$FINAL_ELAPSED" "$FINAL_COUNTS" "" ""
2025-12-21 10:59:41 +00:00
################################################################################
2025-12-22 21:28:03 +00:00
# GENERATE SUMMARY (using DRY function)
2025-12-21 10:59:41 +00:00
################################################################################
2025-12-22 21:28:03 +00:00
if generate_summary "${DETAIL_LOG}" "${SUMMARY_LOG}" "$START_TIME" "$END_TIME"; then
2025-12-21 10:30:01 +00:00
EXIT_CODE=0
else
EXIT_CODE=1
fi
# Record end time for complete phase
record_phase_time "end"
2025-12-21 10:30:01 +00:00
log_message ""
2025-12-21 11:58:44 +00:00
log_message "Logs saved to:"
log_message " ${DETAIL_LOG}"
log_message " ${SUMMARY_LOG}"
if [ -f "${FAILED_TESTS_FILE}" ]; then
log_message " ${FAILED_TESTS_FILE}"
fi
if [ -d "${LOG_DIR}/html-reports" ]; then
log_message ""
log_message "HTML Reports:"
for report in "${LOG_DIR}/html-reports"/*.html; do
[ -f "$report" ] && log_message " $report"
done
fi
2025-12-21 10:30:01 +00:00
echo ""
exit ${EXIT_CODE}