diff options
author | Sebastian Dröge <sebastian@centricular.com> | 2018-09-14 12:54:00 +0300 |
---|---|---|
committer | Sebastian Dröge <sebastian@centricular.com> | 2019-01-24 16:25:56 +0200 |
commit | 708aa8f4eee119a6dd542904ab962030a9337993 (patch) | |
tree | 1c7fb9dfc39482bfa286141c0c823116c0dc9361 | |
parent | e6f5a50cd77c27bb77a524c6c1239ecc88f8fda7 (diff) | |
download | glib-708aa8f4eee119a6dd542904ab962030a9337993.tar.gz |
Add some tests for g_output_stream_writev() and its async variant
-rw-r--r-- | gio/tests/file.c | 567 | ||||
-rw-r--r-- | gio/tests/memory-output-stream.c | 90 |
2 files changed, 657 insertions, 0 deletions
diff --git a/gio/tests/file.c b/gio/tests/file.c index d2f147419..affa0bce6 100644 --- a/gio/tests/file.c +++ b/gio/tests/file.c @@ -1162,6 +1162,562 @@ test_load_bytes_async (void) g_main_loop_unref (data.main_loop); } +static void +test_writev_helper (GOutputVector *vectors, + gsize n_vectors, + gboolean use_bytes_written, + const guint8 *expected_contents, + gsize expected_length) +{ + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GError *error = NULL; + gsize bytes_written = 0; + gboolean res; + guint8 *contents; + gsize length; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + res = g_output_stream_writev_all (ostream, vectors, n_vectors, use_bytes_written ? &bytes_written : NULL, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + if (use_bytes_written) + g_assert_cmpuint (bytes_written, ==, expected_length); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, expected_contents, expected_length); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev() on local file output streams works on a non-empty vector */ +static void +test_writev (void) +{ + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, buffer, sizeof buffer); +} + +/* Test that writev() on local file output streams works on a non-empty vector without returning bytes_written */ +static void +test_writev_no_bytes_written (void) +{ + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + test_writev_helper (vectors, G_N_ELEMENTS (vectors), FALSE, buffer, sizeof buffer); +} + +/* Test that writev() on local file output streams works on 0 vectors */ +static void +test_writev_no_vectors (void) +{ + test_writev_helper (NULL, 0, TRUE, NULL, 0); +} + +/* Test that writev() on local file output streams works on empty vectors */ +static void +test_writev_empty_vectors (void) +{ + GOutputVector vectors[3]; + + vectors[0].buffer = NULL; + vectors[0].size = 0; + vectors[1].buffer = NULL; + vectors[1].size = 0; + vectors[2].buffer = NULL; + vectors[2].size = 0; + + test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, NULL, 0); +} + +/* Test that writev() fails if the sum of sizes in the vector is too big */ +static void +test_writev_too_big_vectors (void) +{ + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GError *error = NULL; + gsize bytes_written = 0; + gboolean res; + guint8 *contents; + gsize length; + GOutputVector vectors[3]; + + vectors[0].buffer = (void*) 1; + vectors[0].size = G_MAXSIZE / 2; + + vectors[1].buffer = (void*) 1; + vectors[1].size = G_MAXSIZE / 2; + + vectors[2].buffer = (void*) 1; + vectors[2].size = G_MAXSIZE / 2; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + res = g_output_stream_writev_all (ostream, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert_cmpuint (bytes_written, ==, 0); + g_assert_false (res); + g_clear_error (&error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, NULL, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +typedef struct +{ + gsize bytes_written; + GOutputVector *vectors; + gsize n_vectors; + GError *error; + gboolean done; +} WritevAsyncData; + +static void +test_writev_async_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GOutputStream *ostream = G_OUTPUT_STREAM (object); + WritevAsyncData *data = user_data; + GError *error = NULL; + gsize bytes_written; + gboolean res; + + res = g_output_stream_writev_finish (ostream, result, &bytes_written, &error); + g_assert_true (res); + g_assert_no_error (error); + data->bytes_written += bytes_written; + + /* skip vectors that have been written in full */ + while (data->n_vectors > 0 && bytes_written >= data->vectors[0].size) + { + bytes_written -= data->vectors[0].size; + ++data->vectors; + --data->n_vectors; + } + /* skip partially written vector data */ + if (bytes_written > 0 && data->n_vectors > 0) + { + data->vectors[0].size -= bytes_written; + data->vectors[0].buffer = ((guint8 *) data->vectors[0].buffer) + bytes_written; + } + + if (data->n_vectors > 0) + g_output_stream_writev_async (ostream, data->vectors, data->n_vectors, 0, NULL, test_writev_async_cb, &data); +} + +/* Test that writev_async() on local file output streams works on a non-empty vector */ +static void +test_writev_async (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + data.vectors = vectors; + data.n_vectors = G_N_ELEMENTS (vectors); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_async (ostream, data.vectors, data.n_vectors, 0, NULL, test_writev_async_cb, &data); + + while (data.n_vectors > 0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, sizeof buffer); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, buffer, sizeof buffer); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +static void +test_writev_all_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GOutputStream *ostream = G_OUTPUT_STREAM (object); + WritevAsyncData *data = user_data; + + g_output_stream_writev_all_finish (ostream, result, &data->bytes_written, &data->error); + data->done = TRUE; +} + +/* Test that writev_async_all() on local file output streams works on a non-empty vector */ +static void +test_writev_async_all (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, sizeof buffer); + g_assert_no_error (data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + + g_assert_cmpmem (contents, length, buffer, sizeof buffer); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev_async_all() on local file output streams handles cancellation correctly */ +static void +test_writev_async_all_cancellation (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + GCancellable *cancellable; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + 5; + vectors[1].size = 12; + + vectors[2].buffer = buffer + 5 + 12; + vectors[2].size = 3; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + cancellable = g_cancellable_new (); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, cancellable, test_writev_all_cb, &data); + + g_cancellable_cancel (cancellable); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); + g_object_unref (cancellable); +} + +/* Test that writev_async_all() with empty vectors is handled correctly */ +static void +test_writev_async_all_empty_vectors (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = NULL; + vectors[0].size = 0; + + vectors[1].buffer = NULL; + vectors[1].size = 0; + + vectors[2].buffer = NULL; + vectors[2].size = 0; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_no_error (data.error); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev_async_all() with no vectors is handled correctly */ +static void +test_writev_async_all_no_vectors (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, NULL, 0, 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_no_error (data.error); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + +/* Test that writev_async_all() with too big vectors is handled correctly */ +static void +test_writev_async_all_too_big_vectors (void) +{ + WritevAsyncData data = { 0 }; + GFile *file; + GFileIOStream *iostream = NULL; + GOutputVector vectors[3]; + GOutputStream *ostream; + GError *error = NULL; + gboolean res; + guint8 *contents; + gsize length; + + vectors[0].buffer = (void*) 1; + vectors[0].size = G_MAXSIZE / 2; + + vectors[1].buffer = (void*) 1; + vectors[1].size = G_MAXSIZE / 2; + + vectors[2].buffer = (void*) 1; + vectors[2].size = G_MAXSIZE / 2; + + file = g_file_new_tmp ("g_file_writev_XXXXXX", + &iostream, NULL); + g_assert_nonnull (file); + g_assert_nonnull (iostream); + + ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); + + g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); + + while (!data.done) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.bytes_written, ==, 0); + g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_clear_error (&data.error); + + res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_object_unref (iostream); + + res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (length, ==, 0); + + g_free (contents); + + g_file_delete (file, NULL, NULL); + g_object_unref (file); +} + int main (int argc, char *argv[]) { @@ -1190,6 +1746,17 @@ main (int argc, char *argv[]) g_test_add_func ("/file/measure-async", test_measure_async); g_test_add_func ("/file/load-bytes", test_load_bytes); g_test_add_func ("/file/load-bytes-async", test_load_bytes_async); + g_test_add_func ("/file/writev", test_writev); + g_test_add_func ("/file/writev/no-bytes-written", test_writev_no_bytes_written); + g_test_add_func ("/file/writev/no-vectors", test_writev_no_vectors); + g_test_add_func ("/file/writev/empty-vectors", test_writev_empty_vectors); + g_test_add_func ("/file/writev/too-big-vectors", test_writev_too_big_vectors); + g_test_add_func ("/file/writev/async", test_writev_async); + g_test_add_func ("/file/writev/async_all", test_writev_async_all); + g_test_add_func ("/file/writev/async_all-empty-vectors", test_writev_async_all_empty_vectors); + g_test_add_func ("/file/writev/async_all-no-vectors", test_writev_async_all_no_vectors); + g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors); + g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation); return g_test_run (); } diff --git a/gio/tests/memory-output-stream.c b/gio/tests/memory-output-stream.c index 4c85993e4..72da6263d 100644 --- a/gio/tests/memory-output-stream.c +++ b/gio/tests/memory-output-stream.c @@ -300,6 +300,94 @@ test_write_bytes (void) g_bytes_unref (bytes2); } +/* Test that writev() works on #GMemoryOutputStream with a non-empty set of vectors. This + * covers the default writev() implementation around write(). */ +static void +test_writev (void) +{ + GOutputStream *mo; + GError *error = NULL; + gboolean res; + gsize bytes_written; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + guint8 *output_buffer; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + vectors[0].size; + vectors[1].size = 12; + + vectors[2].buffer = buffer + vectors[0].size + vectors[1].size; + vectors[2].size = 3; + + mo = (GOutputStream*) g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM, + "realloc-function", g_realloc, + "destroy-function", g_free, + NULL); + res = g_output_stream_writev_all (mo, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); + g_assert_no_error (error); + g_assert_true (res); + g_assert_cmpuint (bytes_written, ==, sizeof buffer); + + g_output_stream_close (mo, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpuint (g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mo)), ==, sizeof buffer); + output_buffer = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mo)); + g_assert_cmpmem (output_buffer, sizeof buffer, buffer, sizeof buffer); + + g_object_unref (mo); +} + +/* Test that writev_nonblocking() works on #GMemoryOutputStream with a non-empty set of vectors. This + * covers the default writev_nonblocking() implementation around write_nonblocking(). */ +static void +test_writev_nonblocking (void) +{ + GOutputStream *mo; + GError *error = NULL; + gboolean res; + gsize bytes_written; + GOutputVector vectors[3]; + const guint8 buffer[] = {1, 2, 3, 4, 5, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 2, 3}; + guint8 *output_buffer; + + vectors[0].buffer = buffer; + vectors[0].size = 5; + + vectors[1].buffer = buffer + vectors[0].size; + vectors[1].size = 12; + + vectors[2].buffer = buffer + vectors[0].size + vectors[1].size; + vectors[2].size = 3; + + mo = (GOutputStream*) g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM, + "realloc-function", g_realloc, + "destroy-function", g_free, + NULL); + res = g_pollable_output_stream_writev_nonblocking (G_POLLABLE_OUTPUT_STREAM (mo), + vectors, G_N_ELEMENTS (vectors), + &bytes_written, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (res, ==, G_POLLABLE_RETURN_OK); + g_assert_cmpuint (bytes_written, ==, sizeof buffer); + + g_output_stream_close (mo, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpuint (g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mo)), ==, sizeof buffer); + output_buffer = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (mo)); + g_assert_cmpmem (output_buffer, sizeof buffer, buffer, sizeof buffer); + + g_object_unref (mo); +} + static void test_steal_as_bytes (void) { @@ -350,6 +438,8 @@ main (int argc, g_test_add_func ("/memory-output-stream/get-data-size", test_data_size); g_test_add_func ("/memory-output-stream/properties", test_properties); g_test_add_func ("/memory-output-stream/write-bytes", test_write_bytes); + g_test_add_func ("/memory-output-stream/writev", test_writev); + g_test_add_func ("/memory-output-stream/writev_nonblocking", test_writev_nonblocking); g_test_add_func ("/memory-output-stream/steal_as_bytes", test_steal_as_bytes); return g_test_run(); |