Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sord_test.c 23KB

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