From 677d85c7f3df3563e990f41964f6ae7d560567a0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 12 May 2018 13:28:47 +0200 Subject: Add model --- test/.clang-tidy | 1 + test/meson.build | 3 + test/run_test_suite.py | 29 ++ test/test_free_null.c | 4 + test/test_model.c | 900 +++++++++++++++++++++++++++++++++++++++++++++++++ test/test_sink.c | 151 +++++++++ test/test_statement.c | 91 +++++ 7 files changed, 1179 insertions(+) create mode 100644 test/test_model.c create mode 100644 test/test_sink.c create mode 100644 test/test_statement.c (limited to 'test') diff --git a/test/.clang-tidy b/test/.clang-tidy index b7dd752b..8e55b93e 100644 --- a/test/.clang-tidy +++ b/test/.clang-tidy @@ -10,6 +10,7 @@ Checks: > -clang-analyzer-nullability.NullabilityBase, -clang-analyzer-nullability.NullableDereferenced, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + -clang-analyzer-valist.Uninitialized, -cppcoreguidelines-avoid-non-const-global-variables, -hicpp-signed-bitwise, -llvmlibc-*, diff --git a/test/meson.build b/test/meson.build index 79ffc85d..84045225 100644 --- a/test/meson.build +++ b/test/meson.build @@ -6,12 +6,15 @@ unit_tests = [ 'cursor', 'env', 'free_null', + 'model', 'node', 'node_syntax', 'nodes', 'overflow', 'read_chunk', 'reader_writer', + 'sink', + 'statement', 'string', 'terse_write', 'uri', diff --git a/test/run_test_suite.py b/test/run_test_suite.py index d25fb3a1..05dc81ca 100755 --- a/test/run_test_suite.py +++ b/test/run_test_suite.py @@ -208,6 +208,23 @@ def _file_equals(patha, pathb): return _show_diff(fa.readlines(), fb.readlines(), patha, pathb) +def _file_lines_equal(patha, pathb, subst_from="", subst_to=""): + import io + + for path in (patha, pathb): + if not os.access(path, os.F_OK): + sys.stderr.write("error: missing file %s" % path) + return False + + la = sorted(set(io.open(patha, encoding="utf-8").readlines())) + lb = sorted(set(io.open(pathb, encoding="utf-8").readlines())) + if la != lb: + _show_diff(la, lb, patha, pathb) + return False + + return True + + def test_suite( manifest_path, base_uri, @@ -306,6 +323,18 @@ def test_suite( command_prefix, ) + # Run model test for positive test (must succeed) + out_filename = os.path.join(out_test_dir, test_name + ".model.out") + + with open(out_filename, "w") as stdout: + proc = subprocess.run([command[0]] + ['-m'] + command[1:], + check=True, + stdout=stdout) + + if proc.returncode == 0 and ((mf + 'result') in model[test]): + if not _file_lines_equal(check_path, out_filename): + results.n_failures += 1 + else: # Negative test with open(out_filename, "w") as stdout: with tempfile.TemporaryFile() as stderr: diff --git a/test/test_free_null.c b/test/test_free_null.c index a101379a..aee79f0e 100644 --- a/test/test_free_null.c +++ b/test/test_free_null.c @@ -33,6 +33,10 @@ main(void) serd_reader_free(NULL); serd_writer_free(NULL); serd_nodes_free(NULL); + serd_model_free(NULL); + serd_statement_free(NULL); + serd_iter_free(NULL); + serd_range_free(NULL); serd_cursor_free(NULL); return 0; diff --git a/test/test_model.c b/test/test_model.c new file mode 100644 index 00000000..cd1728e8 --- /dev/null +++ b/test/test_model.c @@ -0,0 +1,900 @@ +/* + Copyright 2011-2018 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#undef NDEBUG + +#include +#include +#include +#include +#include + +#include "serd/serd.h" + +#define WILDCARD_NODE NULL + +#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +#define RDF_FIRST NS_RDF "first" +#define RDF_REST NS_RDF "rest" +#define RDF_NIL NS_RDF "nil" + +#define N_OBJECTS_PER 2U + +typedef const SerdNode* Quad[4]; + +typedef struct { + Quad query; + int expected_num_results; +} QueryTest; + +static const SerdNode* +manage(SerdWorld* world, SerdNode* node) +{ + return serd_nodes_manage(serd_world_nodes(world), node); +} + +static const SerdNode* +uri(SerdWorld* world, const unsigned num) +{ + char str[] = "eg:0000000000"; + snprintf(str + 3, 11, "%03u", num); + return manage(world, serd_new_uri(SERD_STATIC_STRING(str))); +} + +static int +generate(SerdWorld* world, + SerdModel* model, + size_t n_quads, + const SerdNode* graph) +{ + SerdNodes* nodes = serd_world_nodes(world); + + for (unsigned i = 0; i < n_quads; ++i) { + unsigned num = (i * N_OBJECTS_PER) + 1U; + + const SerdNode* ids[2 + N_OBJECTS_PER]; + for (unsigned j = 0; j < 2 + N_OBJECTS_PER; ++j) { + ids[j] = uri(world, num++); + } + + for (unsigned j = 0; j < N_OBJECTS_PER; ++j) { + assert(!serd_model_add(model, ids[0], ids[1], ids[2 + j], graph)); + } + } + + // Add some literals + + // (98 4 "hello") and (98 4 "hello"^^<5>) + const SerdNode* hello = + manage(world, serd_new_string(SERD_STATIC_STRING("hello"))); + + const SerdNode* hello_gb = + manage(world, + serd_new_plain_literal(SERD_STATIC_STRING("hello"), + SERD_STATIC_STRING("en-gb"))); + + const SerdNode* hello_us = + manage(world, + serd_new_plain_literal(SERD_STATIC_STRING("hello"), + SERD_STATIC_STRING("en-us"))); + + const SerdNode* hello_t4 = serd_nodes_manage( + nodes, + serd_new_typed_literal(SERD_STATIC_STRING("hello"), + serd_node_string_view(uri(world, 4)))); + + const SerdNode* hello_t5 = serd_nodes_manage( + nodes, + serd_new_typed_literal(SERD_STATIC_STRING("hello"), + serd_node_string_view(uri(world, 5)))); + + assert(!serd_model_add(model, uri(world, 98), uri(world, 4), hello, graph)); + assert( + !serd_model_add(model, uri(world, 98), uri(world, 4), hello_t5, graph)); + + // (96 4 "hello"^^<4>) and (96 4 "hello"^^<5>) + assert( + !serd_model_add(model, uri(world, 96), uri(world, 4), hello_t4, graph)); + assert( + !serd_model_add(model, uri(world, 96), uri(world, 4), hello_t5, graph)); + + // (94 5 "hello") and (94 5 "hello"@en-gb) + assert(!serd_model_add(model, uri(world, 94), uri(world, 5), hello, graph)); + assert( + !serd_model_add(model, uri(world, 94), uri(world, 5), hello_gb, graph)); + + // (92 6 "hello"@en-us) and (92 6 "hello"@en-gb) + assert( + !serd_model_add(model, uri(world, 92), uri(world, 6), hello_us, graph)); + assert( + !serd_model_add(model, uri(world, 92), uri(world, 6), hello_gb, graph)); + + // (14 6 "bonjour"@fr) and (14 6 "salut"@fr) + const SerdNode* const bonjour = + manage(world, + serd_new_plain_literal(SERD_STATIC_STRING("bonjour"), + SERD_STATIC_STRING("fr"))); + + const SerdNode* const salut = + manage(world, + serd_new_plain_literal(SERD_STATIC_STRING("salut"), + SERD_STATIC_STRING("fr"))); + + assert(!serd_model_add(model, uri(world, 14), uri(world, 6), bonjour, graph)); + assert(!serd_model_add(model, uri(world, 14), uri(world, 6), salut, graph)); + + // Attempt to add duplicates + assert(serd_model_add(model, uri(world, 14), uri(world, 6), salut, graph)); + + // Add a blank node subject + const SerdNode* ablank = + manage(world, serd_new_blank(SERD_STATIC_STRING("ablank"))); + + assert(!serd_model_add(model, ablank, uri(world, 6), salut, graph)); + + // Add statement with URI object + assert(!serd_model_add(model, ablank, uri(world, 6), uri(world, 7), graph)); + + return EXIT_SUCCESS; +} + +static int +test_read(SerdWorld* world, + SerdModel* model, + const SerdNode* g, + const unsigned n_quads) +{ + SerdIter* iter = serd_model_begin(model); + const SerdStatement* prev = NULL; + for (; !serd_iter_equals(iter, serd_model_end(model)); serd_iter_next(iter)) { + const SerdStatement* statement = serd_iter_get(iter); + assert(statement); + assert(serd_statement_subject(statement)); + assert(serd_statement_predicate(statement)); + assert(serd_statement_object(statement)); + assert(!serd_statement_equals(statement, prev)); + assert(!serd_statement_equals(prev, statement)); + prev = statement; + } + + // Attempt to increment past end + assert(serd_iter_next(iter)); + serd_iter_free(iter); + + const SerdStringView s = SERD_STATIC_STRING("hello"); + + const SerdNode* plain_hello = manage(world, serd_new_string(s)); + + const SerdNode* type4_hello = manage( + world, serd_new_typed_literal(s, serd_node_string_view(uri(world, 4)))); + + const SerdNode* type5_hello = manage( + world, serd_new_typed_literal(s, serd_node_string_view(uri(world, 5)))); + + const SerdNode* gb_hello = + manage(world, serd_new_plain_literal(s, SERD_STATIC_STRING("en-gb"))); + + const SerdNode* us_hello = + manage(world, serd_new_plain_literal(s, SERD_STATIC_STRING("en-us"))); + +#define NUM_PATTERNS 18 + + QueryTest patterns[NUM_PATTERNS] = { + {{NULL, NULL, NULL}, (int)(n_quads * N_OBJECTS_PER) + 12}, + {{uri(world, 1), WILDCARD_NODE, WILDCARD_NODE}, 2}, + {{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), WILDCARD_NODE}, 2}, + {{WILDCARD_NODE, uri(world, 2), uri(world, 4)}, 1}, + {{WILDCARD_NODE, WILDCARD_NODE, uri(world, 4)}, 1}, + {{uri(world, 1), WILDCARD_NODE, WILDCARD_NODE}, 2}, + {{uri(world, 1), WILDCARD_NODE, uri(world, 4)}, 1}, + {{WILDCARD_NODE, uri(world, 2), WILDCARD_NODE}, 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}}; + + Quad match = {uri(world, 1), uri(world, 2), uri(world, 4), g}; + assert(serd_model_ask(model, match[0], match[1], match[2], match[3])); + + Quad nomatch = {uri(world, 1), uri(world, 2), uri(world, 9), g}; + assert( + !serd_model_ask(model, nomatch[0], nomatch[1], nomatch[2], nomatch[3])); + + assert(!serd_model_get(model, NULL, NULL, uri(world, 3), g)); + assert(!serd_model_get(model, uri(world, 1), uri(world, 99), NULL, g)); + + assert(serd_node_equals( + serd_model_get(model, uri(world, 1), uri(world, 2), NULL, g), + uri(world, 3))); + assert(serd_node_equals( + serd_model_get(model, uri(world, 1), NULL, uri(world, 3), g), + uri(world, 2))); + assert(serd_node_equals( + serd_model_get(model, NULL, uri(world, 2), uri(world, 3), g), + uri(world, 1))); + if (g) { + assert(serd_node_equals( + serd_model_get(model, uri(world, 1), uri(world, 2), uri(world, 3), NULL), + g)); + } + + for (unsigned i = 0; i < NUM_PATTERNS; ++i) { + QueryTest test = patterns[i]; + Quad pat = {test.query[0], test.query[1], test.query[2], g}; + + SerdRange* range = serd_model_range(model, pat[0], pat[1], pat[2], pat[3]); + int num_results = 0; + for (; !serd_range_empty(range); serd_range_next(range)) { + ++num_results; + + const SerdStatement* first = serd_range_front(range); + assert(first); + assert(serd_statement_matches(first, pat[0], pat[1], pat[2], pat[3])); + } + + serd_range_free(range); + + assert(num_results == test.expected_num_results); + } + + // Query blank node subject + + const SerdNode* ablank = + manage(world, serd_new_blank(SERD_STATIC_STRING("ablank"))); + + Quad pat = {ablank, 0, 0}; + int num_results = 0; + SerdRange* range = serd_model_range(model, pat[0], pat[1], pat[2], pat[3]); + for (; !serd_range_empty(range); serd_range_next(range)) { + ++num_results; + const SerdStatement* statement = serd_range_front(range); + assert(serd_statement_matches(statement, pat[0], pat[1], pat[2], pat[3])); + } + serd_range_free(range); + + assert(num_results == 2); + + // Test nested queries + const SerdNode* last_subject = 0; + range = serd_model_range(model, NULL, NULL, NULL, NULL); + for (; !serd_range_empty(range); serd_range_next(range)) { + const SerdStatement* statement = serd_range_front(range); + const SerdNode* subject = serd_statement_subject(statement); + if (subject == last_subject) { + continue; + } + + Quad subpat = {subject, 0, 0}; + SerdRange* subrange = + serd_model_range(model, subpat[0], subpat[1], subpat[2], subpat[3]); + const SerdStatement* substatement = serd_range_front(subrange); + uint64_t num_sub_results = 0; + assert(serd_statement_subject(substatement) == subject); + for (; !serd_range_empty(subrange); serd_range_next(subrange)) { + assert(serd_statement_matches(serd_range_front(subrange), + subpat[0], + subpat[1], + subpat[2], + subpat[3])); + ++num_sub_results; + } + serd_range_free(subrange); + assert(num_sub_results == N_OBJECTS_PER); + + uint64_t count = serd_model_count(model, subject, 0, 0, 0); + assert(count == num_sub_results); + + last_subject = subject; + } + serd_range_free(range); + + return 0; +} + +static SerdStatus +expected_error(void* handle, const SerdLogEntry* entry) +{ + (void)handle; + + fprintf(stderr, "expected: "); + vfprintf(stderr, entry->fmt, *entry->args); + return SERD_SUCCESS; +} + +static int +test_free_null(SerdWorld* world, const unsigned n_quads) +{ + (void)world; + (void)n_quads; + + serd_model_free(NULL); // Shouldn't crash + return 0; +} + +static int +test_get_world(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + assert(serd_model_world(model) == world); + serd_model_free(model); + return 0; +} + +static int +test_get_flags(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + const SerdModelFlags flags = SERD_INDEX_OPS | SERD_INDEX_GRAPHS; + SerdModel* model = serd_model_new(world, flags); + assert(serd_model_flags(model) == (SERD_INDEX_SPO | flags)); + serd_model_free(model); + return 0; +} + +static int +test_all_begin(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + SerdRange* all = serd_model_all(model); + SerdIter* begin = serd_model_find(model, NULL, NULL, NULL, NULL); + assert(serd_iter_equals(serd_range_begin(all), begin)); + assert(serd_iter_equals(serd_range_cbegin(all), begin)); + + serd_range_free(all); + serd_iter_free(begin); + serd_model_free(model); + return 0; +} + +static int +test_add_null(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + + serd_world_set_log_func(world, expected_error, NULL); + + assert(serd_model_add(model, 0, 0, 0, 0)); + assert(serd_model_add(model, uri(world, 1), 0, 0, 0)); + assert(serd_model_add(model, uri(world, 1), uri(world, 2), 0, 0)); + assert(serd_model_empty(model)); + + serd_model_free(model); + return 0; +} + +static int +test_add_with_iterator(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + + serd_world_set_log_func(world, expected_error, NULL); + assert( + !serd_model_add(model, uri(world, 1), uri(world, 2), uri(world, 3), 0)); + + // Add a statement with an active iterator + SerdIter* iter = serd_model_begin(model); + assert( + !serd_model_add(model, uri(world, 1), uri(world, 2), uri(world, 4), 0)); + + // Check that iterator has been invalidated + assert(!serd_iter_get(iter)); + assert(serd_iter_next(iter)); + + serd_iter_free(iter); + serd_model_free(model); + return 0; +} + +static int +test_erase_with_iterator(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + + serd_world_set_log_func(world, expected_error, NULL); + assert( + !serd_model_add(model, uri(world, 1), uri(world, 2), uri(world, 3), 0)); + assert( + !serd_model_add(model, uri(world, 4), uri(world, 5), uri(world, 6), 0)); + + // Erase a statement with an active iterator + SerdIter* iter1 = serd_model_begin(model); + SerdIter* iter2 = serd_model_begin(model); + assert(!serd_model_erase(model, iter1)); + + // Check that erased iterator points to the next statement + const SerdStatement* const s1 = serd_iter_get(iter1); + assert(s1); + assert( + serd_statement_matches(s1, uri(world, 4), uri(world, 5), uri(world, 6), 0)); + + // Check that other iterator has been invalidated + assert(!serd_iter_get(iter2)); + assert(serd_iter_next(iter2)); + + serd_iter_free(iter2); + serd_iter_free(iter1); + serd_model_free(model); + return 0; +} + +static int +test_add_erase(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO | SERD_INDEX_GRAPHS); + + // Add (s p "hello") + const SerdNode* s = uri(world, 1); + const SerdNode* p = uri(world, 2); + const SerdNode* hello = + manage(world, serd_new_string(SERD_STATIC_STRING("hello"))); + + assert(!serd_model_add(model, s, p, hello, 0)); + assert(serd_model_ask(model, s, p, hello, 0)); + + // Add (s p "hi") + const SerdNode* hi = manage(world, serd_new_string(SERD_STATIC_STRING("hi"))); + assert(!serd_model_add(model, s, p, hi, NULL)); + assert(serd_model_ask(model, s, p, hi, 0)); + + // Erase (s p "hi") + SerdIter* iter = serd_model_find(model, s, p, hi, NULL); + assert(!serd_model_erase(model, iter)); + assert(serd_model_size(model) == 1); + serd_iter_free(iter); + + // Check that erased statement can not be found + SerdRange* empty = serd_model_range(model, s, p, hi, NULL); + assert(serd_range_empty(empty)); + serd_range_free(empty); + + serd_model_free(model); + return 0; +} + +static int +test_erase_all(SerdWorld* world, const unsigned n_quads) +{ + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + generate(world, model, n_quads, NULL); + + SerdIter* iter = serd_model_begin(model); + while (!serd_iter_equals(iter, serd_model_end(model))) { + assert(!serd_model_erase(model, iter)); + } + + serd_iter_free(iter); + serd_model_free(model); + return 0; +} + +static int +test_copy(SerdWorld* world, const unsigned n_quads) +{ + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + generate(world, model, n_quads, NULL); + + SerdModel* copy = serd_model_copy(model); + assert(serd_model_equals(model, copy)); + + serd_model_free(model); + serd_model_free(copy); + return 0; +} + +static int +test_equals(SerdWorld* world, const unsigned n_quads) +{ + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + generate(world, model, n_quads, NULL); + serd_model_add( + model, uri(world, 0), uri(world, 1), uri(world, 2), uri(world, 3)); + + assert(serd_model_equals(NULL, NULL)); + assert(!serd_model_equals(NULL, model)); + assert(!serd_model_equals(model, NULL)); + + SerdModel* empty = serd_model_new(world, SERD_INDEX_SPO); + assert(!serd_model_equals(model, empty)); + + SerdModel* different = serd_model_new(world, SERD_INDEX_SPO); + generate(world, different, n_quads, NULL); + serd_model_add( + different, uri(world, 1), uri(world, 1), uri(world, 2), uri(world, 3)); + + assert(serd_model_size(model) == serd_model_size(different)); + assert(!serd_model_equals(model, different)); + + serd_model_free(model); + serd_model_free(empty); + serd_model_free(different); + return 0; +} + +static int +test_find_past_end(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + const SerdNode* s = uri(world, 1); + const SerdNode* p = uri(world, 2); + const SerdNode* o = uri(world, 3); + assert(!serd_model_add(model, s, p, o, 0)); + assert(serd_model_ask(model, s, p, o, 0)); + + const SerdNode* huge = uri(world, 999); + SerdRange* range = serd_model_range(model, huge, huge, huge, 0); + assert(serd_range_empty(range)); + + serd_range_free(range); + serd_model_free(model); + return 0; +} + +static int +test_range(SerdWorld* world, const unsigned n_quads) +{ + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + generate(world, model, n_quads, NULL); + + SerdRange* range1 = serd_model_all(model); + SerdRange* range2 = serd_model_all(model); + + assert(!serd_range_empty(range1)); + assert(serd_range_empty(NULL)); + + assert(!serd_range_equals(range1, NULL)); + assert(!serd_range_equals(NULL, range1)); + assert(serd_range_equals(range1, range2)); + + assert(serd_iter_equals(serd_range_begin(range1), serd_range_begin(range2))); + assert( + serd_iter_equals(serd_range_cbegin(range1), serd_range_cbegin(range2))); + assert(serd_iter_equals(serd_range_end(range1), serd_range_end(range2))); + assert(serd_iter_equals(serd_range_cend(range1), serd_range_cend(range2))); + + assert(!serd_range_next(range2)); + assert(!serd_range_equals(range1, range2)); + + serd_range_free(range2); + serd_range_free(range1); + serd_model_free(model); + + return 0; +} + +static int +test_iter_comparison(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + + assert(serd_iter_equals(serd_iter_copy(NULL), NULL)); + + serd_world_set_log_func(world, expected_error, NULL); + assert( + !serd_model_add(model, uri(world, 1), uri(world, 2), uri(world, 3), 0)); + + // Add a statement with an active iterator + SerdIter* iter1 = serd_model_begin(model); + SerdIter* iter2 = serd_model_begin(model); + assert(serd_iter_equals(iter1, iter2)); + + serd_iter_next(iter1); + assert(!serd_iter_equals(iter1, iter2)); + + const SerdIter* end = serd_model_end(model); + assert(serd_iter_equals(iter1, end)); + + serd_iter_free(iter2); + serd_iter_free(iter1); + serd_model_free(model); + return 0; +} + +static int +test_triple_index_read(SerdWorld* world, const unsigned n_quads) +{ + for (unsigned i = 0; i < 6; ++i) { + SerdModel* model = serd_model_new(world, (1u << i)); + generate(world, model, n_quads, 0); + assert(!test_read(world, model, 0, n_quads)); + serd_model_free(model); + } + + return 0; +} + +static int +test_quad_index_read(SerdWorld* world, const unsigned n_quads) +{ + for (unsigned i = 0; i < 6; ++i) { + SerdModel* model = serd_model_new(world, (1u << i) | SERD_INDEX_GRAPHS); + const SerdNode* graph = uri(world, 42); + generate(world, model, n_quads, graph); + assert(!test_read(world, model, graph, n_quads)); + serd_model_free(model); + } + + return 0; +} + +static int +test_remove_graph(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO | SERD_INDEX_GRAPHS); + + // Generate a couple of graphs + const SerdNode* graph42 = uri(world, 42); + const SerdNode* graph43 = uri(world, 43); + generate(world, model, 1, graph42); + generate(world, model, 1, graph43); + + // Remove one graph via range + SerdRange* range = serd_model_range(model, NULL, NULL, NULL, graph43); + SerdStatus st = serd_model_erase_range(model, range); + assert(!st); + serd_range_free(range); + + // Erase the first tuple (an element in the default graph) + SerdIter* iter = serd_model_begin(model); + assert(!serd_model_erase(model, iter)); + serd_iter_free(iter); + + // Ensure only the other graph is left + Quad pat = {0, 0, 0, graph42}; + for (iter = serd_model_begin(model); + !serd_iter_equals(iter, serd_model_end(model)); + serd_iter_next(iter)) { + const SerdStatement* const s = serd_iter_get(iter); + assert(s); + assert(serd_statement_matches(s, pat[0], pat[1], pat[2], pat[3])); + } + serd_iter_free(iter); + + serd_model_free(model); + return 0; +} + +static int +test_default_graph(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO | SERD_INDEX_GRAPHS); + const SerdNode* s = uri(world, 1); + const SerdNode* p = uri(world, 2); + const SerdNode* o = uri(world, 3); + const SerdNode* g1 = uri(world, 101); + const SerdNode* g2 = uri(world, 102); + + // Insert the same statement into two graphs + assert(!serd_model_add(model, s, p, o, g1)); + assert(!serd_model_add(model, s, p, o, g2)); + + // Ensure we only see statement once in the default graph + assert(serd_model_count(model, s, p, o, NULL) == 1); + + serd_model_free(model); + return 0; +} + +static int +test_write_bad_list(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO | SERD_INDEX_GRAPHS); + SerdNodes* nodes = serd_nodes_new(); + + const SerdNode* s = manage(world, serd_new_uri(SERD_STATIC_STRING("urn:s"))); + const SerdNode* p = manage(world, serd_new_uri(SERD_STATIC_STRING("urn:p"))); + const SerdNode* list1 = + manage(world, serd_new_blank(SERD_STATIC_STRING("l1"))); + const SerdNode* list2 = + manage(world, serd_new_blank(SERD_STATIC_STRING("l2"))); + const SerdNode* nofirst = + manage(world, serd_new_blank(SERD_STATIC_STRING("nof"))); + const SerdNode* norest = + manage(world, serd_new_blank(SERD_STATIC_STRING("nor"))); + const SerdNode* pfirst = + manage(world, serd_new_uri(SERD_STATIC_STRING(RDF_FIRST))); + const SerdNode* prest = + manage(world, serd_new_uri(SERD_STATIC_STRING(RDF_REST))); + const SerdNode* val1 = + manage(world, serd_new_string(SERD_STATIC_STRING("a"))); + const SerdNode* val2 = + manage(world, serd_new_string(SERD_STATIC_STRING("b"))); + + // List where second node has no rdf:first + serd_model_add(model, s, p, list1, NULL); + serd_model_add(model, list1, pfirst, val1, NULL); + serd_model_add(model, list1, prest, nofirst, NULL); + + // List where second node has no rdf:rest + serd_model_add(model, s, p, list2, NULL); + serd_model_add(model, list2, pfirst, val1, NULL); + serd_model_add(model, list2, prest, norest, NULL); + serd_model_add(model, norest, pfirst, val2, NULL); + + SerdBuffer buffer = {NULL, 0}; + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + SerdByteSink* out = serd_byte_sink_new_buffer(&buffer); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out); + + SerdRange* all = serd_model_all(model); + serd_range_serialise(all, serd_writer_sink(writer), 0); + serd_range_free(all); + + serd_writer_finish(writer); + const char* str = serd_buffer_sink_finish(&buffer); + const char* expected = "\n" + " (\n" + " \"a\"\n" + " ) , (\n" + " \"a\"\n" + " \"b\"\n" + " ) .\n"; + + assert(!strcmp(str, expected)); + + free(buffer.buf); + serd_writer_free(writer); + serd_byte_sink_free(out); + serd_model_free(model); + serd_env_free(env); + serd_nodes_free(nodes); + return 0; +} + +typedef struct { + size_t n_written; + size_t max_successes; +} FailingWriteFuncState; + +/// Write function that fails after a certain number of writes +static size_t +failing_write_func(const void* buf, size_t size, size_t nmemb, void* stream) +{ + (void)buf; + (void)size; + (void)nmemb; + + FailingWriteFuncState* state = (FailingWriteFuncState*)stream; + + return (++state->n_written > state->max_successes) ? 0 : nmemb; +} + +static int +test_write_error_in_list(SerdWorld* world, const unsigned n_quads) +{ + (void)n_quads; + + serd_world_set_log_func(world, expected_error, NULL); + + SerdModel* model = serd_model_new(world, SERD_INDEX_SPO); + SerdNodes* nodes = serd_nodes_new(); + + const SerdNode* s = manage(world, serd_new_uri(SERD_STATIC_STRING("urn:s"))); + const SerdNode* p = manage(world, serd_new_uri(SERD_STATIC_STRING("urn:p"))); + const SerdNode* l1 = manage(world, serd_new_blank(SERD_STATIC_STRING("l1"))); + + const SerdNode* one = manage(world, serd_new_integer(1, NULL)); + const SerdNode* l2 = manage(world, serd_new_blank(SERD_STATIC_STRING("l2"))); + const SerdNode* two = manage(world, serd_new_integer(2, NULL)); + + const SerdNode* rdf_first = + manage(world, serd_new_uri(SERD_STATIC_STRING(RDF_FIRST))); + const SerdNode* rdf_rest = + manage(world, serd_new_uri(SERD_STATIC_STRING(RDF_REST))); + const SerdNode* rdf_nil = + manage(world, serd_new_uri(SERD_STATIC_STRING(RDF_NIL))); + + serd_model_add(model, s, p, l1, NULL); + serd_model_add(model, l1, rdf_first, one, NULL); + serd_model_add(model, l1, rdf_rest, l2, NULL); + serd_model_add(model, l2, rdf_first, two, NULL); + serd_model_add(model, l2, rdf_rest, rdf_nil, NULL); + + SerdEnv* env = serd_env_new(SERD_EMPTY_STRING()); + + for (size_t max_successes = 0; max_successes < 21; ++max_successes) { + FailingWriteFuncState state = {0, max_successes}; + SerdByteSink* out = + serd_byte_sink_new_function(failing_write_func, &state, 1); + SerdWriter* writer = serd_writer_new(world, SERD_TURTLE, 0, env, out); + + const SerdSink* const sink = serd_writer_sink(writer); + SerdRange* const all = serd_model_all(model); + const SerdStatus st = serd_range_serialise(all, sink, 0); + serd_range_free(all); + + assert(st == SERD_ERR_BAD_WRITE); + + serd_writer_free(writer); + serd_byte_sink_free(out); + } + + serd_env_free(env); + serd_model_free(model); + serd_nodes_free(nodes); + return 0; +} + +int +main(void) +{ + static const unsigned n_quads = 300; + + serd_model_free(NULL); // Shouldn't crash + + typedef int (*TestFunc)(SerdWorld*, unsigned); + + const TestFunc tests[] = {test_free_null, + test_get_world, + test_get_flags, + test_all_begin, + test_add_null, + test_add_with_iterator, + test_erase_with_iterator, + test_add_erase, + test_erase_all, + test_copy, + test_equals, + test_find_past_end, + test_range, + test_iter_comparison, + test_triple_index_read, + test_quad_index_read, + test_remove_graph, + test_default_graph, + test_write_bad_list, + test_write_error_in_list, + NULL}; + + SerdWorld* world = serd_world_new(); + int ret = 0; + + for (const TestFunc* t = tests; *t; ++t) { + serd_world_set_log_func(world, NULL, NULL); + ret += (*t)(world, n_quads); + } + + serd_world_free(world); + return ret; +} diff --git a/test/test_sink.c b/test/test_sink.c new file mode 100644 index 00000000..b1eb8df8 --- /dev/null +++ b/test/test_sink.c @@ -0,0 +1,151 @@ +/* + Copyright 2019-2020 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#undef NDEBUG + +#include "serd/serd.h" + +#include +#include + +#define NS_EG "http://example.org/" + +typedef struct { + const SerdNode* last_base; + const SerdNode* last_name; + const SerdNode* last_namespace; + const SerdNode* last_end; + const SerdStatement* last_statement; + SerdStatus return_status; +} State; + +static SerdStatus +on_base(void* handle, const SerdNode* uri) +{ + State* state = (State*)handle; + + state->last_base = uri; + return state->return_status; +} + +static SerdStatus +on_prefix(void* handle, const SerdNode* name, const SerdNode* uri) +{ + State* state = (State*)handle; + + state->last_name = name; + state->last_namespace = uri; + return state->return_status; +} + +static SerdStatus +on_statement(void* handle, + SerdStatementFlags flags, + const SerdStatement* statement) +{ + (void)flags; + + State* state = (State*)handle; + + state->last_statement = statement; + return state->return_status; +} + +static SerdStatus +on_end(void* handle, const SerdNode* node) +{ + State* state = (State*)handle; + + state->last_end = node; + return state->return_status; +} + +static SerdStatus +on_event(void* handle, const SerdEvent* event) +{ + switch (event->type) { + case SERD_BASE: + return on_base(handle, event->base.uri); + case SERD_PREFIX: + return on_prefix(handle, event->prefix.name, event->prefix.uri); + case SERD_STATEMENT: + return on_statement( + handle, event->statement.flags, event->statement.statement); + case SERD_END: + return on_end(handle, event->end.node); + } + + return SERD_SUCCESS; +} + +int +main(void) +{ + SerdNodes* const nodes = serd_nodes_new(); + + const SerdNode* base = + serd_nodes_manage(nodes, serd_new_uri(SERD_STATIC_STRING(NS_EG))); + + const SerdNode* name = + serd_nodes_manage(nodes, serd_new_string(SERD_STATIC_STRING("eg"))); + + const SerdNode* uri = + serd_nodes_manage(nodes, serd_new_uri(SERD_STATIC_STRING(NS_EG "uri"))); + + const SerdNode* blank = + serd_nodes_manage(nodes, serd_new_blank(SERD_STATIC_STRING("b1"))); + + SerdEnv* env = serd_env_new(serd_node_string_view(base)); + + SerdStatement* const statement = + serd_statement_new(base, uri, blank, NULL, NULL); + + State state = {0, 0, 0, 0, 0, SERD_SUCCESS}; + + // Call functions on a sink with no functions set + + SerdSink* null_sink = serd_sink_new(&state, NULL, NULL); + assert(!serd_sink_write_base(null_sink, base)); + assert(!serd_sink_write_prefix(null_sink, name, uri)); + assert(!serd_sink_write_statement(null_sink, 0, statement)); + assert(!serd_sink_write(null_sink, 0, base, uri, blank, NULL)); + assert(!serd_sink_write_end(null_sink, blank)); + serd_sink_free(null_sink); + + // Try again with a sink that has the event handler set + + SerdSink* sink = serd_sink_new(&state, on_event, NULL); + + assert(!serd_sink_write_base(sink, base)); + assert(serd_node_equals(state.last_base, base)); + + assert(!serd_sink_write_prefix(sink, name, uri)); + assert(serd_node_equals(state.last_name, name)); + assert(serd_node_equals(state.last_namespace, uri)); + + assert(!serd_sink_write_statement(sink, 0, statement)); + assert(serd_statement_equals(state.last_statement, statement)); + + assert(!serd_sink_write_end(sink, blank)); + assert(serd_node_equals(state.last_end, blank)); + + serd_sink_free(sink); + serd_statement_free(statement); + serd_env_free(env); + serd_nodes_free(nodes); + + return 0; +} diff --git a/test/test_statement.c b/test/test_statement.c new file mode 100644 index 00000000..8e1de1a2 --- /dev/null +++ b/test/test_statement.c @@ -0,0 +1,91 @@ +/* + Copyright 2011-2020 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#undef NDEBUG + +#include "serd/serd.h" + +#include +#include + +#define NS_EG "http://example.org/" + +int +main(void) +{ + SerdNodes* const nodes = serd_nodes_new(); + + const SerdNode* const f = + serd_nodes_manage(nodes, serd_new_string(SERD_STATIC_STRING("file"))); + + const SerdNode* const s = + serd_nodes_manage(nodes, serd_new_uri(SERD_STATIC_STRING(NS_EG "s"))); + + const SerdNode* const p = + + serd_nodes_manage(nodes, serd_new_uri(SERD_STATIC_STRING(NS_EG "p"))); + + const SerdNode* const o = + serd_nodes_manage(nodes, serd_new_uri(SERD_STATIC_STRING(NS_EG "o"))); + + const SerdNode* const g = + serd_nodes_manage(nodes, serd_new_uri(SERD_STATIC_STRING(NS_EG "g"))); + + assert(!serd_statement_copy(NULL)); + + SerdCursor* const cursor = serd_cursor_new(f, 1, 1); + SerdStatement* const statement = serd_statement_new(s, p, o, g, cursor); + assert(serd_statement_equals(statement, statement)); + assert(!serd_statement_equals(statement, NULL)); + assert(!serd_statement_equals(NULL, statement)); + assert(serd_statement_subject(statement) == s); + assert(serd_statement_predicate(statement) == p); + assert(serd_statement_object(statement) == o); + assert(serd_statement_graph(statement) == g); + assert(serd_statement_cursor(statement) != cursor); + assert(serd_cursor_equals(serd_statement_cursor(statement), cursor)); + assert(serd_statement_matches(statement, s, p, o, g)); + assert(serd_statement_matches(statement, NULL, p, o, g)); + assert(serd_statement_matches(statement, s, NULL, o, g)); + assert(serd_statement_matches(statement, s, p, NULL, g)); + assert(serd_statement_matches(statement, s, p, o, NULL)); + assert(!serd_statement_matches(statement, o, NULL, NULL, NULL)); + assert(!serd_statement_matches(statement, NULL, o, NULL, NULL)); + assert(!serd_statement_matches(statement, NULL, NULL, s, NULL)); + assert(!serd_statement_matches(statement, NULL, NULL, NULL, s)); + + SerdStatement* const diff_s = serd_statement_new(o, p, o, g, cursor); + assert(!serd_statement_equals(statement, diff_s)); + serd_statement_free(diff_s); + + SerdStatement* const diff_p = serd_statement_new(s, o, o, g, cursor); + assert(!serd_statement_equals(statement, diff_p)); + serd_statement_free(diff_p); + + SerdStatement* const diff_o = serd_statement_new(s, p, s, g, cursor); + assert(!serd_statement_equals(statement, diff_o)); + serd_statement_free(diff_o); + + SerdStatement* const diff_g = serd_statement_new(s, p, o, s, cursor); + assert(!serd_statement_equals(statement, diff_g)); + serd_statement_free(diff_g); + + serd_statement_free(statement); + serd_cursor_free(cursor); + serd_nodes_free(nodes); + + return 0; +} -- cgit v1.2.1