Skip to content
60 changes: 60 additions & 0 deletions codebase_rag/tests/test_jvm_trace_maven_recipe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""The JVM tracing guide's Maven recipe reaches the forked test JVM.

Surefire runs the tests in a forked JVM (`forkCount=1` by default), which
takes its flags from `argLine`. `MAVEN_OPTS` only sets the JVM running Maven
itself, so the guide's `MAVEN_OPTS='-javaagent:...' mvn test` attached the
agent to Maven and traced no test method (issue #2876).
"""

from __future__ import annotations

from pathlib import Path

_GUIDE = Path(__file__).resolve().parents[2] / "docs" / "guide" / "dynamic-tracing.md"


def _jvm_recipe() -> str:
"""The attach recipe and the prose under it, up to the section's demo."""
text = _GUIDE.read_text(encoding="utf-8")
start = text.index("Attach it to any JVM workload")
return text[start : text.index("\n![", start)]


def test_maven_passes_the_agent_through_surefire_arg_line() -> None:
recipe = _jvm_recipe()
assert "mvn test -DargLine='-javaagent:" in recipe, recipe
# The options carry a `;`, which the quotes keep from ending the command.
assert "include=com.example;repo=" in recipe.split("-DargLine='", 1)[1]


def test_maven_opts_is_not_offered_for_a_forked_test_run() -> None:
recipe = _jvm_recipe()
assert "MAVEN_OPTS='-javaagent" not in recipe, recipe
assert "forkCount=0" in recipe, recipe


def test_a_pom_that_already_sets_arg_line_is_covered() -> None:
# JaCoCo and others set argLine; replacing it would drop their agent.
assert "@{argLine}" in _jvm_recipe()


def test_flags_already_in_the_surefire_arg_line_are_kept() -> None:
# `@{argLine}` reads the property a plugin sets, not the flags the element
# held, and without that property the test JVM cannot start. A pom's
# `<argLine>` also wins over `-DargLine`, so those flags need the agent.
recipe = _jvm_recipe()
assert "<argLine>-Xmx512m -javaagent:...</argLine>" in recipe, recipe
assert "overrides `-DargLine`" in recipe, recipe


def test_the_test_jvm_must_be_new_enough_for_the_agent() -> None:
# The agent is built with `--release 24`; an older forked test JVM, which
# a toolchain can pick even when Maven itself runs on JDK 24, aborts with
# UnsupportedClassVersionError before any test runs.
recipe = _jvm_recipe()
assert "JDK 24 or newer" in recipe, recipe
assert "toolchain" in recipe, recipe


def test_the_gradle_line_is_unchanged() -> None:
assert "test { jvmArgs" in _jvm_recipe()
22 changes: 21 additions & 1 deletion docs/guide/dynamic-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,30 @@ Attach it to any JVM workload, most usefully a test run:

```bash
java -javaagent:build/cgr-jvm-agent.jar="include=com.example;repo=/path/to/your-repo" ...
# Maven: MAVEN_OPTS='-javaagent:...' mvn test
# Maven: Surefire runs the tests in a forked JVM, so pass the agent through argLine
mvn test -DargLine='-javaagent:build/cgr-jvm-agent.jar=include=com.example;repo=/path/to/your-repo'
Comment thread
vitali87 marked this conversation as resolved.
Resolved
# Gradle: add the same -javaagent flag to test { jvmArgs ... }
```

The agent is built for JDK 24, so the JVM it attaches to must be
JDK 24 or newer too; an older one refuses to load it and the run fails before
any test. For a test run that is the forked test JVM, which uses the JDK
running Maven or Gradle unless a toolchain picks another.

`MAVEN_OPTS` sets only the JVM that runs Maven itself, not the one Surefire
forks for the tests (`forkCount=1` by default), so an agent passed there
traces no test method; it reaches the tests only with `-DargLine` (or with
`-DforkCount=0`, which runs them inside Maven's JVM). Keep the quotes around
the `argLine` value: the `;` between the agent options would otherwise end
the shell command. To set it once, put it in the Surefire configuration of
`pom.xml`, as `<argLine>-javaagent:...</argLine>`. An `<argLine>` already
there overrides `-DargLine`, so add the agent to it after the flags it holds,
as in `<argLine>-Xmx512m -javaagent:...</argLine>`. When a plugin sets the
`argLine` property instead (JaCoCo does, for its own agent), keep its value
with `<argLine>@{argLine} -javaagent:...</argLine>`. `@{argLine}` reads only
that property, not the flags the element held before, and without the
property the test JVM does not start.


![make jvm-agent, then jlox run under the agent and cgr trace ingest, revealing the visitor dispatch from Expr.Call.accept to Interpreter.visitCallExpr](../assets/demos/dynamic-tracing-jvm.gif)

Expand Down
Loading