Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <module> -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**
Expand All @@ -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`
Expand All @@ -184,6 +197,7 @@ It is illegal to `-q` when running tests!
* By module: `mvn -o -pl <module> verify | tail -500`
* Single class: `mvn -o -pl <module> -Dtest=ClassName verify | tail -500`
* Single method: `mvn -o -pl <module> -Dtest=ClassName#method verify | tail -500`
* Prerequisite: ensure `mvn -o -Pquick install` (root or `-pl <module> -am`) has just run so artifacts are available in `~/.m2`.
5. **Inspect failures**

* **Unit (Surefire):** `<module>/target/surefire-reports/`
Expand Down Expand Up @@ -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 <short summary>`, 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-<issue>-<short-slug>` (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 <short imperative summary>"`.
- Push branch: `git push -u origin GH-XXXX-your-slug`.
- Create PR using default template:
- Preferred: `gh pr create --title "GH-XXXX <summary>" --body-file .github/pull_request_template.md`
- Fallback: `gh pr create --title "GH-XXXX <summary>" --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`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading