summaryrefslogtreecommitdiffstats
path: root/src/sord_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sord_test.c')
-rw-r--r--src/sord_test.c347
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;
+}