Skip to content

Commit 38a8216

Browse files
committed
more tests
1 parent 6363274 commit 38a8216

5 files changed

Lines changed: 882 additions & 0 deletions

File tree

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Eclipse RDF4J contributors.
3+
*
4+
* All rights reserved. This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Distribution License v1.0
6+
* which accompanies this distribution, and is available at
7+
* http://www.eclipse.org/org/documents/edl-v10.php.
8+
*
9+
* SPDX-License-Identifier: BSD-3-Clause
10+
*******************************************************************************/
11+
// Some portions generated by Codex
12+
package org.eclipse.rdf4j.sail.lmdb;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
import java.util.ArrayList;
17+
import java.util.Collections;
18+
import java.util.HashMap;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.Set;
22+
23+
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
24+
import org.eclipse.rdf4j.query.BindingSet;
25+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryBindingSet;
26+
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
27+
import org.eclipse.rdf4j.query.impl.EmptyBindingSet;
28+
import org.junit.jupiter.api.Test;
29+
30+
class LmdbLftjBoundInputParityTest {
31+
32+
@Test
33+
void aliasedPlanShouldRespectPartiallyBoundInputsAcrossInterpretedAndCompiledPaths() {
34+
LmdbLftjPlan plan = syntheticAliasedPlan();
35+
QueryBindingSet bindings = new QueryBindingSet();
36+
bindings.setBinding("a", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:1"));
37+
bindings.setBinding("b", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:2"));
38+
39+
List<String> interpreted = drainRows(new InterpretedQueryAccess(), plan, bindings);
40+
List<String> compiled = drainRows(new CompiledQueryAccess(), plan, bindings);
41+
42+
assertThat(compiled).containsExactlyElementsOf(interpreted);
43+
assertThat(compiled).containsExactly(
44+
"a=urn:person:1|b=urn:person:2|x=urn:person:1|y=urn:person:2|z=urn:person:3",
45+
"a=urn:person:1|b=urn:person:2|x=urn:person:1|y=urn:person:2|z=urn:person:4");
46+
}
47+
48+
@Test
49+
void duplicateAliasedPlanShouldRespectPartiallyBoundInputsAcrossInterpretedAndCompiledPaths() {
50+
LmdbLftjPlan plan = syntheticDuplicateAliasedPlan();
51+
QueryBindingSet bindings = new QueryBindingSet();
52+
bindings.setBinding("a", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:1"));
53+
bindings.setBinding("b", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:2"));
54+
55+
List<String> interpreted = drainRows(new InterpretedQueryAccess(), plan, bindings);
56+
List<String> compiled = drainRows(new CompiledQueryAccess(), plan, bindings);
57+
58+
assertThat(compiled).containsExactlyElementsOf(interpreted);
59+
assertThat(compiled).containsExactly(
60+
"a=urn:person:1|b=urn:person:2|x=urn:person:1|x2=urn:person:1|z=urn:person:3",
61+
"a=urn:person:1|b=urn:person:2|x=urn:person:1|x2=urn:person:1|z=urn:person:4");
62+
}
63+
64+
@Test
65+
void aliasedPlanShouldRetainUnrelatedIncomingBindingsAcrossInterpretedAndCompiledPaths() {
66+
LmdbLftjPlan plan = syntheticAliasedPlan();
67+
QueryBindingSet bindings = new QueryBindingSet();
68+
bindings.setBinding("a", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:1"));
69+
bindings.setBinding("b", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:2"));
70+
bindings.setBinding("seed", LmdbLftjSyntheticScenario.VF.createIRI("urn:seed:fixed"));
71+
72+
List<String> interpreted = drainRows(new InterpretedQueryAccess(), plan, bindings);
73+
List<String> compiled = drainRows(new CompiledQueryAccess(), plan, bindings);
74+
75+
assertThat(compiled).containsExactlyElementsOf(interpreted);
76+
assertThat(compiled).containsExactly(
77+
"a=urn:person:1|b=urn:person:2|seed=urn:seed:fixed|x=urn:person:1|y=urn:person:2|z=urn:person:3",
78+
"a=urn:person:1|b=urn:person:2|seed=urn:seed:fixed|x=urn:person:1|y=urn:person:2|z=urn:person:4");
79+
}
80+
81+
@Test
82+
void aliasedPlanShouldRejectConflictingBoundInputsAcrossInterpretedAndCompiledPaths() {
83+
LmdbLftjPlan plan = syntheticAliasedPlan();
84+
QueryBindingSet bindings = new QueryBindingSet();
85+
bindings.setBinding("a", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:1"));
86+
bindings.setBinding("b", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:1"));
87+
88+
List<String> interpreted = drainRows(new InterpretedQueryAccess(), plan, bindings);
89+
List<String> compiled = drainRows(new CompiledQueryAccess(), plan, bindings);
90+
91+
assertThat(interpreted).isEmpty();
92+
assertThat(compiled).isEmpty();
93+
}
94+
95+
@Test
96+
void aliasedPlanShouldReturnEmptyForUnknownBoundSourceValuesAcrossInterpretedAndCompiledPaths() {
97+
LmdbLftjPlan plan = syntheticAliasedPlan();
98+
QueryBindingSet bindings = new QueryBindingSet();
99+
bindings.setBinding("a", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:99"));
100+
101+
List<String> interpreted = drainRows(new InterpretedQueryAccess(), plan, bindings);
102+
List<String> compiled = drainRows(new CompiledQueryAccess(), plan, bindings);
103+
104+
assertThat(interpreted).isEmpty();
105+
assertThat(compiled).isEmpty();
106+
}
107+
108+
@Test
109+
void projectedHiddenContextPlanShouldPreserveCollapsedMultiplicityAcrossInterpretedAndCompiledPaths() {
110+
LmdbLftjPlan plan = projectedHiddenContextPlan();
111+
QueryBindingSet bindings = new QueryBindingSet();
112+
bindings.setBinding("a", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:1"));
113+
bindings.setBinding("b", LmdbLftjSyntheticScenario.VF.createIRI("urn:person:2"));
114+
115+
List<String> interpreted = drainRows(new InterpretedQueryAccess(true), plan, bindings);
116+
List<String> compiled = drainRows(new CompiledQueryAccess(true), plan, bindings);
117+
118+
String expectedRow = "a=urn:person:1|b=urn:person:2|x=urn:person:1|y=urn:person:2";
119+
assertThat(compiled).containsExactlyElementsOf(interpreted);
120+
assertThat(compiled).hasSize(54).allMatch(expectedRow::equals);
121+
}
122+
123+
private List<String> drainRows(LmdbQueryAccess queryAccess, LmdbLftjPlan plan, BindingSet bindings) {
124+
QueryEvaluationStep evaluationStep = LmdbLftjSyntheticScenario.createEvaluationStep(queryAccess, plan);
125+
List<String> rows = new ArrayList<>();
126+
try (CloseableIteration<BindingSet> iteration = evaluationStep.evaluate(bindings == null
127+
? EmptyBindingSet.getInstance()
128+
: bindings)) {
129+
while (iteration.hasNext()) {
130+
rows.add(render(iteration.next()));
131+
}
132+
}
133+
Collections.sort(rows);
134+
return rows;
135+
}
136+
137+
private String render(BindingSet row) {
138+
List<String> names = new ArrayList<>(row.getBindingNames());
139+
Collections.sort(names);
140+
StringBuilder builder = new StringBuilder();
141+
for (int i = 0; i < names.size(); i++) {
142+
if (i > 0) {
143+
builder.append('|');
144+
}
145+
String name = names.get(i);
146+
builder.append(name).append('=').append(row.getValue(name).stringValue());
147+
}
148+
return builder.toString();
149+
}
150+
151+
private LmdbLftjPlan syntheticAliasedPlan() {
152+
LmdbLftjPlan basePlan = LmdbLftjSyntheticScenario.createPlan();
153+
return new LmdbLftjPlan(basePlan.fallbackExpr().clone(), Set.of("x", "y", "z"), Set.of("x", "y", "z"),
154+
basePlan.variableOrder(), basePlan.patternPlans(),
155+
List.of(
156+
new LmdbLftjPlan.OutputBinding("x", "a"),
157+
new LmdbLftjPlan.OutputBinding("y", "b"),
158+
new LmdbLftjPlan.OutputBinding("z", "c")),
159+
List.of(
160+
new LmdbLftjPlan.InequalityConstraint("a", "b"),
161+
new LmdbLftjPlan.InequalityConstraint("a", "c"),
162+
new LmdbLftjPlan.InequalityConstraint("b", "c")));
163+
}
164+
165+
private LmdbLftjPlan syntheticDuplicateAliasedPlan() {
166+
LmdbLftjPlan basePlan = LmdbLftjSyntheticScenario.createPlan();
167+
return new LmdbLftjPlan(basePlan.fallbackExpr().clone(), Set.of("x", "x2", "z"), Set.of("x", "x2", "z"),
168+
basePlan.variableOrder(), basePlan.patternPlans(),
169+
List.of(
170+
new LmdbLftjPlan.OutputBinding("z", "c"),
171+
new LmdbLftjPlan.OutputBinding("x", "a"),
172+
new LmdbLftjPlan.OutputBinding("x2", "a")),
173+
List.of(
174+
new LmdbLftjPlan.InequalityConstraint("a", "b"),
175+
new LmdbLftjPlan.InequalityConstraint("a", "c"),
176+
new LmdbLftjPlan.InequalityConstraint("b", "c")));
177+
}
178+
179+
private LmdbLftjPlan projectedHiddenContextPlan() {
180+
LmdbLftjPlan basePlan = LmdbLftjSyntheticScenario.createPlanWithHiddenContexts();
181+
return new LmdbLftjPlan(basePlan.fallbackExpr().clone(), Set.of("x", "y"), Set.of("x", "y"),
182+
basePlan.variableOrder(), basePlan.patternPlans(),
183+
List.of(
184+
new LmdbLftjPlan.OutputBinding("x", "a"),
185+
new LmdbLftjPlan.OutputBinding("y", "b")),
186+
List.of());
187+
}
188+
189+
private static final class InterpretedQueryAccess extends LmdbLftjSyntheticScenario.TestQueryAccess {
190+
191+
private InterpretedQueryAccess() {
192+
}
193+
194+
private InterpretedQueryAccess(boolean duplicateContexts) {
195+
super(duplicateContexts);
196+
}
197+
198+
@Override
199+
public boolean lftjCodegenEnabled() {
200+
return false;
201+
}
202+
}
203+
204+
private static final class CompiledQueryAccess extends LmdbLftjSyntheticScenario.TestQueryAccess {
205+
private final Map<String, LmdbLftjCodegenCache.CacheEntry> compiledPlans = new HashMap<>();
206+
207+
private CompiledQueryAccess() {
208+
}
209+
210+
private CompiledQueryAccess(boolean duplicateContexts) {
211+
super(duplicateContexts);
212+
}
213+
214+
@Override
215+
public LmdbLftjCodegenCache.CacheEntry cachedCompiledPlan(String executionKey) {
216+
return compiledPlans.get(executionKey);
217+
}
218+
219+
@Override
220+
public void cacheCompiledPlanSuccess(String executionKey, LmdbCompiledLftjFactory factory) {
221+
compiledPlans.put(executionKey, LmdbLftjCodegenCache.CacheEntry.success(factory));
222+
}
223+
224+
@Override
225+
public void cacheCompiledPlanFailure(String executionKey, String message) {
226+
compiledPlans.put(executionKey, LmdbLftjCodegenCache.CacheEntry.failure(message));
227+
}
228+
}
229+
}

0 commit comments

Comments
 (0)