diff options
author | 2023-06-17 02:10:30 +0200 | |
---|---|---|
committer | 2023-06-17 02:12:19 +0200 | |
commit | de351479641196a85b21875fcab4f6527779b2ff (patch) | |
tree | a792c9a8a7c41e3c1822d44dcf218a9ee135afd0 /subprojects/store-query/src/test/java | |
parent | refactor(query): structural equality matcher (diff) | |
download | refinery-de351479641196a85b21875fcab4f6527779b2ff.tar.gz refinery-de351479641196a85b21875fcab4f6527779b2ff.tar.zst refinery-de351479641196a85b21875fcab4f6527779b2ff.zip |
fix: further Dnf tests and fixes
Diffstat (limited to 'subprojects/store-query/src/test/java')
4 files changed, 722 insertions, 0 deletions
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 new file mode 100644 index 00000000..6d53f184 --- /dev/null +++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/TopologicalSortTest.java | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.dnf; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.store.query.term.NodeVariable; | ||
10 | import tools.refinery.store.query.term.ParameterDirection; | ||
11 | import tools.refinery.store.query.term.Variable; | ||
12 | import tools.refinery.store.query.view.AnySymbolView; | ||
13 | import tools.refinery.store.query.view.KeyOnlyView; | ||
14 | import tools.refinery.store.representation.Symbol; | ||
15 | |||
16 | import java.util.List; | ||
17 | |||
18 | import static org.hamcrest.MatcherAssert.assertThat; | ||
19 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
20 | import static tools.refinery.store.query.literal.Literals.not; | ||
21 | import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo; | ||
22 | |||
23 | class TopologicalSortTest { | ||
24 | private static final Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); | ||
25 | private static final AnySymbolView friendView = new KeyOnlyView<>(friend); | ||
26 | private static final Dnf example = Dnf.of("example", builder -> { | ||
27 | var a = builder.parameter("a", ParameterDirection.IN); | ||
28 | var b = builder.parameter("b", ParameterDirection.IN); | ||
29 | var c = builder.parameter("c", ParameterDirection.OUT); | ||
30 | var d = builder.parameter("d", ParameterDirection.OUT); | ||
31 | builder.clause( | ||
32 | friendView.call(a, b), | ||
33 | friendView.call(b, c), | ||
34 | friendView.call(c, d) | ||
35 | ); | ||
36 | }); | ||
37 | private static final NodeVariable p = Variable.of("p"); | ||
38 | private static final NodeVariable q = Variable.of("q"); | ||
39 | private static final NodeVariable r = Variable.of("r"); | ||
40 | private static final NodeVariable s = Variable.of("s"); | ||
41 | private static final NodeVariable t = Variable.of("t"); | ||
42 | |||
43 | @Test | ||
44 | void topologicalSortTest() { | ||
45 | var actual = Dnf.builder("Actual") | ||
46 | .parameter(p, ParameterDirection.IN) | ||
47 | .parameter(q, ParameterDirection.OUT) | ||
48 | .clause( | ||
49 | not(friendView.call(p, q)), | ||
50 | example.call(p, q, r, s), | ||
51 | example.call(r, t, q, s), | ||
52 | friendView.call(r, t) | ||
53 | ) | ||
54 | .build(); | ||
55 | |||
56 | assertThat(actual, structurallyEqualTo( | ||
57 | List.of( | ||
58 | new SymbolicParameter(p, ParameterDirection.IN), | ||
59 | new SymbolicParameter(q, ParameterDirection.OUT) | ||
60 | ), | ||
61 | List.of( | ||
62 | List.of( | ||
63 | friendView.call(r, t), | ||
64 | example.call(r, t, q, s), | ||
65 | not(friendView.call(p, q)), | ||
66 | example.call(p, q, r, s) | ||
67 | ) | ||
68 | ) | ||
69 | )); | ||
70 | } | ||
71 | |||
72 | @Test | ||
73 | void missingInputTest() { | ||
74 | var builder = Dnf.builder("Actual") | ||
75 | .parameter(p, ParameterDirection.OUT) | ||
76 | .parameter(q, ParameterDirection.OUT) | ||
77 | .clause( | ||
78 | not(friendView.call(p, q)), | ||
79 | example.call(p, q, r, s), | ||
80 | example.call(r, t, q, s), | ||
81 | friendView.call(r, t) | ||
82 | ); | ||
83 | assertThrows(IllegalArgumentException.class, builder::build); | ||
84 | } | ||
85 | |||
86 | @Test | ||
87 | void missingVariableTest() { | ||
88 | var builder = Dnf.builder("Actual") | ||
89 | .parameter(p, ParameterDirection.IN) | ||
90 | .parameter(q, ParameterDirection.OUT) | ||
91 | .clause( | ||
92 | not(friendView.call(p, q)), | ||
93 | example.call(p, q, r, s), | ||
94 | example.call(r, t, q, s) | ||
95 | ); | ||
96 | assertThrows(IllegalArgumentException.class, builder::build); | ||
97 | } | ||
98 | |||
99 | @Test | ||
100 | void circularDependencyTest() { | ||
101 | var builder = Dnf.builder("Actual") | ||
102 | .parameter(p, ParameterDirection.IN) | ||
103 | .parameter(q, ParameterDirection.OUT) | ||
104 | .clause( | ||
105 | not(friendView.call(p, q)), | ||
106 | example.call(p, q, r, s), | ||
107 | example.call(r, t, q, s), | ||
108 | example.call(p, q, r, t) | ||
109 | ); | ||
110 | assertThrows(IllegalArgumentException.class, builder::build); | ||
111 | } | ||
112 | } | ||
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 new file mode 100644 index 00000000..0a44664e --- /dev/null +++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/dnf/VariableDirectionTest.java | |||
@@ -0,0 +1,428 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.dnf; | ||
7 | |||
8 | import org.junit.jupiter.params.ParameterizedTest; | ||
9 | import org.junit.jupiter.params.provider.Arguments; | ||
10 | import org.junit.jupiter.params.provider.MethodSource; | ||
11 | import tools.refinery.store.query.literal.BooleanLiteral; | ||
12 | import tools.refinery.store.query.literal.Literal; | ||
13 | import tools.refinery.store.query.term.DataVariable; | ||
14 | import tools.refinery.store.query.term.NodeVariable; | ||
15 | import tools.refinery.store.query.term.ParameterDirection; | ||
16 | import tools.refinery.store.query.term.Variable; | ||
17 | import tools.refinery.store.query.view.AnySymbolView; | ||
18 | import tools.refinery.store.query.view.FunctionView; | ||
19 | import tools.refinery.store.query.view.KeyOnlyView; | ||
20 | import tools.refinery.store.representation.Symbol; | ||
21 | |||
22 | import java.util.ArrayList; | ||
23 | import java.util.List; | ||
24 | import java.util.stream.Stream; | ||
25 | |||
26 | import static org.hamcrest.MatcherAssert.assertThat; | ||
27 | import static org.hamcrest.Matchers.hasItem; | ||
28 | import static org.hamcrest.Matchers.not; | ||
29 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
30 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
31 | import static tools.refinery.store.query.literal.Literals.assume; | ||
32 | import static tools.refinery.store.query.literal.Literals.not; | ||
33 | import static tools.refinery.store.query.term.int_.IntTerms.*; | ||
34 | |||
35 | class VariableDirectionTest { | ||
36 | private static final Symbol<Boolean> person = new Symbol<>("Person", 1, Boolean.class, false); | ||
37 | private static final Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); | ||
38 | private static final Symbol<Integer> age = new Symbol<>("age", 1, Integer.class, null); | ||
39 | private static final AnySymbolView personView = new KeyOnlyView<>(person); | ||
40 | private static final AnySymbolView friendView = new KeyOnlyView<>(friend); | ||
41 | private static final AnySymbolView ageView = new FunctionView<>(age); | ||
42 | private static final NodeVariable p = Variable.of("p"); | ||
43 | private static final NodeVariable q = Variable.of("q"); | ||
44 | private static final DataVariable<Integer> x = Variable.of("x", Integer.class); | ||
45 | private static final DataVariable<Integer> y = Variable.of("y", Integer.class); | ||
46 | private static final DataVariable<Integer> z = Variable.of("z", Integer.class); | ||
47 | |||
48 | @ParameterizedTest | ||
49 | @MethodSource("clausesWithVariableInput") | ||
50 | void unboundOutVariableTest(List<? extends Literal> clause) { | ||
51 | var builder = Dnf.builder().parameter(p, ParameterDirection.OUT).clause(clause); | ||
52 | assertThrows(IllegalArgumentException.class, builder::build); | ||
53 | } | ||
54 | |||
55 | @ParameterizedTest | ||
56 | @MethodSource("clausesWithVariableInput") | ||
57 | void unboundInVariableTest(List<? extends Literal> clause) { | ||
58 | var builder = Dnf.builder().parameter(p, ParameterDirection.IN).clause(clause); | ||
59 | var dnf = assertDoesNotThrow(builder::build); | ||
60 | var clauses = dnf.getClauses(); | ||
61 | if (clauses.size() > 0) { | ||
62 | assertThat(clauses.get(0).positiveVariables(), hasItem(p)); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | @ParameterizedTest | ||
67 | @MethodSource("clausesWithVariableInput") | ||
68 | void boundPrivateVariableTest(List<? extends Literal> clause) { | ||
69 | var clauseWithBinding = new ArrayList<Literal>(clause); | ||
70 | clauseWithBinding.add(personView.call(p)); | ||
71 | var builder = Dnf.builder().clause(clauseWithBinding); | ||
72 | var dnf = assertDoesNotThrow(builder::build); | ||
73 | var clauses = dnf.getClauses(); | ||
74 | if (clauses.size() > 0) { | ||
75 | assertThat(clauses.get(0).positiveVariables(), hasItem(p)); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | static Stream<Arguments> clausesWithVariableInput() { | ||
80 | return Stream.concat( | ||
81 | clausesNotBindingVariable(), | ||
82 | literalToClauseArgumentStream(literalsWithRequiredVariableInput()) | ||
83 | ); | ||
84 | } | ||
85 | |||
86 | @ParameterizedTest | ||
87 | @MethodSource("clausesNotBindingVariable") | ||
88 | void unboundPrivateVariableTest(List<? extends Literal> clause) { | ||
89 | var builder = Dnf.builder().clause(clause); | ||
90 | var dnf = assertDoesNotThrow(builder::build); | ||
91 | var clauses = dnf.getClauses(); | ||
92 | if (clauses.size() > 0) { | ||
93 | assertThat(clauses.get(0).positiveVariables(), not(hasItem(p))); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | @ParameterizedTest | ||
98 | @MethodSource("clausesNotBindingVariable") | ||
99 | void unboundByEquivalencePrivateVariableTest(List<? extends Literal> clause) { | ||
100 | var r = Variable.of("r"); | ||
101 | var clauseWithEquivalence = new ArrayList<Literal>(clause); | ||
102 | clauseWithEquivalence.add(r.isEquivalent(p)); | ||
103 | var builder = Dnf.builder().clause(clauseWithEquivalence); | ||
104 | assertThrows(IllegalArgumentException.class, builder::build); | ||
105 | } | ||
106 | |||
107 | static Stream<Arguments> clausesNotBindingVariable() { | ||
108 | return Stream.concat( | ||
109 | Stream.of( | ||
110 | Arguments.of(List.of()), | ||
111 | Arguments.of(List.of(BooleanLiteral.TRUE)), | ||
112 | Arguments.of(List.of(BooleanLiteral.FALSE)) | ||
113 | ), | ||
114 | literalToClauseArgumentStream(literalsWithPrivateVariable()) | ||
115 | ); | ||
116 | } | ||
117 | |||
118 | @ParameterizedTest | ||
119 | @MethodSource("literalsWithPrivateVariable") | ||
120 | void unboundTwicePrivateVariableTest(Literal literal) { | ||
121 | var builder = Dnf.builder().clause(not(personView.call(p)), literal); | ||
122 | assertThrows(IllegalArgumentException.class, builder::build); | ||
123 | } | ||
124 | |||
125 | @ParameterizedTest | ||
126 | @MethodSource("literalsWithPrivateVariable") | ||
127 | void unboundTwiceByEquivalencePrivateVariableTest(Literal literal) { | ||
128 | var r = Variable.of("r"); | ||
129 | var builder = Dnf.builder().clause(not(personView.call(r)), r.isEquivalent(p), literal); | ||
130 | assertThrows(IllegalArgumentException.class, builder::build); | ||
131 | } | ||
132 | |||
133 | static Stream<Arguments> literalsWithPrivateVariable() { | ||
134 | var dnfWithOutput = Dnf.builder("WithOutput") | ||
135 | .parameter(p, ParameterDirection.OUT) | ||
136 | .parameter(q, ParameterDirection.OUT) | ||
137 | .clause(friendView.call(p, q)) | ||
138 | .build(); | ||
139 | var dnfWithOutputToAggregate = Dnf.builder("WithOutputToAggregate") | ||
140 | .parameter(p, ParameterDirection.OUT) | ||
141 | .parameter(q, ParameterDirection.OUT) | ||
142 | .parameter(x, ParameterDirection.OUT) | ||
143 | .clause( | ||
144 | friendView.call(p, q), | ||
145 | ageView.call(q, x) | ||
146 | ) | ||
147 | .build(); | ||
148 | |||
149 | return Stream.of( | ||
150 | Arguments.of(not(friendView.call(p, q))), | ||
151 | Arguments.of(y.assign(friendView.count(p, q))), | ||
152 | Arguments.of(y.assign(ageView.aggregate(z, INT_SUM, p, z))), | ||
153 | Arguments.of(not(dnfWithOutput.call(p, q))), | ||
154 | Arguments.of(y.assign(dnfWithOutput.count(p, q))), | ||
155 | Arguments.of(y.assign(dnfWithOutputToAggregate.aggregate(z, INT_SUM, p, q, z))) | ||
156 | ); | ||
157 | } | ||
158 | |||
159 | @ParameterizedTest | ||
160 | @MethodSource("literalsWithRequiredVariableInput") | ||
161 | void unboundPrivateVariableTest(Literal literal) { | ||
162 | var builder = Dnf.builder().clause(literal); | ||
163 | assertThrows(IllegalArgumentException.class, builder::build); | ||
164 | } | ||
165 | |||
166 | @ParameterizedTest | ||
167 | @MethodSource("literalsWithRequiredVariableInput") | ||
168 | void boundPrivateVariableInputTest(Literal literal) { | ||
169 | var builder = Dnf.builder().clause(personView.call(p), literal); | ||
170 | var dnf = assertDoesNotThrow(builder::build); | ||
171 | assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(p)); | ||
172 | } | ||
173 | |||
174 | static Stream<Arguments> literalsWithRequiredVariableInput() { | ||
175 | var dnfWithInput = Dnf.builder("WithInput") | ||
176 | .parameter(p, ParameterDirection.IN) | ||
177 | .parameter(q, ParameterDirection.OUT) | ||
178 | .clause(friendView.call(p, q)).build(); | ||
179 | var dnfWithInputToAggregate = Dnf.builder("WithInputToAggregate") | ||
180 | .parameter(p, ParameterDirection.IN) | ||
181 | .parameter(q, ParameterDirection.OUT) | ||
182 | .parameter(x, ParameterDirection.OUT) | ||
183 | .clause( | ||
184 | friendView.call(p, q), | ||
185 | ageView.call(q, x) | ||
186 | ).build(); | ||
187 | |||
188 | return Stream.of( | ||
189 | Arguments.of(dnfWithInput.call(p, q)), | ||
190 | Arguments.of(dnfWithInput.call(p, p)), | ||
191 | Arguments.of(not(dnfWithInput.call(p, q))), | ||
192 | Arguments.of(not(dnfWithInput.call(p, p))), | ||
193 | Arguments.of(y.assign(dnfWithInput.count(p, q))), | ||
194 | Arguments.of(y.assign(dnfWithInput.count(p, p))), | ||
195 | Arguments.of(y.assign(dnfWithInputToAggregate.aggregate(z, INT_SUM, p, q, z))), | ||
196 | Arguments.of(y.assign(dnfWithInputToAggregate.aggregate(z, INT_SUM, p, p, z))) | ||
197 | ); | ||
198 | } | ||
199 | |||
200 | @ParameterizedTest | ||
201 | @MethodSource("literalsWithVariableOutput") | ||
202 | void boundParameterTest(Literal literal) { | ||
203 | var builder = Dnf.builder().parameter(p, ParameterDirection.OUT).clause(literal); | ||
204 | var dnf = assertDoesNotThrow(builder::build); | ||
205 | assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(p)); | ||
206 | } | ||
207 | |||
208 | @ParameterizedTest | ||
209 | @MethodSource("literalsWithVariableOutput") | ||
210 | void boundTwiceParameterTest(Literal literal) { | ||
211 | var builder = Dnf.builder().parameter(p, ParameterDirection.IN).clause(literal); | ||
212 | var dnf = assertDoesNotThrow(builder::build); | ||
213 | assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(p)); | ||
214 | } | ||
215 | |||
216 | @ParameterizedTest | ||
217 | @MethodSource("literalsWithVariableOutput") | ||
218 | void boundPrivateVariableOutputTest(Literal literal) { | ||
219 | var dnfWithInput = Dnf.builder("WithInput") | ||
220 | .parameter(p, ParameterDirection.IN) | ||
221 | .clause(personView.call(p)) | ||
222 | .build(); | ||
223 | var builder = Dnf.builder().clause(dnfWithInput.call(p), literal); | ||
224 | var dnf = assertDoesNotThrow(builder::build); | ||
225 | assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(p)); | ||
226 | } | ||
227 | |||
228 | @ParameterizedTest | ||
229 | @MethodSource("literalsWithVariableOutput") | ||
230 | void boundTwicePrivateVariableOutputTest(Literal literal) { | ||
231 | var builder = Dnf.builder().clause(personView.call(p), literal); | ||
232 | var dnf = assertDoesNotThrow(builder::build); | ||
233 | assertThat(dnf.getClauses().get(0).positiveVariables(), hasItem(p)); | ||
234 | } | ||
235 | |||
236 | static Stream<Arguments> literalsWithVariableOutput() { | ||
237 | var dnfWithOutput = Dnf.builder("WithOutput") | ||
238 | .parameter(p, ParameterDirection.OUT) | ||
239 | .parameter(q, ParameterDirection.OUT) | ||
240 | .clause(friendView.call(p, q)) | ||
241 | .build(); | ||
242 | |||
243 | return Stream.of( | ||
244 | Arguments.of(friendView.call(p, q)), | ||
245 | Arguments.of(dnfWithOutput.call(p, q)) | ||
246 | ); | ||
247 | } | ||
248 | |||
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.aggregate(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(z, INT_SUM, q, z))), | ||
419 | Arguments.of(dnfWithDataOutput.call(x, q)), | ||
420 | Arguments.of(x.assign(dnfWithOutput.count(q))), | ||
421 | Arguments.of(x.assign(dnfWithOutputToAggregate.aggregate(z, INT_SUM, q, z))) | ||
422 | ); | ||
423 | } | ||
424 | |||
425 | private static Stream<Arguments> literalToClauseArgumentStream(Stream<Arguments> literalArgumentsStream) { | ||
426 | return literalArgumentsStream.map(arguments -> Arguments.of(List.of(arguments.get()[0]))); | ||
427 | } | ||
428 | } | ||
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 new file mode 100644 index 00000000..5293b273 --- /dev/null +++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/AggregationLiteralTest.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.literal; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.store.query.Constraint; | ||
10 | import tools.refinery.store.query.dnf.Dnf; | ||
11 | import tools.refinery.store.query.term.*; | ||
12 | |||
13 | import java.util.List; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import static org.hamcrest.MatcherAssert.assertThat; | ||
17 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
18 | import static org.hamcrest.Matchers.empty; | ||
19 | import static org.junit.jupiter.api.Assertions.assertAll; | ||
20 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
21 | import static tools.refinery.store.query.literal.Literals.not; | ||
22 | import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM; | ||
23 | import static tools.refinery.store.query.term.int_.IntTerms.constant; | ||
24 | |||
25 | class AggregationLiteralTest { | ||
26 | private static final NodeVariable p = Variable.of("p"); | ||
27 | private static final DataVariable<Integer> x = Variable.of("x", Integer.class); | ||
28 | private static final DataVariable<Integer> y = Variable.of("y", Integer.class); | ||
29 | private static final DataVariable<Integer> z = Variable.of("z", Integer.class); | ||
30 | private static final Constraint fakeConstraint = new Constraint() { | ||
31 | @Override | ||
32 | public String name() { | ||
33 | return getClass().getName(); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public List<Parameter> getParameters() { | ||
38 | return List.of( | ||
39 | new Parameter(null, ParameterDirection.OUT), | ||
40 | new Parameter(Integer.class, ParameterDirection.OUT) | ||
41 | ); | ||
42 | } | ||
43 | }; | ||
44 | |||
45 | @Test | ||
46 | void parameterDirectionTest() { | ||
47 | var literal = x.assign(fakeConstraint.aggregate(y, INT_SUM, p, y)); | ||
48 | assertAll( | ||
49 | () -> assertThat(literal.getOutputVariables(), containsInAnyOrder(x)), | ||
50 | () -> assertThat(literal.getInputVariables(Set.of()), empty()), | ||
51 | () -> assertThat(literal.getInputVariables(Set.of(p)), containsInAnyOrder(p)), | ||
52 | () -> assertThat(literal.getPrivateVariables(Set.of()), containsInAnyOrder(p, y)), | ||
53 | () -> assertThat(literal.getPrivateVariables(Set.of(p)), containsInAnyOrder(y)) | ||
54 | ); | ||
55 | } | ||
56 | |||
57 | @Test | ||
58 | void missingAggregationVariableTest() { | ||
59 | var aggregation = fakeConstraint.aggregate(y, INT_SUM, p, z); | ||
60 | assertThrows(IllegalArgumentException.class, () -> x.assign(aggregation)); | ||
61 | } | ||
62 | |||
63 | @Test | ||
64 | void circularAggregationVariableTest() { | ||
65 | var aggregation = fakeConstraint.aggregate(x, INT_SUM, p, x); | ||
66 | assertThrows(IllegalArgumentException.class, () -> x.assign(aggregation)); | ||
67 | } | ||
68 | |||
69 | @Test | ||
70 | void unboundTwiceVariableTest() { | ||
71 | var builder = Dnf.builder() | ||
72 | .clause( | ||
73 | not(fakeConstraint.call(p, y)), | ||
74 | x.assign(fakeConstraint.aggregate(y, INT_SUM, p, y)) | ||
75 | ); | ||
76 | assertThrows(IllegalArgumentException.class, builder::build); | ||
77 | } | ||
78 | |||
79 | @Test | ||
80 | void unboundBoundVariableTest() { | ||
81 | var builder = Dnf.builder() | ||
82 | .clause( | ||
83 | y.assign(constant(27)), | ||
84 | x.assign(fakeConstraint.aggregate(y, INT_SUM, p, y)) | ||
85 | ); | ||
86 | assertThrows(IllegalArgumentException.class, builder::build); | ||
87 | } | ||
88 | } | ||
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/CallLiteralTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/CallLiteralTest.java new file mode 100644 index 00000000..a01c6586 --- /dev/null +++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/literal/CallLiteralTest.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.query.literal; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.store.query.Constraint; | ||
10 | import tools.refinery.store.query.term.NodeVariable; | ||
11 | import tools.refinery.store.query.term.Parameter; | ||
12 | import tools.refinery.store.query.term.ParameterDirection; | ||
13 | import tools.refinery.store.query.term.Variable; | ||
14 | |||
15 | import java.util.List; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import static org.hamcrest.MatcherAssert.assertThat; | ||
19 | import static org.hamcrest.Matchers.containsInAnyOrder; | ||
20 | import static org.hamcrest.Matchers.empty; | ||
21 | import static org.junit.jupiter.api.Assertions.assertAll; | ||
22 | import static tools.refinery.store.query.literal.Literals.not; | ||
23 | |||
24 | class CallLiteralTest { | ||
25 | private static final NodeVariable p = Variable.of("p"); | ||
26 | private static final NodeVariable q = Variable.of("q"); | ||
27 | private static final NodeVariable r = Variable.of("r"); | ||
28 | private static final NodeVariable s = Variable.of("s"); | ||
29 | |||
30 | private static final Constraint fakeConstraint = new Constraint() { | ||
31 | @Override | ||
32 | public String name() { | ||
33 | return getClass().getName(); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public List<Parameter> getParameters() { | ||
38 | return List.of( | ||
39 | new Parameter(null, ParameterDirection.IN), | ||
40 | new Parameter(null, ParameterDirection.IN), | ||
41 | new Parameter(null, ParameterDirection.OUT), | ||
42 | new Parameter(null, ParameterDirection.OUT) | ||
43 | ); | ||
44 | } | ||
45 | }; | ||
46 | |||
47 | @Test | ||
48 | void notRepeatedPositiveDirectionTest() { | ||
49 | var literal = fakeConstraint.call(p, q, r, s); | ||
50 | assertAll( | ||
51 | () -> assertThat(literal.getOutputVariables(), containsInAnyOrder(r, s)), | ||
52 | () -> assertThat(literal.getInputVariables(Set.of()), containsInAnyOrder(p, q)), | ||
53 | () -> assertThat(literal.getInputVariables(Set.of(p, q, r)), containsInAnyOrder(p, q)), | ||
54 | () -> assertThat(literal.getPrivateVariables(Set.of()), empty()), | ||
55 | () -> assertThat(literal.getPrivateVariables(Set.of(p, q, r)), empty()) | ||
56 | ); | ||
57 | } | ||
58 | |||
59 | @Test | ||
60 | void notRepeatedNegativeDirectionTest() { | ||
61 | var literal = not(fakeConstraint.call(p, q, r, s)); | ||
62 | assertAll( | ||
63 | () -> assertThat(literal.getOutputVariables(), empty()), | ||
64 | () -> assertThat(literal.getInputVariables(Set.of()), containsInAnyOrder(p, q)), | ||
65 | () -> assertThat(literal.getInputVariables(Set.of(p, q, r)), containsInAnyOrder(p, q, r)), | ||
66 | () -> assertThat(literal.getPrivateVariables(Set.of()), containsInAnyOrder(r, s)), | ||
67 | () -> assertThat(literal.getPrivateVariables(Set.of(p, q, r)), containsInAnyOrder(s)) | ||
68 | ); | ||
69 | } | ||
70 | |||
71 | @Test | ||
72 | void repeatedPositiveDirectionTest() { | ||
73 | var literal = fakeConstraint.call(p, p, q, q); | ||
74 | assertAll( | ||
75 | () -> assertThat(literal.getOutputVariables(), containsInAnyOrder(q)), | ||
76 | () -> assertThat(literal.getInputVariables(Set.of()), containsInAnyOrder(p)), | ||
77 | () -> assertThat(literal.getInputVariables(Set.of(p, q)), containsInAnyOrder(p)), | ||
78 | () -> assertThat(literal.getPrivateVariables(Set.of()), empty()), | ||
79 | () -> assertThat(literal.getPrivateVariables(Set.of(p, q)), empty()) | ||
80 | ); | ||
81 | } | ||
82 | |||
83 | @Test | ||
84 | void repeatedNegativeDirectionTest() { | ||
85 | var literal = not(fakeConstraint.call(p, p, q, q)); | ||
86 | assertAll( | ||
87 | () -> assertThat(literal.getOutputVariables(), empty()), | ||
88 | () -> assertThat(literal.getInputVariables(Set.of()), containsInAnyOrder(p)), | ||
89 | () -> assertThat(literal.getInputVariables(Set.of(p, q)), containsInAnyOrder(p, q)), | ||
90 | () -> assertThat(literal.getPrivateVariables(Set.of()), containsInAnyOrder(q)), | ||
91 | () -> assertThat(literal.getPrivateVariables(Set.of(p, q)), empty()) | ||
92 | ); | ||
93 | } | ||
94 | } | ||