aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-interpreter/src/test
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-09-16 13:19:31 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-09-16 16:53:01 +0200
commit97b0c4c1192fe5580a7957c844acc8092b56c604 (patch)
treebea3cdf9aaeb5da2864fcf87780d356661af8f63 /subprojects/store-query-interpreter/src/test
parentbuild: fix Sonar quality gate issues (diff)
downloadrefinery-97b0c4c1192fe5580a7957c844acc8092b56c604.tar.gz
refinery-97b0c4c1192fe5580a7957c844acc8092b56c604.tar.zst
refinery-97b0c4c1192fe5580a7957c844acc8092b56c604.zip
chore: remove VIATRA branding
Rename VIATRA subprojects to Refinery Interpreter to avoid interfering with Eclipse Foundation trademarks. Uses refering to a specific (historical) version of VIATRA were kept to avoid ambiguity.
Diffstat (limited to 'subprojects/store-query-interpreter/src/test')
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/DiagonalQueryTest.java391
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java519
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/OrderedResultSetTest.java117
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTest.java794
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTransactionTest.java370
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/StronglyConnectedComponentsTest.java261
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/WeaklyConnectedComponentsTest.java188
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/internal/matcher/MatcherUtilsTest.java239
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryAssertions.java57
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryBackendHint.java27
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEngineTest.java21
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEvaluationHintSource.java24
12 files changed, 3008 insertions, 0 deletions
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/DiagonalQueryTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/DiagonalQueryTest.java
new file mode 100644
index 00000000..76de8679
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/DiagonalQueryTest.java
@@ -0,0 +1,391 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Dnf;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.query.interpreter.tests.QueryEngineTest;
14import tools.refinery.store.query.view.AnySymbolView;
15import tools.refinery.store.query.view.FunctionView;
16import tools.refinery.store.query.view.KeyOnlyView;
17import tools.refinery.store.representation.Symbol;
18import tools.refinery.store.tuple.Tuple;
19
20import java.util.List;
21import java.util.Map;
22import java.util.Optional;
23
24import static tools.refinery.store.query.literal.Literals.not;
25import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM;
26import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertNullableResults;
27import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertResults;
28
29class DiagonalQueryTest {
30 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
31 private static final Symbol<Boolean> friend = Symbol.of("friend", 2);
32 private static final Symbol<Boolean> symbol = Symbol.of("symbol", 4);
33 private static final Symbol<Integer> intSymbol = Symbol.of("intSymbol", 4, Integer.class);
34 private static final AnySymbolView personView = new KeyOnlyView<>(person);
35 private static final AnySymbolView friendView = new KeyOnlyView<>(friend);
36 private static final AnySymbolView symbolView = new KeyOnlyView<>(symbol);
37 private static final FunctionView<Integer> intSymbolView = new FunctionView<>(intSymbol);
38
39 @QueryEngineTest
40 void inputKeyNegationTest(QueryEvaluationHint hint) {
41 var query = Query.of("Diagonal", (builder, p1) -> builder.clause(p2 -> List.of(
42 personView.call(p1),
43 not(symbolView.call(p1, p1, p2, p2))
44 )));
45
46 var store = ModelStore.builder()
47 .symbols(person, symbol)
48 .with(QueryInterpreterAdapter.builder()
49 .defaultHint(hint)
50 .queries(query))
51 .build();
52
53 var model = store.createEmptyModel();
54 var personInterpretation = model.getInterpretation(person);
55 var symbolInterpretation = model.getInterpretation(symbol);
56 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
57 var queryResultSet = queryEngine.getResultSet(query);
58
59 personInterpretation.put(Tuple.of(0), true);
60 personInterpretation.put(Tuple.of(1), true);
61 personInterpretation.put(Tuple.of(2), true);
62
63 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
64 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
65 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
66 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
67
68 queryEngine.flushChanges();
69 assertResults(Map.of(
70 Tuple.of(0), false,
71 Tuple.of(1), true,
72 Tuple.of(2), true,
73 Tuple.of(3), false
74 ), queryResultSet);
75 }
76
77 @QueryEngineTest
78 void subQueryNegationTest(QueryEvaluationHint hint) {
79 var subQuery = Query.of("SubQuery", (builder, p1, p2, p3, p4) -> builder
80 .clause(
81 personView.call(p1),
82 symbolView.call(p1, p2, p3, p4)
83 )
84 .clause(
85 personView.call(p2),
86 symbolView.call(p1, p2, p3, p4)
87 ));
88 var query = Query.of("Diagonal", (builder, p1) -> builder.clause(p2 -> List.of(
89 personView.call(p1),
90 not(subQuery.call(p1, p1, p2, p2))
91 )));
92
93 var store = ModelStore.builder()
94 .symbols(person, symbol)
95 .with(QueryInterpreterAdapter.builder()
96 .defaultHint(hint)
97 .queries(query))
98 .build();
99
100 var model = store.createEmptyModel();
101
102 var personInterpretation = model.getInterpretation(person);
103 var symbolInterpretation = model.getInterpretation(symbol);
104 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
105 var queryResultSet = queryEngine.getResultSet(query);
106
107 personInterpretation.put(Tuple.of(0), true);
108 personInterpretation.put(Tuple.of(1), true);
109 personInterpretation.put(Tuple.of(2), true);
110
111 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
112 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
113 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
114 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
115
116 queryEngine.flushChanges();
117 assertResults(Map.of(
118 Tuple.of(0), false,
119 Tuple.of(1), true,
120 Tuple.of(2), true,
121 Tuple.of(3), false
122 ), queryResultSet);
123 }
124
125 @QueryEngineTest
126 void inputKeyCountTest(QueryEvaluationHint hint) {
127 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder.clause(p2 -> List.of(
128 personView.call(p1),
129 output.assign(symbolView.count(p1, p1, p2, p2))
130 )));
131
132 var store = ModelStore.builder()
133 .symbols(person, symbol)
134 .with(QueryInterpreterAdapter.builder()
135 .defaultHint(hint)
136 .queries(query))
137 .build();
138
139 var model = store.createEmptyModel();
140 var personInterpretation = model.getInterpretation(person);
141 var symbolInterpretation = model.getInterpretation(symbol);
142 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
143 var queryResultSet = queryEngine.getResultSet(query);
144
145 personInterpretation.put(Tuple.of(0), true);
146 personInterpretation.put(Tuple.of(1), true);
147 personInterpretation.put(Tuple.of(2), true);
148
149 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
150 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
151 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
152 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
153 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
154
155 queryEngine.flushChanges();
156 assertNullableResults(Map.of(
157 Tuple.of(0), Optional.of(2),
158 Tuple.of(1), Optional.of(0),
159 Tuple.of(2), Optional.of(0),
160 Tuple.of(3), Optional.empty()
161 ), queryResultSet);
162 }
163
164 @QueryEngineTest
165 void subQueryCountTest(QueryEvaluationHint hint) {
166 var subQuery = Query.of("SubQuery", (builder, p1, p2, p3, p4) -> builder.clause(
167 personView.call(p1),
168 symbolView.call(p1, p2, p3, p4)
169 )
170 .clause(
171 personView.call(p2),
172 symbolView.call(p1, p2, p3, p4)
173 ));
174 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder.clause(p2 -> List.of(
175 personView.call(p1),
176 output.assign(subQuery.count(p1, p1, p2, p2))
177 )));
178
179 var store = ModelStore.builder()
180 .symbols(person, symbol)
181 .with(QueryInterpreterAdapter.builder()
182 .defaultHint(hint)
183 .queries(query))
184 .build();
185
186 var model = store.createEmptyModel();
187 var personInterpretation = model.getInterpretation(person);
188 var symbolInterpretation = model.getInterpretation(symbol);
189 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
190 var queryResultSet = queryEngine.getResultSet(query);
191
192 personInterpretation.put(Tuple.of(0), true);
193 personInterpretation.put(Tuple.of(1), true);
194 personInterpretation.put(Tuple.of(2), true);
195
196 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
197 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
198 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
199 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
200 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
201
202 queryEngine.flushChanges();
203 assertNullableResults(Map.of(
204 Tuple.of(0), Optional.of(2),
205 Tuple.of(1), Optional.of(0),
206 Tuple.of(2), Optional.of(0),
207 Tuple.of(3), Optional.empty()
208 ), queryResultSet);
209 }
210
211 @QueryEngineTest
212 void inputKeyAggregationTest(QueryEvaluationHint hint) {
213 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder
214 .clause((p2) -> List.of(
215 personView.call(p1),
216 output.assign(intSymbolView.aggregate(INT_SUM, p1, p1, p2, p2))
217 )));
218
219 var store = ModelStore.builder()
220 .symbols(person, intSymbol)
221 .with(QueryInterpreterAdapter.builder()
222 .defaultHint(hint)
223 .queries(query))
224 .build();
225
226 var model = store.createEmptyModel();
227 var personInterpretation = model.getInterpretation(person);
228 var intSymbolInterpretation = model.getInterpretation(intSymbol);
229 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
230 var queryResultSet = queryEngine.getResultSet(query);
231
232 personInterpretation.put(Tuple.of(0), true);
233 personInterpretation.put(Tuple.of(1), true);
234 personInterpretation.put(Tuple.of(2), true);
235
236 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
237 intSymbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
238 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
239 intSymbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
240 intSymbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
241
242 queryEngine.flushChanges();
243 assertNullableResults(Map.of(
244 Tuple.of(0), Optional.of(3),
245 Tuple.of(1), Optional.of(0),
246 Tuple.of(2), Optional.of(0),
247 Tuple.of(3), Optional.empty()
248 ), queryResultSet);
249 }
250
251 @QueryEngineTest
252 void subQueryAggregationTest(QueryEvaluationHint hint) {
253 var subQuery = Dnf.of("SubQuery", builder -> {
254 var p1 = builder.parameter("p1");
255 var p2 = builder.parameter("p2");
256 var p3 = builder.parameter("p3");
257 var p4 = builder.parameter("p4");
258 var x = builder.parameter("x", Integer.class);
259 var y = builder.parameter("y", Integer.class);
260 builder.clause(
261 personView.call(p1),
262 intSymbolView.call(p1, p2, p3, p4, x),
263 y.assign(x)
264 );
265 builder.clause(
266 personView.call(p2),
267 intSymbolView.call(p1, p2, p3, p4, x),
268 y.assign(x)
269 );
270 });
271 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder
272 .clause(Integer.class, Integer.class, (p2, y, z) -> List.of(
273 personView.call(p1),
274 output.assign(subQuery.aggregateBy(y, INT_SUM, p1, p1, p2, p2, y, z))
275 )));
276
277 var store = ModelStore.builder()
278 .symbols(person, intSymbol)
279 .with(QueryInterpreterAdapter.builder()
280 .defaultHint(hint)
281 .queries(query))
282 .build();
283
284 var model = store.createEmptyModel();
285 var personInterpretation = model.getInterpretation(person);
286 var intSymbolInterpretation = model.getInterpretation(intSymbol);
287 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
288 var queryResultSet = queryEngine.getResultSet(query);
289
290 personInterpretation.put(Tuple.of(0), true);
291 personInterpretation.put(Tuple.of(1), true);
292 personInterpretation.put(Tuple.of(2), true);
293
294 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
295 intSymbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
296 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
297 intSymbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
298 intSymbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
299
300 queryEngine.flushChanges();
301 assertNullableResults(Map.of(
302 Tuple.of(0), Optional.of(3),
303 Tuple.of(1), Optional.of(0),
304 Tuple.of(2), Optional.of(0),
305 Tuple.of(3), Optional.empty()
306 ), queryResultSet);
307 }
308
309 @QueryEngineTest
310 void inputKeyTransitiveTest(QueryEvaluationHint hint) {
311 var query = Query.of("Diagonal", (builder, p1) -> builder.clause(
312 personView.call(p1),
313 friendView.callTransitive(p1, p1)
314 ));
315
316 var store = ModelStore.builder()
317 .symbols(person, friend)
318 .with(QueryInterpreterAdapter.builder()
319 .defaultHint(hint)
320 .queries(query))
321 .build();
322
323 var model = store.createEmptyModel();
324 var personInterpretation = model.getInterpretation(person);
325 var friendInterpretation = model.getInterpretation(friend);
326 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
327 var queryResultSet = queryEngine.getResultSet(query);
328
329 personInterpretation.put(Tuple.of(0), true);
330 personInterpretation.put(Tuple.of(1), true);
331 personInterpretation.put(Tuple.of(2), true);
332
333 friendInterpretation.put(Tuple.of(0, 0), true);
334 friendInterpretation.put(Tuple.of(0, 1), true);
335 friendInterpretation.put(Tuple.of(1, 2), true);
336
337 queryEngine.flushChanges();
338 assertResults(Map.of(
339 Tuple.of(0), true,
340 Tuple.of(1), false,
341 Tuple.of(2), false,
342 Tuple.of(3), false
343 ), queryResultSet);
344 }
345
346 @QueryEngineTest
347 void subQueryTransitiveTest(QueryEvaluationHint hint) {
348 var subQuery = Query.of("SubQuery", (builder, p1, p2) -> builder
349 .clause(
350 personView.call(p1),
351 friendView.call(p1, p2)
352 )
353 .clause(
354 personView.call(p2),
355 friendView.call(p1, p2)
356 ));
357 var query = Query.of("Diagonal", (builder, p1) -> builder.clause(
358 personView.call(p1),
359 subQuery.callTransitive(p1, p1)
360 ));
361
362 var store = ModelStore.builder()
363 .symbols(person, friend)
364 .with(QueryInterpreterAdapter.builder()
365 .defaultHint(hint)
366 .queries(query))
367 .build();
368
369 var model = store.createEmptyModel();
370 var personInterpretation = model.getInterpretation(person);
371 var friendInterpretation = model.getInterpretation(friend);
372 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
373 var queryResultSet = queryEngine.getResultSet(query);
374
375 personInterpretation.put(Tuple.of(0), true);
376 personInterpretation.put(Tuple.of(1), true);
377 personInterpretation.put(Tuple.of(2), true);
378
379 friendInterpretation.put(Tuple.of(0, 0), true);
380 friendInterpretation.put(Tuple.of(0, 1), true);
381 friendInterpretation.put(Tuple.of(1, 2), true);
382
383 queryEngine.flushChanges();
384 assertResults(Map.of(
385 Tuple.of(0), true,
386 Tuple.of(1), false,
387 Tuple.of(2), false,
388 Tuple.of(3), false
389 ), queryResultSet);
390 }
391}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java
new file mode 100644
index 00000000..00721a34
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java
@@ -0,0 +1,519 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.query.ModelQueryAdapter;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.query.term.Variable;
14import tools.refinery.store.query.interpreter.tests.QueryEngineTest;
15import tools.refinery.store.query.view.AnySymbolView;
16import tools.refinery.store.query.view.FilteredView;
17import tools.refinery.store.query.view.FunctionView;
18import tools.refinery.store.query.view.KeyOnlyView;
19import tools.refinery.store.representation.Symbol;
20import tools.refinery.store.representation.TruthValue;
21import tools.refinery.store.tuple.Tuple;
22
23import java.util.List;
24import java.util.Map;
25import java.util.Optional;
26
27import static org.hamcrest.MatcherAssert.assertThat;
28import static org.hamcrest.Matchers.is;
29import static org.hamcrest.Matchers.nullValue;
30import static org.junit.jupiter.api.Assertions.assertAll;
31import static org.junit.jupiter.api.Assertions.assertThrows;
32import static tools.refinery.store.query.literal.Literals.check;
33import static tools.refinery.store.query.term.int_.IntTerms.*;
34import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertNullableResults;
35import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertResults;
36
37class FunctionalQueryTest {
38 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
39 private static final Symbol<Integer> age = Symbol.of("age", 1, Integer.class);
40 private static final Symbol<TruthValue> friend = Symbol.of("friend", 2, TruthValue.class, TruthValue.FALSE);
41 private static final AnySymbolView personView = new KeyOnlyView<>(person);
42 private static final FunctionView<Integer> ageView = new FunctionView<>(age);
43 private static final AnySymbolView friendMustView = new FilteredView<>(friend, "must", TruthValue::must);
44
45 @QueryEngineTest
46 void inputKeyTest(QueryEvaluationHint hint) {
47 var query = Query.of("InputKey", Integer.class, (builder, p1, output) -> builder.clause(
48 personView.call(p1),
49 ageView.call(p1, output)
50 ));
51
52 var store = ModelStore.builder()
53 .symbols(person, age)
54 .with(QueryInterpreterAdapter.builder()
55 .defaultHint(hint)
56 .queries(query))
57 .build();
58
59 var model = store.createEmptyModel();
60 var personInterpretation = model.getInterpretation(person);
61 var ageInterpretation = model.getInterpretation(age);
62 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
63 var queryResultSet = queryEngine.getResultSet(query);
64
65 personInterpretation.put(Tuple.of(0), true);
66 personInterpretation.put(Tuple.of(1), true);
67
68 ageInterpretation.put(Tuple.of(0), 12);
69 ageInterpretation.put(Tuple.of(1), 24);
70 ageInterpretation.put(Tuple.of(2), 36);
71
72 queryEngine.flushChanges();
73 assertNullableResults(Map.of(
74 Tuple.of(0), Optional.of(12),
75 Tuple.of(1), Optional.of(24),
76 Tuple.of(2), Optional.empty()
77 ), queryResultSet);
78 }
79
80 @QueryEngineTest
81 void predicateTest(QueryEvaluationHint hint) {
82 var subQuery = Query.of("SubQuery", Integer.class, (builder, p1, x) -> builder.clause(
83 personView.call(p1),
84 ageView.call(p1, x)
85 ));
86 var query = Query.of("Predicate", Integer.class, (builder, p1, output) -> builder.clause(
87 personView.call(p1),
88 output.assign(subQuery.call(p1))
89 ));
90
91 var store = ModelStore.builder()
92 .symbols(person, age)
93 .with(QueryInterpreterAdapter.builder()
94 .defaultHint(hint)
95 .queries(query))
96 .build();
97
98 var model = store.createEmptyModel();
99 var personInterpretation = model.getInterpretation(person);
100 var ageInterpretation = model.getInterpretation(age);
101 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
102 var queryResultSet = queryEngine.getResultSet(query);
103
104 personInterpretation.put(Tuple.of(0), true);
105 personInterpretation.put(Tuple.of(1), true);
106
107 ageInterpretation.put(Tuple.of(0), 12);
108 ageInterpretation.put(Tuple.of(1), 24);
109 ageInterpretation.put(Tuple.of(2), 36);
110
111 queryEngine.flushChanges();
112 assertNullableResults(Map.of(
113 Tuple.of(0), Optional.of(12),
114 Tuple.of(1), Optional.of(24),
115 Tuple.of(2), Optional.empty()
116 ), queryResultSet);
117 }
118
119 @QueryEngineTest
120 void computationTest(QueryEvaluationHint hint) {
121 var query = Query.of("Computation", Integer.class, (builder, p1, output) -> builder.clause(() -> {
122 var x = Variable.of("x", Integer.class);
123 return List.of(
124 personView.call(p1),
125 ageView.call(p1, x),
126 output.assign(mul(x, constant(7)))
127 );
128 }));
129
130 var store = ModelStore.builder()
131 .symbols(person, age)
132 .with(QueryInterpreterAdapter.builder()
133 .defaultHint(hint)
134 .queries(query))
135 .build();
136
137 var model = store.createEmptyModel();
138 var personInterpretation = model.getInterpretation(person);
139 var ageInterpretation = model.getInterpretation(age);
140 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
141 var queryResultSet = queryEngine.getResultSet(query);
142
143 personInterpretation.put(Tuple.of(0), true);
144 personInterpretation.put(Tuple.of(1), true);
145
146 ageInterpretation.put(Tuple.of(0), 12);
147 ageInterpretation.put(Tuple.of(1), 24);
148
149 queryEngine.flushChanges();
150 assertNullableResults(Map.of(
151 Tuple.of(0), Optional.of(84),
152 Tuple.of(1), Optional.of(168),
153 Tuple.of(2), Optional.empty()
154 ), queryResultSet);
155 }
156
157 @QueryEngineTest
158 void inputKeyCountTest(QueryEvaluationHint hint) {
159 var query = Query.of("Count", Integer.class, (builder, p1, output) -> builder.clause(
160 personView.call(p1),
161 output.assign(friendMustView.count(p1, Variable.of()))
162 ));
163
164 var store = ModelStore.builder()
165 .symbols(person, friend)
166 .with(QueryInterpreterAdapter.builder()
167 .defaultHint(hint)
168 .queries(query))
169 .build();
170
171 var model = store.createEmptyModel();
172 var personInterpretation = model.getInterpretation(person);
173 var friendInterpretation = model.getInterpretation(friend);
174 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
175 var queryResultSet = queryEngine.getResultSet(query);
176
177 personInterpretation.put(Tuple.of(0), true);
178 personInterpretation.put(Tuple.of(1), true);
179 personInterpretation.put(Tuple.of(2), true);
180
181 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
182 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
183 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
184
185 queryEngine.flushChanges();
186 assertNullableResults(Map.of(
187 Tuple.of(0), Optional.of(1),
188 Tuple.of(1), Optional.of(2),
189 Tuple.of(2), Optional.of(0),
190 Tuple.of(3), Optional.empty()
191 ), queryResultSet);
192 }
193
194 @QueryEngineTest
195 void predicateCountTest(QueryEvaluationHint hint) {
196 var subQuery = Query.of("SubQuery", (builder, p1, p2) -> builder.clause(
197 personView.call(p1),
198 personView.call(p2),
199 friendMustView.call(p1, p2)
200 ));
201 var query = Query.of("Count", Integer.class, (builder, p1, output) -> builder.clause(
202 personView.call(p1),
203 output.assign(subQuery.count(p1, Variable.of()))
204 ));
205
206 var store = ModelStore.builder()
207 .symbols(person, friend)
208 .with(QueryInterpreterAdapter.builder()
209 .defaultHint(hint)
210 .queries(query))
211 .build();
212
213 var model = store.createEmptyModel();
214 var personInterpretation = model.getInterpretation(person);
215 var friendInterpretation = model.getInterpretation(friend);
216 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
217 var queryResultSet = queryEngine.getResultSet(query);
218
219 personInterpretation.put(Tuple.of(0), true);
220 personInterpretation.put(Tuple.of(1), true);
221 personInterpretation.put(Tuple.of(2), true);
222
223 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
224 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
225 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
226
227 queryEngine.flushChanges();
228 assertNullableResults(Map.of(
229 Tuple.of(0), Optional.of(1),
230 Tuple.of(1), Optional.of(2),
231 Tuple.of(2), Optional.of(0),
232 Tuple.of(3), Optional.empty()
233 ), queryResultSet);
234 }
235
236 @QueryEngineTest
237 void inputKeyAggregationTest(QueryEvaluationHint hint) {
238 var query = Query.of("Aggregate", Integer.class, (builder, output) -> builder.clause(
239 output.assign(ageView.aggregate(INT_SUM, Variable.of()))
240 ));
241
242 var store = ModelStore.builder()
243 .symbols(age)
244 .with(QueryInterpreterAdapter.builder()
245 .defaultHint(hint)
246 .queries(query))
247 .build();
248
249 var model = store.createEmptyModel();
250 var ageInterpretation = model.getInterpretation(age);
251 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
252 var queryResultSet = queryEngine.getResultSet(query);
253
254 ageInterpretation.put(Tuple.of(0), 12);
255 ageInterpretation.put(Tuple.of(1), 24);
256
257 queryEngine.flushChanges();
258 assertResults(Map.of(Tuple.of(), 36), queryResultSet);
259 }
260
261 @QueryEngineTest
262 void predicateAggregationTest(QueryEvaluationHint hint) {
263 var subQuery = Query.of("SubQuery", Integer.class, (builder, p1, x) -> builder.clause(
264 personView.call(p1),
265 ageView.call(p1, x)
266 ));
267 var query = Query.of("Aggregate", Integer.class, (builder, output) -> builder.clause(
268 output.assign(subQuery.aggregate(INT_SUM, Variable.of()))
269 ));
270
271 var store = ModelStore.builder()
272 .symbols(person, age)
273 .with(QueryInterpreterAdapter.builder()
274 .defaultHint(hint)
275 .queries(query))
276 .build();
277
278 var model = store.createEmptyModel();
279 var personInterpretation = model.getInterpretation(person);
280 var ageInterpretation = model.getInterpretation(age);
281 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
282 var queryResultSet = queryEngine.getResultSet(query);
283
284 personInterpretation.put(Tuple.of(0), true);
285 personInterpretation.put(Tuple.of(1), true);
286
287 ageInterpretation.put(Tuple.of(0), 12);
288 ageInterpretation.put(Tuple.of(1), 24);
289
290 queryEngine.flushChanges();
291 assertResults(Map.of(Tuple.of(), 36), queryResultSet);
292 }
293
294 @QueryEngineTest
295 void extremeValueTest(QueryEvaluationHint hint) {
296 var subQuery = Query.of("SubQuery", Integer.class, (builder, p1, x) -> builder.clause(
297 personView.call(p1),
298 x.assign(friendMustView.count(p1, Variable.of()))
299 ));
300 var minQuery = Query.of("Min", Integer.class, (builder, output) -> builder.clause(
301 output.assign(subQuery.aggregate(INT_MIN, Variable.of()))
302 ));
303 var maxQuery = Query.of("Max", Integer.class, (builder, output) -> builder.clause(
304 output.assign(subQuery.aggregate(INT_MAX, Variable.of()))
305 ));
306
307 var store = ModelStore.builder()
308 .symbols(person, friend)
309 .with(QueryInterpreterAdapter.builder()
310 .defaultHint(hint)
311 .queries(minQuery, maxQuery))
312 .build();
313
314 var model = store.createEmptyModel();
315 var personInterpretation = model.getInterpretation(person);
316 var friendInterpretation = model.getInterpretation(friend);
317 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
318 var minResultSet = queryEngine.getResultSet(minQuery);
319 var maxResultSet = queryEngine.getResultSet(maxQuery);
320
321 assertResults(Map.of(Tuple.of(), Integer.MAX_VALUE), minResultSet);
322 assertResults(Map.of(Tuple.of(), Integer.MIN_VALUE), maxResultSet);
323
324 personInterpretation.put(Tuple.of(0), true);
325 personInterpretation.put(Tuple.of(1), true);
326 personInterpretation.put(Tuple.of(2), true);
327
328 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
329 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
330 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
331
332 queryEngine.flushChanges();
333 assertResults(Map.of(Tuple.of(), 0), minResultSet);
334 assertResults(Map.of(Tuple.of(), 2), maxResultSet);
335
336 friendInterpretation.put(Tuple.of(2, 0), TruthValue.TRUE);
337 friendInterpretation.put(Tuple.of(2, 1), TruthValue.TRUE);
338
339 queryEngine.flushChanges();
340 assertResults(Map.of(Tuple.of(), 1), minResultSet);
341 assertResults(Map.of(Tuple.of(), 2), maxResultSet);
342
343 friendInterpretation.put(Tuple.of(0, 1), TruthValue.FALSE);
344 friendInterpretation.put(Tuple.of(1, 0), TruthValue.FALSE);
345 friendInterpretation.put(Tuple.of(2, 0), TruthValue.FALSE);
346
347 queryEngine.flushChanges();
348 assertResults(Map.of(Tuple.of(), 0), minResultSet);
349 assertResults(Map.of(Tuple.of(), 1), maxResultSet);
350 }
351
352 @QueryEngineTest
353 void invalidComputationTest(QueryEvaluationHint hint) {
354 var query = Query.of("InvalidComputation", Integer.class,
355 (builder, p1, output) -> builder.clause(Integer.class, (x) -> List.of(
356 personView.call(p1),
357 ageView.call(p1, x),
358 output.assign(div(constant(120), x))
359 )));
360
361 var store = ModelStore.builder()
362 .symbols(person, age)
363 .with(QueryInterpreterAdapter.builder()
364 .defaultHint(hint)
365 .queries(query))
366 .build();
367
368 var model = store.createEmptyModel();
369 var personInterpretation = model.getInterpretation(person);
370 var ageInterpretation = model.getInterpretation(age);
371 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
372 var queryResultSet = queryEngine.getResultSet(query);
373
374 personInterpretation.put(Tuple.of(0), true);
375 personInterpretation.put(Tuple.of(1), true);
376
377 ageInterpretation.put(Tuple.of(0), 0);
378 ageInterpretation.put(Tuple.of(1), 30);
379
380 queryEngine.flushChanges();
381 assertNullableResults(Map.of(
382 Tuple.of(0), Optional.empty(),
383 Tuple.of(1), Optional.of(4),
384 Tuple.of(2), Optional.empty()
385 ), queryResultSet);
386 }
387
388 @QueryEngineTest
389 void invalidAssumeTest(QueryEvaluationHint hint) {
390 var query = Query.of("InvalidAssume", (builder, p1) -> builder.clause(Integer.class, (x) -> List.of(
391 personView.call(p1),
392 ageView.call(p1, x),
393 check(lessEq(div(constant(120), x), constant(5)))
394 )));
395
396 var store = ModelStore.builder()
397 .symbols(person, age)
398 .with(QueryInterpreterAdapter.builder()
399 .defaultHint(hint)
400 .queries(query))
401 .build();
402
403 var model = store.createEmptyModel();
404 var personInterpretation = model.getInterpretation(person);
405 var ageInterpretation = model.getInterpretation(age);
406 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
407 var queryResultSet = queryEngine.getResultSet(query);
408
409 personInterpretation.put(Tuple.of(0), true);
410 personInterpretation.put(Tuple.of(1), true);
411 personInterpretation.put(Tuple.of(2), true);
412
413 ageInterpretation.put(Tuple.of(0), 0);
414 ageInterpretation.put(Tuple.of(1), 30);
415 ageInterpretation.put(Tuple.of(2), 20);
416
417 queryEngine.flushChanges();
418 assertResults(Map.of(
419 Tuple.of(0), false,
420 Tuple.of(1), true,
421 Tuple.of(2), false,
422 Tuple.of(3), false
423 ), queryResultSet);
424 }
425
426 @QueryEngineTest
427 void multipleAssignmentTest(QueryEvaluationHint hint) {
428 var query = Query.of("MultipleAssignment", Integer.class, (builder, p1, p2, output) -> builder
429 .clause(Integer.class, Integer.class, (x1, x2) -> List.of(
430 ageView.call(p1, x1),
431 ageView.call(p2, x2),
432 output.assign(mul(x1, constant(2))),
433 output.assign(mul(x2, constant(3)))
434 )));
435
436 var store = ModelStore.builder()
437 .symbols(age)
438 .with(QueryInterpreterAdapter.builder()
439 .defaultHint(hint)
440 .queries(query))
441 .build();
442
443 var model = store.createEmptyModel();
444 var ageInterpretation = model.getInterpretation(age);
445 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
446 var queryResultSet = queryEngine.getResultSet(query);
447
448 ageInterpretation.put(Tuple.of(0), 3);
449 ageInterpretation.put(Tuple.of(1), 2);
450 ageInterpretation.put(Tuple.of(2), 15);
451 ageInterpretation.put(Tuple.of(3), 10);
452
453 queryEngine.flushChanges();
454 assertNullableResults(Map.of(
455 Tuple.of(0, 1), Optional.of(6),
456 Tuple.of(1, 0), Optional.empty(),
457 Tuple.of(2, 3), Optional.of(30),
458 Tuple.of(3, 2), Optional.empty()
459 ), queryResultSet);
460 }
461
462 @QueryEngineTest
463 void notFunctionalTest(QueryEvaluationHint hint) {
464 var query = Query.of("NotFunctional", Integer.class, (builder, p1, output) -> builder.clause((p2) -> List.of(
465 personView.call(p1),
466 friendMustView.call(p1, p2),
467 ageView.call(p2, output)
468 )));
469
470 var store = ModelStore.builder()
471 .symbols(person, age, friend)
472 .with(QueryInterpreterAdapter.builder()
473 .defaultHint(hint)
474 .queries(query))
475 .build();
476
477 var model = store.createEmptyModel();
478 var personInterpretation = model.getInterpretation(person);
479 var ageInterpretation = model.getInterpretation(age);
480 var friendInterpretation = model.getInterpretation(friend);
481 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
482 var queryResultSet = queryEngine.getResultSet(query);
483
484 personInterpretation.put(Tuple.of(0), true);
485 personInterpretation.put(Tuple.of(1), true);
486 personInterpretation.put(Tuple.of(2), true);
487
488 ageInterpretation.put(Tuple.of(0), 24);
489 ageInterpretation.put(Tuple.of(1), 30);
490 ageInterpretation.put(Tuple.of(2), 36);
491
492 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
493 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
494 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
495
496 queryEngine.flushChanges();
497 var invalidTuple = Tuple.of(1);
498 var cursor = queryResultSet.getAll();
499 assertAll(
500 () -> assertThat("value for key 0", queryResultSet.get(Tuple.of(0)), is(30)),
501 () -> assertThrows(IllegalStateException.class, () -> queryResultSet.get(invalidTuple),
502 "multiple values for key 1"),
503 () -> assertThat("value for key 2", queryResultSet.get(Tuple.of(2)), is(nullValue())),
504 () -> assertThat("value for key 3", queryResultSet.get(Tuple.of(3)), is(nullValue()))
505 );
506 if (hint.getQueryBackendRequirementType() != QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH) {
507 // Local search doesn't support throwing an error on multiple function return values.
508 assertThat("results size", queryResultSet.size(), is(2));
509 assertThrows(IllegalStateException.class, () -> enumerateValues(cursor), "move cursor");
510 }
511 }
512
513 private static void enumerateValues(Cursor<?, ?> cursor) {
514 //noinspection StatementWithEmptyBody
515 while (cursor.move()) {
516 // Nothing do, just let the cursor move through the result set.
517 }
518 }
519}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/OrderedResultSetTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/OrderedResultSetTest.java
new file mode 100644
index 00000000..96d0f302
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/OrderedResultSetTest.java
@@ -0,0 +1,117 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.query.resultset.OrderedResultSet;
13import tools.refinery.store.query.term.Variable;
14import tools.refinery.store.query.view.AnySymbolView;
15import tools.refinery.store.query.view.KeyOnlyView;
16import tools.refinery.store.representation.Symbol;
17import tools.refinery.store.tuple.Tuple;
18
19import static org.hamcrest.MatcherAssert.assertThat;
20import static org.hamcrest.Matchers.is;
21
22class OrderedResultSetTest {
23 private static final Symbol<Boolean> friend = Symbol.of("friend", 2);
24 private static final AnySymbolView friendView = new KeyOnlyView<>(friend);
25
26 @Test
27 void relationalFlushTest() {
28 var query = Query.of("Relation", (builder, p1, p2) -> builder.clause(
29 friendView.call(p1, p2)
30 ));
31
32 var store = ModelStore.builder()
33 .symbols(friend)
34 .with(QueryInterpreterAdapter.builder()
35 .queries(query))
36 .build();
37
38 var model = store.createEmptyModel();
39 var friendInterpretation = model.getInterpretation(friend);
40 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
41 var resultSet = queryEngine.getResultSet(query);
42
43 friendInterpretation.put(Tuple.of(0, 1), true);
44 friendInterpretation.put(Tuple.of(1, 2), true);
45 friendInterpretation.put(Tuple.of(1, 1), true);
46 queryEngine.flushChanges();
47
48 try (var orderedResultSet = new OrderedResultSet<>(resultSet)) {
49 assertThat(orderedResultSet.size(), is(3));
50 assertThat(orderedResultSet.getKey(0), is(Tuple.of(0, 1)));
51 assertThat(orderedResultSet.getKey(1), is(Tuple.of(1, 1)));
52 assertThat(orderedResultSet.getKey(2), is(Tuple.of(1, 2)));
53
54 friendInterpretation.put(Tuple.of(1, 2), false);
55 friendInterpretation.put(Tuple.of(0, 2), true);
56 queryEngine.flushChanges();
57
58 assertThat(orderedResultSet.size(), is(3));
59 assertThat(orderedResultSet.getKey(0), is(Tuple.of(0, 1)));
60 assertThat(orderedResultSet.getKey(1), is(Tuple.of(0, 2)));
61 assertThat(orderedResultSet.getKey(2), is(Tuple.of(1, 1)));
62 }
63 }
64
65 @Test
66 void functionalFlushTest() {
67 var query = Query.of("Function", Integer.class, (builder, p1, output) -> builder.clause(
68 friendView.call(p1, Variable.of()),
69 output.assign(friendView.count(p1, Variable.of()))
70 ));
71
72 var store = ModelStore.builder()
73 .symbols(friend)
74 .with(QueryInterpreterAdapter.builder()
75 .queries(query))
76 .build();
77
78 var model = store.createEmptyModel();
79 var friendInterpretation = model.getInterpretation(friend);
80 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
81 var resultSet = queryEngine.getResultSet(query);
82
83 friendInterpretation.put(Tuple.of(0, 1), true);
84 friendInterpretation.put(Tuple.of(1, 2), true);
85 friendInterpretation.put(Tuple.of(1, 1), true);
86 queryEngine.flushChanges();
87
88 try (var orderedResultSet = new OrderedResultSet<>(resultSet)) {
89 assertThat(orderedResultSet.size(), is(2));
90 assertThat(orderedResultSet.getKey(0), is(Tuple.of(0)));
91 assertThat(orderedResultSet.getKey(1), is(Tuple.of(1)));
92
93 friendInterpretation.put(Tuple.of(0, 1), false);
94 friendInterpretation.put(Tuple.of(2, 1), true);
95 queryEngine.flushChanges();
96
97 assertThat(orderedResultSet.size(), is(2));
98 assertThat(orderedResultSet.getKey(0), is(Tuple.of(1)));
99 assertThat(orderedResultSet.getKey(1), is(Tuple.of(2)));
100
101 friendInterpretation.put(Tuple.of(1, 1), false);
102 queryEngine.flushChanges();
103
104 assertThat(orderedResultSet.size(), is(2));
105 assertThat(orderedResultSet.getKey(0), is(Tuple.of(1)));
106 assertThat(orderedResultSet.getKey(1), is(Tuple.of(2)));
107
108 friendInterpretation.put(Tuple.of(1, 2), false);
109 friendInterpretation.put(Tuple.of(1, 0), true);
110 queryEngine.flushChanges();
111
112 assertThat(orderedResultSet.size(), is(2));
113 assertThat(orderedResultSet.getKey(0), is(Tuple.of(1)));
114 assertThat(orderedResultSet.getKey(1), is(Tuple.of(2)));
115 }
116 }
117}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTest.java
new file mode 100644
index 00000000..72728dcd
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTest.java
@@ -0,0 +1,794 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
9import org.junit.jupiter.api.Test;
10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.query.ModelQueryAdapter;
12import tools.refinery.store.query.dnf.Dnf;
13import tools.refinery.store.query.dnf.Query;
14import tools.refinery.store.query.term.ParameterDirection;
15import tools.refinery.store.query.term.Variable;
16import tools.refinery.store.query.interpreter.tests.QueryEngineTest;
17import tools.refinery.store.query.view.AnySymbolView;
18import tools.refinery.store.query.view.FilteredView;
19import tools.refinery.store.query.view.FunctionView;
20import tools.refinery.store.query.view.KeyOnlyView;
21import tools.refinery.store.representation.Symbol;
22import tools.refinery.store.representation.TruthValue;
23import tools.refinery.store.tuple.Tuple;
24
25import java.util.List;
26import java.util.Map;
27
28import static tools.refinery.store.query.literal.Literals.check;
29import static tools.refinery.store.query.literal.Literals.not;
30import static tools.refinery.store.query.term.int_.IntTerms.constant;
31import static tools.refinery.store.query.term.int_.IntTerms.greaterEq;
32import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertResults;
33
34class QueryTest {
35 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
36 private static final Symbol<TruthValue> friend = Symbol.of("friend", 2, TruthValue.class, TruthValue.FALSE);
37 private static final AnySymbolView personView = new KeyOnlyView<>(person);
38 private static final AnySymbolView friendMustView = new FilteredView<>(friend, "must", TruthValue::must);
39
40 @QueryEngineTest
41 void typeConstraintTest(QueryEvaluationHint hint) {
42 var asset = Symbol.of("Asset", 1);
43
44 var predicate = Query.of("TypeConstraint", (builder, p1) -> builder.clause(personView.call(p1)));
45
46 var store = ModelStore.builder()
47 .symbols(person, asset)
48 .with(QueryInterpreterAdapter.builder()
49 .defaultHint(hint)
50 .queries(predicate))
51 .build();
52
53 var model = store.createEmptyModel();
54 var personInterpretation = model.getInterpretation(person);
55 var assetInterpretation = model.getInterpretation(asset);
56 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
57 var predicateResultSet = queryEngine.getResultSet(predicate);
58
59 personInterpretation.put(Tuple.of(0), true);
60 personInterpretation.put(Tuple.of(1), true);
61
62 assetInterpretation.put(Tuple.of(1), true);
63 assetInterpretation.put(Tuple.of(2), true);
64
65 queryEngine.flushChanges();
66 assertResults(Map.of(
67 Tuple.of(0), true,
68 Tuple.of(1), true,
69 Tuple.of(2), false
70 ), predicateResultSet);
71 }
72
73 @QueryEngineTest
74 void relationConstraintTest(QueryEvaluationHint hint) {
75 var predicate = Query.of("RelationConstraint", (builder, p1, p2) -> builder.clause(
76 personView.call(p1),
77 personView.call(p2),
78 friendMustView.call(p1, p2)
79 ));
80
81 var store = ModelStore.builder()
82 .symbols(person, friend)
83 .with(QueryInterpreterAdapter.builder()
84 .defaultHint(hint)
85 .queries(predicate))
86 .build();
87
88 var model = store.createEmptyModel();
89 var personInterpretation = model.getInterpretation(person);
90 var friendInterpretation = model.getInterpretation(friend);
91 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
92 var predicateResultSet = queryEngine.getResultSet(predicate);
93
94 personInterpretation.put(Tuple.of(0), true);
95 personInterpretation.put(Tuple.of(1), true);
96 personInterpretation.put(Tuple.of(2), true);
97
98 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
99 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
100 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
101 friendInterpretation.put(Tuple.of(1, 3), TruthValue.TRUE);
102
103 queryEngine.flushChanges();
104 assertResults(Map.of(
105 Tuple.of(0, 1), true,
106 Tuple.of(1, 0), true,
107 Tuple.of(1, 2), true,
108 Tuple.of(2, 1), false
109 ), predicateResultSet);
110 }
111
112 @QueryEngineTest
113 void isConstantTest(QueryEvaluationHint hint) {
114 var predicate = Query.of("RelationConstraint", (builder, p1, p2) -> builder.clause(
115 personView.call(p1),
116 p1.isConstant(1),
117 friendMustView.call(p1, p2)
118 ));
119
120 var store = ModelStore.builder()
121 .symbols(person, friend)
122 .with(QueryInterpreterAdapter.builder()
123 .defaultHint(hint)
124 .queries(predicate))
125 .build();
126
127 var model = store.createEmptyModel();
128 var personInterpretation = model.getInterpretation(person);
129 var friendInterpretation = model.getInterpretation(friend);
130 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
131 var predicateResultSet = queryEngine.getResultSet(predicate);
132
133 personInterpretation.put(Tuple.of(0), true);
134 personInterpretation.put(Tuple.of(1), true);
135 personInterpretation.put(Tuple.of(2), true);
136
137 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
138 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
139 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
140
141 queryEngine.flushChanges();
142 assertResults(Map.of(
143 Tuple.of(0, 1), false,
144 Tuple.of(1, 0), true,
145 Tuple.of(1, 2), true,
146 Tuple.of(2, 1), false
147 ), predicateResultSet);
148 }
149
150 @QueryEngineTest
151 void existTest(QueryEvaluationHint hint) {
152 var predicate = Query.of("Exists", (builder, p1) -> builder.clause((p2) -> List.of(
153 personView.call(p1),
154 personView.call(p2),
155 friendMustView.call(p1, p2)
156 )));
157
158 var store = ModelStore.builder()
159 .symbols(person, friend)
160 .with(QueryInterpreterAdapter.builder()
161 .defaultHint(hint)
162 .queries(predicate))
163 .build();
164
165 var model = store.createEmptyModel();
166 var personInterpretation = model.getInterpretation(person);
167 var friendInterpretation = model.getInterpretation(friend);
168 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
169 var predicateResultSet = queryEngine.getResultSet(predicate);
170
171 personInterpretation.put(Tuple.of(0), true);
172 personInterpretation.put(Tuple.of(1), true);
173 personInterpretation.put(Tuple.of(2), true);
174
175 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
176 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
177 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
178 friendInterpretation.put(Tuple.of(3, 2), TruthValue.TRUE);
179
180 queryEngine.flushChanges();
181 assertResults(Map.of(
182 Tuple.of(0), true,
183 Tuple.of(1), true,
184 Tuple.of(2), false,
185 Tuple.of(3), false
186 ), predicateResultSet);
187 }
188
189 @QueryEngineTest
190 void orTest(QueryEvaluationHint hint) {
191 var animal = Symbol.of("Animal", 1);
192 var animalView = new KeyOnlyView<>(animal);
193
194 var predicate = Query.of("Or", (builder, p1, p2) -> builder.clause(
195 personView.call(p1),
196 personView.call(p2),
197 friendMustView.call(p1, p2)
198 ).clause(
199 animalView.call(p1),
200 animalView.call(p2),
201 friendMustView.call(p1, p2)
202 ));
203
204 var store = ModelStore.builder()
205 .symbols(person, animal, friend)
206 .with(QueryInterpreterAdapter.builder()
207 .defaultHint(hint)
208 .queries(predicate))
209 .build();
210
211 var model = store.createEmptyModel();
212 var personInterpretation = model.getInterpretation(person);
213 var animalInterpretation = model.getInterpretation(animal);
214 var friendInterpretation = model.getInterpretation(friend);
215 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
216 var predicateResultSet = queryEngine.getResultSet(predicate);
217
218 personInterpretation.put(Tuple.of(0), true);
219 personInterpretation.put(Tuple.of(1), true);
220
221 animalInterpretation.put(Tuple.of(2), true);
222 animalInterpretation.put(Tuple.of(3), true);
223
224 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
225 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
226 friendInterpretation.put(Tuple.of(2, 3), TruthValue.TRUE);
227 friendInterpretation.put(Tuple.of(3, 0), TruthValue.TRUE);
228
229 queryEngine.flushChanges();
230 assertResults(Map.of(
231 Tuple.of(0, 1), true,
232 Tuple.of(0, 2), false,
233 Tuple.of(2, 3), true,
234 Tuple.of(3, 0), false,
235 Tuple.of(3, 2), false
236 ), predicateResultSet);
237 }
238
239 @QueryEngineTest
240 void equalityTest(QueryEvaluationHint hint) {
241 var predicate = Query.of("Equality", (builder, p1, p2) -> builder.clause(
242 personView.call(p1),
243 personView.call(p2),
244 p1.isEquivalent(p2)
245 ));
246
247 var store = ModelStore.builder()
248 .symbols(person)
249 .with(QueryInterpreterAdapter.builder()
250 .defaultHint(hint)
251 .queries(predicate))
252 .build();
253
254 var model = store.createEmptyModel();
255 var personInterpretation = model.getInterpretation(person);
256 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
257 var predicateResultSet = queryEngine.getResultSet(predicate);
258
259 personInterpretation.put(Tuple.of(0), true);
260 personInterpretation.put(Tuple.of(1), true);
261 personInterpretation.put(Tuple.of(2), true);
262
263 queryEngine.flushChanges();
264 assertResults(Map.of(
265 Tuple.of(0, 0), true,
266 Tuple.of(1, 1), true,
267 Tuple.of(2, 2), true,
268 Tuple.of(0, 1), false,
269 Tuple.of(3, 3), false
270 ), predicateResultSet);
271 }
272
273 @QueryEngineTest
274 void inequalityTest(QueryEvaluationHint hint) {
275 var predicate = Query.of("Inequality", (builder, p1, p2, p3) -> builder.clause(
276 personView.call(p1),
277 personView.call(p2),
278 friendMustView.call(p1, p3),
279 friendMustView.call(p2, p3),
280 p1.notEquivalent(p2)
281 ));
282
283 var store = ModelStore.builder()
284 .symbols(person, friend)
285 .with(QueryInterpreterAdapter.builder()
286 .defaultHint(hint)
287 .queries(predicate))
288 .build();
289
290 var model = store.createEmptyModel();
291 var personInterpretation = model.getInterpretation(person);
292 var friendInterpretation = model.getInterpretation(friend);
293 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
294 var predicateResultSet = queryEngine.getResultSet(predicate);
295
296 personInterpretation.put(Tuple.of(0), true);
297 personInterpretation.put(Tuple.of(1), true);
298 personInterpretation.put(Tuple.of(2), true);
299
300 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
301 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
302
303 queryEngine.flushChanges();
304 assertResults(Map.of(
305 Tuple.of(0, 1, 2), true,
306 Tuple.of(1, 0, 2), true,
307 Tuple.of(0, 0, 2), false
308 ), predicateResultSet);
309 }
310
311 @QueryEngineTest
312 void patternCallTest(QueryEvaluationHint hint) {
313 var friendPredicate = Query.of("Friend", (builder, p1, p2) -> builder.clause(
314 personView.call(p1),
315 personView.call(p2),
316 friendMustView.call(p1, p2)
317 ));
318 var predicate = Query.of("PositivePatternCall", (builder, p3, p4) -> builder.clause(
319 personView.call(p3),
320 personView.call(p4),
321 friendPredicate.call(p3, p4)
322 ));
323
324 var store = ModelStore.builder()
325 .symbols(person, friend)
326 .with(QueryInterpreterAdapter.builder()
327 .defaultHint(hint)
328 .queries(predicate))
329 .build();
330
331 var model = store.createEmptyModel();
332 var personInterpretation = model.getInterpretation(person);
333 var friendInterpretation = model.getInterpretation(friend);
334 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
335 var predicateResultSet = queryEngine.getResultSet(predicate);
336
337 personInterpretation.put(Tuple.of(0), true);
338 personInterpretation.put(Tuple.of(1), true);
339 personInterpretation.put(Tuple.of(2), true);
340
341 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
342 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
343 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
344
345 queryEngine.flushChanges();
346 assertResults(Map.of(
347 Tuple.of(0, 1), true,
348 Tuple.of(1, 0), true,
349 Tuple.of(1, 2), true,
350 Tuple.of(2, 1), false
351 ), predicateResultSet);
352 }
353
354 @QueryEngineTest
355 void patternCallInputArgumentTest(QueryEvaluationHint hint) {
356 var friendPredicate = Dnf.of("Friend", builder -> {
357 var p1 = builder.parameter("p1", ParameterDirection.IN);
358 var p2 = builder.parameter("p2", ParameterDirection.IN);
359 builder.clause(
360 personView.call(p1),
361 personView.call(p2),
362 friendMustView.call(p1, p2)
363 );
364 });
365 var predicate = Query.of("PositivePatternCall", (builder, p3, p4) -> builder.clause(
366 personView.call(p3),
367 personView.call(p4),
368 friendPredicate.call(p3, p4)
369 ));
370
371 var store = ModelStore.builder()
372 .symbols(person, friend)
373 .with(QueryInterpreterAdapter.builder()
374 .defaultHint(hint)
375 .queries(predicate))
376 .build();
377
378 var model = store.createEmptyModel();
379 var personInterpretation = model.getInterpretation(person);
380 var friendInterpretation = model.getInterpretation(friend);
381 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
382 var predicateResultSet = queryEngine.getResultSet(predicate);
383
384 personInterpretation.put(Tuple.of(0), true);
385 personInterpretation.put(Tuple.of(1), true);
386 personInterpretation.put(Tuple.of(2), true);
387
388 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
389 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
390 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
391
392 queryEngine.flushChanges();
393 assertResults(Map.of(
394 Tuple.of(0, 1), true,
395 Tuple.of(1, 0), true,
396 Tuple.of(1, 2), true,
397 Tuple.of(2, 1), false
398 ), predicateResultSet);
399 }
400
401 @QueryEngineTest
402 void negativeRelationViewTest(QueryEvaluationHint hint) {
403 var predicate = Query.of("NegativePatternCall", (builder, p1, p2) -> builder.clause(
404 personView.call(p1),
405 personView.call(p2),
406 not(friendMustView.call(p1, p2))
407 ));
408
409 var store = ModelStore.builder()
410 .symbols(person, friend)
411 .with(QueryInterpreterAdapter.builder()
412 .defaultHint(hint)
413 .queries(predicate))
414 .build();
415
416 var model = store.createEmptyModel();
417 var personInterpretation = model.getInterpretation(person);
418 var friendInterpretation = model.getInterpretation(friend);
419 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
420 var predicateResultSet = queryEngine.getResultSet(predicate);
421
422 personInterpretation.put(Tuple.of(0), true);
423 personInterpretation.put(Tuple.of(1), true);
424 personInterpretation.put(Tuple.of(2), true);
425
426 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
427 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
428 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
429
430 queryEngine.flushChanges();
431 assertResults(Map.of(
432 Tuple.of(0, 0), true,
433 Tuple.of(0, 2), true,
434 Tuple.of(1, 1), true,
435 Tuple.of(2, 0), true,
436 Tuple.of(2, 1), true,
437 Tuple.of(2, 2), true,
438 Tuple.of(0, 1), false,
439 Tuple.of(1, 0), false,
440 Tuple.of(1, 2), false,
441 Tuple.of(0, 3), false
442 ), predicateResultSet);
443 }
444
445 @QueryEngineTest
446 void negativePatternCallTest(QueryEvaluationHint hint) {
447 var friendPredicate = Query.of("Friend", (builder, p1, p2) -> builder.clause(
448 personView.call(p1),
449 personView.call(p2),
450 friendMustView.call(p1, p2)
451 ));
452 var predicate = Query.of("NegativePatternCall", (builder, p3, p4) -> builder.clause(
453 personView.call(p3),
454 personView.call(p4),
455 not(friendPredicate.call(p3, p4))
456 ));
457
458 var store = ModelStore.builder()
459 .symbols(person, friend)
460 .with(QueryInterpreterAdapter.builder()
461 .defaultHint(hint)
462 .queries(predicate))
463 .build();
464
465 var model = store.createEmptyModel();
466 var personInterpretation = model.getInterpretation(person);
467 var friendInterpretation = model.getInterpretation(friend);
468 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
469 var predicateResultSet = queryEngine.getResultSet(predicate);
470
471 personInterpretation.put(Tuple.of(0), true);
472 personInterpretation.put(Tuple.of(1), true);
473 personInterpretation.put(Tuple.of(2), true);
474
475 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
476 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
477 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
478
479 queryEngine.flushChanges();
480 assertResults(Map.of(
481 Tuple.of(0, 0), true,
482 Tuple.of(0, 2), true,
483 Tuple.of(1, 1), true,
484 Tuple.of(2, 0), true,
485 Tuple.of(2, 1), true,
486 Tuple.of(2, 2), true,
487 Tuple.of(0, 1), false,
488 Tuple.of(1, 0), false,
489 Tuple.of(1, 2), false,
490 Tuple.of(0, 3), false
491 ), predicateResultSet);
492 }
493
494 @QueryEngineTest
495 void negativeRelationViewWithQuantificationTest(QueryEvaluationHint hint) {
496 var predicate = Query.of("Negative", (builder, p1) -> builder.clause(
497 personView.call(p1),
498 not(friendMustView.call(p1, Variable.of()))
499 ));
500
501 var store = ModelStore.builder()
502 .symbols(person, friend)
503 .with(QueryInterpreterAdapter.builder()
504 .defaultHint(hint)
505 .queries(predicate))
506 .build();
507
508 var model = store.createEmptyModel();
509 var personInterpretation = model.getInterpretation(person);
510 var friendInterpretation = model.getInterpretation(friend);
511 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
512 var predicateResultSet = queryEngine.getResultSet(predicate);
513
514 personInterpretation.put(Tuple.of(0), true);
515 personInterpretation.put(Tuple.of(1), true);
516 personInterpretation.put(Tuple.of(2), true);
517
518 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
519 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
520
521 queryEngine.flushChanges();
522 assertResults(Map.of(
523 Tuple.of(0), false,
524 Tuple.of(1), true,
525 Tuple.of(2), true,
526 Tuple.of(3), false
527 ), predicateResultSet);
528 }
529
530 @QueryEngineTest
531 void negativeWithQuantificationTest(QueryEvaluationHint hint) {
532 var called = Query.of("Called", (builder, p1, p2) -> builder.clause(
533 personView.call(p1),
534 personView.call(p2),
535 friendMustView.call(p1, p2)
536 ));
537 var predicate = Query.of("Negative", (builder, p1) -> builder.clause(
538 personView.call(p1),
539 not(called.call(p1, Variable.of()))
540 ));
541
542 var store = ModelStore.builder()
543 .symbols(person, friend)
544 .with(QueryInterpreterAdapter.builder()
545 .defaultHint(hint)
546 .queries(predicate))
547 .build();
548
549 var model = store.createEmptyModel();
550 var personInterpretation = model.getInterpretation(person);
551 var friendInterpretation = model.getInterpretation(friend);
552 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
553 var predicateResultSet = queryEngine.getResultSet(predicate);
554
555 personInterpretation.put(Tuple.of(0), true);
556 personInterpretation.put(Tuple.of(1), true);
557 personInterpretation.put(Tuple.of(2), true);
558
559 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
560 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
561
562 queryEngine.flushChanges();
563 assertResults(Map.of(
564 Tuple.of(0), false,
565 Tuple.of(1), true,
566 Tuple.of(2), true,
567 Tuple.of(3), false
568 ), predicateResultSet);
569 }
570
571 @QueryEngineTest
572 void transitiveRelationViewTest(QueryEvaluationHint hint) {
573 var predicate = Query.of("Transitive", (builder, p1, p2) -> builder.clause(
574 personView.call(p1),
575 personView.call(p2),
576 friendMustView.callTransitive(p1, p2)
577 ));
578
579 var store = ModelStore.builder()
580 .symbols(person, friend)
581 .with(QueryInterpreterAdapter.builder()
582 .defaultHint(hint)
583 .queries(predicate))
584 .build();
585
586 var model = store.createEmptyModel();
587 var personInterpretation = model.getInterpretation(person);
588 var friendInterpretation = model.getInterpretation(friend);
589 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
590 var predicateResultSet = queryEngine.getResultSet(predicate);
591
592 personInterpretation.put(Tuple.of(0), true);
593 personInterpretation.put(Tuple.of(1), true);
594 personInterpretation.put(Tuple.of(2), true);
595
596 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
597 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
598
599 queryEngine.flushChanges();
600 assertResults(Map.of(
601 Tuple.of(0, 0), false,
602 Tuple.of(0, 1), true,
603 Tuple.of(0, 2), true,
604 Tuple.of(1, 0), false,
605 Tuple.of(1, 1), false,
606 Tuple.of(1, 2), true,
607 Tuple.of(2, 0), false,
608 Tuple.of(2, 1), false,
609 Tuple.of(2, 2), false,
610 Tuple.of(2, 3), false
611 ), predicateResultSet);
612 }
613
614 @QueryEngineTest
615 void transitivePatternCallTest(QueryEvaluationHint hint) {
616 var called = Query.of("Called", (builder, p1, p2) -> builder.clause(
617 personView.call(p1),
618 personView.call(p2),
619 friendMustView.call(p1, p2)
620 ));
621 var predicate = Query.of("Transitive", (builder, p1, p2) -> builder.clause(
622 personView.call(p1),
623 personView.call(p2),
624 called.callTransitive(p1, p2)
625 ));
626
627 var store = ModelStore.builder()
628 .symbols(person, friend)
629 .with(QueryInterpreterAdapter.builder()
630 .defaultHint(hint)
631 .queries(predicate))
632 .build();
633
634 var model = store.createEmptyModel();
635 var personInterpretation = model.getInterpretation(person);
636 var friendInterpretation = model.getInterpretation(friend);
637 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
638 var predicateResultSet = queryEngine.getResultSet(predicate);
639
640 personInterpretation.put(Tuple.of(0), true);
641 personInterpretation.put(Tuple.of(1), true);
642 personInterpretation.put(Tuple.of(2), true);
643
644 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
645 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
646
647 queryEngine.flushChanges();
648 assertResults(Map.of(
649 Tuple.of(0, 0), false,
650 Tuple.of(0, 1), true,
651 Tuple.of(0, 2), true,
652 Tuple.of(1, 0), false,
653 Tuple.of(1, 1), false,
654 Tuple.of(1, 2), true,
655 Tuple.of(2, 0), false,
656 Tuple.of(2, 1), false,
657 Tuple.of(2, 2), false,
658 Tuple.of(2, 3), false
659 ), predicateResultSet);
660 }
661
662 @Test
663 void filteredIntegerViewTest() {
664 var distance = Symbol.of("distance", 2, Integer.class);
665 var nearView = new FilteredView<>(distance, value -> value < 2);
666 var farView = new FilteredView<>(distance, value -> value >= 5);
667 var dangerQuery = Query.of("danger", (builder, a1, a2) -> builder.clause((a3) -> List.of(
668 a1.notEquivalent(a2),
669 nearView.call(a1, a3),
670 nearView.call(a2, a3),
671 not(farView.call(a1, a2))
672 )));
673 var store = ModelStore.builder()
674 .symbols(distance)
675 .with(QueryInterpreterAdapter.builder()
676 .queries(dangerQuery))
677 .build();
678
679 var model = store.createEmptyModel();
680 var distanceInterpretation = model.getInterpretation(distance);
681 distanceInterpretation.put(Tuple.of(0, 1), 1);
682 distanceInterpretation.put(Tuple.of(1, 0), 1);
683 distanceInterpretation.put(Tuple.of(0, 2), 1);
684 distanceInterpretation.put(Tuple.of(2, 0), 1);
685 distanceInterpretation.put(Tuple.of(1, 2), 3);
686 distanceInterpretation.put(Tuple.of(2, 1), 3);
687 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
688 var dangerResultSet = queryEngine.getResultSet(dangerQuery);
689 queryEngine.flushChanges();
690 assertResults(Map.of(
691 Tuple.of(0, 1), false,
692 Tuple.of(0, 2), false,
693 Tuple.of(1, 2), true,
694 Tuple.of(2, 1), true
695 ), dangerResultSet);
696 }
697
698 @Test
699 void filteredDoubleViewTest() {
700 var distance = Symbol.of("distance", 2, Double.class);
701 var nearView = new FilteredView<>(distance, value -> value < 2);
702 var farView = new FilteredView<>(distance, value -> value >= 5);
703 var dangerQuery = Query.of("danger", (builder, a1, a2) -> builder.clause((a3) -> List.of(
704 a1.notEquivalent(a2),
705 nearView.call(a1, a3),
706 nearView.call(a2, a3),
707 not(farView.call(a1, a2))
708 )));
709 var store = ModelStore.builder()
710 .symbols(distance)
711 .with(QueryInterpreterAdapter.builder()
712 .queries(dangerQuery))
713 .build();
714
715 var model = store.createEmptyModel();
716 var distanceInterpretation = model.getInterpretation(distance);
717 distanceInterpretation.put(Tuple.of(0, 1), 1.0);
718 distanceInterpretation.put(Tuple.of(1, 0), 1.0);
719 distanceInterpretation.put(Tuple.of(0, 2), 1.0);
720 distanceInterpretation.put(Tuple.of(2, 0), 1.0);
721 distanceInterpretation.put(Tuple.of(1, 2), 3.0);
722 distanceInterpretation.put(Tuple.of(2, 1), 3.0);
723 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
724 var dangerResultSet = queryEngine.getResultSet(dangerQuery);
725 queryEngine.flushChanges();
726 assertResults(Map.of(
727 Tuple.of(0, 1), false,
728 Tuple.of(0, 2), false,
729 Tuple.of(1, 2), true,
730 Tuple.of(2, 1), true
731 ), dangerResultSet);
732 }
733
734 @QueryEngineTest
735 void assumeTest(QueryEvaluationHint hint) {
736 var age = Symbol.of("age", 1, Integer.class);
737 var ageView = new FunctionView<>(age);
738
739 var query = Query.of("Constraint", (builder, p1) -> builder.clause(Integer.class, (x) -> List.of(
740 personView.call(p1),
741 ageView.call(p1, x),
742 check(greaterEq(x, constant(18)))
743 )));
744
745 var store = ModelStore.builder()
746 .symbols(person, age)
747 .with(QueryInterpreterAdapter.builder()
748 .defaultHint(hint)
749 .queries(query))
750 .build();
751
752 var model = store.createEmptyModel();
753 var personInterpretation = model.getInterpretation(person);
754 var ageInterpretation = model.getInterpretation(age);
755 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
756 var queryResultSet = queryEngine.getResultSet(query);
757
758 personInterpretation.put(Tuple.of(0), true);
759 personInterpretation.put(Tuple.of(1), true);
760
761 ageInterpretation.put(Tuple.of(0), 12);
762 ageInterpretation.put(Tuple.of(1), 24);
763
764 queryEngine.flushChanges();
765 assertResults(Map.of(
766 Tuple.of(0), false,
767 Tuple.of(1), true,
768 Tuple.of(2), false
769 ), queryResultSet);
770 }
771
772 @Test
773 void alwaysFalseTest() {
774 var predicate = Query.of("AlwaysFalse", builder -> builder.parameter("p1"));
775
776 var store = ModelStore.builder()
777 .symbols(person)
778 .with(QueryInterpreterAdapter.builder()
779 .queries(predicate))
780 .build();
781
782 var model = store.createEmptyModel();
783 var personInterpretation = model.getInterpretation(person);
784 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
785 var predicateResultSet = queryEngine.getResultSet(predicate);
786
787 personInterpretation.put(Tuple.of(0), true);
788 personInterpretation.put(Tuple.of(1), true);
789 personInterpretation.put(Tuple.of(2), true);
790
791 queryEngine.flushChanges();
792 assertResults(Map.of(), predicateResultSet);
793 }
794}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTransactionTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTransactionTest.java
new file mode 100644
index 00000000..1cd05d91
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/QueryTransactionTest.java
@@ -0,0 +1,370 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
9import org.junit.jupiter.api.Test;
10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.query.ModelQueryAdapter;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.query.dnf.RelationalQuery;
14import tools.refinery.store.query.view.AnySymbolView;
15import tools.refinery.store.query.view.FilteredView;
16import tools.refinery.store.query.view.FunctionView;
17import tools.refinery.store.query.view.KeyOnlyView;
18import tools.refinery.store.representation.Symbol;
19import tools.refinery.store.tuple.Tuple;
20
21import java.util.Map;
22import java.util.Optional;
23
24import static org.junit.jupiter.api.Assertions.assertFalse;
25import static org.junit.jupiter.api.Assertions.assertTrue;
26import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertNullableResults;
27import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertResults;
28
29class QueryTransactionTest {
30 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
31 private static final Symbol<Integer> age = Symbol.of("age", 1, Integer.class);
32 private static final AnySymbolView personView = new KeyOnlyView<>(person);
33 private static final AnySymbolView ageView = new FunctionView<>(age);
34 private static final RelationalQuery predicate = Query.of("TypeConstraint", (builder, p1) ->
35 builder.clause(personView.call(p1)));
36
37 @Test
38 void flushTest() {
39 var store = ModelStore.builder()
40 .symbols(person)
41 .with(QueryInterpreterAdapter.builder()
42 .queries(predicate))
43 .build();
44
45 var model = store.createEmptyModel();
46 var personInterpretation = model.getInterpretation(person);
47 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
48 var predicateResultSet = queryEngine.getResultSet(predicate);
49
50 assertResults(Map.of(
51 Tuple.of(0), false,
52 Tuple.of(1), false,
53 Tuple.of(2), false,
54 Tuple.of(3), false
55 ), predicateResultSet);
56 assertFalse(queryEngine.hasPendingChanges());
57
58 personInterpretation.put(Tuple.of(0), true);
59 personInterpretation.put(Tuple.of(1), true);
60
61 assertResults(Map.of(
62 Tuple.of(0), false,
63 Tuple.of(1), false,
64 Tuple.of(2), false,
65 Tuple.of(3), false
66 ), predicateResultSet);
67 assertTrue(queryEngine.hasPendingChanges());
68
69 queryEngine.flushChanges();
70 assertResults(Map.of(
71 Tuple.of(0), true,
72 Tuple.of(1), true,
73 Tuple.of(2), false,
74 Tuple.of(3), false
75 ), predicateResultSet);
76 assertFalse(queryEngine.hasPendingChanges());
77
78 personInterpretation.put(Tuple.of(1), false);
79 personInterpretation.put(Tuple.of(2), true);
80
81 assertResults(Map.of(
82 Tuple.of(0), true,
83 Tuple.of(1), true,
84 Tuple.of(2), false,
85 Tuple.of(3), false
86 ), predicateResultSet);
87 assertTrue(queryEngine.hasPendingChanges());
88
89 queryEngine.flushChanges();
90 assertResults(Map.of(
91 Tuple.of(0), true,
92 Tuple.of(1), false,
93 Tuple.of(2), true,
94 Tuple.of(3), false
95 ), predicateResultSet);
96 assertFalse(queryEngine.hasPendingChanges());
97 }
98
99 @Test
100 void localSearchTest() {
101 var store = ModelStore.builder()
102 .symbols(person)
103 .with(QueryInterpreterAdapter.builder()
104 .defaultHint(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH))
105 .queries(predicate))
106 .build();
107
108 var model = store.createEmptyModel();
109 var personInterpretation = model.getInterpretation(person);
110 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
111 var predicateResultSet = queryEngine.getResultSet(predicate);
112
113 assertResults(Map.of(
114 Tuple.of(0), false,
115 Tuple.of(1), false,
116 Tuple.of(2), false,
117 Tuple.of(3), false
118 ), predicateResultSet);
119 assertFalse(queryEngine.hasPendingChanges());
120
121 personInterpretation.put(Tuple.of(0), true);
122 personInterpretation.put(Tuple.of(1), true);
123
124 assertResults(Map.of(
125 Tuple.of(0), true,
126 Tuple.of(1), true,
127 Tuple.of(2), false,
128 Tuple.of(3), false
129 ), predicateResultSet);
130 assertFalse(queryEngine.hasPendingChanges());
131
132 personInterpretation.put(Tuple.of(1), false);
133 personInterpretation.put(Tuple.of(2), true);
134
135 assertResults(Map.of(
136 Tuple.of(0), true,
137 Tuple.of(1), false,
138 Tuple.of(2), true,
139 Tuple.of(3), false
140 ), predicateResultSet);
141 assertFalse(queryEngine.hasPendingChanges());
142 }
143
144 @Test
145 void unrelatedChangesTest() {
146 var asset = Symbol.of("Asset", 1);
147
148 var store = ModelStore.builder()
149 .symbols(person, asset)
150 .with(QueryInterpreterAdapter.builder()
151 .queries(predicate))
152 .build();
153
154 var model = store.createEmptyModel();
155 var personInterpretation = model.getInterpretation(person);
156 var assetInterpretation = model.getInterpretation(asset);
157 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
158 var predicateResultSet = queryEngine.getResultSet(predicate);
159
160 assertFalse(queryEngine.hasPendingChanges());
161
162 personInterpretation.put(Tuple.of(0), true);
163 personInterpretation.put(Tuple.of(1), true);
164
165 assetInterpretation.put(Tuple.of(1), true);
166 assetInterpretation.put(Tuple.of(2), true);
167
168 assertResults(Map.of(
169 Tuple.of(0), false,
170 Tuple.of(1), false,
171 Tuple.of(2), false,
172 Tuple.of(3), false,
173 Tuple.of(4), false
174 ), predicateResultSet);
175 assertTrue(queryEngine.hasPendingChanges());
176
177 queryEngine.flushChanges();
178 assertResults(Map.of(
179 Tuple.of(0), true,
180 Tuple.of(1), true,
181 Tuple.of(2), false,
182 Tuple.of(3), false,
183 Tuple.of(4), false
184 ), predicateResultSet);
185 assertFalse(queryEngine.hasPendingChanges());
186
187 assetInterpretation.put(Tuple.of(3), true);
188 assertFalse(queryEngine.hasPendingChanges());
189
190 assertResults(Map.of(
191 Tuple.of(0), true,
192 Tuple.of(1), true,
193 Tuple.of(2), false,
194 Tuple.of(3), false,
195 Tuple.of(4), false
196 ), predicateResultSet);
197
198 queryEngine.flushChanges();
199 assertResults(Map.of(
200 Tuple.of(0), true,
201 Tuple.of(1), true,
202 Tuple.of(2), false,
203 Tuple.of(3), false,
204 Tuple.of(4), false
205 ), predicateResultSet);
206 assertFalse(queryEngine.hasPendingChanges());
207 }
208
209 @Test
210 void tupleChangingChangeTest() {
211 var query = Query.of("TypeConstraint", Integer.class, (builder, p1, output) -> builder.clause(
212 personView.call(p1),
213 ageView.call(p1, output)
214 ));
215
216 var store = ModelStore.builder()
217 .symbols(person, age)
218 .with(QueryInterpreterAdapter.builder()
219 .queries(query))
220 .build();
221
222 var model = store.createEmptyModel();
223 var personInterpretation = model.getInterpretation(person);
224 var ageInterpretation = model.getInterpretation(age);
225 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
226 var queryResultSet = queryEngine.getResultSet(query);
227
228 personInterpretation.put(Tuple.of(0), true);
229
230 ageInterpretation.put(Tuple.of(0), 24);
231
232 queryEngine.flushChanges();
233 assertResults(Map.of(Tuple.of(0), 24), queryResultSet);
234
235 ageInterpretation.put(Tuple.of(0), 25);
236
237 queryEngine.flushChanges();
238 assertResults(Map.of(Tuple.of(0), 25), queryResultSet);
239
240 ageInterpretation.put(Tuple.of(0), null);
241
242 queryEngine.flushChanges();
243 assertNullableResults(Map.of(Tuple.of(0), Optional.empty()), queryResultSet);
244 }
245
246 @Test
247 void tuplePreservingUnchangedTest() {
248 var adultView = new FilteredView<>(age, "adult", n -> n != null && n >= 18);
249
250 var query = Query.of("TypeConstraint", (builder, p1) -> builder.clause(
251 personView.call(p1),
252 adultView.call(p1)
253 ));
254
255 var store = ModelStore.builder()
256 .symbols(person, age)
257 .with(QueryInterpreterAdapter.builder()
258 .queries(query))
259 .build();
260
261 var model = store.createEmptyModel();
262 var personInterpretation = model.getInterpretation(person);
263 var ageInterpretation = model.getInterpretation(age);
264 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
265 var queryResultSet = queryEngine.getResultSet(query);
266
267 personInterpretation.put(Tuple.of(0), true);
268
269 ageInterpretation.put(Tuple.of(0), 24);
270
271 queryEngine.flushChanges();
272 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
273
274 ageInterpretation.put(Tuple.of(0), 25);
275
276 queryEngine.flushChanges();
277 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
278
279 ageInterpretation.put(Tuple.of(0), 17);
280
281 queryEngine.flushChanges();
282 assertResults(Map.of(Tuple.of(0), false), queryResultSet);
283 }
284
285 @Test
286 void commitAfterFlushTest() {
287 var store = ModelStore.builder()
288 .symbols(person)
289 .with(QueryInterpreterAdapter.builder()
290 .queries(predicate))
291 .build();
292
293 var model = store.createEmptyModel();
294 var personInterpretation = model.getInterpretation(person);
295 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
296 var predicateResultSet = queryEngine.getResultSet(predicate);
297
298 personInterpretation.put(Tuple.of(0), true);
299 personInterpretation.put(Tuple.of(1), true);
300
301 queryEngine.flushChanges();
302 assertResults(Map.of(
303 Tuple.of(0), true,
304 Tuple.of(1), true,
305 Tuple.of(2), false,
306 Tuple.of(3), false
307 ), predicateResultSet);
308
309 var state1 = model.commit();
310
311 personInterpretation.put(Tuple.of(1), false);
312 personInterpretation.put(Tuple.of(2), true);
313
314 queryEngine.flushChanges();
315 assertResults(Map.of(
316 Tuple.of(0), true,
317 Tuple.of(1), false,
318 Tuple.of(2), true,
319 Tuple.of(3), false
320 ), predicateResultSet);
321
322 model.restore(state1);
323
324 assertFalse(queryEngine.hasPendingChanges());
325 assertResults(Map.of(
326 Tuple.of(0), true,
327 Tuple.of(1), true,
328 Tuple.of(2), false,
329 Tuple.of(3), false
330 ), predicateResultSet);
331 }
332
333 @Test
334 void commitWithoutFlushTest() {
335 var store = ModelStore.builder()
336 .symbols(person)
337 .with(QueryInterpreterAdapter.builder()
338 .queries(predicate))
339 .build();
340
341 var model = store.createEmptyModel();
342 var personInterpretation = model.getInterpretation(person);
343 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
344 var predicateResultSet = queryEngine.getResultSet(predicate);
345
346 personInterpretation.put(Tuple.of(0), true);
347 personInterpretation.put(Tuple.of(1), true);
348
349 assertResults(Map.of(), predicateResultSet);
350 assertTrue(queryEngine.hasPendingChanges());
351
352 var state1 = model.commit();
353
354 personInterpretation.put(Tuple.of(1), false);
355 personInterpretation.put(Tuple.of(2), true);
356
357 assertResults(Map.of(), predicateResultSet);
358 assertTrue(queryEngine.hasPendingChanges());
359
360 model.restore(state1);
361
362 assertResults(Map.of(
363 Tuple.of(0), true,
364 Tuple.of(1), true,
365 Tuple.of(2), false,
366 Tuple.of(3), false
367 ), predicateResultSet);
368 assertFalse(queryEngine.hasPendingChanges());
369 }
370}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/StronglyConnectedComponentsTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/StronglyConnectedComponentsTest.java
new file mode 100644
index 00000000..edbd9aff
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/StronglyConnectedComponentsTest.java
@@ -0,0 +1,261 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.query.literal.Connectivity;
13import tools.refinery.store.query.literal.RepresentativeElectionLiteral;
14import tools.refinery.store.query.view.AnySymbolView;
15import tools.refinery.store.query.view.KeyOnlyView;
16import tools.refinery.store.representation.Symbol;
17import tools.refinery.store.tuple.Tuple;
18
19import java.util.List;
20import java.util.Map;
21
22import static org.hamcrest.MatcherAssert.assertThat;
23import static org.hamcrest.Matchers.is;
24import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertResults;
25
26class StronglyConnectedComponentsTest {
27 private static final Symbol<Boolean> friend = Symbol.of("friend", 2);
28 private static final AnySymbolView friendView = new KeyOnlyView<>(friend);
29
30 @Test
31 void symbolViewTest() {
32 var query = Query.of("SymbolViewRepresentative", (builder, p1, p2) -> builder
33 .clause(v1 -> List.of(
34 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p1, v1),
35 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p2, v1)
36 )));
37
38 var store = ModelStore.builder()
39 .symbols(friend)
40 .with(QueryInterpreterAdapter.builder()
41 .queries(query))
42 .build();
43
44 var model = store.createEmptyModel();
45 var friendInterpretation = model.getInterpretation(friend);
46 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
47 var resultSet = queryEngine.getResultSet(query);
48
49 friendInterpretation.put(Tuple.of(0, 1), true);
50 friendInterpretation.put(Tuple.of(1, 0), true);
51 friendInterpretation.put(Tuple.of(1, 2), true);
52 queryEngine.flushChanges();
53
54 assertResults(Map.of(
55 Tuple.of(0, 0), true,
56 Tuple.of(0, 1), true,
57 Tuple.of(1, 0), true,
58 Tuple.of(1, 1), true,
59 Tuple.of(2, 2), true
60 ), resultSet);
61 }
62
63 @Test
64 void symbolViewInsertTest() {
65 var query = Query.of("SymbolViewRepresentative", (builder, p1, p2) -> builder
66 .clause(v1 -> List.of(
67 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p1, v1),
68 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p2, v1)
69 )));
70
71 var store = ModelStore.builder()
72 .symbols(friend)
73 .with(QueryInterpreterAdapter.builder()
74 .queries(query))
75 .build();
76
77 var model = store.createEmptyModel();
78 var friendInterpretation = model.getInterpretation(friend);
79 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
80 var resultSet = queryEngine.getResultSet(query);
81
82 friendInterpretation.put(Tuple.of(0, 1), true);
83 friendInterpretation.put(Tuple.of(1, 0), true);
84 friendInterpretation.put(Tuple.of(1, 2), true);
85 queryEngine.flushChanges();
86
87 friendInterpretation.put(Tuple.of(2, 0), true);
88 friendInterpretation.put(Tuple.of(0, 3), true);
89 queryEngine.flushChanges();
90
91 assertResults(Map.of(
92 Tuple.of(0, 0), true,
93 Tuple.of(0, 1), true,
94 Tuple.of(0, 2), true,
95 Tuple.of(1, 1), true,
96 Tuple.of(1, 0), true,
97 Tuple.of(1, 2), true,
98 Tuple.of(2, 0), true,
99 Tuple.of(2, 1), true,
100 Tuple.of(2, 2), true,
101 Tuple.of(3, 3), true
102 ), resultSet);
103 }
104
105 @Test
106 void symbolViewDeleteTest() {
107 var query = Query.of("SymbolViewRepresentative", (builder, p1, p2) -> builder
108 .clause(v1 -> List.of(
109 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p1, v1),
110 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p2, v1)
111 )));
112
113 var store = ModelStore.builder()
114 .symbols(friend)
115 .with(QueryInterpreterAdapter.builder()
116 .queries(query))
117 .build();
118
119 var model = store.createEmptyModel();
120 var friendInterpretation = model.getInterpretation(friend);
121 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
122 var resultSet = queryEngine.getResultSet(query);
123
124 friendInterpretation.put(Tuple.of(0, 1), true);
125 friendInterpretation.put(Tuple.of(1, 0), true);
126 friendInterpretation.put(Tuple.of(1, 2), true);
127 queryEngine.flushChanges();
128
129 friendInterpretation.put(Tuple.of(1, 0), false);
130 friendInterpretation.put(Tuple.of(1, 2), false);
131 queryEngine.flushChanges();
132
133 assertResults(Map.of(
134 Tuple.of(0, 0), true,
135 Tuple.of(1, 1), true
136 ), resultSet);
137 }
138
139 @Test
140 void diagonalSymbolViewTest() {
141 var person = Symbol.of("Person", 1);
142 var personView = new KeyOnlyView<>(person);
143
144 var query = Query.of("SymbolViewRepresentative", (builder, p1) -> builder
145 .clause(
146 personView.call(p1),
147 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p1, p1)
148 ));
149
150 var store = ModelStore.builder()
151 .symbols(person, friend)
152 .with(QueryInterpreterAdapter.builder()
153 .queries(query))
154 .build();
155
156 var model = store.createEmptyModel();
157 var personInterpretation = model.getInterpretation(person);
158 var friendInterpretation = model.getInterpretation(friend);
159 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
160 var resultSet = queryEngine.getResultSet(query);
161
162 personInterpretation.put(Tuple.of(0), true);
163 personInterpretation.put(Tuple.of(1), true);
164 personInterpretation.put(Tuple.of(2), true);
165
166 friendInterpretation.put(Tuple.of(0, 1), true);
167 friendInterpretation.put(Tuple.of(1, 0), true);
168 friendInterpretation.put(Tuple.of(1, 2), true);
169 queryEngine.flushChanges();
170
171 assertThat(resultSet.size(), is(2));
172 assertThat(resultSet.get(Tuple.of(2)), is(true));
173 }
174
175 @Test
176 void diagonalDnfTest() {
177 var person = Symbol.of("Person", 1);
178 var personView = new KeyOnlyView<>(person);
179
180 var subQuery = Query.of("SubQuery", (builder, p1, p2) -> builder
181 .clause(
182 personView.call(p1),
183 personView.call(p2),
184 friendView.call(p1, p2)
185 ))
186 .getDnf();
187 var query = Query.of("SymbolViewRepresentative", (builder, p1) -> builder
188 .clause(
189 personView.call(p1),
190 new RepresentativeElectionLiteral(Connectivity.STRONG, subQuery, p1, p1)
191 ));
192
193 var store = ModelStore.builder()
194 .symbols(person, friend)
195 .with(QueryInterpreterAdapter.builder()
196 .queries(query))
197 .build();
198
199 var model = store.createEmptyModel();
200 var personInterpretation = model.getInterpretation(person);
201 var friendInterpretation = model.getInterpretation(friend);
202 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
203 var resultSet = queryEngine.getResultSet(query);
204
205 personInterpretation.put(Tuple.of(0), true);
206 personInterpretation.put(Tuple.of(1), true);
207 personInterpretation.put(Tuple.of(2), true);
208
209 friendInterpretation.put(Tuple.of(0, 1), true);
210 friendInterpretation.put(Tuple.of(1, 0), true);
211 friendInterpretation.put(Tuple.of(1, 2), true);
212 queryEngine.flushChanges();
213
214 assertThat(resultSet.size(), is(2));
215 assertThat(resultSet.get(Tuple.of(2)), is(true));
216 }
217
218 @Test
219 void loopTest() {
220 var query = Query.of("SymbolViewRepresentative", (builder, p1, p2) -> builder
221 .clause(v1 -> List.of(
222 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p1, v1),
223 new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p2, v1)
224 )));
225
226 var store = ModelStore.builder()
227 .symbols(friend)
228 .with(QueryInterpreterAdapter.builder()
229 .queries(query))
230 .build();
231
232 var model = store.createEmptyModel();
233 var friendInterpretation = model.getInterpretation(friend);
234 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
235 var resultSet = queryEngine.getResultSet(query);
236
237 friendInterpretation.put(Tuple.of(0, 1), true);
238 friendInterpretation.put(Tuple.of(1, 2), true);
239 friendInterpretation.put(Tuple.of(2, 3), true);
240 friendInterpretation.put(Tuple.of(3, 0), true);
241 friendInterpretation.put(Tuple.of(3, 4), true);
242 queryEngine.flushChanges();
243
244 assertThat(resultSet.get(Tuple.of(0, 1)), is(true));
245 assertThat(resultSet.get(Tuple.of(1, 2)), is(true));
246 assertThat(resultSet.get(Tuple.of(2, 3)), is(true));
247 assertThat(resultSet.get(Tuple.of(3, 0)), is(true));
248 assertThat(resultSet.get(Tuple.of(3, 4)), is(false));
249
250 friendInterpretation.put(Tuple.of(2, 3), false);
251 queryEngine.flushChanges();
252
253 assertThat(resultSet.get(Tuple.of(0, 1)), is(false));
254 assertThat(resultSet.get(Tuple.of(0, 2)), is(false));
255 assertThat(resultSet.get(Tuple.of(0, 3)), is(false));
256 assertThat(resultSet.get(Tuple.of(1, 2)), is(false));
257 assertThat(resultSet.get(Tuple.of(2, 3)), is(false));
258 assertThat(resultSet.get(Tuple.of(3, 0)), is(false));
259 assertThat(resultSet.get(Tuple.of(3, 4)), is(false));
260 }
261}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/WeaklyConnectedComponentsTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/WeaklyConnectedComponentsTest.java
new file mode 100644
index 00000000..3fc85480
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/WeaklyConnectedComponentsTest.java
@@ -0,0 +1,188 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.query.literal.Connectivity;
13import tools.refinery.store.query.literal.RepresentativeElectionLiteral;
14import tools.refinery.store.query.view.AnySymbolView;
15import tools.refinery.store.query.view.KeyOnlyView;
16import tools.refinery.store.representation.Symbol;
17import tools.refinery.store.tuple.Tuple;
18
19import java.util.List;
20import java.util.Map;
21
22import static org.hamcrest.MatcherAssert.assertThat;
23import static org.hamcrest.Matchers.is;
24import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertResults;
25
26class WeaklyConnectedComponentsTest {
27 private static final Symbol<Boolean> friend = Symbol.of("friend", 2);
28 private static final AnySymbolView friendView = new KeyOnlyView<>(friend);
29
30 @Test
31 void symbolViewTest() {
32 var query = Query.of("SymbolViewRepresentative", (builder, p1, p2) -> builder
33 .clause(v1 -> List.of(
34 new RepresentativeElectionLiteral(Connectivity.WEAK, friendView, p1, v1),
35 new RepresentativeElectionLiteral(Connectivity.WEAK, friendView, p2, v1)
36 )));
37
38 var store = ModelStore.builder()
39 .symbols(friend)
40 .with(QueryInterpreterAdapter.builder()
41 .queries(query))
42 .build();
43
44 var model = store.createEmptyModel();
45 var friendInterpretation = model.getInterpretation(friend);
46 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
47 var resultSet = queryEngine.getResultSet(query);
48
49 friendInterpretation.put(Tuple.of(0, 1), true);
50 friendInterpretation.put(Tuple.of(1, 0), true);
51 friendInterpretation.put(Tuple.of(2, 3), true);
52 queryEngine.flushChanges();
53
54 assertResults(Map.of(
55 Tuple.of(0, 0), true,
56 Tuple.of(0, 1), true,
57 Tuple.of(1, 0), true,
58 Tuple.of(1, 1), true,
59 Tuple.of(2, 2), true,
60 Tuple.of(2, 3), true,
61 Tuple.of(3, 2), true,
62 Tuple.of(3, 3), true
63 ), resultSet);
64 }
65
66 @Test
67 void symbolViewUpdateTest() {
68 var query = Query.of("SymbolViewRepresentative", (builder, p1, p2) -> builder
69 .clause(v1 -> List.of(
70 new RepresentativeElectionLiteral(Connectivity.WEAK, friendView, p1, v1),
71 new RepresentativeElectionLiteral(Connectivity.WEAK, friendView, p2, v1)
72 )));
73
74 var store = ModelStore.builder()
75 .symbols(friend)
76 .with(QueryInterpreterAdapter.builder()
77 .queries(query))
78 .build();
79
80 var model = store.createEmptyModel();
81 var friendInterpretation = model.getInterpretation(friend);
82 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
83 var resultSet = queryEngine.getResultSet(query);
84
85 friendInterpretation.put(Tuple.of(0, 1), true);
86 friendInterpretation.put(Tuple.of(1, 0), true);
87 friendInterpretation.put(Tuple.of(2, 3), true);
88 queryEngine.flushChanges();
89
90 friendInterpretation.put(Tuple.of(2, 3), false);
91 friendInterpretation.put(Tuple.of(1, 0), false);
92 friendInterpretation.put(Tuple.of(1, 2), true);
93 queryEngine.flushChanges();
94
95 assertResults(Map.of(
96 Tuple.of(0, 0), true,
97 Tuple.of(0, 1), true,
98 Tuple.of(0, 2), true,
99 Tuple.of(1, 0), true,
100 Tuple.of(1, 1), true,
101 Tuple.of(1, 2), true,
102 Tuple.of(2, 0), true,
103 Tuple.of(2, 1), true,
104 Tuple.of(2, 2), true
105 ), resultSet);
106 }
107
108 @Test
109 void diagonalSymbolViewTest() {
110 var person = Symbol.of("Person", 1);
111 var personView = new KeyOnlyView<>(person);
112
113 var query = Query.of("SymbolViewRepresentative", (builder, p1) -> builder
114 .clause(
115 personView.call(p1),
116 new RepresentativeElectionLiteral(Connectivity.WEAK, friendView, p1, p1)
117 ));
118
119 var store = ModelStore.builder()
120 .symbols(person, friend)
121 .with(QueryInterpreterAdapter.builder()
122 .queries(query))
123 .build();
124
125 var model = store.createEmptyModel();
126 var personInterpretation = model.getInterpretation(person);
127 var friendInterpretation = model.getInterpretation(friend);
128 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
129 var resultSet = queryEngine.getResultSet(query);
130
131 personInterpretation.put(Tuple.of(0), true);
132 personInterpretation.put(Tuple.of(1), true);
133 personInterpretation.put(Tuple.of(2), true);
134 personInterpretation.put(Tuple.of(3), true);
135
136 friendInterpretation.put(Tuple.of(0, 1), true);
137 friendInterpretation.put(Tuple.of(1, 0), true);
138 friendInterpretation.put(Tuple.of(2, 3), true);
139 queryEngine.flushChanges();
140
141 assertThat(resultSet.size(), is(2));
142 assertThat(resultSet.get(Tuple.of(2)), is(true));
143 }
144
145 @Test
146 void diagonalDnfTest() {
147 var person = Symbol.of("Person", 1);
148 var personView = new KeyOnlyView<>(person);
149
150 var subQuery = Query.of("SubQuery", (builder, p1, p2) -> builder
151 .clause(
152 personView.call(p1),
153 personView.call(p2),
154 friendView.call(p1, p2)
155 ))
156 .getDnf();
157 var query = Query.of("SymbolViewRepresentative", (builder, p1) -> builder
158 .clause(
159 personView.call(p1),
160 new RepresentativeElectionLiteral(Connectivity.WEAK, subQuery, p1, p1)
161 ));
162
163 var store = ModelStore.builder()
164 .symbols(person, friend)
165 .with(QueryInterpreterAdapter.builder()
166 .queries(query))
167 .build();
168
169 var model = store.createEmptyModel();
170 var personInterpretation = model.getInterpretation(person);
171 var friendInterpretation = model.getInterpretation(friend);
172 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
173 var resultSet = queryEngine.getResultSet(query);
174
175 personInterpretation.put(Tuple.of(0), true);
176 personInterpretation.put(Tuple.of(1), true);
177 personInterpretation.put(Tuple.of(2), true);
178 personInterpretation.put(Tuple.of(3), true);
179
180 friendInterpretation.put(Tuple.of(0, 1), true);
181 friendInterpretation.put(Tuple.of(1, 0), true);
182 friendInterpretation.put(Tuple.of(2, 3), true);
183 queryEngine.flushChanges();
184
185 assertThat(resultSet.size(), is(2));
186 assertThat(resultSet.get(Tuple.of(2)), is(true));
187 }
188}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/internal/matcher/MatcherUtilsTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/internal/matcher/MatcherUtilsTest.java
new file mode 100644
index 00000000..1c8044ea
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/internal/matcher/MatcherUtilsTest.java
@@ -0,0 +1,239 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter.internal.matcher;
7
8import tools.refinery.interpreter.matchers.tuple.*;
9import org.junit.jupiter.api.Test;
10import tools.refinery.store.tuple.Tuple;
11import tools.refinery.store.tuple.*;
12
13import java.util.List;
14
15import static org.hamcrest.MatcherAssert.assertThat;
16import static org.hamcrest.Matchers.*;
17import static org.junit.jupiter.api.Assertions.assertThrows;
18
19class MatcherUtilsTest {
20 @Test
21 void toViatra0Test() {
22 var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of());
23 assertThat(viatraTuple.getSize(), is(0));
24 assertThat(viatraTuple, instanceOf(FlatTuple0.class));
25 }
26
27 @Test
28 void toViatra1Test() {
29 var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2));
30 assertThat(viatraTuple.getSize(), is(1));
31 assertThat(viatraTuple.get(0), is(Tuple.of(2)));
32 assertThat(viatraTuple, instanceOf(FlatTuple1.class));
33 }
34
35 @Test
36 void toViatra2Test() {
37 var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3));
38 assertThat(viatraTuple.getSize(), is(2));
39 assertThat(viatraTuple.get(0), is(Tuple.of(2)));
40 assertThat(viatraTuple.get(1), is(Tuple.of(3)));
41 assertThat(viatraTuple, instanceOf(FlatTuple2.class));
42 }
43
44 @Test
45 void toViatra3Test() {
46 var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3, 5));
47 assertThat(viatraTuple.getSize(), is(3));
48 assertThat(viatraTuple.get(0), is(Tuple.of(2)));
49 assertThat(viatraTuple.get(1), is(Tuple.of(3)));
50 assertThat(viatraTuple.get(2), is(Tuple.of(5)));
51 assertThat(viatraTuple, instanceOf(FlatTuple3.class));
52 }
53
54 @Test
55 void toViatra4Test() {
56 var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3, 5, 8));
57 assertThat(viatraTuple.getSize(), is(4));
58 assertThat(viatraTuple.get(0), is(Tuple.of(2)));
59 assertThat(viatraTuple.get(1), is(Tuple.of(3)));
60 assertThat(viatraTuple.get(2), is(Tuple.of(5)));
61 assertThat(viatraTuple.get(3), is(Tuple.of(8)));
62 assertThat(viatraTuple, instanceOf(FlatTuple4.class));
63 }
64
65 @Test
66 void toViatra5Test() {
67 var viatraTuple = MatcherUtils.toViatraTuple(Tuple.of(2, 3, 5, 8, 13));
68 assertThat(viatraTuple.getSize(), is(5));
69 assertThat(viatraTuple.get(0), is(Tuple.of(2)));
70 assertThat(viatraTuple.get(1), is(Tuple.of(3)));
71 assertThat(viatraTuple.get(2), is(Tuple.of(5)));
72 assertThat(viatraTuple.get(3), is(Tuple.of(8)));
73 assertThat(viatraTuple.get(4), is(Tuple.of(13)));
74 assertThat(viatraTuple, instanceOf(FlatTuple.class));
75 }
76
77 @Test
78 void toRefinery0Test() {
79 var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf());
80 assertThat(refineryTuple.getSize(), is(0));
81 assertThat(refineryTuple, instanceOf(Tuple0.class));
82 }
83
84 @Test
85 void toRefinery1Test() {
86 var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2)));
87 assertThat(refineryTuple.getSize(), is(1));
88 assertThat(refineryTuple.get(0), is(2));
89 assertThat(refineryTuple, instanceOf(Tuple1.class));
90 }
91
92 @Test
93 void toRefinery2Test() {
94 var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3)));
95 assertThat(refineryTuple.getSize(), is(2));
96 assertThat(refineryTuple.get(0), is(2));
97 assertThat(refineryTuple.get(1), is(3));
98 assertThat(refineryTuple, instanceOf(Tuple2.class));
99 }
100
101 @Test
102 void toRefinery3Test() {
103 var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5)));
104 assertThat(refineryTuple.getSize(), is(3));
105 assertThat(refineryTuple.get(0), is(2));
106 assertThat(refineryTuple.get(1), is(3));
107 assertThat(refineryTuple.get(2), is(5));
108 assertThat(refineryTuple, instanceOf(Tuple3.class));
109 }
110
111 @Test
112 void toRefinery4Test() {
113 var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5),
114 Tuple.of(8)));
115 assertThat(refineryTuple.getSize(), is(4));
116 assertThat(refineryTuple.get(0), is(2));
117 assertThat(refineryTuple.get(1), is(3));
118 assertThat(refineryTuple.get(2), is(5));
119 assertThat(refineryTuple.get(3), is(8));
120 assertThat(refineryTuple, instanceOf(Tuple4.class));
121 }
122
123 @Test
124 void toRefinery5Test() {
125 var refineryTuple = MatcherUtils.toRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5),
126 Tuple.of(8), Tuple.of(13)));
127 assertThat(refineryTuple.getSize(), is(5));
128 assertThat(refineryTuple.get(0), is(2));
129 assertThat(refineryTuple.get(1), is(3));
130 assertThat(refineryTuple.get(2), is(5));
131 assertThat(refineryTuple.get(3), is(8));
132 assertThat(refineryTuple.get(4), is(13));
133 assertThat(refineryTuple, instanceOf(TupleN.class));
134 }
135
136 @Test
137 void toRefineryInvalidValueTest() {
138 var viatraTuple = Tuples.flatTupleOf(Tuple.of(2), -98);
139 assertThrows(IllegalArgumentException.class, () -> MatcherUtils.toRefineryTuple(viatraTuple));
140 }
141
142 @Test
143 void keyToRefinery0Test() {
144 var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(-99));
145 assertThat(refineryTuple.getSize(), is(0));
146 assertThat(refineryTuple, instanceOf(Tuple0.class));
147 }
148
149 @Test
150 void keyToRefinery1Test() {
151 var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), -99));
152 assertThat(refineryTuple.getSize(), is(1));
153 assertThat(refineryTuple.get(0), is(2));
154 assertThat(refineryTuple, instanceOf(Tuple1.class));
155 }
156
157 @Test
158 void keyToRefinery2Test() {
159 var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), -99));
160 assertThat(refineryTuple.getSize(), is(2));
161 assertThat(refineryTuple.get(0), is(2));
162 assertThat(refineryTuple.get(1), is(3));
163 assertThat(refineryTuple, instanceOf(Tuple2.class));
164 }
165
166 @Test
167 void keyToRefinery3Test() {
168 var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5),
169 -99));
170 assertThat(refineryTuple.getSize(), is(3));
171 assertThat(refineryTuple.get(0), is(2));
172 assertThat(refineryTuple.get(1), is(3));
173 assertThat(refineryTuple.get(2), is(5));
174 assertThat(refineryTuple, instanceOf(Tuple3.class));
175 }
176
177 @Test
178 void keyToRefinery4Test() {
179 var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5),
180 Tuple.of(8), -99));
181 assertThat(refineryTuple.getSize(), is(4));
182 assertThat(refineryTuple.get(0), is(2));
183 assertThat(refineryTuple.get(1), is(3));
184 assertThat(refineryTuple.get(2), is(5));
185 assertThat(refineryTuple.get(3), is(8));
186 assertThat(refineryTuple, instanceOf(Tuple4.class));
187 }
188
189 @Test
190 void keyToRefinery5Test() {
191 var refineryTuple = MatcherUtils.keyToRefineryTuple(Tuples.flatTupleOf(Tuple.of(2), Tuple.of(3), Tuple.of(5),
192 Tuple.of(8), Tuple.of(13), -99));
193 assertThat(refineryTuple.getSize(), is(5));
194 assertThat(refineryTuple.get(0), is(2));
195 assertThat(refineryTuple.get(1), is(3));
196 assertThat(refineryTuple.get(2), is(5));
197 assertThat(refineryTuple.get(3), is(8));
198 assertThat(refineryTuple.get(4), is(13));
199 assertThat(refineryTuple, instanceOf(TupleN.class));
200 }
201
202 @Test
203 void keyToRefineryTooShortTest() {
204 var viatraTuple = Tuples.flatTupleOf();
205 assertThrows(IllegalArgumentException.class, () -> MatcherUtils.keyToRefineryTuple(viatraTuple));
206 }
207
208 @Test
209 void keyToRefineryInvalidValueTest() {
210 var viatraTuple = Tuples.flatTupleOf(Tuple.of(2), -98, -99);
211 assertThrows(IllegalArgumentException.class, () -> MatcherUtils.keyToRefineryTuple(viatraTuple));
212 }
213
214 @Test
215 void getSingleValueTest() {
216 var value = MatcherUtils.getSingleValue(List.of(Tuples.flatTupleOf(Tuple.of(2), -99)));
217 assertThat(value, is(-99));
218 }
219
220 // Static analysis accurately determines that the result is always {@code null}, but we check anyways.
221 @SuppressWarnings("ConstantValue")
222 @Test
223 void getSingleValueNullTest() {
224 var value = MatcherUtils.getSingleValue((Iterable<? extends ITuple>) null);
225 assertThat(value, nullValue());
226 }
227
228 @Test
229 void getSingleValueEmptyTest() {
230 var value = MatcherUtils.getSingleValue(List.of());
231 assertThat(value, nullValue());
232 }
233
234 @Test
235 void getSingleValueMultipleTest() {
236 var viatraTuples = List.of(Tuples.flatTupleOf(Tuple.of(2), -98), Tuples.flatTupleOf(Tuple.of(2), -99));
237 assertThrows(IllegalStateException.class, () -> MatcherUtils.getSingleValue(viatraTuples));
238 }
239}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryAssertions.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryAssertions.java
new file mode 100644
index 00000000..c4659a98
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryAssertions.java
@@ -0,0 +1,57 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter.tests;
7
8import org.junit.jupiter.api.function.Executable;
9import tools.refinery.store.query.resultset.ResultSet;
10import tools.refinery.store.tuple.Tuple;
11
12import java.util.*;
13
14import static org.hamcrest.MatcherAssert.assertThat;
15import static org.hamcrest.Matchers.is;
16import static org.hamcrest.Matchers.nullValue;
17import static org.junit.jupiter.api.Assertions.assertAll;
18
19public final class QueryAssertions {
20 private QueryAssertions() {
21 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
22 }
23
24 public static <T> void assertNullableResults(Map<Tuple, Optional<T>> expected, ResultSet<T> resultSet) {
25 var nullableValuesMap = new LinkedHashMap<Tuple, T>(expected.size());
26 for (var entry : expected.entrySet()) {
27 nullableValuesMap.put(entry.getKey(), entry.getValue().orElse(null));
28 }
29 assertResults(nullableValuesMap, resultSet);
30 }
31
32 public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) {
33 var defaultValue = resultSet.getCanonicalQuery().defaultValue();
34 var filteredExpected = new LinkedHashMap<Tuple, T>();
35 var executables = new ArrayList<Executable>();
36 for (var entry : expected.entrySet()) {
37 var key = entry.getKey();
38 var value = entry.getValue();
39 if (!Objects.equals(value, defaultValue)) {
40 filteredExpected.put(key, value);
41 }
42 executables.add(() -> assertThat("value for key " + key,resultSet.get(key), is(value)));
43 }
44 executables.add(() -> assertThat("results size", resultSet.size(), is(filteredExpected.size())));
45
46 var actual = new LinkedHashMap<Tuple, T>();
47 var cursor = resultSet.getAll();
48 while (cursor.move()) {
49 var key = cursor.getKey();
50 var previous = actual.put(key, cursor.getValue());
51 assertThat("duplicate value for key " + key, previous, nullValue());
52 }
53 executables.add(() -> assertThat("results cursor", actual, is(filteredExpected)));
54
55 assertAll(executables);
56 }
57}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryBackendHint.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryBackendHint.java
new file mode 100644
index 00000000..f9d5b219
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryBackendHint.java
@@ -0,0 +1,27 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter.tests;
7
8import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
9
10/**
11 * Overrides {@link QueryEvaluationHint#toString()} for pretty names in parametric test names.
12 */
13class QueryBackendHint extends QueryEvaluationHint {
14 public QueryBackendHint(BackendRequirement backendRequirementType) {
15 super(null, backendRequirementType);
16 }
17
18 @Override
19 public String toString() {
20 return switch (getQueryBackendRequirementType()) {
21 case UNSPECIFIED -> "default";
22 case DEFAULT_CACHING -> "incremental";
23 case DEFAULT_SEARCH -> "localSearch";
24 default -> throw new IllegalStateException("Unknown BackendRequirement");
25 };
26 }
27}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEngineTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEngineTest.java
new file mode 100644
index 00000000..a5cc7e9c
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEngineTest.java
@@ -0,0 +1,21 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter.tests;
7
8import org.junit.jupiter.params.ParameterizedTest;
9import org.junit.jupiter.params.provider.ArgumentsSource;
10
11import java.lang.annotation.ElementType;
12import java.lang.annotation.Retention;
13import java.lang.annotation.RetentionPolicy;
14import java.lang.annotation.Target;
15
16@ParameterizedTest(name = "backend = {0}")
17@ArgumentsSource(QueryEvaluationHintSource.class)
18@Target(ElementType.METHOD)
19@Retention(RetentionPolicy.RUNTIME)
20public @interface QueryEngineTest {
21}
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEvaluationHintSource.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEvaluationHintSource.java
new file mode 100644
index 00000000..6503ff2f
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/tests/QueryEvaluationHintSource.java
@@ -0,0 +1,24 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter.tests;
7
8import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
9import org.junit.jupiter.api.extension.ExtensionContext;
10import org.junit.jupiter.params.provider.Arguments;
11import org.junit.jupiter.params.provider.ArgumentsProvider;
12
13import java.util.stream.Stream;
14
15public class QueryEvaluationHintSource implements ArgumentsProvider {
16 @Override
17 public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
18 return Stream.of(
19 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.UNSPECIFIED)),
20 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.DEFAULT_CACHING)),
21 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH))
22 );
23 }
24}