diff --git a/AGENTS.md b/AGENTS.md index 18d0e3ef4d7..2bf80dd6b51 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -166,6 +166,18 @@ It is illegal to `-q` when running tests! --- +## Always Install Before Tests (Required) + +The Maven reactor resolves inter-module dependencies from the local Maven repository (`~/.m2/repository`). +Running `install` publishes your changed modules there so downstream modules and tests pick up the correct versions. + +- Always run `mvn -o -Pquick install | tail -200` before you start working. This command typically takes between 10 and 30 seconds. +- Always run `mvn -o -pl -am -Pquick install | tail -200` before any `verify` or test runs. +- If offline resolution fails due to a missing dependency or plugin, rerun the exact `install` command once without `-o`, then return offline. +- Skipping this step can lead to stale or missing artifacts during tests, producing confusing compilation or linkage errors. +- Never ever change the repo location. Never use `-Dmaven.repo.local=.m2_repo`. Instead, ask for permission the first time you run `mvn -o -Pquick install | tail -200`. +--- + ## Quick Start (First 10 Minutes) 1. **Discover** @@ -176,6 +188,7 @@ It is illegal to `-q` when running tests! * **Preferred:** `mvn -o -Pquick install | tail -200` * **Alternative:** `mvn -o -Pquick install | tail -200` + * This step is required before any tests. It installs artifacts to `~/.m2` so the reactor resolves fresh inter-module dependencies. 3. **Format (Java, imports, XML)** * `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` @@ -184,6 +197,7 @@ It is illegal to `-q` when running tests! * By module: `mvn -o -pl verify | tail -500` * Single class: `mvn -o -pl -Dtest=ClassName verify | tail -500` * Single method: `mvn -o -pl -Dtest=ClassName#method verify | tail -500` + * Prerequisite: ensure `mvn -o -Pquick install` (root or `-pl -am`) has just run so artifacts are available in `~/.m2`. 5. **Inspect failures** * **Unit (Surefire):** `/target/surefire-reports/` @@ -483,6 +497,39 @@ Do **not** modify existing headers’ years. --- +## Branching & Commit Conventions + +- Branch names: start with `GH-XXXX` where `XXXX` is the GitHub issue number. Prefer a short, kebab‑case slug after the number when helpful, e.g., `GH-1234-add-trig-writer-check`. +- Commit messages: start with the same prefix, `GH-XXXX `, on every commit in the branch. +- Keep summaries concise, in imperative mood (e.g., “Fix NPE in TriG writer”). +- Example: + - Branch: `GH-1234-add-shacl-validation-metric` + - Commit: `GH-1234 Fix NPE when serializing empty graph` + +--- + +## Branch & PR Workflow (Agent) + +- Name branch: `GH--` (kebab‑case slug). +- Create branch: `git checkout -b GH-XXXX-your-slug`. +- Stage changes: `git add -A` (ensure new Java files have the required header). +- Optional but recommended: run format + quick install. + - `mvn -o -q -T 2C formatter:format impsort:sort xml-format:xml-format` + - `mvn -o -Pquick install | tail -200` +- Commit: `git commit -m "GH-XXXX "`. +- Push branch: `git push -u origin GH-XXXX-your-slug`. +- Create PR using default template: + - Preferred: `gh pr create --title "GH-XXXX " --body-file .github/pull_request_template.md` + - Fallback: `gh pr create --title "GH-XXXX " --body "$(cat .github/pull_request_template.md)"` +- Immediately fill the template (do not leave placeholders): + - Set `GitHub issue resolved: #XXXX`. + - Write a short, accurate change summary (what/why). + - Tick applicable checklist items only (self-contained, tests, squashed, commit message prefix, formatting if run). + - Include `Fixes #XXXX` to auto-close the issue on merge. +- Target the repo default branch (e.g., `origin/HEAD`). + +--- + ## Navigation & Search * Fast file search: `rg --files` diff --git a/core/sail/extensible-store/src/main/java/org/eclipse/rdf4j/sail/extensiblestore/evaluationstatistics/ExtensibleDynamicEvaluationStatistics.java b/core/sail/extensible-store/src/main/java/org/eclipse/rdf4j/sail/extensiblestore/evaluationstatistics/ExtensibleDynamicEvaluationStatistics.java index fe87ca611b8..1020a4113f2 100644 --- a/core/sail/extensible-store/src/main/java/org/eclipse/rdf4j/sail/extensiblestore/evaluationstatistics/ExtensibleDynamicEvaluationStatistics.java +++ b/core/sail/extensible-store/src/main/java/org/eclipse/rdf4j/sail/extensiblestore/evaluationstatistics/ExtensibleDynamicEvaluationStatistics.java @@ -228,7 +228,7 @@ protected double getObjectCardinality(Var var) { @Override protected double getContextCardinality(Var var) { synchronized (monitor) { - if (var.getValue() == null) { + if (var == null || var.getValue() == null) { return defaultContext.cardinality() - defaultContext_removed.cardinality(); } else { return getHllCardinality(contextIndex, contextIndex_removed, var.getValue()); diff --git a/core/sail/extensible-store/src/test/java/org/eclipse/rdf4j/sail/extensiblestore/evaluationstatistics/ExtensibleDynamicEvaluationStatisticsNullContextTest.java b/core/sail/extensible-store/src/test/java/org/eclipse/rdf4j/sail/extensiblestore/evaluationstatistics/ExtensibleDynamicEvaluationStatisticsNullContextTest.java new file mode 100644 index 00000000000..2d52856e701 --- /dev/null +++ b/core/sail/extensible-store/src/test/java/org/eclipse/rdf4j/sail/extensiblestore/evaluationstatistics/ExtensibleDynamicEvaluationStatisticsNullContextTest.java @@ -0,0 +1,47 @@ +/** + * ****************************************************************************** + * Copyright (c) 2025 Eclipse RDF4J contributors. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Distribution License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + * ****************************************************************************** + */ +package org.eclipse.rdf4j.sail.extensiblestore.evaluationstatistics; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.eclipse.rdf4j.query.algebra.Var; +import org.eclipse.rdf4j.query.algebra.ZeroLengthPath; +import org.junit.jupiter.api.Test; + +/** + * Reproduces a NullPointerException in ExtensibleDynamicEvaluationStatistics when evaluating the cardinality of a + * ZeroLengthPath with a null context variable. + * + * The EvaluationStatistics visitor for ZeroLengthPath calls getContextCardinality(node.getContextVar()) where the + * context var may be null. The overridden implementation in ExtensibleDynamicEvaluationStatistics assumed a non-null + * Var and dereferenced it, causing an NPE. + */ +public class ExtensibleDynamicEvaluationStatisticsNullContextTest { + + @Test + public void testZeroLengthPathWithNullContextDoesNotThrow() { + // Given a dynamic evaluation statistics instance with no data loaded + ExtensibleDynamicEvaluationStatistics stats = new ExtensibleDynamicEvaluationStatistics(null); + + // And a ZeroLengthPath with subject and object vars, but no context var (null) + ZeroLengthPath zlp = new ZeroLengthPath(new Var("s"), new Var("o")); + + // When asking for cardinality, this used to throw a NullPointerException because + // getContextCardinality(Var var) dereferenced var without checking for null. + // Then it should simply return a numeric value (0.0 with empty stats), not throw. + double cardinality = stats.getCardinality(zlp); + + // With no statements added, subject/object/context cardinalities are 0 -> overall 0 + assertEquals(0.0, cardinality); + } +}