Add some tests for g_output_stream_writev() and its async variant

This commit is contained in:
Sebastian Dröge 2018-09-14 12:54:00 +03:00
parent e6f5a50cd7
commit 708aa8f4ee
2 changed files with 657 additions and 0 deletions

View File

@ -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 ();
}

View File

@ -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();