aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2023-04-09 11:54:11 -0400
committerDavid Robillard <d@drobilla.net>2023-04-16 22:17:56 -0400
commita6cd7dd91d93015ec118286b868c3fd43133f3ac (patch)
treeb10d88ca099c0d0986c2e48ccca62023a0ac1614 /test
parent1d8cdcef2cc4aaf2e45cfab01a390d6eab56d525 (diff)
downloadserd-a6cd7dd91d93015ec118286b868c3fd43133f3ac.tar.gz
serd-a6cd7dd91d93015ec118286b868c3fd43133f3ac.tar.bz2
serd-a6cd7dd91d93015ec118286b868c3fd43133f3ac.zip
Gracefully handle errors when writing text
Diffstat (limited to 'test')
-rw-r--r--test/test_reader_writer.c97
-rw-r--r--test/test_string.c2
-rw-r--r--test/test_writer.c66
3 files changed, 164 insertions, 1 deletions
diff --git a/test/test_reader_writer.c b/test/test_reader_writer.c
index 32367bf7..83ee7b2f 100644
--- a/test/test_reader_writer.c
+++ b/test/test_reader_writer.c
@@ -10,6 +10,7 @@
#endif
#include <assert.h>
+#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -19,6 +20,11 @@
#define USTR(s) ((const uint8_t*)(s))
typedef struct {
+ size_t n_written;
+ size_t error_offset;
+} ErrorContext;
+
+typedef struct {
int n_base;
int n_prefix;
int n_statement;
@@ -26,6 +32,29 @@ typedef struct {
const SerdNode* graph;
} ReaderTest;
+static const char* const doc_string =
+ "@base <http://drobilla.net/> .\n"
+ "@prefix eg: <http://example.org/> .\n"
+ "eg:g {\n"
+ "<http://example.com/s> eg:p \"l\\n\\\"it\" ,\n"
+ " \"\"\"long\"\"\" ,\n"
+ " \"lang\"@en ;\n"
+ " eg:p <http://example.com/o> .\n"
+ "}\n"
+ "eg:s\n"
+ " <http://example.org/p> [\n"
+ " eg:p 3.0 ,\n"
+ " 4 ,\n"
+ " \"lit\" ,\n"
+ " _:n42 ,\n"
+ " \"t\"^^eg:T\n"
+ " ] ;\n"
+ " eg:p () ;\n"
+ " eg:p\\!q (\"s\" 1 2.0 \"l\"@en eg:o) .\n"
+ "[] eg:p eg:o .\n"
+ "[ eg:p eg:o ] eg:q eg:r .\n"
+ "( eg:o ) eg:t eg:u .\n";
+
static SerdStatus
test_base_sink(void* handle, const SerdNode* uri)
{
@@ -250,6 +279,73 @@ test_read_string(void)
serd_reader_free(reader);
}
+static size_t
+faulty_sink(const void* const buf, const size_t len, void* const stream)
+{
+ (void)buf;
+ (void)len;
+
+ ErrorContext* const ctx = (ErrorContext*)stream;
+ const size_t new_n_written = ctx->n_written + len;
+ if (new_n_written >= ctx->error_offset) {
+ errno = EINVAL;
+ return 0U;
+ }
+
+ ctx->n_written += len;
+ errno = 0;
+ return len;
+}
+
+static SerdStatus
+quiet_error_sink(void* const handle, const SerdError* const e)
+{
+ (void)handle;
+ (void)e;
+ return SERD_SUCCESS;
+}
+
+static void
+test_write_errors(void)
+{
+ ErrorContext ctx = {0U, 0U};
+ const SerdStyle style = (SerdStyle)(SERD_STYLE_STRICT | SERD_STYLE_CURIED);
+
+ const size_t max_offsets[] = {0, 386, 1911, 2003, 386};
+
+ // Test errors at different offsets to hit different code paths
+ for (unsigned s = 1; s <= (unsigned)SERD_TRIG; ++s) {
+ const SerdSyntax syntax = (SerdSyntax)s;
+ for (size_t o = 0; o < max_offsets[s]; ++o) {
+ ctx.n_written = 0;
+ ctx.error_offset = o;
+
+ SerdEnv* const env = serd_env_new(NULL);
+ SerdWriter* const writer =
+ serd_writer_new(syntax, style, env, NULL, faulty_sink, &ctx);
+
+ SerdReader* const reader =
+ serd_reader_new(SERD_TRIG,
+ writer,
+ NULL,
+ (SerdBaseSink)serd_writer_set_base_uri,
+ (SerdPrefixSink)serd_writer_set_prefix,
+ (SerdStatementSink)serd_writer_write_statement,
+ (SerdEndSink)serd_writer_end_anon);
+
+ serd_reader_set_error_sink(reader, quiet_error_sink, NULL);
+ serd_writer_set_error_sink(writer, quiet_error_sink, NULL);
+
+ const SerdStatus st = serd_reader_read_string(reader, USTR(doc_string));
+ assert(st == SERD_ERR_BAD_WRITE);
+
+ serd_reader_free(reader);
+ serd_writer_free(writer);
+ serd_env_free(env);
+ }
+ }
+}
+
static void
test_writer(const char* const path)
{
@@ -470,6 +566,7 @@ main(void)
test_read_chunks(path);
test_read_string();
+ test_write_errors();
test_writer(path);
test_reader(path);
diff --git a/test/test_string.c b/test/test_string.c
index 86ccf982..6767e5ae 100644
--- a/test/test_string.c
+++ b/test/test_string.c
@@ -31,7 +31,7 @@ test_strerror(void)
{
const uint8_t* msg = serd_strerror(SERD_SUCCESS);
assert(!strcmp((const char*)msg, "Success"));
- for (int i = SERD_FAILURE; i <= SERD_ERR_BAD_WRITE; ++i) {
+ for (int i = SERD_FAILURE; i <= SERD_ERR_BAD_TEXT; ++i) {
msg = serd_strerror((SerdStatus)i);
assert(strcmp((const char*)msg, "Success"));
}
diff --git a/test/test_writer.c b/test/test_writer.c
index 356dbfcb..d236d7e6 100644
--- a/test/test_writer.c
+++ b/test/test_writer.c
@@ -84,16 +84,82 @@ test_writer_cleanup(void)
// Finish writing without terminating nodes
assert(!(st = serd_writer_finish(writer)));
+ // Set the base to an empty URI
+ assert(!(st = serd_writer_set_base_uri(writer, NULL)));
+
// Free (which could leak if the writer doesn't clean up the stack properly)
serd_writer_free(writer);
serd_env_free(env);
}
+static void
+test_strict_write(void)
+{
+ const char* const path = "serd_strict_write_test.ttl";
+ FILE* const fd = fopen(path, "wb");
+ assert(fd);
+
+ SerdEnv* const env = serd_env_new(NULL);
+ SerdWriter* const writer = serd_writer_new(
+ SERD_TURTLE, (SerdStyle)SERD_STYLE_STRICT, env, NULL, null_sink, fd);
+
+ assert(writer);
+
+ const uint8_t bad_str[] = {0xFF, 0x90, 'h', 'i', 0};
+
+ SerdNode s = serd_node_from_string(SERD_URI, USTR("http://example.org/s"));
+ SerdNode p = serd_node_from_string(SERD_URI, USTR("http://example.org/p"));
+
+ SerdNode bad_lit = serd_node_from_string(SERD_LITERAL, bad_str);
+ SerdNode bad_uri = serd_node_from_string(SERD_URI, bad_str);
+
+ assert(serd_writer_write_statement(
+ writer, 0, NULL, &s, &p, &bad_lit, NULL, NULL) == SERD_ERR_BAD_TEXT);
+
+ assert(serd_writer_write_statement(
+ writer, 0, NULL, &s, &p, &bad_uri, NULL, NULL) == SERD_ERR_BAD_TEXT);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+ fclose(fd);
+}
+
+// Produce a write error without setting errno
+static size_t
+error_sink(const void* const buf, const size_t len, void* const stream)
+{
+ (void)buf;
+ (void)len;
+ (void)stream;
+ return 0U;
+}
+
+static void
+test_write_error(void)
+{
+ SerdEnv* const env = serd_env_new(NULL);
+ SerdWriter* writer = NULL;
+ SerdStatus st = SERD_SUCCESS;
+
+ SerdNode u = serd_node_from_string(SERD_URI, USTR("http://example.com/u"));
+
+ writer =
+ serd_writer_new(SERD_TURTLE, (SerdStyle)0, env, NULL, error_sink, NULL);
+ assert(writer);
+ st = serd_writer_write_statement(writer, 0U, NULL, &u, &u, &u, NULL, NULL);
+ assert(st == SERD_ERR_BAD_WRITE);
+ serd_writer_free(writer);
+
+ serd_env_free(env);
+}
+
int
main(void)
{
test_write_long_literal();
test_writer_cleanup();
+ test_strict_write();
+ test_write_error();
return 0;
}