diff options
Diffstat (limited to 'subprojects/logic/src/test/java/tools/refinery')
28 files changed, 3351 insertions, 0 deletions
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfBuilderLiteralEliminationTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfBuilderLiteralEliminationTest.java new file mode 100644 index 00000000..d5a9ccad --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfBuilderLiteralEliminationTest.java | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.dnf; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import org.junit.jupiter.params.ParameterizedTest; | ||
10 | import org.junit.jupiter.params.provider.CsvSource; | ||
11 | import tools.refinery.logic.Constraint; | ||
12 | import tools.refinery.logic.literal.BooleanLiteral; | ||
13 | import tools.refinery.logic.term.NodeVariable; | ||
14 | import tools.refinery.logic.term.ParameterDirection; | ||
15 | import tools.refinery.logic.term.Variable; | ||
16 | import tools.refinery.logic.term.bool.BoolTerms; | ||
17 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
18 | |||
19 | import java.util.List; | ||
20 | |||
21 | import static org.hamcrest.MatcherAssert.assertThat; | ||
22 | import static tools.refinery.logic.literal.Literals.check; | ||
23 | import static tools.refinery.logic.literal.Literals.not; | ||
24 | import static tools.refinery.logic.tests.QueryMatchers.structurallyEqualTo; | ||
25 | |||
26 | class DnfBuilderLiteralEliminationTest { | ||
27 | private final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
28 | private final NodeVariable p = Variable.of("p"); | ||
29 | private final NodeVariable q = Variable.of("q"); | ||
30 | private final Dnf trueDnf = Dnf.builder().parameter(p, ParameterDirection.IN).clause().build(); | ||
31 | private final Dnf falseDnf = Dnf.builder().parameter(p).build(); | ||
32 | |||
33 | @Test | ||
34 | void eliminateTrueTest() { | ||
35 | var actual = Dnf.builder() | ||
36 | .parameters(p, q) | ||
37 | .clause(BooleanLiteral.TRUE, friendView.call(p, q)) | ||
38 | .build(); | ||
39 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
40 | |||
41 | assertThat(actual, structurallyEqualTo(expected)); | ||
42 | } | ||
43 | |||
44 | @Test | ||
45 | void eliminateTrueAssumptionTest() { | ||
46 | var actual = Dnf.builder() | ||
47 | .parameters(p, q) | ||
48 | .clause(check(BoolTerms.constant(true)), friendView.call(p, q)) | ||
49 | .build(); | ||
50 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
51 | |||
52 | assertThat(actual, structurallyEqualTo(expected)); | ||
53 | } | ||
54 | |||
55 | @Test | ||
56 | void eliminateFalseTest() { | ||
57 | var actual = Dnf.builder() | ||
58 | .parameters(p, q) | ||
59 | .clause(friendView.call(p, q)) | ||
60 | .clause(friendView.call(q, p), BooleanLiteral.FALSE) | ||
61 | .build(); | ||
62 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
63 | |||
64 | assertThat(actual, structurallyEqualTo(expected)); | ||
65 | } | ||
66 | |||
67 | @ParameterizedTest | ||
68 | @CsvSource(value = { | ||
69 | "false", | ||
70 | "null" | ||
71 | }, nullValues = "null") | ||
72 | void eliminateFalseAssumptionTest(Boolean value) { | ||
73 | var actual = Dnf.builder() | ||
74 | .parameters(p, q) | ||
75 | .clause(friendView.call(p, q)) | ||
76 | .clause(friendView.call(q, p), check(BoolTerms.constant(value))) | ||
77 | .build(); | ||
78 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
79 | |||
80 | assertThat(actual, structurallyEqualTo(expected)); | ||
81 | } | ||
82 | |||
83 | @Test | ||
84 | void alwaysTrueTest() { | ||
85 | var actual = Dnf.builder() | ||
86 | .parameters(List.of(p, q), ParameterDirection.IN) | ||
87 | .clause(friendView.call(p, q)) | ||
88 | .clause(BooleanLiteral.TRUE) | ||
89 | .build(); | ||
90 | var expected = Dnf.builder().parameters(List.of(p, q), ParameterDirection.IN).clause().build(); | ||
91 | |||
92 | assertThat(actual, structurallyEqualTo(expected)); | ||
93 | } | ||
94 | |||
95 | @Test | ||
96 | void alwaysFalseTest() { | ||
97 | var actual = Dnf.builder() | ||
98 | .parameters(p, q) | ||
99 | .clause(friendView.call(p, q), BooleanLiteral.FALSE) | ||
100 | .build(); | ||
101 | var expected = Dnf.builder().parameters(p, q).build(); | ||
102 | |||
103 | assertThat(actual, structurallyEqualTo(expected)); | ||
104 | } | ||
105 | |||
106 | @Test | ||
107 | void eliminateTrueDnfTest() { | ||
108 | var actual = Dnf.builder() | ||
109 | .parameters(p, q) | ||
110 | .clause(trueDnf.call(q), friendView.call(p, q)) | ||
111 | .build(); | ||
112 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
113 | |||
114 | assertThat(actual, structurallyEqualTo(expected)); | ||
115 | } | ||
116 | |||
117 | @Test | ||
118 | void eliminateFalseDnfTest() { | ||
119 | var actual = Dnf.builder() | ||
120 | .parameters(p, q) | ||
121 | .clause(friendView.call(p, q)) | ||
122 | .clause(friendView.call(q, p), falseDnf.call(q)) | ||
123 | .build(); | ||
124 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
125 | |||
126 | assertThat(actual, structurallyEqualTo(expected)); | ||
127 | } | ||
128 | |||
129 | @Test | ||
130 | void alwaysTrueDnfTest() { | ||
131 | var actual = Dnf.builder() | ||
132 | .parameters(List.of(p, q), ParameterDirection.IN) | ||
133 | .clause(friendView.call(p, q)) | ||
134 | .clause(trueDnf.call(q)) | ||
135 | .build(); | ||
136 | var expected = Dnf.builder().parameters(List.of(p, q), ParameterDirection.IN).clause().build(); | ||
137 | |||
138 | assertThat(actual, structurallyEqualTo(expected)); | ||
139 | } | ||
140 | |||
141 | @Test | ||
142 | void alwaysFalseDnfTest() { | ||
143 | var actual = Dnf.builder() | ||
144 | .parameters(p, q) | ||
145 | .clause(friendView.call(p, q), falseDnf.call(q)) | ||
146 | .build(); | ||
147 | var expected = Dnf.builder().parameters(p, q).build(); | ||
148 | |||
149 | assertThat(actual, structurallyEqualTo(expected)); | ||
150 | } | ||
151 | |||
152 | @Test | ||
153 | void eliminateNotFalseDnfTest() { | ||
154 | var actual = Dnf.builder() | ||
155 | .parameters(p, q) | ||
156 | .clause(not(falseDnf.call(q)), friendView.call(p, q)) | ||
157 | .build(); | ||
158 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
159 | |||
160 | assertThat(actual, structurallyEqualTo(expected)); | ||
161 | } | ||
162 | |||
163 | @Test | ||
164 | void eliminateNotTrueDnfTest() { | ||
165 | var actual = Dnf.builder() | ||
166 | .parameters(p, q) | ||
167 | .clause(friendView.call(p, q)) | ||
168 | .clause(friendView.call(q, p), not(trueDnf.call(q))) | ||
169 | .build(); | ||
170 | var expected = Dnf.builder().parameters(p, q).clause(friendView.call(p, q)).build(); | ||
171 | |||
172 | assertThat(actual, structurallyEqualTo(expected)); | ||
173 | } | ||
174 | |||
175 | @Test | ||
176 | void alwaysNotFalseDnfTest() { | ||
177 | var actual = Dnf.builder() | ||
178 | .parameters(List.of(p, q), ParameterDirection.IN) | ||
179 | .clause(friendView.call(p, q)) | ||
180 | .clause(not(falseDnf.call(q))) | ||
181 | .build(); | ||
182 | var expected = Dnf.builder().parameters(List.of(p, q), ParameterDirection.IN).clause().build(); | ||
183 | |||
184 | assertThat(actual, structurallyEqualTo(expected)); | ||
185 | } | ||
186 | |||
187 | @Test | ||
188 | void alwaysNotTrueDnfTest() { | ||
189 | var actual = Dnf.builder() | ||
190 | .parameters(p, q) | ||
191 | .clause(friendView.call(p, q), not(trueDnf.call(q))) | ||
192 | .build(); | ||
193 | var expected = Dnf.builder().parameters(p, q).build(); | ||
194 | |||
195 | assertThat(actual, structurallyEqualTo(expected)); | ||
196 | } | ||
197 | |||
198 | @Test | ||
199 | void removeDuplicateTest() { | ||
200 | var actual = Dnf.of(builder -> builder.clause((p, q) -> List.of( | ||
201 | friendView.call(p, q), | ||
202 | friendView.call(p, q) | ||
203 | ))); | ||
204 | var expected = Dnf.of(builder -> builder.clause((p, q) -> List.of(friendView.call(p, q)))); | ||
205 | |||
206 | assertThat(actual, structurallyEqualTo(expected)); | ||
207 | } | ||
208 | |||
209 | @Test | ||
210 | void removeContradictoryTest() { | ||
211 | var actual = Dnf.of(builder -> builder.clause((p, q) -> List.of( | ||
212 | friendView.call(p, q), | ||
213 | not(friendView.call(p, q)) | ||
214 | ))); | ||
215 | var expected = Dnf.builder().build(); | ||
216 | |||
217 | assertThat(actual, structurallyEqualTo(expected)); | ||
218 | } | ||
219 | |||
220 | @Test | ||
221 | void removeContradictoryUniversalTest() { | ||
222 | var actual = Dnf.of(builder -> builder.clause((p, q) -> List.of( | ||
223 | friendView.call(q, q), | ||
224 | friendView.call(p, q), | ||
225 | not(friendView.call(p, Variable.of())) | ||
226 | ))); | ||
227 | var expected = Dnf.builder().build(); | ||
228 | |||
229 | assertThat(actual, structurallyEqualTo(expected)); | ||
230 | } | ||
231 | |||
232 | @Test | ||
233 | void removeContradictoryExistentialUniversalTest() { | ||
234 | var actual = Dnf.of(builder -> builder.clause((p) -> List.of( | ||
235 | friendView.call(p, Variable.of()), | ||
236 | not(friendView.call(p, Variable.of())) | ||
237 | ))); | ||
238 | var expected = Dnf.builder().build(); | ||
239 | |||
240 | assertThat(actual, structurallyEqualTo(expected)); | ||
241 | } | ||
242 | |||
243 | @Test | ||
244 | void removeContradictoryUniversalParameterTest() { | ||
245 | var actual = Dnf.of(builder -> { | ||
246 | var p = builder.parameter("p"); | ||
247 | builder.clause((q) -> List.of( | ||
248 | friendView.call(q, q), | ||
249 | friendView.call(p, q), | ||
250 | not(friendView.call(p, Variable.of())) | ||
251 | )); | ||
252 | }); | ||
253 | var expected = Dnf.builder().parameter(p).build(); | ||
254 | |||
255 | assertThat(actual, structurallyEqualTo(expected)); | ||
256 | } | ||
257 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfBuilderVariableUnificationTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfBuilderVariableUnificationTest.java new file mode 100644 index 00000000..0e1f77e2 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfBuilderVariableUnificationTest.java | |||
@@ -0,0 +1,322 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.dnf; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.term.ParameterDirection; | ||
11 | import tools.refinery.logic.term.Variable; | ||
12 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
13 | |||
14 | import java.util.List; | ||
15 | |||
16 | import static org.hamcrest.MatcherAssert.assertThat; | ||
17 | import static tools.refinery.logic.tests.QueryMatchers.structurallyEqualTo; | ||
18 | |||
19 | class DnfBuilderVariableUnificationTest { | ||
20 | private final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
21 | private final Constraint childrenView = new FakeKeyOnlyView("children", 2); | ||
22 | |||
23 | @Test | ||
24 | void equalToParameterTest() { | ||
25 | var actual = Dnf.of(builder -> { | ||
26 | var p = builder.parameter("p"); | ||
27 | builder.clause(q -> List.of( | ||
28 | friendView.call(p, q), | ||
29 | p.isEquivalent(q) | ||
30 | )); | ||
31 | }); | ||
32 | |||
33 | var expectedP = Variable.of("p"); | ||
34 | assertThat(actual, structurallyEqualTo( | ||
35 | List.of(new SymbolicParameter(expectedP, ParameterDirection.OUT)), | ||
36 | List.of( | ||
37 | List.of(friendView.call(expectedP, expectedP)) | ||
38 | ) | ||
39 | )); | ||
40 | } | ||
41 | |||
42 | @Test | ||
43 | void equalToParameterReverseTest() { | ||
44 | var actual = Dnf.of(builder -> { | ||
45 | var p = builder.parameter("p"); | ||
46 | builder.clause(q -> List.of( | ||
47 | friendView.call(p, q), | ||
48 | q.isEquivalent(p) | ||
49 | )); | ||
50 | }); | ||
51 | |||
52 | var expectedP = Variable.of("p"); | ||
53 | assertThat(actual, structurallyEqualTo( | ||
54 | List.of(new SymbolicParameter(expectedP, ParameterDirection.OUT)), | ||
55 | List.of( | ||
56 | List.of(friendView.call(expectedP, expectedP)) | ||
57 | ) | ||
58 | )); | ||
59 | } | ||
60 | |||
61 | @Test | ||
62 | void equalQuantifiedTest() { | ||
63 | var actual = Dnf.of(builder -> builder.clause((p, q) -> List.of( | ||
64 | friendView.call(p, q), | ||
65 | p.isEquivalent(q) | ||
66 | ))); | ||
67 | |||
68 | var expectedP = Variable.of("p"); | ||
69 | assertThat(actual, structurallyEqualTo( | ||
70 | List.of(), | ||
71 | List.of( | ||
72 | List.of(friendView.call(expectedP, expectedP)) | ||
73 | ) | ||
74 | )); | ||
75 | } | ||
76 | |||
77 | @Test | ||
78 | void equalQuantifiedTransitiveTest() { | ||
79 | var actual = Dnf.of(builder -> builder.clause((p, q, r) -> List.of( | ||
80 | friendView.call(p, q), | ||
81 | p.isEquivalent(q), | ||
82 | childrenView.call(p, r), | ||
83 | q.isEquivalent(r) | ||
84 | ))); | ||
85 | |||
86 | var expectedP = Variable.of("p"); | ||
87 | assertThat(actual, structurallyEqualTo( | ||
88 | List.of(), | ||
89 | List.of( | ||
90 | List.of(friendView.call(expectedP, expectedP), childrenView.call(expectedP, expectedP)) | ||
91 | ) | ||
92 | )); | ||
93 | } | ||
94 | |||
95 | @Test | ||
96 | void equalQuantifiedTransitiveRemoveDuplicateTest() { | ||
97 | var actual = Dnf.of(builder -> builder.clause((p, q, r) -> List.of( | ||
98 | friendView.call(p, q), | ||
99 | p.isEquivalent(q), | ||
100 | friendView.call(p, r), | ||
101 | q.isEquivalent(r) | ||
102 | ))); | ||
103 | |||
104 | var expectedP = Variable.of("p"); | ||
105 | assertThat(actual, structurallyEqualTo( | ||
106 | List.of(), | ||
107 | List.of( | ||
108 | List.of(friendView.call(expectedP, expectedP)) | ||
109 | ) | ||
110 | )); | ||
111 | } | ||
112 | |||
113 | @Test | ||
114 | void parametersEqualTest() { | ||
115 | var actual = Dnf.of(builder -> { | ||
116 | var p = builder.parameter("p"); | ||
117 | var q = builder.parameter("q"); | ||
118 | builder.clause( | ||
119 | friendView.call(p, q), | ||
120 | p.isEquivalent(q) | ||
121 | ); | ||
122 | }); | ||
123 | |||
124 | var expectedP = Variable.of("p"); | ||
125 | var expectedQ = Variable.of("q"); | ||
126 | assertThat(actual, structurallyEqualTo( | ||
127 | List.of( | ||
128 | new SymbolicParameter(expectedP, ParameterDirection.OUT), | ||
129 | new SymbolicParameter(expectedQ, ParameterDirection.OUT) | ||
130 | ), | ||
131 | List.of( | ||
132 | List.of(friendView.call(expectedP, expectedP), expectedQ.isEquivalent(expectedP)) | ||
133 | ) | ||
134 | )); | ||
135 | } | ||
136 | |||
137 | @Test | ||
138 | void parametersEqualTransitiveTest() { | ||
139 | var actual = Dnf.of(builder -> { | ||
140 | var p = builder.parameter("p"); | ||
141 | var q = builder.parameter("q"); | ||
142 | var r = builder.parameter("r"); | ||
143 | builder.clause( | ||
144 | friendView.call(p, q), | ||
145 | childrenView.call(p, r), | ||
146 | p.isEquivalent(q), | ||
147 | r.isEquivalent(q) | ||
148 | ); | ||
149 | }); | ||
150 | |||
151 | var expectedP = Variable.of("p"); | ||
152 | var expectedQ = Variable.of("q"); | ||
153 | var expectedR = Variable.of("r"); | ||
154 | assertThat(actual, structurallyEqualTo( | ||
155 | List.of( | ||
156 | new SymbolicParameter(expectedP, ParameterDirection.OUT), | ||
157 | new SymbolicParameter(expectedQ, ParameterDirection.OUT), | ||
158 | new SymbolicParameter(expectedR, ParameterDirection.OUT) | ||
159 | ), | ||
160 | List.of( | ||
161 | List.of( | ||
162 | friendView.call(expectedP, expectedP), | ||
163 | expectedQ.isEquivalent(expectedP), | ||
164 | expectedR.isEquivalent(expectedP), | ||
165 | childrenView.call(expectedP, expectedP) | ||
166 | ) | ||
167 | ) | ||
168 | )); | ||
169 | } | ||
170 | |||
171 | @Test | ||
172 | void parameterAndQuantifiedEqualsTest() { | ||
173 | var actual = Dnf.of(builder -> { | ||
174 | var p = builder.parameter("p"); | ||
175 | var q = builder.parameter("q"); | ||
176 | builder.clause((r) -> List.of( | ||
177 | friendView.call(p, r), | ||
178 | p.isEquivalent(r), | ||
179 | childrenView.call(q, r), | ||
180 | q.isEquivalent(r) | ||
181 | )); | ||
182 | }); | ||
183 | |||
184 | |||
185 | var expectedP = Variable.of("p"); | ||
186 | var expectedQ = Variable.of("q"); | ||
187 | assertThat(actual, structurallyEqualTo( | ||
188 | List.of( | ||
189 | new SymbolicParameter(expectedP, ParameterDirection.OUT), | ||
190 | new SymbolicParameter(expectedQ, ParameterDirection.OUT) | ||
191 | ), | ||
192 | List.of( | ||
193 | List.of( | ||
194 | friendView.call(expectedP, expectedP), | ||
195 | expectedQ.isEquivalent(expectedP), | ||
196 | childrenView.call(expectedP, expectedP) | ||
197 | ) | ||
198 | ) | ||
199 | )); | ||
200 | } | ||
201 | |||
202 | @Test | ||
203 | void parameterAndQuantifiedEqualsReverseFirstTest() { | ||
204 | var actual = Dnf.of(builder -> { | ||
205 | var p = builder.parameter("p"); | ||
206 | var q = builder.parameter("q"); | ||
207 | builder.clause((r) -> List.of( | ||
208 | friendView.call(p, r), | ||
209 | r.isEquivalent(p), | ||
210 | childrenView.call(q, r), | ||
211 | q.isEquivalent(r) | ||
212 | )); | ||
213 | }); | ||
214 | |||
215 | var expectedP = Variable.of("p"); | ||
216 | var expectedQ = Variable.of("q"); | ||
217 | assertThat(actual, structurallyEqualTo( | ||
218 | List.of( | ||
219 | new SymbolicParameter(expectedP, ParameterDirection.OUT), | ||
220 | new SymbolicParameter(expectedQ, ParameterDirection.OUT) | ||
221 | ), | ||
222 | List.of( | ||
223 | List.of( | ||
224 | friendView.call(expectedP, expectedP), | ||
225 | expectedQ.isEquivalent(expectedP), | ||
226 | childrenView.call(expectedP, expectedP) | ||
227 | ) | ||
228 | ) | ||
229 | )); | ||
230 | } | ||
231 | |||
232 | @Test | ||
233 | void parameterAndQuantifiedEqualsReverseSecondTest() { | ||
234 | var actual = Dnf.of(builder -> { | ||
235 | var p = builder.parameter("p"); | ||
236 | var q = builder.parameter("q"); | ||
237 | builder.clause((r) -> List.of( | ||
238 | friendView.call(p, r), | ||
239 | p.isEquivalent(r), | ||
240 | childrenView.call(q, r), | ||
241 | r.isEquivalent(q) | ||
242 | )); | ||
243 | }); | ||
244 | |||
245 | var expectedP = Variable.of("p"); | ||
246 | var expectedQ = Variable.of("q"); | ||
247 | assertThat(actual, structurallyEqualTo( | ||
248 | List.of( | ||
249 | new SymbolicParameter(expectedP, ParameterDirection.OUT), | ||
250 | new SymbolicParameter(expectedQ, ParameterDirection.OUT) | ||
251 | ), | ||
252 | List.of( | ||
253 | List.of( | ||
254 | friendView.call(expectedP, expectedP), | ||
255 | expectedQ.isEquivalent(expectedP), | ||
256 | childrenView.call(expectedP, expectedP) | ||
257 | ) | ||
258 | ) | ||
259 | )); | ||
260 | } | ||
261 | |||
262 | @Test | ||
263 | void parameterAndQuantifiedEqualsReverseBoth() { | ||
264 | var actual = Dnf.of(builder -> { | ||
265 | var p = builder.parameter("p"); | ||
266 | var q = builder.parameter("q"); | ||
267 | builder.clause((r) -> List.of( | ||
268 | friendView.call(p, r), | ||
269 | p.isEquivalent(r), | ||
270 | childrenView.call(q, r), | ||
271 | r.isEquivalent(q) | ||
272 | )); | ||
273 | }); | ||
274 | |||
275 | var expectedP = Variable.of("p"); | ||
276 | var expectedQ = Variable.of("q"); | ||
277 | assertThat(actual, structurallyEqualTo( | ||
278 | List.of( | ||
279 | new SymbolicParameter(expectedP, ParameterDirection.OUT), | ||
280 | new SymbolicParameter(expectedQ, ParameterDirection.OUT) | ||
281 | ), | ||
282 | List.of( | ||
283 | List.of( | ||
284 | friendView.call(expectedP, expectedP), | ||
285 | expectedQ.isEquivalent(expectedP), | ||
286 | childrenView.call(expectedP, expectedP) | ||
287 | ) | ||
288 | ) | ||
289 | )); | ||
290 | } | ||
291 | |||
292 | @Test | ||
293 | void parameterAndTwoQuantifiedEqualsTest() { | ||
294 | var actual = Dnf.of(builder -> { | ||
295 | var p = builder.parameter("p"); | ||
296 | var q = builder.parameter("q"); | ||
297 | builder.clause((r, s) -> List.of( | ||
298 | r.isEquivalent(s), | ||
299 | friendView.call(p, r), | ||
300 | p.isEquivalent(r), | ||
301 | childrenView.call(q, s), | ||
302 | q.isEquivalent(s) | ||
303 | )); | ||
304 | }); | ||
305 | |||
306 | var expectedP = Variable.of("p"); | ||
307 | var expectedQ = Variable.of("q"); | ||
308 | assertThat(actual, structurallyEqualTo( | ||
309 | List.of( | ||
310 | new SymbolicParameter(expectedP, ParameterDirection.OUT), | ||
311 | new SymbolicParameter(expectedQ, ParameterDirection.OUT) | ||
312 | ), | ||
313 | List.of( | ||
314 | List.of( | ||
315 | friendView.call(expectedP, expectedP), | ||
316 | expectedQ.isEquivalent(expectedP), | ||
317 | childrenView.call(expectedP, expectedP) | ||
318 | ) | ||
319 | ) | ||
320 | )); | ||
321 | } | ||
322 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfToDefinitionStringTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfToDefinitionStringTest.java new file mode 100644 index 00000000..dd624548 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/DnfToDefinitionStringTest.java | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.dnf; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.term.NodeVariable; | ||
11 | import tools.refinery.logic.term.ParameterDirection; | ||
12 | import tools.refinery.logic.term.Variable; | ||
13 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
14 | |||
15 | import static org.hamcrest.MatcherAssert.assertThat; | ||
16 | import static org.hamcrest.Matchers.is; | ||
17 | import static tools.refinery.logic.literal.Literals.not; | ||
18 | |||
19 | class DnfToDefinitionStringTest { | ||
20 | private static final Constraint personView = new FakeKeyOnlyView("person", 1); | ||
21 | private static final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
22 | private static final NodeVariable p = Variable.of("p"); | ||
23 | private static final NodeVariable q = Variable.of("q"); | ||
24 | |||
25 | @Test | ||
26 | void noClausesTest() { | ||
27 | var dnf = Dnf.builder("Example").parameter(p).build(); | ||
28 | |||
29 | assertThat(dnf.toDefinitionString(), is(""" | ||
30 | pred Example(p) <-> | ||
31 | <no clauses>. | ||
32 | """)); | ||
33 | } | ||
34 | |||
35 | @Test | ||
36 | void noParametersTest() { | ||
37 | var dnf = Dnf.builder("Example").build(); | ||
38 | |||
39 | assertThat(dnf.toDefinitionString(), is(""" | ||
40 | pred Example() <-> | ||
41 | <no clauses>. | ||
42 | """)); | ||
43 | } | ||
44 | |||
45 | @Test | ||
46 | void emptyClauseTest() { | ||
47 | var dnf = Dnf.builder("Example").parameter(p, ParameterDirection.IN).clause().build(); | ||
48 | |||
49 | assertThat(dnf.toDefinitionString(), is(""" | ||
50 | pred Example(in p) <-> | ||
51 | <empty>. | ||
52 | """)); | ||
53 | } | ||
54 | |||
55 | @Test | ||
56 | void relationViewPositiveTest() { | ||
57 | var dnf = Dnf.builder("Example").parameter(p).clause(friendView.call(p, q)).build(); | ||
58 | |||
59 | assertThat(dnf.toDefinitionString(), is(""" | ||
60 | pred Example(p) <-> | ||
61 | friend(p, q). | ||
62 | """)); | ||
63 | } | ||
64 | |||
65 | @Test | ||
66 | void relationViewNegativeTest() { | ||
67 | var dnf = Dnf.builder("Example") | ||
68 | .parameter(p, ParameterDirection.IN) | ||
69 | .clause(not(friendView.call(p, q))) | ||
70 | .build(); | ||
71 | |||
72 | assertThat(dnf.toDefinitionString(), is(""" | ||
73 | pred Example(in p) <-> | ||
74 | !(friend(p, q)). | ||
75 | """)); | ||
76 | } | ||
77 | |||
78 | @Test | ||
79 | void relationViewTransitiveTest() { | ||
80 | var dnf = Dnf.builder("Example").parameter(p).clause(friendView.callTransitive(p, q)).build(); | ||
81 | |||
82 | assertThat(dnf.toDefinitionString(), is(""" | ||
83 | pred Example(p) <-> | ||
84 | friend+(p, q). | ||
85 | """)); | ||
86 | } | ||
87 | |||
88 | @Test | ||
89 | void multipleParametersTest() { | ||
90 | var dnf = Dnf.builder("Example").parameters(p, q).clause(friendView.call(p, q)).build(); | ||
91 | |||
92 | assertThat(dnf.toDefinitionString(), is(""" | ||
93 | pred Example(p, q) <-> | ||
94 | friend(p, q). | ||
95 | """)); | ||
96 | } | ||
97 | |||
98 | @Test | ||
99 | void multipleLiteralsTest() { | ||
100 | var dnf = Dnf.builder("Example") | ||
101 | .parameter(p) | ||
102 | .clause( | ||
103 | personView.call(p), | ||
104 | personView.call(q), | ||
105 | friendView.call(p, q) | ||
106 | ) | ||
107 | .build(); | ||
108 | |||
109 | assertThat(dnf.toDefinitionString(), is(""" | ||
110 | pred Example(p) <-> | ||
111 | person(p), | ||
112 | person(q), | ||
113 | friend(p, q). | ||
114 | """)); | ||
115 | } | ||
116 | |||
117 | @Test | ||
118 | void multipleClausesTest() { | ||
119 | var dnf = Dnf.builder("Example") | ||
120 | .parameter(p) | ||
121 | .clause(friendView.call(p, q)) | ||
122 | .clause(friendView.call(q, p)) | ||
123 | .build(); | ||
124 | |||
125 | assertThat(dnf.toDefinitionString(), is(""" | ||
126 | pred Example(p) <-> | ||
127 | friend(p, q) | ||
128 | ; | ||
129 | friend(q, p). | ||
130 | """)); | ||
131 | } | ||
132 | |||
133 | @Test | ||
134 | void dnfTest() { | ||
135 | var r = Variable.of("r"); | ||
136 | var s = Variable.of("s"); | ||
137 | var called = Dnf.builder("Called").parameters(r, s).clause(friendView.call(r, s)).build(); | ||
138 | var dnf = Dnf.builder("Example") | ||
139 | .parameter(p) | ||
140 | .clause( | ||
141 | personView.call(p), | ||
142 | personView.call(q), | ||
143 | not(called.call(p, q)) | ||
144 | ) | ||
145 | .build(); | ||
146 | |||
147 | assertThat(dnf.toDefinitionString(), is(""" | ||
148 | pred Example(p) <-> | ||
149 | person(p), | ||
150 | person(q), | ||
151 | !(@Dnf Called(p, q)). | ||
152 | """)); | ||
153 | } | ||
154 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/dnf/HashCodeTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/HashCodeTest.java new file mode 100644 index 00000000..e140be1e --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/HashCodeTest.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.dnf; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.term.NodeVariable; | ||
11 | import tools.refinery.logic.term.Variable; | ||
12 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
13 | |||
14 | import static org.hamcrest.MatcherAssert.assertThat; | ||
15 | import static org.hamcrest.Matchers.is; | ||
16 | import static org.hamcrest.Matchers.not; | ||
17 | |||
18 | class HashCodeTest { | ||
19 | private static final Constraint personView = new FakeKeyOnlyView("Person", 1); | ||
20 | private static final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
21 | private static final NodeVariable p = Variable.of("p"); | ||
22 | private static final NodeVariable q = Variable.of("q"); | ||
23 | |||
24 | @Test | ||
25 | void flatEqualsTest() { | ||
26 | var expected = Dnf.builder("Expected").parameters(q).clause(personView.call(q)).build(); | ||
27 | var actual = Dnf.builder("Actual").parameters(p).clause(personView.call(p)).build(); | ||
28 | |||
29 | assertThat(actual.hashCodeWithSubstitution(), is(expected.hashCodeWithSubstitution())); | ||
30 | } | ||
31 | |||
32 | @Test | ||
33 | void flatNotEqualsTest() { | ||
34 | var expected = Dnf.builder("Expected").parameters(q).clause(friendView.call(q, q)).build(); | ||
35 | var actual = Dnf.builder("Actual").parameters(p).clause(friendView.call(p, q)).build(); | ||
36 | |||
37 | assertThat(actual.hashCodeWithSubstitution(), not(expected.hashCodeWithSubstitution())); | ||
38 | } | ||
39 | |||
40 | @Test | ||
41 | void deepEqualsTest() { | ||
42 | var expected2 = Dnf.builder("Expected2").parameters(p).clause(personView.call(p)).build(); | ||
43 | var expected = Dnf.builder("Expected").parameters(q).clause( | ||
44 | expected2.call(q) | ||
45 | ).build(); | ||
46 | var actual = Dnf.builder("Actual").parameters(q).clause( | ||
47 | expected2.call(q) | ||
48 | ).build(); | ||
49 | |||
50 | assertThat(actual.hashCodeWithSubstitution(), is(expected.hashCodeWithSubstitution())); | ||
51 | } | ||
52 | |||
53 | @Test | ||
54 | void deepNotEqualsTest() { | ||
55 | var expected = Dnf.builder("Expected").parameters(q).clause( | ||
56 | Dnf.builder("Expected2").parameters(p).clause(personView.call(p)).build().call(q) | ||
57 | ).build(); | ||
58 | var actual = Dnf.builder("Actual").parameters(q).clause( | ||
59 | Dnf.builder("Actual2").parameters(p).clause(personView.call(p)).build().call(q) | ||
60 | ).build(); | ||
61 | |||
62 | assertThat(actual.hashCodeWithSubstitution(), not(expected.hashCodeWithSubstitution())); | ||
63 | } | ||
64 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/dnf/TopologicalSortTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/TopologicalSortTest.java new file mode 100644 index 00000000..8ea27cc9 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/TopologicalSortTest.java | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.dnf; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.InvalidQueryException; | ||
11 | import tools.refinery.logic.term.NodeVariable; | ||
12 | import tools.refinery.logic.term.ParameterDirection; | ||
13 | import tools.refinery.logic.term.Variable; | ||
14 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
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.logic.literal.Literals.not; | ||
21 | import static tools.refinery.logic.tests.QueryMatchers.structurallyEqualTo; | ||
22 | |||
23 | class TopologicalSortTest { | ||
24 | private static final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
25 | private static final Dnf example = Dnf.of("example", builder -> { | ||
26 | var a = builder.parameter("a", ParameterDirection.IN); | ||
27 | var b = builder.parameter("b", ParameterDirection.IN); | ||
28 | var c = builder.parameter("c", ParameterDirection.OUT); | ||
29 | var d = builder.parameter("d", ParameterDirection.OUT); | ||
30 | builder.clause( | ||
31 | friendView.call(a, b), | ||
32 | friendView.call(b, c), | ||
33 | friendView.call(c, d) | ||
34 | ); | ||
35 | }); | ||
36 | private static final NodeVariable p = Variable.of("p"); | ||
37 | private static final NodeVariable q = Variable.of("q"); | ||
38 | private static final NodeVariable r = Variable.of("r"); | ||
39 | private static final NodeVariable s = Variable.of("s"); | ||
40 | private static final NodeVariable t = Variable.of("t"); | ||
41 | |||
42 | @Test | ||
43 | void topologicalSortTest() { | ||
44 | var actual = Dnf.builder("Actual") | ||
45 | .parameter(p, ParameterDirection.IN) | ||
46 | .parameter(q, ParameterDirection.OUT) | ||
47 | .clause( | ||
48 | not(friendView.call(p, q)), | ||
49 | example.call(p, q, r, s), | ||
50 | example.call(r, t, q, s), | ||
51 | friendView.call(r, t) | ||
52 | ) | ||
53 | .build(); | ||
54 | |||
55 | assertThat(actual, structurallyEqualTo( | ||
56 | List.of( | ||
57 | new SymbolicParameter(p, ParameterDirection.IN), | ||
58 | new SymbolicParameter(q, ParameterDirection.OUT) | ||
59 | ), | ||
60 | List.of( | ||
61 | List.of( | ||
62 | friendView.call(r, t), | ||
63 | example.call(r, t, q, s), | ||
64 | not(friendView.call(p, q)), | ||
65 | example.call(p, q, r, s) | ||
66 | ) | ||
67 | ) | ||
68 | )); | ||
69 | } | ||
70 | |||
71 | @Test | ||
72 | void missingInputTest() { | ||
73 | var builder = Dnf.builder("Actual") | ||
74 | .parameter(p, ParameterDirection.OUT) | ||
75 | .parameter(q, ParameterDirection.OUT) | ||
76 | .clause( | ||
77 | not(friendView.call(p, q)), | ||
78 | example.call(p, q, r, s), | ||
79 | example.call(r, t, q, s), | ||
80 | friendView.call(r, t) | ||
81 | ); | ||
82 | assertThrows(InvalidQueryException.class, builder::build); | ||
83 | } | ||
84 | |||
85 | @Test | ||
86 | void missingVariableTest() { | ||
87 | var builder = Dnf.builder("Actual") | ||
88 | .parameter(p, ParameterDirection.IN) | ||
89 | .parameter(q, ParameterDirection.OUT) | ||
90 | .clause( | ||
91 | not(friendView.call(p, q)), | ||
92 | example.call(p, q, r, s), | ||
93 | example.call(r, t, q, s) | ||
94 | ); | ||
95 | assertThrows(InvalidQueryException.class, builder::build); | ||
96 | } | ||
97 | |||
98 | @Test | ||
99 | void circularDependencyTest() { | ||
100 | var builder = Dnf.builder("Actual") | ||
101 | .parameter(p, ParameterDirection.IN) | ||
102 | .parameter(q, ParameterDirection.OUT) | ||
103 | .clause( | ||
104 | not(friendView.call(p, q)), | ||
105 | example.call(p, q, r, s), | ||
106 | example.call(r, t, q, s), | ||
107 | example.call(p, q, r, t) | ||
108 | ); | ||
109 | assertThrows(InvalidQueryException.class, builder::build); | ||
110 | } | ||
111 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/dnf/VariableDirectionTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/VariableDirectionTest.java new file mode 100644 index 00000000..f9f39b8a --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/dnf/VariableDirectionTest.java | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023-2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.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.logic.Constraint; | ||
12 | import tools.refinery.logic.literal.BooleanLiteral; | ||
13 | import tools.refinery.logic.literal.Literal; | ||
14 | import tools.refinery.logic.term.DataVariable; | ||
15 | import tools.refinery.logic.term.NodeVariable; | ||
16 | import tools.refinery.logic.term.ParameterDirection; | ||
17 | import tools.refinery.logic.term.Variable; | ||
18 | import tools.refinery.logic.tests.FakeFunctionView; | ||
19 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
20 | |||
21 | import java.util.ArrayList; | ||
22 | import java.util.List; | ||
23 | import java.util.stream.Stream; | ||
24 | |||
25 | import static org.hamcrest.MatcherAssert.assertThat; | ||
26 | import static org.hamcrest.Matchers.hasItem; | ||
27 | import static org.hamcrest.Matchers.not; | ||
28 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
29 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
30 | import static tools.refinery.logic.literal.Literals.not; | ||
31 | import static tools.refinery.logic.term.int_.IntTerms.INT_SUM; | ||
32 | |||
33 | class VariableDirectionTest { | ||
34 | private static final Constraint personView = new FakeKeyOnlyView("Person", 1); | ||
35 | private static final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
36 | private static final FakeFunctionView<Integer> ageView = new FakeFunctionView<>("age", 1, Integer.class); | ||
37 | private static final NodeVariable p = Variable.of("p"); | ||
38 | private static final NodeVariable q = Variable.of("q"); | ||
39 | private static final DataVariable<Integer> x = Variable.of("x", Integer.class); | ||
40 | private static final DataVariable<Integer> y = Variable.of("y", Integer.class); | ||
41 | private static final DataVariable<Integer> z = Variable.of("z", Integer.class); | ||
42 | |||
43 | @ParameterizedTest | ||
44 | @MethodSource("clausesWithVariableInput") | ||
45 | void unboundOutVariableTest(List<? extends Literal> clause) { | ||
46 | var builder = Dnf.builder().parameter(p, ParameterDirection.OUT).clause(clause); | ||
47 | assertThrows(InvalidClauseException.class, builder::build); | ||
48 | } | ||
49 | |||
50 | @ParameterizedTest | ||
51 | @MethodSource("clausesWithVariableInput") | ||
52 | void unboundInVariableTest(List<? extends Literal> clause) { | ||
53 | var builder = Dnf.builder().parameter(p, ParameterDirection.IN).clause(clause); | ||
54 | var dnf = assertDoesNotThrow(builder::build); | ||
55 | var clauses = dnf.getClauses(); | ||
56 | if (!clauses.isEmpty()) { | ||
57 | assertThat(clauses.getFirst().positiveVariables(), hasItem(p)); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | @ParameterizedTest | ||
62 | @MethodSource("clausesWithVariableInput") | ||
63 | void boundPrivateVariableTest(List<? extends Literal> clause) { | ||
64 | var clauseWithBinding = new ArrayList<Literal>(clause); | ||
65 | clauseWithBinding.add(personView.call(p)); | ||
66 | var builder = Dnf.builder().clause(clauseWithBinding); | ||
67 | var dnf = assertDoesNotThrow(builder::build); | ||
68 | var clauses = dnf.getClauses(); | ||
69 | if (!clauses.isEmpty()) { | ||
70 | assertThat(clauses.getFirst().positiveVariables(), hasItem(p)); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static Stream<Arguments> clausesWithVariableInput() { | ||
75 | return Stream.concat( | ||
76 | clausesNotBindingVariable(), | ||
77 | literalToClauseArgumentStream(literalsWithRequiredVariableInput()) | ||
78 | ); | ||
79 | } | ||
80 | |||
81 | @ParameterizedTest | ||
82 | @MethodSource("clausesNotBindingVariable") | ||
83 | void unboundPrivateVariableTest(List<? extends Literal> clause) { | ||
84 | var builder = Dnf.builder().clause(clause); | ||
85 | var dnf = assertDoesNotThrow(builder::build); | ||
86 | var clauses = dnf.getClauses(); | ||
87 | if (!clauses.isEmpty()) { | ||
88 | assertThat(clauses.getFirst().positiveVariables(), not(hasItem(p))); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | @ParameterizedTest | ||
93 | @MethodSource("clausesNotBindingVariable") | ||
94 | void unboundByEquivalencePrivateVariableTest(List<? extends Literal> clause) { | ||
95 | var r = Variable.of("r"); | ||
96 | var clauseWithEquivalence = new ArrayList<Literal>(clause); | ||
97 | clauseWithEquivalence.add(r.isEquivalent(p)); | ||
98 | var builder = Dnf.builder().clause(clauseWithEquivalence); | ||
99 | assertThrows(InvalidClauseException.class, builder::build); | ||
100 | } | ||
101 | |||
102 | static Stream<Arguments> clausesNotBindingVariable() { | ||
103 | return Stream.concat( | ||
104 | Stream.of( | ||
105 | Arguments.of(List.of()), | ||
106 | Arguments.of(List.of(BooleanLiteral.TRUE)), | ||
107 | Arguments.of(List.of(BooleanLiteral.FALSE)) | ||
108 | ), | ||
109 | literalToClauseArgumentStream(literalsWithPrivateVariable()) | ||
110 | ); | ||
111 | } | ||
112 | |||
113 | @ParameterizedTest | ||
114 | @MethodSource("literalsWithPrivateVariable") | ||
115 | void unboundTwicePrivateVariableTest(Literal literal) { | ||
116 | var builder = Dnf.builder().clause(not(personView.call(p)), literal); | ||
117 | assertThrows(InvalidClauseException.class, builder::build); | ||
118 | } | ||
119 | |||
120 | @ParameterizedTest | ||
121 | @MethodSource("literalsWithPrivateVariable") | ||
122 | void unboundTwiceByEquivalencePrivateVariableTest(Literal literal) { | ||
123 | var r = Variable.of("r"); | ||
124 | var builder = Dnf.builder().clause(not(personView.call(r)), r.isEquivalent(p), literal); | ||
125 | assertThrows(InvalidClauseException.class, builder::build); | ||
126 | } | ||
127 | |||
128 | static Stream<Arguments> literalsWithPrivateVariable() { | ||
129 | var dnfWithOutput = Dnf.builder("WithOutput") | ||
130 | .parameter(p, ParameterDirection.OUT) | ||
131 | .parameter(q, ParameterDirection.OUT) | ||
132 | .clause(friendView.call(p, q)) | ||
133 | .build(); | ||
134 | var dnfWithOutputToAggregate = Dnf.builder("WithOutputToAggregate") | ||
135 | .parameter(p, ParameterDirection.OUT) | ||
136 | .parameter(q, ParameterDirection.OUT) | ||
137 | .parameter(x, ParameterDirection.OUT) | ||
138 | .clause( | ||
139 | friendView.call(p, q), | ||
140 | ageView.call(q, x) | ||
141 | ) | ||
142 | .build(); | ||
143 | |||
144 | return Stream.of( | ||
145 | Arguments.of(not(friendView.call(p, q))), | ||
146 | Arguments.of(y.assign(friendView.count(p, q))), | ||
147 | Arguments.of(y.assign(ageView.aggregate(INT_SUM, p))), | ||
148 | Arguments.of(not(dnfWithOutput.call(p, q))), | ||
149 | Arguments.of(y.assign(dnfWithOutput.count(p, q))), | ||
150 | Arguments.of(y.assign(dnfWithOutputToAggregate.aggregateBy(z, INT_SUM, p, q, z))) | ||
151 | ); | ||
152 | } | ||
153 | |||
154 | @ParameterizedTest | ||
155 | @MethodSource("literalsWithRequiredVariableInput") | ||
156 | void unboundPrivateVariableTest(Literal literal) { | ||
157 | var builder = Dnf.builder().clause(literal); | ||
158 | assertThrows(InvalidClauseException.class, builder::build); | ||
159 | } | ||
160 | |||
161 | @ParameterizedTest | ||
162 | @MethodSource("literalsWithRequiredVariableInput") | ||
163 | void boundPrivateVariableInputTest(Literal literal) { | ||
164 | var builder = Dnf.builder().clause(personView.call(p), literal); | ||
165 | var dnf = assertDoesNotThrow(builder::build); | ||
166 | assertThat(dnf.getClauses().getFirst().positiveVariables(), hasItem(p)); | ||
167 | } | ||
168 | |||
169 | static Stream<Arguments> literalsWithRequiredVariableInput() { | ||
170 | var dnfWithInput = Dnf.builder("WithInput") | ||
171 | .parameter(p, ParameterDirection.IN) | ||
172 | .parameter(q, ParameterDirection.OUT) | ||
173 | .clause(friendView.call(p, q)).build(); | ||
174 | var dnfWithInputToAggregate = Dnf.builder("WithInputToAggregate") | ||
175 | .parameter(p, ParameterDirection.IN) | ||
176 | .parameter(q, ParameterDirection.OUT) | ||
177 | .parameter(x, ParameterDirection.OUT) | ||
178 | .clause( | ||
179 | friendView.call(p, q), | ||
180 | ageView.call(q, x) | ||
181 | ).build(); | ||
182 | |||
183 | return Stream.of( | ||
184 | Arguments.of(dnfWithInput.call(p, q)), | ||
185 | Arguments.of(dnfWithInput.call(p, p)), | ||
186 | Arguments.of(not(dnfWithInput.call(p, q))), | ||
187 | Arguments.of(not(dnfWithInput.call(p, p))), | ||
188 | Arguments.of(y.assign(dnfWithInput.count(p, q))), | ||
189 | Arguments.of(y.assign(dnfWithInput.count(p, p))), | ||
190 | Arguments.of(y.assign(dnfWithInputToAggregate.aggregateBy(z, INT_SUM, p, q, z))), | ||
191 | Arguments.of(y.assign(dnfWithInputToAggregate.aggregateBy(z, INT_SUM, p, p, z))) | ||
192 | ); | ||
193 | } | ||
194 | |||
195 | @ParameterizedTest | ||
196 | @MethodSource("literalsWithVariableOutput") | ||
197 | void boundParameterTest(Literal literal) { | ||
198 | var builder = Dnf.builder().parameter(p, ParameterDirection.OUT).clause(literal); | ||
199 | var dnf = assertDoesNotThrow(builder::build); | ||
200 | assertThat(dnf.getClauses().getFirst().positiveVariables(), hasItem(p)); | ||
201 | } | ||
202 | |||
203 | @ParameterizedTest | ||
204 | @MethodSource("literalsWithVariableOutput") | ||
205 | void boundTwiceParameterTest(Literal literal) { | ||
206 | var builder = Dnf.builder().parameter(p, ParameterDirection.IN).clause(literal); | ||
207 | var dnf = assertDoesNotThrow(builder::build); | ||
208 | assertThat(dnf.getClauses().getFirst().positiveVariables(), hasItem(p)); | ||
209 | } | ||
210 | |||
211 | @ParameterizedTest | ||
212 | @MethodSource("literalsWithVariableOutput") | ||
213 | void boundPrivateVariableOutputTest(Literal literal) { | ||
214 | var dnfWithInput = Dnf.builder("WithInput") | ||
215 | .parameter(p, ParameterDirection.IN) | ||
216 | .clause(personView.call(p)) | ||
217 | .build(); | ||
218 | var builder = Dnf.builder().clause(dnfWithInput.call(p), literal); | ||
219 | var dnf = assertDoesNotThrow(builder::build); | ||
220 | assertThat(dnf.getClauses().getFirst().positiveVariables(), hasItem(p)); | ||
221 | } | ||
222 | |||
223 | @ParameterizedTest | ||
224 | @MethodSource("literalsWithVariableOutput") | ||
225 | void boundTwicePrivateVariableOutputTest(Literal literal) { | ||
226 | var builder = Dnf.builder().clause(personView.call(p), literal); | ||
227 | var dnf = assertDoesNotThrow(builder::build); | ||
228 | assertThat(dnf.getClauses().getFirst().positiveVariables(), hasItem(p)); | ||
229 | } | ||
230 | |||
231 | static Stream<Arguments> literalsWithVariableOutput() { | ||
232 | var dnfWithOutput = Dnf.builder("WithOutput") | ||
233 | .parameter(p, ParameterDirection.OUT) | ||
234 | .parameter(q, ParameterDirection.OUT) | ||
235 | .clause(friendView.call(p, q)) | ||
236 | .build(); | ||
237 | |||
238 | return Stream.of( | ||
239 | Arguments.of(friendView.call(p, q)), | ||
240 | Arguments.of(dnfWithOutput.call(p, q)) | ||
241 | ); | ||
242 | } | ||
243 | |||
244 | private static Stream<Arguments> literalToClauseArgumentStream(Stream<Arguments> literalArgumentsStream) { | ||
245 | return literalArgumentsStream.map(arguments -> Arguments.of(List.of(arguments.get()[0]))); | ||
246 | } | ||
247 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/literal/AggregationLiteralTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/literal/AggregationLiteralTest.java new file mode 100644 index 00000000..76639e18 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/literal/AggregationLiteralTest.java | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.literal; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.InvalidQueryException; | ||
11 | import tools.refinery.logic.dnf.Dnf; | ||
12 | import tools.refinery.logic.dnf.InvalidClauseException; | ||
13 | import tools.refinery.logic.term.*; | ||
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.*; | ||
20 | import static org.junit.jupiter.api.Assertions.assertAll; | ||
21 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
22 | import static tools.refinery.logic.literal.Literals.not; | ||
23 | import static tools.refinery.logic.term.int_.IntTerms.INT_SUM; | ||
24 | import static tools.refinery.logic.term.int_.IntTerms.constant; | ||
25 | |||
26 | class AggregationLiteralTest { | ||
27 | private static final NodeVariable p = Variable.of("p"); | ||
28 | private static final DataVariable<Integer> x = Variable.of("x", Integer.class); | ||
29 | private static final DataVariable<Integer> y = Variable.of("y", Integer.class); | ||
30 | private static final DataVariable<Integer> z = Variable.of("z", Integer.class); | ||
31 | private static final Constraint fakeConstraint = new Constraint() { | ||
32 | @Override | ||
33 | public String name() { | ||
34 | return getClass().getName(); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public List<Parameter> getParameters() { | ||
39 | return List.of( | ||
40 | new Parameter(null, ParameterDirection.OUT), | ||
41 | new Parameter(Integer.class, ParameterDirection.OUT) | ||
42 | ); | ||
43 | } | ||
44 | }; | ||
45 | |||
46 | @Test | ||
47 | void parameterDirectionTest() { | ||
48 | var literal = x.assign(fakeConstraint.aggregateBy(y, INT_SUM, p, y)); | ||
49 | assertAll( | ||
50 | () -> assertThat(literal.getOutputVariables(), containsInAnyOrder(x)), | ||
51 | () -> assertThat(literal.getInputVariables(Set.of()), empty()), | ||
52 | () -> assertThat(literal.getInputVariables(Set.of(p)), containsInAnyOrder(p)), | ||
53 | () -> assertThat(literal.getPrivateVariables(Set.of()), containsInAnyOrder(p, y)), | ||
54 | () -> assertThat(literal.getPrivateVariables(Set.of(p)), containsInAnyOrder(y)) | ||
55 | ); | ||
56 | } | ||
57 | |||
58 | @Test | ||
59 | void missingAggregationVariableTest() { | ||
60 | var aggregation = fakeConstraint.aggregateBy(y, INT_SUM, p, z); | ||
61 | assertThrows(InvalidQueryException.class, () -> x.assign(aggregation)); | ||
62 | } | ||
63 | |||
64 | @Test | ||
65 | void circularAggregationVariableTest() { | ||
66 | var aggregation = fakeConstraint.aggregateBy(x, INT_SUM, p, x); | ||
67 | assertThrows(InvalidQueryException.class, () -> x.assign(aggregation)); | ||
68 | } | ||
69 | |||
70 | @Test | ||
71 | void unboundTwiceVariableTest() { | ||
72 | var builder = Dnf.builder() | ||
73 | .clause( | ||
74 | not(fakeConstraint.call(p, y)), | ||
75 | x.assign(fakeConstraint.aggregateBy(y, INT_SUM, p, y)) | ||
76 | ); | ||
77 | assertThrows(InvalidClauseException.class, builder::build); | ||
78 | } | ||
79 | |||
80 | @Test | ||
81 | void unboundBoundVariableTest() { | ||
82 | var builder = Dnf.builder() | ||
83 | .clause( | ||
84 | y.assign(constant(27)), | ||
85 | x.assign(fakeConstraint.aggregateBy(y, INT_SUM, p, y)) | ||
86 | ); | ||
87 | assertThrows(InvalidClauseException.class, builder::build); | ||
88 | } | ||
89 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/literal/CallLiteralTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/literal/CallLiteralTest.java new file mode 100644 index 00000000..0fb2e7c9 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/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.logic.literal; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.term.NodeVariable; | ||
11 | import tools.refinery.logic.term.Parameter; | ||
12 | import tools.refinery.logic.term.ParameterDirection; | ||
13 | import tools.refinery.logic.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.logic.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 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/rewriter/DuplicateDnfRemoverTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/rewriter/DuplicateDnfRemoverTest.java new file mode 100644 index 00000000..7b2ce8b2 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/rewriter/DuplicateDnfRemoverTest.java | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.rewriter; | ||
7 | |||
8 | import org.junit.jupiter.api.BeforeEach; | ||
9 | import org.junit.jupiter.api.Test; | ||
10 | import tools.refinery.logic.Constraint; | ||
11 | import tools.refinery.logic.dnf.Query; | ||
12 | import tools.refinery.logic.literal.AbstractCallLiteral; | ||
13 | import tools.refinery.logic.literal.Reduction; | ||
14 | import tools.refinery.logic.term.Variable; | ||
15 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
16 | |||
17 | import java.util.List; | ||
18 | |||
19 | import static org.hamcrest.MatcherAssert.assertThat; | ||
20 | import static org.hamcrest.Matchers.is; | ||
21 | import static org.hamcrest.Matchers.not; | ||
22 | import static tools.refinery.logic.literal.Literals.not; | ||
23 | |||
24 | class DuplicateDnfRemoverTest { | ||
25 | private final static Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
26 | |||
27 | private DuplicateDnfRemover sut; | ||
28 | |||
29 | @BeforeEach | ||
30 | void beforeEach() { | ||
31 | sut = new DuplicateDnfRemover(); | ||
32 | } | ||
33 | |||
34 | @Test | ||
35 | void removeDuplicateSimpleTest() { | ||
36 | var one = Query.of("One", (builder, x, y) -> builder.clause( | ||
37 | friendView.call(x, y), | ||
38 | friendView.call(y, x) | ||
39 | )); | ||
40 | var two = Query.of("Two", (builder, x, y) -> builder.clause( | ||
41 | friendView.call(x, y), | ||
42 | friendView.call(y, x) | ||
43 | )); | ||
44 | |||
45 | var oneResult = sut.rewrite(one); | ||
46 | var twoResult = sut.rewrite(two); | ||
47 | |||
48 | assertThat(oneResult, is(twoResult)); | ||
49 | assertThat(one, is(oneResult)); | ||
50 | } | ||
51 | |||
52 | @Test | ||
53 | void notDuplicateSimpleTest() { | ||
54 | var one = Query.of("One", (builder, x, y) -> builder.clause( | ||
55 | friendView.call(x, y), | ||
56 | friendView.call(y, x) | ||
57 | )); | ||
58 | var two = Query.of("Two", (builder, x, y) -> builder.clause((z) -> List.of( | ||
59 | friendView.call(x, y), | ||
60 | friendView.call(y, z) | ||
61 | ))); | ||
62 | |||
63 | var oneResult = sut.rewrite(one); | ||
64 | var twoResult = sut.rewrite(two); | ||
65 | |||
66 | assertThat(one, is(oneResult)); | ||
67 | assertThat(two, is(twoResult)); | ||
68 | } | ||
69 | |||
70 | @Test | ||
71 | void removeDuplicateRecursiveTest() { | ||
72 | var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause( | ||
73 | friendView.call(x, y), | ||
74 | friendView.call(y, x) | ||
75 | )); | ||
76 | var one = Query.of("One", (builder, x) -> builder.clause( | ||
77 | oneSubQuery.call(x, Variable.of()) | ||
78 | )); | ||
79 | var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause( | ||
80 | friendView.call(x, y), | ||
81 | friendView.call(y, x) | ||
82 | )); | ||
83 | var two = Query.of("Two", (builder, x) -> builder.clause( | ||
84 | twoSubQuery.call(x, Variable.of()) | ||
85 | )); | ||
86 | |||
87 | var oneResult = sut.rewrite(one); | ||
88 | var twoResult = sut.rewrite(two); | ||
89 | |||
90 | assertThat(oneResult, is(twoResult)); | ||
91 | assertThat(one, is(oneResult)); | ||
92 | } | ||
93 | |||
94 | @Test | ||
95 | void notDuplicateRecursiveTest() { | ||
96 | var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause( | ||
97 | friendView.call(x, y), | ||
98 | friendView.call(y, x) | ||
99 | )); | ||
100 | var one = Query.of("One", (builder, x) -> builder.clause( | ||
101 | oneSubQuery.call(x, Variable.of()) | ||
102 | )); | ||
103 | var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause( | ||
104 | friendView.call(x, y), | ||
105 | friendView.call(y, x) | ||
106 | )); | ||
107 | var two = Query.of("Two", (builder, x) -> builder.clause( | ||
108 | twoSubQuery.call(Variable.of(), x) | ||
109 | )); | ||
110 | |||
111 | var oneResult = sut.rewrite(one); | ||
112 | var twoResult = sut.rewrite(two); | ||
113 | |||
114 | assertThat(one, is(oneResult)); | ||
115 | assertThat(oneResult, is(not(twoResult))); | ||
116 | |||
117 | var oneCall = (AbstractCallLiteral) oneResult.getDnf().getClauses().getFirst().literals().getFirst(); | ||
118 | var twoCall = (AbstractCallLiteral) twoResult.getDnf().getClauses().getFirst().literals().getFirst(); | ||
119 | |||
120 | assertThat(oneCall.getTarget(), is(twoCall.getTarget())); | ||
121 | } | ||
122 | |||
123 | @Test | ||
124 | void removeContradictionTest() { | ||
125 | var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause( | ||
126 | friendView.call(x, y), | ||
127 | friendView.call(y, x) | ||
128 | )); | ||
129 | var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause( | ||
130 | friendView.call(x, y), | ||
131 | friendView.call(y, x) | ||
132 | )); | ||
133 | var query = Query.of("Contradiction", (builder, x, y) -> builder.clause( | ||
134 | oneSubQuery.call(x, y), | ||
135 | not(twoSubQuery.call(x, y)) | ||
136 | )); | ||
137 | |||
138 | var result = sut.rewrite(query); | ||
139 | |||
140 | assertThat(result.getDnf().getReduction(), is(Reduction.ALWAYS_FALSE)); | ||
141 | } | ||
142 | |||
143 | @Test | ||
144 | void removeQuantifiedContradictionTest() { | ||
145 | var oneSubQuery = Query.of("OneSubQuery", (builder, x, y) -> builder.clause( | ||
146 | friendView.call(x, y), | ||
147 | friendView.call(y, x) | ||
148 | )); | ||
149 | var twoSubQuery = Query.of("TwoSubQuery", (builder, x, y) -> builder.clause( | ||
150 | friendView.call(x, y), | ||
151 | friendView.call(y, x) | ||
152 | )); | ||
153 | var query = Query.of("Contradiction", (builder, x) -> builder.clause( | ||
154 | oneSubQuery.call(x, Variable.of()), | ||
155 | not(twoSubQuery.call(x, Variable.of())) | ||
156 | )); | ||
157 | |||
158 | var result = sut.rewrite(query); | ||
159 | |||
160 | assertThat(result.getDnf().getReduction(), is(Reduction.ALWAYS_FALSE)); | ||
161 | } | ||
162 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/rewriter/InputParameterResolverTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/rewriter/InputParameterResolverTest.java new file mode 100644 index 00000000..5e5fdb64 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/rewriter/InputParameterResolverTest.java | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.rewriter; | ||
7 | |||
8 | import org.junit.jupiter.api.BeforeEach; | ||
9 | import org.junit.jupiter.api.Test; | ||
10 | import tools.refinery.logic.Constraint; | ||
11 | import tools.refinery.logic.dnf.Dnf; | ||
12 | import tools.refinery.logic.dnf.Query; | ||
13 | import tools.refinery.logic.term.ParameterDirection; | ||
14 | import tools.refinery.logic.term.Variable; | ||
15 | import tools.refinery.logic.tests.FakeKeyOnlyView; | ||
16 | |||
17 | import java.util.List; | ||
18 | |||
19 | import static org.hamcrest.MatcherAssert.assertThat; | ||
20 | import static org.hamcrest.Matchers.is; | ||
21 | import static tools.refinery.logic.literal.Literals.not; | ||
22 | import static tools.refinery.logic.tests.QueryMatchers.structurallyEqualTo; | ||
23 | |||
24 | class InputParameterResolverTest { | ||
25 | private final static Constraint personView = new FakeKeyOnlyView("Person", 1); | ||
26 | private final static Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
27 | |||
28 | private InputParameterResolver sut; | ||
29 | |||
30 | @BeforeEach | ||
31 | void beforeEach() { | ||
32 | sut = new InputParameterResolver(); | ||
33 | } | ||
34 | |||
35 | @Test | ||
36 | void inlineSingleClauseTest() { | ||
37 | var dnf = Dnf.of("SubQuery", builder -> { | ||
38 | var x = builder.parameter("x", ParameterDirection.OUT); | ||
39 | builder.clause(friendView.call(x, Variable.of())); | ||
40 | }); | ||
41 | var query = Query.of("Actual", (builder, x) -> builder.clause( | ||
42 | dnf.call(x), | ||
43 | personView.call(x) | ||
44 | )); | ||
45 | |||
46 | var actual = sut.rewrite(query); | ||
47 | |||
48 | var expected = Query.of("Expected", (builder, x) -> builder.clause( | ||
49 | friendView.call(x, Variable.of()), | ||
50 | personView.call(x) | ||
51 | )); | ||
52 | |||
53 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); | ||
54 | } | ||
55 | |||
56 | @Test | ||
57 | void inlineSingleClauseWIthInputTest() { | ||
58 | var dnf = Dnf.of("SubQuery", builder -> { | ||
59 | var x = builder.parameter("x", ParameterDirection.IN); | ||
60 | builder.clause(not(friendView.call(x, Variable.of()))); | ||
61 | }); | ||
62 | var query = Query.of("Actual", (builder, x) -> builder.clause( | ||
63 | dnf.call(x), | ||
64 | personView.call(x) | ||
65 | )); | ||
66 | |||
67 | var actual = sut.rewrite(query); | ||
68 | |||
69 | var expected = Query.of("Expected", (builder, x) -> builder.clause( | ||
70 | personView.call(x), | ||
71 | not(friendView.call(x, Variable.of())) | ||
72 | )); | ||
73 | |||
74 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); | ||
75 | } | ||
76 | |||
77 | @Test | ||
78 | void singleLiteralDemandSetTest() { | ||
79 | var dnf = Dnf.of("SubQuery", builder -> { | ||
80 | var x = builder.parameter("x", ParameterDirection.IN); | ||
81 | builder.clause(not(friendView.call(x, Variable.of()))); | ||
82 | builder.clause(not(friendView.call(Variable.of(), x))); | ||
83 | }); | ||
84 | var query = Query.of("Actual", (builder, x) -> builder.clause( | ||
85 | dnf.call(x), | ||
86 | personView.call(x) | ||
87 | )); | ||
88 | |||
89 | var actual = sut.rewrite(query); | ||
90 | |||
91 | var expectedSubQuery = Dnf.of("ExpectedSubQuery", builder -> { | ||
92 | var x = builder.parameter("x", ParameterDirection.OUT); | ||
93 | builder.clause( | ||
94 | personView.call(x), | ||
95 | not(friendView.call(x, Variable.of())) | ||
96 | ); | ||
97 | builder.clause( | ||
98 | personView.call(x), | ||
99 | not(friendView.call(Variable.of(), x)) | ||
100 | ); | ||
101 | }); | ||
102 | var expected = Query.of("Expected", (builder, x) -> builder.clause( | ||
103 | personView.call(x), | ||
104 | expectedSubQuery.call(x) | ||
105 | )); | ||
106 | |||
107 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); | ||
108 | } | ||
109 | |||
110 | @Test | ||
111 | void multipleLiteralDemandSetTest() { | ||
112 | var dnf = Dnf.of("SubQuery", builder -> { | ||
113 | var x = builder.parameter("x", ParameterDirection.IN); | ||
114 | var y = builder.parameter("y", ParameterDirection.IN); | ||
115 | builder.clause(not(friendView.call(x, y))); | ||
116 | builder.clause(not(friendView.call(y, x))); | ||
117 | }); | ||
118 | var query = Query.of("Actual", (builder, p1) -> builder.clause(p2 -> List.of( | ||
119 | not(dnf.call(p1, p2)), | ||
120 | personView.call(p1), | ||
121 | personView.call(p2) | ||
122 | ))); | ||
123 | |||
124 | var actual = sut.rewrite(query); | ||
125 | |||
126 | var context = Dnf.of("Context", builder -> { | ||
127 | var x = builder.parameter("x", ParameterDirection.OUT); | ||
128 | var y = builder.parameter("y", ParameterDirection.OUT); | ||
129 | builder.clause( | ||
130 | personView.call(x), | ||
131 | personView.call(y) | ||
132 | ); | ||
133 | }); | ||
134 | var expectedSubQuery = Dnf.of("ExpectedSubQuery", builder -> { | ||
135 | var x = builder.parameter("x", ParameterDirection.OUT); | ||
136 | var y = builder.parameter("x", ParameterDirection.OUT); | ||
137 | builder.clause( | ||
138 | context.call(x, y), | ||
139 | not(friendView.call(x, y)) | ||
140 | ); | ||
141 | builder.clause( | ||
142 | context.call(x, y), | ||
143 | not(friendView.call(y, x)) | ||
144 | ); | ||
145 | }); | ||
146 | var expected = Query.of("Expected", (builder, p1) -> builder.clause(p2 -> List.of( | ||
147 | context.call(p1, p2), | ||
148 | not(expectedSubQuery.call(p1, p2)) | ||
149 | ))); | ||
150 | |||
151 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); | ||
152 | } | ||
153 | |||
154 | @Test | ||
155 | void multipleParameterDemandSetTest() { | ||
156 | var dnf = Dnf.of("SubQuery", builder -> { | ||
157 | var x = builder.parameter("x", ParameterDirection.IN); | ||
158 | var y = builder.parameter("y", ParameterDirection.IN); | ||
159 | builder.clause(not(friendView.call(x, y))); | ||
160 | builder.clause(not(friendView.call(y, x))); | ||
161 | }); | ||
162 | var query = Query.of("Actual", (builder, p1) -> builder.clause( | ||
163 | not(dnf.call(p1, p1)), | ||
164 | personView.call(p1) | ||
165 | )); | ||
166 | |||
167 | var actual = sut.rewrite(query); | ||
168 | |||
169 | var expectedSubQuery = Dnf.of("ExpectedSubQuery", builder -> { | ||
170 | var x = builder.parameter("x", ParameterDirection.OUT); | ||
171 | var y = builder.parameter("y", ParameterDirection.OUT); | ||
172 | builder.clause( | ||
173 | y.isEquivalent(x), | ||
174 | personView.call(x), | ||
175 | not(friendView.call(x, x)) | ||
176 | ); | ||
177 | }); | ||
178 | var expected = Query.of("Expected", (builder, p1) -> builder.clause( | ||
179 | personView.call(p1), | ||
180 | not(expectedSubQuery.call(p1, p1)) | ||
181 | )); | ||
182 | |||
183 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); | ||
184 | } | ||
185 | |||
186 | @Test | ||
187 | void eliminateDoubleNegationTest() { | ||
188 | var dnf = Dnf.of("SubQuery", builder -> { | ||
189 | var x = builder.parameter("x", ParameterDirection.IN); | ||
190 | builder.clause(not(friendView.call(x, Variable.of()))); | ||
191 | }); | ||
192 | var query = Query.of("Actual", (builder, p1) -> builder.clause( | ||
193 | personView.call(p1), | ||
194 | not(dnf.call(p1)) | ||
195 | )); | ||
196 | |||
197 | var actual = sut.rewrite(query); | ||
198 | |||
199 | var expected = Query.of("Actual", (builder, p1) -> builder.clause( | ||
200 | personView.call(p1), | ||
201 | friendView.call(p1, Variable.of()) | ||
202 | )); | ||
203 | |||
204 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); | ||
205 | } | ||
206 | |||
207 | @Test | ||
208 | void identityWhenNoWorkToDoTest() { | ||
209 | var dnf = Dnf.of("SubQuery", builder -> { | ||
210 | var x = builder.parameter("x", ParameterDirection.OUT); | ||
211 | builder.clause( | ||
212 | personView.call(x), | ||
213 | not(friendView.call(x, Variable.of())) | ||
214 | ); | ||
215 | }); | ||
216 | var query = Query.of("Actual", (builder, p1) -> builder.clause( | ||
217 | personView.call(p1), | ||
218 | not(dnf.call(p1)) | ||
219 | )); | ||
220 | |||
221 | var actual = sut.rewrite(query); | ||
222 | |||
223 | assertThat(actual, is(query)); | ||
224 | } | ||
225 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/TermSubstitutionTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/TermSubstitutionTest.java new file mode 100644 index 00000000..52b21692 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/TermSubstitutionTest.java | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term; | ||
7 | |||
8 | import org.junit.jupiter.api.Assertions; | ||
9 | import org.junit.jupiter.params.ParameterizedTest; | ||
10 | import org.junit.jupiter.params.provider.Arguments; | ||
11 | import org.junit.jupiter.params.provider.MethodSource; | ||
12 | import tools.refinery.logic.equality.DnfEqualityChecker; | ||
13 | import tools.refinery.logic.equality.SubstitutingLiteralEqualityHelper; | ||
14 | import tools.refinery.logic.substitution.Substitution; | ||
15 | import tools.refinery.logic.term.bool.BoolTerms; | ||
16 | import tools.refinery.logic.term.int_.IntTerms; | ||
17 | import tools.refinery.logic.term.real.RealTerms; | ||
18 | import tools.refinery.logic.term.uppercardinality.UpperCardinality; | ||
19 | import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms; | ||
20 | |||
21 | import java.util.List; | ||
22 | import java.util.stream.Stream; | ||
23 | |||
24 | class TermSubstitutionTest { | ||
25 | private final static DataVariable<Integer> intA = Variable.of("intA", Integer.class); | ||
26 | private final static DataVariable<Integer> intB = Variable.of("intB", Integer.class); | ||
27 | private final static DataVariable<Double> realA = Variable.of("realA", Double.class); | ||
28 | private final static DataVariable<Double> realB = Variable.of("realB", Double.class); | ||
29 | private final static DataVariable<Boolean> boolA = Variable.of("boolA", Boolean.class); | ||
30 | private final static DataVariable<Boolean> boolB = Variable.of("boolB", Boolean.class); | ||
31 | private final static DataVariable<UpperCardinality> upperCardinalityA = Variable.of("upperCardinalityA", | ||
32 | UpperCardinality.class); | ||
33 | private final static DataVariable<UpperCardinality> upperCardinalityB = Variable.of("upperCardinalityB", | ||
34 | UpperCardinality.class); | ||
35 | private final static Substitution substitution = Substitution.builder() | ||
36 | .put(intA, intB) | ||
37 | .put(intB, intA) | ||
38 | .put(realA, realB) | ||
39 | .put(realB, realA) | ||
40 | .put(boolA, boolB) | ||
41 | .put(boolB, boolA) | ||
42 | .put(upperCardinalityA, upperCardinalityB) | ||
43 | .put(upperCardinalityB, upperCardinalityA) | ||
44 | .build(); | ||
45 | |||
46 | @ParameterizedTest | ||
47 | @MethodSource | ||
48 | void substitutionTest(AnyTerm term) { | ||
49 | var substitutedTerm1 = term.substitute(substitution); | ||
50 | Assertions.assertNotEquals(term, substitutedTerm1, "Original term is not equal to substituted term"); | ||
51 | var helper = new SubstitutingLiteralEqualityHelper(DnfEqualityChecker.DEFAULT, List.of(), List.of()); | ||
52 | Assertions.assertTrue(term.equalsWithSubstitution(helper, substitutedTerm1), "Terms are equal by helper"); | ||
53 | // The {@link #substitution} is its own inverse. | ||
54 | var substitutedTerm2 = substitutedTerm1.substitute(substitution); | ||
55 | Assertions.assertEquals(term, substitutedTerm2, "Original term is not equal to back-substituted term"); | ||
56 | } | ||
57 | |||
58 | static Stream<Arguments> substitutionTest() { | ||
59 | return Stream.of( | ||
60 | Arguments.of(IntTerms.plus(intA)), | ||
61 | Arguments.of(IntTerms.minus(intA)), | ||
62 | Arguments.of(IntTerms.add(intA, intB)), | ||
63 | Arguments.of(IntTerms.sub(intA, intB)), | ||
64 | Arguments.of(IntTerms.mul(intA, intB)), | ||
65 | Arguments.of(IntTerms.div(intA, intB)), | ||
66 | Arguments.of(IntTerms.pow(intA, intB)), | ||
67 | Arguments.of(IntTerms.min(intA, intB)), | ||
68 | Arguments.of(IntTerms.max(intA, intB)), | ||
69 | Arguments.of(IntTerms.eq(intA, intB)), | ||
70 | Arguments.of(IntTerms.notEq(intA, intB)), | ||
71 | Arguments.of(IntTerms.less(intA, intB)), | ||
72 | Arguments.of(IntTerms.lessEq(intA, intB)), | ||
73 | Arguments.of(IntTerms.greater(intA, intB)), | ||
74 | Arguments.of(IntTerms.greaterEq(intA, intB)), | ||
75 | Arguments.of(IntTerms.asInt(realA)), | ||
76 | Arguments.of(RealTerms.plus(realA)), | ||
77 | Arguments.of(RealTerms.minus(realA)), | ||
78 | Arguments.of(RealTerms.add(realA, realB)), | ||
79 | Arguments.of(RealTerms.sub(realA, realB)), | ||
80 | Arguments.of(RealTerms.mul(realA, realB)), | ||
81 | Arguments.of(RealTerms.div(realA, realB)), | ||
82 | Arguments.of(RealTerms.pow(realA, realB)), | ||
83 | Arguments.of(RealTerms.min(realA, realB)), | ||
84 | Arguments.of(RealTerms.max(realA, realB)), | ||
85 | Arguments.of(RealTerms.asReal(intA)), | ||
86 | Arguments.of(BoolTerms.not(boolA)), | ||
87 | Arguments.of(BoolTerms.and(boolA, boolB)), | ||
88 | Arguments.of(BoolTerms.or(boolA, boolB)), | ||
89 | Arguments.of(BoolTerms.xor(boolA, boolB)), | ||
90 | Arguments.of(RealTerms.eq(realA, realB)), | ||
91 | Arguments.of(UpperCardinalityTerms.add(upperCardinalityA, upperCardinalityB)), | ||
92 | Arguments.of(UpperCardinalityTerms.mul(upperCardinalityA, upperCardinalityB)), | ||
93 | Arguments.of(UpperCardinalityTerms.min(upperCardinalityA, upperCardinalityB)), | ||
94 | Arguments.of(UpperCardinalityTerms.max(upperCardinalityA, upperCardinalityB)) | ||
95 | ); | ||
96 | } | ||
97 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/bool/BoolTermsEvaluateTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/bool/BoolTermsEvaluateTest.java new file mode 100644 index 00000000..7f65591f --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/bool/BoolTermsEvaluateTest.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.bool; | ||
7 | |||
8 | import org.junit.jupiter.params.ParameterizedTest; | ||
9 | import org.junit.jupiter.params.provider.CsvSource; | ||
10 | import tools.refinery.logic.term.bool.BoolTerms; | ||
11 | import tools.refinery.logic.valuation.Valuation; | ||
12 | |||
13 | import static org.hamcrest.MatcherAssert.assertThat; | ||
14 | import static org.hamcrest.Matchers.is; | ||
15 | |||
16 | class BoolTermsEvaluateTest { | ||
17 | @ParameterizedTest(name = "!{0} == {1}") | ||
18 | @CsvSource(value = { | ||
19 | "false, true", | ||
20 | "true, false", | ||
21 | "null, null" | ||
22 | }, nullValues = "null") | ||
23 | void notTest(Boolean a, Boolean result) { | ||
24 | var term = BoolTerms.not(BoolTerms.constant(a)); | ||
25 | assertThat(term.getType(), is(Boolean.class)); | ||
26 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
27 | } | ||
28 | |||
29 | @ParameterizedTest(name = "{0} && {1} == {2}") | ||
30 | @CsvSource(value = { | ||
31 | "false, false, false", | ||
32 | "false, true, false", | ||
33 | "true, false, false", | ||
34 | "true, true, true", | ||
35 | "false, null, null", | ||
36 | "null, false, null", | ||
37 | "null, null, null" | ||
38 | }, nullValues = "null") | ||
39 | void andTest(Boolean a, Boolean b, Boolean result) { | ||
40 | var term = BoolTerms.and(BoolTerms.constant(a), BoolTerms.constant(b)); | ||
41 | assertThat(term.getType(), is(Boolean.class)); | ||
42 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
43 | } | ||
44 | |||
45 | @ParameterizedTest(name = "{0} || {1} == {2}") | ||
46 | @CsvSource(value = { | ||
47 | "false, false, false", | ||
48 | "false, true, true", | ||
49 | "true, false, true", | ||
50 | "true, true, true", | ||
51 | "true, null, null", | ||
52 | "null, true, null", | ||
53 | "null, null, null" | ||
54 | }, nullValues = "null") | ||
55 | void orTest(Boolean a, Boolean b, Boolean result) { | ||
56 | var term = BoolTerms.or(BoolTerms.constant(a), BoolTerms.constant(b)); | ||
57 | assertThat(term.getType(), is(Boolean.class)); | ||
58 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
59 | } | ||
60 | |||
61 | @ParameterizedTest(name = "{0} ^^ {1} == {2}") | ||
62 | @CsvSource(value = { | ||
63 | "false, false, false", | ||
64 | "false, true, true", | ||
65 | "true, false, true", | ||
66 | "true, true, false", | ||
67 | "false, null, null", | ||
68 | "null, false, null", | ||
69 | "null, null, null" | ||
70 | }, nullValues = "null") | ||
71 | void xorTest(Boolean a, Boolean b, Boolean result) { | ||
72 | var term = BoolTerms.xor(BoolTerms.constant(a), BoolTerms.constant(b)); | ||
73 | assertThat(term.getType(), is(Boolean.class)); | ||
74 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
75 | } | ||
76 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/CardinalityIntervalTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/CardinalityIntervalTest.java new file mode 100644 index 00000000..ee2dd61c --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/CardinalityIntervalTest.java | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.cardinalityinterval; | ||
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 | |||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import static org.hamcrest.MatcherAssert.assertThat; | ||
15 | import static org.hamcrest.Matchers.equalTo; | ||
16 | import static tools.refinery.logic.term.cardinalityinterval.CardinalityIntervals.*; | ||
17 | |||
18 | class CardinalityIntervalTest { | ||
19 | @ParameterizedTest(name = "min({0}, {1}) == {2}") | ||
20 | @MethodSource | ||
21 | void minTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
22 | assertThat(a.min(b), equalTo(expected)); | ||
23 | } | ||
24 | |||
25 | static Stream<Arguments> minTest() { | ||
26 | return Stream.of( | ||
27 | Arguments.of(atMost(1), atMost(1), atMost(1)), | ||
28 | Arguments.of(atMost(1), between(2, 3), atMost(1)), | ||
29 | Arguments.of(atMost(1), atLeast(2), atMost(1)), | ||
30 | Arguments.of(atMost(1), ERROR, ERROR), | ||
31 | Arguments.of(atLeast(1), atLeast(2), atLeast(1)), | ||
32 | Arguments.of(atLeast(1), ERROR, ERROR), | ||
33 | Arguments.of(ERROR, atLeast(2), ERROR), | ||
34 | Arguments.of(ERROR, ERROR, ERROR) | ||
35 | ); | ||
36 | } | ||
37 | |||
38 | @ParameterizedTest(name = "max({0}, {1}) == {2}") | ||
39 | @MethodSource | ||
40 | void maxTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
41 | assertThat(a.max(b), equalTo(expected)); | ||
42 | } | ||
43 | |||
44 | static Stream<Arguments> maxTest() { | ||
45 | return Stream.of( | ||
46 | Arguments.of(atMost(1), atMost(1), atMost(1)), | ||
47 | Arguments.of(atMost(1), between(2, 3), between(2, 3)), | ||
48 | Arguments.of(atMost(1), atLeast(2), atLeast(2)), | ||
49 | Arguments.of(atMost(1), ERROR, ERROR), | ||
50 | Arguments.of(atLeast(1), atLeast(2), atLeast(2)), | ||
51 | Arguments.of(atLeast(1), ERROR, ERROR), | ||
52 | Arguments.of(ERROR, atLeast(2), ERROR), | ||
53 | Arguments.of(ERROR, ERROR, ERROR) | ||
54 | ); | ||
55 | } | ||
56 | |||
57 | @ParameterizedTest(name = "{0} + {1} == {2}") | ||
58 | @MethodSource | ||
59 | void addTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
60 | assertThat(a.add(b), equalTo(expected)); | ||
61 | } | ||
62 | |||
63 | static Stream<Arguments> addTest() { | ||
64 | return Stream.of( | ||
65 | Arguments.of(atMost(1), atMost(1), atMost(2)), | ||
66 | Arguments.of(atMost(1), between(2, 3), between(2, 4)), | ||
67 | Arguments.of(atMost(1), atLeast(2), atLeast(2)), | ||
68 | Arguments.of(atMost(1), ERROR, ERROR), | ||
69 | Arguments.of(atLeast(1), atLeast(2), atLeast(3)), | ||
70 | Arguments.of(atLeast(1), ERROR, ERROR), | ||
71 | Arguments.of(ERROR, atLeast(2), ERROR), | ||
72 | Arguments.of(ERROR, ERROR, ERROR) | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
77 | @MethodSource | ||
78 | void multiplyTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
79 | assertThat(a.multiply(b), equalTo(expected)); | ||
80 | } | ||
81 | |||
82 | static Stream<Arguments> multiplyTest() { | ||
83 | return Stream.of( | ||
84 | Arguments.of(between(2, 3), between(4, 5), between(8, 15)), | ||
85 | Arguments.of(atLeast(2), between(4, 5), atLeast(8)), | ||
86 | Arguments.of(between(2, 3), atLeast(4), atLeast(8)), | ||
87 | Arguments.of(between(2, 3), ERROR, ERROR), | ||
88 | Arguments.of(ERROR, between(4, 5), ERROR), | ||
89 | Arguments.of(ERROR, ERROR, ERROR) | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | @ParameterizedTest(name = "{0} /\\ {1} == {2}") | ||
94 | @MethodSource | ||
95 | void meetTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
96 | assertThat(a.meet(b), equalTo(expected)); | ||
97 | } | ||
98 | |||
99 | static Stream<Arguments> meetTest() { | ||
100 | return Stream.of( | ||
101 | Arguments.of(atMost(1), atMost(2), atMost(1)), | ||
102 | Arguments.of(atMost(2), between(1, 3), between(1, 2)), | ||
103 | Arguments.of(atMost(1), between(1, 3), exactly(1)), | ||
104 | Arguments.of(atMost(1), between(2, 3), ERROR), | ||
105 | Arguments.of(atMost(1), ERROR, ERROR), | ||
106 | Arguments.of(ERROR, atMost(1), ERROR), | ||
107 | Arguments.of(ERROR, ERROR, ERROR) | ||
108 | ); | ||
109 | } | ||
110 | |||
111 | @ParameterizedTest(name = "{0} \\/ {1} == {2}") | ||
112 | @MethodSource | ||
113 | void joinTest(CardinalityInterval a, CardinalityInterval b, CardinalityInterval expected) { | ||
114 | assertThat(a.join(b), equalTo(expected)); | ||
115 | } | ||
116 | |||
117 | static Stream<Arguments> joinTest() { | ||
118 | return Stream.of( | ||
119 | Arguments.of(atMost(1), atMost(2), atMost(2)), | ||
120 | Arguments.of(atMost(2), between(1, 3), atMost(3)), | ||
121 | Arguments.of(atMost(1), between(2, 3), atMost(3)), | ||
122 | Arguments.of(atMost(1), ERROR, atMost(1)), | ||
123 | Arguments.of(ERROR, atMost(1), atMost(1)), | ||
124 | Arguments.of(ERROR, ERROR, ERROR) | ||
125 | ); | ||
126 | } | ||
127 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/CardinalityIntervalsTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/CardinalityIntervalsTest.java new file mode 100644 index 00000000..d68df335 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/CardinalityIntervalsTest.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 | */ | ||
6 | package tools.refinery.logic.term.cardinalityinterval; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.term.uppercardinality.UpperCardinalities; | ||
10 | |||
11 | import static org.hamcrest.MatcherAssert.assertThat; | ||
12 | import static org.hamcrest.Matchers.equalTo; | ||
13 | |||
14 | class CardinalityIntervalsTest { | ||
15 | @Test | ||
16 | void betweenEmptyTest() { | ||
17 | var interval = CardinalityIntervals.between(2, 1); | ||
18 | assertThat(interval.isError(), equalTo(true)); | ||
19 | } | ||
20 | |||
21 | @Test | ||
22 | void betweenNegativeUpperBoundTest() { | ||
23 | var interval = CardinalityIntervals.between(0, -1); | ||
24 | assertThat(interval.upperBound(), equalTo(UpperCardinalities.UNBOUNDED)); | ||
25 | assertThat(interval.isError(), equalTo(false)); | ||
26 | } | ||
27 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/EmptyCardinalityIntervalTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/EmptyCardinalityIntervalTest.java new file mode 100644 index 00000000..0dbc7f61 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/EmptyCardinalityIntervalTest.java | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.cardinalityinterval; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | |||
10 | import static org.hamcrest.MatcherAssert.assertThat; | ||
11 | import static org.hamcrest.Matchers.lessThan; | ||
12 | |||
13 | class EmptyCardinalityIntervalTest { | ||
14 | @Test | ||
15 | void inconsistentBoundsTest() { | ||
16 | assertThat(CardinalityIntervals.ERROR.upperBound().compareToInt(CardinalityIntervals.ERROR.lowerBound()), | ||
17 | lessThan(0)); | ||
18 | } | ||
19 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/FiniteCardinalityIntervalTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/FiniteCardinalityIntervalTest.java new file mode 100644 index 00000000..588b25ab --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/cardinalityinterval/FiniteCardinalityIntervalTest.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 | */ | ||
6 | package tools.refinery.logic.term.cardinalityinterval; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.term.uppercardinality.UpperCardinality; | ||
10 | import tools.refinery.logic.term.uppercardinality.UpperCardinalities; | ||
11 | |||
12 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
13 | |||
14 | class FiniteCardinalityIntervalTest { | ||
15 | @Test | ||
16 | void invalidLowerBoundConstructorTest() { | ||
17 | assertThrows(IllegalArgumentException.class, () -> new NonEmptyCardinalityInterval(-1, | ||
18 | UpperCardinalities.UNBOUNDED)); | ||
19 | } | ||
20 | |||
21 | @Test | ||
22 | void invalidUpperBoundConstructorTest() { | ||
23 | var upperCardinality = UpperCardinality.of(1); | ||
24 | assertThrows(IllegalArgumentException.class, () -> new NonEmptyCardinalityInterval(2, | ||
25 | upperCardinality)); | ||
26 | } | ||
27 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/int_/IntTermsEvaluateTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/int_/IntTermsEvaluateTest.java new file mode 100644 index 00000000..55d9b740 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/int_/IntTermsEvaluateTest.java | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.int_; | ||
7 | |||
8 | import org.junit.jupiter.params.ParameterizedTest; | ||
9 | import org.junit.jupiter.params.provider.Arguments; | ||
10 | import org.junit.jupiter.params.provider.CsvSource; | ||
11 | import org.junit.jupiter.params.provider.MethodSource; | ||
12 | import tools.refinery.logic.term.int_.IntTerms; | ||
13 | import tools.refinery.logic.term.real.RealTerms; | ||
14 | import tools.refinery.logic.valuation.Valuation; | ||
15 | |||
16 | import java.util.stream.Stream; | ||
17 | |||
18 | import static org.hamcrest.Matchers.is; | ||
19 | import static org.hamcrest.MatcherAssert.assertThat; | ||
20 | |||
21 | class IntTermsEvaluateTest { | ||
22 | @ParameterizedTest(name = "+{0} == {1}") | ||
23 | @CsvSource(value = { | ||
24 | "2, 2", | ||
25 | "null, null" | ||
26 | }, nullValues = "null") | ||
27 | void plusTest(Integer a, Integer result) { | ||
28 | var term = IntTerms.plus(IntTerms.constant(a)); | ||
29 | assertThat(term.getType(), is(Integer.class)); | ||
30 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
31 | } | ||
32 | |||
33 | @ParameterizedTest(name = "-{0} == {1}") | ||
34 | @CsvSource(value = { | ||
35 | "2, -2", | ||
36 | "null, null" | ||
37 | }, nullValues = "null") | ||
38 | void minusTest(Integer a, Integer result) { | ||
39 | var term = IntTerms.minus(IntTerms.constant(a)); | ||
40 | assertThat(term.getType(), is(Integer.class)); | ||
41 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
42 | } | ||
43 | |||
44 | @ParameterizedTest(name = "{0} + {1} == {2}") | ||
45 | @CsvSource(value = { | ||
46 | "1, 2, 3", | ||
47 | "null, 2, null", | ||
48 | "1, null, null", | ||
49 | "null, null, null" | ||
50 | }, nullValues = "null") | ||
51 | void addTest(Integer a, Integer b, Integer result) { | ||
52 | var term = IntTerms.add(IntTerms.constant(a), IntTerms.constant(b)); | ||
53 | assertThat(term.getType(), is(Integer.class)); | ||
54 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
55 | } | ||
56 | |||
57 | @ParameterizedTest(name = "{0} - {1} == {2}") | ||
58 | @CsvSource(value = { | ||
59 | "1, 3, -2", | ||
60 | "null, 3, null", | ||
61 | "1, null, null", | ||
62 | "null, null, null" | ||
63 | }, nullValues = "null") | ||
64 | void subTest(Integer a, Integer b, Integer result) { | ||
65 | var term = IntTerms.sub(IntTerms.constant(a), IntTerms.constant(b)); | ||
66 | assertThat(term.getType(), is(Integer.class)); | ||
67 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
68 | } | ||
69 | |||
70 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
71 | @CsvSource(value = { | ||
72 | "2, 3, 6", | ||
73 | "null, 3, null", | ||
74 | "2, null, null", | ||
75 | "null, null, null" | ||
76 | }, nullValues = "null") | ||
77 | void mulTest(Integer a, Integer b, Integer result) { | ||
78 | var term = IntTerms.mul(IntTerms.constant(a), IntTerms.constant(b)); | ||
79 | assertThat(term.getType(), is(Integer.class)); | ||
80 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
81 | } | ||
82 | |||
83 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
84 | @CsvSource(value = { | ||
85 | "6, 3, 2", | ||
86 | "7, 3, 2", | ||
87 | "6, 0, null", | ||
88 | "null, 3, null", | ||
89 | "6, null, null", | ||
90 | "null, null, null" | ||
91 | }, nullValues = "null") | ||
92 | void divTest(Integer a, Integer b, Integer result) { | ||
93 | var term = IntTerms.div(IntTerms.constant(a), IntTerms.constant(b)); | ||
94 | assertThat(term.getType(), is(Integer.class)); | ||
95 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
96 | } | ||
97 | |||
98 | @ParameterizedTest(name = "{0} ** {1} == {2}") | ||
99 | @CsvSource(value = { | ||
100 | "1, 0, 1", | ||
101 | "1, 3, 1", | ||
102 | "1, -3, null", | ||
103 | "2, 0, 1", | ||
104 | "2, 2, 4", | ||
105 | "2, 3, 8", | ||
106 | "2, 4, 16", | ||
107 | "2, 5, 32", | ||
108 | "2, 6, 64", | ||
109 | "2, -3, null", | ||
110 | "null, 3, null", | ||
111 | "2, null, null", | ||
112 | "null, null, null" | ||
113 | }, nullValues = "null") | ||
114 | void powTest(Integer a, Integer b, Integer result) { | ||
115 | var term = IntTerms.pow(IntTerms.constant(a), IntTerms.constant(b)); | ||
116 | assertThat(term.getType(), is(Integer.class)); | ||
117 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
118 | } | ||
119 | |||
120 | @ParameterizedTest(name = "min({0}, {1}) == {2}") | ||
121 | @CsvSource(value = { | ||
122 | "1, 2, 1", | ||
123 | "2, 1, 1", | ||
124 | "null, 2, null", | ||
125 | "1, null, null", | ||
126 | "null, null, null" | ||
127 | }, nullValues = "null") | ||
128 | void minTest(Integer a, Integer b, Integer result) { | ||
129 | var term = IntTerms.min(IntTerms.constant(a), IntTerms.constant(b)); | ||
130 | assertThat(term.getType(), is(Integer.class)); | ||
131 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
132 | } | ||
133 | |||
134 | @ParameterizedTest(name = "max({0}, {1}) == {2}") | ||
135 | @CsvSource(value = { | ||
136 | "1, 2, 2", | ||
137 | "2, 1, 2", | ||
138 | "null, 2, null", | ||
139 | "1, null, null", | ||
140 | "null, null, null" | ||
141 | }, nullValues = "null") | ||
142 | void maxTest(Integer a, Integer b, Integer result) { | ||
143 | var term = IntTerms.max(IntTerms.constant(a), IntTerms.constant(b)); | ||
144 | assertThat(term.getType(), is(Integer.class)); | ||
145 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
146 | } | ||
147 | |||
148 | @ParameterizedTest(name = "({0} == {1}) == {2}") | ||
149 | @CsvSource(value = { | ||
150 | "1, 1, true", | ||
151 | "1, 2, false", | ||
152 | "null, 1, null", | ||
153 | "1, null, null", | ||
154 | "null, null, null" | ||
155 | }, nullValues = "null") | ||
156 | void eqTest(Integer a, Integer b, Boolean result) { | ||
157 | var term = IntTerms.eq(IntTerms.constant(a), IntTerms.constant(b)); | ||
158 | assertThat(term.getType(), is(Boolean.class)); | ||
159 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
160 | } | ||
161 | |||
162 | @ParameterizedTest(name = "({0} != {1}) == {2}") | ||
163 | @CsvSource(value = { | ||
164 | "1, 1, false", | ||
165 | "1, 2, true", | ||
166 | "null, 1, null", | ||
167 | "1, null, null", | ||
168 | "null, null, null" | ||
169 | }, nullValues = "null") | ||
170 | void notEqTest(Integer a, Integer b, Boolean result) { | ||
171 | var term = IntTerms.notEq(IntTerms.constant(a), IntTerms.constant(b)); | ||
172 | assertThat(term.getType(), is(Boolean.class)); | ||
173 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
174 | } | ||
175 | |||
176 | @ParameterizedTest(name = "({0} < {1}) == {2}") | ||
177 | @CsvSource(value = { | ||
178 | "1, -2, false", | ||
179 | "1, 1, false", | ||
180 | "1, 2, true", | ||
181 | "null, 1, null", | ||
182 | "1, null, null", | ||
183 | "null, null, null" | ||
184 | }, nullValues = "null") | ||
185 | void lessTest(Integer a, Integer b, Boolean result) { | ||
186 | var term = IntTerms.less(IntTerms.constant(a), IntTerms.constant(b)); | ||
187 | assertThat(term.getType(), is(Boolean.class)); | ||
188 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
189 | } | ||
190 | |||
191 | @ParameterizedTest(name = "({0} <= {1}) == {2}") | ||
192 | @CsvSource(value = { | ||
193 | "1, -2, false", | ||
194 | "1, 1, true", | ||
195 | "1, 2, true", | ||
196 | "null, 1, null", | ||
197 | "1, null, null", | ||
198 | "null, null, null" | ||
199 | }, nullValues = "null") | ||
200 | void lessEqTest(Integer a, Integer b, Boolean result) { | ||
201 | var term = IntTerms.lessEq(IntTerms.constant(a), IntTerms.constant(b)); | ||
202 | assertThat(term.getType(), is(Boolean.class)); | ||
203 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
204 | } | ||
205 | |||
206 | @ParameterizedTest(name = "({0} > {1}) == {2}") | ||
207 | @CsvSource(value = { | ||
208 | "1, -2, true", | ||
209 | "1, 1, false", | ||
210 | "1, 2, false", | ||
211 | "null, 1, null", | ||
212 | "1, null, null", | ||
213 | "null, null, null" | ||
214 | }, nullValues = "null") | ||
215 | void greaterTest(Integer a, Integer b, Boolean result) { | ||
216 | var term = IntTerms.greater(IntTerms.constant(a), IntTerms.constant(b)); | ||
217 | assertThat(term.getType(), is(Boolean.class)); | ||
218 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
219 | } | ||
220 | |||
221 | @ParameterizedTest(name = "({0} >= {1}) == {2}") | ||
222 | @CsvSource(value = { | ||
223 | "1, -2, true", | ||
224 | "1, 1, true", | ||
225 | "1, 2, false", | ||
226 | "null, 1, null", | ||
227 | "1, null, null", | ||
228 | "null, null, null" | ||
229 | }, nullValues = "null") | ||
230 | void greaterEqTest(Integer a, Integer b, Boolean result) { | ||
231 | var term = IntTerms.greaterEq(IntTerms.constant(a), IntTerms.constant(b)); | ||
232 | assertThat(term.getType(), is(Boolean.class)); | ||
233 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
234 | } | ||
235 | |||
236 | @ParameterizedTest(name = "{0} as int == {1}") | ||
237 | @MethodSource | ||
238 | void asIntTest(Double a, Integer result) { | ||
239 | var term = IntTerms.asInt(RealTerms.constant(a)); | ||
240 | assertThat(term.getType(), is(Integer.class)); | ||
241 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
242 | } | ||
243 | |||
244 | static Stream<Arguments> asIntTest() { | ||
245 | return Stream.of( | ||
246 | Arguments.of(2.0, 2), | ||
247 | Arguments.of(2.1, 2), | ||
248 | Arguments.of(2.9, 2), | ||
249 | Arguments.of(-2.0, -2), | ||
250 | Arguments.of(-2.1, -2), | ||
251 | Arguments.of(-2.9, -2), | ||
252 | Arguments.of(0.0, 0), | ||
253 | Arguments.of(-0.0, 0), | ||
254 | Arguments.of(Double.POSITIVE_INFINITY, Integer.MAX_VALUE), | ||
255 | Arguments.of(Double.NEGATIVE_INFINITY, Integer.MIN_VALUE), | ||
256 | Arguments.of(Double.NaN, null), | ||
257 | Arguments.of(null, null) | ||
258 | ); | ||
259 | } | ||
260 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/real/RealTermEvaluateTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/real/RealTermEvaluateTest.java new file mode 100644 index 00000000..042d1807 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/real/RealTermEvaluateTest.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 | */ | ||
6 | package tools.refinery.logic.term.real; | ||
7 | |||
8 | import org.hamcrest.Matcher; | ||
9 | import org.junit.jupiter.params.ParameterizedTest; | ||
10 | import org.junit.jupiter.params.provider.CsvSource; | ||
11 | import tools.refinery.logic.term.int_.IntTerms; | ||
12 | import tools.refinery.logic.term.real.RealTerms; | ||
13 | import tools.refinery.logic.valuation.Valuation; | ||
14 | |||
15 | import static org.hamcrest.MatcherAssert.assertThat; | ||
16 | import static org.hamcrest.Matchers.*; | ||
17 | |||
18 | class RealTermEvaluateTest { | ||
19 | public static final double TOLERANCE = 1e-6; | ||
20 | |||
21 | private static Matcher<Double> closeToOrNull(Double expected) { | ||
22 | return expected == null ? nullValue(Double.class) : closeTo(expected, TOLERANCE); | ||
23 | } | ||
24 | |||
25 | @ParameterizedTest(name = "+{0} == {1}") | ||
26 | @CsvSource(value = { | ||
27 | "2.5, 2.5", | ||
28 | "null, null" | ||
29 | }, nullValues = "null") | ||
30 | void plusTest(Double a, Double result) { | ||
31 | var term = RealTerms.plus(RealTerms.constant(a)); | ||
32 | assertThat(term.getType(), is(Double.class)); | ||
33 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
34 | } | ||
35 | |||
36 | @ParameterizedTest(name = "-{0} == {1}") | ||
37 | @CsvSource(value = { | ||
38 | "2.5, -2.5", | ||
39 | "null, null" | ||
40 | }, nullValues = "null") | ||
41 | void minusTest(Double a, Double result) { | ||
42 | var term = RealTerms.minus(RealTerms.constant(a)); | ||
43 | assertThat(term.getType(), is(Double.class)); | ||
44 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
45 | } | ||
46 | |||
47 | @ParameterizedTest(name = "{0} + {1} == {2}") | ||
48 | @CsvSource(value = { | ||
49 | "1.2, 2.3, 3.5", | ||
50 | "null, 2.3, null", | ||
51 | "1.2, null, null", | ||
52 | "null, null, null" | ||
53 | }, nullValues = "null") | ||
54 | void addTest(Double a, Double b, Double result) { | ||
55 | var term = RealTerms.add(RealTerms.constant(a), RealTerms.constant(b)); | ||
56 | assertThat(term.getType(), is(Double.class)); | ||
57 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
58 | } | ||
59 | |||
60 | @ParameterizedTest(name = "{0} - {1} == {2}") | ||
61 | @CsvSource(value = { | ||
62 | "1.2, 3.4, -2.2", | ||
63 | "null, 3.4, null", | ||
64 | "1.2, null, null", | ||
65 | "null, null, null" | ||
66 | }, nullValues = "null") | ||
67 | void subTest(Double a, Double b, Double result) { | ||
68 | var term = RealTerms.sub(RealTerms.constant(a), RealTerms.constant(b)); | ||
69 | assertThat(term.getType(), is(Double.class)); | ||
70 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
71 | } | ||
72 | |||
73 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
74 | @CsvSource(value = { | ||
75 | "2.3, 3.4, 7.82", | ||
76 | "null, 3.4, null", | ||
77 | "2.3, null, null", | ||
78 | "null, null, null" | ||
79 | }, nullValues = "null") | ||
80 | void mulTest(Double a, Double b, Double result) { | ||
81 | var term = RealTerms.mul(RealTerms.constant(a), RealTerms.constant(b)); | ||
82 | assertThat(term.getType(), is(Double.class)); | ||
83 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
84 | } | ||
85 | |||
86 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
87 | @CsvSource(value = { | ||
88 | "7.82, 3.4, 2.3", | ||
89 | "null, 3.4, null", | ||
90 | "7.82, null, null", | ||
91 | "null, null, null" | ||
92 | }, nullValues = "null") | ||
93 | void divTest(Double a, Double b, Double result) { | ||
94 | var term = RealTerms.div(RealTerms.constant(a), RealTerms.constant(b)); | ||
95 | assertThat(term.getType(), is(Double.class)); | ||
96 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
97 | } | ||
98 | |||
99 | @ParameterizedTest(name = "{0} ** {1} == {2}") | ||
100 | @CsvSource(value = { | ||
101 | "2.0, 6.0, 64.0", | ||
102 | "null, 6.0, null", | ||
103 | "2.0, null, null", | ||
104 | "null, null, null" | ||
105 | }, nullValues = "null") | ||
106 | void powTest(Double a, Double b, Double result) { | ||
107 | var term = RealTerms.pow(RealTerms.constant(a), RealTerms.constant(b)); | ||
108 | assertThat(term.getType(), is(Double.class)); | ||
109 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
110 | } | ||
111 | |||
112 | @ParameterizedTest(name = "min({0}, {1}) == {2}") | ||
113 | @CsvSource(value = { | ||
114 | "1.5, 2.7, 1.5", | ||
115 | "2.7, 1.5, 1.5", | ||
116 | "null, 2.7, null", | ||
117 | "1.5, null, null", | ||
118 | "null, null, null" | ||
119 | }, nullValues = "null") | ||
120 | void minTest(Double a, Double b, Double result) { | ||
121 | var term = RealTerms.min(RealTerms.constant(a), RealTerms.constant(b)); | ||
122 | assertThat(term.getType(), is(Double.class)); | ||
123 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
124 | } | ||
125 | |||
126 | @ParameterizedTest(name = "max({0}, {1}) == {2}") | ||
127 | @CsvSource(value = { | ||
128 | "1.5, 2.7, 2.7", | ||
129 | "2.7, 1.7, 2.7", | ||
130 | "null, 2.7, null", | ||
131 | "1.5, null, null", | ||
132 | "null, null, null" | ||
133 | }, nullValues = "null") | ||
134 | void maxTest(Double a, Double b, Double result) { | ||
135 | var term = RealTerms.max(RealTerms.constant(a), RealTerms.constant(b)); | ||
136 | assertThat(term.getType(), is(Double.class)); | ||
137 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
138 | } | ||
139 | |||
140 | @ParameterizedTest(name = "({0} == {1}) == {2}") | ||
141 | @CsvSource(value = { | ||
142 | "1.5, 1.5, true", | ||
143 | "1.5, 2.7, false", | ||
144 | "null, 1.5, null", | ||
145 | "1.5, null, null", | ||
146 | "null, null, null" | ||
147 | }, nullValues = "null") | ||
148 | void eqTest(Double a, Double b, Boolean result) { | ||
149 | var term = RealTerms.eq(RealTerms.constant(a), RealTerms.constant(b)); | ||
150 | assertThat(term.getType(), is(Boolean.class)); | ||
151 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
152 | } | ||
153 | |||
154 | @ParameterizedTest(name = "({0} != {1}) == {2}") | ||
155 | @CsvSource(value = { | ||
156 | "1.5, 1.5, false", | ||
157 | "1.5, 2.7, true", | ||
158 | "null, 1.5, null", | ||
159 | "1.5, null, null", | ||
160 | "null, null, null" | ||
161 | }, nullValues = "null") | ||
162 | void notEqTest(Double a, Double b, Boolean result) { | ||
163 | var term = RealTerms.notEq(RealTerms.constant(a), RealTerms.constant(b)); | ||
164 | assertThat(term.getType(), is(Boolean.class)); | ||
165 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
166 | } | ||
167 | |||
168 | @ParameterizedTest(name = "({0} < {1}) == {2}") | ||
169 | @CsvSource(value = { | ||
170 | "1.5, -2.7, false", | ||
171 | "1.5, 1.5, false", | ||
172 | "1.5, 2.7, true", | ||
173 | "null, 1.5, null", | ||
174 | "1.5, null, null", | ||
175 | "null, null, null" | ||
176 | }, nullValues = "null") | ||
177 | void lessTest(Double a, Double b, Boolean result) { | ||
178 | var term = RealTerms.less(RealTerms.constant(a), RealTerms.constant(b)); | ||
179 | assertThat(term.getType(), is(Boolean.class)); | ||
180 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
181 | } | ||
182 | |||
183 | @ParameterizedTest(name = "({0} <= {1}) == {2}") | ||
184 | @CsvSource(value = { | ||
185 | "1.5, -2.7, false", | ||
186 | "1.5, 1.5, true", | ||
187 | "1.5, 2.7, true", | ||
188 | "null, 1.5, null", | ||
189 | "1.5, null, null", | ||
190 | "null, null, null" | ||
191 | }, nullValues = "null") | ||
192 | void lessEqTest(Double a, Double b, Boolean result) { | ||
193 | var term = RealTerms.lessEq(RealTerms.constant(a), RealTerms.constant(b)); | ||
194 | assertThat(term.getType(), is(Boolean.class)); | ||
195 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
196 | } | ||
197 | |||
198 | @ParameterizedTest(name = "({0} > {1}) == {2}") | ||
199 | @CsvSource(value = { | ||
200 | "1.5, -2.7, true", | ||
201 | "1.5, 1.5, false", | ||
202 | "1.5, 2.7, false", | ||
203 | "null, 1.5, null", | ||
204 | "1.5, null, null", | ||
205 | "null, null, null" | ||
206 | }, nullValues = "null") | ||
207 | void greaterTest(Double a, Double b, Boolean result) { | ||
208 | var term = RealTerms.greater(RealTerms.constant(a), RealTerms.constant(b)); | ||
209 | assertThat(term.getType(), is(Boolean.class)); | ||
210 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
211 | } | ||
212 | |||
213 | @ParameterizedTest(name = "({0} >= {1}) == {2}") | ||
214 | @CsvSource(value = { | ||
215 | "1.5, -2.7, true", | ||
216 | "1.5, 1.5, true", | ||
217 | "1.5, 2.7, false", | ||
218 | "null, 1.5, null", | ||
219 | "1.5, null, null", | ||
220 | "null, null, null" | ||
221 | }, nullValues = "null") | ||
222 | void greaterEqTest(Double a, Double b, Boolean result) { | ||
223 | var term = RealTerms.greaterEq(RealTerms.constant(a), RealTerms.constant(b)); | ||
224 | assertThat(term.getType(), is(Boolean.class)); | ||
225 | assertThat(term.evaluate(Valuation.empty()), is(result)); | ||
226 | } | ||
227 | |||
228 | @ParameterizedTest(name = "{0} as real == {1}") | ||
229 | @CsvSource(value = { | ||
230 | "0, 0.0", | ||
231 | "5, 5.0", | ||
232 | "null, null" | ||
233 | }, nullValues = "null") | ||
234 | void asRealTest(Integer a, Double result) { | ||
235 | var term = RealTerms.asReal(IntTerms.constant(a)); | ||
236 | assertThat(term.getType(), is(Double.class)); | ||
237 | assertThat(term.evaluate(Valuation.empty()), closeToOrNull(result)); | ||
238 | } | ||
239 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/FiniteUpperCardinalityTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/FiniteUpperCardinalityTest.java new file mode 100644 index 00000000..8a57f029 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/FiniteUpperCardinalityTest.java | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.uppercardinality; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | |||
10 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
11 | |||
12 | class FiniteUpperCardinalityTest { | ||
13 | @Test | ||
14 | void invalidConstructorTest() { | ||
15 | assertThrows(IllegalArgumentException.class, () -> new FiniteUpperCardinality(-1)); | ||
16 | } | ||
17 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitiesTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitiesTest.java new file mode 100644 index 00000000..bdb6a833 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitiesTest.java | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.uppercardinality; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import org.junit.jupiter.params.ParameterizedTest; | ||
10 | import org.junit.jupiter.params.provider.ValueSource; | ||
11 | |||
12 | import static org.hamcrest.MatcherAssert.assertThat; | ||
13 | import static org.hamcrest.Matchers.equalTo; | ||
14 | import static org.hamcrest.Matchers.instanceOf; | ||
15 | |||
16 | class UpperCardinalitiesTest { | ||
17 | @ParameterizedTest | ||
18 | @ValueSource(ints = {0, 1, 255, 256, 1000, Integer.MAX_VALUE}) | ||
19 | void valueOfBoundedTest(int value) { | ||
20 | var upperCardinality = UpperCardinalities.atMost(value); | ||
21 | assertThat(upperCardinality, instanceOf(FiniteUpperCardinality.class)); | ||
22 | assertThat(((FiniteUpperCardinality) upperCardinality).finiteUpperBound(), equalTo(value)); | ||
23 | } | ||
24 | |||
25 | @Test | ||
26 | void valueOfUnboundedTest() { | ||
27 | var upperCardinality = UpperCardinalities.atMost(-1); | ||
28 | assertThat(upperCardinality, instanceOf(UnboundedUpperCardinality.class)); | ||
29 | } | ||
30 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitySumAggregatorStreamTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitySumAggregatorStreamTest.java new file mode 100644 index 00000000..fc8522d4 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitySumAggregatorStreamTest.java | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.uppercardinality; | ||
7 | |||
8 | import org.hamcrest.Matchers; | ||
9 | import org.junit.jupiter.params.ParameterizedTest; | ||
10 | import org.junit.jupiter.params.provider.Arguments; | ||
11 | import org.junit.jupiter.params.provider.MethodSource; | ||
12 | |||
13 | import java.util.List; | ||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import static org.hamcrest.MatcherAssert.assertThat; | ||
17 | |||
18 | class UpperCardinalitySumAggregatorStreamTest { | ||
19 | @ParameterizedTest | ||
20 | @MethodSource | ||
21 | void testStream(List<UpperCardinality> list, UpperCardinality expected) { | ||
22 | var result = UpperCardinalitySumAggregator.INSTANCE.aggregateStream(list.stream()); | ||
23 | assertThat(result, Matchers.is(expected)); | ||
24 | } | ||
25 | |||
26 | static Stream<Arguments> testStream() { | ||
27 | return Stream.of( | ||
28 | Arguments.of(List.of(), UpperCardinalities.ZERO), | ||
29 | Arguments.of(List.of(UpperCardinality.of(3)), UpperCardinality.of(3)), | ||
30 | Arguments.of( | ||
31 | List.of( | ||
32 | UpperCardinality.of(2), | ||
33 | UpperCardinality.of(3) | ||
34 | ), | ||
35 | UpperCardinality.of(5) | ||
36 | ), | ||
37 | Arguments.of(List.of(UpperCardinalities.UNBOUNDED), UpperCardinalities.UNBOUNDED), | ||
38 | Arguments.of( | ||
39 | List.of( | ||
40 | UpperCardinalities.UNBOUNDED, | ||
41 | UpperCardinalities.UNBOUNDED | ||
42 | ), | ||
43 | UpperCardinalities.UNBOUNDED | ||
44 | ), | ||
45 | Arguments.of( | ||
46 | List.of( | ||
47 | UpperCardinalities.UNBOUNDED, | ||
48 | UpperCardinality.of(3) | ||
49 | ), | ||
50 | UpperCardinalities.UNBOUNDED | ||
51 | ) | ||
52 | ); | ||
53 | } | ||
54 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitySumAggregatorTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitySumAggregatorTest.java new file mode 100644 index 00000000..e252b097 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalitySumAggregatorTest.java | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.uppercardinality; | ||
7 | |||
8 | import org.hamcrest.MatcherAssert; | ||
9 | import org.hamcrest.Matchers; | ||
10 | import org.junit.jupiter.api.BeforeEach; | ||
11 | import org.junit.jupiter.api.Test; | ||
12 | import tools.refinery.logic.term.StatefulAggregate; | ||
13 | |||
14 | import static org.hamcrest.MatcherAssert.assertThat; | ||
15 | |||
16 | class UpperCardinalitySumAggregatorTest { | ||
17 | private StatefulAggregate<UpperCardinality, UpperCardinality> accumulator; | ||
18 | |||
19 | @BeforeEach | ||
20 | void beforeEach() { | ||
21 | accumulator = UpperCardinalitySumAggregator.INSTANCE.createEmptyAggregate(); | ||
22 | } | ||
23 | |||
24 | @Test | ||
25 | void emptyAggregationTest() { | ||
26 | MatcherAssert.assertThat(accumulator.getResult(), Matchers.is(UpperCardinality.of(0))); | ||
27 | } | ||
28 | |||
29 | @Test | ||
30 | void singleBoundedTest() { | ||
31 | accumulator.add(UpperCardinality.of(3)); | ||
32 | MatcherAssert.assertThat(accumulator.getResult(), Matchers.is(UpperCardinality.of(3))); | ||
33 | } | ||
34 | |||
35 | @Test | ||
36 | void multipleBoundedTest() { | ||
37 | accumulator.add(UpperCardinality.of(2)); | ||
38 | accumulator.add(UpperCardinality.of(3)); | ||
39 | MatcherAssert.assertThat(accumulator.getResult(), Matchers.is(UpperCardinality.of(5))); | ||
40 | } | ||
41 | |||
42 | @Test | ||
43 | void singleUnboundedTest() { | ||
44 | accumulator.add(UpperCardinalities.UNBOUNDED); | ||
45 | assertThat(accumulator.getResult(), Matchers.is(UpperCardinalities.UNBOUNDED)); | ||
46 | } | ||
47 | |||
48 | @Test | ||
49 | void multipleUnboundedTest() { | ||
50 | accumulator.add(UpperCardinalities.UNBOUNDED); | ||
51 | accumulator.add(UpperCardinalities.UNBOUNDED); | ||
52 | assertThat(accumulator.getResult(), Matchers.is(UpperCardinalities.UNBOUNDED)); | ||
53 | } | ||
54 | |||
55 | @Test | ||
56 | void removeBoundedTest() { | ||
57 | accumulator.add(UpperCardinality.of(2)); | ||
58 | accumulator.add(UpperCardinality.of(3)); | ||
59 | accumulator.remove(UpperCardinality.of(2)); | ||
60 | MatcherAssert.assertThat(accumulator.getResult(), Matchers.is(UpperCardinality.of(3))); | ||
61 | } | ||
62 | |||
63 | @Test | ||
64 | void removeAllUnboundedTest() { | ||
65 | accumulator.add(UpperCardinalities.UNBOUNDED); | ||
66 | accumulator.add(UpperCardinality.of(3)); | ||
67 | accumulator.remove(UpperCardinalities.UNBOUNDED); | ||
68 | MatcherAssert.assertThat(accumulator.getResult(), Matchers.is(UpperCardinality.of(3))); | ||
69 | } | ||
70 | |||
71 | @Test | ||
72 | void removeSomeUnboundedTest() { | ||
73 | accumulator.add(UpperCardinalities.UNBOUNDED); | ||
74 | accumulator.add(UpperCardinalities.UNBOUNDED); | ||
75 | accumulator.add(UpperCardinality.of(3)); | ||
76 | accumulator.remove(UpperCardinalities.UNBOUNDED); | ||
77 | assertThat(accumulator.getResult(), Matchers.is(UpperCardinalities.UNBOUNDED)); | ||
78 | } | ||
79 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalityTermsEvaluateTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalityTermsEvaluateTest.java new file mode 100644 index 00000000..ab71b716 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalityTermsEvaluateTest.java | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.uppercardinality; | ||
7 | |||
8 | import org.hamcrest.Matchers; | ||
9 | import org.junit.jupiter.params.ParameterizedTest; | ||
10 | import org.junit.jupiter.params.provider.Arguments; | ||
11 | import org.junit.jupiter.params.provider.MethodSource; | ||
12 | import tools.refinery.logic.valuation.Valuation; | ||
13 | |||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import static org.hamcrest.MatcherAssert.assertThat; | ||
17 | import static org.hamcrest.Matchers.is; | ||
18 | |||
19 | class UpperCardinalityTermsEvaluateTest { | ||
20 | @ParameterizedTest(name = "min({0}, {1}) == {2}") | ||
21 | @MethodSource | ||
22 | void minTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
23 | var term = UpperCardinalityTerms.min(UpperCardinalityTerms.constant(a), UpperCardinalityTerms.constant(b)); | ||
24 | assertThat(term.getType(), is(UpperCardinality.class)); | ||
25 | assertThat(term.evaluate(Valuation.empty()), Matchers.is(expected)); | ||
26 | } | ||
27 | |||
28 | static Stream<Arguments> minTest() { | ||
29 | return Stream.of( | ||
30 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
31 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(0)), | ||
32 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
33 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinality.of(0)), | ||
34 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinality.of(0)), | ||
35 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
36 | Arguments.of(UpperCardinality.of(1), null, null), | ||
37 | Arguments.of(null, UpperCardinality.of(1), null), | ||
38 | Arguments.of(null, null, null) | ||
39 | ); | ||
40 | } | ||
41 | |||
42 | @ParameterizedTest(name = "max({0}, {1}) == {2}") | ||
43 | @MethodSource | ||
44 | void maxTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
45 | var term = UpperCardinalityTerms.max(UpperCardinalityTerms.constant(a), UpperCardinalityTerms.constant(b)); | ||
46 | assertThat(term.getType(), is(UpperCardinality.class)); | ||
47 | assertThat(term.evaluate(Valuation.empty()), Matchers.is(expected)); | ||
48 | } | ||
49 | |||
50 | static Stream<Arguments> maxTest() { | ||
51 | return Stream.of( | ||
52 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
53 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(1)), | ||
54 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(1)), | ||
55 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
56 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinalities.UNBOUNDED), | ||
57 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
58 | Arguments.of(UpperCardinality.of(1), null, null), | ||
59 | Arguments.of(null, UpperCardinality.of(1), null), | ||
60 | Arguments.of(null, null, null) | ||
61 | ); | ||
62 | } | ||
63 | |||
64 | @ParameterizedTest(name = "{0} + {1} == {2}") | ||
65 | @MethodSource | ||
66 | void addTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
67 | var term = UpperCardinalityTerms.add(UpperCardinalityTerms.constant(a), UpperCardinalityTerms.constant(b)); | ||
68 | assertThat(term.getType(), is(UpperCardinality.class)); | ||
69 | assertThat(term.evaluate(Valuation.empty()), Matchers.is(expected)); | ||
70 | } | ||
71 | |||
72 | static Stream<Arguments> addTest() { | ||
73 | return Stream.of( | ||
74 | Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(5)), | ||
75 | Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
76 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), | ||
77 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
78 | Arguments.of(UpperCardinality.of(1), null, null), | ||
79 | Arguments.of(null, UpperCardinality.of(1), null), | ||
80 | Arguments.of(null, null, null) | ||
81 | ); | ||
82 | } | ||
83 | |||
84 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
85 | @MethodSource | ||
86 | void mulTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
87 | var term = UpperCardinalityTerms.mul(UpperCardinalityTerms.constant(a), UpperCardinalityTerms.constant(b)); | ||
88 | assertThat(term.getType(), is(UpperCardinality.class)); | ||
89 | assertThat(term.evaluate(Valuation.empty()), Matchers.is(expected)); | ||
90 | } | ||
91 | |||
92 | static Stream<Arguments> mulTest() { | ||
93 | return Stream.of( | ||
94 | Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(6)), | ||
95 | Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
96 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), | ||
97 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
98 | Arguments.of(UpperCardinality.of(1), null, null), | ||
99 | Arguments.of(null, UpperCardinality.of(1), null), | ||
100 | Arguments.of(null, null, null) | ||
101 | ); | ||
102 | } | ||
103 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalityTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalityTest.java new file mode 100644 index 00000000..70cb6695 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/term/uppercardinality/UpperCardinalityTest.java | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.term.uppercardinality; | ||
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 | |||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import static org.hamcrest.MatcherAssert.assertThat; | ||
15 | import static org.hamcrest.Matchers.equalTo; | ||
16 | |||
17 | class UpperCardinalityTest { | ||
18 | @ParameterizedTest(name = "min({0}, {1}) == {2}") | ||
19 | @MethodSource | ||
20 | void minTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
21 | assertThat(a.min(b), equalTo(expected)); | ||
22 | } | ||
23 | |||
24 | static Stream<Arguments> minTest() { | ||
25 | return Stream.of( | ||
26 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
27 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(0)), | ||
28 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
29 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinality.of(0)), | ||
30 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinality.of(0)), | ||
31 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
32 | ); | ||
33 | } | ||
34 | |||
35 | @ParameterizedTest(name = "max({0}, {1}) == {2}") | ||
36 | @MethodSource | ||
37 | void maxTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
38 | assertThat(a.max(b), equalTo(expected)); | ||
39 | } | ||
40 | |||
41 | static Stream<Arguments> maxTest() { | ||
42 | return Stream.of( | ||
43 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), UpperCardinality.of(0)), | ||
44 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), UpperCardinality.of(1)), | ||
45 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), UpperCardinality.of(1)), | ||
46 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
47 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), UpperCardinalities.UNBOUNDED), | ||
48 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
49 | ); | ||
50 | } | ||
51 | |||
52 | @ParameterizedTest(name = "{0} + {1} == {2}") | ||
53 | @MethodSource | ||
54 | void addTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
55 | assertThat(a.add(b), equalTo(expected)); | ||
56 | } | ||
57 | |||
58 | static Stream<Arguments> addTest() { | ||
59 | return Stream.of( | ||
60 | Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(5)), | ||
61 | Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
62 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), | ||
63 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
64 | ); | ||
65 | } | ||
66 | |||
67 | @ParameterizedTest(name = "{0} * {1} == {2}") | ||
68 | @MethodSource | ||
69 | void multiplyTest(UpperCardinality a, UpperCardinality b, UpperCardinality expected) { | ||
70 | assertThat(a.multiply(b), equalTo(expected)); | ||
71 | } | ||
72 | |||
73 | static Stream<Arguments> multiplyTest() { | ||
74 | return Stream.of( | ||
75 | Arguments.of(UpperCardinality.of(2), UpperCardinality.of(3), UpperCardinality.of(6)), | ||
76 | Arguments.of(UpperCardinality.of(2), UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED), | ||
77 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(2), UpperCardinalities.UNBOUNDED), | ||
78 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED) | ||
79 | ); | ||
80 | } | ||
81 | |||
82 | @ParameterizedTest(name = "{0}.compareTo({1}) == {2}") | ||
83 | @MethodSource | ||
84 | void compareToTest(UpperCardinality a, UpperCardinality b, int expected) { | ||
85 | assertThat(a.compareTo(b), equalTo(expected)); | ||
86 | } | ||
87 | |||
88 | static Stream<Arguments> compareToTest() { | ||
89 | return Stream.of( | ||
90 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(0), 0), | ||
91 | Arguments.of(UpperCardinality.of(0), UpperCardinality.of(1), -1), | ||
92 | Arguments.of(UpperCardinality.of(1), UpperCardinality.of(0), 1), | ||
93 | Arguments.of(UpperCardinality.of(0), UpperCardinalities.UNBOUNDED, -1), | ||
94 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinality.of(0), 1), | ||
95 | Arguments.of(UpperCardinalities.UNBOUNDED, UpperCardinalities.UNBOUNDED, 0) | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | @ParameterizedTest(name = "{0}.compareToInt({1}) == {2}") | ||
100 | @MethodSource | ||
101 | void compareToIntTest(UpperCardinality a, int b, int expected) { | ||
102 | assertThat(a.compareToInt(b), equalTo(expected)); | ||
103 | } | ||
104 | |||
105 | static Stream<Arguments> compareToIntTest() { | ||
106 | return Stream.of( | ||
107 | Arguments.of(UpperCardinality.of(3), -1, 1), | ||
108 | Arguments.of(UpperCardinality.of(3), 2, 1), | ||
109 | Arguments.of(UpperCardinality.of(3), 3, 0), | ||
110 | Arguments.of(UpperCardinality.of(3), 4, -1), | ||
111 | Arguments.of(UpperCardinalities.UNBOUNDED, -1, 1), | ||
112 | Arguments.of(UpperCardinalities.UNBOUNDED, 3, 1) | ||
113 | ); | ||
114 | } | ||
115 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/tests/FakeFunctionView.java b/subprojects/logic/src/test/java/tools/refinery/logic/tests/FakeFunctionView.java new file mode 100644 index 00000000..4a55f561 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/tests/FakeFunctionView.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.tests; | ||
7 | |||
8 | import tools.refinery.logic.Constraint; | ||
9 | import tools.refinery.logic.term.*; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Arrays; | ||
13 | import java.util.List; | ||
14 | |||
15 | public record FakeFunctionView<T>(String name, int keyArity, Class<T> valueType) implements Constraint { | ||
16 | @Override | ||
17 | public int arity() { | ||
18 | return keyArity + 1; | ||
19 | } | ||
20 | |||
21 | @Override | ||
22 | public List<Parameter> getParameters() { | ||
23 | var parameters = new Parameter[keyArity + 1]; | ||
24 | Arrays.fill(parameters, Parameter.NODE_OUT); | ||
25 | parameters[keyArity] = new Parameter(valueType, ParameterDirection.OUT); | ||
26 | return List.of(parameters); | ||
27 | } | ||
28 | |||
29 | public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, List<NodeVariable> arguments) { | ||
30 | return targetVariable -> { | ||
31 | var placeholderVariable = Variable.of(valueType); | ||
32 | var argumentsWithPlaceholder = new ArrayList<Variable>(arguments.size() + 1); | ||
33 | argumentsWithPlaceholder.addAll(arguments); | ||
34 | argumentsWithPlaceholder.add(placeholderVariable); | ||
35 | return aggregateBy(placeholderVariable, aggregator, argumentsWithPlaceholder).toLiteral(targetVariable); | ||
36 | }; | ||
37 | } | ||
38 | |||
39 | public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, NodeVariable... arguments) { | ||
40 | return aggregate(aggregator, List.of(arguments)); | ||
41 | } | ||
42 | |||
43 | public AssignedValue<T> leftJoin(T defaultValue, List<NodeVariable> arguments) { | ||
44 | return targetVariable -> { | ||
45 | var placeholderVariable = Variable.of(valueType); | ||
46 | var argumentsWithPlaceholder = new ArrayList<Variable>(arguments.size() + 1); | ||
47 | argumentsWithPlaceholder.addAll(arguments); | ||
48 | argumentsWithPlaceholder.add(placeholderVariable); | ||
49 | return leftJoinBy(placeholderVariable, defaultValue, argumentsWithPlaceholder).toLiteral(targetVariable); | ||
50 | }; | ||
51 | } | ||
52 | |||
53 | public AssignedValue<T> leftJoin(T defaultValue, NodeVariable... arguments) { | ||
54 | return leftJoin(defaultValue, List.of(arguments)); | ||
55 | |||
56 | } | ||
57 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/tests/FakeKeyOnlyView.java b/subprojects/logic/src/test/java/tools/refinery/logic/tests/FakeKeyOnlyView.java new file mode 100644 index 00000000..7e09ddab --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/tests/FakeKeyOnlyView.java | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.tests; | ||
7 | |||
8 | import tools.refinery.logic.Constraint; | ||
9 | import tools.refinery.logic.term.Parameter; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.List; | ||
13 | |||
14 | public record FakeKeyOnlyView(String name, int arity) implements Constraint { | ||
15 | @Override | ||
16 | public List<Parameter> getParameters() { | ||
17 | var parameters = new Parameter[arity]; | ||
18 | Arrays.fill(parameters, Parameter.NODE_OUT); | ||
19 | return List.of(parameters); | ||
20 | } | ||
21 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/tests/StructurallyEqualToRawTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/tests/StructurallyEqualToRawTest.java new file mode 100644 index 00000000..52a22ce1 --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/tests/StructurallyEqualToRawTest.java | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.tests; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.dnf.Dnf; | ||
11 | import tools.refinery.logic.dnf.SymbolicParameter; | ||
12 | import tools.refinery.logic.term.NodeVariable; | ||
13 | import tools.refinery.logic.term.ParameterDirection; | ||
14 | import tools.refinery.logic.term.Variable; | ||
15 | |||
16 | import java.util.List; | ||
17 | |||
18 | import static org.hamcrest.CoreMatchers.containsString; | ||
19 | import static org.hamcrest.MatcherAssert.assertThat; | ||
20 | import static org.hamcrest.Matchers.allOf; | ||
21 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
22 | import static tools.refinery.logic.tests.QueryMatchers.structurallyEqualTo; | ||
23 | |||
24 | class StructurallyEqualToRawTest { | ||
25 | private static final Constraint personView = new FakeKeyOnlyView("Person", 1); | ||
26 | private static final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
27 | private static final NodeVariable p = Variable.of("p"); | ||
28 | private static final NodeVariable q = Variable.of("q"); | ||
29 | |||
30 | @Test | ||
31 | void flatEqualsTest() { | ||
32 | var actual = Dnf.builder("Actual").parameters(p).clause(personView.call(p)).build(); | ||
33 | |||
34 | assertThat(actual, structurallyEqualTo( | ||
35 | List.of(new SymbolicParameter(q, ParameterDirection.OUT)), | ||
36 | List.of(List.of(personView.call(q))) | ||
37 | )); | ||
38 | } | ||
39 | |||
40 | @Test | ||
41 | void flatNotEqualsTest() { | ||
42 | var actual = Dnf.builder("Actual").parameters(p).clause(friendView.call(p, q)).build(); | ||
43 | |||
44 | var assertion = structurallyEqualTo( | ||
45 | List.of(new SymbolicParameter(q, ParameterDirection.OUT)), | ||
46 | List.of(List.of(friendView.call(q, q))) | ||
47 | ); | ||
48 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
49 | } | ||
50 | |||
51 | @Test | ||
52 | void deepEqualsTest() { | ||
53 | var actual = Dnf.builder("Actual").parameters(q).clause( | ||
54 | Dnf.builder("Actual2").parameters(p).clause(personView.call(p)).build().call(q) | ||
55 | ).build(); | ||
56 | |||
57 | assertThat(actual, structurallyEqualTo( | ||
58 | List.of(new SymbolicParameter(q, ParameterDirection.OUT)), | ||
59 | List.of( | ||
60 | List.of( | ||
61 | Dnf.builder("Expected2").parameters(p).clause(personView.call(p)).build().call(q) | ||
62 | ) | ||
63 | ) | ||
64 | )); | ||
65 | } | ||
66 | |||
67 | @Test | ||
68 | void deepNotEqualsTest() { | ||
69 | var actual = Dnf.builder("Actual").parameter(q).clause( | ||
70 | Dnf.builder("Actual2").parameters(p).clause(friendView.call(p, q)).build().call(q) | ||
71 | ).build(); | ||
72 | |||
73 | var assertion = structurallyEqualTo( | ||
74 | List.of(new SymbolicParameter(q, ParameterDirection.OUT)), | ||
75 | List.of( | ||
76 | List.of( | ||
77 | Dnf.builder("Expected2") | ||
78 | .parameters(p) | ||
79 | .clause(friendView.call(p, p)) | ||
80 | .build() | ||
81 | .call(q) | ||
82 | ) | ||
83 | ) | ||
84 | ); | ||
85 | var error = assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
86 | assertThat(error.getMessage(), allOf(containsString("Expected2"), containsString("Actual2"))); | ||
87 | } | ||
88 | |||
89 | @Test | ||
90 | void parameterListLengthMismatchTest() { | ||
91 | var actual = Dnf.builder("Actual").parameters(p, q).clause( | ||
92 | friendView.call(p, q) | ||
93 | ).build(); | ||
94 | |||
95 | var assertion = structurallyEqualTo( | ||
96 | List.of(new SymbolicParameter(p, ParameterDirection.OUT)), | ||
97 | List.of(List.of(friendView.call(p, p))) | ||
98 | ); | ||
99 | |||
100 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
101 | } | ||
102 | |||
103 | @Test | ||
104 | void parameterDirectionMismatchTest() { | ||
105 | var actual = Dnf.builder("Actual").parameter(p, ParameterDirection.IN).clause( | ||
106 | personView.call(p) | ||
107 | ).build(); | ||
108 | |||
109 | var assertion = structurallyEqualTo( | ||
110 | List.of(new SymbolicParameter(p, ParameterDirection.OUT)), | ||
111 | List.of(List.of(personView.call(p))) | ||
112 | ); | ||
113 | |||
114 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
115 | } | ||
116 | |||
117 | @Test | ||
118 | void clauseCountMismatchTest() { | ||
119 | var actual = Dnf.builder("Actual").parameters(p, q).clause( | ||
120 | friendView.call(p, q) | ||
121 | ).build(); | ||
122 | |||
123 | var assertion = structurallyEqualTo( | ||
124 | List.of( | ||
125 | new SymbolicParameter(p, ParameterDirection.OUT), | ||
126 | new SymbolicParameter(q, ParameterDirection.OUT) | ||
127 | ), | ||
128 | List.of( | ||
129 | List.of(friendView.call(p, q)), | ||
130 | List.of(friendView.call(q, p)) | ||
131 | ) | ||
132 | ); | ||
133 | |||
134 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
135 | } | ||
136 | |||
137 | @Test | ||
138 | void literalCountMismatchTest() { | ||
139 | var actual = Dnf.builder("Actual").parameters(p, q).clause( | ||
140 | friendView.call(p, q) | ||
141 | ).build(); | ||
142 | |||
143 | var assertion = structurallyEqualTo( | ||
144 | List.of( | ||
145 | new SymbolicParameter(p, ParameterDirection.OUT), | ||
146 | new SymbolicParameter(q, ParameterDirection.OUT) | ||
147 | ), | ||
148 | List.of( | ||
149 | List.of(friendView.call(p, q), friendView.call(q, p)) | ||
150 | ) | ||
151 | ); | ||
152 | |||
153 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
154 | } | ||
155 | } | ||
diff --git a/subprojects/logic/src/test/java/tools/refinery/logic/tests/StructurallyEqualToTest.java b/subprojects/logic/src/test/java/tools/refinery/logic/tests/StructurallyEqualToTest.java new file mode 100644 index 00000000..663b115a --- /dev/null +++ b/subprojects/logic/src/test/java/tools/refinery/logic/tests/StructurallyEqualToTest.java | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.logic.tests; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.logic.Constraint; | ||
10 | import tools.refinery.logic.dnf.Dnf; | ||
11 | import tools.refinery.logic.term.NodeVariable; | ||
12 | import tools.refinery.logic.term.ParameterDirection; | ||
13 | import tools.refinery.logic.term.Variable; | ||
14 | |||
15 | import static org.hamcrest.CoreMatchers.containsString; | ||
16 | import static org.hamcrest.MatcherAssert.assertThat; | ||
17 | import static org.junit.jupiter.api.Assertions.assertThrows; | ||
18 | import static tools.refinery.logic.tests.QueryMatchers.structurallyEqualTo; | ||
19 | |||
20 | class StructurallyEqualToTest { | ||
21 | private static final Constraint personView = new FakeKeyOnlyView("Person", 1); | ||
22 | private static final Constraint friendView = new FakeKeyOnlyView("friend", 2); | ||
23 | private static final NodeVariable p = Variable.of("p"); | ||
24 | private static final NodeVariable q = Variable.of("q"); | ||
25 | |||
26 | @Test | ||
27 | void flatEqualsTest() { | ||
28 | var expected = Dnf.builder("Expected").parameters(q).clause(personView.call(q)).build(); | ||
29 | var actual = Dnf.builder("Actual").parameters(p).clause(personView.call(p)).build(); | ||
30 | |||
31 | assertThat(actual, structurallyEqualTo(expected)); | ||
32 | } | ||
33 | |||
34 | @Test | ||
35 | void flatNotEqualsTest() { | ||
36 | var expected = Dnf.builder("Expected").parameters(q).clause(friendView.call(q, q)).build(); | ||
37 | var actual = Dnf.builder("Actual").parameters(p).clause(friendView.call(p, q)).build(); | ||
38 | |||
39 | var assertion = structurallyEqualTo(expected); | ||
40 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
41 | } | ||
42 | |||
43 | @Test | ||
44 | void deepEqualsTest() { | ||
45 | var expected = Dnf.builder("Expected").parameters(q).clause( | ||
46 | Dnf.builder("Expected2").parameters(p).clause(personView.call(p)).build().call(q) | ||
47 | ).build(); | ||
48 | var actual = Dnf.builder("Actual").parameters(q).clause( | ||
49 | Dnf.builder("Actual2").parameters(p).clause(personView.call(p)).build().call(q) | ||
50 | ).build(); | ||
51 | |||
52 | assertThat(actual, structurallyEqualTo(expected)); | ||
53 | } | ||
54 | |||
55 | @Test | ||
56 | void deepNotEqualsTest() { | ||
57 | var expected = Dnf.builder("Expected").parameters(q).clause( | ||
58 | Dnf.builder("Expected2").parameters(p).clause(friendView.call(p, p)).build().call(q) | ||
59 | ).build(); | ||
60 | var actual = Dnf.builder("Actual").parameter(q).clause( | ||
61 | Dnf.builder("Actual2").parameters(p).clause(friendView.call(p, q)).build().call(q) | ||
62 | ).build(); | ||
63 | |||
64 | var assertion = structurallyEqualTo(expected); | ||
65 | var error = assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
66 | assertThat(error.getMessage(), containsString(" called from Expected/1 ")); | ||
67 | } | ||
68 | |||
69 | @Test | ||
70 | void parameterListLengthMismatchTest() { | ||
71 | var expected = Dnf.builder("Expected").parameter(p).clause( | ||
72 | friendView.call(p, p) | ||
73 | ).build(); | ||
74 | var actual = Dnf.builder("Actual").parameters(p, q).clause( | ||
75 | friendView.call(p, q) | ||
76 | ).build(); | ||
77 | |||
78 | var assertion = structurallyEqualTo(expected); | ||
79 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
80 | } | ||
81 | |||
82 | @Test | ||
83 | void parameterDirectionMismatchTest() { | ||
84 | var expected = Dnf.builder("Expected").parameter(p, ParameterDirection.OUT).clause( | ||
85 | personView.call(p) | ||
86 | ).build(); | ||
87 | var actual = Dnf.builder("Actual").parameter(p, ParameterDirection.IN).clause( | ||
88 | personView.call(p) | ||
89 | ).build(); | ||
90 | |||
91 | var assertion = structurallyEqualTo(expected); | ||
92 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
93 | } | ||
94 | |||
95 | @Test | ||
96 | void clauseCountMismatchTest() { | ||
97 | var expected = Dnf.builder("Expected") | ||
98 | .parameters(p, q) | ||
99 | .clause(friendView.call(p, q)) | ||
100 | .clause(friendView.call(q, p)) | ||
101 | .build(); | ||
102 | var actual = Dnf.builder("Actual").parameters(p, q).clause( | ||
103 | friendView.call(p, q) | ||
104 | ).build(); | ||
105 | |||
106 | var assertion = structurallyEqualTo(expected); | ||
107 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
108 | } | ||
109 | |||
110 | @Test | ||
111 | void literalCountMismatchTest() { | ||
112 | var expected = Dnf.builder("Expected").parameters(p, q).clause( | ||
113 | friendView.call(p, q), | ||
114 | friendView.call(q, p) | ||
115 | ).build(); | ||
116 | var actual = Dnf.builder("Actual").parameters(p, q).clause( | ||
117 | friendView.call(p, q) | ||
118 | ).build(); | ||
119 | |||
120 | var assertion = structurallyEqualTo(expected); | ||
121 | assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); | ||
122 | } | ||
123 | } | ||