Skip to content

Commit eaef258

Browse files
authored
Fix Github CI integration tests
Better use of memory, consistent runs of integration tests
2 parents d7e232c + 28b12a6 commit eaef258

File tree

7 files changed

+189
-31
lines changed

7 files changed

+189
-31
lines changed

build.gradle

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ subprojects {
105105
// gradle might stop the test run due to the failFast but still concludes with BUILD SUCCESSFUL (if the retry is successful)
106106
failFast = false
107107
useJUnitPlatform()
108-
jvmArgs += ["-Xmx1024m",
108+
// Reduced from 1024m to 640m - unit tests don't need as much as integration tests
109+
jvmArgs += ["-Xmx640m",
109110
"-XX:+StartAttachListener",
110111
"-XX:+HeapDumpOnOutOfMemoryError",
111112
"-XX:HeapDumpPath=/var/log/uaa-tests.hprof"
@@ -120,8 +121,21 @@ subprojects {
120121
}
121122

122123
tasks.register('integrationTest', Test) {
124+
dependsOn subprojects.integrationTest
125+
123126
useJUnitPlatform()
124127

128+
// This prevents integrationTests from hanging indefinitely
129+
timeout = Duration.ofMinutes(180)
130+
131+
// Integration test workers need same memory as unit tests
132+
// Actual CI configuration is controlled via integration_tests.sh script
133+
jvmArgs += ["-Xmx640m",
134+
"-XX:+StartAttachListener",
135+
"-XX:+HeapDumpOnOutOfMemoryError",
136+
"-XX:HeapDumpPath=/var/log/uaa-tests.hprof"
137+
]
138+
125139
// Enable JaCoCo for integration tests
126140
jacoco {
127141
enabled = true
@@ -198,10 +212,6 @@ tasks.register('manifests', Copy) {
198212
into("build/sample-manifests")
199213
}
200214

201-
tasks.register('integrationTest', Test) {
202-
dependsOn subprojects.integrationTest
203-
}
204-
205215
tasks.register('cleanBootTomcatDir') {
206216
String tomcatBase = file("scripts/boot/tomcat/").getAbsolutePath()
207217
delete(java.nio.file.Path.of(tomcatBase))

dependencies.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ext {
55

66
// Versions shared between multiple dependencies
77
versions.apacheDsVersion = "2.0.0.AM27"
8-
versions.bouncyCastleFipsVersion = "2.1.1"
8+
versions.bouncyCastleFipsVersion = "2.1.2"
99
versions.bouncyCastlePkixFipsVersion = "2.1.9"
1010
versions.bouncyCastleTlsFipsVersion = "2.1.20"
1111
versions.springBootVersion = "3.5.7"

gradle.properties

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
version=0.0.0
22

33
# Required for LdapMockMvcTests when asserting it can find a user in a different language
4-
org.gradle.jvmargs=-Dfile.encoding=utf8 -XX:+StartAttachListener -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/uaa-tests.hprof
4+
# Optimized for Gradle 9.0 + Kotlin 2.2 to prevent test hanging and classloading deadlocks
5+
# CICompilerCount=2 and ProcessReaper flag prevent thread contention in containers
6+
org.gradle.jvmargs=-Dfile.encoding=utf8 \
7+
-XX:+StartAttachListener \
8+
-XX:+HeapDumpOnOutOfMemoryError \
9+
-XX:HeapDumpPath=/var/log/uaa-tests.hprof \
10+
-Xmx1024m \
11+
-Xms1024m \
12+
-XX:MaxMetaspaceSize=256m \
13+
-XX:+UseG1GC \
14+
-XX:MaxGCPauseMillis=100 \
15+
-XX:ParallelGCThreads=2 \
16+
-XX:CICompilerCount=2 \
17+
-Djdk.lang.processReaperUseDefaultStackSize=true
518

6-
org.gradle.parallel=true
19+
# Disable parallel execution to prevent classloading deadlocks with Kotlin 2.2
20+
org.gradle.parallel=false
721

822
signing.keyId=
923
signing.password=
@@ -12,4 +26,5 @@ ossrhUsername=
1226
ossrhPassword=
1327
group=org.cloudfoundry.identity
1428
archivesBaseName="uaa"
15-
org.gradle.workers.max=6
29+
# Limit workers to 2 for memory-constrained environments (CI containers)
30+
org.gradle.workers.max=2

scripts/integration_tests.sh

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ set -eu
66
# Global env vars:
77
# UAA_GRADLE_INT_TEST_COMMAND: Gradle command to run integration tests (default: integrationTest)
88
# this could include :cloudfoundry-identity-server:integrationTest --tests to run specific tests
9+
# jvm_heap: JVM heap size for UAA boot server (default: 640m)
10+
# jvm_metaspace: JVM metaspace size for UAA boot server (default: 192m)
11+
# gradle_heap: JVM heap size for Gradle daemon (default: 1024m)
12+
# gradle_test_heap: JVM heap size for Gradle test workers (default: 640m)
913
#######################################
1014
function main() {
1115
local script_dir; script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
@@ -24,18 +28,38 @@ function main() {
2428

2529
local wd launch_boot assemble_code integration_test_code
2630
wd=$(pwd)
27-
echo "Setting heap to ${jvm_heap:=768m}"
28-
echo "Setting metaspace to ${jvm_metaspace:=256m}"
31+
temp_dir=${script_dir}/tmp
32+
mkdir -p "${temp_dir}"
33+
34+
# Memory settings optimized for Gradle 9.0 with Kotlin 2.2
35+
# Boot server needs enough memory to handle test requests without crashing
36+
# Increased Gradle daemon heap to 1GB to prevent hanging with 2 workers
37+
# --no-configuration-cache prevents stale Kotlin compiler state reuse between daemon processes
38+
# logging.manager is set to org.apache.logging.log4j.jul.LogManager to prevent log4j2 from using java.util.logging
39+
echo "Setting boot heap to ${jvm_heap:=640m}"
40+
echo "Setting boot metaspace to ${jvm_metaspace:=192m}"
41+
echo "Setting Gradle daemon heap to ${gradle_heap:=1024m}"
42+
echo "Setting test worker heap to ${gradle_test_heap:=640m}"
2943

3044
readonly launch_boot="nohup java \
31-
-XX:+UseParallelGC \
32-
-Xmx${jvm_heap} \
45+
-XX:+UseG1GC \
46+
-XX:G1HeapRegionSize=1m \
47+
-Xms64m -Xmx${jvm_heap} \
3348
-XX:MaxMetaspaceSize=${jvm_metaspace} \
49+
-XX:MetaspaceSize=${jvm_metaspace} \
50+
-XX:+UseStringDeduplication \
51+
-XX:MaxGCPauseMillis=200 \
3452
-XX:+HeapDumpOnOutOfMemoryError \
3553
-XX:HeapDumpPath=${wd} \
3654
-DCLOUDFOUNDRY_CONFIG_PATH=${wd}/scripts/boot \
55+
-Dlogging.config=${wd}/scripts/boot/log4j2.properties \
56+
-Dlog4j.configurationFile=${wd}/scripts/boot/log4j2.properties \
57+
-Dlog4j2.formatMsgNoLookups=true \
58+
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
3759
-DSECRETS_DIR=${wd}/scripts/boot \
3860
-Djava.security.egd=file:/dev/./urandom \
61+
-Djava.io.tmpdir=${temp_dir} \
62+
-Dorg.bouncycastle.native.loader.install_dir=${temp_dir} \
3963
-Dmetrics.perRequestMetrics=true \
4064
-Dserver.servlet.context-path=/uaa \
4165
-Dserver.tomcat.basedir=${wd}/scripts/boot/tomcat \
@@ -50,46 +74,89 @@ function main() {
5074
-jar ${wd}/uaa/build/libs/cloudfoundry-identity-uaa-0.0.0.war \
5175
> boot.log 2>&1 &"
5276

77+
# Explicit Gradle daemon memory for Kotlin 2.2 with additional GC tuning
5378
readonly assemble_code="./gradlew '-Dspring.profiles.active=${test_profile}' \
5479
'-Djava.security.egd=file:/dev/./urandom' \
80+
'-Dorg.gradle.jvmargs=-Dfile.encoding=utf8 -Xms64m -Xmx${gradle_heap} -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=100' \
5581
assemble \
82+
--no-watch-fs \
5683
--no-daemon \
57-
--max-workers=4 \
84+
--no-configuration-cache \
85+
--max-workers=2 \
5886
--stacktrace \
5987
--console=plain"
6088

89+
# Explicit memory limits for test JVMs with GC tuning and classloader fixes
90+
# All flags required to prevent classloading deadlocks and thread starvation during test init
91+
# --no-configuration-cache prevents stale Kotlin compiler state reuse between daemon processes
92+
readonly compile_test_code="./gradlew \
93+
'-Dspring.profiles.active=${test_profile}' \
94+
'-Djava.security.egd=file:/dev/./urandom' \
95+
'-DskipUaaAutoStart=true' \
96+
'-Dorg.gradle.jvmargs=-Dfile.encoding=utf8 -Xms64m -Xmx${gradle_test_heap} -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:ParallelGCThreads=2 -XX:CICompilerCount=2 -Djdk.lang.processReaperUseDefaultStackSize=true' \
97+
'-Dorg.gradle.daemon.idletimeout=300000' \
98+
'-Dorg.gradle.parallel=false' \
99+
'-Dorg.gradle.workers.max=2' \
100+
clean assemble compileTestJava \
101+
--no-watch-fs \
102+
--no-daemon \
103+
--no-configuration-cache \
104+
--max-workers=2 \
105+
--stacktrace \
106+
--console=plain"
107+
108+
# Explicit memory limits for test JVMs with GC tuning and classloader fixes
109+
# All flags required to prevent classloading deadlocks and thread starvation during test init
61110
readonly integration_test_code="./gradlew \
62111
'-Dspring.profiles.active=${test_profile}' \
63112
'-Djava.security.egd=file:/dev/./urandom' \
64113
'-DskipUaaAutoStart=true' \
114+
'-Dorg.gradle.jvmargs=-Dfile.encoding=utf8 -Xms64m -Xmx${gradle_test_heap} -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:ParallelGCThreads=2 -XX:CICompilerCount=2 -Djdk.lang.processReaperUseDefaultStackSize=true' \
115+
'-Dorg.gradle.daemon.idletimeout=300000' \
116+
'-Dorg.gradle.parallel=false' \
117+
'-Dorg.gradle.workers.max=2' \
65118
${UAA_GRADLE_INT_TEST_COMMAND:-integrationTest} \
66-
--stacktrace \
119+
--no-watch-fs \
67120
--no-daemon \
121+
--no-configuration-cache \
122+
--max-workers=2 \
123+
--stacktrace \
68124
--console=plain"
69125

70-
set -x
71126
if [[ "${RUN_TESTS:-true}" = 'true' ]]; then
127+
set -x
72128
eval "$assemble_code"
73129

74-
# Always start the boot server before running integration tests
130+
# Start and ensure the boot server is running before integration tests
75131
eval "$launch_boot"
76132
echo $! > boot.pid
133+
{ set +x; } 2>/dev/null
134+
77135
if is_boot_running ; then
78136
echo "Boot started. Can continue to run tests."
79137
else
80-
echo "Boot did not start - failing"
138+
echo "Boot did not start, failing"
81139
cat boot.log
82140
exit 1
83141
fi
84142

143+
if [[ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]]; then
144+
export DBUS_SESSION_BUS_ADDRESS=/dev/null
145+
fi
146+
set -x
147+
eval "$compile_test_code"
85148
eval "$integration_test_code"
86-
149+
{ set +x; } 2>/dev/null
150+
87151
# Clean up: kill the boot server
88152
if [[ -f boot.pid ]]; then
89-
kill -9 "$(cat boot.pid)" || true
153+
local pid; pid=$(cat boot.pid)
154+
echo "Sending SIGKILL (kill -9) to UAA process (pid=${pid})"
155+
kill -9 "${pid}" || true
90156
rm boot.pid
91157
fi
92158
else
159+
set -x
93160
echo "$integration_test_code"
94161
bash
95162
fi

scripts/lib_util_helper.sh

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,26 @@
22
set -eu
33

44
########################################
5-
# Check if Boot has started by looking for a specific line in the log file
5+
# Check if Boot has started by checking if the port is responding
66
##########################################
77
function is_boot_running() {
8-
local log_file="boot.log"
9-
local target_line="Started UaaBootApplication"
8+
local port=${PORT:-8080}
109
local timeout=600 # Timeout in seconds
1110

1211
local start_time
1312
start_time=$(date +%s)
1413

14+
echo
15+
echo "Waiting for the UAA server to start, only partial log messages will be shown as it progresses:"
1516
while true; do
16-
if grep "$target_line" "$log_file"; then
17-
echo "Boot Start was found in the log file."
17+
# Use curl to check if the port is responding
18+
# Any HTTP response (even 4xx/5xx) indicates the server is running
19+
if curl -ks --max-time 5 -o /dev/null --connect-timeout 2 -u "admin:adminsecret" \
20+
--data "client_id=admin&grant_type=client_credentials" \
21+
-X POST "http://localhost:${port}/uaa/oauth/token" 2>/dev/null; then
22+
echo
23+
echo "Boot is running on port ${port}."
24+
grep "Started UaaBootApplication" boot.log
1825
return 0
1926
fi
2027

@@ -23,15 +30,40 @@ function is_boot_running() {
2330
elapsed_time=$((current_time - start_time))
2431

2532
if [[ "$elapsed_time" -ge "$timeout" ]]; then
26-
echo "Timeout reached. Boot did not start"
33+
echo
34+
echo "Timeout reached. Boot did not start on port ${port}"
35+
curl -ksS --max-time 5 --connect-timeout 2 -u "admin:adminsecret" \
36+
--data "client_id=admin&grant_type=client_credentials" \
37+
-X POST "http://localhost:${port}/uaa/info" || true
38+
39+
thread_dump_on_boot_pid
2740
return 1
2841
fi
2942

30-
tail -n 1 "$log_file"
43+
tail -n 1 boot.log
3144
sleep 1 # Check every second
3245
done
3346
}
3447

48+
########################################
49+
# thread_dump_on_boot_pid
50+
# Display Memeory info and Request a thread dump on the pid in boot.pid
51+
##########################################
52+
function thread_dump_on_boot_pid() {
53+
local pid
54+
pid=$(cat boot.pid)
55+
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
56+
echo "Collecting JVM diagnostics..."
57+
jstat -gccause "${pid}" 2>/dev/null || true
58+
jstat -gccapacity "${pid}" 2>/dev/null || true
59+
jstat -gcmetacapacity "${pid}" 2>/dev/null || true
60+
echo "Sending SIGQUIT (kill -3) to Thread Dump on UAA process (pid=${pid})"
61+
kill -3 "$pid" || true
62+
# Wait a moment for the thread dump to be written
63+
sleep 2
64+
fi
65+
}
66+
3567
########################################
3668
# setup_hosts_file
3769
# Appends test-zone and other necessary host entries to /etc/hosts

scripts/unit_tests.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ function main() {
2222
start_ldap
2323

2424
set -x
25+
26+
./gradlew "-Dspring.profiles.active=${test_profile}" \
27+
"-Djava.security.egd=file:/dev/./urandom" \
28+
clean assemble compileTestJava \
29+
--stacktrace \
30+
--no-daemon \
31+
--console=plain
32+
2533
./gradlew "-Dspring.profiles.active=${test_profile}" \
2634
"-Djava.security.egd=file:/dev/./urandom" \
2735
${UAA_GRADLE_UNIT_TEST_COMMAND:-test} \

uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/feature/DefaultIntegrationTestConfig.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,45 @@ public UaaWebDriver webDriver() {
7272
private static ChromeOptions getChromeOptions() {
7373
ChromeOptions options = new ChromeOptions();
7474
options.addArguments(
75-
"--verbose",
7675
// Comment the following line to run selenium test browser in Headed Mode
77-
"--headless",
76+
"--headless=new", // Use new headless mode (more stable)
7877
"--guest", //attempt to disable password checkups that disrupt the flow
7978
"--disable-web-security",
8079
"--ignore-certificate-errors",
8180
"--allow-running-insecure-content",
8281
"--allow-insecure-localhost",
83-
"--no-sandbox",
82+
"--no-sandbox", // Required for Docker/CI environments
8483
"--disable-gpu",
85-
"--remote-allow-origins=*"
84+
"--remote-allow-origins=*",
85+
"--disable-dev-shm-usage", // Overcome limited resource problems in Docker
86+
// Additional stability flags
87+
"--disable-extensions",
88+
"--disable-software-rasterizer",
89+
"--disable-background-timer-throttling",
90+
"--disable-backgrounding-occluded-windows",
91+
"--disable-renderer-backgrounding",
92+
"--disable-blink-features=AutomationControlled",
93+
"--disable-features=TranslateUI",
94+
// Hang detection and renderer stability flags
95+
"--disable-hang-monitor", // Prevents Chrome from killing "hung" renderer processes (useful for slow backend responses)
96+
"--disable-background-networking", // Reduces background network activity that could interfere with test requests
97+
"--disable-features=RendererScheduling", // Disables aggressive renderer scheduling that might cause timeouts
98+
"--run-all-compositor-stages-before-draw", // Ensures all rendering stages complete before drawing (prevents partial renders)
99+
"--disable-prompt-on-repost",
100+
"--disable-sync",
101+
"--disable-component-extensions-with-background-pages",
102+
"--force-color-profile=srgb",
103+
"--no-first-run",
104+
"--no-default-browser-check",
105+
"--disable-default-apps",
106+
"--disable-popup-blocking",
107+
"--test-type",
108+
"--disable-infobars"
86109
);
87110
options.setAcceptInsecureCerts(true);
111+
112+
// Set page load strategy to 'normal' to ensure proper page load detection
113+
options.setPageLoadStrategy(org.openqa.selenium.PageLoadStrategy.NORMAL);
88114

89115
return options;
90116
}

0 commit comments

Comments
 (0)