aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/test/java/tools
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-query-viatra/src/test/java/tools')
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java390
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java483
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/OrderedResultSetTest.java117
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java832
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java357
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java51
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorTest.java87
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java239
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java57
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java27
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java21
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java24
12 files changed, 2120 insertions, 565 deletions
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
new file mode 100644
index 00000000..6aae2ebe
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
@@ -0,0 +1,390 @@
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.viatra;
7
8import org.eclipse.viatra.query.runtime.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.viatra.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.viatra.tests.QueryAssertions.assertNullableResults;
27import static tools.refinery.store.query.viatra.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.builder()
96 .defaultHint(hint)
97 .queries(query))
98 .build();
99
100 var model = store.createEmptyModel();
101 var personInterpretation = model.getInterpretation(person);
102 var symbolInterpretation = model.getInterpretation(symbol);
103 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
104 var queryResultSet = queryEngine.getResultSet(query);
105
106 personInterpretation.put(Tuple.of(0), true);
107 personInterpretation.put(Tuple.of(1), true);
108 personInterpretation.put(Tuple.of(2), true);
109
110 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
111 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
112 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
113 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
114
115 queryEngine.flushChanges();
116 assertResults(Map.of(
117 Tuple.of(0), false,
118 Tuple.of(1), true,
119 Tuple.of(2), true,
120 Tuple.of(3), false
121 ), queryResultSet);
122 }
123
124 @QueryEngineTest
125 void inputKeyCountTest(QueryEvaluationHint hint) {
126 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder.clause(p2 -> List.of(
127 personView.call(p1),
128 output.assign(symbolView.count(p1, p1, p2, p2))
129 )));
130
131 var store = ModelStore.builder()
132 .symbols(person, symbol)
133 .with(ViatraModelQueryAdapter.builder()
134 .defaultHint(hint)
135 .queries(query))
136 .build();
137
138 var model = store.createEmptyModel();
139 var personInterpretation = model.getInterpretation(person);
140 var symbolInterpretation = model.getInterpretation(symbol);
141 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
142 var queryResultSet = queryEngine.getResultSet(query);
143
144 personInterpretation.put(Tuple.of(0), true);
145 personInterpretation.put(Tuple.of(1), true);
146 personInterpretation.put(Tuple.of(2), true);
147
148 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
149 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
150 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
151 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
152 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
153
154 queryEngine.flushChanges();
155 assertNullableResults(Map.of(
156 Tuple.of(0), Optional.of(2),
157 Tuple.of(1), Optional.of(0),
158 Tuple.of(2), Optional.of(0),
159 Tuple.of(3), Optional.empty()
160 ), queryResultSet);
161 }
162
163 @QueryEngineTest
164 void subQueryCountTest(QueryEvaluationHint hint) {
165 var subQuery = Query.of("SubQuery", (builder, p1, p2, p3, p4) -> builder.clause(
166 personView.call(p1),
167 symbolView.call(p1, p2, p3, p4)
168 )
169 .clause(
170 personView.call(p2),
171 symbolView.call(p1, p2, p3, p4)
172 ));
173 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder.clause(p2 -> List.of(
174 personView.call(p1),
175 output.assign(subQuery.count(p1, p1, p2, p2))
176 )));
177
178 var store = ModelStore.builder()
179 .symbols(person, symbol)
180 .with(ViatraModelQueryAdapter.builder()
181 .defaultHint(hint)
182 .queries(query))
183 .build();
184
185 var model = store.createEmptyModel();
186 var personInterpretation = model.getInterpretation(person);
187 var symbolInterpretation = model.getInterpretation(symbol);
188 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
189 var queryResultSet = queryEngine.getResultSet(query);
190
191 personInterpretation.put(Tuple.of(0), true);
192 personInterpretation.put(Tuple.of(1), true);
193 personInterpretation.put(Tuple.of(2), true);
194
195 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
196 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
197 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
198 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
199 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
200
201 queryEngine.flushChanges();
202 assertNullableResults(Map.of(
203 Tuple.of(0), Optional.of(2),
204 Tuple.of(1), Optional.of(0),
205 Tuple.of(2), Optional.of(0),
206 Tuple.of(3), Optional.empty()
207 ), queryResultSet);
208 }
209
210 @QueryEngineTest
211 void inputKeyAggregationTest(QueryEvaluationHint hint) {
212 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder
213 .clause((p2) -> List.of(
214 personView.call(p1),
215 output.assign(intSymbolView.aggregate(INT_SUM, p1, p1, p2, p2))
216 )));
217
218 var store = ModelStore.builder()
219 .symbols(person, intSymbol)
220 .with(ViatraModelQueryAdapter.builder()
221 .defaultHint(hint)
222 .queries(query))
223 .build();
224
225 var model = store.createEmptyModel();
226 var personInterpretation = model.getInterpretation(person);
227 var intSymbolInterpretation = model.getInterpretation(intSymbol);
228 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
229 var queryResultSet = queryEngine.getResultSet(query);
230
231 personInterpretation.put(Tuple.of(0), true);
232 personInterpretation.put(Tuple.of(1), true);
233 personInterpretation.put(Tuple.of(2), true);
234
235 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
236 intSymbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
237 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
238 intSymbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
239 intSymbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
240
241 queryEngine.flushChanges();
242 assertNullableResults(Map.of(
243 Tuple.of(0), Optional.of(3),
244 Tuple.of(1), Optional.of(0),
245 Tuple.of(2), Optional.of(0),
246 Tuple.of(3), Optional.empty()
247 ), queryResultSet);
248 }
249
250 @QueryEngineTest
251 void subQueryAggregationTest(QueryEvaluationHint hint) {
252 var subQuery = Dnf.of("SubQuery", builder -> {
253 var p1 = builder.parameter("p1");
254 var p2 = builder.parameter("p2");
255 var p3 = builder.parameter("p3");
256 var p4 = builder.parameter("p4");
257 var x = builder.parameter("x", Integer.class);
258 var y = builder.parameter("y", Integer.class);
259 builder.clause(
260 personView.call(p1),
261 intSymbolView.call(p1, p2, p3, p4, x),
262 y.assign(x)
263 );
264 builder.clause(
265 personView.call(p2),
266 intSymbolView.call(p1, p2, p3, p4, x),
267 y.assign(x)
268 );
269 });
270 var query = Query.of("Diagonal", Integer.class, (builder, p1, output) -> builder
271 .clause(Integer.class, Integer.class, (p2, y, z) -> List.of(
272 personView.call(p1),
273 output.assign(subQuery.aggregateBy(y, INT_SUM, p1, p1, p2, p2, y, z))
274 )));
275
276 var store = ModelStore.builder()
277 .symbols(person, intSymbol)
278 .with(ViatraModelQueryAdapter.builder()
279 .defaultHint(hint)
280 .queries(query))
281 .build();
282
283 var model = store.createEmptyModel();
284 var personInterpretation = model.getInterpretation(person);
285 var intSymbolInterpretation = model.getInterpretation(intSymbol);
286 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
287 var queryResultSet = queryEngine.getResultSet(query);
288
289 personInterpretation.put(Tuple.of(0), true);
290 personInterpretation.put(Tuple.of(1), true);
291 personInterpretation.put(Tuple.of(2), true);
292
293 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
294 intSymbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
295 intSymbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
296 intSymbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
297 intSymbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
298
299 queryEngine.flushChanges();
300 assertNullableResults(Map.of(
301 Tuple.of(0), Optional.of(3),
302 Tuple.of(1), Optional.of(0),
303 Tuple.of(2), Optional.of(0),
304 Tuple.of(3), Optional.empty()
305 ), queryResultSet);
306 }
307
308 @QueryEngineTest
309 void inputKeyTransitiveTest(QueryEvaluationHint hint) {
310 var query = Query.of("Diagonal", (builder, p1) -> builder.clause(
311 personView.call(p1),
312 friendView.callTransitive(p1, p1)
313 ));
314
315 var store = ModelStore.builder()
316 .symbols(person, friend)
317 .with(ViatraModelQueryAdapter.builder()
318 .defaultHint(hint)
319 .queries(query))
320 .build();
321
322 var model = store.createEmptyModel();
323 var personInterpretation = model.getInterpretation(person);
324 var friendInterpretation = model.getInterpretation(friend);
325 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
326 var queryResultSet = queryEngine.getResultSet(query);
327
328 personInterpretation.put(Tuple.of(0), true);
329 personInterpretation.put(Tuple.of(1), true);
330 personInterpretation.put(Tuple.of(2), true);
331
332 friendInterpretation.put(Tuple.of(0, 0), true);
333 friendInterpretation.put(Tuple.of(0, 1), true);
334 friendInterpretation.put(Tuple.of(1, 2), true);
335
336 queryEngine.flushChanges();
337 assertResults(Map.of(
338 Tuple.of(0), true,
339 Tuple.of(1), false,
340 Tuple.of(2), false,
341 Tuple.of(3), false
342 ), queryResultSet);
343 }
344
345 @QueryEngineTest
346 void subQueryTransitiveTest(QueryEvaluationHint hint) {
347 var subQuery = Query.of("SubQuery", (builder, p1, p2) -> builder
348 .clause(
349 personView.call(p1),
350 friendView.call(p1, p2)
351 )
352 .clause(
353 personView.call(p2),
354 friendView.call(p1, p2)
355 ));
356 var query = Query.of("Diagonal", (builder, p1) -> builder.clause(
357 personView.call(p1),
358 subQuery.callTransitive(p1, p1)
359 ));
360
361 var store = ModelStore.builder()
362 .symbols(person, friend)
363 .with(ViatraModelQueryAdapter.builder()
364 .defaultHint(hint)
365 .queries(query))
366 .build();
367
368 var model = store.createEmptyModel();
369 var personInterpretation = model.getInterpretation(person);
370 var friendInterpretation = model.getInterpretation(friend);
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 personInterpretation.put(Tuple.of(2), true);
377
378 friendInterpretation.put(Tuple.of(0, 0), true);
379 friendInterpretation.put(Tuple.of(0, 1), true);
380 friendInterpretation.put(Tuple.of(1, 2), true);
381
382 queryEngine.flushChanges();
383 assertResults(Map.of(
384 Tuple.of(0), true,
385 Tuple.of(1), false,
386 Tuple.of(2), false,
387 Tuple.of(3), false
388 ), queryResultSet);
389 }
390}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
new file mode 100644
index 00000000..258127e7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
@@ -0,0 +1,483 @@
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.viatra;
7
8import org.eclipse.viatra.query.runtime.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.viatra.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.assume;
33import static tools.refinery.store.query.term.int_.IntTerms.*;
34import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
35import static tools.refinery.store.query.viatra.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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 assume(lessEq(div(constant(120), x), constant(5)))
394 )));
395
396 var store = ModelStore.builder()
397 .symbols(person, age)
398 .with(ViatraModelQueryAdapter.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 notFunctionalTest(QueryEvaluationHint hint) {
428 var query = Query.of("NotFunctional", Integer.class, (builder, p1, output) -> builder.clause((p2) -> List.of(
429 personView.call(p1),
430 friendMustView.call(p1, p2),
431 ageView.call(p2, output)
432 )));
433
434 var store = ModelStore.builder()
435 .symbols(person, age, friend)
436 .with(ViatraModelQueryAdapter.builder()
437 .defaultHint(hint)
438 .queries(query))
439 .build();
440
441 var model = store.createEmptyModel();
442 var personInterpretation = model.getInterpretation(person);
443 var ageInterpretation = model.getInterpretation(age);
444 var friendInterpretation = model.getInterpretation(friend);
445 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
446 var queryResultSet = queryEngine.getResultSet(query);
447
448 personInterpretation.put(Tuple.of(0), true);
449 personInterpretation.put(Tuple.of(1), true);
450 personInterpretation.put(Tuple.of(2), true);
451
452 ageInterpretation.put(Tuple.of(0), 24);
453 ageInterpretation.put(Tuple.of(1), 30);
454 ageInterpretation.put(Tuple.of(2), 36);
455
456 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
457 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
458 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
459
460 queryEngine.flushChanges();
461 var invalidTuple = Tuple.of(1);
462 var cursor = queryResultSet.getAll();
463 assertAll(
464 () -> assertThat("value for key 0", queryResultSet.get(Tuple.of(0)), is(30)),
465 () -> assertThrows(IllegalStateException.class, () -> queryResultSet.get(invalidTuple),
466 "multiple values for key 1"),
467 () -> assertThat("value for key 2", queryResultSet.get(Tuple.of(2)), is(nullValue())),
468 () -> assertThat("value for key 3", queryResultSet.get(Tuple.of(3)), is(nullValue()))
469 );
470 if (hint.getQueryBackendRequirementType() != QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH) {
471 // Local search doesn't support throwing an error on multiple function return values.
472 assertThat("results size", queryResultSet.size(), is(2));
473 assertThrows(IllegalStateException.class, () -> enumerateValues(cursor), "move cursor");
474 }
475 }
476
477 private static void enumerateValues(Cursor<?, ?> cursor) {
478 //noinspection StatementWithEmptyBody
479 while (cursor.move()) {
480 // Nothing do, just let the cursor move through the result set.
481 }
482 }
483}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/OrderedResultSetTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/OrderedResultSetTest.java
new file mode 100644
index 00000000..8ede6c80
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/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.viatra;
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(ViatraModelQueryAdapter.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(ViatraModelQueryAdapter.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-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
index 6a37b54a..25bcb0dc 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
@@ -1,47 +1,57 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
2 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
3import org.junit.jupiter.api.Test; 9import org.junit.jupiter.api.Test;
4import tools.refinery.store.model.ModelStore; 10import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.DNF; 11import tools.refinery.store.query.ModelQueryAdapter;
6import tools.refinery.store.query.ModelQuery; 12import tools.refinery.store.query.dnf.Query;
7import tools.refinery.store.query.Variable; 13import tools.refinery.store.query.term.Variable;
8import tools.refinery.store.query.atom.*; 14import tools.refinery.store.query.viatra.tests.QueryEngineTest;
9import tools.refinery.store.query.view.FilteredRelationView; 15import tools.refinery.store.query.view.AnySymbolView;
10import tools.refinery.store.query.view.KeyOnlyRelationView; 16import tools.refinery.store.query.view.FilteredView;
17import tools.refinery.store.query.view.FunctionView;
18import tools.refinery.store.query.view.KeyOnlyView;
11import tools.refinery.store.representation.Symbol; 19import tools.refinery.store.representation.Symbol;
12import tools.refinery.store.representation.TruthValue; 20import tools.refinery.store.representation.TruthValue;
13import tools.refinery.store.tuple.Tuple; 21import tools.refinery.store.tuple.Tuple;
14import tools.refinery.store.tuple.TupleLike;
15 22
16import java.util.HashSet; 23import java.util.List;
17import java.util.Set; 24import java.util.Map;
18import java.util.stream.Stream;
19 25
20import static org.junit.jupiter.api.Assertions.assertEquals; 26import static tools.refinery.store.query.literal.Literals.assume;
27import static tools.refinery.store.query.literal.Literals.not;
28import static tools.refinery.store.query.term.int_.IntTerms.constant;
29import static tools.refinery.store.query.term.int_.IntTerms.greaterEq;
30import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
21 31
22class QueryTest { 32class QueryTest {
23 @Test 33 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
24 void typeConstraintTest() { 34 private static final Symbol<TruthValue> friend = Symbol.of("friend", 2, TruthValue.class, TruthValue.FALSE);
25 var person = new Symbol<>("Person", 1, Boolean.class, false); 35 private static final AnySymbolView personView = new KeyOnlyView<>(person);
26 var asset = new Symbol<>("Asset", 1, Boolean.class, false); 36 private static final AnySymbolView friendMustView = new FilteredView<>(friend, "must", TruthValue::must);
27 var personView = new KeyOnlyRelationView<>(person); 37
28 38 @QueryEngineTest
29 var p1 = new Variable("p1"); 39 void typeConstraintTest(QueryEvaluationHint hint) {
30 var predicate = DNF.builder("TypeConstraint") 40 var asset = Symbol.of("Asset", 1);
31 .parameters(p1) 41
32 .clause(new RelationViewAtom(personView, p1)) 42 var predicate = Query.of("TypeConstraint", (builder, p1) -> builder.clause(personView.call(p1)));
33 .build();
34 43
35 var store = ModelStore.builder() 44 var store = ModelStore.builder()
36 .symbols(person, asset) 45 .symbols(person, asset)
37 .with(ViatraModelQuery.ADAPTER) 46 .with(ViatraModelQueryAdapter.builder()
38 .queries(predicate) 47 .defaultHint(hint)
48 .queries(predicate))
39 .build(); 49 .build();
40 50
41 var model = store.createEmptyModel(); 51 var model = store.createEmptyModel();
42 var personInterpretation = model.getInterpretation(person); 52 var personInterpretation = model.getInterpretation(person);
43 var assetInterpretation = model.getInterpretation(asset); 53 var assetInterpretation = model.getInterpretation(asset);
44 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 54 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
45 var predicateResultSet = queryEngine.getResultSet(predicate); 55 var predicateResultSet = queryEngine.getResultSet(predicate);
46 56
47 personInterpretation.put(Tuple.of(0), true); 57 personInterpretation.put(Tuple.of(0), true);
@@ -51,42 +61,34 @@ class QueryTest {
51 assetInterpretation.put(Tuple.of(2), true); 61 assetInterpretation.put(Tuple.of(2), true);
52 62
53 queryEngine.flushChanges(); 63 queryEngine.flushChanges();
54 assertEquals(2, predicateResultSet.countResults()); 64 assertResults(Map.of(
55 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0), Tuple.of(1))); 65 Tuple.of(0), true,
66 Tuple.of(1), true,
67 Tuple.of(2), false
68 ), predicateResultSet);
56 } 69 }
57 70
58 @Test 71 @QueryEngineTest
59 void relationConstraintTest() { 72 void relationConstraintTest(QueryEvaluationHint hint) {
60 var person = new Symbol<>("Person", 1, Boolean.class, false); 73 var predicate = Query.of("RelationConstraint", (builder, p1, p2) -> builder.clause(
61 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 74 personView.call(p1),
62 var personView = new KeyOnlyRelationView<>(person); 75 personView.call(p2),
63 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 76 friendMustView.call(p1, p2)
64 77 ));
65 var p1 = new Variable("p1");
66 var p2 = new Variable("p2");
67 var predicate = DNF.builder("RelationConstraint")
68 .parameters(p1, p2)
69 .clause(
70 new RelationViewAtom(personView, p1),
71 new RelationViewAtom(personView, p2),
72 new RelationViewAtom(friendMustView, p1, p2)
73 )
74 .build();
75 78
76 var store = ModelStore.builder() 79 var store = ModelStore.builder()
77 .symbols(person, friend) 80 .symbols(person, friend)
78 .with(ViatraModelQuery.ADAPTER) 81 .with(ViatraModelQueryAdapter.builder()
79 .queries(predicate) 82 .defaultHint(hint)
83 .queries(predicate))
80 .build(); 84 .build();
81 85
82 var model = store.createEmptyModel(); 86 var model = store.createEmptyModel();
83 var personInterpretation = model.getInterpretation(person); 87 var personInterpretation = model.getInterpretation(person);
84 var friendInterpretation = model.getInterpretation(friend); 88 var friendInterpretation = model.getInterpretation(friend);
85 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 89 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
86 var predicateResultSet = queryEngine.getResultSet(predicate); 90 var predicateResultSet = queryEngine.getResultSet(predicate);
87 91
88 assertEquals(0, predicateResultSet.countResults());
89
90 personInterpretation.put(Tuple.of(0), true); 92 personInterpretation.put(Tuple.of(0), true);
91 personInterpretation.put(Tuple.of(1), true); 93 personInterpretation.put(Tuple.of(1), true);
92 personInterpretation.put(Tuple.of(2), true); 94 personInterpretation.put(Tuple.of(2), true);
@@ -94,97 +96,36 @@ class QueryTest {
94 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE); 96 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
95 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE); 97 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
96 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 98 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
97 99 friendInterpretation.put(Tuple.of(1, 3), TruthValue.TRUE);
98 assertEquals(0, predicateResultSet.countResults());
99 100
100 queryEngine.flushChanges(); 101 queryEngine.flushChanges();
101 assertEquals(3, predicateResultSet.countResults()); 102 assertResults(Map.of(
102 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(1, 2))); 103 Tuple.of(0, 1), true,
104 Tuple.of(1, 0), true,
105 Tuple.of(1, 2), true,
106 Tuple.of(2, 1), false
107 ), predicateResultSet);
103 } 108 }
104 109
105 @Test 110 @QueryEngineTest
106 void andTest() { 111 void existTest(QueryEvaluationHint hint) {
107 var person = new Symbol<>("Person", 1, Boolean.class, false); 112 var predicate = Query.of("Exists", (builder, p1) -> builder.clause((p2) -> List.of(
108 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 113 personView.call(p1),
109 var personView = new KeyOnlyRelationView<>(person); 114 personView.call(p2),
110 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 115 friendMustView.call(p1, p2)
111 116 )));
112 var p1 = new Variable("p1");
113 var p2 = new Variable("p2");
114 var predicate = DNF.builder("RelationConstraint")
115 .parameters(p1, p2)
116 .clause(
117 new RelationViewAtom(personView, p1),
118 new RelationViewAtom(personView, p2),
119 new RelationViewAtom(friendMustView, p1, p2),
120 new RelationViewAtom(friendMustView, p2, p1)
121 )
122 .build();
123 117
124 var store = ModelStore.builder() 118 var store = ModelStore.builder()
125 .symbols(person, friend) 119 .symbols(person, friend)
126 .with(ViatraModelQuery.ADAPTER) 120 .with(ViatraModelQueryAdapter.builder()
127 .queries(predicate) 121 .defaultHint(hint)
122 .queries(predicate))
128 .build(); 123 .build();
129 124
130 var model = store.createEmptyModel(); 125 var model = store.createEmptyModel();
131 var personInterpretation = model.getInterpretation(person); 126 var personInterpretation = model.getInterpretation(person);
132 var friendInterpretation = model.getInterpretation(friend); 127 var friendInterpretation = model.getInterpretation(friend);
133 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 128 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
134 var predicateResultSet = queryEngine.getResultSet(predicate);
135
136 assertEquals(0, predicateResultSet.countResults());
137
138 personInterpretation.put(Tuple.of(0), true);
139 personInterpretation.put(Tuple.of(1), true);
140 personInterpretation.put(Tuple.of(2), true);
141
142 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
143 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
144
145 queryEngine.flushChanges();
146 assertEquals(0, predicateResultSet.countResults());
147
148 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
149 queryEngine.flushChanges();
150 assertEquals(2, predicateResultSet.countResults());
151 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0)));
152
153 friendInterpretation.put(Tuple.of(2, 0), TruthValue.TRUE);
154 queryEngine.flushChanges();
155 assertEquals(4, predicateResultSet.countResults());
156 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(0, 2),
157 Tuple.of(2, 0)));
158 }
159
160 @Test
161 void existTest() {
162 var person = new Symbol<>("Person", 1, Boolean.class, false);
163 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
164 var personView = new KeyOnlyRelationView<>(person);
165 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
166
167 var p1 = new Variable("p1");
168 var p2 = new Variable("p2");
169 var predicate = DNF.builder("RelationConstraint")
170 .parameters(p1)
171 .clause(
172 new RelationViewAtom(personView, p1),
173 new RelationViewAtom(personView, p2),
174 new RelationViewAtom(friendMustView, p1, p2)
175 )
176 .build();
177
178 var store = ModelStore.builder()
179 .symbols(person, friend)
180 .with(ViatraModelQuery.ADAPTER)
181 .queries(predicate)
182 .build();
183
184 var model = store.createEmptyModel();
185 var personInterpretation = model.getInterpretation(person);
186 var friendInterpretation = model.getInterpretation(friend);
187 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
188 var predicateResultSet = queryEngine.getResultSet(predicate); 129 var predicateResultSet = queryEngine.getResultSet(predicate);
189 130
190 personInterpretation.put(Tuple.of(0), true); 131 personInterpretation.put(Tuple.of(0), true);
@@ -194,50 +135,44 @@ class QueryTest {
194 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE); 135 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
195 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE); 136 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
196 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 137 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
197 138 friendInterpretation.put(Tuple.of(3, 2), TruthValue.TRUE);
198 assertEquals(0, predicateResultSet.countResults());
199 139
200 queryEngine.flushChanges(); 140 queryEngine.flushChanges();
201 assertEquals(2, predicateResultSet.countResults()); 141 assertResults(Map.of(
202 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0), Tuple.of(1))); 142 Tuple.of(0), true,
143 Tuple.of(1), true,
144 Tuple.of(2), false,
145 Tuple.of(3), false
146 ), predicateResultSet);
203 } 147 }
204 148
205 @Test 149 @QueryEngineTest
206 void orTest() { 150 void orTest(QueryEvaluationHint hint) {
207 var person = new Symbol<>("Person", 1, Boolean.class, false); 151 var animal = Symbol.of("Animal", 1);
208 var animal = new Symbol<>("Animal", 1, Boolean.class, false); 152 var animalView = new KeyOnlyView<>(animal);
209 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 153
210 var personView = new KeyOnlyRelationView<>(person); 154 var predicate = Query.of("Or", (builder, p1, p2) -> builder.clause(
211 var animalView = new KeyOnlyRelationView<>(animal); 155 personView.call(p1),
212 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 156 personView.call(p2),
213 157 friendMustView.call(p1, p2)
214 var p1 = new Variable("p1"); 158 ).clause(
215 var p2 = new Variable("p2"); 159 animalView.call(p1),
216 var predicate = DNF.builder("Or") 160 animalView.call(p2),
217 .parameters(p1, p2) 161 friendMustView.call(p1, p2)
218 .clause( 162 ));
219 new RelationViewAtom(personView, p1),
220 new RelationViewAtom(personView, p2),
221 new RelationViewAtom(friendMustView, p1, p2)
222 )
223 .clause(
224 new RelationViewAtom(animalView, p1),
225 new RelationViewAtom(animalView, p2),
226 new RelationViewAtom(friendMustView, p1, p2)
227 )
228 .build();
229 163
230 var store = ModelStore.builder() 164 var store = ModelStore.builder()
231 .symbols(person, animal, friend) 165 .symbols(person, animal, friend)
232 .with(ViatraModelQuery.ADAPTER) 166 .with(ViatraModelQueryAdapter.builder()
233 .queries(predicate) 167 .defaultHint(hint)
168 .queries(predicate))
234 .build(); 169 .build();
235 170
236 var model = store.createEmptyModel(); 171 var model = store.createEmptyModel();
237 var personInterpretation = model.getInterpretation(person); 172 var personInterpretation = model.getInterpretation(person);
238 var animalInterpretation = model.getInterpretation(animal); 173 var animalInterpretation = model.getInterpretation(animal);
239 var friendInterpretation = model.getInterpretation(friend); 174 var friendInterpretation = model.getInterpretation(friend);
240 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 175 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
241 var predicateResultSet = queryEngine.getResultSet(predicate); 176 var predicateResultSet = queryEngine.getResultSet(predicate);
242 177
243 personInterpretation.put(Tuple.of(0), true); 178 personInterpretation.put(Tuple.of(0), true);
@@ -252,35 +187,33 @@ class QueryTest {
252 friendInterpretation.put(Tuple.of(3, 0), TruthValue.TRUE); 187 friendInterpretation.put(Tuple.of(3, 0), TruthValue.TRUE);
253 188
254 queryEngine.flushChanges(); 189 queryEngine.flushChanges();
255 assertEquals(2, predicateResultSet.countResults()); 190 assertResults(Map.of(
256 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(2, 3))); 191 Tuple.of(0, 1), true,
192 Tuple.of(0, 2), false,
193 Tuple.of(2, 3), true,
194 Tuple.of(3, 0), false,
195 Tuple.of(3, 2), false
196 ), predicateResultSet);
257 } 197 }
258 198
259 @Test 199 @QueryEngineTest
260 void equalityTest() { 200 void equalityTest(QueryEvaluationHint hint) {
261 var person = new Symbol<>("Person", 1, Boolean.class, false); 201 var predicate = Query.of("Equality", (builder, p1, p2) -> builder.clause(
262 var personView = new KeyOnlyRelationView<>(person); 202 personView.call(p1),
263 203 personView.call(p2),
264 var p1 = new Variable("p1"); 204 p1.isEquivalent(p2)
265 var p2 = new Variable("p2"); 205 ));
266 var predicate = DNF.builder("Equality")
267 .parameters(p1, p2)
268 .clause(
269 new RelationViewAtom(personView, p1),
270 new RelationViewAtom(personView, p2),
271 new EquivalenceAtom(p1, p2)
272 )
273 .build();
274 206
275 var store = ModelStore.builder() 207 var store = ModelStore.builder()
276 .symbols(person) 208 .symbols(person)
277 .with(ViatraModelQuery.ADAPTER) 209 .with(ViatraModelQueryAdapter.builder()
278 .queries(predicate) 210 .defaultHint(hint)
211 .queries(predicate))
279 .build(); 212 .build();
280 213
281 var model = store.createEmptyModel(); 214 var model = store.createEmptyModel();
282 var personInterpretation = model.getInterpretation(person); 215 var personInterpretation = model.getInterpretation(person);
283 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 216 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
284 var predicateResultSet = queryEngine.getResultSet(predicate); 217 var predicateResultSet = queryEngine.getResultSet(predicate);
285 218
286 personInterpretation.put(Tuple.of(0), true); 219 personInterpretation.put(Tuple.of(0), true);
@@ -288,41 +221,36 @@ class QueryTest {
288 personInterpretation.put(Tuple.of(2), true); 221 personInterpretation.put(Tuple.of(2), true);
289 222
290 queryEngine.flushChanges(); 223 queryEngine.flushChanges();
291 assertEquals(3, predicateResultSet.countResults()); 224 assertResults(Map.of(
292 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 0), Tuple.of(1, 1), Tuple.of(2, 2))); 225 Tuple.of(0, 0), true,
226 Tuple.of(1, 1), true,
227 Tuple.of(2, 2), true,
228 Tuple.of(0, 1), false,
229 Tuple.of(3, 3), false
230 ), predicateResultSet);
293 } 231 }
294 232
295 @Test 233 @QueryEngineTest
296 void inequalityTest() { 234 void inequalityTest(QueryEvaluationHint hint) {
297 var person = new Symbol<>("Person", 1, Boolean.class, false); 235 var predicate = Query.of("Inequality", (builder, p1, p2, p3) -> builder.clause(
298 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 236 personView.call(p1),
299 var personView = new KeyOnlyRelationView<>(person); 237 personView.call(p2),
300 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 238 friendMustView.call(p1, p3),
301 239 friendMustView.call(p2, p3),
302 var p1 = new Variable("p1"); 240 p1.notEquivalent(p2)
303 var p2 = new Variable("p2"); 241 ));
304 var p3 = new Variable("p3");
305 var predicate = DNF.builder("Inequality")
306 .parameters(p1, p2, p3)
307 .clause(
308 new RelationViewAtom(personView, p1),
309 new RelationViewAtom(personView, p2),
310 new RelationViewAtom(friendMustView, p1, p3),
311 new RelationViewAtom(friendMustView, p2, p3),
312 new EquivalenceAtom(false, p1, p2)
313 )
314 .build();
315 242
316 var store = ModelStore.builder() 243 var store = ModelStore.builder()
317 .symbols(person, friend) 244 .symbols(person, friend)
318 .with(ViatraModelQuery.ADAPTER) 245 .with(ViatraModelQueryAdapter.builder()
319 .queries(predicate) 246 .defaultHint(hint)
247 .queries(predicate))
320 .build(); 248 .build();
321 249
322 var model = store.createEmptyModel(); 250 var model = store.createEmptyModel();
323 var personInterpretation = model.getInterpretation(person); 251 var personInterpretation = model.getInterpretation(person);
324 var friendInterpretation = model.getInterpretation(friend); 252 var friendInterpretation = model.getInterpretation(friend);
325 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 253 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
326 var predicateResultSet = queryEngine.getResultSet(predicate); 254 var predicateResultSet = queryEngine.getResultSet(predicate);
327 255
328 personInterpretation.put(Tuple.of(0), true); 256 personInterpretation.put(Tuple.of(0), true);
@@ -333,49 +261,37 @@ class QueryTest {
333 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 261 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
334 262
335 queryEngine.flushChanges(); 263 queryEngine.flushChanges();
336 assertEquals(2, predicateResultSet.countResults()); 264 assertResults(Map.of(
337 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1, 2), Tuple.of(1, 0, 2))); 265 Tuple.of(0, 1, 2), true,
266 Tuple.of(1, 0, 2), true,
267 Tuple.of(0, 0, 2), false
268 ), predicateResultSet);
338 } 269 }
339 270
340 @Test 271 @QueryEngineTest
341 void patternCallTest() { 272 void patternCallTest(QueryEvaluationHint hint) {
342 var person = new Symbol<>("Person", 1, Boolean.class, false); 273 var friendPredicate = Query.of("Friend", (builder, p1, p2) -> builder.clause(
343 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 274 personView.call(p1),
344 var personView = new KeyOnlyRelationView<>(person); 275 personView.call(p2),
345 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 276 friendMustView.call(p1, p2)
346 277 ));
347 var p1 = new Variable("p1"); 278 var predicate = Query.of("PositivePatternCall", (builder, p3, p4) -> builder.clause(
348 var p2 = new Variable("p2"); 279 personView.call(p3),
349 var friendPredicate = DNF.builder("RelationConstraint") 280 personView.call(p4),
350 .parameters(p1, p2) 281 friendPredicate.call(p3, p4)
351 .clause( 282 ));
352 new RelationViewAtom(personView, p1),
353 new RelationViewAtom(personView, p2),
354 new RelationViewAtom(friendMustView, p1, p2)
355 )
356 .build();
357
358 var p3 = new Variable("p3");
359 var p4 = new Variable("p4");
360 var predicate = DNF.builder("PositivePatternCall")
361 .parameters(p3, p4)
362 .clause(
363 new RelationViewAtom(personView, p3),
364 new RelationViewAtom(personView, p4),
365 new DNFCallAtom(friendPredicate, p3, p4)
366 )
367 .build();
368 283
369 var store = ModelStore.builder() 284 var store = ModelStore.builder()
370 .symbols(person, friend) 285 .symbols(person, friend)
371 .with(ViatraModelQuery.ADAPTER) 286 .with(ViatraModelQueryAdapter.builder()
372 .queries(predicate) 287 .defaultHint(hint)
288 .queries(predicate))
373 .build(); 289 .build();
374 290
375 var model = store.createEmptyModel(); 291 var model = store.createEmptyModel();
376 var personInterpretation = model.getInterpretation(person); 292 var personInterpretation = model.getInterpretation(person);
377 var friendInterpretation = model.getInterpretation(friend); 293 var friendInterpretation = model.getInterpretation(friend);
378 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 294 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
379 var predicateResultSet = queryEngine.getResultSet(predicate); 295 var predicateResultSet = queryEngine.getResultSet(predicate);
380 296
381 personInterpretation.put(Tuple.of(0), true); 297 personInterpretation.put(Tuple.of(0), true);
@@ -387,37 +303,33 @@ class QueryTest {
387 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 303 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
388 304
389 queryEngine.flushChanges(); 305 queryEngine.flushChanges();
390 assertEquals(3, predicateResultSet.countResults()); 306 assertResults(Map.of(
307 Tuple.of(0, 1), true,
308 Tuple.of(1, 0), true,
309 Tuple.of(1, 2), true,
310 Tuple.of(2, 1), false
311 ), predicateResultSet);
391 } 312 }
392 313
393 @Test 314 @QueryEngineTest
394 void negativeRelationViewTest() { 315 void negativeRelationViewTest(QueryEvaluationHint hint) {
395 var person = new Symbol<>("Person", 1, Boolean.class, false); 316 var predicate = Query.of("NegativePatternCall", (builder, p1, p2) -> builder.clause(
396 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 317 personView.call(p1),
397 var personView = new KeyOnlyRelationView<>(person); 318 personView.call(p2),
398 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 319 not(friendMustView.call(p1, p2))
399 320 ));
400 var p1 = new Variable("p1");
401 var p2 = new Variable("p2");
402 var predicate = DNF.builder("NegativePatternCall")
403 .parameters(p1, p2)
404 .clause(
405 new RelationViewAtom(personView, p1),
406 new RelationViewAtom(personView, p2),
407 new RelationViewAtom(false, friendMustView, p1, p2)
408 )
409 .build();
410 321
411 var store = ModelStore.builder() 322 var store = ModelStore.builder()
412 .symbols(person, friend) 323 .symbols(person, friend)
413 .with(ViatraModelQuery.ADAPTER) 324 .with(ViatraModelQueryAdapter.builder()
414 .queries(predicate) 325 .defaultHint(hint)
326 .queries(predicate))
415 .build(); 327 .build();
416 328
417 var model = store.createEmptyModel(); 329 var model = store.createEmptyModel();
418 var personInterpretation = model.getInterpretation(person); 330 var personInterpretation = model.getInterpretation(person);
419 var friendInterpretation = model.getInterpretation(friend); 331 var friendInterpretation = model.getInterpretation(friend);
420 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 332 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
421 var predicateResultSet = queryEngine.getResultSet(predicate); 333 var predicateResultSet = queryEngine.getResultSet(predicate);
422 334
423 personInterpretation.put(Tuple.of(0), true); 335 personInterpretation.put(Tuple.of(0), true);
@@ -429,48 +341,44 @@ class QueryTest {
429 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 341 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
430 342
431 queryEngine.flushChanges(); 343 queryEngine.flushChanges();
432 assertEquals(6, predicateResultSet.countResults()); 344 assertResults(Map.of(
345 Tuple.of(0, 0), true,
346 Tuple.of(0, 2), true,
347 Tuple.of(1, 1), true,
348 Tuple.of(2, 0), true,
349 Tuple.of(2, 1), true,
350 Tuple.of(2, 2), true,
351 Tuple.of(0, 1), false,
352 Tuple.of(1, 0), false,
353 Tuple.of(1, 2), false,
354 Tuple.of(0, 3), false
355 ), predicateResultSet);
433 } 356 }
434 357
435 @Test 358 @QueryEngineTest
436 void negativePatternCallTest() { 359 void negativePatternCallTest(QueryEvaluationHint hint) {
437 var person = new Symbol<>("Person", 1, Boolean.class, false); 360 var friendPredicate = Query.of("Friend", (builder, p1, p2) -> builder.clause(
438 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 361 personView.call(p1),
439 var personView = new KeyOnlyRelationView<>(person); 362 personView.call(p2),
440 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 363 friendMustView.call(p1, p2)
441 364 ));
442 var p1 = new Variable("p1"); 365 var predicate = Query.of("NegativePatternCall", (builder, p3, p4) -> builder.clause(
443 var p2 = new Variable("p2"); 366 personView.call(p3),
444 var friendPredicate = DNF.builder("RelationConstraint") 367 personView.call(p4),
445 .parameters(p1, p2) 368 not(friendPredicate.call(p3, p4))
446 .clause( 369 ));
447 new RelationViewAtom(personView, p1),
448 new RelationViewAtom(personView, p2),
449 new RelationViewAtom(friendMustView, p1, p2)
450 )
451 .build();
452
453 var p3 = new Variable("p3");
454 var p4 = new Variable("p4");
455 var predicate = DNF.builder("NegativePatternCall")
456 .parameters(p3, p4)
457 .clause(
458 new RelationViewAtom(personView, p3),
459 new RelationViewAtom(personView, p4),
460 new DNFCallAtom(false, friendPredicate, p3, p4)
461 )
462 .build();
463 370
464 var store = ModelStore.builder() 371 var store = ModelStore.builder()
465 .symbols(person, friend) 372 .symbols(person, friend)
466 .with(ViatraModelQuery.ADAPTER) 373 .with(ViatraModelQueryAdapter.builder()
467 .queries(predicate) 374 .defaultHint(hint)
375 .queries(predicate))
468 .build(); 376 .build();
469 377
470 var model = store.createEmptyModel(); 378 var model = store.createEmptyModel();
471 var personInterpretation = model.getInterpretation(person); 379 var personInterpretation = model.getInterpretation(person);
472 var friendInterpretation = model.getInterpretation(friend); 380 var friendInterpretation = model.getInterpretation(friend);
473 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 381 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
474 var predicateResultSet = queryEngine.getResultSet(predicate); 382 var predicateResultSet = queryEngine.getResultSet(predicate);
475 383
476 personInterpretation.put(Tuple.of(0), true); 384 personInterpretation.put(Tuple.of(0), true);
@@ -482,37 +390,38 @@ class QueryTest {
482 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 390 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
483 391
484 queryEngine.flushChanges(); 392 queryEngine.flushChanges();
485 assertEquals(6, predicateResultSet.countResults()); 393 assertResults(Map.of(
394 Tuple.of(0, 0), true,
395 Tuple.of(0, 2), true,
396 Tuple.of(1, 1), true,
397 Tuple.of(2, 0), true,
398 Tuple.of(2, 1), true,
399 Tuple.of(2, 2), true,
400 Tuple.of(0, 1), false,
401 Tuple.of(1, 0), false,
402 Tuple.of(1, 2), false,
403 Tuple.of(0, 3), false
404 ), predicateResultSet);
486 } 405 }
487 406
488 @Test 407 @QueryEngineTest
489 void negativeRelationViewWithQuantificationTest() { 408 void negativeRelationViewWithQuantificationTest(QueryEvaluationHint hint) {
490 var person = new Symbol<>("Person", 1, Boolean.class, false); 409 var predicate = Query.of("Negative", (builder, p1) -> builder.clause(
491 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 410 personView.call(p1),
492 var personView = new KeyOnlyRelationView<>(person); 411 not(friendMustView.call(p1, Variable.of()))
493 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 412 ));
494
495 var p1 = new Variable("p1");
496 var p2 = new Variable("p2");
497
498 var predicate = DNF.builder("Count")
499 .parameters(p1)
500 .clause(
501 new RelationViewAtom(personView, p1),
502 new RelationViewAtom(false, friendMustView, p1, p2)
503 )
504 .build();
505 413
506 var store = ModelStore.builder() 414 var store = ModelStore.builder()
507 .symbols(person, friend) 415 .symbols(person, friend)
508 .with(ViatraModelQuery.ADAPTER) 416 .with(ViatraModelQueryAdapter.builder()
509 .queries(predicate) 417 .defaultHint(hint)
418 .queries(predicate))
510 .build(); 419 .build();
511 420
512 var model = store.createEmptyModel(); 421 var model = store.createEmptyModel();
513 var personInterpretation = model.getInterpretation(person); 422 var personInterpretation = model.getInterpretation(person);
514 var friendInterpretation = model.getInterpretation(friend); 423 var friendInterpretation = model.getInterpretation(friend);
515 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 424 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
516 var predicateResultSet = queryEngine.getResultSet(predicate); 425 var predicateResultSet = queryEngine.getResultSet(predicate);
517 426
518 personInterpretation.put(Tuple.of(0), true); 427 personInterpretation.put(Tuple.of(0), true);
@@ -523,46 +432,37 @@ class QueryTest {
523 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE); 432 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
524 433
525 queryEngine.flushChanges(); 434 queryEngine.flushChanges();
526 assertEquals(2, predicateResultSet.countResults()); 435 assertResults(Map.of(
436 Tuple.of(0), false,
437 Tuple.of(1), true,
438 Tuple.of(2), true,
439 Tuple.of(3), false
440 ), predicateResultSet);
527 } 441 }
528 442
529 @Test 443 @QueryEngineTest
530 void negativeWithQuantificationTest() { 444 void negativeWithQuantificationTest(QueryEvaluationHint hint) {
531 var person = new Symbol<>("Person", 1, Boolean.class, false); 445 var called = Query.of("Called", (builder, p1, p2) -> builder.clause(
532 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 446 personView.call(p1),
533 var personView = new KeyOnlyRelationView<>(person); 447 personView.call(p2),
534 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 448 friendMustView.call(p1, p2)
535 449 ));
536 var p1 = new Variable("p1"); 450 var predicate = Query.of("Negative", (builder, p1) -> builder.clause(
537 var p2 = new Variable("p2"); 451 personView.call(p1),
538 452 not(called.call(p1, Variable.of()))
539 var called = DNF.builder("Called") 453 ));
540 .parameters(p1, p2)
541 .clause(
542 new RelationViewAtom(personView, p1),
543 new RelationViewAtom(personView, p2),
544 new RelationViewAtom(friendMustView, p1, p2)
545 )
546 .build();
547
548 var predicate = DNF.builder("Count")
549 .parameters(p1)
550 .clause(
551 new RelationViewAtom(personView, p1),
552 new DNFCallAtom(false, called, p1, p2)
553 )
554 .build();
555 454
556 var store = ModelStore.builder() 455 var store = ModelStore.builder()
557 .symbols(person, friend) 456 .symbols(person, friend)
558 .with(ViatraModelQuery.ADAPTER) 457 .with(ViatraModelQueryAdapter.builder()
559 .queries(predicate) 458 .defaultHint(hint)
459 .queries(predicate))
560 .build(); 460 .build();
561 461
562 var model = store.createEmptyModel(); 462 var model = store.createEmptyModel();
563 var personInterpretation = model.getInterpretation(person); 463 var personInterpretation = model.getInterpretation(person);
564 var friendInterpretation = model.getInterpretation(friend); 464 var friendInterpretation = model.getInterpretation(friend);
565 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 465 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
566 var predicateResultSet = queryEngine.getResultSet(predicate); 466 var predicateResultSet = queryEngine.getResultSet(predicate);
567 467
568 personInterpretation.put(Tuple.of(0), true); 468 personInterpretation.put(Tuple.of(0), true);
@@ -573,37 +473,33 @@ class QueryTest {
573 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE); 473 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
574 474
575 queryEngine.flushChanges(); 475 queryEngine.flushChanges();
576 assertEquals(2, predicateResultSet.countResults()); 476 assertResults(Map.of(
477 Tuple.of(0), false,
478 Tuple.of(1), true,
479 Tuple.of(2), true,
480 Tuple.of(3), false
481 ), predicateResultSet);
577 } 482 }
578 483
579 @Test 484 @QueryEngineTest
580 void transitiveRelationViewTest() { 485 void transitiveRelationViewTest(QueryEvaluationHint hint) {
581 var person = new Symbol<>("Person", 1, Boolean.class, false); 486 var predicate = Query.of("Transitive", (builder, p1, p2) -> builder.clause(
582 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 487 personView.call(p1),
583 var personView = new KeyOnlyRelationView<>(person); 488 personView.call(p2),
584 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 489 friendMustView.callTransitive(p1, p2)
585 490 ));
586 var p1 = new Variable("p1");
587 var p2 = new Variable("p2");
588 var predicate = DNF.builder("TransitivePatternCall")
589 .parameters(p1, p2)
590 .clause(
591 new RelationViewAtom(personView, p1),
592 new RelationViewAtom(personView, p2),
593 new RelationViewAtom(CallPolarity.TRANSITIVE, friendMustView, p1, p2)
594 )
595 .build();
596 491
597 var store = ModelStore.builder() 492 var store = ModelStore.builder()
598 .symbols(person, friend) 493 .symbols(person, friend)
599 .with(ViatraModelQuery.ADAPTER) 494 .with(ViatraModelQueryAdapter.builder()
600 .queries(predicate) 495 .defaultHint(hint)
496 .queries(predicate))
601 .build(); 497 .build();
602 498
603 var model = store.createEmptyModel(); 499 var model = store.createEmptyModel();
604 var personInterpretation = model.getInterpretation(person); 500 var personInterpretation = model.getInterpretation(person);
605 var friendInterpretation = model.getInterpretation(friend); 501 var friendInterpretation = model.getInterpretation(friend);
606 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 502 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
607 var predicateResultSet = queryEngine.getResultSet(predicate); 503 var predicateResultSet = queryEngine.getResultSet(predicate);
608 504
609 personInterpretation.put(Tuple.of(0), true); 505 personInterpretation.put(Tuple.of(0), true);
@@ -614,48 +510,44 @@ class QueryTest {
614 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 510 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
615 511
616 queryEngine.flushChanges(); 512 queryEngine.flushChanges();
617 assertEquals(3, predicateResultSet.countResults()); 513 assertResults(Map.of(
514 Tuple.of(0, 0), false,
515 Tuple.of(0, 1), true,
516 Tuple.of(0, 2), true,
517 Tuple.of(1, 0), false,
518 Tuple.of(1, 1), false,
519 Tuple.of(1, 2), true,
520 Tuple.of(2, 0), false,
521 Tuple.of(2, 1), false,
522 Tuple.of(2, 2), false,
523 Tuple.of(2, 3), false
524 ), predicateResultSet);
618 } 525 }
619 526
620 @Test 527 @QueryEngineTest
621 void transitivePatternCallTest() { 528 void transitivePatternCallTest(QueryEvaluationHint hint) {
622 var person = new Symbol<>("Person", 1, Boolean.class, false); 529 var called = Query.of("Called", (builder, p1, p2) -> builder.clause(
623 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 530 personView.call(p1),
624 var personView = new KeyOnlyRelationView<>(person); 531 personView.call(p2),
625 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 532 friendMustView.call(p1, p2)
626 533 ));
627 var p1 = new Variable("p1"); 534 var predicate = Query.of("Transitive", (builder, p1, p2) -> builder.clause(
628 var p2 = new Variable("p2"); 535 personView.call(p1),
629 var friendPredicate = DNF.builder("RelationConstraint") 536 personView.call(p2),
630 .parameters(p1, p2) 537 called.callTransitive(p1, p2)
631 .clause( 538 ));
632 new RelationViewAtom(personView, p1),
633 new RelationViewAtom(personView, p2),
634 new RelationViewAtom(friendMustView, p1, p2)
635 )
636 .build();
637
638 var p3 = new Variable("p3");
639 var p4 = new Variable("p4");
640 var predicate = DNF.builder("TransitivePatternCall")
641 .parameters(p3, p4)
642 .clause(
643 new RelationViewAtom(personView, p3),
644 new RelationViewAtom(personView, p4),
645 new DNFCallAtom(CallPolarity.TRANSITIVE, friendPredicate, p3, p4)
646 )
647 .build();
648 539
649 var store = ModelStore.builder() 540 var store = ModelStore.builder()
650 .symbols(person, friend) 541 .symbols(person, friend)
651 .with(ViatraModelQuery.ADAPTER) 542 .with(ViatraModelQueryAdapter.builder()
652 .queries(predicate) 543 .defaultHint(hint)
544 .queries(predicate))
653 .build(); 545 .build();
654 546
655 var model = store.createEmptyModel(); 547 var model = store.createEmptyModel();
656 var personInterpretation = model.getInterpretation(person); 548 var personInterpretation = model.getInterpretation(person);
657 var friendInterpretation = model.getInterpretation(friend); 549 var friendInterpretation = model.getInterpretation(friend);
658 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 550 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
659 var predicateResultSet = queryEngine.getResultSet(predicate); 551 var predicateResultSet = queryEngine.getResultSet(predicate);
660 552
661 personInterpretation.put(Tuple.of(0), true); 553 personInterpretation.put(Tuple.of(0), true);
@@ -666,16 +558,150 @@ class QueryTest {
666 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 558 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
667 559
668 queryEngine.flushChanges(); 560 queryEngine.flushChanges();
669 assertEquals(3, predicateResultSet.countResults()); 561 assertResults(Map.of(
562 Tuple.of(0, 0), false,
563 Tuple.of(0, 1), true,
564 Tuple.of(0, 2), true,
565 Tuple.of(1, 0), false,
566 Tuple.of(1, 1), false,
567 Tuple.of(1, 2), true,
568 Tuple.of(2, 0), false,
569 Tuple.of(2, 1), false,
570 Tuple.of(2, 2), false,
571 Tuple.of(2, 3), false
572 ), predicateResultSet);
573 }
574
575 @Test
576 void filteredIntegerViewTest() {
577 var distance = Symbol.of("distance", 2, Integer.class);
578 var nearView = new FilteredView<>(distance, value -> value < 2);
579 var farView = new FilteredView<>(distance, value -> value >= 5);
580 var dangerQuery = Query.of("danger", (builder, a1, a2) -> builder.clause((a3) -> List.of(
581 a1.notEquivalent(a2),
582 nearView.call(a1, a3),
583 nearView.call(a2, a3),
584 not(farView.call(a1, a2))
585 )));
586 var store = ModelStore.builder()
587 .symbols(distance)
588 .with(ViatraModelQueryAdapter.builder()
589 .queries(dangerQuery))
590 .build();
591
592 var model = store.createEmptyModel();
593 var distanceInterpretation = model.getInterpretation(distance);
594 distanceInterpretation.put(Tuple.of(0, 1), 1);
595 distanceInterpretation.put(Tuple.of(1, 0), 1);
596 distanceInterpretation.put(Tuple.of(0, 2), 1);
597 distanceInterpretation.put(Tuple.of(2, 0), 1);
598 distanceInterpretation.put(Tuple.of(1, 2), 3);
599 distanceInterpretation.put(Tuple.of(2, 1), 3);
600 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
601 var dangerResultSet = queryEngine.getResultSet(dangerQuery);
602 queryEngine.flushChanges();
603 assertResults(Map.of(
604 Tuple.of(0, 1), false,
605 Tuple.of(0, 2), false,
606 Tuple.of(1, 2), true,
607 Tuple.of(2, 1), true
608 ), dangerResultSet);
609 }
610
611 @Test
612 void filteredDoubleViewTest() {
613 var distance = Symbol.of("distance", 2, Double.class);
614 var nearView = new FilteredView<>(distance, value -> value < 2);
615 var farView = new FilteredView<>(distance, value -> value >= 5);
616 var dangerQuery = Query.of("danger", (builder, a1, a2) -> builder.clause((a3) -> List.of(
617 a1.notEquivalent(a2),
618 nearView.call(a1, a3),
619 nearView.call(a2, a3),
620 not(farView.call(a1, a2))
621 )));
622 var store = ModelStore.builder()
623 .symbols(distance)
624 .with(ViatraModelQueryAdapter.builder()
625 .queries(dangerQuery))
626 .build();
627
628 var model = store.createEmptyModel();
629 var distanceInterpretation = model.getInterpretation(distance);
630 distanceInterpretation.put(Tuple.of(0, 1), 1.0);
631 distanceInterpretation.put(Tuple.of(1, 0), 1.0);
632 distanceInterpretation.put(Tuple.of(0, 2), 1.0);
633 distanceInterpretation.put(Tuple.of(2, 0), 1.0);
634 distanceInterpretation.put(Tuple.of(1, 2), 3.0);
635 distanceInterpretation.put(Tuple.of(2, 1), 3.0);
636 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
637 var dangerResultSet = queryEngine.getResultSet(dangerQuery);
638 queryEngine.flushChanges();
639 assertResults(Map.of(
640 Tuple.of(0, 1), false,
641 Tuple.of(0, 2), false,
642 Tuple.of(1, 2), true,
643 Tuple.of(2, 1), true
644 ), dangerResultSet);
645 }
646
647 @QueryEngineTest
648 void assumeTest(QueryEvaluationHint hint) {
649 var age = Symbol.of("age", 1, Integer.class);
650 var ageView = new FunctionView<>(age);
651
652 var query = Query.of("Constraint", (builder, p1) -> builder.clause(Integer.class, (x) -> List.of(
653 personView.call(p1),
654 ageView.call(p1, x),
655 assume(greaterEq(x, constant(18)))
656 )));
657
658 var store = ModelStore.builder()
659 .symbols(person, age)
660 .with(ViatraModelQueryAdapter.builder()
661 .defaultHint(hint)
662 .queries(query))
663 .build();
664
665 var model = store.createEmptyModel();
666 var personInterpretation = model.getInterpretation(person);
667 var ageInterpretation = model.getInterpretation(age);
668 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
669 var queryResultSet = queryEngine.getResultSet(query);
670
671 personInterpretation.put(Tuple.of(0), true);
672 personInterpretation.put(Tuple.of(1), true);
673
674 ageInterpretation.put(Tuple.of(0), 12);
675 ageInterpretation.put(Tuple.of(1), 24);
676
677 queryEngine.flushChanges();
678 assertResults(Map.of(
679 Tuple.of(0), false,
680 Tuple.of(1), true,
681 Tuple.of(2), false
682 ), queryResultSet);
670 } 683 }
671 684
672 static void compareMatchSets(Stream<TupleLike> matchSet, Set<Tuple> expected) { 685 @Test
673 Set<Tuple> translatedMatchSet = new HashSet<>(); 686 void alwaysFalseTest() {
674 var iterator = matchSet.iterator(); 687 var predicate = Query.of("AlwaysFalse", builder -> builder.parameter("p1"));
675 while (iterator.hasNext()) { 688
676 var element = iterator.next(); 689 var store = ModelStore.builder()
677 translatedMatchSet.add(element.toTuple()); 690 .symbols(person)
678 } 691 .with(ViatraModelQueryAdapter.builder()
679 assertEquals(expected, translatedMatchSet); 692 .queries(predicate))
693 .build();
694
695 var model = store.createEmptyModel();
696 var personInterpretation = model.getInterpretation(person);
697 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
698 var predicateResultSet = queryEngine.getResultSet(predicate);
699
700 personInterpretation.put(Tuple.of(0), true);
701 personInterpretation.put(Tuple.of(1), true);
702 personInterpretation.put(Tuple.of(2), true);
703
704 queryEngine.flushChanges();
705 assertResults(Map.of(), predicateResultSet);
680 } 706 }
681} 707}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
index 98995339..66f043c6 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
@@ -1,43 +1,163 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
2 7
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
9import org.junit.jupiter.api.Disabled;
3import org.junit.jupiter.api.Test; 10import org.junit.jupiter.api.Test;
4import tools.refinery.store.model.ModelStore; 11import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.DNF; 12import tools.refinery.store.query.ModelQueryAdapter;
6import tools.refinery.store.query.ModelQuery; 13import tools.refinery.store.query.dnf.Query;
7import tools.refinery.store.query.Variable; 14import tools.refinery.store.query.dnf.RelationalQuery;
8import tools.refinery.store.query.atom.RelationViewAtom; 15import tools.refinery.store.query.view.AnySymbolView;
9import tools.refinery.store.query.view.KeyOnlyRelationView; 16import tools.refinery.store.query.view.FilteredView;
17import tools.refinery.store.query.view.FunctionView;
18import tools.refinery.store.query.view.KeyOnlyView;
10import tools.refinery.store.representation.Symbol; 19import tools.refinery.store.representation.Symbol;
11import tools.refinery.store.tuple.Tuple; 20import tools.refinery.store.tuple.Tuple;
12 21
13import static org.junit.jupiter.api.Assertions.*; 22import java.util.Map;
23import java.util.Optional;
24
25import static org.junit.jupiter.api.Assertions.assertFalse;
26import static org.junit.jupiter.api.Assertions.assertTrue;
27import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
28import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
14 29
15class QueryTransactionTest { 30class QueryTransactionTest {
31 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
32 private static final Symbol<Integer> age = Symbol.of("age", 1, Integer.class);
33 private static final AnySymbolView personView = new KeyOnlyView<>(person);
34 private static final AnySymbolView ageView = new FunctionView<>(age);
35 private static final RelationalQuery predicate = Query.of("TypeConstraint", (builder, p1) ->
36 builder.clause(personView.call(p1)));
37
16 @Test 38 @Test
17 void flushTest() { 39 void flushTest() {
18 var person = new Symbol<>("Person", 1, Boolean.class, false); 40 var store = ModelStore.builder()
19 var asset = new Symbol<>("Asset", 1, Boolean.class, false); 41 .symbols(person)
20 var personView = new KeyOnlyRelationView<>(person); 42 .with(ViatraModelQueryAdapter.builder()
21 43 .queries(predicate))
22 var p1 = new Variable("p1");
23 var predicate = DNF.builder("TypeConstraint")
24 .parameters(p1)
25 .clause(new RelationViewAtom(personView, p1))
26 .build(); 44 .build();
27 45
46 var model = store.createEmptyModel();
47 var personInterpretation = model.getInterpretation(person);
48 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
49 var predicateResultSet = queryEngine.getResultSet(predicate);
50
51 assertResults(Map.of(
52 Tuple.of(0), false,
53 Tuple.of(1), false,
54 Tuple.of(2), false,
55 Tuple.of(3), false
56 ), predicateResultSet);
57 assertFalse(queryEngine.hasPendingChanges());
58
59 personInterpretation.put(Tuple.of(0), true);
60 personInterpretation.put(Tuple.of(1), true);
61
62 assertResults(Map.of(
63 Tuple.of(0), false,
64 Tuple.of(1), false,
65 Tuple.of(2), false,
66 Tuple.of(3), false
67 ), predicateResultSet);
68 assertTrue(queryEngine.hasPendingChanges());
69
70 queryEngine.flushChanges();
71 assertResults(Map.of(
72 Tuple.of(0), true,
73 Tuple.of(1), true,
74 Tuple.of(2), false,
75 Tuple.of(3), false
76 ), predicateResultSet);
77 assertFalse(queryEngine.hasPendingChanges());
78
79 personInterpretation.put(Tuple.of(1), false);
80 personInterpretation.put(Tuple.of(2), true);
81
82 assertResults(Map.of(
83 Tuple.of(0), true,
84 Tuple.of(1), true,
85 Tuple.of(2), false,
86 Tuple.of(3), false
87 ), predicateResultSet);
88 assertTrue(queryEngine.hasPendingChanges());
89
90 queryEngine.flushChanges();
91 assertResults(Map.of(
92 Tuple.of(0), true,
93 Tuple.of(1), false,
94 Tuple.of(2), true,
95 Tuple.of(3), false
96 ), predicateResultSet);
97 assertFalse(queryEngine.hasPendingChanges());
98 }
99
100 @Test
101 void localSearchTest() {
102 var store = ModelStore.builder()
103 .symbols(person)
104 .with(ViatraModelQueryAdapter.builder()
105 .defaultHint(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH))
106 .queries(predicate))
107 .build();
108
109 var model = store.createEmptyModel();
110 var personInterpretation = model.getInterpretation(person);
111 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
112 var predicateResultSet = queryEngine.getResultSet(predicate);
113
114 assertResults(Map.of(
115 Tuple.of(0), false,
116 Tuple.of(1), false,
117 Tuple.of(2), false,
118 Tuple.of(3), false
119 ), predicateResultSet);
120 assertFalse(queryEngine.hasPendingChanges());
121
122 personInterpretation.put(Tuple.of(0), true);
123 personInterpretation.put(Tuple.of(1), true);
124
125 assertResults(Map.of(
126 Tuple.of(0), true,
127 Tuple.of(1), true,
128 Tuple.of(2), false,
129 Tuple.of(3), false
130 ), predicateResultSet);
131 assertFalse(queryEngine.hasPendingChanges());
132
133 personInterpretation.put(Tuple.of(1), false);
134 personInterpretation.put(Tuple.of(2), true);
135
136 assertResults(Map.of(
137 Tuple.of(0), true,
138 Tuple.of(1), false,
139 Tuple.of(2), true,
140 Tuple.of(3), false
141 ), predicateResultSet);
142 assertFalse(queryEngine.hasPendingChanges());
143 }
144
145 @Test
146 void unrelatedChangesTest() {
147 var asset = Symbol.of("Asset", 1);
148
28 var store = ModelStore.builder() 149 var store = ModelStore.builder()
29 .symbols(person, asset) 150 .symbols(person, asset)
30 .with(ViatraModelQuery.ADAPTER) 151 .with(ViatraModelQueryAdapter.builder()
31 .queries(predicate) 152 .queries(predicate))
32 .build(); 153 .build();
33 154
34 var model = store.createEmptyModel(); 155 var model = store.createEmptyModel();
35 var personInterpretation = model.getInterpretation(person); 156 var personInterpretation = model.getInterpretation(person);
36 var assetInterpretation = model.getInterpretation(asset); 157 var assetInterpretation = model.getInterpretation(asset);
37 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 158 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
38 var predicateResultSet = queryEngine.getResultSet(predicate); 159 var predicateResultSet = queryEngine.getResultSet(predicate);
39 160
40 assertEquals(0, predicateResultSet.countResults());
41 assertFalse(queryEngine.hasPendingChanges()); 161 assertFalse(queryEngine.hasPendingChanges());
42 162
43 personInterpretation.put(Tuple.of(0), true); 163 personInterpretation.put(Tuple.of(0), true);
@@ -46,19 +166,208 @@ class QueryTransactionTest {
46 assetInterpretation.put(Tuple.of(1), true); 166 assetInterpretation.put(Tuple.of(1), true);
47 assetInterpretation.put(Tuple.of(2), true); 167 assetInterpretation.put(Tuple.of(2), true);
48 168
49 assertEquals(0, predicateResultSet.countResults()); 169 assertResults(Map.of(
170 Tuple.of(0), false,
171 Tuple.of(1), false,
172 Tuple.of(2), false,
173 Tuple.of(3), false,
174 Tuple.of(4), false
175 ), predicateResultSet);
50 assertTrue(queryEngine.hasPendingChanges()); 176 assertTrue(queryEngine.hasPendingChanges());
51 177
52 queryEngine.flushChanges(); 178 queryEngine.flushChanges();
53 assertEquals(2, predicateResultSet.countResults()); 179 assertResults(Map.of(
180 Tuple.of(0), true,
181 Tuple.of(1), true,
182 Tuple.of(2), false,
183 Tuple.of(3), false,
184 Tuple.of(4), false
185 ), predicateResultSet);
54 assertFalse(queryEngine.hasPendingChanges()); 186 assertFalse(queryEngine.hasPendingChanges());
55 187
56 personInterpretation.put(Tuple.of(4), true); 188 assetInterpretation.put(Tuple.of(3), true);
57 assertEquals(2, predicateResultSet.countResults()); 189 assertFalse(queryEngine.hasPendingChanges());
58 assertTrue(queryEngine.hasPendingChanges()); 190
191 assertResults(Map.of(
192 Tuple.of(0), true,
193 Tuple.of(1), true,
194 Tuple.of(2), false,
195 Tuple.of(3), false,
196 Tuple.of(4), false
197 ), predicateResultSet);
198
199 queryEngine.flushChanges();
200 assertResults(Map.of(
201 Tuple.of(0), true,
202 Tuple.of(1), true,
203 Tuple.of(2), false,
204 Tuple.of(3), false,
205 Tuple.of(4), false
206 ), predicateResultSet);
207 assertFalse(queryEngine.hasPendingChanges());
208 }
209
210 @Test
211 void tupleChangingChangeTest() {
212 var query = Query.of("TypeConstraint", Integer.class, (builder, p1, output) -> builder.clause(
213 personView.call(p1),
214 ageView.call(p1, output)
215 ));
216
217 var store = ModelStore.builder()
218 .symbols(person, age)
219 .with(ViatraModelQueryAdapter.builder()
220 .queries(query))
221 .build();
222
223 var model = store.createEmptyModel();
224 var personInterpretation = model.getInterpretation(person);
225 var ageInterpretation = model.getInterpretation(age);
226 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
227 var queryResultSet = queryEngine.getResultSet(query);
228
229 personInterpretation.put(Tuple.of(0), true);
230
231 ageInterpretation.put(Tuple.of(0), 24);
232
233 queryEngine.flushChanges();
234 assertResults(Map.of(Tuple.of(0), 24), queryResultSet);
235
236 ageInterpretation.put(Tuple.of(0), 25);
59 237
60 queryEngine.flushChanges(); 238 queryEngine.flushChanges();
61 assertEquals(3, predicateResultSet.countResults()); 239 assertResults(Map.of(Tuple.of(0), 25), queryResultSet);
240
241 ageInterpretation.put(Tuple.of(0), null);
242
243 queryEngine.flushChanges();
244 assertNullableResults(Map.of(Tuple.of(0), Optional.empty()), queryResultSet);
245 }
246
247 @Test
248 void tuplePreservingUnchangedTest() {
249 var adultView = new FilteredView<>(age, "adult", n -> n != null && n >= 18);
250
251 var query = Query.of("TypeConstraint", (builder, p1) -> builder.clause(
252 personView.call(p1),
253 adultView.call(p1)
254 ));
255
256 var store = ModelStore.builder()
257 .symbols(person, age)
258 .with(ViatraModelQueryAdapter.builder()
259 .queries(query))
260 .build();
261
262 var model = store.createEmptyModel();
263 var personInterpretation = model.getInterpretation(person);
264 var ageInterpretation = model.getInterpretation(age);
265 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
266 var queryResultSet = queryEngine.getResultSet(query);
267
268 personInterpretation.put(Tuple.of(0), true);
269
270 ageInterpretation.put(Tuple.of(0), 24);
271
272 queryEngine.flushChanges();
273 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
274
275 ageInterpretation.put(Tuple.of(0), 25);
276
277 queryEngine.flushChanges();
278 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
279
280 ageInterpretation.put(Tuple.of(0), 17);
281
282 queryEngine.flushChanges();
283 assertResults(Map.of(Tuple.of(0), false), queryResultSet);
284 }
285
286 @Disabled("TODO Fix DiffCursor")
287 @Test
288 void commitAfterFlushTest() {
289 var store = ModelStore.builder()
290 .symbols(person)
291 .with(ViatraModelQueryAdapter.builder()
292 .queries(predicate))
293 .build();
294
295 var model = store.createEmptyModel();
296 var personInterpretation = model.getInterpretation(person);
297 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
298 var predicateResultSet = queryEngine.getResultSet(predicate);
299
300 personInterpretation.put(Tuple.of(0), true);
301 personInterpretation.put(Tuple.of(1), true);
302
303 queryEngine.flushChanges();
304 assertResults(Map.of(
305 Tuple.of(0), true,
306 Tuple.of(1), true,
307 Tuple.of(2), false,
308 Tuple.of(3), false
309 ), predicateResultSet);
310
311 var state1 = model.commit();
312
313 personInterpretation.put(Tuple.of(1), false);
314 personInterpretation.put(Tuple.of(2), true);
315
316 queryEngine.flushChanges();
317 assertResults(Map.of(
318 Tuple.of(0), true,
319 Tuple.of(1), false,
320 Tuple.of(2), true,
321 Tuple.of(3), false
322 ), predicateResultSet);
323
324 model.restore(state1);
325
326 assertFalse(queryEngine.hasPendingChanges());
327 assertResults(Map.of(
328 Tuple.of(0), true,
329 Tuple.of(1), true,
330 Tuple.of(2), false,
331 Tuple.of(3), false
332 ), predicateResultSet);
333 }
334
335 @Disabled("TODO Fix DiffCursor")
336 @Test
337 void commitWithoutFlushTest() {
338 var store = ModelStore.builder()
339 .symbols(person)
340 .with(ViatraModelQueryAdapter.builder()
341 .queries(predicate))
342 .build();
343
344 var model = store.createEmptyModel();
345 var personInterpretation = model.getInterpretation(person);
346 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
347 var predicateResultSet = queryEngine.getResultSet(predicate);
348
349 personInterpretation.put(Tuple.of(0), true);
350 personInterpretation.put(Tuple.of(1), true);
351
352 assertResults(Map.of(), predicateResultSet);
353 assertTrue(queryEngine.hasPendingChanges());
354
355 var state1 = model.commit();
356
357 personInterpretation.put(Tuple.of(1), false);
358 personInterpretation.put(Tuple.of(2), true);
359
360 assertResults(Map.of(), predicateResultSet);
361 assertTrue(queryEngine.hasPendingChanges());
362
363 model.restore(state1);
364
365 assertResults(Map.of(
366 Tuple.of(0), true,
367 Tuple.of(1), true,
368 Tuple.of(2), false,
369 Tuple.of(3), false
370 ), predicateResultSet);
62 assertFalse(queryEngine.hasPendingChanges()); 371 assertFalse(queryEngine.hasPendingChanges());
63 } 372 }
64} 373}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java
deleted file mode 100644
index c529117e..00000000
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorStreamTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
1package tools.refinery.store.query.viatra.internal.cardinality;
2
3import org.junit.jupiter.params.ParameterizedTest;
4import org.junit.jupiter.params.provider.Arguments;
5import org.junit.jupiter.params.provider.MethodSource;
6import tools.refinery.store.representation.cardinality.UpperCardinalities;
7import tools.refinery.store.representation.cardinality.UpperCardinality;
8
9import java.util.List;
10import java.util.stream.Stream;
11
12import static org.hamcrest.MatcherAssert.assertThat;
13import static org.hamcrest.Matchers.equalTo;
14
15class UpperCardinalitySumAggregationOperatorStreamTest {
16 @ParameterizedTest
17 @MethodSource
18 void testStream(List<UpperCardinality> list, UpperCardinality expected) {
19 var result = UpperCardinalitySumAggregationOperator.INSTANCE.aggregateStream(list.stream());
20 assertThat(result, equalTo(expected));
21 }
22
23 static Stream<Arguments> testStream() {
24 return Stream.of(
25 Arguments.of(List.of(), UpperCardinalities.ZERO),
26 Arguments.of(List.of(UpperCardinality.of(3)), UpperCardinality.of(3)),
27 Arguments.of(
28 List.of(
29 UpperCardinality.of(2),
30 UpperCardinality.of(3)
31 ),
32 UpperCardinality.of(5)
33 ),
34 Arguments.of(List.of(UpperCardinalities.UNBOUNDED), UpperCardinalities.UNBOUNDED),
35 Arguments.of(
36 List.of(
37 UpperCardinalities.UNBOUNDED,
38 UpperCardinalities.UNBOUNDED
39 ),
40 UpperCardinalities.UNBOUNDED
41 ),
42 Arguments.of(
43 List.of(
44 UpperCardinalities.UNBOUNDED,
45 UpperCardinality.of(3)
46 ),
47 UpperCardinalities.UNBOUNDED
48 )
49 );
50 }
51}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorTest.java
deleted file mode 100644
index 20dad543..00000000
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperatorTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
1package tools.refinery.store.query.viatra.internal.cardinality;
2
3import org.junit.jupiter.api.BeforeEach;
4import org.junit.jupiter.api.Test;
5import tools.refinery.store.representation.cardinality.UpperCardinalities;
6import tools.refinery.store.representation.cardinality.UpperCardinality;
7
8import static org.hamcrest.MatcherAssert.assertThat;
9import static org.hamcrest.Matchers.equalTo;
10
11class UpperCardinalitySumAggregationOperatorTest {
12 private UpperCardinalitySumAggregationOperator.Accumulator accumulator;
13
14 @BeforeEach
15 void beforeEach() {
16 accumulator = UpperCardinalitySumAggregationOperator.INSTANCE.createNeutral();
17 }
18
19 @Test
20 void emptyAggregationTest() {
21 assertResult(UpperCardinality.of(0));
22 }
23
24 @Test
25 void singleBoundedTest() {
26 insert(UpperCardinality.of(3));
27 assertResult(UpperCardinality.of(3));
28 }
29
30 @Test
31 void multipleBoundedTest() {
32 insert(UpperCardinality.of(2));
33 insert(UpperCardinality.of(3));
34 assertResult(UpperCardinality.of(5));
35 }
36
37 @Test
38 void singleUnboundedTest() {
39 insert(UpperCardinalities.UNBOUNDED);
40 assertResult(UpperCardinalities.UNBOUNDED);
41 }
42
43 @Test
44 void multipleUnboundedTest() {
45 insert(UpperCardinalities.UNBOUNDED);
46 insert(UpperCardinalities.UNBOUNDED);
47 assertResult(UpperCardinalities.UNBOUNDED);
48 }
49
50 @Test
51 void removeBoundedTest() {
52 insert(UpperCardinality.of(2));
53 insert(UpperCardinality.of(3));
54 remove(UpperCardinality.of(2));
55 assertResult(UpperCardinality.of(3));
56 }
57
58 @Test
59 void removeAllUnboundedTest() {
60 insert(UpperCardinalities.UNBOUNDED);
61 insert(UpperCardinality.of(3));
62 remove(UpperCardinalities.UNBOUNDED);
63 assertResult(UpperCardinality.of(3));
64 }
65
66 @Test
67 void removeSomeUnboundedTest() {
68 insert(UpperCardinalities.UNBOUNDED);
69 insert(UpperCardinalities.UNBOUNDED);
70 insert(UpperCardinality.of(3));
71 remove(UpperCardinalities.UNBOUNDED);
72 assertResult(UpperCardinalities.UNBOUNDED);
73 }
74
75 private void insert(UpperCardinality value) {
76 accumulator = UpperCardinalitySumAggregationOperator.INSTANCE.update(accumulator, value, true);
77 }
78
79 private void remove(UpperCardinality value) {
80 accumulator = UpperCardinalitySumAggregationOperator.INSTANCE.update(accumulator, value, false);
81 }
82
83 private void assertResult(UpperCardinality expected) {
84 var result = UpperCardinalitySumAggregationOperator.INSTANCE.getAggregate(accumulator);
85 assertThat(result, equalTo(expected));
86 }
87}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtilsTest.java
new file mode 100644
index 00000000..968c6c5e
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/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.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.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-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java
new file mode 100644
index 00000000..ca089a9d
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/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.viatra.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.getQuery().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-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
new file mode 100644
index 00000000..dc0e92c8
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/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.viatra.tests;
7
8import org.eclipse.viatra.query.runtime.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-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java
new file mode 100644
index 00000000..d4f16da7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/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.viatra.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-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
new file mode 100644
index 00000000..9e75d5f3
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/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.viatra.tests;
7
8import org.eclipse.viatra.query.runtime.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}