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 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /*
  2. Copyright 2011-2016 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 }, (int)(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. static int
  326. finished(SordWorld* world, SordModel* sord, int status)
  327. {
  328. sord_free(sord);
  329. sord_world_free(world);
  330. return status;
  331. }
  332. int
  333. main(int argc, char** argv)
  334. {
  335. static const size_t n_quads = 300;
  336. sord_free(NULL); // Shouldn't crash
  337. SordWorld* world = sord_world_new();
  338. // Attempt to create invalid URI
  339. fprintf(stderr, "expected ");
  340. SordNode* bad_uri = sord_new_uri(world, USTR("noscheme"));
  341. if (bad_uri) {
  342. return test_fail("Successfully created invalid URI \"noscheme\"\n");
  343. }
  344. sord_node_free(world, bad_uri);
  345. sord_world_set_error_sink(world, expected_error, NULL);
  346. // Attempt to create invalid CURIE
  347. SerdNode base = serd_node_from_string(SERD_URI, USTR("http://example.org/"));
  348. SerdEnv* env = serd_env_new(&base);
  349. SerdNode sbadns = serd_node_from_string(SERD_CURIE, USTR("badns:"));
  350. SordNode* badns = sord_node_from_serd_node(world, env, &sbadns, NULL, NULL);
  351. if (badns) {
  352. return test_fail("Successfully created CURIE with bad namespace\n");
  353. }
  354. sord_node_free(world, badns);
  355. serd_env_free(env);
  356. // Attempt to create node from garbage
  357. SerdNode junk = SERD_NODE_NULL;
  358. junk.type = (SerdType)1234;
  359. if (sord_node_from_serd_node(world, env, &junk, NULL, NULL)) {
  360. return test_fail("Successfully created node from garbage serd node\n");
  361. }
  362. // Attempt to create NULL node
  363. SordNode* nil_node = sord_node_from_serd_node(
  364. world, NULL, &SERD_NODE_NULL, NULL, NULL);
  365. if (nil_node) {
  366. return test_fail("Successfully created NULL node\n");
  367. }
  368. sord_node_free(world, nil_node);
  369. // Attempt to double-free a node
  370. SordNode* garbage = sord_new_uri(world, USTR("urn:garbage"));
  371. sord_node_free(world, garbage);
  372. sord_world_set_error_sink(world, expected_error, NULL);
  373. sord_node_free(world, garbage);
  374. sord_world_set_error_sink(world, unexpected_error, NULL);
  375. if (n_expected_errors != 2) {
  376. return test_fail("Successfully freed node twice\n");
  377. }
  378. sord_world_set_error_sink(world, unexpected_error, NULL);
  379. // Check node flags are set properly
  380. SordNode* with_newline = sord_new_literal(world, NULL, USTR("a\nb"), NULL);
  381. if (!(sord_node_get_flags(with_newline) & SERD_HAS_NEWLINE)) {
  382. return test_fail("Newline flag not set\n");
  383. }
  384. SordNode* with_quote = sord_new_literal(world, NULL, USTR("a\"b"), NULL);
  385. if (!(sord_node_get_flags(with_quote) & SERD_HAS_QUOTE)) {
  386. return test_fail("Quote flag not set\n");
  387. }
  388. // Create with minimal indexing
  389. SordModel* sord = sord_new(world, SORD_SPO, false);
  390. generate(world, sord, n_quads, NULL);
  391. if (test_read(world, sord, NULL, n_quads)) {
  392. sord_free(sord);
  393. sord_world_free(world);
  394. return EXIT_FAILURE;
  395. }
  396. // Check adding tuples with NULL fields fails
  397. sord_world_set_error_sink(world, expected_error, NULL);
  398. const size_t initial_num_quads = sord_num_quads(sord);
  399. SordQuad tup = { 0, 0, 0, 0};
  400. if (sord_add(sord, tup)) {
  401. return test_fail("Added NULL tuple\n");
  402. }
  403. tup[0] = uri(world, 1);
  404. if (sord_add(sord, tup)) {
  405. return test_fail("Added tuple with NULL P and O\n");
  406. }
  407. tup[1] = uri(world, 2);
  408. if (sord_add(sord, tup)) {
  409. return test_fail("Added tuple with NULL O\n");
  410. }
  411. if (sord_num_quads(sord) != initial_num_quads) {
  412. return test_fail("Num quads %zu != %zu\n",
  413. sord_num_quads(sord), initial_num_quads);
  414. }
  415. // Check adding tuples with an active iterator fails
  416. SordIter* iter = sord_begin(sord);
  417. tup[2] = uri(world, 3);
  418. if (sord_add(sord, tup)) {
  419. return test_fail("Added tuple with active iterator\n");
  420. }
  421. // Check removing tuples with several active iterator fails
  422. SordIter* iter2 = sord_begin(sord);
  423. if (!sord_erase(sord, iter)) {
  424. return test_fail("Erased tuple with several active iterators\n");
  425. }
  426. n_expected_errors = 0;
  427. sord_remove(sord, tup);
  428. if (n_expected_errors != 1) {
  429. return test_fail("Removed tuple with several active iterators\n");
  430. }
  431. sord_iter_free(iter);
  432. sord_iter_free(iter2);
  433. sord_world_set_error_sink(world, unexpected_error, NULL);
  434. // Check interning merges equivalent values
  435. SordNode* uri_id = sord_new_uri(world, USTR("http://example.org"));
  436. SordNode* blank_id = sord_new_blank(world, USTR("testblank"));
  437. SordNode* lit_id = sord_new_literal(world, uri_id, USTR("hello"), NULL);
  438. if (sord_node_get_type(uri_id) != SORD_URI) {
  439. return test_fail("URI node has incorrect type\n");
  440. } else if (sord_node_get_type(blank_id) != SORD_BLANK) {
  441. return test_fail("Blank node has incorrect type\n");
  442. } else if (sord_node_get_type(lit_id) != SORD_LITERAL) {
  443. return test_fail("Literal node has incorrect type\n");
  444. }
  445. const size_t initial_num_nodes = sord_num_nodes(world);
  446. SordNode* uri_id2 = sord_new_uri(world, USTR("http://example.org"));
  447. SordNode* blank_id2 = sord_new_blank(world, USTR("testblank"));
  448. SordNode* lit_id2 = sord_new_literal(world, uri_id, USTR("hello"), NULL);
  449. if (uri_id2 != uri_id || !sord_node_equals(uri_id2, uri_id)) {
  450. fprintf(stderr, "Fail: URI interning failed (duplicates)\n");
  451. return finished(world, sord, EXIT_FAILURE);
  452. } else if (blank_id2 != blank_id
  453. || !sord_node_equals(blank_id2, blank_id)) {
  454. fprintf(stderr, "Fail: Blank node interning failed (duplicates)\n");
  455. return finished(world, sord, EXIT_FAILURE);
  456. } else if (lit_id2 != lit_id || !sord_node_equals(lit_id2, lit_id)) {
  457. fprintf(stderr, "Fail: Literal interning failed (duplicates)\n");
  458. return finished(world, sord, EXIT_FAILURE);
  459. }
  460. if (sord_num_nodes(world) != initial_num_nodes) {
  461. return test_fail("Num nodes %zu != %zu\n",
  462. sord_num_nodes(world), initial_num_nodes);
  463. }
  464. const uint8_t ni_hao[] = { 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD };
  465. SordNode* chello = sord_new_literal(world, NULL, ni_hao, "cmn");
  466. // Test literal length
  467. size_t n_bytes;
  468. size_t n_chars;
  469. const uint8_t* str = sord_node_get_string_counted(lit_id2, &n_bytes);
  470. if (strcmp((const char*)str, "hello")) {
  471. return test_fail("Literal node corrupt\n");
  472. } else if (n_bytes != strlen("hello")) {
  473. return test_fail("ASCII literal byte count incorrect\n");
  474. }
  475. str = sord_node_get_string_measured(lit_id2, &n_bytes, &n_chars);
  476. if (n_bytes != strlen("hello") || n_chars != strlen("hello")) {
  477. return test_fail("ASCII literal measured length incorrect\n");
  478. }
  479. str = sord_node_get_string_measured(chello, &n_bytes, &n_chars);
  480. if (n_bytes != 6) {
  481. return test_fail("Multi-byte literal byte count incorrect\n");
  482. } else if (n_chars != 2) {
  483. return test_fail("Multi-byte literal character count incorrect\n");
  484. }
  485. // Check interning doesn't clash non-equivalent values
  486. SordNode* uri_id3 = sord_new_uri(world, USTR("http://example.orgX"));
  487. SordNode* blank_id3 = sord_new_blank(world, USTR("testblankX"));
  488. SordNode* lit_id3 = sord_new_literal(world, uri_id, USTR("helloX"), NULL);
  489. if (uri_id3 == uri_id || sord_node_equals(uri_id3, uri_id)) {
  490. fprintf(stderr, "Fail: URI interning failed (clash)\n");
  491. return finished(world, sord, EXIT_FAILURE);
  492. } else if (blank_id3 == blank_id || sord_node_equals(blank_id3, blank_id)) {
  493. fprintf(stderr, "Fail: Blank node interning failed (clash)\n");
  494. return finished(world, sord, EXIT_FAILURE);
  495. } else if (lit_id3 == lit_id || sord_node_equals(lit_id3, lit_id)) {
  496. fprintf(stderr, "Fail: Literal interning failed (clash)\n");
  497. return finished(world, sord, EXIT_FAILURE);
  498. }
  499. // Check literal interning
  500. SordNode* lit4 = sord_new_literal(world, NULL, USTR("hello"), NULL);
  501. SordNode* lit5 = sord_new_literal(world, uri_id2, USTR("hello"), NULL);
  502. SordNode* lit6 = sord_new_literal(world, NULL, USTR("hello"), "en-ca");
  503. if (lit4 == lit5 || sord_node_equals(lit4, lit5)
  504. || lit4 == lit6 || sord_node_equals(lit4, lit6)
  505. || lit5 == lit6 || sord_node_equals(lit5, lit6)) {
  506. fprintf(stderr, "Fail: Literal interning failed (type/lang clash)\n");
  507. return finished(world, sord, EXIT_FAILURE);
  508. }
  509. // Check relative URI construction
  510. SordNode* reluri = sord_new_relative_uri(
  511. world, USTR("a/b"), USTR("http://example.org/"));
  512. if (strcmp((const char*)sord_node_get_string(reluri),
  513. "http://example.org/a/b")) {
  514. fprintf(stderr, "Fail: Bad relative URI constructed: <%s>\n",
  515. sord_node_get_string(reluri));
  516. return finished(world, sord, EXIT_FAILURE);
  517. }
  518. SordNode* reluri2 = sord_new_relative_uri(
  519. world, USTR("http://drobilla.net/"), USTR("http://example.org/"));
  520. if (strcmp((const char*)sord_node_get_string(reluri2),
  521. "http://drobilla.net/")) {
  522. fprintf(stderr, "Fail: Bad relative URI constructed: <%s>\n",
  523. sord_node_get_string(reluri));
  524. return finished(world, sord, EXIT_FAILURE);
  525. }
  526. // Check comparison with NULL
  527. sord_node_free(world, uri_id);
  528. sord_node_free(world, blank_id);
  529. sord_node_free(world, lit_id);
  530. sord_node_free(world, uri_id2);
  531. sord_node_free(world, blank_id2);
  532. sord_node_free(world, lit_id2);
  533. sord_node_free(world, uri_id3);
  534. sord_node_free(world, blank_id3);
  535. sord_node_free(world, lit_id3);
  536. sord_free(sord);
  537. static const char* const index_names[6] = {
  538. "spo", "sop", "ops", "osp", "pso", "pos"
  539. };
  540. for (int i = 0; i < 6; ++i) {
  541. sord = sord_new(world, (1 << i), false);
  542. printf("Testing Index `%s'\n", index_names[i]);
  543. generate(world, sord, n_quads, 0);
  544. if (test_read(world, sord, 0, n_quads))
  545. return finished(world, sord, EXIT_FAILURE);
  546. sord_free(sord);
  547. }
  548. static const char* const graph_index_names[6] = {
  549. "gspo", "gsop", "gops", "gosp", "gpso", "gpos"
  550. };
  551. for (int i = 0; i < 6; ++i) {
  552. sord = sord_new(world, (1 << i), true);
  553. printf("Testing Index `%s'\n", graph_index_names[i]);
  554. SordNode* graph = uri(world, 42);
  555. generate(world, sord, n_quads, graph);
  556. if (test_read(world, sord, graph, n_quads))
  557. return finished(world, sord, EXIT_FAILURE);
  558. sord_free(sord);
  559. }
  560. // Test removing
  561. sord = sord_new(world, SORD_SPO, true);
  562. tup[0] = uri(world, 1);
  563. tup[1] = uri(world, 2);
  564. tup[2] = sord_new_literal(world, 0, USTR("hello"), NULL);
  565. tup[3] = 0;
  566. sord_add(sord, tup);
  567. if (!sord_ask(sord, tup[0], tup[1], tup[2], tup[3])) {
  568. fprintf(stderr, "Failed to add tuple\n");
  569. return finished(world, sord, EXIT_FAILURE);
  570. }
  571. sord_node_free(world, (SordNode*)tup[2]);
  572. tup[2] = sord_new_literal(world, 0, USTR("hi"), NULL);
  573. sord_add(sord, tup);
  574. sord_remove(sord, tup);
  575. if (sord_num_quads(sord) != 1) {
  576. fprintf(stderr, "Remove failed (%zu quads, expected 1)\n",
  577. sord_num_quads(sord));
  578. return finished(world, sord, EXIT_FAILURE);
  579. }
  580. iter = sord_find(sord, tup);
  581. if (!sord_iter_end(iter)) {
  582. fprintf(stderr, "Found removed tuple\n");
  583. return finished(world, sord, EXIT_FAILURE);
  584. }
  585. sord_iter_free(iter);
  586. // Test double remove (silent success)
  587. sord_remove(sord, tup);
  588. // Load a couple graphs
  589. SordNode* graph42 = uri(world, 42);
  590. SordNode* graph43 = uri(world, 43);
  591. generate(world, sord, 1, graph42);
  592. generate(world, sord, 1, graph43);
  593. // Remove one graph via iterator
  594. SerdStatus st;
  595. iter = sord_search(sord, NULL, NULL, NULL, graph43);
  596. while (!sord_iter_end(iter)) {
  597. if ((st = sord_erase(sord, iter))) {
  598. fprintf(stderr, "Remove by iterator failed (%s)\n",
  599. serd_strerror(st));
  600. return finished(world, sord, EXIT_FAILURE);
  601. }
  602. }
  603. sord_iter_free(iter);
  604. // Erase the first tuple (an element in the default graph)
  605. iter = sord_begin(sord);
  606. if (sord_erase(sord, iter)) {
  607. return test_fail("Failed to erase begin iterator on non-empty model\n");
  608. }
  609. sord_iter_free(iter);
  610. // Ensure only the other graph is left
  611. SordQuad quad;
  612. SordQuad pat = { 0, 0, 0, graph42 };
  613. for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) {
  614. sord_iter_get(iter, quad);
  615. if (!sord_quad_match(quad, pat)) {
  616. fprintf(stderr, "Graph removal via iteration failed\n");
  617. return finished(world, sord, EXIT_FAILURE);
  618. }
  619. }
  620. sord_iter_free(iter);
  621. // Load file into two separate graphs
  622. sord_free(sord);
  623. sord = sord_new(world, SORD_SPO, true);
  624. env = serd_env_new(&base);
  625. SordNode* graph1 = sord_new_uri(world, USTR("http://example.org/graph1"));
  626. SordNode* graph2 = sord_new_uri(world, USTR("http://example.org/graph2"));
  627. SerdReader* reader = sord_new_reader(sord, env, SERD_TURTLE, graph1);
  628. if ((st = serd_reader_read_string(reader, USTR("<s> <p> <o> .")))) {
  629. fprintf(stderr, "Failed to read string (%s)\n", serd_strerror(st));
  630. return finished(world, sord, EXIT_FAILURE);
  631. }
  632. serd_reader_free(reader);
  633. reader = sord_new_reader(sord, env, SERD_TURTLE, graph2);
  634. if ((st = serd_reader_read_string(reader, USTR("<s> <p> <o> .")))) {
  635. fprintf(stderr, "Failed to re-read string (%s)\n", serd_strerror(st));
  636. return finished(world, sord, EXIT_FAILURE);
  637. }
  638. serd_reader_free(reader);
  639. serd_env_free(env);
  640. // Ensure we only see triple once
  641. size_t n_triples = 0;
  642. for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) {
  643. fprintf(stderr, "%s %s %s %s\n",
  644. sord_node_get_string(sord_iter_get_node(iter, SORD_SUBJECT)),
  645. sord_node_get_string(sord_iter_get_node(iter, SORD_PREDICATE)),
  646. sord_node_get_string(sord_iter_get_node(iter, SORD_OBJECT)),
  647. sord_node_get_string(sord_iter_get_node(iter, SORD_GRAPH)));
  648. ++n_triples;
  649. }
  650. sord_iter_free(iter);
  651. if (n_triples != 1) {
  652. fprintf(stderr, "Found duplicate triple\n");
  653. return finished(world, sord, EXIT_FAILURE);
  654. }
  655. // Test SPO iteration on an SOP indexed store
  656. sord_free(sord);
  657. sord = sord_new(world, SORD_SOP, false);
  658. generate(world, sord, 1, graph42);
  659. for (iter = sord_begin(sord); !sord_iter_end(iter); sord_iter_next(iter)) {
  660. ++n_triples;
  661. }
  662. sord_iter_free(iter);
  663. return finished(world, sord, EXIT_SUCCESS);
  664. }