diff options
author | David Robillard <d@drobilla.net> | 2012-01-05 17:05:48 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2012-01-05 17:05:48 +0000 |
commit | 9c6d55b0c831370562ced1577fb3d4934cf7dbca (patch) | |
tree | 81a1b6eac615edccf3890dde77a5671bad24e560 | |
parent | 33fc3600004690a9e1868b5fb1ca81da79c2fd61 (diff) | |
download | sord-9c6d55b0c831370562ced1577fb3d4934cf7dbca.tar.gz sord-9c6d55b0c831370562ced1577fb3d4934cf7dbca.tar.bz2 sord-9c6d55b0c831370562ced1577fb3d4934cf7dbca.zip |
Fix comparison of typed literals.
Take advantage of interning in sord_node_equals().
Improve test coverage.
git-svn-id: http://svn.drobilla.net/sord/trunk@185 3d64ff67-21c5-427c-a301-fe4f08042e5a
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | src/sord.c | 30 | ||||
-rw-r--r-- | src/sord_test.c | 176 |
3 files changed, 168 insertions, 40 deletions
@@ -3,6 +3,8 @@ sord (UNRELEASED) unstable; urgency=low * Use path variables in pkgconfig files * Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) * Tolerate serd passing NULL nodes to reader callback (serd 0.6.0) + * Fix comparison of typed literals + * Take advantage of interning in sord_node_equals() -- David Robillard <d@drobilla.net> (UNRELEASED) @@ -199,16 +199,17 @@ sord_world_free(SordWorld* world) free(world); } +/** Compare nodes, considering NULL a wildcard match. */ static inline int sord_node_compare(const SordNode* a, const SordNode* b) { if (a == b || !a || !b) { - return 0; + return 0; // Exact or wildcard match } else if (a->node.type != b->node.type) { return a->node.type - b->node.type; } - int cmp; + int cmp = 0; switch (a->node.type) { case SERD_URI: case SERD_BLANK: @@ -217,7 +218,13 @@ sord_node_compare(const SordNode* a, const SordNode* b) cmp = strcmp((const char*)sord_node_get_string(a), (const char*)sord_node_get_string(b)); if (cmp == 0) { - cmp = sord_node_compare(a->datatype, b->datatype); + // Note: Can't use sord_node_compare here since it does wildcards + if (!a->datatype || !b->datatype) { + cmp = a->datatype - b->datatype; + } else { + cmp = strcmp((const char*)a->datatype->node.buf, + (const char*)b->datatype->node.buf); + } } if (cmp == 0) { if (!a->lang || !b->lang) { @@ -226,23 +233,16 @@ sord_node_compare(const SordNode* a, const SordNode* b) cmp = strcmp(a->lang, b->lang); } } - return cmp; default: - break; // never reached + break; } - assert(false); - return 0; + return cmp; } bool sord_node_equals(const SordNode* a, const SordNode* b) { - if (!a || !b) { - return (a == b); - } else { - // FIXME: nodes are interned, this can be much faster - return (a == b) || (sord_node_compare(a, b) == 0); - } + return a == b; // Nodes are interned } /** Return true iff IDs are equivalent, or one is a wildcard */ @@ -605,6 +605,10 @@ sord_new(SordWorld* world, unsigned indices, bool graphs) sord->indices[DEFAULT_ORDER] = zix_tree_new( false, sord_quad_compare, (void*)orderings[DEFAULT_ORDER]); } + if (graphs && !sord->indices[DEFAULT_GRAPH_ORDER]) { + sord->indices[DEFAULT_GRAPH_ORDER] = zix_tree_new( + false, sord_quad_compare, (void*)orderings[DEFAULT_GRAPH_ORDER]); + } return sord; } diff --git a/src/sord_test.c b/src/sord_test.c index cd6a6dc..76a0fb2 100644 --- a/src/sord_test.c +++ b/src/sord_test.c @@ -55,7 +55,7 @@ test_fail(const char* fmt, ...) } int -generate(SordWorld* world, SordModel* sord, size_t n_quads, size_t n_objects_per) +generate(SordWorld* world, SordModel* sord, size_t n_quads, size_t n_objects_per, SordNode* graph) { fprintf(stderr, "Generating %zu (S P *) quads with %zu objects each\n", n_quads, n_objects_per); @@ -81,6 +81,8 @@ generate(SordWorld* world, SordModel* sord, size_t n_quads, size_t n_objects_per } // Add some literals + + // (98 4 "hello") and (98 4 "hello"^^<5>) SordQuad tup = { 0, 0, 0, 0}; tup[0] = uri(world, 98); tup[1] = uri(world, 4); @@ -88,8 +90,46 @@ generate(SordWorld* world, SordModel* sord, size_t n_quads, size_t n_objects_per tup[3] = 0; sord_add(sord, tup); sord_node_free(world, (SordNode*)tup[2]); - tup[2] = sord_new_literal(world, 0, USTR("hi"), NULL); + tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL); + if (!sord_add(sord, tup)) { + return test_fail("Failed to add typed literal\n"); + } + + // (96 4 "hello"^^<4>) and (96 4 "hello"^^<5>) + tup[0] = uri(world, 96); + tup[1] = uri(world, 4); + tup[2] = sord_new_literal(world, uri(world, 4), USTR("hello"), NULL); + tup[3] = 0; + sord_add(sord, tup); + sord_node_free(world, (SordNode*)tup[2]); + tup[2] = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL); + if (!sord_add(sord, tup)) { + return test_fail("Failed to add typed literal\n"); + } + + // (94 5 "hello") and (94 5 "hello"@en-gb) + tup[0] = uri(world, 94); + tup[1] = uri(world, 5); + tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); + tup[3] = 0; + sord_add(sord, tup); + sord_node_free(world, (SordNode*)tup[2]); + tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb"); + if (!sord_add(sord, tup)) { + return test_fail("Failed to add literal with language\n"); + } + + // (92 6 "hello"@en-us) and (92 5 "hello"@en-gb) + tup[0] = uri(world, 92); + tup[1] = uri(world, 6); + tup[2] = sord_new_literal(world, 0, USTR("hello"), "en-us"); + tup[3] = 0; sord_add(sord, tup); + sord_node_free(world, (SordNode*)tup[2]); + tup[2] = sord_new_literal(world, NULL, USTR("hello"), "en-gb"); + if (!sord_add(sord, tup)) { + return test_fail("Failed to add literal with language\n"); + } sord_node_free(world, (SordNode*)tup[0]); sord_node_free(world, (SordNode*)tup[2]); @@ -126,13 +166,13 @@ generate(SordWorld* world, SordModel* sord, size_t n_quads, size_t n_objects_per } #define TUP_FMT "(%6s %6s %6s)" -#define TUP_FMT_ARGS(t) \ +#define TUP_FMT_ARGS(t) \ ((t)[0] ? sord_node_get_string((t)[0]) : USTR("*")), \ ((t)[1] ? sord_node_get_string((t)[1]) : USTR("*")), \ ((t)[2] ? sord_node_get_string((t)[2]) : USTR("*")) int -test_read(SordWorld* world, SordModel* sord, +test_read(SordWorld* world, SordModel* sord, SordNode* g, const size_t n_quads, const int n_objects_per) { int ret = EXIT_SUCCESS; @@ -154,10 +194,16 @@ test_read(SordWorld* world, SordModel* sord, sord_iter_free(iter); -#define NUM_PATTERNS 9 + SordNode* plain_hello = sord_new_literal(world, 0, USTR("hello"), NULL); + SordNode* type4_hello = sord_new_literal(world, uri(world, 4), USTR("hello"), NULL); + SordNode* type5_hello = sord_new_literal(world, uri(world, 5), USTR("hello"), NULL); + SordNode* gb_hello = sord_new_literal(world, NULL, USTR("hello"), "en-gb"); + SordNode* us_hello = sord_new_literal(world, NULL, USTR("hello"), "en-us"); + +#define NUM_PATTERNS 17 QueryTest patterns[NUM_PATTERNS] = { - { { 0, 0, 0 }, (n_quads * n_objects_per) + 6 }, + { { 0, 0, 0 }, (n_quads * n_objects_per) + 12 }, { { uri(world, 9), uri(world, 9), uri(world, 9) }, 0 }, { { uri(world, 1), uri(world, 2), uri(world, 4) }, 1 }, { { uri(world, 3), uri(world, 4), uri(world, 0) }, 2 }, @@ -165,15 +211,23 @@ test_read(SordWorld* world, SordModel* sord, { { uri(world, 0), uri(world, 0), uri(world, 4) }, 1 }, { { uri(world, 1), uri(world, 0), uri(world, 0) }, 2 }, { { uri(world, 1), uri(world, 0), uri(world, 4) }, 1 }, - { { uri(world, 0), uri(world, 2), uri(world, 0) }, 2 } }; - - SordQuad match = { uri(world, 1), uri(world, 2), uri(world, 4) }; + { { uri(world, 0), uri(world, 2), uri(world, 0) }, 2 }, + { { uri(world, 98), uri(world, 4), plain_hello }, 1 }, + { { uri(world, 98), uri(world, 4), type5_hello }, 1 }, + { { uri(world, 96), uri(world, 4), type4_hello }, 1 }, + { { uri(world, 96), uri(world, 4), type5_hello }, 1 }, + { { uri(world, 94), uri(world, 5), plain_hello }, 1 }, + { { uri(world, 94), uri(world, 5), gb_hello }, 1 }, + { { uri(world, 92), uri(world, 6), gb_hello }, 1 }, + { { uri(world, 92), uri(world, 6), us_hello }, 1 } }; + + SordQuad match = { uri(world, 1), uri(world, 2), uri(world, 4), g }; if (!sord_contains(sord, match)) { return test_fail("Fail: No match for " TUP_FMT "\n", TUP_FMT_ARGS(match)); } - SordQuad nomatch = { uri(world, 1), uri(world, 2), uri(world, 9) }; + SordQuad nomatch = { uri(world, 1), uri(world, 2), uri(world, 9), g }; if (sord_contains(sord, nomatch)) { return test_fail("Fail: False match for " TUP_FMT "\n", TUP_FMT_ARGS(nomatch)); @@ -181,7 +235,7 @@ test_read(SordWorld* world, SordModel* sord, for (unsigned i = 0; i < NUM_PATTERNS; ++i) { QueryTest test = patterns[i]; - SordQuad pat = { test.query[0], test.query[1], test.query[2], 0 }; + SordQuad pat = { test.query[0], test.query[1], test.query[2], g }; fprintf(stderr, "Query " TUP_FMT "... ", TUP_FMT_ARGS(pat)); iter = sord_find(sord, pat); @@ -252,8 +306,9 @@ test_read(SordWorld* world, SordModel* sord, } sord_iter_free(subiter); if (num_sub_results != n_objects_per) { - return test_fail("Fail: Nested query failed (got %d results, expected %d)\n", - num_sub_results, n_objects_per); + return test_fail("Fail: Nested query " TUP_FMT " failed" + "(got %d results, expected %d)\n", + TUP_FMT_ARGS(subpat), num_sub_results, n_objects_per); } last_subject = id[0]; } @@ -275,48 +330,102 @@ main(int argc, char** argv) // Create with minimal indexing SordModel* sord = sord_new(world, SORD_SPO, false); - generate(world, sord, n_quads, n_objects_per); + generate(world, sord, n_quads, n_objects_per, NULL); - if (test_read(world, sord, n_quads, n_objects_per)) { + if (test_read(world, sord, NULL, n_quads, n_objects_per)) { sord_free(sord); sord_world_free(world); return EXIT_FAILURE; } + // Check adding tuples with NULL fields fails + const size_t initial_num_quads = sord_num_quads(sord); + SordQuad tup = { 0, 0, 0, 0}; + if (sord_add(sord, tup)) { + return test_fail("Added NULL tuple\n"); + } + tup[0] = uri(world, 1); + if (sord_add(sord, tup)) { + return test_fail("Added tuple with NULL P and O\n"); + } + tup[1] = uri(world, 2); + if (sord_add(sord, tup)) { + return test_fail("Added tuple with NULL O\n"); + } + + if (sord_num_quads(sord) != initial_num_quads) { + return test_fail("Num quads %zu != %zu\n", + sord_num_quads(sord), initial_num_quads); + } + // Check interning merges equivalent values SordNode* uri_id = sord_new_uri(world, USTR("http://example.org")); - SordNode* blank_id = sord_new_uri(world, USTR("testblank")); + SordNode* blank_id = sord_new_blank(world, USTR("testblank")); SordNode* lit_id = sord_new_literal(world, uri_id, USTR("hello"), NULL); - //sord_clear_cache(write); + if (sord_node_get_type(uri_id) != SORD_URI) { + return test_fail("URI node has incorrect type\n"); + } else if (sord_node_get_type(blank_id) != SORD_BLANK) { + return test_fail("Blank node has incorrect type\n"); + } else if (sord_node_get_type(lit_id) != SORD_LITERAL) { + return test_fail("Literal node has incorrect type\n"); + } + + const size_t initial_num_nodes = sord_num_nodes(world); + SordNode* uri_id2 = sord_new_uri(world, USTR("http://example.org")); - SordNode* blank_id2 = sord_new_uri(world, USTR("testblank")); + SordNode* blank_id2 = sord_new_blank(world, USTR("testblank")); SordNode* lit_id2 = sord_new_literal(world, uri_id, USTR("hello"), NULL); - if (uri_id2 != uri_id) { + if (uri_id2 != uri_id || !sord_node_equals(uri_id2, uri_id)) { fprintf(stderr, "Fail: URI interning failed (duplicates)\n"); goto fail; - } else if (blank_id2 != blank_id) { + } else if (blank_id2 != blank_id || !sord_node_equals(blank_id2, blank_id)) { fprintf(stderr, "Fail: Blank node interning failed (duplicates)\n"); goto fail; - } else if (lit_id2 != lit_id) { + } else if (lit_id2 != lit_id || !sord_node_equals(lit_id2, lit_id)) { fprintf(stderr, "Fail: Literal interning failed (duplicates)\n"); goto fail; } + size_t len; + const uint8_t* str = sord_node_get_string_counted(lit_id2, &len); + if (strcmp((const char*)str, "hello")) { + return test_fail("Literal node corrupt\n"); + } else if (len != strlen("hello")) { + return test_fail("Literal length incorrect\n"); + } + + if (sord_num_nodes(world) != initial_num_nodes) { + return test_fail("Num nodes %zu != %zu\n", + sord_num_nodes(world), initial_num_nodes); + } + // Check interning doesn't clash non-equivalent values SordNode* uri_id3 = sord_new_uri(world, USTR("http://example.orgX")); SordNode* blank_id3 = sord_new_uri(world, USTR("testblankX")); SordNode* lit_id3 = sord_new_literal(world, uri_id, USTR("helloX"), NULL); - if (uri_id3 == uri_id) { + if (uri_id3 == uri_id || sord_node_equals(uri_id3, uri_id)) { fprintf(stderr, "Fail: URI interning failed (clash)\n"); goto fail; - } else if (blank_id3 == blank_id) { + } else if (blank_id3 == blank_id || sord_node_equals(blank_id3, blank_id)) { fprintf(stderr, "Fail: Blank node interning failed (clash)\n"); goto fail; - } else if (lit_id3 == lit_id) { + } else if (lit_id3 == lit_id || sord_node_equals(lit_id3, lit_id)) { fprintf(stderr, "Fail: Literal interning failed (clash)\n"); goto fail; } + // Check literal interning + SordNode* lit4 = sord_new_literal(world, NULL, USTR("hello"), NULL); + SordNode* lit5 = sord_new_literal(world, uri_id2, USTR("hello"), NULL); + SordNode* lit6 = sord_new_literal(world, NULL, USTR("hello"), "en-ca"); + if (lit4 == lit5 || sord_node_equals(lit4, lit5) + || lit4 == lit6 || sord_node_equals(lit4, lit6) + || lit5 == lit6 || sord_node_equals(lit5, lit6)) { + fprintf(stderr, "Fail: Literal interning failed (type/lang clash)\n"); + goto fail; + } + + // Check comparison with NULL sord_node_free(world, uri_id); sord_node_free(world, blank_id); sord_node_free(world, lit_id); @@ -335,15 +444,28 @@ main(int argc, char** argv) for (int i = 0; i < 6; ++i) { sord = sord_new(world, (1 << i), false); printf("Testing Index `%s'\n", index_names[i]); - generate(world, sord, n_quads, n_objects_per); - if (test_read(world, sord, n_quads, n_objects_per)) + generate(world, sord, n_quads, n_objects_per, 0); + if (test_read(world, sord, 0, n_quads, n_objects_per)) + goto fail; + sord_free(sord); + } + + static const char* const graph_index_names[6] = { + "gspo", "gsop", "gops", "gosp", "gpso", "gpos" + }; + + for (int i = 0; i < 6; ++i) { + sord = sord_new(world, (1 << i), true); + printf("Testing Index `%s'\n", graph_index_names[i]); + SordNode* graph = uri(world, 42); + generate(world, sord, n_quads, n_objects_per, graph); + if (test_read(world, sord, graph, n_quads, n_objects_per)) goto fail; sord_free(sord); } // Test removing sord = sord_new(world, SORD_SPO, false); - SordQuad tup = { 0, 0, 0, 0}; tup[0] = uri(world, 1); tup[1] = uri(world, 2); tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL); |