Chris@0
|
1 /* $Id$
|
Chris@0
|
2
|
Chris@0
|
3 Part of SWI-Prolog
|
Chris@0
|
4
|
Chris@0
|
5 Author: Jan Wielemaker
|
Chris@0
|
6 E-mail: wielemak@science.uva.nl
|
Chris@0
|
7 WWW: http://www.swi-prolog.org
|
Chris@0
|
8 Copyright (C): 2006, University of Amsterdam
|
Chris@0
|
9
|
Chris@0
|
10 This program is free software; you can redistribute it and/or
|
Chris@0
|
11 modify it under the terms of the GNU General Public License
|
Chris@0
|
12 as published by the Free Software Foundation; either version 2
|
Chris@0
|
13 of the License, or (at your option) any later version.
|
Chris@0
|
14
|
Chris@0
|
15 This program is distributed in the hope that it will be useful,
|
Chris@0
|
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@0
|
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@0
|
18 GNU General Public License for more details.
|
Chris@0
|
19
|
Chris@0
|
20 You should have received a copy of the GNU Lesser General Public
|
Chris@0
|
21 License along with this library; if not, write to the Free Software
|
Chris@0
|
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
Chris@0
|
23
|
Chris@0
|
24 As a special exception, if you link this library with other files,
|
Chris@0
|
25 compiled with a Free Software compiler, to produce an executable, this
|
Chris@0
|
26 library does not by itself cause the resulting executable to be covered
|
Chris@0
|
27 by the GNU General Public License. This exception does not however
|
Chris@0
|
28 invalidate any other reasons why the executable file might be covered by
|
Chris@0
|
29 the GNU General Public License.
|
Chris@0
|
30 */
|
Chris@0
|
31
|
Chris@0
|
32 :- module(test_sparql,
|
Chris@0
|
33 [ sparql_parse/3, % +Text, -Query, +Options
|
Chris@0
|
34
|
Chris@0
|
35 load_manifests/1, % Load 'arq' or 'dawg' manifests
|
Chris@0
|
36
|
Chris@0
|
37 show_test/1, % +NameOrIRI
|
Chris@0
|
38 show_test_data/1, % +NameOrIRI
|
Chris@0
|
39 edit_test_data/1, % +NameOrIRI
|
Chris@0
|
40 edit_test_result/1, % +NameOrIRI
|
Chris@0
|
41 edit_test/1, % +NameOrIRI
|
Chris@0
|
42 list_tests/1, % +Class
|
Chris@0
|
43
|
Chris@0
|
44 % SYNTAX TESTS
|
Chris@0
|
45 syntax_test/1, % +NameOrIRI
|
Chris@0
|
46 syntax_test/2, % +NameOrIRI, -Query
|
Chris@0
|
47 run_syntax_tests/0,
|
Chris@0
|
48 test_query_listing/0,
|
Chris@0
|
49 test_query_listing/1, % +NameOrIRI
|
Chris@0
|
50
|
Chris@0
|
51 % QUERY TESTS
|
Chris@0
|
52 run_query_tests/0,
|
Chris@0
|
53 query_test/1 % +NameOrIRI
|
Chris@0
|
54 ]).
|
Chris@0
|
55 :- use_module(sparql_grammar).
|
Chris@0
|
56 :- use_module(sparql).
|
Chris@0
|
57 :- use_module(library('semweb/rdf_db')).
|
Chris@0
|
58 :- use_module(library('semweb/rdfs')).
|
Chris@0
|
59 :- use_module(library('url')).
|
Chris@0
|
60 :- use_module(test_manifest).
|
Chris@0
|
61 :- use_module(sparql_xml_result).
|
Chris@0
|
62 :- use_module(rdf_entailment, []).
|
Chris@0
|
63 :- use_module(no_entailment, []).
|
Chris@0
|
64
|
Chris@0
|
65 :- dynamic
|
Chris@0
|
66 failed_result/2,
|
Chris@0
|
67 passed/1,
|
Chris@0
|
68 failed/1,
|
Chris@0
|
69 skipped/1.
|
Chris@0
|
70
|
Chris@0
|
71 %% blocked(?Name)
|
Chris@0
|
72 %
|
Chris@0
|
73 % Blocked tests
|
Chris@0
|
74
|
Chris@0
|
75 :- multifile
|
Chris@0
|
76 blocked/1.
|
Chris@0
|
77
|
Chris@0
|
78 blocked('extendedType-literal-ne').
|
Chris@0
|
79 blocked('typePromotion-decimal-decimal-pass').
|
Chris@0
|
80 blocked('extendedType-ne-fail').
|
Chris@0
|
81 % Requires named graphs
|
Chris@0
|
82 blocked('untrusted-graphs-002').
|
Chris@0
|
83 blocked('untrusted-graphs-004').
|
Chris@0
|
84 blocked('untrusted-graphs-005').
|
Chris@0
|
85 blocked('source-query-001').
|
Chris@0
|
86 blocked('source-query-002').
|
Chris@0
|
87 blocked('source-query-003').
|
Chris@0
|
88 blocked('source-query-005').
|
Chris@0
|
89 blocked('dawg-source-simple-001').
|
Chris@0
|
90 blocked('dawg-source-simple-002').
|
Chris@0
|
91 blocked('dawg-source-simple-003').
|
Chris@0
|
92 blocked('dawg-source-simple-004').
|
Chris@0
|
93 blocked('dawg-source-simple-005').
|
Chris@0
|
94 % Requires xsd:dateTime semantics
|
Chris@0
|
95 blocked('sparql-query-example-Testing-Values-RDFterm-equal-2').
|
Chris@0
|
96 blocked('sparql-query-example-Testing-Values-1').
|
Chris@0
|
97 % ARQ tests with .srj result file
|
Chris@0
|
98 blocked('strlen - 1').
|
Chris@0
|
99 blocked('strlen - 2').
|
Chris@0
|
100
|
Chris@0
|
101
|
Chris@0
|
102 /*******************************
|
Chris@0
|
103 * QUERY TESTS *
|
Chris@0
|
104 *******************************/
|
Chris@0
|
105
|
Chris@0
|
106 % TBD: As we have to have an empty database we must first collect
|
Chris@0
|
107 % all info in the Prolog database :-( Alternatively we need a way
|
Chris@0
|
108 % to set the current database (thread-local!)
|
Chris@0
|
109
|
Chris@0
|
110 % run_query_tests
|
Chris@0
|
111 %
|
Chris@0
|
112 % Load all manifests and execute the tests.
|
Chris@0
|
113
|
Chris@0
|
114 run_query_tests :-
|
Chris@0
|
115 clean_tests,
|
Chris@0
|
116 run_all_query_tests.
|
Chris@0
|
117
|
Chris@0
|
118 run_all_query_tests :-
|
Chris@0
|
119 ( current_test(_, Test),
|
Chris@0
|
120 test_name(Test, Name),
|
Chris@0
|
121 ( blocked(Name)
|
Chris@0
|
122 -> assert(skipped(Test)),
|
Chris@0
|
123 fail
|
Chris@0
|
124 ; true
|
Chris@0
|
125 ),
|
Chris@0
|
126 query_test(Test),
|
Chris@0
|
127 fail ; true
|
Chris@0
|
128 ),
|
Chris@0
|
129
|
Chris@0
|
130 findall(T, passed(T), Passed), length(Passed, NPassed),
|
Chris@0
|
131 findall(T, failed(T), Failed), length(Failed, NFailed),
|
Chris@0
|
132 findall(T, skipped(T), Skipped), length(Skipped, NSkipped),
|
Chris@0
|
133 format('Passed: ~D; failed: ~D; skipped ~D~n',
|
Chris@0
|
134 [NPassed, NFailed, NSkipped]).
|
Chris@0
|
135
|
Chris@0
|
136 query_test(Name) :-
|
Chris@0
|
137 test_name(Test, Name), !,
|
Chris@0
|
138 query_test(Test).
|
Chris@0
|
139 query_test(Test) :-
|
Chris@0
|
140 test_name(Test, Name),
|
Chris@0
|
141 format('~`=t BEGIN ~q ~`=t~72|~n', [Name]),
|
Chris@0
|
142 test_query(Test, Query),
|
Chris@0
|
143 % Compile the query
|
Chris@0
|
144 ( catch(sparql_compile(Query, Compiled,
|
Chris@0
|
145 [ type(Type),
|
Chris@0
|
146 entailment(none)
|
Chris@0
|
147 ]), E, true)
|
Chris@0
|
148 -> ( var(E)
|
Chris@0
|
149 -> true
|
Chris@0
|
150 ; print_message(error, E),
|
Chris@0
|
151 assert(failed(Test)),
|
Chris@0
|
152 fail
|
Chris@0
|
153 )
|
Chris@0
|
154 ; format('FAILED to compile ~q~n', [Name]),
|
Chris@0
|
155 assert(failed(Test)),
|
Chris@0
|
156 fail
|
Chris@0
|
157 ),
|
Chris@0
|
158 % get the correct result
|
Chris@0
|
159 result_to_prolog(Type, Test, PrologResult),
|
Chris@0
|
160 % load the data
|
Chris@0
|
161 rdf_reset_db,
|
Chris@0
|
162 ( test_data_file(Test, DataFile)
|
Chris@0
|
163 -> load_triples_to_db(DataFile)
|
Chris@0
|
164 ; true
|
Chris@0
|
165 ),
|
Chris@0
|
166
|
Chris@0
|
167 flag(c, C, C+1),
|
Chris@0
|
168 % run the query
|
Chris@0
|
169 catch(findall(Result, sparql_run(Compiled, Result), Results),
|
Chris@0
|
170 E, true),
|
Chris@0
|
171 ( var(E)
|
Chris@0
|
172 -> % compare the result
|
Chris@0
|
173 compare_results(Test, Type, PrologResult, Results)
|
Chris@0
|
174 ; print_message(error, E),
|
Chris@0
|
175 assert(failed(Test)),
|
Chris@0
|
176 fail
|
Chris@0
|
177 ).
|
Chris@0
|
178
|
Chris@0
|
179
|
Chris@0
|
180 %% compare_results(+Test, +Type, +Correct, +Results)
|
Chris@0
|
181 %
|
Chris@0
|
182 % NOTE: Some tests (notably syntax tests) do not have results. In
|
Chris@0
|
183 % that case Results is bound to no_result and we do not compare.
|
Chris@0
|
184
|
Chris@0
|
185 compare_results(_, _, no_result, _) :- !.
|
Chris@0
|
186 compare_results(Test, ask, ask(Bool), [Bool]) :- !,
|
Chris@0
|
187 assert(passed(Test)).
|
Chris@0
|
188 compare_results(Test, Type, select(ColNames, Rows), Result) :-
|
Chris@0
|
189 Type = select(MyColNames),
|
Chris@0
|
190 same_colnames(ColNames, MyColNames, Map),
|
Chris@0
|
191 ( Map == nomap
|
Chris@0
|
192 -> RowsMyOrder = Rows
|
Chris@0
|
193 ; map_rows(Rows, Map, RowsMyOrder)
|
Chris@0
|
194 ),
|
Chris@0
|
195 var_blank_nodes_in_rows(RowsMyOrder, RowsMyOrderV),
|
Chris@0
|
196 var_blank_nodes_in_rows(Result, ResultV),
|
Chris@0
|
197 sort(RowsMyOrderV, OkRows), % TBD: match blank nodes!
|
Chris@0
|
198 sort(ResultV, MyRows),
|
Chris@0
|
199 match_rows(MyRows, OkRows, MyExtra, OkExtra), !,
|
Chris@0
|
200 ( MyExtra == [],
|
Chris@0
|
201 OkExtra == []
|
Chris@0
|
202 -> assert(passed(Test))
|
Chris@0
|
203 ; test_name(Test, Name),
|
Chris@0
|
204 format('~`=t ~q ~`=t~72|~n', [Name]),
|
Chris@0
|
205 format('TYPE: ~q~n', [Type]),
|
Chris@0
|
206 format('MISSED: ~p~n', [OkExtra]),
|
Chris@0
|
207 format('EXTRA: ~p~n', [MyExtra]),
|
Chris@0
|
208 format('~`=t~72|~n~n', []),
|
Chris@0
|
209 assert(failed(Test))
|
Chris@0
|
210 ).
|
Chris@0
|
211 compare_results(Test, Type, Correct, Result) :-
|
Chris@0
|
212 test_name(Test, Name),
|
Chris@0
|
213 format('~`=t ~q ~`=t~72|~n', [Name]),
|
Chris@0
|
214 format('TYPE: ~q~n', [Type]),
|
Chris@0
|
215 format('CORRECT: ~p~n', [Correct]),
|
Chris@0
|
216 format('WE: ~p~n', [Result]),
|
Chris@0
|
217 format('~`=t~72|~n~n', []),
|
Chris@0
|
218 assert(failed(Test)).
|
Chris@0
|
219
|
Chris@0
|
220 %% same_colnames(+Names1, +Names2, -Map)
|
Chris@0
|
221 %
|
Chris@0
|
222 % Is true if Names1 and Names2 contain the same names in possibly
|
Chris@0
|
223 % different order. If the order is diffent, Map is unified to a
|
Chris@0
|
224 % term map(RowLeft, RowRight), where variable bindings between the
|
Chris@0
|
225 % row-terms express the mapping between the rows.
|
Chris@0
|
226
|
Chris@0
|
227 same_colnames(Names, Names, nomap) :- !.
|
Chris@0
|
228 same_colnames(Names1, Names2, map(R1,R2)) :-
|
Chris@0
|
229 msort(Names1, S),
|
Chris@0
|
230 msort(Names2, S),
|
Chris@0
|
231 length(Names1, Len),
|
Chris@0
|
232 functor(R1, row, Len),
|
Chris@0
|
233 functor(R2, row, Len),
|
Chris@0
|
234 fill_vars(Names1, 1, Names2, R1, R2).
|
Chris@0
|
235
|
Chris@0
|
236 fill_vars([], _, _, _, _).
|
Chris@0
|
237 fill_vars([H|T], I, Names, R1, R2) :-
|
Chris@0
|
238 arg(I, R1, V),
|
Chris@0
|
239 nth1(I2, Names, H),
|
Chris@0
|
240 arg(I2, R2, V),
|
Chris@0
|
241 IN is I + 1,
|
Chris@0
|
242 fill_vars(T, IN, Names, R1, R2).
|
Chris@0
|
243
|
Chris@0
|
244 map_rows([], _, []).
|
Chris@0
|
245 map_rows([H0|T0], Map, [H|T]) :-
|
Chris@0
|
246 copy_term(Map, map(H0, H)),
|
Chris@0
|
247 map_rows(T0, Map, T).
|
Chris@0
|
248
|
Chris@0
|
249
|
Chris@0
|
250 %% var_blank_nodes_in_rows(+Rows0, -Rows)
|
Chris@0
|
251 %
|
Chris@0
|
252 % Substitute blank nodes in rows by variables. Note that blank
|
Chris@0
|
253 % nodes of multiple rows are not related.
|
Chris@0
|
254
|
Chris@0
|
255 var_blank_nodes_in_rows([], []).
|
Chris@0
|
256 var_blank_nodes_in_rows([H0|T0], [H|T]) :-
|
Chris@0
|
257 var_blank_nodes(H0, H),
|
Chris@0
|
258 var_blank_nodes(T0, T).
|
Chris@0
|
259
|
Chris@0
|
260
|
Chris@0
|
261 %% var_blank_nodes(+Term, -VarBlanks)
|
Chris@0
|
262 %
|
Chris@0
|
263 % Process Term, replacing blank nodes with variables.
|
Chris@0
|
264
|
Chris@0
|
265 var_blank_nodes(Term0, Term) :-
|
Chris@0
|
266 empty_assoc(Vars),
|
Chris@0
|
267 var_blank_nodes(Term0, Vars, _, Term).
|
Chris@0
|
268
|
Chris@0
|
269 var_blank_nodes(BN, Vars0, Vars, V) :-
|
Chris@0
|
270 rdf_is_bnode(BN), !,
|
Chris@0
|
271 ( get_assoc(BN, Vars0, V)
|
Chris@0
|
272 -> Vars = Vars0
|
Chris@0
|
273 ; put_assoc(BN, Vars0, V, Vars)
|
Chris@0
|
274 ).
|
Chris@0
|
275 var_blank_nodes(Term0, Vars0, Vars, Term) :-
|
Chris@0
|
276 compound(Term0), !,
|
Chris@0
|
277 functor(Term0, Name, Arity),
|
Chris@0
|
278 functor(Term, Name, Arity),
|
Chris@0
|
279 var_blank_nodes_args(0, Arity, Term0, Vars0, Vars, Term).
|
Chris@0
|
280 var_blank_nodes(Term, Vars, Vars, Term).
|
Chris@0
|
281
|
Chris@0
|
282 var_blank_nodes_args(I, I, _, Vars, Vars, _) :- !.
|
Chris@0
|
283 var_blank_nodes_args(I0, Arity, Term0, Vars0, Vars, Term) :-
|
Chris@0
|
284 I is I0 + 1,
|
Chris@0
|
285 arg(I, Term0, A0),
|
Chris@0
|
286 var_blank_nodes(A0, Vars0, Vars1, A),
|
Chris@0
|
287 arg(I, Term, A),
|
Chris@0
|
288 var_blank_nodes_args(I, Arity, Term0, Vars1, Vars, Term).
|
Chris@0
|
289
|
Chris@0
|
290
|
Chris@0
|
291 %% match_rows(+Rows0, +Rows1, -Extra1, -Extra2)
|
Chris@0
|
292 %
|
Chris@0
|
293 % Succeed if both sets of rows are the same. Note that there may
|
Chris@0
|
294 % be blank nodes in the rows. These are already substituted by
|
Chris@0
|
295 % Prolog variables. This is basically a permutation problem. First
|
Chris@0
|
296 % we decide to which row each row can match. Then we sort them to
|
Chris@0
|
297 % the lowest number of matches and finally we start the
|
Chris@0
|
298 % non-deterministic matching process.
|
Chris@0
|
299
|
Chris@0
|
300 match_rows(Rows1, Rows2, Extra1, Extra2) :-
|
Chris@0
|
301 candidate_matches(Rows1, Rows2, Extra1, Extra1Tail, Candidates),
|
Chris@0
|
302 keysort(Candidates, Sorted),
|
Chris@0
|
303 same_rows(Sorted, Rows2, Extra1Tail, Extra2).
|
Chris@0
|
304
|
Chris@0
|
305 candidate_matches([], _, E, E, []).
|
Chris@0
|
306 candidate_matches([H0|T0], Rows, E0, E, [L-Rs|T]) :-
|
Chris@0
|
307 setof(R, member_row(H0, R, Rows), Rs), !,
|
Chris@0
|
308 length(Rs, L),
|
Chris@0
|
309 candidate_matches(T0, Rows, E0, E, T).
|
Chris@0
|
310 candidate_matches([H0|T0], Rows, [H0|E0], E, T) :-
|
Chris@0
|
311 candidate_matches(T0, Rows, E0, E, T).
|
Chris@0
|
312
|
Chris@0
|
313 same_rows(E, [], E, []) :- !.
|
Chris@0
|
314 same_rows([], E, [], E) :- !.
|
Chris@0
|
315 same_rows([_-Rs|T], Rows, E1, E2) :-
|
Chris@0
|
316 member(R, Rs),
|
Chris@0
|
317 select_eq(R, Rows, Rest),
|
Chris@0
|
318 same_rows(T, Rest, E1, E2), !.
|
Chris@0
|
319
|
Chris@0
|
320 select_eq(V, Set, Rest) :-
|
Chris@0
|
321 select(X, Set, Rest),
|
Chris@0
|
322 V == X.
|
Chris@0
|
323
|
Chris@0
|
324 member_row(R, R1, Rows) :-
|
Chris@0
|
325 member(R1, Rows),
|
Chris@0
|
326 same_row(R, R1).
|
Chris@0
|
327
|
Chris@0
|
328 same_row(R, R) :- !.
|
Chris@0
|
329 same_row(R1, R2) :-
|
Chris@0
|
330 forall(arg(I, R1, A1),
|
Chris@0
|
331 ( arg(I, R2, A2),
|
Chris@0
|
332 same_value(A1, A2))).
|
Chris@0
|
333
|
Chris@0
|
334 same_value(V, V) :- !.
|
Chris@0
|
335 same_value(literal(type(T,V1)), literal(type(T,V2))) :-
|
Chris@0
|
336 xsdp_numeric_uri(T, _),
|
Chris@0
|
337 to_number(V1, N1),
|
Chris@0
|
338 to_number(V2, N2),
|
Chris@0
|
339 N1 =:= N2.
|
Chris@0
|
340
|
Chris@0
|
341 to_number(a, _) :- !, fail. % catch variables
|
Chris@0
|
342 to_number(N, N) :-
|
Chris@0
|
343 number(N), !.
|
Chris@0
|
344 to_number(A, N) :-
|
Chris@0
|
345 catch(atom_number(A, N), _, fail).
|
Chris@0
|
346
|
Chris@0
|
347
|
Chris@0
|
348 /*******************************
|
Chris@0
|
349 * RDF RESULTS *
|
Chris@0
|
350 *******************************/
|
Chris@0
|
351
|
Chris@0
|
352 %% result_to_prolog(+Type, +Test, -Result)
|
Chris@0
|
353 %
|
Chris@0
|
354 % Turn the RDF result into a more manageble form. For CONSTRUCT
|
Chris@0
|
355 % and DESCRIBE queries the result is a set of triples. For the
|
Chris@0
|
356 % others the format is described below:
|
Chris@0
|
357 %
|
Chris@0
|
358 % # ASK
|
Chris@0
|
359 %% ask(Bool)
|
Chris@0
|
360 %
|
Chris@0
|
361 % # SELECT
|
Chris@0
|
362 % select([Name1, ...],
|
Chris@0
|
363 % [ row(V1, ...),
|
Chris@0
|
364 % ...
|
Chris@0
|
365 % ])
|
Chris@0
|
366
|
Chris@0
|
367 result_to_prolog(Type, Test, Result) :-
|
Chris@0
|
368 ( test_result_file(Test, ResultFile)
|
Chris@0
|
369 -> ( file_name_extension(_, Ext, ResultFile),
|
Chris@0
|
370 ( Ext == srx
|
Chris@0
|
371 -> sparql_read_xml_result(ResultFile, Result)
|
Chris@0
|
372 ; ( Type == construct
|
Chris@0
|
373 ; Type == describe
|
Chris@0
|
374 )
|
Chris@0
|
375 -> load_triples(ResultFile, Result)
|
Chris@0
|
376 ; rdf_reset_db,
|
Chris@0
|
377 load_triples_to_db(ResultFile),
|
Chris@0
|
378 prolog_result(Result),
|
Chris@0
|
379 rdf_reset_db
|
Chris@0
|
380 )
|
Chris@0
|
381 -> true
|
Chris@0
|
382 ; test_name(Test, Name),
|
Chris@0
|
383 format('FAILED to interpret results for ~q~n', [Name]),
|
Chris@0
|
384 assert(failed_result(Test, ResultFile)),
|
Chris@0
|
385 fail
|
Chris@0
|
386 )
|
Chris@0
|
387 ; Result = no_result
|
Chris@0
|
388 ).
|
Chris@0
|
389
|
Chris@0
|
390 prolog_result(ask(True)) :-
|
Chris@0
|
391 rdf(Result, rdf:type, r:'ResultSet'),
|
Chris@0
|
392 rdf(Result, r:boolean, literal(type(xsd:boolean, True))), !.
|
Chris@0
|
393 prolog_result(select(ColNames, Rows)) :-
|
Chris@0
|
394 rdf(Result, rdf:type, r:'ResultSet'),
|
Chris@0
|
395 colnames(Result, ColNames),
|
Chris@0
|
396 result_rows(Result, ColNames, Rows).
|
Chris@0
|
397
|
Chris@0
|
398 colnames(Result, ColNames) :-
|
Chris@0
|
399 findall(N, rdf(Result, r:resultVariable, literal(N)), ColNames).
|
Chris@0
|
400
|
Chris@0
|
401 result_rows(Result, ColNames, Rows) :-
|
Chris@0
|
402 findall(R, result_row(Result, ColNames, R), Rows).
|
Chris@0
|
403
|
Chris@0
|
404 result_row(Result, ColNames, Row) :-
|
Chris@0
|
405 rdf(Result, r:solution, S),
|
Chris@0
|
406 result_values(ColNames, S, Values),
|
Chris@0
|
407 Row =.. [row|Values].
|
Chris@0
|
408
|
Chris@0
|
409 result_values([], _, []).
|
Chris@0
|
410 result_values([Name|Names], S, [Value|Values]) :-
|
Chris@0
|
411 ( rdf(S, r:binding, Binding),
|
Chris@0
|
412 rdf(Binding, r:variable, literal(Name)),
|
Chris@0
|
413 rdf(Binding, r:value, Value)
|
Chris@0
|
414 -> true
|
Chris@0
|
415 ; Value = '$null$'
|
Chris@0
|
416 ),
|
Chris@0
|
417 result_values(Names, S, Values).
|
Chris@0
|
418
|
Chris@0
|
419
|
Chris@0
|
420 /*******************************
|
Chris@0
|
421 * SYNTAX TESTS *
|
Chris@0
|
422 *******************************/
|
Chris@0
|
423
|
Chris@0
|
424 data_dir('Tests/sparql/data-xml').
|
Chris@0
|
425
|
Chris@0
|
426 % run_syntax_tests/0
|
Chris@0
|
427 %
|
Chris@0
|
428 % Load both the SyntaxDev and all normal tests and runs them
|
Chris@0
|
429 % through the parser. Does not involve any semantic checking.
|
Chris@0
|
430
|
Chris@0
|
431 run_syntax_tests :-
|
Chris@0
|
432 clean_tests,
|
Chris@0
|
433 load_syntax_manifests,
|
Chris@0
|
434 run_all_syntax_tests.
|
Chris@0
|
435
|
Chris@0
|
436 load_syntax_manifests :-
|
Chris@0
|
437 load_manifests(arq),
|
Chris@0
|
438 dev_manifest_files(Files),
|
Chris@0
|
439 maplist(load_triples_to_db, Files).
|
Chris@0
|
440
|
Chris@0
|
441 dev_manifest_files(Files) :-
|
Chris@0
|
442 data_dir(Dir),
|
Chris@0
|
443 atom_concat(Dir, '/SyntaxDev/*/manifest.{ttl,rdf}', Pattern),
|
Chris@0
|
444 expand_file_name(Pattern, Files).
|
Chris@0
|
445
|
Chris@0
|
446
|
Chris@0
|
447 /*******************************
|
Chris@0
|
448 * RUNNING TESTS *
|
Chris@0
|
449 *******************************/
|
Chris@0
|
450
|
Chris@0
|
451 run_all_syntax_tests :-
|
Chris@0
|
452 forall(current_test(_, Test),
|
Chris@0
|
453 (blocked_test(Test) -> true ; syntax_test(Test))),
|
Chris@0
|
454 findall(T, passed(T), Passed), length(Passed, NPassed),
|
Chris@0
|
455 findall(T, failed(T), Failed), length(Failed, NFailed),
|
Chris@0
|
456 findall(T, skipped(T), Skipped), length(Skipped, NSkipped),
|
Chris@0
|
457 format('Passed: ~D; failed: ~D; skipped ~D~n',
|
Chris@0
|
458 [NPassed, NFailed, NSkipped]).
|
Chris@0
|
459
|
Chris@0
|
460 syntax_test(Name) :-
|
Chris@0
|
461 syntax_test(Name, _Query).
|
Chris@0
|
462
|
Chris@0
|
463 syntax_test(Name, Query) :-
|
Chris@0
|
464 test_name(Test, Name), !,
|
Chris@0
|
465 syntax_test(Test, Query).
|
Chris@0
|
466 syntax_test(Test, Query) :-
|
Chris@0
|
467 test_query(Test, Codes),
|
Chris@0
|
468 syntax_test(Test, Codes, Query).
|
Chris@0
|
469
|
Chris@0
|
470 blocked_test(Test) :-
|
Chris@0
|
471 test_name(Test, Name),
|
Chris@0
|
472 blocked(Name).
|
Chris@0
|
473
|
Chris@0
|
474 syntax_test(Test, Codes, Query) :-
|
Chris@0
|
475 pos_syntax_test(Test), !,
|
Chris@0
|
476 ( catch(sparql_parse(Codes, Query, []), E, true)
|
Chris@0
|
477 -> ( var(E)
|
Chris@0
|
478 -> assert(passed(Test))
|
Chris@0
|
479 ; test_name(Test, Name),
|
Chris@0
|
480 format('PARSE TEST ERROR: ~w: ', [Name]),
|
Chris@0
|
481 print_message(error, E),
|
Chris@0
|
482 assert(failed(Test))
|
Chris@0
|
483 )
|
Chris@0
|
484 ; assert(failed(Test)),
|
Chris@0
|
485 test_name(Test, Name),
|
Chris@0
|
486 format('PARSE TEST FAILED: ~q~n', [Name])
|
Chris@0
|
487 ).
|
Chris@0
|
488 syntax_test(Test, Codes, Query) :-
|
Chris@0
|
489 neg_syntax_test(Test), !,
|
Chris@0
|
490 ( catch(sparql_parse(Codes, Query, []), E, true)
|
Chris@0
|
491 -> ( nonvar(E)
|
Chris@0
|
492 -> assert(passed(Test))
|
Chris@0
|
493 ; rdf_has(Test, mf:name, literal(Name)),
|
Chris@0
|
494 format('NEG TEST SUCCEEDED: ~w: ', [Name]),
|
Chris@0
|
495 assert(failed(Test))
|
Chris@0
|
496 )
|
Chris@0
|
497 ; assert(failed(Test)),
|
Chris@0
|
498 rdf_has(Test, mf:name, Name),
|
Chris@0
|
499 format('NEG TEST FAILED WITHOUT ERROR: ~w: ', [Name])
|
Chris@0
|
500 ).
|
Chris@0
|
501 syntax_test(Test, _, _) :-
|
Chris@0
|
502 assert(skipped(Test)).
|
Chris@0
|
503
|
Chris@0
|
504 pos_syntax_test(Test) :- % SyntaxDev
|
Chris@0
|
505 rdfs_individual_of(Test, mfx:'TestSyntax'), !.
|
Chris@0
|
506 pos_syntax_test(Test) :- % Semantic tests
|
Chris@0
|
507 rdf_has(Test, mf:action, Action),
|
Chris@0
|
508 rdf_has(Action, qt:query, _), !.
|
Chris@0
|
509
|
Chris@0
|
510 neg_syntax_test(Test) :-
|
Chris@0
|
511 rdfs_individual_of(Test, mfx:'TestBadSyntax'), !.
|
Chris@0
|
512
|
Chris@0
|
513
|
Chris@0
|
514 /*******************************
|
Chris@0
|
515 * QUERY LISTING *
|
Chris@0
|
516 *******************************/
|
Chris@0
|
517
|
Chris@0
|
518 test_query_listing :-
|
Chris@0
|
519 ( current_test(_, Test),
|
Chris@0
|
520 pos_syntax_test(Test),
|
Chris@0
|
521 test_query_listing(Test),
|
Chris@0
|
522 fail ; true
|
Chris@0
|
523 ).
|
Chris@0
|
524
|
Chris@0
|
525 test_query_listing(Name) :-
|
Chris@0
|
526 test_name(Test, Name), !,
|
Chris@0
|
527 test_query_listing(Test).
|
Chris@0
|
528 test_query_listing(Test) :-
|
Chris@0
|
529 test_name(Test, Name),
|
Chris@0
|
530 test_query(Test, Codes),
|
Chris@0
|
531 format('~`=t ~w ~`=t~72|~n', [Name]),
|
Chris@0
|
532 format('~s', [Codes]),
|
Chris@0
|
533 format('~`-t~72|~n'),
|
Chris@0
|
534 ( catch(sparql_parse(Codes, Query, []), E, true)
|
Chris@0
|
535 -> ( var(E)
|
Chris@0
|
536 -> list_query(Query)
|
Chris@0
|
537 ; print_message(error, E)
|
Chris@0
|
538 )
|
Chris@0
|
539 ; format('FAILED TO PARSE~n')
|
Chris@0
|
540 ),
|
Chris@0
|
541 format('~`=t~72|~n~n').
|
Chris@0
|
542
|
Chris@0
|
543 list_query(select(Vars, _, Query, _)) :-
|
Chris@0
|
544 portray_clause(select(Vars) :- Query).
|
Chris@0
|
545 list_query(construct(Templ, _, Query, _)) :-
|
Chris@0
|
546 portray_clause(construct(Templ) :- Query).
|
Chris@0
|
547 list_query(ask(_, Query)) :-
|
Chris@0
|
548 portray_clause(ask :- Query).
|
Chris@0
|
549 list_query(describe(Vars, _, Query, _)) :-
|
Chris@0
|
550 portray_clause(describe(Vars) :- Query).
|
Chris@0
|
551
|
Chris@0
|
552
|
Chris@0
|
553 /*******************************
|
Chris@0
|
554 * GENERIC TEST STUFF *
|
Chris@0
|
555 *******************************/
|
Chris@0
|
556
|
Chris@0
|
557 clean_tests :-
|
Chris@0
|
558 retractall(failed_result(_, _)),
|
Chris@0
|
559 retractall(passed(_)),
|
Chris@0
|
560 retractall(failed(_)),
|
Chris@0
|
561 retractall(skipped(_)),
|
Chris@0
|
562 rdf_reset_db.
|
Chris@0
|
563
|
Chris@0
|
564 list_tests(passed) :-
|
Chris@0
|
565 forall(passed(Test),
|
Chris@0
|
566 ( test_name(Test, Name),
|
Chris@0
|
567 format('PASSED: ~q~n', [Name]))).
|
Chris@0
|
568 list_tests(failed) :-
|
Chris@0
|
569 forall(failed(Test),
|
Chris@0
|
570 ( test_name(Test, Name),
|
Chris@0
|
571 format('FAILED: ~q~n', [Name]))).
|
Chris@0
|
572 list_tests(skipped) :-
|
Chris@0
|
573 forall(skipped(Test),
|
Chris@0
|
574 ( test_name(Test, Name),
|
Chris@0
|
575 format('SKIPPED: ~q~n', [Name]))).
|
Chris@0
|
576
|
Chris@0
|
577
|
Chris@0
|
578 /*******************************
|
Chris@0
|
579 * DEBUG *
|
Chris@0
|
580 *******************************/
|
Chris@0
|
581
|
Chris@0
|
582 user:portray(String) :-
|
Chris@0
|
583 is_list(String),
|
Chris@0
|
584 length(String, Len),
|
Chris@0
|
585 Len > 3,
|
Chris@0
|
586 ascii_list(String),
|
Chris@0
|
587 format('"~s"', [String]).
|
Chris@0
|
588 user:portray(IRI) :-
|
Chris@0
|
589 atom(IRI),
|
Chris@0
|
590 rdf_global_id(NS:Local, IRI),
|
Chris@0
|
591 Local \== '',
|
Chris@0
|
592 format('~w:~w', [NS, Local]).
|
Chris@0
|
593
|
Chris@0
|
594 ascii_list([]).
|
Chris@0
|
595 ascii_list([H|T]) :-
|
Chris@0
|
596 ascii_char(H),
|
Chris@0
|
597 ascii_list(T).
|
Chris@0
|
598
|
Chris@0
|
599 ascii_char(C) :-
|
Chris@0
|
600 integer(C),
|
Chris@0
|
601 between(32, 127, C), !.
|
Chris@0
|
602 ascii_char(0'\r).
|
Chris@0
|
603 ascii_char(0'\n).
|
Chris@0
|
604 ascii_char(0'\t).
|