diff --git a/build.gradle b/build.gradle index 1077d4579a9..c86582437fb 100644 --- a/build.gradle +++ b/build.gradle @@ -21,8 +21,6 @@ buildscript { apply(from: "dependencies.gradle") -def applicationPort = project.hasProperty('port') ? project.getProperty('port').toInteger() : 8080 - allprojects { apply(plugin: "io.spring.dependency-management") apply(plugin: "org.barfuin.gradle.jacocolog") @@ -122,6 +120,9 @@ subprojects { tasks.register('integrationTest', Test) { useJUnitPlatform() + // This prevents integrationTests from hanging indefinitely + timeout = Duration.ofMinutes(180) + // Enable JaCoCo for integration tests jacoco { enabled = true diff --git a/dependencies.gradle b/dependencies.gradle index 0ab7517ff96..4220c3cae54 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -5,7 +5,7 @@ ext { // Versions shared between multiple dependencies versions.apacheDsVersion = "2.0.0.AM27" -versions.bouncyCastleFipsVersion = "2.1.1" +versions.bouncyCastleFipsVersion = "2.1.2" versions.bouncyCastlePkixFipsVersion = "2.1.9" versions.bouncyCastleTlsFipsVersion = "2.1.20" versions.springBootVersion = "3.5.7" diff --git a/scripts/integration_tests.sh b/scripts/integration_tests.sh index 50784221885..5c88a91f4bc 100755 --- a/scripts/integration_tests.sh +++ b/scripts/integration_tests.sh @@ -22,20 +22,28 @@ function main() { pushd "$(dirname ${script_dir})" start_ldap - local wd launch_boot assemble_code integration_test_code + local wd launch_boot compile_assemble_code run_integration_tests wd=$(pwd) + temp_dir=${script_dir}/tmp + mkdir -p "${temp_dir}" echo "Setting heap to ${jvm_heap:=768m}" echo "Setting metaspace to ${jvm_metaspace:=256m}" readonly launch_boot="nohup java \ - -XX:+UseParallelGC \ + -XX:+UseG1GC \ + -XX:G1HeapRegionSize=1m \ -Xmx${jvm_heap} \ -XX:MaxMetaspaceSize=${jvm_metaspace} \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=${wd} \ -DCLOUDFOUNDRY_CONFIG_PATH=${wd}/scripts/boot \ + -Dlogging.config=${wd}/scripts/boot/log4j2.properties \ + -Dlog4j.configurationFile=${wd}/scripts/boot/log4j2.properties \ + -Dlog4j2.formatMsgNoLookups=true \ -DSECRETS_DIR=${wd}/scripts/boot \ -Djava.security.egd=file:/dev/./urandom \ + -Djava.io.tmpdir=${temp_dir} \ + -Dorg.bouncycastle.native.loader.install_dir=${temp_dir} \ -Dmetrics.perRequestMetrics=true \ -Dserver.servlet.context-path=/uaa \ -Dserver.tomcat.basedir=${wd}/scripts/boot/tomcat \ @@ -50,47 +58,65 @@ function main() { -jar ${wd}/uaa/build/libs/cloudfoundry-identity-uaa-0.0.0.war \ > boot.log 2>&1 &" - readonly assemble_code="./gradlew '-Dspring.profiles.active=${test_profile}' \ + readonly compile_assemble_code="./gradlew '-Dspring.profiles.active=${test_profile}' \ '-Djava.security.egd=file:/dev/./urandom' \ - assemble \ + assemble compileTestJava \ + --no-watch-fs \ --no-daemon \ --max-workers=4 \ --stacktrace \ --console=plain" - readonly integration_test_code="./gradlew \ + readonly run_integration_tests="./gradlew \ '-Dspring.profiles.active=${test_profile}' \ '-Djava.security.egd=file:/dev/./urandom' \ '-DskipUaaAutoStart=true' \ ${UAA_GRADLE_INT_TEST_COMMAND:-integrationTest} \ - --stacktrace \ + --no-watch-fs \ --no-daemon \ + --max-workers=2 \ + --stacktrace \ --console=plain" - set -x if [[ "${RUN_TESTS:-true}" = 'true' ]]; then - eval "$assemble_code" + set -x + eval "$compile_assemble_code" - # Always start the boot server before running integration tests + # Start and ensure the boot server is running before integration tests eval "$launch_boot" echo $! > boot.pid + { set +x; } 2>/dev/null + if is_boot_running ; then echo "Boot started. Can continue to run tests." else - echo "Boot did not start - failing" + echo "Boot did not start, failing" cat boot.log exit 1 fi - eval "$integration_test_code" + if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then + export DBUS_SESSION_BUS_ADDRESS=/dev/null + fi + set -x + export GRADLE_OPTS="\ + -XX:+UseG1GC \ + -XX:G1HeapRegionSize=1m \ + -Xmx1024m \ + " + eval "$run_integration_tests" + { set +x; } 2>/dev/null # Clean up: kill the boot server if [[ -f boot.pid ]]; then - kill -9 "$(cat boot.pid)" || true + local pid; pid=$(cat boot.pid) + echo "Sending SIGKILL (kill -9) to UAA process (pid=${pid})" + kill -9 "${pid}" || true rm boot.pid fi else - echo "$integration_test_code" + set -x + echo "$run_integration_tests" bash fi popd diff --git a/scripts/lib_util_helper.sh b/scripts/lib_util_helper.sh index dd7a6810646..48ffeb511a3 100755 --- a/scripts/lib_util_helper.sh +++ b/scripts/lib_util_helper.sh @@ -2,19 +2,26 @@ set -eu ######################################## -# Check if Boot has started by looking for a specific line in the log file +# Check if Boot has started by checking if the port is responding ########################################## function is_boot_running() { - local log_file="boot.log" - local target_line="Started UaaBootApplication" + local port=${PORT:-8080} local timeout=600 # Timeout in seconds local start_time start_time=$(date +%s) + echo + echo "Waiting for the UAA server to start, only partial log messages will be shown as it progresses:" while true; do - if grep "$target_line" "$log_file"; then - echo "Boot Start was found in the log file." + # Use curl to check if the port is responding + # Any HTTP response (even 4xx/5xx) indicates the server is running + if curl -ks --max-time 5 -o /dev/null --connect-timeout 2 -u "admin:adminsecret" \ + --data "client_id=admin&grant_type=client_credentials" \ + -X POST "http://localhost:${port}/uaa/oauth/token" 2>/dev/null; then + echo + echo "Boot is running on port ${port}." + grep "Started UaaBootApplication" boot.log return 0 fi @@ -23,15 +30,40 @@ function is_boot_running() { elapsed_time=$((current_time - start_time)) if [[ "$elapsed_time" -ge "$timeout" ]]; then - echo "Timeout reached. Boot did not start" + echo + echo "Timeout reached. Boot did not start on port ${port}" + curl -ksS --max-time 5 --connect-timeout 2 -u "admin:adminsecret" \ + --data "client_id=admin&grant_type=client_credentials" \ + -X POST "http://localhost:${port}/uaa/info" || true + + thread_dump_on_boot_pid return 1 fi - tail -n 1 "$log_file" + tail -n 1 boot.log sleep 1 # Check every second done } +######################################## +# thread_dump_on_boot_pid +# Display Memeory info and Request a thread dump on the pid in boot.pid +########################################## +function thread_dump_on_boot_pid() { + local pid + pid=$(cat boot.pid) + if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then + echo "Collecting JVM diagnostics..." + jstat -gccause "${pid}" 2>/dev/null || true + jstat -gccapacity "${pid}" 2>/dev/null || true + jstat -gcmetacapacity "${pid}" 2>/dev/null || true + echo "Sending SIGQUIT (kill -3) to Thread Dump on UAA process (pid=${pid})" + kill -3 "$pid" || true + # Wait a moment for the thread dump to be written + sleep 2 + fi +} + ######################################## # setup_hosts_file # Appends test-zone and other necessary host entries to /etc/hosts diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java index c961e93377d..4b05e9e74ae 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java @@ -72,19 +72,45 @@ public UaaWebDriver webDriver() { private static ChromeOptions getChromeOptions() { ChromeOptions options = new ChromeOptions(); options.addArguments( - "--verbose", // Comment the following line to run selenium test browser in Headed Mode - "--headless", + "--headless=new", // Use new headless mode (more stable) "--guest", //attempt to disable password checkups that disrupt the flow "--disable-web-security", "--ignore-certificate-errors", "--allow-running-insecure-content", "--allow-insecure-localhost", - "--no-sandbox", + "--no-sandbox", // Required for Docker/CI environments "--disable-gpu", - "--remote-allow-origins=*" + "--remote-allow-origins=*", + "--disable-dev-shm-usage", // Overcome limited resource problems in Docker + // Additional stability flags + "--disable-extensions", + "--disable-software-rasterizer", + "--disable-background-timer-throttling", + "--disable-backgrounding-occluded-windows", + "--disable-renderer-backgrounding", + "--disable-blink-features=AutomationControlled", + "--disable-features=TranslateUI", + // Hang detection and renderer stability flags + "--disable-hang-monitor", // Prevents Chrome from killing "hung" renderer processes (useful for slow backend responses) + "--disable-background-networking", // Reduces background network activity that could interfere with test requests + "--disable-features=RendererScheduling", // Disables aggressive renderer scheduling that might cause timeouts + "--run-all-compositor-stages-before-draw", // Ensures all rendering stages complete before drawing (prevents partial renders) + "--disable-prompt-on-repost", + "--disable-sync", + "--disable-component-extensions-with-background-pages", + "--force-color-profile=srgb", + "--no-first-run", + "--no-default-browser-check", + "--disable-default-apps", + "--disable-popup-blocking", + "--test-type", + "--disable-infobars" ); options.setAcceptInsecureCerts(true); + + // Set page load strategy to 'normal' to ensure proper page load detection + options.setPageLoadStrategy(org.openqa.selenium.PageLoadStrategy.NORMAL); return options; }