diff options
Diffstat (limited to 'src/sord_test.c')
-rw-r--r-- | src/sord_test.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/src/sord_test.c b/src/sord_test.c new file mode 100644 index 0000000..371e5c1 --- /dev/null +++ b/src/sord_test.c @@ -0,0 +1,347 @@ +/* Sord, a lightweight RDF model library. + * Copyright 2010-2011 David Robillard <d@drobilla.net> + * + * Sord is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Sord is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define _XOPEN_SOURCE 500 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "sord/sord.h" + +static const int DIGITS = 3; +static const int MAX_NUM = 999; + +typedef struct { SordTuple query; int expected_num_results; } QueryTest; + +static SordID +uri(Sord sord, int num) +{ + if (num == 0) + return 0; + + char uri[] = "eg:000"; + const size_t uri_len = 3 + DIGITS; + char* uri_num = uri + 3; // First `0' + snprintf(uri_num, DIGITS + 1, "%0*d", DIGITS, num); + return sord_get_uri_counted(sord, true, uri, uri_len); +} + +void +generate(Sord sord, size_t n_tuples, size_t n_objects_per) +{ + fprintf(stderr, "Generating %zu (S P *) tuples with %zu objects each\n", + n_tuples, n_objects_per); + + for (size_t i = 0; i < n_tuples; ++i) { + int num = (i * n_objects_per) + 1; + + SordID ids[2 + n_objects_per]; + for (size_t j = 0; j < 2 + n_objects_per; ++j) { + ids[j] = uri(sord, num++); + } + + for (size_t j = 0; j < n_objects_per; ++j) { + SordTuple tup = { ids[0], ids[1], ids[2 + j] }; + sord_add(sord, tup); + } + } + + // Add some literals + SordTuple tup; + tup[0] = uri(sord, 98); + tup[1] = uri(sord, 4); + tup[2] = sord_get_literal(sord, true, 0, "hello", NULL); + tup[3] = 0; + sord_add(sord, tup); + tup[2] = sord_get_literal(sord, true, 0, "hi", NULL); + sord_add(sord, tup); + + tup[0] = uri(sord, 14); + tup[2] = sord_get_literal(sord, true, 0, "bonjour", "fr"); + sord_add(sord, tup); + tup[2] = sord_get_literal(sord, true, 0, "salut", "fr"); + sord_add(sord, tup); + + // Attempt to add some duplicates + sord_add(sord, tup); + sord_add(sord, tup); + + // Add a blank node subject + tup[0] = sord_get_blank(sord, true, "ablank"); + sord_add(sord, tup); + + tup[1] = uri(sord, 6); + tup[2] = uri(sord, 7); + sord_add(sord, tup); +} + +/** Trivial function to return EXIT_FAILURE (useful as a breakpoint) */ +int +test_fail() +{ + return EXIT_FAILURE; +} + +#define TUP_FMT "(%6s %6s %6s)" +#define TUP_FMT_ARGS(t) \ + (sord_node_load(sord, (t)[0]) \ + ? sord_node_get_string(sord_node_load(sord, (t)[0])) : "*"), \ + (sord_node_load(sord, (t)[1]) \ + ? sord_node_get_string(sord_node_load(sord, (t)[1])) : "*"), \ + (sord_node_load(sord, (t)[2]) \ + ? sord_node_get_string(sord_node_load(sord, (t)[2])) : "*") + +int +test_read(Sord sord, const size_t n_tuples, const int n_objects_per) +{ + int ret = EXIT_SUCCESS; + + SordTuple id; + + SordIter iter = sord_begin(sord); + if (sord_iter_get_sord(iter) != sord) { + fprintf(stderr, "Fail: Iterator has incorrect sord pointer\n"); + return test_fail(); + } + + for (; !sord_iter_is_end(iter); sord_iter_increment(iter)) + sord_iter_get(iter, id); + + // Attempt to increment past end + if (!sord_iter_increment(iter)) { + fprintf(stderr, "Fail: Successfully incremented past end\n"); + return test_fail(); + } + + sord_iter_free(iter); + +#define NUM_PATTERNS 9 + + QueryTest patterns[NUM_PATTERNS] = { + { { 0, 0, 0 }, (n_tuples * n_objects_per) + 6 }, + { { uri(sord, 9), uri(sord, 9), uri(sord, 9) }, 0 }, + { { uri(sord, 1), uri(sord, 2), uri(sord, 4) }, 1 }, + { { uri(sord, 3), uri(sord, 4), uri(sord, 0) }, 2 }, + { { uri(sord, 0), uri(sord, 2), uri(sord, 4) }, 1 }, + { { uri(sord, 0), uri(sord, 0), uri(sord, 4) }, 1 }, + { { uri(sord, 1), uri(sord, 0), uri(sord, 0) }, 2 }, + { { uri(sord, 1), uri(sord, 0), uri(sord, 4) }, 1 }, + { { uri(sord, 0), uri(sord, 2), uri(sord, 0) }, 2 } }; + + for (unsigned i = 0; i < NUM_PATTERNS; ++i) { + QueryTest test = patterns[i]; + SordTuple pat = { test.query[0], test.query[1], test.query[2], 0 }; + fprintf(stderr, "Query " TUP_FMT "... ", TUP_FMT_ARGS(pat)); + + iter = sord_find(sord, pat); + int num_results = 0; + for (; !sord_iter_is_end(iter); sord_iter_increment(iter)) { + sord_iter_get(iter, id); + ++num_results; + if (!sord_tuple_match(pat, id)) { + sord_iter_free(iter); + fprintf(stderr, "Fail: Query result " TUP_FMT " does not match pattern\n", + TUP_FMT_ARGS(id)); + return test_fail(); + } + } + sord_iter_free(iter); + if (num_results != test.expected_num_results) { + fprintf(stderr, "Fail: Expected %d results, got %d\n", + test.expected_num_results, num_results); + return test_fail(); + } + fprintf(stderr, "OK (%u matches)\n", test.expected_num_results); + } + + // Query blank node subject + SordTuple pat = { sord_get_blank(sord, true, "ablank"), 0, 0 }; + if (!pat[0]) { + fprintf(stderr, "Blank node subject lost\n"); + return test_fail(); + } + fprintf(stderr, "Query " TUP_FMT "... ", TUP_FMT_ARGS(pat)); + iter = sord_find(sord, pat); + int num_results = 0; + for (; !sord_iter_is_end(iter); sord_iter_increment(iter)) { + sord_iter_get(iter, id); + ++num_results; + if (!sord_tuple_match(pat, id)) { + sord_iter_free(iter); + fprintf(stderr, "Fail: Query result " TUP_FMT " does not match pattern\n", + TUP_FMT_ARGS(id)); + return test_fail(); + } + } + fprintf(stderr, "OK\n"); + sord_iter_free(iter); + if (num_results != 2) { + fprintf(stderr, "Blank node subject query failed\n"); + return test_fail(); + } + + // Test nested queries + fprintf(stderr, "Nested Queries... "); + pat[0] = pat[1] = pat[2] = 0; + SordID last_subject = 0; + iter = sord_find(sord, pat); + for (; !sord_iter_is_end(iter); sord_iter_increment(iter)) { + sord_iter_get(iter, id); + if (id[0] == last_subject) + continue; + + SordTuple subpat = { id[0], 0, 0 }; + SordIter subiter = sord_find(sord, subpat); + int num_sub_results = 0; + for (; !sord_iter_is_end(subiter); sord_iter_increment(subiter)) { + SordTuple subid; + sord_iter_get(subiter, subid); + if (!sord_tuple_match(subpat, subid)) { + sord_iter_free(iter); + sord_iter_free(subiter); + fprintf(stderr, "Fail: Nested query result does not match pattern\n"); + return test_fail(); + } + ++num_sub_results; + } + sord_iter_free(subiter); + if (num_sub_results != n_objects_per) { + fprintf(stderr, "Fail: Nested query failed (got %d results, expected %d)\n", + num_sub_results, n_objects_per); + return test_fail(); + } + last_subject = id[0]; + } + fprintf(stderr, "OK\n\n"); + sord_iter_free(iter); + + return ret; +} + +int +test_write(Sord sord, const size_t n_tuples, const int n_objects_per) +{ + int ret = EXIT_SUCCESS; + + fprintf(stderr, "Removing Statements... "); + + // Remove statements + SordIter iter; + for (iter = sord_begin(sord); !sord_iter_is_end(iter);) { + sord_remove_iter(sord, iter); + } + sord_iter_free(iter); + + const int num_tuples = sord_num_tuples(sord); + if (num_tuples != 0) { + fprintf(stderr, "Fail: All tuples removed but %d tuples remain\n", num_tuples); + return test_fail(); + } + + fprintf(stderr, "OK\n\n"); + + return ret; +} + +int +main(int argc, char** argv) +{ + static const size_t n_tuples = 300; + static const int n_objects_per = 2; + + sord_free(NULL); // Shouldn't crash + + // Create with default options + Sord sord = sord_new("testdb"); + sord_set_option(sord, "http://unknown", "something", SORD_LITERAL, NULL, NULL); + sord_open(sord); + generate(sord, n_tuples, n_objects_per); + + if (test_read(sord, n_tuples, n_objects_per)) { + sord_free(sord); + return EXIT_FAILURE; + } + + // Check interning merges equivalent values + SordID uri_id = sord_get_uri(sord, true, "http://example.org"); + SordID blank_id = sord_get_uri(sord, true, "testblank"); + SordID lit_id = sord_get_literal(sord, true, uri_id, "hello", NULL); + //sord_clear_cache(write); + SordID uri_id2 = sord_get_uri(sord, false, "http://example.org"); + SordID blank_id2 = sord_get_uri(sord, false, "testblank"); + SordID lit_id2 = sord_get_literal(sord, false, uri_id, "hello", NULL); + if (uri_id2 != uri_id) { + fprintf(stderr, "Fail: URI interning failed (duplicates)\n"); + goto fail; + } else if (blank_id2 != blank_id) { + fprintf(stderr, "Fail: Blank node interning failed (duplicates)\n"); + goto fail; + } else if (lit_id2 != lit_id) { + fprintf(stderr, "Fail: Literal interning failed (duplicates)\n"); + goto fail; + } + + // Check interning doesn't clash non-equivalent values + SordID uri_id3 = sord_get_uri(sord, false, "http://example.orgX"); + SordID blank_id3 = sord_get_uri(sord, false, "testblankX"); + SordID lit_id3 = sord_get_literal(sord, false, uri_id, "helloX", NULL); + if (uri_id3 == uri_id) { + fprintf(stderr, "Fail: URI interning failed (clash)\n"); + goto fail; + } else if (blank_id3 == blank_id) { + fprintf(stderr, "Fail: Blank node interning failed (clash)\n"); + goto fail; + } else if (lit_id3 == lit_id) { + fprintf(stderr, "Fail: Literal interning failed (clash)\n"); + goto fail; + } + + sord_free(sord); + + // Test each pattern type with each index + static const char* const index_names[6] = { + "spo", "sop", "ops", "osp", "pso", "pos" + }; + + char* option = strdup("http://drobilla.net/ns/sord#index-xxx"); + const size_t option_len = strlen(option); + for (int i = 0; i < 6; ++i) { + strncpy(option + option_len - 3, index_names[i], 3); + sord = sord_new("testdb"); + sord_set_option(sord, option, "true", SORD_LITERAL, NULL, NULL); + printf("Testing Index `%s'\n", index_names[i]); + sord_open(sord); + generate(sord, n_tuples, n_objects_per); + if (test_read(sord, n_tuples, n_objects_per)) + goto fail; + sord_free(sord); + } + free(option); + + sord = sord_new("testdb"); + sord_open(sord); + if (test_write(sord, n_tuples, n_objects_per)) + goto fail; + + sord_free(sord); + + return EXIT_SUCCESS; + +fail: + sord_free(sord); + return EXIT_FAILURE; +} |