aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-query/src/test/java')
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfBuilderLiteralEliminationTest.java55
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfToDefinitionStringTest.java4
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/HashCodeTest.java67
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/TopologicalSortTest.java7
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java189
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/literal/AggregationLiteralTest.java13
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/DuplicateDnfRemoverTest.java164
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java228
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/term/TermSubstitutionTest.java6
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/utils/OrderStatisticTreeTest.java2
10 files changed, 534 insertions, 201 deletions
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfBuilderLiteralEliminationTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfBuilderLiteralEliminationTest.java
index e17496e3..6a2dc0c7 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfBuilderLiteralEliminationTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfBuilderLiteralEliminationTest.java
@@ -20,7 +20,7 @@ import tools.refinery.store.representation.Symbol;
20import java.util.List; 20import java.util.List;
21 21
22import static org.hamcrest.MatcherAssert.assertThat; 22import static org.hamcrest.MatcherAssert.assertThat;
23import static tools.refinery.store.query.literal.Literals.assume; 23import static tools.refinery.store.query.literal.Literals.check;
24import static tools.refinery.store.query.literal.Literals.not; 24import static tools.refinery.store.query.literal.Literals.not;
25import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo; 25import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo;
26 26
@@ -47,7 +47,7 @@ class DnfBuilderLiteralEliminationTest {
47 void eliminateTrueAssumptionTest() { 47 void eliminateTrueAssumptionTest() {
48 var actual = Dnf.builder() 48 var actual = Dnf.builder()
49 .parameters(p, q) 49 .parameters(p, q)
50 .clause(assume(BoolTerms.constant(true)), friendView.call(p, q)) 50 .clause(check(BoolTerms.constant(true)), friendView.call(p, q))
51 .build(); 51 .build();
52 var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); 52 var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build();
53 53
@@ -75,7 +75,7 @@ class DnfBuilderLiteralEliminationTest {
75 var actual = Dnf.builder() 75 var actual = Dnf.builder()
76 .parameters(p, q) 76 .parameters(p, q)
77 .clause(friendView.call(p, q)) 77 .clause(friendView.call(p, q))
78 .clause(friendView.call(q, p), assume(BoolTerms.constant(value))) 78 .clause(friendView.call(q, p), check(BoolTerms.constant(value)))
79 .build(); 79 .build();
80 var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); 80 var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build();
81 81
@@ -207,4 +207,53 @@ class DnfBuilderLiteralEliminationTest {
207 207
208 assertThat(actual, structurallyEqualTo(expected)); 208 assertThat(actual, structurallyEqualTo(expected));
209 } 209 }
210
211 @Test
212 void removeContradictoryTest() {
213 var actual = Dnf.of(builder -> builder.clause((p, q) -> List.of(
214 friendView.call(p, q),
215 not(friendView.call(p, q))
216 )));
217 var expected = Dnf.builder().build();
218
219 assertThat(actual, structurallyEqualTo(expected));
220 }
221
222 @Test
223 void removeContradictoryUniversalTest() {
224 var actual = Dnf.of(builder -> builder.clause((p, q) -> List.of(
225 friendView.call(q, q),
226 friendView.call(p, q),
227 not(friendView.call(p, Variable.of()))
228 )));
229 var expected = Dnf.builder().build();
230
231 assertThat(actual, structurallyEqualTo(expected));
232 }
233
234 @Test
235 void removeContradictoryExistentialUniversalTest() {
236 var actual = Dnf.of(builder -> builder.clause((p) -> List.of(
237 friendView.call(p, Variable.of()),
238 not(friendView.call(p, Variable.of()))
239 )));
240 var expected = Dnf.builder().build();
241
242 assertThat(actual, structurallyEqualTo(expected));
243 }
244
245 @Test
246 void removeContradictoryUniversalParameterTest() {
247 var actual = Dnf.of(builder -> {
248 var p = builder.parameter("p");
249 builder.clause((q) -> List.of(
250 friendView.call(q, q),
251 friendView.call(p, q),
252 not(friendView.call(p, Variable.of()))
253 ));
254 });
255 var expected = Dnf.builder().parameter(p).build();
256
257 assertThat(actual, structurallyEqualTo(expected));
258 }
210} 259}
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfToDefinitionStringTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfToDefinitionStringTest.java
index d75d7f17..12cfaa4e 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfToDefinitionStringTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/DnfToDefinitionStringTest.java
@@ -50,7 +50,7 @@ class DnfToDefinitionStringTest {
50 var dnf = Dnf.builder("Example").parameter(p, ParameterDirection.IN).clause().build(); 50 var dnf = Dnf.builder("Example").parameter(p, ParameterDirection.IN).clause().build();
51 51
52 assertThat(dnf.toDefinitionString(), is(""" 52 assertThat(dnf.toDefinitionString(), is("""
53 pred Example(@In p) <-> 53 pred Example(in p) <->
54 <empty>. 54 <empty>.
55 """)); 55 """));
56 } 56 }
@@ -73,7 +73,7 @@ class DnfToDefinitionStringTest {
73 .build(); 73 .build();
74 74
75 assertThat(dnf.toDefinitionString(), is(""" 75 assertThat(dnf.toDefinitionString(), is("""
76 pred Example(@In p) <-> 76 pred Example(in p) <->
77 !(@RelationView("key") friend(p, q)). 77 !(@RelationView("key") friend(p, q)).
78 """)); 78 """));
79 } 79 }
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/HashCodeTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/HashCodeTest.java
new file mode 100644
index 00000000..0c8eaeed
--- /dev/null
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/HashCodeTest.java
@@ -0,0 +1,67 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.dnf;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.query.term.NodeVariable;
10import tools.refinery.store.query.term.Variable;
11import tools.refinery.store.query.view.AnySymbolView;
12import tools.refinery.store.query.view.KeyOnlyView;
13import tools.refinery.store.representation.Symbol;
14
15import static org.hamcrest.MatcherAssert.assertThat;
16import static org.hamcrest.Matchers.is;
17import static org.hamcrest.Matchers.not;
18
19class HashCodeTest {
20 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
21 private static final Symbol<Boolean> friend = Symbol.of("friend", 2);
22 private static final AnySymbolView personView = new KeyOnlyView<>(person);
23 private static final AnySymbolView friendView = new KeyOnlyView<>(friend);
24 private static final NodeVariable p = Variable.of("p");
25 private static final NodeVariable q = Variable.of("q");
26
27 @Test
28 void flatEqualsTest() {
29 var expected = Dnf.builder("Expected").parameters(q).clause(personView.call(q)).build();
30 var actual = Dnf.builder("Actual").parameters(p).clause(personView.call(p)).build();
31
32 assertThat(actual.hashCodeWithSubstitution(), is(expected.hashCodeWithSubstitution()));
33 }
34
35 @Test
36 void flatNotEqualsTest() {
37 var expected = Dnf.builder("Expected").parameters(q).clause(friendView.call(q, q)).build();
38 var actual = Dnf.builder("Actual").parameters(p).clause(friendView.call(p, q)).build();
39
40 assertThat(actual.hashCodeWithSubstitution(), not(expected.hashCodeWithSubstitution()));
41 }
42
43 @Test
44 void deepEqualsTest() {
45 var expected2 = Dnf.builder("Expected2").parameters(p).clause(personView.call(p)).build();
46 var expected = Dnf.builder("Expected").parameters(q).clause(
47 expected2.call(q)
48 ).build();
49 var actual = Dnf.builder("Actual").parameters(q).clause(
50 expected2.call(q)
51 ).build();
52
53 assertThat(actual.hashCodeWithSubstitution(), is(expected.hashCodeWithSubstitution()));
54 }
55
56 @Test
57 void deepNotEqualsTest() {
58 var expected = Dnf.builder("Expected").parameters(q).clause(
59 Dnf.builder("Expected2").parameters(p).clause(personView.call(p)).build().call(q)
60 ).build();
61 var actual = Dnf.builder("Actual").parameters(q).clause(
62 Dnf.builder("Actual2").parameters(p).clause(personView.call(p)).build().call(q)
63 ).build();
64
65 assertThat(actual.hashCodeWithSubstitution(), not(expected.hashCodeWithSubstitution()));
66 }
67}
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/TopologicalSortTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/TopologicalSortTest.java
index e22dbb21..854bd469 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/TopologicalSortTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/TopologicalSortTest.java
@@ -6,6 +6,7 @@
6package tools.refinery.store.query.dnf; 6package tools.refinery.store.query.dnf;
7 7
8import org.junit.jupiter.api.Test; 8import org.junit.jupiter.api.Test;
9import tools.refinery.store.query.InvalidQueryException;
9import tools.refinery.store.query.term.NodeVariable; 10import tools.refinery.store.query.term.NodeVariable;
10import tools.refinery.store.query.term.ParameterDirection; 11import tools.refinery.store.query.term.ParameterDirection;
11import tools.refinery.store.query.term.Variable; 12import tools.refinery.store.query.term.Variable;
@@ -80,7 +81,7 @@ class TopologicalSortTest {
80 example.call(r, t, q, s), 81 example.call(r, t, q, s),
81 friendView.call(r, t) 82 friendView.call(r, t)
82 ); 83 );
83 assertThrows(IllegalArgumentException.class, builder::build); 84 assertThrows(InvalidQueryException.class, builder::build);
84 } 85 }
85 86
86 @Test 87 @Test
@@ -93,7 +94,7 @@ class TopologicalSortTest {
93 example.call(p, q, r, s), 94 example.call(p, q, r, s),
94 example.call(r, t, q, s) 95 example.call(r, t, q, s)
95 ); 96 );
96 assertThrows(IllegalArgumentException.class, builder::build); 97 assertThrows(InvalidQueryException.class, builder::build);
97 } 98 }
98 99
99 @Test 100 @Test
@@ -107,6 +108,6 @@ class TopologicalSortTest {
107 example.call(r, t, q, s), 108 example.call(r, t, q, s),
108 example.call(p, q, r, t) 109 example.call(p, q, r, t)
109 ); 110 );
110 assertThrows(IllegalArgumentException.class, builder::build); 111 assertThrows(InvalidQueryException.class, builder::build);
111 } 112 }
112} 113}
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java
index c52d26b2..fc3f5d48 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java
@@ -28,9 +28,8 @@ import static org.hamcrest.Matchers.hasItem;
28import static org.hamcrest.Matchers.not; 28import static org.hamcrest.Matchers.not;
29import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 29import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
30import static org.junit.jupiter.api.Assertions.assertThrows; 30import static org.junit.jupiter.api.Assertions.assertThrows;
31import static tools.refinery.store.query.literal.Literals.assume;
32import static tools.refinery.store.query.literal.Literals.not; 31import static tools.refinery.store.query.literal.Literals.not;
33import static tools.refinery.store.query.term.int_.IntTerms.*; 32import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM;
34 33
35class VariableDirectionTest { 34class VariableDirectionTest {
36 private static final Symbol<Boolean> person = Symbol.of("Person", 1); 35 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
@@ -49,7 +48,7 @@ class VariableDirectionTest {
49 @MethodSource("clausesWithVariableInput") 48 @MethodSource("clausesWithVariableInput")
50 void unboundOutVariableTest(List<? extends Literal> clause) { 49 void unboundOutVariableTest(List<? extends Literal> clause) {
51 var builder = Dnf.builder().parameter(p, ParameterDirection.OUT).clause(clause); 50 var builder = Dnf.builder().parameter(p, ParameterDirection.OUT).clause(clause);
52 assertThrows(IllegalArgumentException.class, builder::build); 51 assertThrows(InvalidClauseException.class, builder::build);
53 } 52 }
54 53
55 @ParameterizedTest 54 @ParameterizedTest
@@ -101,7 +100,7 @@ class VariableDirectionTest {
101 var clauseWithEquivalence = new ArrayList<Literal>(clause); 100 var clauseWithEquivalence = new ArrayList<Literal>(clause);
102 clauseWithEquivalence.add(r.isEquivalent(p)); 101 clauseWithEquivalence.add(r.isEquivalent(p));
103 var builder = Dnf.builder().clause(clauseWithEquivalence); 102 var builder = Dnf.builder().clause(clauseWithEquivalence);
104 assertThrows(IllegalArgumentException.class, builder::build); 103 assertThrows(InvalidClauseException.class, builder::build);
105 } 104 }
106 105
107 static Stream<Arguments> clausesNotBindingVariable() { 106 static Stream<Arguments> clausesNotBindingVariable() {
@@ -119,7 +118,7 @@ class VariableDirectionTest {
119 @MethodSource("literalsWithPrivateVariable") 118 @MethodSource("literalsWithPrivateVariable")
120 void unboundTwicePrivateVariableTest(Literal literal) { 119 void unboundTwicePrivateVariableTest(Literal literal) {
121 var builder = Dnf.builder().clause(not(personView.call(p)), literal); 120 var builder = Dnf.builder().clause(not(personView.call(p)), literal);
122 assertThrows(IllegalArgumentException.class, builder::build); 121 assertThrows(InvalidClauseException.class, builder::build);
123 } 122 }
124 123
125 @ParameterizedTest 124 @ParameterizedTest
@@ -127,7 +126,7 @@ class VariableDirectionTest {
127 void unboundTwiceByEquivalencePrivateVariableTest(Literal literal) { 126 void unboundTwiceByEquivalencePrivateVariableTest(Literal literal) {
128 var r = Variable.of("r"); 127 var r = Variable.of("r");
129 var builder = Dnf.builder().clause(not(personView.call(r)), r.isEquivalent(p), literal); 128 var builder = Dnf.builder().clause(not(personView.call(r)), r.isEquivalent(p), literal);
130 assertThrows(IllegalArgumentException.class, builder::build); 129 assertThrows(InvalidClauseException.class, builder::build);
131 } 130 }
132 131
133 static Stream<Arguments> literalsWithPrivateVariable() { 132 static Stream<Arguments> literalsWithPrivateVariable() {
@@ -160,7 +159,7 @@ class VariableDirectionTest {
160 @MethodSource("literalsWithRequiredVariableInput") 159 @MethodSource("literalsWithRequiredVariableInput")
161 void unboundPrivateVariableTest(Literal literal) { 160 void unboundPrivateVariableTest(Literal literal) {
162 var builder = Dnf.builder().clause(literal); 161 var builder = Dnf.builder().clause(literal);
163 assertThrows(IllegalArgumentException.class, builder::build); 162 assertThrows(InvalidClauseException.class, builder::build);
164 } 163 }
165 164
166 @ParameterizedTest 165 @ParameterizedTest
@@ -246,182 +245,6 @@ class VariableDirectionTest {
246 ); 245 );
247 } 246 }
248 247
249 @ParameterizedTest
250 @MethodSource("clausesWithDataVariableInput")
251 void unboundOutDataVariableTest(List<? extends Literal> clause) {
252 var builder = Dnf.builder().parameter(x, ParameterDirection.OUT).clause(clause);
253 assertThrows(IllegalArgumentException.class, builder::build);
254 }
255
256 @ParameterizedTest
257 @MethodSource("clausesWithDataVariableInput")
258 void unboundInDataVariableTest(List<? extends Literal> clause) {
259 var builder = Dnf.builder().parameter(x, ParameterDirection.IN).clause(clause);
260 var dnf = assertDoesNotThrow(builder::build);
261 var clauses = dnf.getClauses();
262 if (clauses.size() > 0) {
263 assertThat(clauses.get(0).positiveVariables(), hasItem(x));
264 }
265 }
266
267 @ParameterizedTest
268 @MethodSource("clausesWithDataVariableInput")
269 void boundPrivateDataVariableTest(List<? extends Literal> clause) {
270 var clauseWithBinding = new ArrayList<Literal>(clause);
271 clauseWithBinding.add(x.assign(constant(27)));
272 var builder = Dnf.builder().clause(clauseWithBinding);
273 var dnf = assertDoesNotThrow(builder::build);
274 var clauses = dnf.getClauses();
275 if (clauses.size() > 0) {
276 assertThat(clauses.get(0).positiveVariables(), hasItem(x));
277 }
278 }
279
280 static Stream<Arguments> clausesWithDataVariableInput() {
281 return Stream.concat(
282 clausesNotBindingDataVariable(),
283 literalToClauseArgumentStream(literalsWithRequiredDataVariableInput())
284 );
285 }
286
287 @ParameterizedTest
288 @MethodSource("clausesNotBindingDataVariable")
289 void unboundPrivateDataVariableTest(List<? extends Literal> clause) {
290 var builder = Dnf.builder().clause(clause);
291 var dnf = assertDoesNotThrow(builder::build);
292 var clauses = dnf.getClauses();
293 if (clauses.size() > 0) {
294 assertThat(clauses.get(0).positiveVariables(), not(hasItem(x)));
295 }
296 }
297
298 static Stream<Arguments> clausesNotBindingDataVariable() {
299 return Stream.concat(
300 Stream.of(
301 Arguments.of(List.of()),
302 Arguments.of(List.of(BooleanLiteral.TRUE)),
303 Arguments.of(List.of(BooleanLiteral.FALSE))
304 ),
305 literalToClauseArgumentStream(literalsWithPrivateDataVariable())
306 );
307 }
308
309 @ParameterizedTest
310 @MethodSource("literalsWithPrivateDataVariable")
311 void unboundTwicePrivateDataVariableTest(Literal literal) {
312 var builder = Dnf.builder().clause(not(ageView.call(p, x)), literal);
313 assertThrows(IllegalArgumentException.class, builder::build);
314 }
315
316 static Stream<Arguments> literalsWithPrivateDataVariable() {
317 var dnfWithOutput = Dnf.builder("WithDataOutput")
318 .parameter(y, ParameterDirection.OUT)
319 .parameter(q, ParameterDirection.OUT)
320 .clause(ageView.call(q, y))
321 .build();
322
323 return Stream.of(
324 Arguments.of(not(ageView.call(q, x))),
325 Arguments.of(y.assign(ageView.count(q, x))),
326 Arguments.of(not(dnfWithOutput.call(x, q)))
327 );
328 }
329
330 @ParameterizedTest
331 @MethodSource("literalsWithRequiredDataVariableInput")
332 void unboundPrivateDataVariableTest(Literal literal) {
333 var builder = Dnf.builder().clause(literal);
334 assertThrows(IllegalArgumentException.class, builder::build);
335 }
336
337 static Stream<Arguments> literalsWithRequiredDataVariableInput() {
338 var dnfWithInput = Dnf.builder("WithDataInput")
339 .parameter(y, ParameterDirection.IN)
340 .parameter(q, ParameterDirection.OUT)
341 .clause(ageView.call(q, x))
342 .build();
343 // We are passing {@code y} to the parameter named {@code right} of {@code greaterEq}.
344 @SuppressWarnings("SuspiciousNameCombination")
345 var dnfWithInputToAggregate = Dnf.builder("WithDataInputToAggregate")
346 .parameter(y, ParameterDirection.IN)
347 .parameter(q, ParameterDirection.OUT)
348 .parameter(x, ParameterDirection.OUT)
349 .clause(
350 friendView.call(p, q),
351 ageView.call(q, x),
352 assume(greaterEq(x, y))
353 )
354 .build();
355
356 return Stream.of(
357 Arguments.of(dnfWithInput.call(x, q)),
358 Arguments.of(not(dnfWithInput.call(x, q))),
359 Arguments.of(y.assign(dnfWithInput.count(x, q))),
360 Arguments.of(y.assign(dnfWithInputToAggregate.aggregateBy(z, INT_SUM, x, q, z)))
361 );
362 }
363
364 @ParameterizedTest
365 @MethodSource("literalsWithDataVariableOutput")
366 void boundDataParameterTest(Literal literal) {
367 var builder = Dnf.builder().parameter(x, ParameterDirection.OUT).clause(literal);
368 var dnf = assertDoesNotThrow(builder::build);
369 assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(x));
370 }
371
372 @ParameterizedTest
373 @MethodSource("literalsWithDataVariableOutput")
374 void boundTwiceDataParameterTest(Literal literal) {
375 var builder = Dnf.builder().parameter(x, ParameterDirection.IN).clause(literal);
376 assertThrows(IllegalArgumentException.class, builder::build);
377 }
378
379 @ParameterizedTest
380 @MethodSource("literalsWithDataVariableOutput")
381 void boundPrivateDataVariableOutputTest(Literal literal) {
382 var dnfWithInput = Dnf.builder("WithInput")
383 .parameter(x, ParameterDirection.IN)
384 .clause(assume(greaterEq(x, constant(24))))
385 .build();
386 var builder = Dnf.builder().clause(dnfWithInput.call(x), literal);
387 var dnf = assertDoesNotThrow(builder::build);
388 assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(x));
389 }
390
391 @ParameterizedTest
392 @MethodSource("literalsWithDataVariableOutput")
393 void boundTwicePrivateDataVariableOutputTest(Literal literal) {
394 var builder = Dnf.builder().clause(x.assign(constant(27)), literal);
395 assertThrows(IllegalArgumentException.class, builder::build);
396 }
397
398 static Stream<Arguments> literalsWithDataVariableOutput() {
399 var dnfWithOutput = Dnf.builder("WithOutput")
400 .parameter(q, ParameterDirection.OUT)
401 .clause(personView.call(q))
402 .build();
403 var dnfWithDataOutput = Dnf.builder("WithDataOutput")
404 .parameter(y, ParameterDirection.OUT)
405 .parameter(q, ParameterDirection.OUT)
406 .clause(ageView.call(q, y))
407 .build();
408 var dnfWithOutputToAggregate = Dnf.builder("WithDataOutputToAggregate")
409 .parameter(q, ParameterDirection.OUT)
410 .parameter(y, ParameterDirection.OUT)
411 .clause(ageView.call(q, y))
412 .build();
413
414 return Stream.of(
415 Arguments.of(x.assign(constant(24))),
416 Arguments.of(ageView.call(q, x)),
417 Arguments.of(x.assign(personView.count(q))),
418 Arguments.of(x.assign(ageView.aggregate(INT_SUM, q))),
419 Arguments.of(dnfWithDataOutput.call(x, q)),
420 Arguments.of(x.assign(dnfWithOutput.count(q))),
421 Arguments.of(x.assign(dnfWithOutputToAggregate.aggregateBy(z, INT_SUM, q, z)))
422 );
423 }
424
425 private static Stream<Arguments> literalToClauseArgumentStream(Stream<Arguments> literalArgumentsStream) { 248 private static Stream<Arguments> literalToClauseArgumentStream(Stream<Arguments> literalArgumentsStream) {
426 return literalArgumentsStream.map(arguments -> Arguments.of(List.of(arguments.get()[0]))); 249 return literalArgumentsStream.map(arguments -> Arguments.of(List.of(arguments.get()[0])));
427 } 250 }
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/AggregationLiteralTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/AggregationLiteralTest.java
index 35910e08..ddd57e96 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/AggregationLiteralTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/AggregationLiteralTest.java
@@ -7,15 +7,16 @@ package tools.refinery.store.query.literal;
7 7
8import org.junit.jupiter.api.Test; 8import org.junit.jupiter.api.Test;
9import tools.refinery.store.query.Constraint; 9import tools.refinery.store.query.Constraint;
10import tools.refinery.store.query.InvalidQueryException;
10import tools.refinery.store.query.dnf.Dnf; 11import tools.refinery.store.query.dnf.Dnf;
12import tools.refinery.store.query.dnf.InvalidClauseException;
11import tools.refinery.store.query.term.*; 13import tools.refinery.store.query.term.*;
12 14
13import java.util.List; 15import java.util.List;
14import java.util.Set; 16import java.util.Set;
15 17
16import static org.hamcrest.MatcherAssert.assertThat; 18import static org.hamcrest.MatcherAssert.assertThat;
17import static org.hamcrest.Matchers.containsInAnyOrder; 19import static org.hamcrest.Matchers.*;
18import static org.hamcrest.Matchers.empty;
19import static org.junit.jupiter.api.Assertions.assertAll; 20import static org.junit.jupiter.api.Assertions.assertAll;
20import static org.junit.jupiter.api.Assertions.assertThrows; 21import static org.junit.jupiter.api.Assertions.assertThrows;
21import static tools.refinery.store.query.literal.Literals.not; 22import static tools.refinery.store.query.literal.Literals.not;
@@ -57,13 +58,13 @@ class AggregationLiteralTest {
57 @Test 58 @Test
58 void missingAggregationVariableTest() { 59 void missingAggregationVariableTest() {
59 var aggregation = fakeConstraint.aggregateBy(y, INT_SUM, p, z); 60 var aggregation = fakeConstraint.aggregateBy(y, INT_SUM, p, z);
60 assertThrows(IllegalArgumentException.class, () -> x.assign(aggregation)); 61 assertThrows(InvalidQueryException.class, () -> x.assign(aggregation));
61 } 62 }
62 63
63 @Test 64 @Test
64 void circularAggregationVariableTest() { 65 void circularAggregationVariableTest() {
65 var aggregation = fakeConstraint.aggregateBy(x, INT_SUM, p, x); 66 var aggregation = fakeConstraint.aggregateBy(x, INT_SUM, p, x);
66 assertThrows(IllegalArgumentException.class, () -> x.assign(aggregation)); 67 assertThrows(InvalidQueryException.class, () -> x.assign(aggregation));
67 } 68 }
68 69
69 @Test 70 @Test
@@ -73,7 +74,7 @@ class AggregationLiteralTest {
73 not(fakeConstraint.call(p, y)), 74 not(fakeConstraint.call(p, y)),
74 x.assign(fakeConstraint.aggregateBy(y, INT_SUM, p, y)) 75 x.assign(fakeConstraint.aggregateBy(y, INT_SUM, p, y))
75 ); 76 );
76 assertThrows(IllegalArgumentException.class, builder::build); 77 assertThrows(InvalidClauseException.class, builder::build);
77 } 78 }
78 79
79 @Test 80 @Test
@@ -83,6 +84,6 @@ class AggregationLiteralTest {
83 y.assign(constant(27)), 84 y.assign(constant(27)),
84 x.assign(fakeConstraint.aggregateBy(y, INT_SUM, p, y)) 85 x.assign(fakeConstraint.aggregateBy(y, INT_SUM, p, y))
85 ); 86 );
86 assertThrows(IllegalArgumentException.class, builder::build); 87 assertThrows(InvalidClauseException.class, builder::build);
87 } 88 }
88} 89}
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/DuplicateDnfRemoverTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/DuplicateDnfRemoverTest.java
new file mode 100644
index 00000000..ebb24ab5
--- /dev/null
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/DuplicateDnfRemoverTest.java
@@ -0,0 +1,164 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.rewriter;
7
8import org.junit.jupiter.api.BeforeEach;
9import org.junit.jupiter.api.Test;
10import tools.refinery.store.query.dnf.Query;
11import tools.refinery.store.query.literal.AbstractCallLiteral;
12import tools.refinery.store.query.literal.Reduction;
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;
17
18import java.util.List;
19
20import static org.hamcrest.MatcherAssert.assertThat;
21import static org.hamcrest.Matchers.is;
22import static org.hamcrest.Matchers.not;
23import static tools.refinery.store.query.literal.Literals.not;
24
25class DuplicateDnfRemoverTest {
26 private final static Symbol<Boolean> friend = Symbol.of("friend", 2);
27 private final static AnySymbolView friendView = new KeyOnlyView<>(friend);
28
29 private DuplicateDnfRemover sut;
30
31 @BeforeEach
32 void beforeEach() {
33 sut = new DuplicateDnfRemover();
34 }
35
36 @Test
37 void removeDuplicateSimpleTest() {
38 var one = Query.of("One", (builder, x, y) -> builder.clause(
39 friendView.call(x, y),
40 friendView.call(y, x)
41 ));
42 var two = Query.of("Two", (builder, x, y) -> builder.clause(
43 friendView.call(x, y),
44 friendView.call(y, x)
45 ));
46
47 var oneResult = sut.rewrite(one);
48 var twoResult = sut.rewrite(two);
49
50 assertThat(oneResult, is(twoResult));
51 assertThat(one, is(oneResult));
52 }
53
54 @Test
55 void notDuplicateSimpleTest() {
56 var one = Query.of("One", (builder, x, y) -> builder.clause(
57 friendView.call(x, y),
58 friendView.call(y, x)
59 ));
60 var two = Query.of("Two", (builder, x, y) -> builder.clause((z) -> List.of(
61 friendView.call(x, y),
62 friendView.call(y, z)
63 )));
64
65 var oneResult = sut.rewrite(one);
66 var twoResult = sut.rewrite(two);
67
68 assertThat(one, is(oneResult));
69 assertThat(two, is(twoResult));
70 }
71
72 @Test
73 void removeDuplicateRecursiveTest() {
74 var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause(
75 friendView.call(x, y),
76 friendView.call(y, x)
77 ));
78 var one = Query.of("One", (builder, x) -> builder.clause(
79 oneSubQuery.call(x, Variable.of())
80 ));
81 var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause(
82 friendView.call(x, y),
83 friendView.call(y, x)
84 ));
85 var two = Query.of("Two", (builder, x) -> builder.clause(
86 twoSubQuery.call(x, Variable.of())
87 ));
88
89 var oneResult = sut.rewrite(one);
90 var twoResult = sut.rewrite(two);
91
92 assertThat(oneResult, is(twoResult));
93 assertThat(one, is(oneResult));
94 }
95
96 @Test
97 void notDuplicateRecursiveTest() {
98 var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause(
99 friendView.call(x, y),
100 friendView.call(y, x)
101 ));
102 var one = Query.of("One", (builder, x) -> builder.clause(
103 oneSubQuery.call(x, Variable.of())
104 ));
105 var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause(
106 friendView.call(x, y),
107 friendView.call(y, x)
108 ));
109 var two = Query.of("Two", (builder, x) -> builder.clause(
110 twoSubQuery.call(Variable.of(), x)
111 ));
112
113 var oneResult = sut.rewrite(one);
114 var twoResult = sut.rewrite(two);
115
116 assertThat(one, is(oneResult));
117 assertThat(oneResult, is(not(twoResult)));
118
119 var oneCall = (AbstractCallLiteral) oneResult.getDnf().getClauses().get(0).literals().get(0);
120 var twoCall = (AbstractCallLiteral) twoResult.getDnf().getClauses().get(0).literals().get(0);
121
122 assertThat(oneCall.getTarget(), is(twoCall.getTarget()));
123 }
124
125 @Test
126 void removeContradictionTest() {
127 var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause(
128 friendView.call(x, y),
129 friendView.call(y, x)
130 ));
131 var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause(
132 friendView.call(x, y),
133 friendView.call(y, x)
134 ));
135 var query = Query.of("Contradiction", (builder, x, y) -> builder.clause(
136 oneSubQuery.call(x, y),
137 not(twoSubQuery.call(x, y))
138 ));
139
140 var result = sut.rewrite(query);
141
142 assertThat(result.getDnf().getReduction(), is(Reduction.ALWAYS_FALSE));
143 }
144
145 @Test
146 void removeQuantifiedContradictionTest() {
147 var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause(
148 friendView.call(x, y),
149 friendView.call(y, x)
150 ));
151 var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause(
152 friendView.call(x, y),
153 friendView.call(y, x)
154 ));
155 var query = Query.of("Contradiction", (builder, x) -> builder.clause(
156 oneSubQuery.call(x, Variable.of()),
157 not(twoSubQuery.call(x, Variable.of()))
158 ));
159
160 var result = sut.rewrite(query);
161
162 assertThat(result.getDnf().getReduction(), is(Reduction.ALWAYS_FALSE));
163 }
164}
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java
new file mode 100644
index 00000000..ef0077e4
--- /dev/null
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java
@@ -0,0 +1,228 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.rewriter;
7
8import org.junit.jupiter.api.BeforeEach;
9import org.junit.jupiter.api.Test;
10import tools.refinery.store.query.dnf.Dnf;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.query.term.ParameterDirection;
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;
17
18import java.util.List;
19
20import static org.hamcrest.MatcherAssert.assertThat;
21import static org.hamcrest.Matchers.is;
22import static tools.refinery.store.query.literal.Literals.not;
23import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo;
24
25class InputParameterResolverTest {
26 private final static Symbol<Boolean> person = Symbol.of("Person", 1);
27 private final static Symbol<Boolean> friend = Symbol.of("friend", 2);
28 private final static AnySymbolView personView = new KeyOnlyView<>(person);
29 private final static AnySymbolView friendView = new KeyOnlyView<>(friend);
30
31 private InputParameterResolver sut;
32
33 @BeforeEach
34 void beforeEach() {
35 sut = new InputParameterResolver();
36 }
37
38 @Test
39 void inlineSingleClauseTest() {
40 var dnf = Dnf.of("SubQuery", builder -> {
41 var x = builder.parameter("x", ParameterDirection.OUT);
42 builder.clause(friendView.call(x, Variable.of()));
43 });
44 var query = Query.of("Actual", (builder, x) -> builder.clause(
45 dnf.call(x),
46 personView.call(x)
47 ));
48
49 var actual = sut.rewrite(query);
50
51 var expected = Query.of("Expected", (builder, x) -> builder.clause(
52 friendView.call(x, Variable.of()),
53 personView.call(x)
54 ));
55
56 assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf()));
57 }
58
59 @Test
60 void inlineSingleClauseWIthInputTest() {
61 var dnf = Dnf.of("SubQuery", builder -> {
62 var x = builder.parameter("x", ParameterDirection.IN);
63 builder.clause(not(friendView.call(x, Variable.of())));
64 });
65 var query = Query.of("Actual", (builder, x) -> builder.clause(
66 dnf.call(x),
67 personView.call(x)
68 ));
69
70 var actual = sut.rewrite(query);
71
72 var expected = Query.of("Expected", (builder, x) -> builder.clause(
73 personView.call(x),
74 not(friendView.call(x, Variable.of()))
75 ));
76
77 assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf()));
78 }
79
80 @Test
81 void singleLiteralDemandSetTest() {
82 var dnf = Dnf.of("SubQuery", builder -> {
83 var x = builder.parameter("x", ParameterDirection.IN);
84 builder.clause(not(friendView.call(x, Variable.of())));
85 builder.clause(not(friendView.call(Variable.of(), x)));
86 });
87 var query = Query.of("Actual", (builder, x) -> builder.clause(
88 dnf.call(x),
89 personView.call(x)
90 ));
91
92 var actual = sut.rewrite(query);
93
94 var expectedSubQuery = Dnf.of("ExpectedSubQuery", builder -> {
95 var x = builder.parameter("x", ParameterDirection.OUT);
96 builder.clause(
97 personView.call(x),
98 not(friendView.call(x, Variable.of()))
99 );
100 builder.clause(
101 personView.call(x),
102 not(friendView.call(Variable.of(), x))
103 );
104 });
105 var expected = Query.of("Expected", (builder, x) -> builder.clause(
106 personView.call(x),
107 expectedSubQuery.call(x)
108 ));
109
110 assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf()));
111 }
112
113 @Test
114 void multipleLiteralDemandSetTest() {
115 var dnf = Dnf.of("SubQuery", builder -> {
116 var x = builder.parameter("x", ParameterDirection.IN);
117 var y = builder.parameter("y", ParameterDirection.IN);
118 builder.clause(not(friendView.call(x, y)));
119 builder.clause(not(friendView.call(y, x)));
120 });
121 var query = Query.of("Actual", (builder, p1) -> builder.clause(p2 -> List.of(
122 not(dnf.call(p1, p2)),
123 personView.call(p1),
124 personView.call(p2)
125 )));
126
127 var actual = sut.rewrite(query);
128
129 var context = Dnf.of("Context", builder -> {
130 var x = builder.parameter("x", ParameterDirection.OUT);
131 var y = builder.parameter("y", ParameterDirection.OUT);
132 builder.clause(
133 personView.call(x),
134 personView.call(y)
135 );
136 });
137 var expectedSubQuery = Dnf.of("ExpectedSubQuery", builder -> {
138 var x = builder.parameter("x", ParameterDirection.OUT);
139 var y = builder.parameter("x", ParameterDirection.OUT);
140 builder.clause(
141 context.call(x, y),
142 not(friendView.call(x, y))
143 );
144 builder.clause(
145 context.call(x, y),
146 not(friendView.call(y, x))
147 );
148 });
149 var expected = Query.of("Expected", (builder, p1) -> builder.clause(p2 -> List.of(
150 context.call(p1, p2),
151 not(expectedSubQuery.call(p1, p2))
152 )));
153
154 assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf()));
155 }
156
157 @Test
158 void multipleParameterDemandSetTest() {
159 var dnf = Dnf.of("SubQuery", builder -> {
160 var x = builder.parameter("x", ParameterDirection.IN);
161 var y = builder.parameter("y", ParameterDirection.IN);
162 builder.clause(not(friendView.call(x, y)));
163 builder.clause(not(friendView.call(y, x)));
164 });
165 var query = Query.of("Actual", (builder, p1) -> builder.clause(
166 not(dnf.call(p1, p1)),
167 personView.call(p1)
168 ));
169
170 var actual = sut.rewrite(query);
171
172 var expectedSubQuery = Dnf.of("ExpectedSubQuery", builder -> {
173 var x = builder.parameter("x", ParameterDirection.OUT);
174 var y = builder.parameter("y", ParameterDirection.OUT);
175 builder.clause(
176 y.isEquivalent(x),
177 personView.call(x),
178 not(friendView.call(x, x))
179 );
180 });
181 var expected = Query.of("Expected", (builder, p1) -> builder.clause(
182 personView.call(p1),
183 not(expectedSubQuery.call(p1, p1))
184 ));
185
186 assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf()));
187 }
188
189 @Test
190 void eliminateDoubleNegationTest() {
191 var dnf = Dnf.of("SubQuery", builder -> {
192 var x = builder.parameter("x", ParameterDirection.IN);
193 builder.clause(not(friendView.call(x, Variable.of())));
194 });
195 var query = Query.of("Actual", (builder, p1) -> builder.clause(
196 personView.call(p1),
197 not(dnf.call(p1))
198 ));
199
200 var actual = sut.rewrite(query);
201
202 var expected = Query.of("Actual", (builder, p1) -> builder.clause(
203 personView.call(p1),
204 friendView.call(p1, Variable.of())
205 ));
206
207 assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf()));
208 }
209
210 @Test
211 void identityWhenNoWorkToDoTest() {
212 var dnf = Dnf.of("SubQuery", builder -> {
213 var x = builder.parameter("x", ParameterDirection.OUT);
214 builder.clause(
215 personView.call(x),
216 not(friendView.call(x, Variable.of()))
217 );
218 });
219 var query = Query.of("Actual", (builder, p1) -> builder.clause(
220 personView.call(p1),
221 not(dnf.call(p1))
222 ));
223
224 var actual = sut.rewrite(query);
225
226 assertThat(actual, is(query));
227 }
228}
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/term/TermSubstitutionTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/term/TermSubstitutionTest.java
index 1cbc101a..1fae2492 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/term/TermSubstitutionTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/term/TermSubstitutionTest.java
@@ -9,8 +9,8 @@ import org.junit.jupiter.api.Assertions;
9import org.junit.jupiter.params.ParameterizedTest; 9import org.junit.jupiter.params.ParameterizedTest;
10import org.junit.jupiter.params.provider.Arguments; 10import org.junit.jupiter.params.provider.Arguments;
11import org.junit.jupiter.params.provider.MethodSource; 11import org.junit.jupiter.params.provider.MethodSource;
12import tools.refinery.store.query.dnf.Dnf; 12import tools.refinery.store.query.equality.DnfEqualityChecker;
13import tools.refinery.store.query.equality.LiteralEqualityHelper; 13import tools.refinery.store.query.equality.SubstitutingLiteralEqualityHelper;
14import tools.refinery.store.query.substitution.Substitution; 14import tools.refinery.store.query.substitution.Substitution;
15import tools.refinery.store.query.term.bool.BoolTerms; 15import tools.refinery.store.query.term.bool.BoolTerms;
16import tools.refinery.store.query.term.int_.IntTerms; 16import tools.refinery.store.query.term.int_.IntTerms;
@@ -48,7 +48,7 @@ class TermSubstitutionTest {
48 void substitutionTest(AnyTerm term) { 48 void substitutionTest(AnyTerm term) {
49 var substitutedTerm1 = term.substitute(substitution); 49 var substitutedTerm1 = term.substitute(substitution);
50 Assertions.assertNotEquals(term, substitutedTerm1, "Original term is not equal to substituted term"); 50 Assertions.assertNotEquals(term, substitutedTerm1, "Original term is not equal to substituted term");
51 var helper = new LiteralEqualityHelper(Dnf::equals, List.of(), List.of()); 51 var helper = new SubstitutingLiteralEqualityHelper(DnfEqualityChecker.DEFAULT, List.of(), List.of());
52 Assertions.assertTrue(term.equalsWithSubstitution(helper, substitutedTerm1), "Terms are equal by helper"); 52 Assertions.assertTrue(term.equalsWithSubstitution(helper, substitutedTerm1), "Terms are equal by helper");
53 // The {@link #substitution} is its own inverse. 53 // The {@link #substitution} is its own inverse.
54 var substitutedTerm2 = substitutedTerm1.substitute(substitution); 54 var substitutedTerm2 = substitutedTerm1.substitute(substitution);
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/utils/OrderStatisticTreeTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/utils/OrderStatisticTreeTest.java
index cbb48603..0ac88ed7 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/utils/OrderStatisticTreeTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/utils/OrderStatisticTreeTest.java
@@ -2,7 +2,7 @@
2 * Copyright (c) 2021 Rodion Efremov 2 * Copyright (c) 2021 Rodion Efremov
3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/> 3 * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/>
4 * 4 *
5 * SPDX-License-Identifier: MIT OR EPL-2.0 5 * SPDX-License-Identifier: MIT
6 */ 6 */
7package tools.refinery.store.query.utils; 7package tools.refinery.store.query.utils;
8 8