1 /*
2 Copyright 2011-2016 David Robillard <d@drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "serd/serd.h"
18 #include "sord/sord.h"
19
20 #include <inttypes.h>
21 #include <stdarg.h>
22 #include <stdbool.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #ifdef __GNUC__
29 # define SORD_LOG_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
30 #else
31 # define SORD_LOG_FUNC(fmt, arg1)
32 #endif
33
34 static const int DIGITS = 3;
35 static const unsigned n_objects_per = 2;
36
37 static int n_expected_errors = 0;
38
39 typedef struct {
40 SordQuad query;
41 int expected_num_results;
42 } QueryTest;
43
44 #define USTR(s) ((const uint8_t*)(s))
45
46 static SordNode*
uri(SordWorld * world,int num)47 uri(SordWorld* world, int num)
48 {
49 if (num == 0) {
50 return 0;
51 }
52
53 char str[] = "eg:000";
54 char* uri_num = str + 3; // First `0'
55 snprintf(uri_num, DIGITS + 1, "%0*d", DIGITS, num);
56 return sord_new_uri(world, (const uint8_t*)str);
57 }
58
59 SORD_LOG_FUNC(1, 2)
60 static int
test_fail(const char * fmt,...)61 test_fail(const char* fmt, ...)
62 {
63 va_list args;
64 va_start(args, fmt);
65 fprintf(stderr, "error: ");
66 vfprintf(stderr, fmt, args);
67 va_end(args);
68 return 1;
69 }
70
71 static int
generate(SordWorld * world,SordModel * sord,size_t n_quads,SordNode * graph)72 generate(SordWorld* world, SordModel* sord, size_t n_quads, SordNode* graph)
73 {
74 fprintf(stderr,
75 "Generating %zu (S P *) quads with %u objects each\n",
76 n_quads,
77 n_objects_per);
78
79 for (size_t i = 0; i < n_quads; ++i) {
80 int num = (i * n_objects_per) + 1;
81
82 SordNode* ids[2 + n_objects_per];
83 for (unsigned j = 0; j < 2 + n_objects_per; ++j) {
84 ids[j] = uri(world, num++);
85 }
86
87 for (unsigned j = 0; j < n_objects_per; ++j) {
88 SordQuad tup = {ids[0], ids[1], ids[2 + j], graph};
89 if (!sord_add(sord, tup)) {
90 return test_fail("Fail: Failed to add quad\n");
91 }
92 }
93
94 for (unsigned j = 0; j < 2 + n_objects_per; ++j) {
95 sord_node_free(world, ids[j]);
96 }
97 }
98
99 // Add some literals
100
101 // (98 4 "hello") and (98 4 "hello"^^<5>)
102 SordQuad tup = {0, 0, 0, 0};
103 tup[0] = uri(world, 98);
104 tup[1] = uri(world, 4);
105 tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL);
106 tup[3] = graph;
107 sord_add(sord, tup);
108 sord_node_free(world, (SordNode*)tup[2]);
109 tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL);
110 if (!sord_add(sord, tup)) {
111 return test_fail("Failed to add typed literal\n");
112 }
113
114 // (96 4 "hello"^^<4>) and (96 4 "hello"^^<5>)
115 tup[0] = uri(world, 96);
116 tup[1] = uri(world, 4);
117 tup[2] = sord_new_literal(world, uri(world, 4), USTR("hello"), NULL);
118 tup[3] = graph;
119 sord_add(sord, tup);
120 sord_node_free(world, (SordNode*)tup[2]);
121 tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL);
122 if (!sord_add(sord, tup)) {
123 return test_fail("Failed to add typed literal\n");
124 }
125
126 // (94 5 "hello") and (94 5 "hello"@en-gb)
127 tup[0] = uri(world, 94);
128 tup[1] = uri(world, 5);
129 tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL);
130 tup[3] = graph;
131 sord_add(sord, tup);
132 sord_node_free(world, (SordNode*)tup[2]);
133 tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb");
134 if (!sord_add(sord, tup)) {
135 return test_fail("Failed to add literal with language\n");
136 }
137
138 // (92 6 "hello"@en-us) and (92 5 "hello"@en-gb)
139 tup[0] = uri(world, 92);
140 tup[1] = uri(world, 6);
141 tup[2] = sord_new_literal(world, 0, USTR("hello"), "en-us");
142 tup[3] = graph;
143 sord_add(sord, tup);
144 sord_node_free(world, (SordNode*)tup[2]);
145 tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb");
146 if (!sord_add(sord, tup)) {
147 return test_fail("Failed to add literal with language\n");
148 }
149
150 sord_node_free(world, (SordNode*)tup[0]);
151 sord_node_free(world, (SordNode*)tup[2]);
152 tup[0] = uri(world, 14);
153 tup[2] = sord_new_literal(world, 0, USTR("bonjour"), "fr");
154 sord_add(sord, tup);
155 sord_node_free(world, (SordNode*)tup[2]);
156 tup[2] = sord_new_literal(world, 0, USTR("salut"), "fr");
157 sord_add(sord, tup);
158
159 // Attempt to add some duplicates
160 if (sord_add(sord, tup)) {
161 return test_fail("Fail: Successfully added duplicate quad\n");
162 }
163 if (sord_add(sord, tup)) {
164 return test_fail("Fail: Successfully added duplicate quad\n");
165 }
166
167 // Add a blank node subject
168 sord_node_free(world, (SordNode*)tup[0]);
169 tup[0] = sord_new_blank(world, USTR("ablank"));
170 sord_add(sord, tup);
171
172 sord_node_free(world, (SordNode*)tup[1]);
173 sord_node_free(world, (SordNode*)tup[2]);
174 tup[1] = uri(world, 6);
175 tup[2] = uri(world, 7);
176 sord_add(sord, tup);
177 sord_node_free(world, (SordNode*)tup[0]);
178 sord_node_free(world, (SordNode*)tup[1]);
179 sord_node_free(world, (SordNode*)tup[2]);
180
181 return EXIT_SUCCESS;
182 }
183
184 #define TUP_FMT "(%6s %6s %6s)"
185 #define TUP_FMT_ARGS(t) \
186 ((t)[0] ? sord_node_get_string((t)[0]) : USTR("*")), \
187 ((t)[1] ? sord_node_get_string((t)[1]) : USTR("*")), \
188 ((t)[2] ? sord_node_get_string((t)[2]) : USTR("*"))
189
190 static int
test_read(SordWorld * world,SordModel * sord,SordNode * g,const size_t n_quads)191 test_read(SordWorld* world, SordModel* sord, SordNode* g, const size_t n_quads)
192 {
193 int ret = EXIT_SUCCESS;
194
195 SordQuad id;
196
197 SordIter* iter = sord_begin(sord);
198 if (sord_iter_get_model(iter) != sord) {
199 return test_fail("Fail: Iterator has incorrect sord pointer\n");
200 }
201
202 for (; !sord_iter_end(iter); sord_iter_next(iter)) {
203 sord_iter_get(iter, id);
204 }
205
206 // Attempt to increment past end
207 if (!sord_iter_next(iter)) {
208 return test_fail("Fail: Successfully incremented past end\n");
209 }
210
211 sord_iter_free(iter);
212
213 const uint8_t* s = USTR("hello");
214 SordNode* plain_hello = sord_new_literal(world, 0, s, NULL);
215 SordNode* type4_hello = sord_new_literal(world, uri(world, 4), s, NULL);
216 SordNode* type5_hello = sord_new_literal(world, uri(world, 5), s, NULL);
217 SordNode* gb_hello = sord_new_literal(world, NULL, s, "en-gb");
218 SordNode* us_hello = sord_new_literal(world, NULL, s, "en-us");
219
220 #define NUM_PATTERNS 18
221
222 QueryTest patterns[NUM_PATTERNS] = {
223 {{0, 0, 0}, (int)(n_quads * n_objects_per) + 12},
224 {{uri(world, 1), 0, 0}, 2},
225 {{uri(world, 9), uri(world, 9), uri(world, 9)}, 0},
226 {{uri(world, 1), uri(world, 2), uri(world, 4)}, 1},
227 {{uri(world, 3), uri(world, 4), uri(world, 0)}, 2},
228 {{uri(world, 0), uri(world, 2), uri(world, 4)}, 1},
229 {{uri(world, 0), uri(world, 0), uri(world, 4)}, 1},
230 {{uri(world, 1), uri(world, 0), uri(world, 0)}, 2},
231 {{uri(world, 1), uri(world, 0), uri(world, 4)}, 1},
232 {{uri(world, 0), uri(world, 2), uri(world, 0)}, 2},
233 {{uri(world, 98), uri(world, 4), plain_hello}, 1},
234 {{uri(world, 98), uri(world, 4), type5_hello}, 1},
235 {{uri(world, 96), uri(world, 4), type4_hello}, 1},
236 {{uri(world, 96), uri(world, 4), type5_hello}, 1},
237 {{uri(world, 94), uri(world, 5), plain_hello}, 1},
238 {{uri(world, 94), uri(world, 5), gb_hello}, 1},
239 {{uri(world, 92), uri(world, 6), gb_hello}, 1},
240 {{uri(world, 92), uri(world, 6), us_hello}, 1}};
241
242 SordQuad match = {uri(world, 1), uri(world, 2), uri(world, 4), g};
243 if (!sord_contains(sord, match)) {
244 return test_fail("Fail: No match for " TUP_FMT "\n", TUP_FMT_ARGS(match));
245 }
246
247 SordQuad nomatch = {uri(world, 1), uri(world, 2), uri(world, 9), g};
248 if (sord_contains(sord, nomatch)) {
249 return test_fail("Fail: False match for " TUP_FMT "\n",
250 TUP_FMT_ARGS(nomatch));
251 }
252
253 if (sord_get(sord, NULL, NULL, uri(world, 3), g)) {
254 return test_fail("Fail: Get *,*,3 succeeded\n");
255 } else if (!sord_node_equals(
256 sord_get(sord, uri(world, 1), uri(world, 2), NULL, g),
257 uri(world, 3))) {
258 return test_fail("Fail: Get 1,2,* != 3\n");
259 } else if (!sord_node_equals(
260 sord_get(sord, uri(world, 1), NULL, uri(world, 3), g),
261 uri(world, 2))) {
262 return test_fail("Fail: Get 1,*,3 != 2\n");
263 } else if (!sord_node_equals(
264 sord_get(sord, NULL, uri(world, 2), uri(world, 3), g),
265 uri(world, 1))) {
266 return test_fail("Fail: Get *,2,3 != 1\n");
267 }
268
269 for (unsigned i = 0; i < NUM_PATTERNS; ++i) {
270 QueryTest test = patterns[i];
271 SordQuad pat = {test.query[0], test.query[1], test.query[2], g};
272 fprintf(stderr, "Query " TUP_FMT "... ", TUP_FMT_ARGS(pat));
273
274 iter = sord_find(sord, pat);
275 int num_results = 0;
276 for (; !sord_iter_end(iter); sord_iter_next(iter)) {
277 sord_iter_get(iter, id);
278 ++num_results;
279 if (!sord_quad_match(pat, id)) {
280 sord_iter_free(iter);
281 return test_fail("Fail: Query result " TUP_FMT
282 " does not match pattern\n",
283 TUP_FMT_ARGS(id));
284 }
285 }
286 sord_iter_free(iter);
287 if (num_results != test.expected_num_results) {
288 return test_fail("Fail: Expected %d results, got %d\n",
289 test.expected_num_results,
290 num_results);
291 }
292 fprintf(stderr, "OK (%i matches)\n", test.expected_num_results);
293 }
294
295 // Query blank node subject
296 SordQuad pat = {sord_new_blank(world, USTR("ablank")), 0, 0};
297 if (!pat[0]) {
298 return test_fail("Blank node subject lost\n");
299 }
300 fprintf(stderr, "Query " TUP_FMT "... ", TUP_FMT_ARGS(pat));
301 iter = sord_find(sord, pat);
302 int num_results = 0;
303 for (; !sord_iter_end(iter); sord_iter_next(iter)) {
304 sord_iter_get(iter, id);
305 ++num_results;
306 if (!sord_quad_match(pat, id)) {
307 sord_iter_free(iter);
308 return test_fail("Fail: Query result " TUP_FMT
309 " does not match pattern\n",
310 TUP_FMT_ARGS(id));
311 }
312 }
313 fprintf(stderr, "OK\n");
314 sord_node_free(world, (SordNode*)pat[0]);
315 sord_iter_free(iter);
316 if (num_results != 2) {
317 return test_fail("Blank node subject query failed\n");
318 }
319
320 // Test nested queries
321 fprintf(stderr, "Nested Queries... ");
322 const SordNode* last_subject = 0;
323 iter = sord_search(sord, NULL, NULL, NULL, NULL);
324 for (; !sord_iter_end(iter); sord_iter_next(iter)) {
325 sord_iter_get(iter, id);
326 if (id[0] == last_subject) {
327 continue;
328 }
329
330 SordQuad subpat = {id[0], 0, 0};
331 SordIter* subiter = sord_find(sord, subpat);
332 unsigned num_sub_results = 0;
333 if (sord_iter_get_node(subiter, SORD_SUBJECT) != id[0]) {
334 return test_fail("Fail: Incorrect initial submatch\n");
335 }
336 for (; !sord_iter_end(subiter); sord_iter_next(subiter)) {
337 SordQuad subid;
338 sord_iter_get(subiter, subid);
339 if (!sord_quad_match(subpat, subid)) {
340 sord_iter_free(iter);
341 sord_iter_free(subiter);
342 return test_fail("Fail: Nested query result does not match pattern\n");
343 }
344 ++num_sub_results;
345 }
346 sord_iter_free(subiter);
347 if (num_sub_results != n_objects_per) {
348 return test_fail("Fail: Nested query " TUP_FMT " failed"
349 " (%u results, expected %u)\n",
350 TUP_FMT_ARGS(subpat),
351 num_sub_results,
352 n_objects_per);
353 }
354
355 uint64_t count = sord_count(sord, id[0], 0, 0, 0);
356 if (count != num_sub_results) {
357 return test_fail("Fail: Query " TUP_FMT " sord_count() %" PRIu64
358 "does not match result count %u\n",
359 TUP_FMT_ARGS(subpat),
360 count,
361 num_sub_results);
362 }
363
364 last_subject = id[0];
365 }
366 fprintf(stderr, "OK\n\n");
367 sord_iter_free(iter);
368
369 return ret;
370 }
371
372 static SerdStatus
unexpected_error(void * handle,const SerdError * error)373 unexpected_error(void* handle, const SerdError* error)
374 {
375 fprintf(stderr, "unexpected error: ");
376 vfprintf(stderr, error->fmt, *error->args);
377 return SERD_SUCCESS;
378 }
379
380 static SerdStatus
expected_error(void * handle,const SerdError * error)381 expected_error(void* handle, const SerdError* error)
382 {
383 fprintf(stderr, "expected error: ");
384 vfprintf(stderr, error->fmt, *error->args);
385 ++n_expected_errors;
386 return SERD_SUCCESS;
387 }
388
389 static int
finished(SordWorld * world,SordModel * sord,int status)390 finished(SordWorld* world, SordModel* sord, int status)
391 {
392 sord_free(sord);
393 sord_world_free(world);
394 return status;
395 }
396
397 int
main(int argc,char ** argv)398 main(int argc, char** argv)
399 {
400 static const size_t n_quads = 300;
401
402 sord_free(NULL); // Shouldn't crash
403
404 SordWorld* world = sord_world_new();
405
406 // Attempt to create invalid URI
407 fprintf(stderr, "expected ");
408 SordNode* bad_uri = sord_new_uri(world, USTR("noscheme"));
409 if (bad_uri) {
410 return test_fail("Successfully created invalid URI \"noscheme\"\n");
411 }
412 sord_node_free(world, bad_uri);
413
414 sord_world_set_error_sink(world, expected_error, NULL);
415
416 // Attempt to create invalid CURIE
417 SerdNode base = serd_node_from_string(SERD_URI, USTR("http://example.org/"));
418 SerdEnv* env = serd_env_new(&base);
419 SerdNode sbad = serd_node_from_string(SERD_CURIE, USTR("bad:"));
420 SordNode* bad = sord_node_from_serd_node(world, env, &sbad, NULL, NULL);
421 if (bad) {
422 return test_fail("Successfully created CURIE with bad namespace\n");
423 }
424 sord_node_free(world, bad);
425 serd_env_free(env);
426
427 // Attempt to create node from garbage
428 SerdNode junk = SERD_NODE_NULL;
429 junk.type = (SerdType)1234;
430 if (sord_node_from_serd_node(world, env, &junk, NULL, NULL)) {
431 return test_fail("Successfully created node from garbage serd node\n");
432 }
433
434 // Attempt to create NULL node
435 SordNode* nil_node =
436 sord_node_from_serd_node(world, NULL, &SERD_NODE_NULL, NULL, NULL);
437 if (nil_node) {
438 return test_fail("Successfully created NULL node\n");
439 }
440 sord_node_free(world, nil_node);
441
442 // Check node flags are set properly
443 SordNode* with_newline = sord_new_literal(world, NULL, USTR("a\nb"), NULL);
444 if (!(sord_node_get_flags(with_newline) & SERD_HAS_NEWLINE)) {
445 return test_fail("Newline flag not set\n");
446 }
447 SordNode* with_quote = sord_new_literal(world, NULL, USTR("a\"b"), NULL);
448 if (!(sord_node_get_flags(with_quote) & SERD_HAS_QUOTE)) {
449 return test_fail("Quote flag not set\n");
450 }
451
452 // Create with minimal indexing
453 SordModel* sord = sord_new(world, SORD_SPO, false);
454 generate(world, sord, n_quads, NULL);
455
456 if (test_read(world, sord, NULL, n_quads)) {
457 sord_free(sord);
458 sord_world_free(world);
459 return EXIT_FAILURE;
460 }
461
462 // Check adding tuples with NULL fields fails
463 sord_world_set_error_sink(world, expected_error, NULL);
464 const size_t initial_num_quads = sord_num_quads(sord);
465 SordQuad tup = {0, 0, 0, 0};
466 if (sord_add(sord, tup)) {
467 return test_fail("Added NULL tuple\n");
468 }
469 tup[0] = uri(world, 1);
470 if (sord_add(sord, tup)) {
471 return test_fail("Added tuple with NULL P and O\n");
472 }
473 tup[1] = uri(world, 2);
474 if (sord_add(sord, tup)) {
475 return test_fail("Added tuple with NULL O\n");
476 }
477
478 if (sord_num_quads(sord) != initial_num_quads) {
479 return test_fail(
480 "Num quads %zu != %zu\n", sord_num_quads(sord), initial_num_quads);
481 }
482
483 // Check adding tuples with an active iterator fails
484 SordIter* iter = sord_begin(sord);
485 tup[2] = uri(world, 3);
486 if (sord_add(sord, tup)) {
487 return test_fail("Added tuple with active iterator\n");
488 }
489
490 // Check removing tuples with several active iterator fails
491 SordIter* iter2 = sord_begin(sord);
492 if (!sord_erase(sord, iter)) {
493 return test_fail("Erased tuple with several active iterators\n");
494 }
495 n_expected_errors = 0;
496 sord_remove(sord, tup);
497 if (n_expected_errors != 1) {
498 return test_fail("Removed tuple with several active iterators\n");
499 }
500 sord_iter_free(iter);
501 sord_iter_free(iter2);
502
503 sord_world_set_error_sink(world, unexpected_error, NULL);
504
505 // Check interning merges equivalent values
506 SordNode* uri_id = sord_new_uri(world, USTR("http://example.org"));
507 SordNode* blank_id = sord_new_blank(world, USTR("testblank"));
508 SordNode* lit_id = sord_new_literal(world, uri_id, USTR("hello"), NULL);
509 if (sord_node_get_type(uri_id) != SORD_URI) {
510 return test_fail("URI node has incorrect type\n");
511 } else if (sord_node_get_type(blank_id) != SORD_BLANK) {
512 return test_fail("Blank node has incorrect type\n");
513 } else if (sord_node_get_type(lit_id) != SORD_LITERAL) {
514 return test_fail("Literal node has incorrect type\n");
515 }
516
517 const size_t initial_num_nodes = sord_num_nodes(world);
518
519 SordNode* uri_id2 = sord_new_uri(world, USTR("http://example.org"));
520 SordNode* blank_id2 = sord_new_blank(world, USTR("testblank"));
521 SordNode* lit_id2 = sord_new_literal(world, uri_id, USTR("hello"), NULL);
522 if (uri_id2 != uri_id || !sord_node_equals(uri_id2, uri_id)) {
523 fprintf(stderr, "Fail: URI interning failed (duplicates)\n");
524 return finished(world, sord, EXIT_FAILURE);
525 } else if (blank_id2 != blank_id || !sord_node_equals(blank_id2, blank_id)) {
526 fprintf(stderr, "Fail: Blank node interning failed (duplicates)\n");
527 return finished(world, sord, EXIT_FAILURE);
528 } else if (lit_id2 != lit_id || !sord_node_equals(lit_id2, lit_id)) {
529 fprintf(stderr, "Fail: Literal interning failed (duplicates)\n");
530 return finished(world, sord, EXIT_FAILURE);
531 }
532
533 if (sord_num_nodes(world) != initial_num_nodes) {
534 return test_fail(
535 "Num nodes %zu != %zu\n", sord_num_nodes(world), initial_num_nodes);
536 }
537
538 const uint8_t ni_hao[] = {0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD, 0};
539 SordNode* chello = sord_new_literal(world, NULL, ni_hao, "cmn");
540
541 // Test literal length
542 size_t n_bytes;
543 size_t n_chars;
544 const uint8_t* str = sord_node_get_string_counted(lit_id2, &n_bytes);
545 if (strcmp((const char*)str, "hello")) {
546 return test_fail("Literal node corrupt\n");
547 } else if (n_bytes != strlen("hello")) {
548 return test_fail("ASCII literal byte count incorrect\n");
549 }
550
551 str = sord_node_get_string_measured(lit_id2, &n_bytes, &n_chars);
552 if (n_bytes != strlen("hello") || n_chars != strlen("hello")) {
553 return test_fail("ASCII literal measured length incorrect\n");
554 } else if (strcmp((const char*)str, "hello")) {
555 return test_fail("ASCII literal string incorrect\n");
556 }
557
558 str = sord_node_get_string_measured(chello, &n_bytes, &n_chars);
559 if (n_bytes != 6) {
560 return test_fail("Multi-byte literal byte count incorrect\n");
561 } else if (n_chars != 2) {
562 return test_fail("Multi-byte literal character count incorrect\n");
563 } else if (strcmp((const char*)str, (const char*)ni_hao)) {
564 return test_fail("Multi-byte literal string incorrect\n");
565 }
566
567 // Check interning doesn't clash non-equivalent values
568 SordNode* uri_id3 = sord_new_uri(world, USTR("http://example.orgX"));
569 SordNode* blank_id3 = sord_new_blank(world, USTR("testblankX"));
570 SordNode* lit_id3 = sord_new_literal(world, uri_id, USTR("helloX"), NULL);
571 if (uri_id3 == uri_id || sord_node_equals(uri_id3, uri_id)) {
572 fprintf(stderr, "Fail: URI interning failed (clash)\n");
573 return finished(world, sord, EXIT_FAILURE);
574 } else if (blank_id3 == blank_id || sord_node_equals(blank_id3, blank_id)) {
575 fprintf(stderr, "Fail: Blank node interning failed (clash)\n");
576 return finished(world, sord, EXIT_FAILURE);
577 } else if (lit_id3 == lit_id || sord_node_equals(lit_id3, lit_id)) {
578 fprintf(stderr, "Fail: Literal interning failed (clash)\n");
579 return finished(world, sord, EXIT_FAILURE);
580 }
581
582 // Check literal interning
583 SordNode* lit4 = sord_new_literal(world, NULL, USTR("hello"), NULL);
584 SordNode* lit5 = sord_new_literal(world, uri_id2, USTR("hello"), NULL);
585 SordNode* lit6 = sord_new_literal(world, NULL, USTR("hello"), "en-ca");
586 if (lit4 == lit5 || sord_node_equals(lit4, lit5) || lit4 == lit6 ||
587 sord_node_equals(lit4, lit6) || lit5 == lit6 ||
588 sord_node_equals(lit5, lit6)) {
589 fprintf(stderr, "Fail: Literal interning failed (type/lang clash)\n");
590 return finished(world, sord, EXIT_FAILURE);
591 }
592
593 // Check relative URI construction
594 SordNode* reluri =
595 sord_new_relative_uri(world, USTR("a/b"), USTR("http://example.org/"));
596 if (strcmp((const char*)sord_node_get_string(reluri),
597 "http://example.org/a/b")) {
598 fprintf(stderr,
599 "Fail: Bad relative URI constructed: <%s>\n",
600 sord_node_get_string(reluri));
601 return finished(world, sord, EXIT_FAILURE);
602 }
603 SordNode* reluri2 = sord_new_relative_uri(
604 world, USTR("http://drobilla.net/"), USTR("http://example.org/"));
605 if (strcmp((const char*)sord_node_get_string(reluri2),
606 "http://drobilla.net/")) {
607 fprintf(stderr,
608 "Fail: Bad relative URI constructed: <%s>\n",
609 sord_node_get_string(reluri));
610 return finished(world, sord, EXIT_FAILURE);
611 }
612
613 // Check comparison with NULL
614 sord_node_free(world, uri_id);
615 sord_node_free(world, blank_id);
616 sord_node_free(world, lit_id);
617 sord_node_free(world, uri_id2);
618 sord_node_free(world, blank_id2);
619 sord_node_free(world, lit_id2);
620 sord_node_free(world, uri_id3);
621 sord_node_free(world, blank_id3);
622 sord_node_free(world, lit_id3);
623 sord_free(sord);
624
625 static const char* const index_names[6] = {
626 "spo", "sop", "ops", "osp", "pso", "pos"};
627
628 for (int i = 0; i < 6; ++i) {
629 sord = sord_new(world, (1 << i), false);
630 printf("Testing Index `%s'\n", index_names[i]);
631 generate(world, sord, n_quads, 0);
632 if (test_read(world, sord, 0, n_quads)) {
633 return finished(world, sord, EXIT_FAILURE);
634 }
635 sord_free(sord);
636 }
637
638 static const char* const graph_index_names[6] = {
639 "gspo", "gsop", "gops", "gosp", "gpso", "gpos"};
640
641 for (int i = 0; i < 6; ++i) {
642 sord = sord_new(world, (1 << i), true);
643 printf("Testing Index `%s'\n", graph_index_names[i]);
644 SordNode* graph = uri(world, 42);
645 generate(world, sord, n_quads, graph);
646 if (test_read(world, sord, graph, n_quads)) {
647 return finished(world, sord, EXIT_FAILURE);
648 }
649 sord_free(sord);
650 }
651
652 // Test removing
653 sord = sord_new(world, SORD_SPO, true);
654 tup[0] = uri(world, 1);
655 tup[1] = uri(world, 2);
656 tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL);
657 tup[3] = 0;
658 sord_add(sord, tup);
659 if (!sord_ask(sord, tup[0], tup[1], tup[2], tup[3])) {
660 fprintf(stderr, "Failed to add tuple\n");
661 return finished(world, sord, EXIT_FAILURE);
662 }
663 sord_node_free(world, (SordNode*)tup[2]);
664 tup[2] = sord_new_literal(world, 0, USTR("hi"), NULL);
665 sord_add(sord, tup);
666 sord_remove(sord, tup);
667 if (sord_num_quads(sord) != 1) {
668 fprintf(
669 stderr, "Remove failed (%zu quads, expected 1)\n", sord_num_quads(sord));
670 return finished(world, sord, EXIT_FAILURE);
671 }
672
673 iter = sord_find(sord, tup);
674 if (!sord_iter_end(iter)) {
675 fprintf(stderr, "Found removed tuple\n");
676 return finished(world, sord, EXIT_FAILURE);
677 }
678 sord_iter_free(iter);
679
680 // Test double remove (silent success)
681 sord_remove(sord, tup);
682
683 // Load a couple graphs
684 SordNode* graph42 = uri(world, 42);
685 SordNode* graph43 = uri(world, 43);
686 generate(world, sord, 1, graph42);
687 generate(world, sord, 1, graph43);
688
689 // Remove one graph via iterator
690 SerdStatus st;
691 iter = sord_search(sord, NULL, NULL, NULL, graph43);
692 while (!sord_iter_end(iter)) {
693 if ((st = sord_erase(sord, iter))) {
694 fprintf(stderr, "Remove by iterator failed (%s)\n", serd_strerror(st));
695 return finished(world, sord, EXIT_FAILURE);
696 }
697 }
698 sord_iter_free(iter);
699
700 // Erase the first tuple (an element in the default graph)
701 iter = sord_begin(sord);
702 if (sord_erase(sord, iter)) {
703 return test_fail("Failed to erase begin iterator on non-empty model\n");
704 }
705 sord_iter_free(iter);
706
707 // Ensure only the other graph is left
708 SordQuad quad;
709 SordQuad pat = {0, 0, 0, graph42};
710 for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) {
711 sord_iter_get(iter, quad);
712 if (!sord_quad_match(quad, pat)) {
713 fprintf(stderr, "Graph removal via iteration failed\n");
714 return finished(world, sord, EXIT_FAILURE);
715 }
716 }
717 sord_iter_free(iter);
718
719 // Load file into two separate graphs
720 sord_free(sord);
721 sord = sord_new(world, SORD_SPO, true);
722 env = serd_env_new(&base);
723 SordNode* graph1 = sord_new_uri(world, USTR("http://example.org/graph1"));
724 SordNode* graph2 = sord_new_uri(world, USTR("http://example.org/graph2"));
725 SerdReader* reader = sord_new_reader(sord, env, SERD_TURTLE, graph1);
726 if ((st = serd_reader_read_string(reader, USTR("<s> <p> <o> .")))) {
727 fprintf(stderr, "Failed to read string (%s)\n", serd_strerror(st));
728 return finished(world, sord, EXIT_FAILURE);
729 }
730 serd_reader_free(reader);
731 reader = sord_new_reader(sord, env, SERD_TURTLE, graph2);
732 if ((st = serd_reader_read_string(reader, USTR("<s> <p> <o> .")))) {
733 fprintf(stderr, "Failed to re-read string (%s)\n", serd_strerror(st));
734 return finished(world, sord, EXIT_FAILURE);
735 }
736 serd_reader_free(reader);
737 serd_env_free(env);
738
739 // Ensure we only see triple once
740 size_t n_triples = 0;
741 for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) {
742 fprintf(stderr,
743 "%s %s %s %s\n",
744 sord_node_get_string(sord_iter_get_node(iter, SORD_SUBJECT)),
745 sord_node_get_string(sord_iter_get_node(iter, SORD_PREDICATE)),
746 sord_node_get_string(sord_iter_get_node(iter, SORD_OBJECT)),
747 sord_node_get_string(sord_iter_get_node(iter, SORD_GRAPH)));
748
749 ++n_triples;
750 }
751 sord_iter_free(iter);
752 if (n_triples != 1) {
753 fprintf(stderr, "Found duplicate triple\n");
754 return finished(world, sord, EXIT_FAILURE);
755 }
756
757 // Test SPO iteration on an SOP indexed store
758 sord_free(sord);
759 sord = sord_new(world, SORD_SOP, false);
760 generate(world, sord, 1, graph42);
761 for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) {
762 ++n_triples;
763 }
764 sord_iter_free(iter);
765
766 return finished(world, sord, EXIT_SUCCESS);
767 }
768