2 Copyright 2011-2016 David Robillard <http://drobilla.net>
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.
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.
22 #include "sord/sord.h"
24 static const int DIGITS
= 3;
25 static const unsigned n_objects_per
= 2;
27 static int n_expected_errors
= 0;
31 int expected_num_results
;
34 #define USTR(s) ((const uint8_t*)(s))
37 uri(SordWorld
* world
, int num
)
42 char str
[] = "eg:000";
43 char* uri_num
= str
+ 3; // First `0'
44 snprintf(uri_num
, DIGITS
+ 1, "%0*d", DIGITS
, num
);
45 return sord_new_uri(world
, (const uint8_t*)str
);
49 test_fail(const char* fmt
, ...)
53 fprintf(stderr
, "error: ");
54 vfprintf(stderr
, fmt
, args
);
60 generate(SordWorld
* world
,
65 fprintf(stderr
, "Generating %zu (S P *) quads with %u objects each\n",
66 n_quads
, n_objects_per
);
68 for (size_t i
= 0; i
< n_quads
; ++i
) {
69 int num
= (i
* n_objects_per
) + 1;
71 SordNode
* ids
[2 + n_objects_per
];
72 for (unsigned j
= 0; j
< 2 + n_objects_per
; ++j
) {
73 ids
[j
] = uri(world
, num
++);
76 for (unsigned j
= 0; j
< n_objects_per
; ++j
) {
77 SordQuad tup
= { ids
[0], ids
[1], ids
[2 + j
], graph
};
78 if (!sord_add(sord
, tup
)) {
79 return test_fail("Fail: Failed to add quad\n");
83 for (unsigned j
= 0; j
< 2 + n_objects_per
; ++j
) {
84 sord_node_free(world
, ids
[j
]);
90 // (98 4 "hello") and (98 4 "hello"^^<5>)
91 SordQuad tup
= { 0, 0, 0, 0 };
92 tup
[0] = uri(world
, 98);
93 tup
[1] = uri(world
, 4);
94 tup
[2] = sord_new_literal(world
, 0, USTR("hello"), NULL
);
97 sord_node_free(world
, (SordNode
*)tup
[2]);
98 tup
[2] = sord_new_literal(world
, uri(world
, 5), USTR("hello"), NULL
);
99 if (!sord_add(sord
, tup
)) {
100 return test_fail("Failed to add typed literal\n");
103 // (96 4 "hello"^^<4>) and (96 4 "hello"^^<5>)
104 tup
[0] = uri(world
, 96);
105 tup
[1] = uri(world
, 4);
106 tup
[2] = sord_new_literal(world
, uri(world
, 4), USTR("hello"), NULL
);
109 sord_node_free(world
, (SordNode
*)tup
[2]);
110 tup
[2] = sord_new_literal(world
, uri(world
, 5), USTR("hello"), NULL
);
111 if (!sord_add(sord
, tup
)) {
112 return test_fail("Failed to add typed literal\n");
115 // (94 5 "hello") and (94 5 "hello"@en-gb)
116 tup
[0] = uri(world
, 94);
117 tup
[1] = uri(world
, 5);
118 tup
[2] = sord_new_literal(world
, 0, USTR("hello"), NULL
);
121 sord_node_free(world
, (SordNode
*)tup
[2]);
122 tup
[2] = sord_new_literal(world
, NULL
, USTR("hello"), "en-gb");
123 if (!sord_add(sord
, tup
)) {
124 return test_fail("Failed to add literal with language\n");
127 // (92 6 "hello"@en-us) and (92 5 "hello"@en-gb)
128 tup
[0] = uri(world
, 92);
129 tup
[1] = uri(world
, 6);
130 tup
[2] = sord_new_literal(world
, 0, USTR("hello"), "en-us");
133 sord_node_free(world
, (SordNode
*)tup
[2]);
134 tup
[2] = sord_new_literal(world
, NULL
, USTR("hello"), "en-gb");
135 if (!sord_add(sord
, tup
)) {
136 return test_fail("Failed to add literal with language\n");
139 sord_node_free(world
, (SordNode
*)tup
[0]);
140 sord_node_free(world
, (SordNode
*)tup
[2]);
141 tup
[0] = uri(world
, 14);
142 tup
[2] = sord_new_literal(world
, 0, USTR("bonjour"), "fr");
144 sord_node_free(world
, (SordNode
*)tup
[2]);
145 tup
[2] = sord_new_literal(world
, 0, USTR("salut"), "fr");
148 // Attempt to add some duplicates
149 if (sord_add(sord
, tup
)) {
150 return test_fail("Fail: Successfully added duplicate quad\n");
152 if (sord_add(sord
, tup
)) {
153 return test_fail("Fail: Successfully added duplicate quad\n");
156 // Add a blank node subject
157 sord_node_free(world
, (SordNode
*)tup
[0]);
158 tup
[0] = sord_new_blank(world
, USTR("ablank"));
161 sord_node_free(world
, (SordNode
*)tup
[1]);
162 sord_node_free(world
, (SordNode
*)tup
[2]);
163 tup
[1] = uri(world
, 6);
164 tup
[2] = uri(world
, 7);
166 sord_node_free(world
, (SordNode
*)tup
[0]);
167 sord_node_free(world
, (SordNode
*)tup
[1]);
168 sord_node_free(world
, (SordNode
*)tup
[2]);
173 #define TUP_FMT "(%6s %6s %6s)"
174 #define TUP_FMT_ARGS(t) \
175 ((t)[0] ? sord_node_get_string((t)[0]) : USTR("*")), \
176 ((t)[1] ? sord_node_get_string((t)[1]) : USTR("*")), \
177 ((t)[2] ? sord_node_get_string((t)[2]) : USTR("*"))
180 test_read(SordWorld
* world
, SordModel
* sord
, SordNode
* g
,
181 const size_t n_quads
)
183 int ret
= EXIT_SUCCESS
;
187 SordIter
* iter
= sord_begin(sord
);
188 if (sord_iter_get_model(iter
) != sord
) {
189 return test_fail("Fail: Iterator has incorrect sord pointer\n");
192 for (; !sord_iter_end(iter
); sord_iter_next(iter
))
193 sord_iter_get(iter
, id
);
195 // Attempt to increment past end
196 if (!sord_iter_next(iter
)) {
197 return test_fail("Fail: Successfully incremented past end\n");
200 sord_iter_free(iter
);
202 const uint8_t* s
= USTR("hello");
203 SordNode
* plain_hello
= sord_new_literal(world
, 0, s
, NULL
);
204 SordNode
* type4_hello
= sord_new_literal(world
, uri(world
, 4), s
, NULL
);
205 SordNode
* type5_hello
= sord_new_literal(world
, uri(world
, 5), s
, NULL
);
206 SordNode
* gb_hello
= sord_new_literal(world
, NULL
, s
, "en-gb");
207 SordNode
* us_hello
= sord_new_literal(world
, NULL
, s
, "en-us");
209 #define NUM_PATTERNS 18
211 QueryTest patterns
[NUM_PATTERNS
] = {
212 { { 0, 0, 0 }, (int)(n_quads
* n_objects_per
) + 12 },
213 { { uri(world
, 1), 0, 0 }, 2 },
214 { { uri(world
, 9), uri(world
, 9), uri(world
, 9) }, 0 },
215 { { uri(world
, 1), uri(world
, 2), uri(world
, 4) }, 1 },
216 { { uri(world
, 3), uri(world
, 4), uri(world
, 0) }, 2 },
217 { { uri(world
, 0), uri(world
, 2), uri(world
, 4) }, 1 },
218 { { uri(world
, 0), uri(world
, 0), uri(world
, 4) }, 1 },
219 { { uri(world
, 1), uri(world
, 0), uri(world
, 0) }, 2 },
220 { { uri(world
, 1), uri(world
, 0), uri(world
, 4) }, 1 },
221 { { uri(world
, 0), uri(world
, 2), uri(world
, 0) }, 2 },
222 { { uri(world
, 98), uri(world
, 4), plain_hello
}, 1 },
223 { { uri(world
, 98), uri(world
, 4), type5_hello
}, 1 },
224 { { uri(world
, 96), uri(world
, 4), type4_hello
}, 1 },
225 { { uri(world
, 96), uri(world
, 4), type5_hello
}, 1 },
226 { { uri(world
, 94), uri(world
, 5), plain_hello
}, 1 },
227 { { uri(world
, 94), uri(world
, 5), gb_hello
}, 1 },
228 { { uri(world
, 92), uri(world
, 6), gb_hello
}, 1 },
229 { { uri(world
, 92), uri(world
, 6), us_hello
}, 1 } };
231 SordQuad match
= { uri(world
, 1), uri(world
, 2), uri(world
, 4), g
};
232 if (!sord_contains(sord
, match
)) {
233 return test_fail("Fail: No match for " TUP_FMT
"\n",
234 TUP_FMT_ARGS(match
));
237 SordQuad nomatch
= { uri(world
, 1), uri(world
, 2), uri(world
, 9), g
};
238 if (sord_contains(sord
, nomatch
)) {
239 return test_fail("Fail: False match for " TUP_FMT
"\n",
240 TUP_FMT_ARGS(nomatch
));
243 if (sord_get(sord
, NULL
, NULL
, uri(world
, 3), g
)) {
244 return test_fail("Fail: Get *,*,3 succeeded\n");
245 } else if (!sord_node_equals(
246 sord_get(sord
, uri(world
, 1), uri(world
, 2), NULL
, g
),
248 return test_fail("Fail: Get 1,2,* != 3\n");
249 } else if (!sord_node_equals(
250 sord_get(sord
, uri(world
, 1), NULL
, uri(world
, 3), g
),
252 return test_fail("Fail: Get 1,*,3 != 2\n");
253 } else if (!sord_node_equals(
254 sord_get(sord
, NULL
, uri(world
, 2), uri(world
, 3), g
),
256 return test_fail("Fail: Get *,2,3 != 1\n");
259 for (unsigned i
= 0; i
< NUM_PATTERNS
; ++i
) {
260 QueryTest test
= patterns
[i
];
261 SordQuad pat
= { test
.query
[0], test
.query
[1], test
.query
[2], g
};
262 fprintf(stderr
, "Query " TUP_FMT
"... ", TUP_FMT_ARGS(pat
));
264 iter
= sord_find(sord
, pat
);
266 for (; !sord_iter_end(iter
); sord_iter_next(iter
)) {
267 sord_iter_get(iter
, id
);
269 if (!sord_quad_match(pat
, id
)) {
270 sord_iter_free(iter
);
272 "Fail: Query result " TUP_FMT
" does not match pattern\n",
276 sord_iter_free(iter
);
277 if (num_results
!= test
.expected_num_results
) {
278 return test_fail("Fail: Expected %d results, got %d\n",
279 test
.expected_num_results
, num_results
);
281 fprintf(stderr
, "OK (%u matches)\n", test
.expected_num_results
);
284 // Query blank node subject
285 SordQuad pat
= { sord_new_blank(world
, USTR("ablank")), 0, 0 };
287 return test_fail("Blank node subject lost\n");
289 fprintf(stderr
, "Query " TUP_FMT
"... ", TUP_FMT_ARGS(pat
));
290 iter
= sord_find(sord
, pat
);
292 for (; !sord_iter_end(iter
); sord_iter_next(iter
)) {
293 sord_iter_get(iter
, id
);
295 if (!sord_quad_match(pat
, id
)) {
296 sord_iter_free(iter
);
298 "Fail: Query result " TUP_FMT
" does not match pattern\n",
302 fprintf(stderr
, "OK\n");
303 sord_node_free(world
, (SordNode
*)pat
[0]);
304 sord_iter_free(iter
);
305 if (num_results
!= 2) {
306 return test_fail("Blank node subject query failed\n");
309 // Test nested queries
310 fprintf(stderr
, "Nested Queries... ");
311 const SordNode
* last_subject
= 0;
312 iter
= sord_search(sord
, NULL
, NULL
, NULL
, NULL
);
313 for (; !sord_iter_end(iter
); sord_iter_next(iter
)) {
314 sord_iter_get(iter
, id
);
315 if (id
[0] == last_subject
)
318 SordQuad subpat
= { id
[0], 0, 0 };
319 SordIter
* subiter
= sord_find(sord
, subpat
);
320 uint64_t num_sub_results
= 0;
321 if (sord_iter_get_node(subiter
, SORD_SUBJECT
) != id
[0]) {
322 return test_fail("Fail: Incorrect initial submatch\n");
324 for (; !sord_iter_end(subiter
); sord_iter_next(subiter
)) {
326 sord_iter_get(subiter
, subid
);
327 if (!sord_quad_match(subpat
, subid
)) {
328 sord_iter_free(iter
);
329 sord_iter_free(subiter
);
331 "Fail: Nested query result does not match pattern\n");
335 sord_iter_free(subiter
);
336 if (num_sub_results
!= n_objects_per
) {
338 "Fail: Nested query " TUP_FMT
" failed"
339 " (%d results, expected %d)\n",
340 TUP_FMT_ARGS(subpat
), num_sub_results
, n_objects_per
);
343 uint64_t count
= sord_count(sord
, id
[0], 0, 0, 0);
344 if (count
!= num_sub_results
) {
345 return test_fail("Fail: Query " TUP_FMT
" sord_count() %d"
346 "does not match result count %d\n",
347 TUP_FMT_ARGS(subpat
), count
, num_sub_results
);
350 last_subject
= id
[0];
352 fprintf(stderr
, "OK\n\n");
353 sord_iter_free(iter
);
359 unexpected_error(void* handle
, const SerdError
* error
)
361 fprintf(stderr
, "unexpected error: ");
362 vfprintf(stderr
, error
->fmt
, *error
->args
);
367 expected_error(void* handle
, const SerdError
* error
)
369 fprintf(stderr
, "expected error: ");
370 vfprintf(stderr
, error
->fmt
, *error
->args
);
376 finished(SordWorld
* world
, SordModel
* sord
, int status
)
379 sord_world_free(world
);
384 main(int argc
, char** argv
)
386 static const size_t n_quads
= 300;
388 sord_free(NULL
); // Shouldn't crash
390 SordWorld
* world
= sord_world_new();
393 // Attempt to create invalid URI
394 fprintf(stderr
, "expected ");
395 SordNode
* bad_uri
= sord_new_uri(world
, USTR("noscheme"));
397 return test_fail("Successfully created invalid URI \"noscheme\"\n");
399 sord_node_free(world
, bad_uri
);
401 sord_world_set_error_sink(world
, expected_error
, NULL
);
403 // Attempt to create invalid CURIE
404 SerdNode base
= serd_node_from_string(SERD_URI
, USTR("http://example.org/"));
405 SerdEnv
* env
= serd_env_new(&base
);
406 SerdNode sbadns
= serd_node_from_string(SERD_CURIE
, USTR("badns:"));
407 SordNode
* badns
= sord_node_from_serd_node(world
, env
, &sbadns
, NULL
, NULL
);
409 return test_fail("Successfully created CURIE with bad namespace\n");
411 sord_node_free(world
, badns
);
414 // Attempt to create node from garbage
415 SerdNode junk
= SERD_NODE_NULL
;
416 junk
.type
= (SerdType
)1234;
417 if (sord_node_from_serd_node(world
, env
, &junk
, NULL
, NULL
)) {
418 return test_fail("Successfully created node from garbage serd node\n");
421 // Attempt to create NULL node
422 SordNode
* nil_node
= sord_node_from_serd_node(
423 world
, NULL
, &SERD_NODE_NULL
, NULL
, NULL
);
425 return test_fail("Successfully created NULL node\n");
427 sord_node_free(world
, nil_node
);
429 // Attempt to double-free a node
430 SordNode
* garbage
= sord_new_uri(world
, USTR("urn:garbage"));
431 sord_node_free(world
, garbage
);
432 sord_world_set_error_sink(world
, expected_error
, NULL
);
433 sord_node_free(world
, garbage
);
434 sord_world_set_error_sink(world
, unexpected_error
, NULL
);
435 if (n_expected_errors
!= 2) {
436 return test_fail("Successfully freed node twice\n");
439 sord_world_set_error_sink(world
, unexpected_error
, NULL
);
441 // Check node flags are set properly
442 SordNode
* with_newline
= sord_new_literal(world
, NULL
, USTR("a\nb"), NULL
);
443 if (!(sord_node_get_flags(with_newline
) & SERD_HAS_NEWLINE
)) {
444 return test_fail("Newline flag not set\n");
446 SordNode
* with_quote
= sord_new_literal(world
, NULL
, USTR("a\"b"), NULL
);
447 if (!(sord_node_get_flags(with_quote
) & SERD_HAS_QUOTE
)) {
448 return test_fail("Quote flag not set\n");
451 // Create with minimal indexing
452 SordModel
* sord
= sord_new(world
, SORD_SPO
, false);
453 generate(world
, sord
, n_quads
, NULL
);
455 if (test_read(world
, sord
, NULL
, n_quads
)) {
457 sord_world_free(world
);
461 // Check adding tuples with NULL fields fails
462 sord_world_set_error_sink(world
, expected_error
, NULL
);
463 const size_t initial_num_quads
= sord_num_quads(sord
);
464 SordQuad tup
= { 0, 0, 0, 0};
465 if (sord_add(sord
, tup
)) {
466 return test_fail("Added NULL tuple\n");
468 tup
[0] = uri(world
, 1);
469 if (sord_add(sord
, tup
)) {
470 return test_fail("Added tuple with NULL P and O\n");
472 tup
[1] = uri(world
, 2);
473 if (sord_add(sord
, tup
)) {
474 return test_fail("Added tuple with NULL O\n");
477 if (sord_num_quads(sord
) != initial_num_quads
) {
478 return test_fail("Num quads %zu != %zu\n",
479 sord_num_quads(sord
), initial_num_quads
);
482 // Check adding tuples with an active iterator fails
483 SordIter
* iter
= sord_begin(sord
);
484 tup
[2] = uri(world
, 3);
485 if (sord_add(sord
, tup
)) {
486 return test_fail("Added tuple with active iterator\n");
489 // Check removing tuples with several active iterator fails
490 SordIter
* iter2
= sord_begin(sord
);
491 if (!sord_erase(sord
, iter
)) {
492 return test_fail("Erased tuple with several active iterators\n");
494 n_expected_errors
= 0;
495 sord_remove(sord
, tup
);
496 if (n_expected_errors
!= 1) {
497 return test_fail("Removed tuple with several active iterators\n");
499 sord_iter_free(iter
);
500 sord_iter_free(iter2
);
502 sord_world_set_error_sink(world
, unexpected_error
, NULL
);
504 // Check interning merges equivalent values
505 SordNode
* uri_id
= sord_new_uri(world
, USTR("http://example.org"));
506 SordNode
* blank_id
= sord_new_blank(world
, USTR("testblank"));
507 SordNode
* lit_id
= sord_new_literal(world
, uri_id
, USTR("hello"), NULL
);
508 if (sord_node_get_type(uri_id
) != SORD_URI
) {
509 return test_fail("URI node has incorrect type\n");
510 } else if (sord_node_get_type(blank_id
) != SORD_BLANK
) {
511 return test_fail("Blank node has incorrect type\n");
512 } else if (sord_node_get_type(lit_id
) != SORD_LITERAL
) {
513 return test_fail("Literal node has incorrect type\n");
516 const size_t initial_num_nodes
= sord_num_nodes(world
);
518 SordNode
* uri_id2
= sord_new_uri(world
, USTR("http://example.org"));
519 SordNode
* blank_id2
= sord_new_blank(world
, USTR("testblank"));
520 SordNode
* lit_id2
= sord_new_literal(world
, uri_id
, USTR("hello"), NULL
);
521 if (uri_id2
!= uri_id
|| !sord_node_equals(uri_id2
, uri_id
)) {
522 fprintf(stderr
, "Fail: URI interning failed (duplicates)\n");
523 return finished(world
, sord
, EXIT_FAILURE
);
524 } else if (blank_id2
!= blank_id
525 || !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
);
533 if (sord_num_nodes(world
) != initial_num_nodes
) {
534 return test_fail("Num nodes %zu != %zu\n",
535 sord_num_nodes(world
), initial_num_nodes
);
538 const uint8_t ni_hao
[] = { 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD };
539 SordNode
* chello
= sord_new_literal(world
, NULL
, ni_hao
, "cmn");
541 // Test literal length
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");
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");
556 str
= sord_node_get_string_measured(chello
, &n_bytes
, &n_chars
);
558 return test_fail("Multi-byte literal byte count incorrect\n");
559 } else if (n_chars
!= 2) {
560 return test_fail("Multi-byte literal character count incorrect\n");
563 // Check interning doesn't clash non-equivalent values
564 SordNode
* uri_id3
= sord_new_uri(world
, USTR("http://example.orgX"));
565 SordNode
* blank_id3
= sord_new_blank(world
, USTR("testblankX"));
566 SordNode
* lit_id3
= sord_new_literal(world
, uri_id
, USTR("helloX"), NULL
);
567 if (uri_id3
== uri_id
|| sord_node_equals(uri_id3
, uri_id
)) {
568 fprintf(stderr
, "Fail: URI interning failed (clash)\n");
569 return finished(world
, sord
, EXIT_FAILURE
);
570 } else if (blank_id3
== blank_id
|| sord_node_equals(blank_id3
, blank_id
)) {
571 fprintf(stderr
, "Fail: Blank node interning failed (clash)\n");
572 return finished(world
, sord
, EXIT_FAILURE
);
573 } else if (lit_id3
== lit_id
|| sord_node_equals(lit_id3
, lit_id
)) {
574 fprintf(stderr
, "Fail: Literal interning failed (clash)\n");
575 return finished(world
, sord
, EXIT_FAILURE
);
578 // Check literal interning
579 SordNode
* lit4
= sord_new_literal(world
, NULL
, USTR("hello"), NULL
);
580 SordNode
* lit5
= sord_new_literal(world
, uri_id2
, USTR("hello"), NULL
);
581 SordNode
* lit6
= sord_new_literal(world
, NULL
, USTR("hello"), "en-ca");
582 if (lit4
== lit5
|| sord_node_equals(lit4
, lit5
)
583 || lit4
== lit6
|| sord_node_equals(lit4
, lit6
)
584 || lit5
== lit6
|| sord_node_equals(lit5
, lit6
)) {
585 fprintf(stderr
, "Fail: Literal interning failed (type/lang clash)\n");
586 return finished(world
, sord
, EXIT_FAILURE
);
589 // Check relative URI construction
590 SordNode
* reluri
= sord_new_relative_uri(
591 world
, USTR("a/b"), USTR("http://example.org/"));
592 if (strcmp((const char*)sord_node_get_string(reluri
),
593 "http://example.org/a/b")) {
594 fprintf(stderr
, "Fail: Bad relative URI constructed: <%s>\n",
595 sord_node_get_string(reluri
));
596 return finished(world
, sord
, EXIT_FAILURE
);
598 SordNode
* reluri2
= sord_new_relative_uri(
599 world
, USTR("http://drobilla.net/"), USTR("http://example.org/"));
600 if (strcmp((const char*)sord_node_get_string(reluri2
),
601 "http://drobilla.net/")) {
602 fprintf(stderr
, "Fail: Bad relative URI constructed: <%s>\n",
603 sord_node_get_string(reluri
));
604 return finished(world
, sord
, EXIT_FAILURE
);
607 // Check comparison with NULL
608 sord_node_free(world
, uri_id
);
609 sord_node_free(world
, blank_id
);
610 sord_node_free(world
, lit_id
);
611 sord_node_free(world
, uri_id2
);
612 sord_node_free(world
, blank_id2
);
613 sord_node_free(world
, lit_id2
);
614 sord_node_free(world
, uri_id3
);
615 sord_node_free(world
, blank_id3
);
616 sord_node_free(world
, lit_id3
);
619 static const char* const index_names
[6] = {
620 "spo", "sop", "ops", "osp", "pso", "pos"
623 for (int i
= 0; i
< 6; ++i
) {
624 sord
= sord_new(world
, (1 << i
), false);
625 printf("Testing Index `%s'\n", index_names
[i
]);
626 generate(world
, sord
, n_quads
, 0);
627 if (test_read(world
, sord
, 0, n_quads
))
628 return finished(world
, sord
, EXIT_FAILURE
);
632 static const char* const graph_index_names
[6] = {
633 "gspo", "gsop", "gops", "gosp", "gpso", "gpos"
636 for (int i
= 0; i
< 6; ++i
) {
637 sord
= sord_new(world
, (1 << i
), true);
638 printf("Testing Index `%s'\n", graph_index_names
[i
]);
639 SordNode
* graph
= uri(world
, 42);
640 generate(world
, sord
, n_quads
, graph
);
641 if (test_read(world
, sord
, graph
, n_quads
))
642 return finished(world
, sord
, EXIT_FAILURE
);
647 sord
= sord_new(world
, SORD_SPO
, true);
648 tup
[0] = uri(world
, 1);
649 tup
[1] = uri(world
, 2);
650 tup
[2] = sord_new_literal(world
, 0, USTR("hello"), NULL
);
653 if (!sord_ask(sord
, tup
[0], tup
[1], tup
[2], tup
[3])) {
654 fprintf(stderr
, "Failed to add tuple\n");
655 return finished(world
, sord
, EXIT_FAILURE
);
657 sord_node_free(world
, (SordNode
*)tup
[2]);
658 tup
[2] = sord_new_literal(world
, 0, USTR("hi"), NULL
);
660 sord_remove(sord
, tup
);
661 if (sord_num_quads(sord
) != 1) {
662 fprintf(stderr
, "Remove failed (%zu quads, expected 1)\n",
663 sord_num_quads(sord
));
664 return finished(world
, sord
, EXIT_FAILURE
);
667 iter
= sord_find(sord
, tup
);
668 if (!sord_iter_end(iter
)) {
669 fprintf(stderr
, "Found removed tuple\n");
670 return finished(world
, sord
, EXIT_FAILURE
);
672 sord_iter_free(iter
);
674 // Test double remove (silent success)
675 sord_remove(sord
, tup
);
677 // Load a couple graphs
678 SordNode
* graph42
= uri(world
, 42);
679 SordNode
* graph43
= uri(world
, 43);
680 generate(world
, sord
, 1, graph42
);
681 generate(world
, sord
, 1, graph43
);
683 // Remove one graph via iterator
685 iter
= sord_search(sord
, NULL
, NULL
, NULL
, graph43
);
686 while (!sord_iter_end(iter
)) {
687 if ((st
= sord_erase(sord
, iter
))) {
688 fprintf(stderr
, "Remove by iterator failed (%s)\n",
690 return finished(world
, sord
, EXIT_FAILURE
);
693 sord_iter_free(iter
);
695 // Erase the first tuple (an element in the default graph)
696 iter
= sord_begin(sord
);
697 if (sord_erase(sord
, iter
)) {
698 return test_fail("Failed to erase begin iterator on non-empty model\n");
700 sord_iter_free(iter
);
702 // Ensure only the other graph is left
704 SordQuad pat
= { 0, 0, 0, graph42
};
705 for (iter
= sord_begin(sord
); !sord_iter_end(iter
); sord_iter_next(iter
)) {
706 sord_iter_get(iter
, quad
);
707 if (!sord_quad_match(quad
, pat
)) {
708 fprintf(stderr
, "Graph removal via iteration failed\n");
709 return finished(world
, sord
, EXIT_FAILURE
);
712 sord_iter_free(iter
);
714 // Load file into two separate graphs
716 sord
= sord_new(world
, SORD_SPO
, true);
717 env
= serd_env_new(&base
);
718 SordNode
* graph1
= sord_new_uri(world
, USTR("http://example.org/graph1"));
719 SordNode
* graph2
= sord_new_uri(world
, USTR("http://example.org/graph2"));
720 SerdReader
* reader
= sord_new_reader(sord
, env
, SERD_TURTLE
, graph1
);
721 if ((st
= serd_reader_read_string(reader
, USTR("<s> <p> <o> .")))) {
722 fprintf(stderr
, "Failed to read string (%s)\n", serd_strerror(st
));
723 return finished(world
, sord
, EXIT_FAILURE
);
725 serd_reader_free(reader
);
726 reader
= sord_new_reader(sord
, env
, SERD_TURTLE
, graph2
);
727 if ((st
= serd_reader_read_string(reader
, USTR("<s> <p> <o> .")))) {
728 fprintf(stderr
, "Failed to re-read string (%s)\n", serd_strerror(st
));
729 return finished(world
, sord
, EXIT_FAILURE
);
731 serd_reader_free(reader
);
734 // Ensure we only see triple once
735 size_t n_triples
= 0;
736 for (iter
= sord_begin(sord
); !sord_iter_end(iter
); sord_iter_next(iter
)) {
737 fprintf(stderr
, "%s %s %s %s\n",
738 sord_node_get_string(sord_iter_get_node(iter
, SORD_SUBJECT
)),
739 sord_node_get_string(sord_iter_get_node(iter
, SORD_PREDICATE
)),
740 sord_node_get_string(sord_iter_get_node(iter
, SORD_OBJECT
)),
741 sord_node_get_string(sord_iter_get_node(iter
, SORD_GRAPH
)));
745 sord_iter_free(iter
);
746 if (n_triples
!= 1) {
747 fprintf(stderr
, "Found duplicate triple\n");
748 return finished(world
, sord
, EXIT_FAILURE
);
751 // Test SPO iteration on an SOP indexed store
753 sord
= sord_new(world
, SORD_SOP
, false);
754 generate(world
, sord
, 1, graph42
);
755 for (iter
= sord_begin(sord
); !sord_iter_end(iter
); sord_iter_next(iter
)) {
758 sord_iter_free(iter
);
760 return finished(world
, sord
, EXIT_SUCCESS
);