diff options
author | 2023-11-18 19:43:09 +0100 | |
---|---|---|
committer | 2023-11-19 14:41:17 +0100 | |
commit | 392242099439fd3f21abb87d55ce94050e71ccb5 (patch) | |
tree | 2be45d93ae8ebbd3dd0cc051c3db26f0318cb7d2 /subprojects/language/src/test | |
parent | fix: upper and lower scopes (diff) | |
download | refinery-392242099439fd3f21abb87d55ce94050e71ccb5.tar.gz refinery-392242099439fd3f21abb87d55ce94050e71ccb5.tar.zst refinery-392242099439fd3f21abb87d55ce94050e71ccb5.zip |
feat(language): arity validation
Diffstat (limited to 'subprojects/language/src/test')
-rw-r--r-- | subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java new file mode 100644 index 00000000..68e9fa8d --- /dev/null +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ArityValidationTest.java | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.tests.validation; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import org.eclipse.emf.common.util.Diagnostic; | ||
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.Test; | ||
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import org.junit.jupiter.params.ParameterizedTest; | ||
15 | import org.junit.jupiter.params.provider.Arguments; | ||
16 | import org.junit.jupiter.params.provider.MethodSource; | ||
17 | import org.junit.jupiter.params.provider.ValueSource; | ||
18 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | ||
19 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
20 | import tools.refinery.language.validation.ProblemValidator; | ||
21 | |||
22 | import java.util.stream.Stream; | ||
23 | |||
24 | import static org.hamcrest.MatcherAssert.assertThat; | ||
25 | import static org.hamcrest.Matchers.*; | ||
26 | |||
27 | @ExtendWith(InjectionExtension.class) | ||
28 | @InjectWith(ProblemInjectorProvider.class) | ||
29 | class ArityValidationTest { | ||
30 | @Inject | ||
31 | private ProblemParseHelper parseHelper; | ||
32 | |||
33 | @ParameterizedTest | ||
34 | @ValueSource(strings = {""" | ||
35 | pred Foo(node n) <-> false. | ||
36 | """, """ | ||
37 | pred Foo(node n, node m) <-> false. | ||
38 | """, """ | ||
39 | enum Foo { FOO_A, FOO_B } | ||
40 | """}) | ||
41 | void invalidSupertypeTest(String supertypeDefinition) { | ||
42 | var problem = parseHelper.parse(""" | ||
43 | %s | ||
44 | |||
45 | class Bar extends Foo. | ||
46 | """.formatted(supertypeDefinition)); | ||
47 | var issues = problem.validate(); | ||
48 | assertThat(issues, hasItem(allOf( | ||
49 | hasProperty("severity", is(Diagnostic.ERROR)), | ||
50 | hasProperty("issueCode", is(ProblemValidator.INVALID_SUPERTYPE_ISSUE)), | ||
51 | hasProperty("message", stringContainsInOrder("Foo", "Bar")) | ||
52 | ))); | ||
53 | } | ||
54 | |||
55 | @ParameterizedTest | ||
56 | @ValueSource(strings = {""" | ||
57 | class Foo. | ||
58 | """, """ | ||
59 | abstract class Foo. | ||
60 | """}) | ||
61 | void validSupertypeTest(String supertypeDefinition) { | ||
62 | var problem = parseHelper.parse(""" | ||
63 | %s | ||
64 | |||
65 | class Bar extends Foo. | ||
66 | """.formatted(supertypeDefinition)); | ||
67 | var issues = problem.validate(); | ||
68 | assertThat(issues, empty()); | ||
69 | } | ||
70 | |||
71 | @ParameterizedTest | ||
72 | @ValueSource(strings = {""" | ||
73 | foo(). | ||
74 | """, """ | ||
75 | foo(a1, a2, a3). | ||
76 | """, """ | ||
77 | pred bar() <-> foo(). | ||
78 | """, """ | ||
79 | pred bar(node n) <-> foo(n, n, n). | ||
80 | """, """ | ||
81 | pred bar(foo n) <-> false. | ||
82 | """, """ | ||
83 | scope foo = 1..10. | ||
84 | """, """ | ||
85 | class Bar { | ||
86 | foo[] f | ||
87 | } | ||
88 | """, """ | ||
89 | class Bar { | ||
90 | refers foo[] f | ||
91 | } | ||
92 | """}) | ||
93 | void invalidArityTest(String usage) { | ||
94 | var problem = parseHelper.parse(""" | ||
95 | pred foo(node a, node b) <-> a != b. | ||
96 | |||
97 | %s | ||
98 | """.formatted(usage)); | ||
99 | var issues = problem.validate(); | ||
100 | assertThat(issues, hasItem(allOf( | ||
101 | hasProperty("severity", is(Diagnostic.ERROR)), | ||
102 | hasProperty("issueCode", is(ProblemValidator.INVALID_ARITY_ISSUE)), | ||
103 | hasProperty("message", containsString("foo")) | ||
104 | ))); | ||
105 | } | ||
106 | |||
107 | @ParameterizedTest | ||
108 | @ValueSource(strings = {""" | ||
109 | foo(a). | ||
110 | """, """ | ||
111 | pred bar(node m) <-> !foo(m). | ||
112 | """, """ | ||
113 | pred bar(foo f) <-> true. | ||
114 | """, """ | ||
115 | scope foo = 1..10. | ||
116 | """, """ | ||
117 | class Bar { | ||
118 | foo[] quux | ||
119 | } | ||
120 | """, """ | ||
121 | class Bar { | ||
122 | refers foo[] quux | ||
123 | } | ||
124 | """}) | ||
125 | void validUnaryArityTest(String supertypeDefinition) { | ||
126 | var problem = parseHelper.parse(""" | ||
127 | pred foo(node n) <-> false. | ||
128 | |||
129 | %s | ||
130 | """.formatted(supertypeDefinition)); | ||
131 | var issues = problem.validate(); | ||
132 | assertThat(issues, empty()); | ||
133 | } | ||
134 | |||
135 | @ParameterizedTest | ||
136 | @ValueSource(strings = {""" | ||
137 | foo(a, b). | ||
138 | """, """ | ||
139 | pred bar(node m) <-> !foo(m, m). | ||
140 | """, /* Also test for parameters without any type annotation. */ """ | ||
141 | pred bar(m) <-> foo(m, m). | ||
142 | """}) | ||
143 | void validBinaryArityTest(String supertypeDefinition) { | ||
144 | var problem = parseHelper.parse(""" | ||
145 | pred foo(node n, node m) <-> false. | ||
146 | |||
147 | %s | ||
148 | """.formatted(supertypeDefinition)); | ||
149 | var issues = problem.validate(); | ||
150 | assertThat(issues, empty()); | ||
151 | } | ||
152 | |||
153 | @Test | ||
154 | void notResolvedArityTest() { | ||
155 | var problem = parseHelper.parse(""" | ||
156 | notResolved(a, b). | ||
157 | """); | ||
158 | var issues = problem.validate(); | ||
159 | assertThat(issues, not(contains(hasProperty("issueCode", is(ProblemValidator.INVALID_ARITY_ISSUE))))); | ||
160 | } | ||
161 | |||
162 | @Test | ||
163 | void validTransitiveClosure() { | ||
164 | var problem = parseHelper.parse(""" | ||
165 | pred foo(node a, node b) <-> false. | ||
166 | |||
167 | pred bar(a, b) <-> foo+(a, b). | ||
168 | """); | ||
169 | var issues = problem.validate(); | ||
170 | assertThat(issues, not(contains(hasProperty("issueCode", | ||
171 | is(ProblemValidator.INVALID_TRANSITIVE_CLOSURE_ISSUE))))); | ||
172 | } | ||
173 | |||
174 | @Test | ||
175 | void invalidTransitiveClosure() { | ||
176 | // 0 and 1 argument transitive closures do not get parsed as transitive closure | ||
177 | // due to the ambiguity with the addition operator {@code a + (b)}. | ||
178 | var problem = parseHelper.parse(""" | ||
179 | pred foo(node a, node b) <-> false. | ||
180 | |||
181 | pred bar(node a, node b) <-> foo+(a, b, a). | ||
182 | """); | ||
183 | var issues = problem.validate(); | ||
184 | assertThat(issues, hasItem(allOf( | ||
185 | hasProperty("severity", is(Diagnostic.ERROR)), | ||
186 | hasProperty("issueCode", is(ProblemValidator.INVALID_TRANSITIVE_CLOSURE_ISSUE)) | ||
187 | ))); | ||
188 | } | ||
189 | |||
190 | @ParameterizedTest | ||
191 | @MethodSource | ||
192 | void invalidReferenceTypeTest(String definition, String referenceKind) { | ||
193 | var problem = parseHelper.parse(""" | ||
194 | %s | ||
195 | |||
196 | class Bar { | ||
197 | %s Foo foo | ||
198 | } | ||
199 | """.formatted(definition, referenceKind)); | ||
200 | var issues = problem.validate(); | ||
201 | assertThat(issues, allOf( | ||
202 | hasItem(allOf( | ||
203 | hasProperty("severity", is(Diagnostic.ERROR)), | ||
204 | hasProperty("issueCode", is(ProblemValidator.INVALID_REFERENCE_TYPE_ISSUE)), | ||
205 | hasProperty("message", stringContainsInOrder("Foo", "foo")) | ||
206 | )), | ||
207 | not(hasItem(hasProperty("issueCode", is(ProblemValidator.INVALID_ARITY_ISSUE)))) | ||
208 | )); | ||
209 | } | ||
210 | |||
211 | static Stream<Arguments> invalidReferenceTypeTest() { | ||
212 | return Stream.of( | ||
213 | "pred Foo(node n) <-> true.", | ||
214 | "pred Foo(node n, node m) <-> true.", | ||
215 | "enum Foo { FOO_A, FOO_B }" | ||
216 | ).flatMap(definition -> Stream.of( | ||
217 | Arguments.of(definition, "contains"), | ||
218 | Arguments.of(definition, "container") | ||
219 | )); | ||
220 | } | ||
221 | |||
222 | |||
223 | @ParameterizedTest | ||
224 | @MethodSource | ||
225 | void validReferenceTypeTest(String definition, String referenceKind) { | ||
226 | var problem = parseHelper.parse(""" | ||
227 | %s | ||
228 | |||
229 | class Bar { | ||
230 | %s Foo foo | ||
231 | } | ||
232 | """.formatted(definition, referenceKind)); | ||
233 | var issues = problem.validate(); | ||
234 | assertThat(issues, not(hasItem(hasProperty("issueCode", anyOf( | ||
235 | is(ProblemValidator.INVALID_REFERENCE_TYPE_ISSUE), | ||
236 | is(ProblemValidator.INVALID_ARITY_ISSUE) | ||
237 | ))))); | ||
238 | } | ||
239 | |||
240 | static Stream<Arguments> validReferenceTypeTest() { | ||
241 | return Stream.of( | ||
242 | "class Foo.", | ||
243 | "abstract class Foo." | ||
244 | ).flatMap(definition -> Stream.of( | ||
245 | Arguments.of(definition, "contains"), | ||
246 | Arguments.of(definition, "container") | ||
247 | )); | ||
248 | } | ||
249 | } | ||