diff --git a/.circleci/config.yml b/.circleci/config.yml
index 42ccd67f4..ad7717c1e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -143,8 +143,16 @@ commands:
./codecov --version
- # Upload the coverage report.
- ./codecov upload-coverage $COMMON_ARGS
+ # Prefer the aggregated multi-module report.
+ JACOCO_AGG="sqrl-testing/sqrl-coverage/target/site/jacoco-aggregate/jacoco.xml"
+ if [ -f "$JACOCO_AGG" ]; then
+ echo "=== Using aggregate JaCoCo report: $JACOCO_AGG ==="
+ ls -lh "$JACOCO_AGG"
+ ./codecov upload-coverage $COMMON_ARGS --file "$JACOCO_AGG"
+ else
+ echo "=== No aggregate JaCoCo report found; skipping coverage upload ==="
+ # Avoid uploading empty/partial reports (they can drag down overall coverage).
+ fi
# Upload the test reports.
./codecov do-upload --report-type test_results $COMMON_ARGS
@@ -289,15 +297,20 @@ jobs:
name: Auth ghcr
command: echo "$GITHUB_TOKEN" | docker login ghcr.io -u "$GITHUB_USER" --password-stdin
- run:
- name: Build with Docker images
+ name: Build Docker images
command: mvn -U clean -T1C -B install -DonlyImages -Ddocker.image.tag=local-${CIRCLE_SHA1}
- run:
name: Run container tests using TestContainers
command: |
mvn -B install -DonlyContainerE2E \
-pl :sqrl-testing-container \
+ -Dsqrl.container.coverage=true \
-Ddocker.image.tag=local-${CIRCLE_SHA1} \
-Dmcp.inspector.version=$MCP_TAG
+ - run:
+ name: Generate aggregate JaCoCo report
+ command: mvn -B -pl :sqrl-coverage -am -DskipTests -DskipITs verify
+ - save-test-results
deploy:
docker:
@@ -316,7 +329,7 @@ jobs:
echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
- run:
name: Build shaded JAR
- command: mvn -U clean -T1C -B deploy -DonlyJars ${CIRCLE_TAG:+-Prelease} -Deasyjacoco.skip=true
+ command: mvn -U clean -T1C -B deploy -DonlyJars ${CIRCLE_TAG:+-Prelease} -Djacoco.skip=true
build-images:
# use a full VM so we can run Docker / Buildx
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
deleted file mode 100644
index d477ef5cf..000000000
--- a/.mvn/extensions.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- com.marvinformatics.jacoco
- easy-jacoco-maven-plugin
- 0.1.4
-
-
diff --git a/CLAUDE.md b/CLAUDE.md
index 3d4d64644..178e9f805 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -47,11 +47,11 @@ mvn verify
# Coverage report
mvn jacoco:report
-# Test specific module (ALWAYS include -Deasyjacoco.skip when using -pl)
-mvn test -pl sqrl-planner -Deasyjacoco.skip
+# Test specific module
+mvn test -pl sqrl-planner
# Test specific test method in module
-mvn test -pl sqrl-tools/sqrl-config -Dtest=TestClassName#testMethodName -Deasyjacoco.skip
+mvn test -pl sqrl-tools/sqrl-config -Dtest=TestClassName#testMethodName
# Container tests (requires Docker images to be built)
mvn -B install -DonlyContainerE2E -pl :sqrl-testing-container -Dit.test=TestClassName
diff --git a/pom.xml b/pom.xml
index 7cc657dd0..eac20212f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -172,7 +172,6 @@
1.4.13
- 0.1.4
5.4
5.0.0
3.2.8
@@ -855,28 +854,56 @@
org/**
-
-
-
- com.marvinformatics.jacoco
- easy-jacoco-maven-plugin
- 0.1.4
-
-
-
-
-
- INSTRUCTION
- COVEREDRATIO
- 0.70
-
-
-
-
-
- true
-
-
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+
+ prepare-agent-integration
+
+ prepare-agent-integration
+
+
+
+ report
+
+ report
+
+ verify
+
+
+ report-integration
+
+ report-integration
+
+ verify
+
+
+ check
+
+ check
+
+ verify
+
+ false
+
+
+ BUNDLE
+
+
+ INSTRUCTION
+ COVEREDRATIO
+ 0.70
+
+
+
+
+
+
+
diff --git a/sqrl-cli/pom.xml b/sqrl-cli/pom.xml
index 0708a5406..da0656388 100644
--- a/sqrl-cli/pom.xml
+++ b/sqrl-cli/pom.xml
@@ -442,36 +442,33 @@
instrument
-
-
- org.jacoco
- org.jacoco.agent
- ${jacoco.version}
- runtime
-
-
-
- com.marvinformatics.jacoco
- easy-jacoco-maven-plugin
- ${easy-jacoco-maven-plugin.version}
-
-
- instrument-uber-jar
-
- instrument-jar
-
-
- ${project.build.directory}/sqrl-cli.jar
- ${project.build.directory}/sqrl-cli.jar
-
- com/datasqrl/*
-
-
-
-
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
+
+
+ instrument-uber-jar
+
+ instrument
+
+ package
+
+
+ com/datasqrl/**
+
+
+
+
+ restore-instrumented-classes
+
+ restore-instrumented-classes
+
+ verify
+
+
diff --git a/sqrl-server/sqrl-server-vertx/pom.xml b/sqrl-server/sqrl-server-vertx/pom.xml
index 6ce823190..86ab6acda 100644
--- a/sqrl-server/sqrl-server-vertx/pom.xml
+++ b/sqrl-server/sqrl-server-vertx/pom.xml
@@ -91,23 +91,29 @@
- com.marvinformatics.jacoco
- easy-jacoco-maven-plugin
- ${easy-jacoco-maven-plugin.version}
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.version}
instrument-uber-jar
- instrument-jar
+ instrument
+ package
- ${project.build.directory}/vertx-server.jar
- ${project.build.directory}/vertx-server.jar
- com/datasqrl/*
+ com/datasqrl/**
+
+ restore-instrumented-classes
+
+ restore-instrumented-classes
+
+ verify
+
diff --git a/sqrl-testing/pom.xml b/sqrl-testing/pom.xml
index 6e8e43eaf..77ea55961 100644
--- a/sqrl-testing/pom.xml
+++ b/sqrl-testing/pom.xml
@@ -30,6 +30,7 @@
sqrl-testing-integration
+ sqrl-coverage
diff --git a/sqrl-testing/sqrl-coverage/pom.xml b/sqrl-testing/sqrl-coverage/pom.xml
new file mode 100644
index 000000000..b5a80d3a8
--- /dev/null
+++ b/sqrl-testing/sqrl-coverage/pom.xml
@@ -0,0 +1,108 @@
+
+
+
+ 4.0.0
+
+
+ com.datasqrl
+ sqrl-testing
+ 1.0-SNAPSHOT
+
+
+ sqrl-coverage
+ SQRL :: Coverage
+ pom
+
+
+
+
+ com.datasqrl
+ sqrl-cli
+ ${project.version}
+
+
+ com.datasqrl
+ sqrl-planner
+ ${project.version}
+
+
+ com.datasqrl
+ sqrl-discovery
+ ${project.version}
+
+
+ com.datasqrl
+ sqrl-server-vertx-base
+ ${project.version}
+
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ report-aggregate
+
+ report-aggregate
+
+ verify
+
+
+ **/target/jacoco.exec
+ **/target/jacoco-it.exec
+ **/target/jacoco/jacoco-container.exec
+
+
+
+
+
+
+
+
+
+
+
+ include-container-testing
+
+
+ com.datasqrl
+ sqrl-testing-container
+ ${project.version}
+
+
+
+
+
+ dev
+
+
+ com.datasqrl
+ sqrl-testing-container
+ ${project.version}
+
+
+
+
+
diff --git a/sqrl-testing/sqrl-testing-container/src/test/java/com/datasqrl/container/testing/SqrlContainerTestBase.java b/sqrl-testing/sqrl-testing-container/src/test/java/com/datasqrl/container/testing/SqrlContainerTestBase.java
index d792d215f..f4147b5f9 100644
--- a/sqrl-testing/sqrl-testing-container/src/test/java/com/datasqrl/container/testing/SqrlContainerTestBase.java
+++ b/sqrl-testing/sqrl-testing-container/src/test/java/com/datasqrl/container/testing/SqrlContainerTestBase.java
@@ -71,6 +71,8 @@ protected void commonTearDown() {
protected static final String SQRL_CMD_IMAGE = "datasqrl/cmd";
protected static final String SQRL_SERVER_IMAGE = "datasqrl/sqrl-server";
protected static final String BUILD_DIR = "/build";
+ protected static final String JACOCO_OUT_DIR = "/jacoco";
+ protected static final String JACOCO_AGENT_PATH = "/jacoco-agent.jar";
protected static final int HTTP_SERVER_PORT = 8888;
protected static Network sharedNetwork;
@@ -115,6 +117,8 @@ protected GenericContainer> createCmdContainer(Path workingDir, boolean debug)
.withFileSystemBind(workingDir.toString(), BUILD_DIR, BindMode.READ_WRITE)
.withEnv("TZ", "America/Los_Angeles");
+ container = configureJacocoCoverage(container, "cmd");
+
if (debug) {
container = container.withEnv("SQRL_DEBUG", "1");
}
@@ -127,14 +131,19 @@ protected GenericContainer> createServerContainer(Path workingDir) {
var deployPlanPath = workingDir.resolve("build/deploy/plan");
assertThat(deployPlanPath).exists().isDirectory();
- return new GenericContainer<>(DockerImageName.parse(SQRL_SERVER_IMAGE + ":" + getImageTag()))
- .withNetwork(sharedNetwork)
- .withExposedPorts(HTTP_SERVER_PORT)
- .withFileSystemBind(deployPlanPath.toString(), "/opt/sqrl/config", BindMode.READ_ONLY)
- .withEnv("SQRL_DEBUG", "1")
- .waitingFor(
- Wait.forLogMessage(".*HTTP server listening on port 8888.*", 1)
- .withStartupTimeout(Duration.ofSeconds(30)));
+ var container =
+ new GenericContainer<>(DockerImageName.parse(SQRL_SERVER_IMAGE + ":" + getImageTag()))
+ .withNetwork(sharedNetwork)
+ .withExposedPorts(HTTP_SERVER_PORT)
+ .withFileSystemBind(deployPlanPath.toString(), "/opt/sqrl/config", BindMode.READ_ONLY)
+ .withEnv("SQRL_DEBUG", "1")
+ .waitingFor(
+ Wait.forLogMessage(".*HTTP server listening on port 8888.*", 1)
+ .withStartupTimeout(Duration.ofSeconds(30)));
+
+ container = configureJacocoCoverage(container, "server");
+
+ return container;
}
protected void compileSqrlProject(Path workingDir) {
@@ -290,6 +299,75 @@ private String getImageTag() {
return System.getProperty("docker.image.tag", "local");
}
+ @SneakyThrows
+ private GenericContainer> configureJacocoCoverage(GenericContainer> container, String component) {
+ if (!Boolean.parseBoolean(System.getProperty("sqrl.container.coverage", "false"))) {
+ return container;
+ }
+
+ var jacocoAgentJar = findJacocoAgentJar();
+ if (jacocoAgentJar == null) {
+ log.warn("Container coverage enabled but JaCoCo agent jar not found in ~/.m2; skipping");
+ return container;
+ }
+
+ var jacocoOutDir = Paths.get("target", "jacoco").toAbsolutePath();
+ var hostDestFile = jacocoOutDir.resolve("jacoco-container.exec");
+ Files.createDirectories(jacocoOutDir);
+
+ var containerDestFile = JACOCO_OUT_DIR + "/jacoco-container.exec";
+ var agentArg =
+ "-javaagent:"
+ + JACOCO_AGENT_PATH
+ + "=destfile="
+ + containerDestFile
+ + ",append=true,excludes=org/**";
+
+ var toolOptions = agentArg;
+
+ log.info(
+ "Enabling container JaCoCo for {}: agent={} destfile={}",
+ component,
+ jacocoAgentJar,
+ hostDestFile);
+
+ return container
+ .withFileSystemBind(jacocoOutDir.toString(), JACOCO_OUT_DIR, BindMode.READ_WRITE)
+ .withFileSystemBind(jacocoAgentJar.toString(), JACOCO_AGENT_PATH, BindMode.READ_ONLY)
+ .withEnv("JAVA_TOOL_OPTIONS", toolOptions);
+ }
+
+ @Nullable
+ private Path findJacocoAgentJar() {
+ var m2 = Paths.get(System.getProperty("user.home"), ".m2", "repository");
+ var jacocoDir = m2.resolve(Paths.get("org", "jacoco", "org.jacoco.agent"));
+ if (!Files.exists(jacocoDir)) {
+ return null;
+ }
+
+ try (var stream = Files.list(jacocoDir)) {
+ var versions =
+ stream
+ .filter(Files::isDirectory)
+ .map(p -> p.getFileName().toString())
+ .sorted()
+ .toList();
+ for (int i = versions.size() - 1; i >= 0; i--) {
+ var version = versions.get(i);
+ var candidate =
+ jacocoDir.resolve(
+ Paths.get(version, "org.jacoco.agent-" + version + "-runtime.jar"));
+ if (Files.exists(candidate)) {
+ return candidate;
+ }
+ }
+ return null;
+ } catch (Exception e) {
+ log.warn("Failed to locate JaCoCo agent jar under {}", jacocoDir, e);
+ return null;
+ }
+ }
+
protected String getDockerRunCommand(GenericContainer> container, Path workingDir) {
var sb = new StringBuilder();
sb.append("docker run -it --rm");
diff --git a/sqrl-testing/sqrl-testing-integration/pom.xml b/sqrl-testing/sqrl-testing-integration/pom.xml
index 5bd33ceae..6c7d9f20d 100644
--- a/sqrl-testing/sqrl-testing-integration/pom.xml
+++ b/sqrl-testing/sqrl-testing-integration/pom.xml
@@ -142,4 +142,5 @@
test
+