Skip to content

Commit 5a3859a

Browse files
committed
Add JRE tests
1 parent 7a5b8d6 commit 5a3859a

File tree

13 files changed

+309
-125
lines changed

13 files changed

+309
-125
lines changed

cf_cli_java_plugin.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,21 @@ var commands = []Command{
228228
JMAP_COMMAND=$(find -executable -name jmap | head -1 | tr -d [:space:])
229229
# SAP JVM: Wrap everything in an if statement in case jvmmon is available
230230
JVMMON_COMMAND=$(find -executable -name jvmmon | head -1 | tr -d [:space:])
231+
# if we have neither jmap nor jvmmon, we cannot generate a heap dump and should exit with an error
232+
if [ -z "${JMAP_COMMAND}" ] && [ -z "${JVMMON_COMMAND}" ]; then
233+
echo >&2 "jvmmon or jmap are required for generating heap dump, you can modify your application manifest.yaml on the 'JBP_CONFIG_OPEN_JDK_JRE' environment variable. This could be done like this:
234+
---
235+
applications:
236+
- name: <APP_NAME>
237+
memory: 1G
238+
path: <PATH_TO_BUILD_ARTIFACT>
239+
buildpack: https://github.com/cloudfoundry/java-buildpack
240+
env:
241+
JBP_CONFIG_OPEN_JDK_JRE: '{ jre: { repository_root: "https://java-buildpack.cloudfoundry.org/openjdk-jdk/bionic/x86_64", version: 11.+ } }'
242+
243+
"
244+
exit 1
245+
fi
231246
if [ -n "${JMAP_COMMAND}" ]; then
232247
OUTPUT=$( ${JMAP_COMMAND} -dump:format=b,file=@FILE_NAME $(pidof java) ) || STATUS_CODE=$?
233248
if [ ! -s @FILE_NAME ]; then echo >&2 ${OUTPUT}; exit 1; fi
@@ -248,8 +263,24 @@ fi`,
248263
Name: "thread-dump",
249264
Description: "Generate a thread dump from a running Java application",
250265
GenerateFiles: false,
251-
SshCommand: "JSTACK_COMMAND=`find -executable -name jstack | head -1`; if [ -n \"${JSTACK_COMMAND}\" ]; then ${JSTACK_COMMAND} $(pidof java); exit 0; fi; " +
252-
"JVMMON_COMMAND=`find -executable -name jvmmon | head -1`; if [ -n \"${JVMMON_COMMAND}\" ]; then ${JVMMON_COMMAND} -pid $(pidof java) -c \"print stacktrace\"; fi",
266+
SshCommand: `JSTACK_COMMAND=$(find -executable -name jstack | head -1);
267+
JVMMON_COMMAND=$(find -executable -name jvmmon | head -1)
268+
if [ -z "${JMAP_COMMAND}" ] && [ -z "${JVMMON_COMMAND}" ]; then
269+
echo >&2 "jvmmon or jmap are required for generating heap dump, you can modify your application manifest.yaml on the 'JBP_CONFIG_OPEN_JDK_JRE' environment variable. This could be done like this:
270+
---
271+
applications:
272+
- name: <APP_NAME>
273+
memory: 1G
274+
path: <PATH_TO_BUILD_ARTIFACT>
275+
buildpack: https://github.com/cloudfoundry/java-buildpack
276+
env:
277+
JBP_CONFIG_OPEN_JDK_JRE: '{ jre: { repository_root: "https://java-buildpack.cloudfoundry.org/openjdk-jdk/bionic/x86_64", version: 11.+ } }'
278+
279+
"
280+
exit 1
281+
fi
282+
if [ -n \"${JSTACK_COMMAND}\" ]; then ${JSTACK_COMMAND} $(pidof java); exit 0; fi;
283+
if [ -n \"${JVMMON_COMMAND}\" ]; then ${JVMMON_COMMAND} -pid $(pidof java) -c \"print stacktrace\"; fi`,
253284
},
254285
{
255286
Name: "vm-info",

test/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Example output:
6464
- **`test_asprof.py`** - Async-profiler tests (SapMachine only)
6565
- **`test_cf_java_plugin.py`** - Integration and workflow tests
6666
- **`test_disk_full.py`** - Tests for disk full scenarios (e.g., heap dump with no space left)
67+
- ** `test_jre21.py`** - JRE21/non-SapMachine21-specific tests (e.g., heap dump, thread dump, etc.)
6768

6869
## Test Selection & Execution
6970

@@ -158,7 +159,7 @@ from framework.decorators import test
158159
from framework.runner import TestBase
159160

160161
class TestExample(TestBase):
161-
@test("all") # or @test("sapmachine21")
162+
@test # or @test(ine21")
162163
def test_heap_dump_basic(self, t, app):
163164
t.heap_dump("--local-dir .") \
164165
.should_succeed() \

test/apps/jre21/manifest.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
applications:
3+
- name: jre21
4+
random-route: true
5+
path: test.jar
6+
memory: 1024M
7+
buildpacks:
8+
- https://github.com/cloudfoundry/java-buildpack.git
9+
env:
10+
TARGET_RUNTIME: tomcat
11+
JBP_CONFIG_COMPONENTS: '{jres: ["JavaBuildpack::Jre::OpenJdkJRE"]}'
12+
JBP_CONFIG_OPEN_JDK_JRE: '{ jre: { version: 21.+ } }'

test/apps/jre21/test.jar

19.7 MB
Binary file not shown.

test/framework/decorators.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,25 @@
1313

1414

1515
def test(*apps, no_restart=False):
16-
"""Ultra-minimal test decorator.
16+
"""Test decorator.
1717
1818
Usage:
19-
@test("all")
20-
@test("sapmachine11", "sapmachine21")
21-
@test("all", no_restart=True) # Skip app restart after test
19+
@test
20+
@test(no_restart=True) # Skip app restart after test
2221
2322
Args:
24-
*apps: App names to test on ("all", "sapmachine11", "sapmachine21")
23+
*apps: App names to test on, defaults to "sapmachine21"
2524
no_restart: If True, skip app restart after test
2625
"""
2726

2827
# Determine which apps to test
2928
if "all" in apps or not apps:
3029
test_apps = get_available_apps()
30+
elif not apps:
31+
# If no apps specified, default to sapmachine21
32+
test_apps = ["sapmachine21"]
3133
else:
34+
# Use the provided apps directly
3235
test_apps = list(apps)
3336

3437
def decorator(test_func):

test/test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,14 @@ def disk_full(ctx):
554554
return run_command([str(PYTEST), "test_disk_full.py"] + ctx.obj["pytest_args"])
555555

556556

557+
@cli.command()
558+
@click.pass_context
559+
def jre21(ctx):
560+
"""Run JRE21-specific tests."""
561+
click.echo(f"{GREEN}Running JRE21 tests...")
562+
return run_command([str(PYTEST), "test_jre21.py"] + ctx.obj["pytest_args"])
563+
564+
557565
@cli.command()
558566
@click.pass_context
559567
def all(ctx):

test/test_asprof.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@
1111
class TestAsprofBasic(TestBase):
1212
"""Basic async-profiler functionality."""
1313

14-
# @test("sapmachine11", no_restart=True)
14+
# @test(ine11", no_restart=True)
1515
# def test_asprof_not_present(self, t, app):
1616
# """Test that async-profiler is not present in JDK 21."""
1717
# t.run(f"asprof {app} --args 'status'").should_fail().should_contain("not found")
1818

19-
@test("sapmachine21", no_restart=True)
19+
@test(no_restart=True)
2020
def test_status_no_profiling(self, t, app):
2121
"""Test asprof status when no profiling is active."""
2222
t.run(f"asprof-status {app}").should_succeed()
2323

24-
@test("sapmachine21", no_restart=True)
24+
@test(no_restart=True)
2525
def test_start_provides_stop_instruction(self, t, app):
2626
"""Test that asprof-start provides clear stop instructions."""
2727
t.run(f"asprof-start-cpu {app}").should_succeed().should_contain(f"Use 'cf java asprof-stop {app}'")
@@ -31,7 +31,7 @@ def test_start_provides_stop_instruction(self, t, app):
3131
"*.jfr"
3232
).should_not_create_file()
3333

34-
@test("sapmachine21", no_restart=True)
34+
@test(no_restart=True)
3535
def test_basic_profile(self, t, app):
3636
"""Test basic async-profiler profile start and stop."""
3737
# Start profiling
@@ -40,7 +40,7 @@ def test_basic_profile(self, t, app):
4040
# Clean up
4141
t.run(f"asprof-stop {app}").should_succeed().should_create_file("*.jfr").should_create_no_remote_files()
4242

43-
@test("sapmachine21", no_restart=True)
43+
@test(no_restart=True)
4444
def test_dry_run_commands(self, t, app):
4545
"""Test async-profiler commands dry run functionality."""
4646
commands = [
@@ -55,7 +55,7 @@ def test_dry_run_commands(self, t, app):
5555
for cmd in commands:
5656
t.run(f"{cmd} {app} --dry-run").should_succeed().should_contain("cf ssh").no_files()
5757

58-
@test("sapmachine21", no_restart=True)
58+
@test(no_restart=True)
5959
def test_asprof_error_handling(self, t, app):
6060
"""Test error messages for invalid flags."""
6161
t.run(f"asprof-start-cpu {app} --invalid-flag").should_fail().no_files().should_contain("invalid")
@@ -64,7 +64,7 @@ def test_asprof_error_handling(self, t, app):
6464
class TestAsprofProfiles(TestBase):
6565
"""Different async-profiler profiling modes."""
6666

67-
@test("sapmachine21", no_restart=True)
67+
@test(no_restart=True)
6868
def test_cpu_profiling(self, t, app):
6969
"""Test CPU profiling with async-profiler."""
7070
# Start CPU profiling
@@ -81,7 +81,7 @@ def test_cpu_profiling(self, t, app):
8181
"jdk.NativeLibrary", 10
8282
)
8383

84-
@test("sapmachine21", no_restart=True)
84+
@test(no_restart=True)
8585
def test_wall_clock_profiling(self, t, app):
8686
"""Test wall-clock profiling mode."""
8787
t.run(f"asprof-start-wall {app}").should_succeed()
@@ -90,7 +90,7 @@ def test_wall_clock_profiling(self, t, app):
9090

9191
t.run(f"asprof-stop {app} --local-dir .").should_succeed().should_create_file(f"{app}-asprof-*.jfr")
9292

93-
@test("sapmachine21", no_restart=True)
93+
@test(no_restart=True)
9494
def test_allocation_profiling(self, t, app):
9595
"""Test allocation profiling mode."""
9696
t.run(f"asprof-start-alloc {app}").should_succeed()
@@ -99,15 +99,15 @@ def test_allocation_profiling(self, t, app):
9999

100100
t.run(f"asprof-stop {app} --local-dir .").should_succeed().should_create_file(f"{app}-asprof-*.jfr")
101101

102-
@test("sapmachine21", no_restart=True)
102+
@test(no_restart=True)
103103
def test_allocation_profiling_dry_run(self, t, app):
104104
"""Test allocation profiling dry run."""
105105
# This should not create any files, just show the command
106106
t.run(f"asprof-start-alloc {app} --dry-run").should_succeed().should_contain("-e alloc").no_files()
107107
t.run(f"asprof-status {app}").should_succeed().no_files().should_contain("Profiler is not active")
108108
t.run(f"asprof-stop {app}").should_succeed()
109109

110-
@test("sapmachine21", no_restart=True)
110+
@test(no_restart=True)
111111
def test_lock_profiling(self, t, app):
112112
"""Test lock profiling mode."""
113113
t.run(f"asprof-start-lock {app}").should_succeed()
@@ -120,7 +120,7 @@ def test_lock_profiling(self, t, app):
120120
class TestAsprofAdvanced(TestBase):
121121
"""Advanced async-profiler scenarios."""
122122

123-
@test("sapmachine21", no_restart=True)
123+
@test(no_restart=True)
124124
def test_stop_without_download(self, t, app):
125125
"""Test stopping profiling without downloading results."""
126126
# Start profiling
@@ -131,7 +131,7 @@ def test_stop_without_download(self, t, app):
131131
# Stop without download
132132
t.run(f"asprof-stop {app} --no-download").should_succeed().should_not_create_file("*.jfr")
133133

134-
@test("sapmachine21", no_restart=True)
134+
@test(no_restart=True)
135135
def test_keep_remote_file(self, t, app):
136136
"""Test keeping profiling file on remote after download."""
137137
# Start profiling
@@ -144,7 +144,7 @@ def test_keep_remote_file(self, t, app):
144144
f"{app}-asprof-*.jfr"
145145
).should_create_remote_file(file_extension=".jfr")
146146

147-
@test("sapmachine21", no_restart=True)
147+
@test(no_restart=True)
148148
def test_workflow_with_multiple_checks(self, t, app):
149149
"""Test complete workflow with comprehensive checks."""
150150
# Test each step of the profiling workflow
@@ -166,7 +166,7 @@ def test_workflow_with_multiple_checks(self, t, app):
166166
class TestAsprofLifecycle(TestBase):
167167
"""Complete async-profiler workflow tests."""
168168

169-
@test("sapmachine21", no_restart=True)
169+
@test(no_restart=True)
170170
def test_full_cpu_profiling_workflow(self, t, app):
171171
"""Test complete CPU profiling workflow with validation."""
172172
# 1. Verify no profiling initially
@@ -189,7 +189,7 @@ def test_full_cpu_profiling_workflow(self, t, app):
189189
# 6. Verify profiling has stopped
190190
t.run(f"asprof-status {app}").should_succeed().no_files().should_contain("Profiler is not active")
191191

192-
@test("sapmachine21", no_restart=True)
192+
@test(no_restart=True)
193193
def test_multiple_profiling_sessions(self, t, app):
194194
"""Test running multiple profiling sessions in sequence."""
195195
profiling_modes = ["cpu", "wall", "alloc"]
@@ -207,22 +207,22 @@ def test_multiple_profiling_sessions(self, t, app):
207207
class TestAsprofCommand(TestBase):
208208
"""Tests for the general asprof command with --args (distinct from asprof-* commands)."""
209209

210-
@test("sapmachine21", no_restart=True)
210+
@test(no_restart=True)
211211
def test_asprof_help_command(self, t, app):
212212
"""Test asprof help command via --args."""
213213
t.run(f"asprof {app} --args '--help'").should_succeed().should_contain("profiler").no_files()
214214

215-
@test("sapmachine21", no_restart=True)
215+
@test(no_restart=True)
216216
def test_asprof_version_command(self, t, app):
217217
"""Test asprof version command."""
218218
t.run(f"asprof {app} --args '--version'").should_succeed().should_start_with("Async-profiler ").no_files()
219219

220-
@test("sapmachine21", no_restart=True)
220+
@test(no_restart=True)
221221
def test_asprof_status_via_args(self, t, app):
222222
"""Test asprof status via --args (different from asprof-status command)."""
223223
t.run(f"asprof {app} --args 'status'").should_succeed().no_files().should_contain("Profiler is not active")
224224

225-
@test("sapmachine21", no_restart=True)
225+
@test(no_restart=True)
226226
def test_asprof_start_auto_no_download(self, t, app):
227227
"""Test that asprof start commands automatically set no-download."""
228228
t.run(f"asprof {app} --args 'start -e cpu -f /tmp/asprof/bla.jfr'").should_succeed().no_files()
@@ -233,7 +233,7 @@ def test_asprof_start_auto_no_download(self, t, app):
233233
"*.jfr"
234234
).should_create_no_remote_files()
235235

236-
@test("sapmachine21", no_restart=True)
236+
@test(no_restart=True)
237237
def test_asprof_with_custom_output_file(self, t, app):
238238
"""Test asprof with custom output file using @FSPATH."""
239239
# Start profiling with custom file in the asprof folder
@@ -244,7 +244,7 @@ def test_asprof_with_custom_output_file(self, t, app):
244244
# Stop and download
245245
t.run(f"asprof {app} --args 'stop' --local-dir .").should_succeed().should_create_file("custom-profile.jfr")
246246

247-
@test("sapmachine21", no_restart=True)
247+
@test(no_restart=True)
248248
def test_asprof_collect_multiple_files(self, t, app):
249249
"""Test that asprof collects multiple files from the asprof folder."""
250250
# Create multiple files via asprof
@@ -259,7 +259,7 @@ def test_asprof_collect_multiple_files(self, t, app):
259259
"cpu.jfr"
260260
).should_create_file("alloc.jfr")
261261

262-
@test("sapmachine21")
262+
@test
263263
def test_asprof_keep_remote_files(self, t, app):
264264
"""Test keeping remote files with asprof."""
265265
# Generate a file and keep it
@@ -269,7 +269,7 @@ def test_asprof_keep_remote_files(self, t, app):
269269
"keep-test.jfr"
270270
).should_create_remote_file("keep-test.jfr")
271271

272-
@test("sapmachine21", no_restart=True)
272+
@test(no_restart=True)
273273
def test_asprof_invalid_args_flag_for_non_args_commands(self, t, app):
274274
"""Test that --args flag is rejected for commands that don't support it."""
275275
# asprof-start commands don't use @ARGS, so --args should be rejected
@@ -281,40 +281,40 @@ def test_asprof_invalid_args_flag_for_non_args_commands(self, t, app):
281281
class TestAsprofEdgeCases(TestBase):
282282
"""Edge cases and error conditions for async-profiler."""
283283

284-
@test("sapmachine21")
284+
@test
285285
def test_asprof_start_commands_file_flags_validation(self, t, app):
286286
"""Test that asprof-start commands reject inappropriate file flags."""
287287
# asprof-start commands have GenerateFiles=false, so some file flags should be rejected
288288
for flag in ["--keep", "--no-download"]:
289289
t.run(f"asprof-start-cpu {app} {flag}").should_fail().should_contain("not supported for asprof-start-cpu")
290290

291-
#@test("sapmachine21")
292-
#def test_asprof_stop_requires_prior_start(self, t, app):
291+
# @test
292+
# def test_asprof_stop_requires_prior_start(self, t, app):
293293
# """Test asprof-stop behavior when no profiling is active."""
294294
# t.run(f"asprof-stop {app}").should_fail().should_contain("[ERROR] Profiler has not started").no_files()
295295

296-
@test("sapmachine21")
296+
@test
297297
def test_asprof_different_event_types(self, t, app):
298298
"""Test CPU event type via asprof command."""
299299
# Test CPU event type
300300
t.run(f"asprof {app} --args 'start -e cpu'").should_succeed()
301301
time.sleep(0.5)
302302
t.run(f"asprof {app} --args 'stop'").should_succeed()
303303

304-
@test("sapmachine21", no_restart=True)
304+
@test(no_restart=True)
305305
def test_asprof_output_formats(self, t, app):
306306
"""Test JFR output format with asprof."""
307307
t.run(f"asprof {app} --args 'start -e cpu -o jfr -f @FSPATH/profile.jfr'").should_succeed()
308308
time.sleep(0.5)
309309
t.run(f"asprof {app} --args 'stop' --local-dir .").should_succeed().should_create_file("profile.jfr")
310310

311-
@test("sapmachine21", no_restart=True)
311+
@test(no_restart=True)
312312
def test_asprof_recursive_args_validation(self, t, app):
313313
"""Test that @ARGS cannot contain itself in asprof."""
314314
# This should fail due to the validation in replaceVariables
315315
t.run(f"asprof {app} --args 'echo @ARGS'").should_fail()
316316

317-
@test("sapmachine21", no_restart=True)
317+
@test(no_restart=True)
318318
def test_asprof_profiling_duration_and_interval(self, t, app):
319319
"""Test asprof with duration parameter."""
320320
# Test duration parameter
@@ -323,7 +323,7 @@ def test_asprof_profiling_duration_and_interval(self, t, app):
323323
t.run(f"asprof {app} --args 'status'").should_succeed() # Should show no active profiling
324324
t.run(f"asprof {app} --args 'stop' --local-dir .").should_succeed().should_create_file("duration.jfr")
325325

326-
@test("sapmachine21", no_restart=True)
326+
@test(no_restart=True)
327327
def test_asprof_list_command(self, t, app):
328328
# List should show available files
329329
t.run(f"asprof {app} --args 'list'").should_succeed().no_files().should_contain("Basic events:")
@@ -332,7 +332,7 @@ def test_asprof_list_command(self, t, app):
332332
class TestAsprofAdvancedFeatures(TestBase):
333333
"""Advanced async-profiler features and workflows."""
334334

335-
@test("sapmachine21", no_restart=True)
335+
@test(no_restart=True)
336336
def test_asprof_flamegraph_generation(self, t, app):
337337
"""Test flamegraph generation with asprof."""
338338
t.run(f"asprof {app} --args 'start -e cpu'").should_succeed()

0 commit comments

Comments
 (0)