diff options
author | Benjamin Otte <otte@gnome.org> | 2009-07-01 19:03:19 +0200 |
---|---|---|
committer | Benjamin Otte <otte@gnome.org> | 2009-07-01 19:03:19 +0200 |
commit | 2262d76b33094304ece0d0d9cd5920682599a49b (patch) | |
tree | c57f89a29fb4d52d81e96521a44b8e44c0a9b9e1 /tests | |
parent | 65cc5d895ae125b09f2403761f434fd78ef05af7 (diff) |
Move gio tests from gio/tests/ to tests/gio/
This avoids getting tests built every time when working on libgio and
running make in the gio/ directory.
Diffstat (limited to 'tests')
27 files changed, 6482 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index da1397643..162fc4c09 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,6 @@ include $(top_srcdir)/Makefile.decl -SUBDIRS=gobject refcount +SUBDIRS=gobject refcount gio AM_CPPFLAGS = \ -I$(top_srcdir) \ diff --git a/tests/gio/.gitignore b/tests/gio/.gitignore new file mode 100644 index 000000000..b815e877f --- /dev/null +++ b/tests/gio/.gitignore @@ -0,0 +1,24 @@ +buffered-input-stream +desktop-app-info +g-icon +simple-async-result +unix-streams +data-input-stream +data-output-stream +g-file +g-file-info +live-g-file +memory-input-stream +memory-output-stream +filter-streams +sleepy-stream +resolver +connectable +echo-server +httpd +readwrite +send-data +socket-client +socket-server +srvtarget +contexts diff --git a/tests/gio/Makefile.am b/tests/gio/Makefile.am new file mode 100644 index 000000000..8b5d30bf0 --- /dev/null +++ b/tests/gio/Makefile.am @@ -0,0 +1,119 @@ +include $(top_srcdir)/Makefile.decl + +INCLUDES = \ + -g \ + -I$(top_srcdir) \ + -I$(top_srcdir)/glib \ + -I$(top_srcdir)/gmodule \ + -I$(top_srcdir)/gobject \ + -I$(top_srcdir)/gio \ + $(GLIB_DEBUG_FLAGS) \ + -DSRCDIR=\""$(srcdir)"\" + +noinst_PROGRAMS = $(TEST_PROGS) $(SAMPLE_PROGS) +progs_ldadd = \ + $(top_builddir)/glib/libglib-2.0.la \ + $(top_builddir)/gobject/libgobject-2.0.la \ + $(top_builddir)/gio/libgio-2.0.la + + +TEST_PROGS += \ + memory-input-stream \ + memory-output-stream \ + readwrite \ + g-file \ + g-file-info \ + data-input-stream \ + data-output-stream \ + g-icon \ + buffered-input-stream \ + sleepy-stream \ + filter-streams \ + simple-async-result \ + srvtarget \ + contexts + +SAMPLE_PROGS = resolver socket-server socket-client echo-server httpd send-data + +if OS_UNIX +TEST_PROGS += live-g-file unix-streams desktop-app-info +endif + +memory_input_stream_SOURCES = memory-input-stream.c +memory_input_stream_LDADD = $(progs_ldadd) + +memory_output_stream_SOURCES = memory-output-stream.c +memory_output_stream_LDADD = $(progs_ldadd) + +g_file_SOURCES = g-file.c +g_file_LDADD = $(progs_ldadd) + +readwrite_SOURCES = readwrite.c +readwrite_LDADD = $(progs_ldadd) + +g_file_info_SOURCES = g-file-info.c +g_file_info_LDADD = $(progs_ldadd) + +data_input_stream_SOURCES = data-input-stream.c +data_input_stream_LDADD = $(progs_ldadd) + +data_output_stream_SOURCES = data-output-stream.c +data_output_stream_LDADD = $(progs_ldadd) + +g_icon_SOURCES = g-icon.c +g_icon_LDADD = $(progs_ldadd) + +buffered_input_stream_SOURCES = buffered-input-stream.c +buffered_input_stream_LDADD = $(progs_ldadd) + +live_g_file_SOURCES = live-g-file.c +live_g_file_LDADD = $(progs_ldadd) + +desktop_app_info_SOURCES = desktop-app-info.c +desktop_app_info_LDADD = $(progs_ldadd) + +unix_streams_SOURCES = unix-streams.c +unix_streams_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +simple_async_result_SOURCES = simple-async-result.c +simple_async_result_LDADD = $(progs_ldadd) + +sleepy_stream_SOURCES = sleepy-stream.c +sleepy_stream_LDADD = $(progs_ldadd) + +filter_streams_SOURCES = filter-streams.c +filter_streams_LDADD = $(progs_ldadd) + +resolver_SOURCES = resolver.c +resolver_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +socket_server_SOURCES = socket-server.c +socket_server_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +socket_client_SOURCES = socket-client.c +socket_client_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +echo_server_SOURCES = echo-server.c +echo_server_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +httpd_SOURCES = httpd.c +httpd_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +send_data_SOURCES = send-data.c +send_data_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +srvtarget_SOURCES = srvtarget.c +srvtarget_LDADD = $(progs_ldadd) + +contexts_SOURCES = contexts.c +contexts_LDADD = $(progs_ldadd) \ + $(top_builddir)/gthread/libgthread-2.0.la + +DISTCLEAN_FILES = applications/mimeinfo.cache diff --git a/tests/gio/buffered-input-stream.c b/tests/gio/buffered-input-stream.c new file mode 100644 index 000000000..ceb38b9fb --- /dev/null +++ b/tests/gio/buffered-input-stream.c @@ -0,0 +1,60 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc. + * Authors: Matthias Clasen <mclasen@redhat.com> + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +static void +test_read_byte (void) +{ + GInputStream *base; + GInputStream *in; + + g_test_bug ("562393"); + + base = g_memory_input_stream_new_from_data ("abcdefghijk", -1, NULL); + in = g_buffered_input_stream_new (base); + + g_assert_cmpint (g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, NULL), ==, 'a'); + g_assert_cmpint (g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, NULL), ==, 'b'); + g_assert_cmpint (g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, NULL), ==, 'c'); + + g_assert_cmpint (g_input_stream_skip (in, 3, NULL, NULL), ==, 3); + + g_assert_cmpint (g_buffered_input_stream_read_byte (G_BUFFERED_INPUT_STREAM (in), NULL, NULL), ==, 'g'); +} + + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/"); + + g_test_add_func ("/buffered-input-stream/read-byte", test_read_byte); + + return g_test_run(); +} diff --git a/tests/gio/contexts.c b/tests/gio/contexts.c new file mode 100644 index 000000000..3fe16cb1f --- /dev/null +++ b/tests/gio/contexts.c @@ -0,0 +1,190 @@ +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +#define TEST_FILE (SRCDIR "/Makefile.am") +char *test_file_buffer; +gsize test_file_size; +static char async_read_buffer[8192]; + +static void +read_data (GObject *source, GAsyncResult *result, gpointer loop) +{ + GInputStream *in = G_INPUT_STREAM (source); + GError *error = NULL; + gssize nread; + + nread = g_input_stream_read_finish (in, result, &error); + g_assert_no_error (error); + + g_assert_cmpint (nread, >, 0); + g_assert_cmpint (nread, <=, MIN(sizeof (async_read_buffer), test_file_size)); + g_assert (memcmp (async_read_buffer, test_file_buffer, nread) == 0); + + g_main_loop_quit (loop); +} + +static void +opened_for_read (GObject *source, GAsyncResult *result, gpointer loop) +{ + GFile *file = G_FILE (source); + GFileInputStream *in; + GError *error = NULL; + + in = g_file_read_finish (file, result, &error); + g_assert_no_error (error); + + memset (async_read_buffer, 0, sizeof (async_read_buffer)); + g_input_stream_read_async (G_INPUT_STREAM (in), + async_read_buffer, sizeof (async_read_buffer), + G_PRIORITY_DEFAULT, NULL, + read_data, loop); +} + +/* Test 1: Async I/O started in a thread with a thread-default context + * will stick to that thread, and will complete even if the default + * main loop is blocked. (NB: the last part would not be true if we + * were testing GFileMonitor!) + */ + +static gboolean idle_start_test1_thread (gpointer loop); +static gpointer test1_thread (gpointer user_data); + +static GCond *test1_cond; +static GMutex *test1_mutex; + +static void +test_thread_independence (void) +{ + GMainLoop *loop; + + test1_cond = g_cond_new (); + test1_mutex = g_mutex_new (); + + loop = g_main_loop_new (NULL, FALSE); + g_idle_add (idle_start_test1_thread, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_mutex_free (test1_mutex); + g_cond_free (test1_cond); +} + +static gboolean +idle_start_test1_thread (gpointer loop) +{ + GTimeVal time; + GThread *thread; + gboolean io_completed; + + g_mutex_lock (test1_mutex); + thread = g_thread_create (test1_thread, NULL, TRUE, NULL); + + g_get_current_time (&time); + time.tv_sec += 2; + io_completed = g_cond_timed_wait (test1_cond, test1_mutex, &time); + g_assert (io_completed); + g_thread_join (thread); + + g_mutex_unlock (test1_mutex); + g_main_loop_quit (loop); + return FALSE; +} + +static gpointer +test1_thread (gpointer user_data) +{ + GMainContext *context; + GMainLoop *loop; + GFile *file; + + /* Wait for main thread to be waiting on test1_cond */ + g_mutex_lock (test1_mutex); + g_mutex_unlock (test1_mutex); + + context = g_main_context_new (); + g_assert (g_main_context_get_thread_default () == NULL); + g_main_context_push_thread_default (context); + g_assert (g_main_context_get_thread_default () == context); + + file = g_file_new_for_path (TEST_FILE); + g_assert (g_file_supports_thread_contexts (file)); + + loop = g_main_loop_new (context, FALSE); + g_file_read_async (file, G_PRIORITY_DEFAULT, NULL, + opened_for_read, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_cond_signal (test1_cond); + return NULL; +} + +/* Test 2: If we push a thread-default context in the main thread, we + * can run async ops in that context without running the default + * context. + */ + +static gboolean test2_fail (gpointer user_data); + +static void +test_context_independence (void) +{ + GMainContext *context; + GMainLoop *loop; + GFile *file; + guint default_timeout; + GSource *thread_default_timeout; + + context = g_main_context_new (); + g_assert (g_main_context_get_thread_default () == NULL); + g_main_context_push_thread_default (context); + g_assert (g_main_context_get_thread_default () == context); + + file = g_file_new_for_path (TEST_FILE); + g_assert (g_file_supports_thread_contexts (file)); + + /* Add a timeout to the main loop, to fail immediately if it gets run */ + default_timeout = g_timeout_add_full (G_PRIORITY_HIGH, 0, + test2_fail, NULL, NULL); + /* Add a timeout to the alternate loop, to fail if the I/O *doesn't* run */ + thread_default_timeout = g_timeout_source_new_seconds (2); + g_source_set_callback (thread_default_timeout, test2_fail, NULL, NULL); + g_source_attach (thread_default_timeout, context); + + loop = g_main_loop_new (context, FALSE); + g_file_read_async (file, G_PRIORITY_DEFAULT, NULL, + opened_for_read, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_source_remove (default_timeout); + g_source_destroy (thread_default_timeout); + g_source_unref (thread_default_timeout); +} + +static gboolean +test2_fail (gpointer user_data) +{ + g_assert_not_reached (); + return FALSE; +} + +int +main (int argc, char **argv) +{ + GError *error = NULL; + + g_thread_init (NULL); + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_file_get_contents (TEST_FILE, &test_file_buffer, + &test_file_size, &error); + g_assert_no_error (error); + + g_test_add_func ("/gio/contexts/thread-independence", test_thread_independence); + g_test_add_func ("/gio/contexts/context-independence", test_context_independence); + + return g_test_run(); +} diff --git a/tests/gio/data-input-stream.c b/tests/gio/data-input-stream.c new file mode 100644 index 000000000..368866ebb --- /dev/null +++ b/tests/gio/data-input-stream.c @@ -0,0 +1,336 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc. + * Authors: Tomas Bzatek <tbzatek@redhat.com> + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +#define MAX_LINES 0xFFF +#define MAX_BYTES 0x10000 + +static void +test_seek_to_start (GInputStream *stream) +{ + GError *error = NULL; + gboolean res = g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); +} + +static void +test_read_lines (GDataStreamNewlineType newline_type) +{ + GInputStream *stream; + GInputStream *base_stream; + GError *error = NULL; + char *data; + int line; + const char* lines[MAX_LINES]; + const char* endl[4] = {"\n", "\r", "\r\n", "\n"}; + + /* prepare data */ + int i; + for (i = 0; i < MAX_LINES; i++) + lines[i] = "some_text"; + + base_stream = g_memory_input_stream_new (); + g_assert (base_stream != NULL); + stream = G_INPUT_STREAM (g_data_input_stream_new (base_stream)); + g_assert(stream != NULL); + + /* Byte order testing */ + g_data_input_stream_set_byte_order (G_DATA_INPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + g_assert_cmpint (g_data_input_stream_get_byte_order (G_DATA_INPUT_STREAM (stream)), ==, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + g_data_input_stream_set_byte_order (G_DATA_INPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN); + g_assert_cmpint (g_data_input_stream_get_byte_order (G_DATA_INPUT_STREAM (stream)), ==, G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN); + + /* Line ends testing */ + g_data_input_stream_set_newline_type (G_DATA_INPUT_STREAM (stream), newline_type); + g_assert_cmpint (g_data_input_stream_get_newline_type (G_DATA_INPUT_STREAM (stream)), ==, newline_type); + + + /* Add sample data */ + for (i = 0; i < MAX_LINES; i++) + g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (base_stream), + g_strconcat (lines[i], endl[newline_type], NULL), -1, NULL); + + /* Seek to the start */ + test_seek_to_start (base_stream); + + /* Test read line */ + error = NULL; + data = (char*)1; + line = 0; + while (data) + { + gsize length = -1; + data = g_data_input_stream_read_line (G_DATA_INPUT_STREAM (stream), &length, NULL, &error); + if (data) + { + g_assert_cmpstr (data, ==, lines[line]); + g_assert_no_error (error); + line++; + } + } + g_assert_cmpint (line, ==, MAX_LINES); + + + g_object_unref (base_stream); + g_object_unref (stream); +} + +static void +test_read_lines_LF (void) +{ + test_read_lines (G_DATA_STREAM_NEWLINE_TYPE_LF); +} + +static void +test_read_lines_CR (void) +{ + test_read_lines (G_DATA_STREAM_NEWLINE_TYPE_CR); +} + +static void +test_read_lines_CR_LF (void) +{ + test_read_lines (G_DATA_STREAM_NEWLINE_TYPE_CR_LF); +} + + +static void +test_read_until (void) +{ + GInputStream *stream; + GInputStream *base_stream; + GError *error = NULL; + char *data; + int line; + int i; + +#define REPEATS 10 /* number of rounds */ +#define DATA_STRING " part1 # part2 $ part3 % part4 ^" +#define DATA_PART_LEN 7 /* number of characters between separators */ +#define DATA_SEP "#$%^" + const int DATA_PARTS_NUM = strlen (DATA_SEP) * REPEATS; + + base_stream = g_memory_input_stream_new (); + stream = G_INPUT_STREAM (g_data_input_stream_new (base_stream)); + + for (i = 0; i < REPEATS; i++) + g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (base_stream), DATA_STRING, -1, NULL); + + /* Test stop characters */ + error = NULL; + data = (char*)1; + line = 0; + while (data) + { + gsize length = -1; + data = g_data_input_stream_read_until (G_DATA_INPUT_STREAM (stream), DATA_SEP, &length, NULL, &error); + if (data) + { + g_assert_cmpint (strlen (data), ==, DATA_PART_LEN); + g_assert_no_error (error); + line++; + } + } + g_assert_no_error (error); + g_assert_cmpint (line, ==, DATA_PARTS_NUM); + + + g_object_unref (base_stream); + g_object_unref (stream); +} + +enum TestDataType { + TEST_DATA_BYTE = 0, + TEST_DATA_INT16, + TEST_DATA_UINT16, + TEST_DATA_INT32, + TEST_DATA_UINT32, + TEST_DATA_INT64, + TEST_DATA_UINT64 +}; + +#define TEST_DATA_RETYPE_BUFF(a, t, v) \ + (a == TEST_DATA_BYTE ? (t) *(guchar*)v : \ + (a == TEST_DATA_INT16 ? (t) *(gint16*)v : \ + (a == TEST_DATA_UINT16 ? (t) *(guint16*)v : \ + (a == TEST_DATA_INT32 ? (t) *(gint32*)v : \ + (a == TEST_DATA_UINT32 ? (t) *(guint32*)v : \ + (a == TEST_DATA_INT64 ? (t) *(gint64*)v : \ + (t) *(guint64*)v )))))) + + +static void +test_data_array (GInputStream *stream, GInputStream *base_stream, + gpointer buffer, int len, + enum TestDataType data_type, GDataStreamByteOrder byte_order) +{ + GError *error = NULL; + int pos = 0; + int data_size = 1; + gint64 data; + GDataStreamByteOrder native; + gboolean swap; + + /* Seek to start */ + test_seek_to_start (base_stream); + + /* Set correct data size */ + switch (data_type) + { + case TEST_DATA_BYTE: + data_size = 1; + break; + case TEST_DATA_INT16: + case TEST_DATA_UINT16: + data_size = 2; + break; + case TEST_DATA_INT32: + case TEST_DATA_UINT32: + data_size = 4; + break; + case TEST_DATA_INT64: + case TEST_DATA_UINT64: + data_size = 8; + break; + default: + g_assert_not_reached (); + break; + } + + /* Set flag to swap bytes if needed */ + native = (G_BYTE_ORDER == G_BIG_ENDIAN) ? G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN : G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN; + swap = (byte_order != G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN) && (byte_order != native); + + data = 1; + while (data != 0) + { + switch (data_type) + { + case TEST_DATA_BYTE: + data = g_data_input_stream_read_byte (G_DATA_INPUT_STREAM (stream), NULL, &error); + break; + case TEST_DATA_INT16: + data = g_data_input_stream_read_int16 (G_DATA_INPUT_STREAM (stream), NULL, &error); + if (swap) + data = (gint16)GUINT16_SWAP_LE_BE((gint16)data); + break; + case TEST_DATA_UINT16: + data = g_data_input_stream_read_uint16 (G_DATA_INPUT_STREAM (stream), NULL, &error); + if (swap) + data = (guint16)GUINT16_SWAP_LE_BE((guint16)data); + break; + case TEST_DATA_INT32: + data = g_data_input_stream_read_int32 (G_DATA_INPUT_STREAM (stream), NULL, &error); + if (swap) + data = (gint32)GUINT32_SWAP_LE_BE((gint32)data); + break; + case TEST_DATA_UINT32: + data = g_data_input_stream_read_uint32 (G_DATA_INPUT_STREAM (stream), NULL, &error); + if (swap) + data = (guint32)GUINT32_SWAP_LE_BE((guint32)data); + break; + case TEST_DATA_INT64: + data = g_data_input_stream_read_int64 (G_DATA_INPUT_STREAM (stream), NULL, &error); + if (swap) + data = (gint64)GUINT64_SWAP_LE_BE((gint64)data); + break; + case TEST_DATA_UINT64: + data = g_data_input_stream_read_uint64 (G_DATA_INPUT_STREAM (stream), NULL, &error); + if (swap) + data = (guint64)GUINT64_SWAP_LE_BE((guint64)data); + break; + default: + g_assert_not_reached (); + break; + } + if ((data) && (! error)) + g_assert_cmpint (data, ==, TEST_DATA_RETYPE_BUFF(data_type, gint64, ((guchar*)buffer + pos))); + + pos += data_size; + } + if (pos < len + 1) + g_assert_no_error (error); + g_assert_cmpint (pos - data_size, ==, len); +} + +static void +test_read_int (void) +{ + GInputStream *stream; + GInputStream *base_stream; + GRand *randomizer; + int i; + gpointer buffer; + + randomizer = g_rand_new (); + buffer = g_malloc0 (MAX_BYTES); + + /* Fill in some random data */ + for (i = 0; i < MAX_BYTES; i++) + { + guchar x = 0; + while (! x) + x = (guchar)g_rand_int (randomizer); + *(guchar*)((guchar*)buffer + sizeof(guchar) * i) = x; + } + + base_stream = g_memory_input_stream_new (); + stream = G_INPUT_STREAM (g_data_input_stream_new (base_stream)); + g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (base_stream), buffer, MAX_BYTES, NULL); + + + for (i = 0; i < 3; i++) + { + int j; + g_data_input_stream_set_byte_order (G_DATA_INPUT_STREAM (stream), i); + + for (j = 0; j <= TEST_DATA_UINT64; j++) + test_data_array (stream, base_stream, buffer, MAX_BYTES, j, i); + } + + g_object_unref (base_stream); + g_object_unref (stream); + g_rand_free (randomizer); + g_free (buffer); +} + + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/data-input-stream/read-lines-LF", test_read_lines_LF); + g_test_add_func ("/data-input-stream/read-lines-CR", test_read_lines_CR); + g_test_add_func ("/data-input-stream/read-lines-CR-LF", test_read_lines_CR_LF); + g_test_add_func ("/data-input-stream/read-until", test_read_until); + g_test_add_func ("/data-input-stream/read-int", test_read_int); + + return g_test_run(); +} diff --git a/tests/gio/data-output-stream.c b/tests/gio/data-output-stream.c new file mode 100644 index 000000000..01723897e --- /dev/null +++ b/tests/gio/data-output-stream.c @@ -0,0 +1,289 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc. + * Authors: Tomas Bzatek <tbzatek@redhat.com> + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +#define MAX_LINES 0xFFF +#define MAX_LINES_BUFF 0xFFFFFF +#define MAX_BYTES_BINARY 0x100 + +static void +test_read_lines (GDataStreamNewlineType newline_type) +{ + GOutputStream *stream; + GOutputStream *base_stream; + GError *error = NULL; + gpointer data; + char *lines; + int size; + int i; + +#define TEST_STRING "some_text" + + const char* endl[4] = {"\n", "\r", "\r\n", "\n"}; + + + data = g_malloc0 (MAX_LINES_BUFF); + lines = g_malloc0 ((strlen (TEST_STRING) + strlen (endl[newline_type])) * MAX_LINES + 1); + + /* initialize objects */ + base_stream = g_memory_output_stream_new (data, MAX_LINES_BUFF, NULL, NULL); + stream = G_OUTPUT_STREAM (g_data_output_stream_new (base_stream)); + + + /* fill data */ + for (i = 0; i < MAX_LINES; i++) + { + gboolean res; + char *s = g_strconcat (TEST_STRING, endl[newline_type], NULL); + res = g_data_output_stream_put_string (G_DATA_OUTPUT_STREAM (stream), s, NULL, &error); + g_stpcpy ((char*)(lines + i*strlen(s)), s); + g_assert_no_error (error); + g_assert (res == TRUE); + } + + /* Byte order testing */ + g_data_output_stream_set_byte_order (G_DATA_OUTPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + g_assert_cmpint (g_data_output_stream_get_byte_order (G_DATA_OUTPUT_STREAM (stream)), ==, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); + g_data_output_stream_set_byte_order (G_DATA_OUTPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN); + g_assert_cmpint (g_data_output_stream_get_byte_order (G_DATA_OUTPUT_STREAM (stream)), ==, G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN); + + /* compare data */ + size = strlen (data); + g_assert_cmpint (size, <, MAX_LINES_BUFF); + g_assert_cmpstr ((char*)data, ==, lines); + + g_object_unref (base_stream); + g_object_unref (stream); + g_free (data); + g_free (lines); +} + +static void +test_read_lines_LF (void) +{ + test_read_lines (G_DATA_STREAM_NEWLINE_TYPE_LF); +} + +static void +test_read_lines_CR (void) +{ + test_read_lines (G_DATA_STREAM_NEWLINE_TYPE_CR); +} + +static void +test_read_lines_CR_LF (void) +{ + test_read_lines (G_DATA_STREAM_NEWLINE_TYPE_CR_LF); +} + +enum TestDataType { + TEST_DATA_BYTE = 0, + TEST_DATA_INT16, + TEST_DATA_UINT16, + TEST_DATA_INT32, + TEST_DATA_UINT32, + TEST_DATA_INT64, + TEST_DATA_UINT64 +}; + +static void +test_data_array (guchar *buffer, gsize len, + enum TestDataType data_type, GDataStreamByteOrder byte_order) +{ + GOutputStream *stream; + GOutputStream *base_stream; + guchar *stream_data; + + GError *error = NULL; + guint pos; + GDataStreamByteOrder native; + gboolean swap; + gboolean res; + + /* create objects */ + stream_data = g_malloc0 (len); + base_stream = g_memory_output_stream_new (stream_data, len, NULL, NULL); + stream = G_OUTPUT_STREAM (g_data_output_stream_new (base_stream)); + g_data_output_stream_set_byte_order (G_DATA_OUTPUT_STREAM (stream), byte_order); + + /* Set flag to swap bytes if needed */ + native = (G_BYTE_ORDER == G_BIG_ENDIAN) ? G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN : G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN; + swap = (byte_order != G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN) && (byte_order != native); + + /* set len to length of buffer cast to actual type */ + switch (data_type) + { + case TEST_DATA_BYTE: + break; + case TEST_DATA_INT16: + case TEST_DATA_UINT16: + g_assert_cmpint (len % 2, ==, 0); + case TEST_DATA_INT32: + case TEST_DATA_UINT32: + g_assert_cmpint (len % 4, ==, 0); + case TEST_DATA_INT64: + case TEST_DATA_UINT64: + g_assert_cmpint (len % 8, ==, 0); + len /= 8; + break; + default: + g_assert_not_reached (); + break; + } + + /* Write data to the file */ + for (pos = 0; pos < len; pos++) + { + switch (data_type) + { + case TEST_DATA_BYTE: + res = g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (stream), buffer[pos], NULL, &error); + break; + case TEST_DATA_INT16: + res = g_data_output_stream_put_int16 (G_DATA_OUTPUT_STREAM (stream), ((gint16 *) buffer)[pos], NULL, &error); + break; + case TEST_DATA_UINT16: + res = g_data_output_stream_put_uint16 (G_DATA_OUTPUT_STREAM (stream), ((guint16 *) buffer)[pos], NULL, &error); + break; + case TEST_DATA_INT32: + res = g_data_output_stream_put_int32 (G_DATA_OUTPUT_STREAM (stream), ((gint32 *) buffer)[pos], NULL, &error); + break; + case TEST_DATA_UINT32: + res = g_data_output_stream_put_uint32 (G_DATA_OUTPUT_STREAM (stream), ((guint32 *) buffer)[pos], NULL, &error); + break; + case TEST_DATA_INT64: + res = g_data_output_stream_put_int64 (G_DATA_OUTPUT_STREAM (stream), ((gint64 *) buffer)[pos], NULL, &error); + break; + case TEST_DATA_UINT64: + res = g_data_output_stream_put_uint64 (G_DATA_OUTPUT_STREAM (stream), ((guint64 *) buffer)[pos], NULL, &error); + break; + default: + g_assert_not_reached (); + break; + } + g_assert_no_error (error); + g_assert_cmpint (res, ==, TRUE); + } + + /* Compare data back */ + for (pos = 0; pos < len; pos++) + { + switch (data_type) + { + case TEST_DATA_BYTE: + /* swapping unnecessary */ + g_assert_cmpint (buffer[pos], ==, stream_data[pos]); + break; + case TEST_DATA_UINT16: + if (swap) + g_assert_cmpint (GUINT16_SWAP_LE_BE (((guint16 *) buffer)[pos]), ==, ((guint16 *) stream_data)[pos]); + else + g_assert_cmpint (((guint16 *) buffer)[pos], ==, ((guint16 *) stream_data)[pos]); + break; + case TEST_DATA_INT16: + if (swap) + g_assert_cmpint ((gint16) GUINT16_SWAP_LE_BE (((gint16 *) buffer)[pos]), ==, ((gint16 *) stream_data)[pos]); + else + g_assert_cmpint (((gint16 *) buffer)[pos], ==, ((gint16 *) stream_data)[pos]); + break; + case TEST_DATA_UINT32: + if (swap) + g_assert_cmpint (GUINT32_SWAP_LE_BE (((guint32 *) buffer)[pos]), ==, ((guint32 *) stream_data)[pos]); + else + g_assert_cmpint (((guint32 *) buffer)[pos], ==, ((guint32 *) stream_data)[pos]); + break; + case TEST_DATA_INT32: + if (swap) + g_assert_cmpint ((gint32) GUINT32_SWAP_LE_BE (((gint32 *) buffer)[pos]), ==, ((gint32 *) stream_data)[pos]); + else + g_assert_cmpint (((gint32 *) buffer)[pos], ==, ((gint32 *) stream_data)[pos]); + break; + case TEST_DATA_UINT64: + if (swap) + g_assert_cmpint (GUINT64_SWAP_LE_BE (((guint64 *) buffer)[pos]), ==, ((guint64 *) stream_data)[pos]); + else + g_assert_cmpint (((guint64 *) buffer)[pos], ==, ((guint64 *) stream_data)[pos]); + break; + case TEST_DATA_INT64: + if (swap) + g_assert_cmpint ((gint64) GUINT64_SWAP_LE_BE (((gint64 *) buffer)[pos]), ==, ((gint64 *) stream_data)[pos]); + else + g_assert_cmpint (((gint64 *) buffer)[pos], ==, ((gint64 *) stream_data)[pos]); + break; + default: + g_assert_not_reached (); + break; + } + } + + g_object_unref (base_stream); + g_object_unref (stream); + g_free (stream_data); +} + +static void +test_read_int (void) +{ + GRand *randomizer; + gpointer buffer; + int i; + + randomizer = g_rand_new (); + buffer = g_malloc0(MAX_BYTES_BINARY); + + /* Fill in some random data */ + for (i = 0; i < MAX_BYTES_BINARY; i++) + { + guchar x = 0; + while (! x) x = (guchar)g_rand_int (randomizer); + *(guchar*)((guchar*)buffer + sizeof (guchar) * i) = x; + } + + for (i = 0; i < 3; i++) + { + int j; + for (j = 0; j <= TEST_DATA_UINT64; j++) + test_data_array (buffer, MAX_BYTES_BINARY, j, i); + } + + g_rand_free (randomizer); + g_free (buffer); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/data-input-stream/read-lines-LF", test_read_lines_LF); + g_test_add_func ("/data-input-stream/read-lines-CR", test_read_lines_CR); + g_test_add_func ("/data-input-stream/read-lines-CR-LF", test_read_lines_CR_LF); + g_test_add_func ("/data-input-stream/read-int", test_read_int); + + return g_test_run(); +} diff --git a/tests/gio/desktop-app-info.c b/tests/gio/desktop-app-info.c new file mode 100644 index 000000000..62ccd03a7 --- /dev/null +++ b/tests/gio/desktop-app-info.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2008 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Matthias Clasen + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <gio/gdesktopappinfo.h> +#include <stdlib.h> +#include <string.h> + +static char *basedir; + +static GAppInfo * +create_app_info (const char *name) +{ + GError *error; + GAppInfo *info; + + error = NULL; + info = g_app_info_create_from_commandline ("/usr/bin/true blah", + name, + G_APP_INFO_CREATE_NONE, + &error); + g_assert (error == NULL); + + /* this is necessary to ensure that the info is saved */ + g_app_info_set_as_default_for_type (info, "application/x-blah", &error); + g_assert (error == NULL); + g_app_info_remove_supports_type (info, "application/x-blah", &error); + g_assert (error == NULL); + g_app_info_reset_type_associations ("application/x-blah"); + + return info; +} + +static void +test_delete (void) +{ + GAppInfo *info; + + const char *id; + char *filename; + gboolean res; + + info = create_app_info ("Blah"); + + id = g_app_info_get_id (info); + g_assert (id != NULL); + + filename = g_build_filename (basedir, "applications", id, NULL); + + res = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert (res); + + res = g_app_info_can_delete (info); + g_assert (res); + + res = g_app_info_delete (info); + g_assert (res); + + res = g_file_test (filename, G_FILE_TEST_EXISTS); + g_assert (!res); + + g_object_unref (info); + + if (g_file_test ("/usr/share/applications/gedit.desktop", G_FILE_TEST_EXISTS)) + { + info = (GAppInfo*)g_desktop_app_info_new ("gedit.desktop"); + g_assert (info); + + res = g_app_info_can_delete (info); + g_assert (!res); + + res = g_app_info_delete (info); + g_assert (!res); + } +} + +static void +test_default (void) +{ + GAppInfo *info, *info1, *info2, *info3; + GList *list; + GError *error = NULL; + + info1 = create_app_info ("Blah1"); + info2 = create_app_info ("Blah2"); + info3 = create_app_info ("Blah3"); + + g_app_info_set_as_default_for_type (info1, "application/x-test", &error); + g_assert (error == NULL); + + g_app_info_set_as_default_for_type (info2, "application/x-test", &error); + g_assert (error == NULL); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (g_list_length (list) == 2); + + /* check that both are in the list, info2 before info1 */ + info = (GAppInfo *)list->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0); + + info = (GAppInfo *)list->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info1)) == 0); + + g_list_foreach (list, (GFunc)g_object_unref, NULL); + g_list_free (list); + + /* now try adding something at the end */ + g_app_info_add_supports_type (info3, "application/x-test", &error); + g_assert (error == NULL); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (g_list_length (list) == 3); + + /* check that all are in the list, info2, info1, info3 */ + info = (GAppInfo *)list->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0); + + info = (GAppInfo *)list->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info1)) == 0); + + info = (GAppInfo *)list->next->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info3)) == 0); + + g_list_foreach (list, (GFunc)g_object_unref, NULL); + g_list_free (list); + + /* now remove info1 again */ + g_app_info_remove_supports_type (info1, "application/x-test", &error); + g_assert (error == NULL); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (g_list_length (list) == 2); + + /* check that both are in the list, info2 before info3 */ + info = (GAppInfo *)list->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info2)) == 0); + + info = (GAppInfo *)list->next->data; + g_assert (g_strcmp0 (g_app_info_get_id (info), g_app_info_get_id (info3)) == 0); + + g_list_foreach (list, (GFunc)g_object_unref, NULL); + g_list_free (list); + + /* now clean it all up */ + g_app_info_reset_type_associations ("application/x-test"); + + list = g_app_info_get_all_for_type ("application/x-test"); + g_assert (list == NULL); + + g_app_info_delete (info1); + g_app_info_delete (info2); + g_app_info_delete (info3); + + g_object_unref (info1); + g_object_unref (info2); +} + +static void +cleanup_dir_recurse (GFile *parent, GFile *root) +{ + gboolean res; + GError *error; + GFileEnumerator *enumerator; + GFileInfo *info; + GFile *descend; + char *relative_path; + + g_assert (root != NULL); + + error = NULL; + enumerator = + g_file_enumerate_children (parent, "*", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, + &error); + if (! enumerator) + return; + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + while ((info) && (!error)) + { + descend = g_file_get_child (parent, g_file_info_get_name (info)); + g_assert (descend != NULL); + relative_path = g_file_get_relative_path (root, descend); + g_assert (relative_path != NULL); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + cleanup_dir_recurse (descend, root); + + error = NULL; + res = g_file_delete (descend, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + + g_object_unref (descend); + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + } + g_assert (error == NULL); + + error = NULL; + res = g_file_enumerator_close (enumerator, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert (error == NULL); +} + +static void +cleanup_subdirs (const char *base_dir) +{ + GFile *base, *file; + + base = g_file_new_for_path (base_dir); + file = g_file_get_child (base, "applications"); + cleanup_dir_recurse (file, file); + g_object_unref (file); + file = g_file_get_child (base, "mime"); + cleanup_dir_recurse (file, file); + g_object_unref (file); +} + +int +main (int argc, + char *argv[]) +{ + gint result; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + basedir = g_get_current_dir (); + g_setenv ("XDG_DATA_HOME", basedir, TRUE); + cleanup_subdirs (basedir); + + g_test_add_func ("/desktop-app-info/delete", test_delete); + g_test_add_func ("/desktop-app-info/default", test_default); + + result = g_test_run (); + + cleanup_subdirs (basedir); + + return result; +} diff --git a/tests/gio/echo-server.c b/tests/gio/echo-server.c new file mode 100644 index 000000000..7e0551098 --- /dev/null +++ b/tests/gio/echo-server.c @@ -0,0 +1,73 @@ +#include <gio/gio.h> +#include <string.h> + +#define MESSAGE "Welcome to the echo service!\n" + +int port = 7777; +static GOptionEntry cmd_entries[] = { + {"port", 'p', 0, G_OPTION_ARG_INT, &port, + "Local port to bind to", NULL}, + {NULL} +}; + + +static gboolean +handler (GThreadedSocketService *service, + GSocketConnection *connection, + GSocketListener *listener, + gpointer user_data) +{ + GOutputStream *out; + GInputStream *in; + char buffer[1024]; + gssize size; + + out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); + in = g_io_stream_get_input_stream (G_IO_STREAM (connection)); + + g_output_stream_write_all (out, MESSAGE, strlen (MESSAGE), + NULL, NULL, NULL); + + while (0 < (size = g_input_stream_read (in, buffer, + sizeof buffer, NULL, NULL))) + g_output_stream_write (out, buffer, size, NULL, NULL); + + return TRUE; +} + +int +main (int argc, char *argv[]) +{ + GSocketService *service; + GOptionContext *context; + GError *error = NULL; + + g_type_init (); + g_thread_init (NULL); + + context = g_option_context_new (" - Test GSocket server stuff"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + service = g_threaded_socket_service_new (10); + + if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), + port, + NULL, + &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + g_print ("Echo service listening on port %d\n", port); + + g_signal_connect (service, "run", G_CALLBACK (handler), NULL); + + g_main_loop_run (g_main_loop_new (NULL, FALSE)); + g_assert_not_reached (); +} diff --git a/tests/gio/filter-streams.c b/tests/gio/filter-streams.c new file mode 100644 index 000000000..fab17ff9f --- /dev/null +++ b/tests/gio/filter-streams.c @@ -0,0 +1,239 @@ +/* + * Copyright © 2009 Codethink Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Ryan Lortie <desrt@desrt.ca> + */ + +#include <glib/glib.h> +#include <gio/gio.h> + +static void +test_input_filter (void) +{ + GInputStream *base, *f1, *f2; + + g_test_bug ("568394"); + base = g_memory_input_stream_new_from_data ("abcdefghijk", -1, NULL); + f1 = g_buffered_input_stream_new (base); + f2 = g_buffered_input_stream_new (base); + + g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (f1), FALSE); + + g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f1)) == base); + g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f2)) == base); + + g_assert (!g_input_stream_is_closed (base)); + g_assert (!g_input_stream_is_closed (f1)); + g_assert (!g_input_stream_is_closed (f2)); + + g_object_unref (f1); + + g_assert (!g_input_stream_is_closed (base)); + g_assert (!g_input_stream_is_closed (f2)); + + g_object_unref (f2); + + g_assert (g_input_stream_is_closed (base)); + + g_object_unref (base); +} + +static void +test_output_filter (void) +{ + GOutputStream *base, *f1, *f2; + + base = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); + f1 = g_buffered_output_stream_new (base); + f2 = g_buffered_output_stream_new (base); + + g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (f1), FALSE); + + g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f1)) == base); + g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f2)) == base); + + g_assert (!g_output_stream_is_closed (base)); + g_assert (!g_output_stream_is_closed (f1)); + g_assert (!g_output_stream_is_closed (f2)); + + g_object_unref (f1); + + g_assert (!g_output_stream_is_closed (base)); + g_assert (!g_output_stream_is_closed (f2)); + + g_object_unref (f2); + + g_assert (g_output_stream_is_closed (base)); + + g_object_unref (base); +} + +gpointer expected_obj; +gpointer expected_data; +gboolean callback_happened; + +static void +in_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + g_assert (object == expected_obj); + g_assert (user_data == expected_data); + g_assert (callback_happened == FALSE); + + g_input_stream_close_finish (expected_obj, result, &error); + g_assert (error == NULL); + + callback_happened = TRUE; +} + +static void +test_input_async (void) +{ + GInputStream *base, *f1, *f2; + + base = g_memory_input_stream_new_from_data ("abcdefghijk", -1, NULL); + f1 = g_buffered_input_stream_new (base); + f2 = g_buffered_input_stream_new (base); + + g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (f1), FALSE); + + g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f1)) == base); + g_assert (g_filter_input_stream_get_base_stream (G_FILTER_INPUT_STREAM (f2)) == base); + + g_assert (!g_input_stream_is_closed (base)); + g_assert (!g_input_stream_is_closed (f1)); + g_assert (!g_input_stream_is_closed (f2)); + + expected_obj = f1; + expected_data = g_malloc (20); + callback_happened = FALSE; + g_input_stream_close_async (f1, 0, NULL, in_cb, expected_data); + + g_assert (callback_happened == FALSE); + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + g_assert (callback_happened == TRUE); + + g_assert (!g_input_stream_is_closed (base)); + g_assert (!g_input_stream_is_closed (f2)); + g_free (expected_data); + g_object_unref (f1); + g_assert (!g_input_stream_is_closed (base)); + g_assert (!g_input_stream_is_closed (f2)); + + expected_obj = f2; + expected_data = g_malloc (20); + callback_happened = FALSE; + g_input_stream_close_async (f2, 0, NULL, in_cb, expected_data); + + g_assert (callback_happened == FALSE); + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + g_assert (callback_happened == TRUE); + + g_assert (g_input_stream_is_closed (base)); + g_assert (g_input_stream_is_closed (f2)); + g_free (expected_data); + g_object_unref (f2); + + g_assert (g_input_stream_is_closed (base)); + g_object_unref (base); +} + +static void +out_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + + g_assert (object == expected_obj); + g_assert (user_data == expected_data); + g_assert (callback_happened == FALSE); + + g_output_stream_close_finish (expected_obj, result, &error); + g_assert (error == NULL); + + callback_happened = TRUE; +} + + +static void +test_output_async (void) +{ + GOutputStream *base, *f1, *f2; + + base = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); + f1 = g_buffered_output_stream_new (base); + f2 = g_buffered_output_stream_new (base); + + g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (f1), FALSE); + + g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f1)) == base); + g_assert (g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (f2)) == base); + + g_assert (!g_output_stream_is_closed (base)); + g_assert (!g_output_stream_is_closed (f1)); + g_assert (!g_output_stream_is_closed (f2)); + + expected_obj = f1; + expected_data = g_malloc (20); + callback_happened = FALSE; + g_output_stream_close_async (f1, 0, NULL, out_cb, expected_data); + + g_assert (callback_happened == FALSE); + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + g_assert (callback_happened == TRUE); + + g_assert (!g_output_stream_is_closed (base)); + g_assert (!g_output_stream_is_closed (f2)); + g_free (expected_data); + g_object_unref (f1); + g_assert (!g_output_stream_is_closed (base)); + g_assert (!g_output_stream_is_closed (f2)); + + expected_obj = f2; + expected_data = g_malloc (20); + callback_happened = FALSE; + g_output_stream_close_async (f2, 0, NULL, out_cb, expected_data); + + g_assert (callback_happened == FALSE); + while (g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); + g_assert (callback_happened == TRUE); + + g_assert (g_output_stream_is_closed (base)); + g_assert (g_output_stream_is_closed (f2)); + g_free (expected_data); + g_object_unref (f2); + + g_assert (g_output_stream_is_closed (base)); + g_object_unref (base); +} + + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/"); + + g_type_init (); + g_test_add_func ("/filter-stream/input", test_input_filter); + g_test_add_func ("/filter-stream/output", test_output_filter); + g_test_add_func ("/filter-stream/async-input", test_input_async); + g_test_add_func ("/filter-stream/async-output", test_output_async); + + return g_test_run(); +} diff --git a/tests/gio/g-file-info.c b/tests/gio/g-file-info.c new file mode 100644 index 000000000..80bdd6e46 --- /dev/null +++ b/tests/gio/g-file-info.c @@ -0,0 +1,125 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc. + * Authors: Tomas Bzatek <tbzatek@redhat.com> + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +#define TEST_NAME "Prilis zlutoucky kun" +#define TEST_DISPLAY_NAME "UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88" +#define TEST_SIZE 0xFFFFFFF0 + +static void +test_assigned_values (GFileInfo *info) +{ + const char *name, *display_name, *mistake; + guint64 size; + GFileType type; + + /* Test for attributes presence */ + g_assert (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_NAME) == TRUE); + g_assert (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) == TRUE); + g_assert (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE) == TRUE); + g_assert (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME) == FALSE); + + /* Retrieve data back and compare */ + + name = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME); + display_name = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); + mistake = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME); + size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE); + type = g_file_info_get_file_type (info); + + g_assert_cmpstr (name, ==, TEST_NAME); + g_assert_cmpstr (display_name, ==, TEST_DISPLAY_NAME); + g_assert (mistake == NULL); + g_assert_cmpint (size, ==, TEST_SIZE); + g_assert_cmpstr (name, ==, g_file_info_get_name (info)); + g_assert_cmpstr (display_name, ==, g_file_info_get_display_name (info)); + g_assert_cmpint (size, ==, g_file_info_get_size (info) ); + g_assert_cmpint (type, ==, G_FILE_TYPE_DIRECTORY); +} + + + +static void +test_g_file_info (void) +{ + GFileInfo *info; + GFileInfo *info_dup; + GFileInfo *info_copy; + char **attr_list; + + info = g_file_info_new (); + + /* Test for empty instance */ + attr_list = g_file_info_list_attributes (info, NULL); + g_assert (attr_list != NULL); + g_assert (*attr_list == NULL); + + g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME, TEST_NAME); + g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, TEST_DISPLAY_NAME); + g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE, TEST_SIZE); + g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); + + /* The attr list should not be empty now */ + attr_list = g_file_info_list_attributes (info, NULL); + g_assert (attr_list != NULL); + g_assert (*attr_list != NULL); + + test_assigned_values (info); + + /* Test dups */ + info_dup = g_file_info_dup (info); + g_assert (info_dup != NULL); + test_assigned_values (info_dup); + + info_copy = g_file_info_new (); + g_file_info_copy_into (info_dup, info_copy); + g_assert (info_copy != NULL); + test_assigned_values (info_copy); + + /* Test remove attribute */ + g_assert (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER) == FALSE); + g_file_info_set_attribute_int32 (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER, 10); + g_assert (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER) == TRUE); + + g_file_info_remove_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER); + g_assert (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER) == FALSE); + + g_object_unref (info); + g_object_unref (info_dup); + g_object_unref (info_copy); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/g-file-info/test_g_file_info", test_g_file_info); + + return g_test_run(); +} diff --git a/tests/gio/g-file.c b/tests/gio/g-file.c new file mode 100644 index 000000000..8bba592aa --- /dev/null +++ b/tests/gio/g-file.c @@ -0,0 +1,538 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc. + * Authors: Tomas Bzatek <tbzatek@redhat.com> + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +struct TestPathsWithOper { + const char *path1; + gboolean equal; + gboolean use_uri; + const char *path2; + const char *path3; +}; + + + +/* TODO: + * - test on Windows + * + **/ + +static void +test_g_file_new_null (void) +{ + const char *paths[] = {"/", + "/tmp///", + "/non-existent-file", + "/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88", + NULL + }; + const char *uris[] = {"file:///", + "file:///tmp///", + "non-existent-uri:///some-dir/", + "file:///UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88", + NULL + }; + + GFile *file = NULL; + + int i = 0; + while (paths[i]) + { + file = g_file_new_for_path (paths[i++]); + g_assert (file != NULL); + g_object_unref (file); + } + + i = 0; + while (uris[i]) + { + file = g_file_new_for_uri (uris[i++]); + g_assert (file != NULL); + g_object_unref(file); + } +} + + + +static gboolean +compare_two_files (const gboolean use_uri, const char *path1, const char *path2) +{ + GFile *file1 = NULL; + GFile *file2 = NULL; + gboolean equal; + + if (use_uri) + { + file1 = g_file_new_for_uri (path1); + file2 = g_file_new_for_uri (path2); + } + else + { + file1 = g_file_new_for_path (path1); + file2 = g_file_new_for_path (path2); + } + + g_assert (file1 != NULL); + g_assert (file2 != NULL); + + equal = g_file_equal (file1, file2); + + g_object_unref (file1); + g_object_unref (file2); + + return equal; +} + +static void +test_g_file_new_for_path (void) +{ + const struct TestPathsWithOper cmp_paths[] = + { + {"/", TRUE, 0, "/./"}, + {"//", TRUE, 0, "//"}, + {"//", TRUE, 0, "//./"}, + {"/", TRUE, 0, "/.//"}, + {"/", TRUE, 0, "/././"}, + {"/tmp", TRUE, 0, "/tmp/d/../"}, + {"/", TRUE, 0, "/somedir/../"}, + {"/", FALSE, 0, "/somedir/.../"}, + {"//tmp/dir1", TRUE, 0, "//tmp/dir1"}, + {"/tmp/dir1", TRUE, 0, "///tmp/dir1"}, + {"/tmp/dir1", TRUE, 0, "////tmp/dir1"}, + {"/tmp/dir1", TRUE, 0, "/tmp/./dir1"}, + {"/tmp/dir1", TRUE, 0, "/tmp//dir1"}, + {"/tmp/dir1", TRUE, 0, "/tmp///dir1///"}, + {"/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88", TRUE, 0, "/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88/"} + }; + + guint i; + for (i = 0; i < G_N_ELEMENTS (cmp_paths); i++) + { + gboolean equal = compare_two_files (FALSE, cmp_paths[i].path1, cmp_paths[i].path2); + g_assert_cmpint (equal, ==, cmp_paths[i].equal); + } +} + + + +static void +test_g_file_new_for_uri (void) +{ + const struct TestPathsWithOper cmp_uris[] = { + {"file:///", TRUE, 0, "file:///./"}, + {"file:////", TRUE, 0, "file:////"}, + {"file:////", TRUE, 0, "file:////./"}, + {"file:///", TRUE, 0, "file:///.//"}, + {"file:///", TRUE, 0, "file:///././"}, + {"file:///tmp", TRUE, 0, "file:///tmp/d/../"}, + {"file:///", TRUE, 0, "file:///somedir/../"}, + {"file:///", FALSE, 0, "file:///somedir/.../"}, + {"file:////tmp/dir1", TRUE, 0, "file:////tmp/dir1"}, + {"file:///tmp/dir1", TRUE, 0, "file:///tmp/./dir1"}, + {"file:///tmp/dir1", TRUE, 0, "file:///tmp//dir1"}, + {"file:///tmp/dir1", TRUE, 0, "file:///tmp///dir1///"}, + {"file:///UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88", TRUE, 0, "file:///UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88/"} + }; + + guint i; + for (i = 0; i < G_N_ELEMENTS (cmp_uris); i++) + { + gboolean equal = compare_two_files (TRUE, cmp_uris[i].path1, cmp_uris[i].path2); + g_assert_cmpint (equal, ==, cmp_uris[i].equal); + } +} + + + +static gboolean +dup_equals (const gboolean use_uri, const char *path) +{ + GFile *file1 = NULL; + GFile *file2 = NULL; + gboolean equal; + + if (use_uri) + file1 = g_file_new_for_uri (path); + else + file1 = g_file_new_for_path (path); + + g_assert (file1 != NULL); + + file2 = g_file_dup (file1); + + g_assert (file2 != NULL); + + equal = g_file_equal (file1, file2); + + g_object_unref (file1); + g_object_unref (file2); + + return equal; +} + +static void +test_g_file_dup (void) +{ + const struct TestPathsWithOper dup_paths[] = + { + {"/", 0, FALSE, ""}, + {"file:///", 0, TRUE, ""}, + {"totalnonsense", 0, FALSE, ""}, + {"/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88", 0, FALSE, ""}, + {"file:///UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88", 0, TRUE, ""}, + }; + + guint i; + for (i = 0; i < G_N_ELEMENTS (dup_paths); i++) + { + gboolean equal = dup_equals (dup_paths[i].use_uri, dup_paths[i].path1); + g_assert (equal == TRUE); + } +} + + + +static gboolean +parse_check_utf8 (const gboolean use_uri, const char *path, const char *result_parse_name) +{ + GFile *file1 = NULL; + GFile *file2 = NULL; + char *parsed_name; + gboolean is_utf8_valid; + gboolean equal; + + if (use_uri) + file1 = g_file_new_for_uri (path); + else + file1 = g_file_new_for_path (path); + + g_assert (file1 != NULL); + + parsed_name = g_file_get_parse_name (file1); + + g_assert (parsed_name != NULL); + + /* UTF-8 validation */ + is_utf8_valid = g_utf8_validate (parsed_name, -1, NULL); + g_assert (is_utf8_valid == TRUE); + + if (result_parse_name) + g_assert_cmpstr (parsed_name, ==, result_parse_name); + + file2 = g_file_parse_name (parsed_name); + + g_assert (file2 != NULL); + + equal = g_file_equal (file1, file2); + + g_object_unref (file1); + g_object_unref (file2); + + g_free (parsed_name); + + return equal; +} + +static void +test_g_file_get_parse_name_utf8 (void) +{ + const struct TestPathsWithOper strings[] = + { + {"/", 0, FALSE, "/"}, + {"file:///", 0, TRUE, "/"}, + {"totalnonsense", 0, FALSE, NULL}, + {"/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88", 0, FALSE, NULL /* Depends on local file encoding */}, + {"file:///invalid%08/UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88/", 0, TRUE, "file:///invalid%08/UTF-8%20p\xc5\x99\xc3\xadli\xc5\xa1%20\xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd%20k\xc5\xaf\xc5\x88"}, + }; + + guint i; + for (i = 0; i < G_N_ELEMENTS (strings); i++) + { + gboolean equal = parse_check_utf8 (strings[i].use_uri, strings[i].path1, strings[i].path2); + g_assert (equal == TRUE); + } +} + +static char * +resolve_arg (const gboolean is_uri_only, const char *arg) +{ + GFile *file1 = NULL; + char *uri = NULL; + char *path = NULL; + char *s = NULL; + + file1 = g_file_new_for_commandline_arg (arg); + g_assert (file1 != NULL); + + /* Test if we get URI string */ + uri = g_file_get_uri (file1); + g_assert_cmpstr (uri, !=, NULL); + g_print ("%s\n",uri); + + /* Test if we get correct value of the local path */ + path = g_file_get_path (file1); + if (is_uri_only) + g_assert_cmpstr (path, ==, NULL); + else + g_assert (g_path_is_absolute (path) == TRUE); + + /* Get the URI scheme and compare it with expected one */ + s = g_file_get_uri_scheme (file1); + + g_object_unref (file1); + g_free (uri); + g_free (path); + + return s; +} + +static void +test_g_file_new_for_commandline_arg (void) +{ + /* TestPathsWithOper.use_uri represents IsURIOnly here */ + const struct TestPathsWithOper arg_data[] = + { + {"./", 0, FALSE, "file"}, + {"../", 0, FALSE, "file"}, + {"/tmp", 0, FALSE, "file"}, + {"//UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88", 0, FALSE, "file"}, + {"file:///UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88/", 0, FALSE, "file"}, +#if 0 + {"http://www.gtk.org/", 0, TRUE, "http"}, + {"ftp://user:pass@ftp.gimp.org/", 0, TRUE, "ftp"}, +#endif + }; + GFile *file; + char *resolved; + char *cwd; + guint i; + + for (i = 0; i < G_N_ELEMENTS (arg_data); i++) + { + char *s = resolve_arg (arg_data[i].use_uri, arg_data[i].path1); + g_assert_cmpstr (s, ==, arg_data[i].path2); + g_free (s); + } + + /* Manual test for getting correct cwd */ + file = g_file_new_for_commandline_arg ("./"); + resolved = g_file_get_path (file); + cwd = g_get_current_dir (); + g_assert_cmpstr (resolved, ==, cwd); + g_object_unref (file); + g_free (resolved); + g_free (cwd); +} + +static char* +get_relative_path (const gboolean use_uri, const gboolean should_have_prefix, const char *dir1, const char *dir2) +{ + GFile *file1 = NULL; + GFile *file2 = NULL; + GFile *file3 = NULL; + gboolean has_prefix = FALSE; + char *relative_path = NULL; + + if (use_uri) + { + file1 = g_file_new_for_uri (dir1); + file2 = g_file_new_for_uri (dir2); + } + else + { + file1 = g_file_new_for_path (dir1); + file2 = g_file_new_for_path (dir2); + } + + g_assert (file1 != NULL); + g_assert (file2 != NULL); + + has_prefix = g_file_has_prefix (file2, file1); + g_print ("%s %s\n", dir1, dir2); + g_assert (has_prefix == should_have_prefix); + + relative_path = g_file_get_relative_path (file1, file2); + if (should_have_prefix) + { + g_assert (relative_path != NULL); + + file3 = g_file_resolve_relative_path (file1, relative_path); + g_assert (g_file_equal (file2, file3) == TRUE); + } + + if (file1) + g_object_unref (file1); + if (file2) + g_object_unref (file2); + if (file3) + g_object_unref (file3); + + return relative_path; +} + +static void +test_g_file_has_prefix (void) +{ + /* TestPathsWithOper.equal represents here if the dir belongs to the directory structure */ + const struct TestPathsWithOper dirs[] = + { + /* path1 equal uri path2 path3 */ + {"/dir1", TRUE, FALSE, "/dir1/dir2/dir3/", "dir2/dir3"}, + {"/dir1/", TRUE, FALSE, "/dir1/dir2/dir3/", "dir2/dir3"}, + {"/dir1", TRUE, FALSE, "/dir1/dir2/dir3", "dir2/dir3"}, + {"/dir1/", TRUE, FALSE, "/dir1/dir2/dir3", "dir2/dir3"}, + {"/tmp/", FALSE, FALSE, "/something/", NULL}, + {"/dir1/dir2", FALSE, FALSE, "/dir1/", NULL}, + {"//dir1/new", TRUE, FALSE, "//dir1/new/dir2/dir3", "dir2/dir3"}, + {"/dir/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88", TRUE, FALSE, "/dir/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88/dir2", "dir2"}, + {"file:///dir1", TRUE, TRUE, "file:///dir1/dir2/dir3/", "dir2/dir3"}, + {"file:///dir1/", TRUE, TRUE, "file:///dir1/dir2/dir3/", "dir2/dir3"}, + {"file:///dir1", TRUE, TRUE, "file:///dir1/dir2/dir3", "dir2/dir3"}, + {"file:///dir1/", TRUE, TRUE, "file:///dir1/dir2/dir3", "dir2/dir3"}, + {"file:///tmp/", FALSE, TRUE, "file:///something/", NULL}, + {"file:///dir1/dir2", FALSE, TRUE, "file:///dir1/", NULL}, + {"file:////dir1/new", TRUE, TRUE, "file:////dir1/new/dir2/dir3", "dir2/dir3"}, + {"file:///dir/UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88", TRUE, TRUE, "file:///dir/UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88/dir2", "dir2"}, +#if 0 + {"dav://www.gtk.org/plan/", TRUE, TRUE, "dav://www.gtk.org/plan/meetings/20071218.txt", "meetings/20071218.txt"}, + {"dav://www.gtk.org/plan/meetings", TRUE, TRUE, "dav://www.gtk.org/plan/meetings/20071218.txt", "20071218.txt"}, +#endif + }; + + guint i; + for (i = 0; i < G_N_ELEMENTS (dirs); i++) + { + char *s = get_relative_path (dirs[i].use_uri, dirs[i].equal, dirs[i].path1, dirs[i].path2); + if (dirs[i].equal) + g_assert_cmpstr (s, ==, dirs[i].path3); + g_free (s); + } +} + +static void +roundtrip_parent_child (const gboolean use_uri, const gboolean under_root_descending, + const char *path, const char *dir_holder) +{ + GFile *files[6] = {NULL}; + guint i; + + if (use_uri) + { + files[0] = g_file_new_for_uri (path); + files[1] = g_file_new_for_uri (path); + } + else + { + files[0] = g_file_new_for_path (path); + files[1] = g_file_new_for_path (path); + } + + g_assert (files[0] != NULL); + g_assert (files[1] != NULL); + + files[2] = g_file_get_child (files[1], dir_holder); + g_assert (files[2] != NULL); + + files[3] = g_file_get_parent (files[2]); + g_assert (files[3] != NULL); + g_assert (g_file_equal (files[3], files[0]) == TRUE); + + files[4] = g_file_get_parent (files[3]); + /* Don't go lower beyond the root */ + if (under_root_descending) + g_assert (files[4] == NULL); + else + { + g_assert (files[4] != NULL); + + files[5] = g_file_get_child (files[4], dir_holder); + g_assert (files[5] != NULL); + g_assert (g_file_equal (files[5], files[0]) == TRUE); + } + + for (i = 0; i < G_N_ELEMENTS (files); i++) + { + if (files[i]) + g_object_unref (files[i]); + } +} + +static void +test_g_file_get_parent_child (void) +{ + const struct TestPathsWithOper paths[] = + { + /* path root_desc uri dir_holder */ + {"/dir1/dir", FALSE, FALSE, "dir"}, + {"/dir", FALSE, FALSE, "dir"}, + {"/", TRUE, FALSE, "dir"}, + {"/UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88/", FALSE, FALSE, "UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88"}, + {"file:///dir1/dir", FALSE, TRUE, "dir"}, + {"file:///dir", FALSE, TRUE, "dir"}, + {"file:///", TRUE, TRUE, "dir"}, + {"file:///UTF-8%20p%C5%99%C3%ADli%C5%A1%20%C5%BElu%C5%A5ou%C4%8Dk%C3%BD%20k%C5%AF%C5%88/", FALSE, TRUE, "UTF-8 p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88"}, + {"dav://www.gtk.org/plan/meetings", FALSE, TRUE, "meetings"}, + }; + + guint i; + for (i = 0; i < G_N_ELEMENTS (paths); i++) + roundtrip_parent_child (paths[i].use_uri, paths[i].equal, paths[i].path1, paths[i].path2); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + + /* Testing whether g_file_new_for_path() or g_file_new_for_uri() always returns non-NULL result */ + g_test_add_func ("/g-file/test_g_file_new_null", test_g_file_new_null); + + /* Testing whether the g_file_new_for_path() correctly canonicalizes strings and two files equals (g_file_equal()) */ + g_test_add_func ("/g-file/test_g_file_new_for_path", test_g_file_new_for_path); + + /* Testing whether the g_file_new_for_uri() correctly canonicalizes strings and two files equals (g_file_equal()) */ + g_test_add_func ("/g-file/test_g_file_new_for_uri", test_g_file_new_for_uri); + + /* Testing g_file_dup() equals original file via g_file_equal() */ + g_test_add_func ("/g-file/test_g_file_dup", test_g_file_dup); + + /* Testing g_file_get_parse_name() to return correct UTF-8 string */ + g_test_add_func ("/g-file/test_g_file_get_parse_name_utf8", test_g_file_get_parse_name_utf8); + + /* Testing g_file_new_for_commandline_arg() for correct relavive path resolution and correct path/URI guess */ + g_test_add_func ("/g-file/test_g_file_new_for_commandline_arg", test_g_file_new_for_commandline_arg); + + /* Testing g_file_has_prefix(), g_file_get_relative_path() and g_file_resolve_relative_path() to return and process correct relative paths */ + g_test_add_func ("/g-file/test_g_file_has_prefix", test_g_file_has_prefix); + + /* Testing g_file_get_parent() and g_file_get_child() */ + g_test_add_func ("/g-file/test_g_file_get_parent_child", test_g_file_get_parent_child); + + return g_test_run(); +} diff --git a/tests/gio/g-icon.c b/tests/gio/g-icon.c new file mode 100644 index 000000000..5fe457134 --- /dev/null +++ b/tests/gio/g-icon.c @@ -0,0 +1,245 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + * Authors: David Zeuthen <davidz@redhat.com> + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +static void +test_g_icon_serialize (void) +{ + GIcon *icon; + GIcon *icon2; + GIcon *icon3; + GIcon *icon4; + GIcon *icon5; + GEmblem *emblem1; + GEmblem *emblem2; + const char *uri; + GFile *location; + char *data; + GError *error; + + error = NULL; + + /* check that GFileIcon and GThemedIcon serialize to the encoding specified */ + + uri = "file:///some/native/path/to/an/icon.png"; + location = g_file_new_for_uri (uri); + icon = g_file_icon_new (location); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "/some/native/path/to/an/icon.png"); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + g_object_unref (location); + + uri = "file:///some/native/path/to/an/icon with spaces.png"; + location = g_file_new_for_uri (uri); + icon = g_file_icon_new (location); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "/some/native/path/to/an/icon with spaces.png"); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + g_object_unref (location); + + uri = "sftp:///some/non-native/path/to/an/icon.png"; + location = g_file_new_for_uri (uri); + icon = g_file_icon_new (location); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "sftp:///some/non-native/path/to/an/icon.png"); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + g_object_unref (location); + +#if 0 + uri = "sftp:///some/non-native/path/to/an/icon with spaces.png"; + location = g_file_new_for_uri (uri); + icon = g_file_icon_new (location); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "sftp:///some/non-native/path/to/an/icon%20with%20spaces.png"); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + g_object_unref (location); +#endif + + icon = g_themed_icon_new ("network-server"); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "network-server"); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + + /* Check that we can serialize from well-known specified formats */ + icon = g_icon_new_for_string ("network-server%", &error); + g_assert_no_error (error); + icon2 = g_themed_icon_new ("network-server%"); + g_assert (g_icon_equal (icon, icon2)); + g_object_unref (icon); + g_object_unref (icon2); + + icon = g_icon_new_for_string ("/path/to/somewhere.png", &error); + g_assert_no_error (error); + location = g_file_new_for_commandline_arg ("/path/to/somewhere.png"); + icon2 = g_file_icon_new (location); + g_assert (g_icon_equal (icon, icon2)); + g_object_unref (icon); + g_object_unref (icon2); + g_object_unref (location); + + icon = g_icon_new_for_string ("/path/to/somewhere with whitespace.png", &error); + g_assert_no_error (error); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "/path/to/somewhere with whitespace.png"); + g_free (data); + location = g_file_new_for_commandline_arg ("/path/to/somewhere with whitespace.png"); + icon2 = g_file_icon_new (location); + g_assert (g_icon_equal (icon, icon2)); + g_object_unref (location); + g_object_unref (icon2); + location = g_file_new_for_commandline_arg ("/path/to/somewhere%20with%20whitespace.png"); + icon2 = g_file_icon_new (location); + g_assert (!g_icon_equal (icon, icon2)); + g_object_unref (location); + g_object_unref (icon2); + g_object_unref (icon); + + icon = g_icon_new_for_string ("sftp:///path/to/somewhere.png", &error); + g_assert_no_error (error); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "sftp:///path/to/somewhere.png"); + g_free (data); + location = g_file_new_for_commandline_arg ("sftp:///path/to/somewhere.png"); + icon2 = g_file_icon_new (location); + g_assert (g_icon_equal (icon, icon2)); + g_object_unref (icon); + g_object_unref (icon2); + g_object_unref (location); + +#if 0 + icon = g_icon_new_for_string ("sftp:///path/to/somewhere with whitespace.png", &error); + g_assert_no_error (error); + data = g_icon_to_string (icon); + g_assert_cmpstr (data, ==, "sftp:///path/to/somewhere%20with%20whitespace.png"); + g_free (data); + location = g_file_new_for_commandline_arg ("sftp:///path/to/somewhere with whitespace.png"); + icon2 = g_file_icon_new (location); + g_assert (g_icon_equal (icon, icon2)); + g_object_unref (location); + g_object_unref (icon2); + location = g_file_new_for_commandline_arg ("sftp:///path/to/somewhere%20with%20whitespace.png"); + icon2 = g_file_icon_new (location); + g_assert (g_icon_equal (icon, icon2)); + g_object_unref (location); + g_object_unref (icon2); + g_object_unref (icon); +#endif + + /* Check that GThemedIcon serialization works */ + + icon = g_themed_icon_new ("network-server"); + g_themed_icon_append_name (G_THEMED_ICON (icon), "computer"); + data = g_icon_to_string (icon); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + + icon = g_themed_icon_new ("icon name with whitespace"); + g_themed_icon_append_name (G_THEMED_ICON (icon), "computer"); + data = g_icon_to_string (icon); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + + icon = g_themed_icon_new_with_default_fallbacks ("network-server-xyz"); + g_themed_icon_append_name (G_THEMED_ICON (icon), "computer"); + data = g_icon_to_string (icon); + icon2 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon, icon2)); + g_free (data); + g_object_unref (icon); + g_object_unref (icon2); + + /* Check that GEmblemedIcon serialization works */ + + icon = g_themed_icon_new ("face-smirk"); + icon2 = g_themed_icon_new ("emblem-important"); + g_themed_icon_append_name (G_THEMED_ICON (icon2), "emblem-shared"); + location = g_file_new_for_uri ("file:///some/path/somewhere.png"); + icon3 = g_file_icon_new (location); + g_object_unref (location); + emblem1 = g_emblem_new_with_origin (icon2, G_EMBLEM_ORIGIN_DEVICE); + emblem2 = g_emblem_new_with_origin (icon3, G_EMBLEM_ORIGIN_LIVEMETADATA); + icon4 = g_emblemed_icon_new (icon, emblem1); + g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (icon4), emblem2); + data = g_icon_to_string (icon4); + icon5 = g_icon_new_for_string (data, &error); + g_assert_no_error (error); + g_assert (g_icon_equal (icon4, icon5)); + g_object_unref (emblem1); + g_object_unref (emblem2); + g_object_unref (icon); + g_object_unref (icon2); + g_object_unref (icon3); + g_object_unref (icon4); + g_object_unref (icon5); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/g-icon/serialize", test_g_icon_serialize); + + return g_test_run(); +} diff --git a/tests/gio/httpd.c b/tests/gio/httpd.c new file mode 100644 index 000000000..c3aaf33bb --- /dev/null +++ b/tests/gio/httpd.c @@ -0,0 +1,183 @@ +#include <gio/gio.h> +#include <string.h> + +static int port = 8080; +static char *root = NULL; +static GOptionEntry cmd_entries[] = { + {"port", 'p', 0, G_OPTION_ARG_INT, &port, + "Local port to bind to", NULL}, + {NULL} +}; + +static void +send_error (GOutputStream *out, + int error_code, + const char *reason) +{ + char *res; + + res = g_strdup_printf ("HTTP/1.0 %d %s\r\n\r\n" + "<html><head><title>%d %s</title></head>" + "<body>%s</body></html>", + error_code, reason, + error_code, reason, + reason); + g_output_stream_write_all (out, res, strlen (res), NULL, NULL, NULL); + g_free (res); +} + +static gboolean +handler (GThreadedSocketService *service, + GSocketConnection *connection, + GSocketListener *listener, + gpointer user_data) +{ + GOutputStream *out; + GInputStream *in; + GFileInputStream *file_in; + GDataInputStream *data; + char *line, *escaped, *tmp, *query, *unescaped, *path, *version; + GFile *f; + GError *error; + GFileInfo *info; + GString *s; + + in = g_io_stream_get_input_stream (G_IO_STREAM (connection)); + out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); + + data = g_data_input_stream_new (in); + /* Be tolerant of input */ + g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_ANY); + + line = g_data_input_stream_read_line (data, NULL, NULL, NULL); + + if (line == NULL) + { + send_error (out, 400, "Invalid request"); + goto out; + } + + if (!g_str_has_prefix (line, "GET ")) + { + send_error (out, 501, "Only GET implemented"); + goto out; + } + + escaped = line + 4; /* Skip "GET " */ + + version = NULL; + tmp = strchr (escaped, ' '); + if (tmp != NULL) + { + *tmp = 0; + version = tmp + 1; + } + + query = strchr (escaped, '?'); + if (query != NULL) + *query++ = 0; + + unescaped = g_uri_unescape_string (escaped, NULL); + path = g_build_filename (root, unescaped, NULL); + g_free (unescaped); + f = g_file_new_for_path (path); + g_free (path); + + error = NULL; + file_in = g_file_read (f, NULL, &error); + if (file_in == NULL) + { + send_error (out, 404, error->message); + g_error_free (error); + goto out; + } + + s = g_string_new ("HTTP/1.0 200 OK\r\n"); + info = g_file_input_stream_query_info (file_in, + G_FILE_ATTRIBUTE_STANDARD_SIZE "," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + NULL, NULL); + if (info) + { + const char *content_type; + char *mime_type; + + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) + g_string_append_printf (s, "Content-Length: %"G_GINT64_FORMAT"\r\n", + g_file_info_get_size (info)); + content_type = g_file_info_get_content_type (info); + if (content_type) + { + mime_type = g_content_type_get_mime_type (content_type); + if (mime_type) + { + g_string_append_printf (s, "Content-Type: %s\r\n", + mime_type); + g_free (mime_type); + } + } + } + g_string_append (s, "\r\n"); + + if (g_output_stream_write_all (out, + s->str, s->len, + NULL, NULL, NULL)) + { + g_output_stream_splice (out, + G_INPUT_STREAM (file_in), + 0, NULL, NULL); + } + g_string_free (s, TRUE); + + g_input_stream_close (G_INPUT_STREAM (file_in), NULL, NULL); + g_object_unref (file_in); + + out: + g_object_unref (data); + + return TRUE; +} + +int +main (int argc, char *argv[]) +{ + GSocketService *service; + GOptionContext *context; + GError *error = NULL; + + g_type_init (); + g_thread_init (NULL); + + context = g_option_context_new ("<http root dir> - Simple HTTP server"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (argc != 2) + { + g_printerr ("Root directory not specified\n"); + return 1; + } + + root = g_strdup (argv[1]); + + service = g_threaded_socket_service_new (10); + if (!g_socket_listener_add_inet_port (G_SOCKET_LISTENER (service), + port, + NULL, + &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + g_print ("Http server listening on port %d\n", port); + + g_signal_connect (service, "run", G_CALLBACK (handler), NULL); + + g_main_loop_run (g_main_loop_new (NULL, FALSE)); + g_assert_not_reached (); +} diff --git a/tests/gio/live-g-file.c b/tests/gio/live-g-file.c new file mode 100644 index 000000000..e0f9bfff1 --- /dev/null +++ b/tests/gio/live-g-file.c @@ -0,0 +1,1211 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc. + * Authors: Tomas Bzatek <tbzatek@redhat.com> + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#define DEFAULT_TEST_DIR "testdir_live-g-file" + +#define PATTERN_FILE_SIZE 0x10000 +#define TEST_HANDLE_SPECIAL TRUE + +enum StructureExtraFlags +{ + TEST_DELETE_NORMAL = 1 << 0, + TEST_DELETE_TRASH = 1 << 1, + TEST_DELETE_NON_EMPTY = 1 << 2, + TEST_DELETE_FAILURE = 1 << 3, + TEST_NOT_EXISTS = 1 << 4, + TEST_ENUMERATE_FILE = 1 << 5, + TEST_NO_ACCESS = 1 << 6, + TEST_COPY = 1 << 7, + TEST_MOVE = 1 << 8, + TEST_COPY_ERROR_RECURSE = 1 << 9, + TEST_ALREADY_EXISTS = 1 << 10, + TEST_TARGET_IS_FILE = 1 << 11, + TEST_CREATE = 1 << 12, + TEST_REPLACE = 1 << 13, + TEST_APPEND = 1 << 14, + TEST_OPEN = 1 << 15, + TEST_OVERWRITE = 1 << 16, + TEST_INVALID_SYMLINK = 1 << 17, +}; + +struct StructureItem +{ + const char *filename; + const char *link_to; + GFileType file_type; + GFileCreateFlags create_flags; + guint32 mode; + gboolean handle_special; + enum StructureExtraFlags extra_flags; +}; + +#define TEST_DIR_NO_ACCESS "dir_no-access" +#define TEST_DIR_NO_WRITE "dir_no-write" +#define TEST_DIR_TARGET "dir-target" +#define TEST_NAME_NOT_EXISTS "not_exists" +#define TEST_TARGET_FILE "target-file" + + +static const struct StructureItem sample_struct[] = { +/* filename link file_type create_flags mode | handle_special | extra_flags */ + {"dir1", NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_DELETE_NORMAL | TEST_DELETE_NON_EMPTY | TEST_REPLACE | TEST_OPEN}, + {"dir1/subdir", NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_COPY_ERROR_RECURSE | TEST_APPEND}, + {"dir2", NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_DELETE_NORMAL | TEST_MOVE | TEST_CREATE}, + {TEST_DIR_TARGET, NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_COPY_ERROR_RECURSE}, + {TEST_DIR_NO_ACCESS, NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_PRIVATE, S_IRUSR + S_IWUSR + S_IRGRP + S_IWGRP + S_IROTH + S_IWOTH, 0, TEST_NO_ACCESS | TEST_OPEN}, + {TEST_DIR_NO_WRITE, NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_PRIVATE, S_IRUSR + S_IXUSR + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH, 0, 0}, + {TEST_TARGET_FILE, NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_OPEN}, + {"normal_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_ENUMERATE_FILE | TEST_CREATE | TEST_OVERWRITE}, + {"normal_file-symlink", "normal_file", G_FILE_TYPE_SYMBOLIC_LINK, G_FILE_CREATE_NONE, 0, 0, TEST_ENUMERATE_FILE | TEST_COPY | TEST_OPEN}, + {"executable_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, S_IRWXU + S_IRWXG + S_IRWXO, 0, TEST_DELETE_TRASH | TEST_COPY | TEST_OPEN | TEST_OVERWRITE | TEST_REPLACE}, + {"private_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_PRIVATE, 0, 0, TEST_COPY | TEST_OPEN | TEST_OVERWRITE | TEST_APPEND}, + {"normal_file2", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_OVERWRITE | TEST_REPLACE}, + {"readonly_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, S_IRUSR + S_IRGRP + S_IROTH, 0, TEST_DELETE_NORMAL | TEST_OPEN}, + {"UTF_pr\xcc\x8ci\xcc\x81lis\xcc\x8c z", + NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_CREATE | TEST_OPEN | TEST_OVERWRITE}, + {"dir_pr\xcc\x8ci\xcc\x81lis\xcc\x8c z", + NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_DELETE_NORMAL | TEST_CREATE}, + {"pattern_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_COPY | TEST_OPEN | TEST_APPEND}, + {TEST_NAME_NOT_EXISTS, NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_DELETE_NORMAL | TEST_NOT_EXISTS | TEST_COPY | TEST_OPEN}, + {TEST_NAME_NOT_EXISTS, NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_DELETE_TRASH | TEST_NOT_EXISTS | TEST_MOVE}, + {"not_exists2", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_NOT_EXISTS | TEST_CREATE}, + {"not_exists3", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_NOT_EXISTS | TEST_REPLACE}, + {"not_exists4", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_NOT_EXISTS | TEST_APPEND}, + {"dir_no-execute/file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_DELETE_NORMAL | TEST_DELETE_FAILURE | TEST_NOT_EXISTS | TEST_OPEN}, + {"lost_symlink", "nowhere", G_FILE_TYPE_SYMBOLIC_LINK, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_DELETE_NORMAL | TEST_OPEN | TEST_INVALID_SYMLINK}, + }; + +static gboolean write_test; +static gboolean verbose; +static gboolean posix_compat; + +#ifdef G_HAVE_ISO_VARARGS +#define log(...) if (verbose) g_print (__VA_ARGS__) +#elif defined(G_HAVE_GNUC_VARARGS) +#define log(msg...) if (verbose) g_print (msg) +#else /* no varargs macros */ +static void log (const g_char *format, ...) +{ + va_list args; + va_start (args, format); + if (verbose) g_print (format, args); + va_end (args); +} +#endif + +static GFile * +create_empty_file (GFile * parent, const char *filename, + GFileCreateFlags create_flags) +{ + GFile *child; + gboolean res; + GError *error; + GFileOutputStream *outs; + + child = g_file_get_child (parent, filename); + g_assert (child != NULL); + + error = NULL; + outs = g_file_replace (child, NULL, FALSE, create_flags, NULL, &error); + g_assert_no_error (error); + g_assert (outs != NULL); + error = NULL; + res = g_output_stream_close (G_OUTPUT_STREAM (outs), NULL, &error); + g_object_unref (outs); + return child; +} + +static GFile * +create_empty_dir (GFile * parent, const char *filename) +{ + GFile *child; + gboolean res; + GError *error; + + child = g_file_get_child (parent, filename); + g_assert (child != NULL); + error = NULL; + res = g_file_make_directory (child, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + return child; +} + +static GFile * +create_symlink (GFile * parent, const char *filename, const char *points_to) +{ + GFile *child; + gboolean res; + GError *error; + + child = g_file_get_child (parent, filename); + g_assert (child != NULL); + error = NULL; + res = g_file_make_symbolic_link (child, points_to, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + return child; +} + +static void +test_create_structure (gconstpointer test_data) +{ + GFile *root; + GFile *child; + gboolean res; + GError *error; + GFileOutputStream *outs; + GDataOutputStream *outds; + guint i; + struct StructureItem item; + + g_assert (test_data != NULL); + log ("\n Going to create testing structure in '%s'...\n", + (char *) test_data); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + + /* create root directory */ + res = g_file_make_directory (root, NULL, NULL); + /* don't care about errors here */ + + /* create any other items */ + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + item = sample_struct[i]; + if ((item.handle_special) + || ((!posix_compat) + && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK))) + continue; + + child = NULL; + switch (item.file_type) + { + case G_FILE_TYPE_REGULAR: + log (" Creating file '%s'...\n", item.filename); + child = create_empty_file (root, item.filename, item.create_flags); + break; + case G_FILE_TYPE_DIRECTORY: + log (" Creating directory '%s'...\n", item.filename); + child = create_empty_dir (root, item.filename); + break; + case G_FILE_TYPE_SYMBOLIC_LINK: + log (" Creating symlink '%s' --> '%s'...\n", item.filename, + item.link_to); + child = create_symlink (root, item.filename, item.link_to); + break; + case G_FILE_TYPE_UNKNOWN: + case G_FILE_TYPE_SPECIAL: + case G_FILE_TYPE_SHORTCUT: + case G_FILE_TYPE_MOUNTABLE: + default: + break; + } + g_assert (child != NULL); + + if ((item.mode > 0) && (posix_compat)) + { + error = NULL; + res = + g_file_set_attribute_uint32 (child, G_FILE_ATTRIBUTE_UNIX_MODE, + item.mode, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + } + + g_object_unref (child); + } + + /* create a pattern file */ + log (" Creating pattern file..."); + child = g_file_get_child (root, "pattern_file"); + g_assert (child != NULL); + + error = NULL; + outs = + g_file_replace (child, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error); + g_assert_no_error (error); + + g_assert (outs != NULL); + outds = g_data_output_stream_new (G_OUTPUT_STREAM (outs)); + g_assert (outds != NULL); + for (i = 0; i < PATTERN_FILE_SIZE; i++) + { + error = NULL; + res = g_data_output_stream_put_byte (outds, i % 256, NULL, &error); + g_assert_no_error (error); + } + error = NULL; + res = g_output_stream_close (G_OUTPUT_STREAM (outs), NULL, &error); + g_assert_no_error (error); + g_object_unref (outds); + g_object_unref (outs); + g_object_unref (child); + log (" done.\n"); + + g_object_unref (root); +} + +static GFile * +file_exists (GFile * parent, const char *filename, gboolean * result) +{ + GFile *child; + gboolean res; + + if (result) + *result = FALSE; + + child = g_file_get_child (parent, filename); + g_assert (child != NULL); + res = g_file_query_exists (child, NULL); + if (result) + *result = res; + + return child; +} + +static void +test_attributes (struct StructureItem item, GFileInfo * info) +{ + GFileType ftype; + guint32 mode; + const char *name, *display_name, *edit_name, *copy_name, *symlink_target; + gboolean utf8_valid; + gboolean has_attr; + gboolean is_symlink; + gboolean can_read, can_write; + + /* standard::type */ + has_attr = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE); + g_assert_cmpint (has_attr, ==, TRUE); + ftype = g_file_info_get_file_type (info); + g_assert_cmpint (ftype, !=, G_FILE_TYPE_UNKNOWN); + g_assert_cmpint (ftype, ==, item.file_type); + + /* unix::mode */ + if ((item.mode > 0) && (posix_compat)) + { + mode = + g_file_info_get_attribute_uint32 (info, + G_FILE_ATTRIBUTE_UNIX_MODE) & 0xFFF; + g_assert_cmpint (mode, ==, item.mode); + } + + /* access::can-read */ + if (item.file_type != G_FILE_TYPE_SYMBOLIC_LINK) + { + can_read = + g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_READ); + g_assert_cmpint (can_read, ==, TRUE); + } + + /* access::can-write */ + if ((write_test) && ((item.extra_flags & TEST_OVERWRITE) == TEST_OVERWRITE)) + { + can_write = + g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); + g_assert_cmpint (can_write, ==, TRUE); + } + + /* standard::name */ + name = g_file_info_get_name (info); + g_assert (name != NULL); + + /* standard::display-name */ + display_name = g_file_info_get_display_name (info); + g_assert (display_name != NULL); + utf8_valid = g_utf8_validate (display_name, -1, NULL); + g_assert_cmpint (utf8_valid, ==, TRUE); + + /* standard::edit-name */ + edit_name = g_file_info_get_edit_name (info); + if (edit_name) + { + utf8_valid = g_utf8_validate (edit_name, -1, NULL); + g_assert_cmpint (utf8_valid, ==, TRUE); + } + + /* standard::copy-name */ + copy_name = + g_file_info_get_attribute_string (info, + G_FILE_ATTRIBUTE_STANDARD_COPY_NAME); + if (copy_name) + { + utf8_valid = g_utf8_validate (copy_name, -1, NULL); + g_assert_cmpint (utf8_valid, ==, TRUE); + } + + /* standard::is-symlink */ + if (posix_compat) + { + is_symlink = g_file_info_get_is_symlink (info); + g_assert_cmpint (is_symlink, ==, + item.file_type == G_FILE_TYPE_SYMBOLIC_LINK); + } + + /* standard::symlink-target */ + if ((item.file_type == G_FILE_TYPE_SYMBOLIC_LINK) && (posix_compat)) + { + symlink_target = g_file_info_get_symlink_target (info); + g_assert_cmpstr (symlink_target, ==, item.link_to); + } +} + +static void +test_initial_structure (gconstpointer test_data) +{ + GFile *root; + GFile *child; + gboolean res; + GError *error; + GFileInputStream *ins; + guint i; + GFileInfo *info; + guint32 size; + guchar *buffer; + gssize read, total_read; + struct StructureItem item; + + + g_assert (test_data != NULL); + log ("\n Testing sample structure in '%s'...\n", (char *) test_data); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + res = g_file_query_exists (root, NULL); + g_assert_cmpint (res, ==, TRUE); + + /* test the structure */ + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + item = sample_struct[i]; + if (((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK)) + || (item.handle_special)) + continue; + + log (" Testing file '%s'...\n", item.filename); + + child = file_exists (root, item.filename, &res); + g_assert (child != NULL); + g_assert_cmpint (res, ==, TRUE); + + error = NULL; + info = + g_file_query_info (child, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, &error); + g_assert_no_error (error); + g_assert (info != NULL); + + test_attributes (item, info); + + g_object_unref (child); + } + + /* read and test the pattern file */ + log (" Testing pattern file...\n"); + child = file_exists (root, "pattern_file", &res); + g_assert (child != NULL); + g_assert_cmpint (res, ==, TRUE); + + error = NULL; + info = + g_file_query_info (child, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, + &error); + g_assert_no_error (error); + g_assert (info != NULL); + size = g_file_info_get_size (info); + g_assert_cmpint (size, ==, PATTERN_FILE_SIZE); + + error = NULL; + ins = g_file_read (child, NULL, &error); + g_assert (ins != NULL); + g_assert_no_error (error); + + buffer = g_malloc (PATTERN_FILE_SIZE); + total_read = 0; + + while (total_read < PATTERN_FILE_SIZE) + { + error = NULL; + read = + g_input_stream_read (G_INPUT_STREAM (ins), buffer + total_read, + PATTERN_FILE_SIZE, NULL, &error); + g_assert_no_error (error); + total_read += read; + log (" read %"G_GSSIZE_FORMAT" bytes, total = %"G_GSSIZE_FORMAT" of %d.\n", + read, total_read, PATTERN_FILE_SIZE); + } + g_assert_cmpint (total_read, ==, PATTERN_FILE_SIZE); + + error = NULL; + res = g_input_stream_close (G_INPUT_STREAM (ins), NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (res, ==, TRUE); + + for (i = 0; i < PATTERN_FILE_SIZE; i++) + g_assert_cmpint (*(buffer + i), ==, i % 256); + + g_object_unref (ins); + g_object_unref (child); + g_free (buffer); + g_object_unref (root); +} + +static void +traverse_recurse_dirs (GFile * parent, GFile * root) +{ + gboolean res; + GError *error; + GFileEnumerator *enumerator; + GFileInfo *info; + GFile *descend; + char *relative_path; + guint i; + gboolean found; + + g_assert (root != NULL); + + error = NULL; + enumerator = + g_file_enumerate_children (parent, "*", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, + &error); + g_assert (enumerator != NULL); + g_assert_no_error (error); + + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + while ((info) && (!error)) + { + descend = g_file_get_child (parent, g_file_info_get_name (info)); + g_assert (descend != NULL); + relative_path = g_file_get_relative_path (root, descend); + g_assert (relative_path != NULL); + + found = FALSE; + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + if (strcmp (sample_struct[i].filename, relative_path) == 0) + { + /* test the attributes again */ + test_attributes (sample_struct[i], info); + + found = TRUE; + break; + } + } + g_assert_cmpint (found, ==, TRUE); + + log (" Found file %s, relative to root: %s\n", + g_file_info_get_display_name (info), relative_path); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + traverse_recurse_dirs (descend, root); + + g_object_unref (descend); + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + } + g_assert_no_error (error); + + error = NULL; + res = g_file_enumerator_close (enumerator, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); +} + +static void +test_traverse_structure (gconstpointer test_data) +{ + GFile *root; + gboolean res; + + g_assert (test_data != NULL); + log ("\n Traversing through the sample structure in '%s'...\n", + (char *) test_data); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + res = g_file_query_exists (root, NULL); + g_assert_cmpint (res, ==, TRUE); + + traverse_recurse_dirs (root, root); + + g_object_unref (root); +} + + + + +static void +test_enumerate (gconstpointer test_data) +{ + GFile *root, *child; + gboolean res; + GError *error; + GFileEnumerator *enumerator; + GFileInfo *info; + guint i; + struct StructureItem item; + + + g_assert (test_data != NULL); + log ("\n Test enumerate '%s'...\n", (char *) test_data); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + res = g_file_query_exists (root, NULL); + g_assert_cmpint (res, ==, TRUE); + + + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + item = sample_struct[i]; + if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK)) + continue; + + if (((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) || + (((item.extra_flags & TEST_NO_ACCESS) == TEST_NO_ACCESS) + && posix_compat) + || ((item.extra_flags & TEST_ENUMERATE_FILE) == + TEST_ENUMERATE_FILE)) + { + log (" Testing file '%s'\n", item.filename); + child = g_file_get_child (root, item.filename); + g_assert (child != NULL); + error = NULL; + enumerator = + g_file_enumerate_children (child, "*", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, &error); + + if ((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) + { + g_assert (enumerator == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + } + if ((item.extra_flags & TEST_ENUMERATE_FILE) == TEST_ENUMERATE_FILE) + { + g_assert (enumerator == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY); + } + if ((item.extra_flags & TEST_NO_ACCESS) == TEST_NO_ACCESS) + { + g_assert (enumerator != NULL); + + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + g_assert (info == NULL); + g_assert_no_error (error); + /* no items should be found, no error should be logged */ + } + + if (error) + g_error_free (error); + + if (enumerator) + { + error = NULL; + res = g_file_enumerator_close (enumerator, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + } + g_object_unref (child); + } + } + g_object_unref (root); +} + +static void +do_copy_move (GFile * root, struct StructureItem item, const char *target_dir, + enum StructureExtraFlags extra_flags) +{ + GFile *dst_dir, *src_file, *dst_file; + gboolean res; + GError *error; + + log (" do_copy_move: '%s' --> '%s'\n", item.filename, target_dir); + + dst_dir = g_file_get_child (root, target_dir); + g_assert (dst_dir != NULL); + src_file = g_file_get_child (root, item.filename); + g_assert (src_file != NULL); + dst_file = g_file_get_child (dst_dir, item.filename); + g_assert (dst_file != NULL); + + error = NULL; + if ((item.extra_flags & TEST_COPY) == TEST_COPY) + res = + g_file_copy (src_file, dst_file, + G_FILE_COPY_NOFOLLOW_SYMLINKS | + ((extra_flags == + TEST_OVERWRITE) ? G_FILE_COPY_OVERWRITE : + G_FILE_COPY_NONE), NULL, NULL, NULL, &error); + else + res = + g_file_move (src_file, dst_file, G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL, + NULL, NULL, &error); + + if (error) + log (" res = %d, error code %d = %s\n", res, error->code, + error->message); + + /* copying file/directory to itself (".") */ + if (((item.extra_flags & TEST_NOT_EXISTS) != TEST_NOT_EXISTS) && + (extra_flags == TEST_ALREADY_EXISTS)) + { + g_assert_cmpint (res, ==, FALSE); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + } + /* target file is a file, overwrite is not set */ + else if (((item.extra_flags & TEST_NOT_EXISTS) != TEST_NOT_EXISTS) && + (extra_flags == TEST_TARGET_IS_FILE)) + { + g_assert_cmpint (res, ==, FALSE); + if (item.file_type == G_FILE_TYPE_DIRECTORY) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE); + else + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY); + } + /* source file is directory */ + else if ((item.extra_flags & TEST_COPY_ERROR_RECURSE) == + TEST_COPY_ERROR_RECURSE) + { + g_assert_cmpint (res, ==, FALSE); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE); + } + /* source or target path doesn't exist */ + else if (((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) || + (extra_flags == TEST_NOT_EXISTS)) + { + g_assert_cmpint (res, ==, FALSE); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + } + /* source or target path permission denied */ + else if (((item.extra_flags & TEST_NO_ACCESS) == TEST_NO_ACCESS) || + (extra_flags == TEST_NO_ACCESS)) + { + g_assert_cmpint (res, ==, FALSE); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED); + } + /* no error should be found, all exceptions defined above */ + else + { + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + } + + if (error) + g_error_free (error); + + + g_object_unref (dst_dir); + g_object_unref (src_file); + g_object_unref (dst_file); +} + +static void +test_copy_move (gconstpointer test_data) +{ + GFile *root; + gboolean res; + guint i; + struct StructureItem item; + + log ("\n"); + + g_assert (test_data != NULL); + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + res = g_file_query_exists (root, NULL); + g_assert_cmpint (res, ==, TRUE); + + + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + item = sample_struct[i]; + + if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK)) + continue; + + if (((item.extra_flags & TEST_COPY) == TEST_COPY) || + ((item.extra_flags & TEST_MOVE) == TEST_MOVE)) + { + /* test copy/move to a directory, expecting no errors if source files exist */ + do_copy_move (root, item, TEST_DIR_TARGET, 0); + + /* some files have been already moved so we can't count with them in the tests */ + if ((item.extra_flags & TEST_COPY) == TEST_COPY) + { + /* test overwrite for flagged files */ + if ((item.extra_flags & TEST_OVERWRITE) == TEST_OVERWRITE) + { + do_copy_move (root, item, TEST_DIR_TARGET, TEST_OVERWRITE); + } + /* source = target, should return G_IO_ERROR_EXISTS */ + do_copy_move (root, item, ".", TEST_ALREADY_EXISTS); + /* target is file */ + do_copy_move (root, item, TEST_TARGET_FILE, + TEST_TARGET_IS_FILE); + /* target path is invalid */ + do_copy_move (root, item, TEST_NAME_NOT_EXISTS, + TEST_NOT_EXISTS); + + /* tests on POSIX-compatible filesystems */ + if (posix_compat) + { + /* target directory is not accessible (no execute flag) */ + do_copy_move (root, item, TEST_DIR_NO_ACCESS, + TEST_NO_ACCESS); + /* target directory is readonly */ + do_copy_move (root, item, TEST_DIR_NO_WRITE, + TEST_NO_ACCESS); + } + } + } + } + g_object_unref (root); +} + +static void +test_create (gconstpointer test_data) +{ + GFile *root, *child; + gboolean res; + GError *error; + guint i; + struct StructureItem item; + GFileOutputStream *os; + + g_assert (test_data != NULL); + log ("\n"); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + res = g_file_query_exists (root, NULL); + g_assert_cmpint (res, ==, TRUE); + + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + item = sample_struct[i]; + + if (((item.extra_flags & TEST_CREATE) == TEST_CREATE) || + ((item.extra_flags & TEST_REPLACE) == TEST_REPLACE) || + ((item.extra_flags & TEST_APPEND) == TEST_APPEND)) + { + log (" test_create: '%s'\n", item.filename); + + child = g_file_get_child (root, item.filename); + g_assert (child != NULL); + error = NULL; + os = NULL; + + if ((item.extra_flags & TEST_CREATE) == TEST_CREATE) + os = g_file_create (child, item.create_flags, NULL, &error); + else if ((item.extra_flags & TEST_REPLACE) == TEST_REPLACE) + os = + g_file_replace (child, NULL, TRUE, item.create_flags, NULL, + &error); + else if ((item.extra_flags & TEST_APPEND) == TEST_APPEND) + os = g_file_append_to (child, item.create_flags, NULL, &error); + + + if (error) + log (" error code %d = %s\n", error->code, error->message); + + if (((item.extra_flags & TEST_NOT_EXISTS) == 0) && + ((item.extra_flags & TEST_CREATE) == TEST_CREATE)) + { + g_assert (os == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + } + else if (item.file_type == G_FILE_TYPE_DIRECTORY) + { + g_assert (os == NULL); + if ((item.extra_flags & TEST_CREATE) == TEST_CREATE) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + else + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY); + } + else + { + g_assert (os != NULL); + g_assert_no_error (error); + } + + if (error) + g_error_free (error); + + if (os) + { + error = NULL; + res = + g_output_stream_close (G_OUTPUT_STREAM (os), NULL, &error); + if (error) + log (" g_output_stream_close: error %d = %s\n", + error->code, error->message); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + } + g_object_unref (child); + } + } + g_object_unref (root); +} + +static void +test_open (gconstpointer test_data) +{ + GFile *root, *child; + gboolean res; + GError *error; + guint i; + struct StructureItem item; + GFileInputStream *input_stream; + + g_assert (test_data != NULL); + log ("\n"); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + res = g_file_query_exists (root, NULL); + g_assert_cmpint (res, ==, TRUE); + + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + item = sample_struct[i]; + + if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK)) + continue; + + if ((item.extra_flags & TEST_OPEN) == TEST_OPEN) + { + log (" test_open: '%s'\n", item.filename); + + child = g_file_get_child (root, item.filename); + g_assert (child != NULL); + error = NULL; + input_stream = g_file_read (child, NULL, &error); + + if (((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) || + ((item.extra_flags & TEST_INVALID_SYMLINK) == + TEST_INVALID_SYMLINK)) + { + g_assert (input_stream == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + } + else if (item.file_type == G_FILE_TYPE_DIRECTORY) + { + g_assert (input_stream == NULL); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY); + } + else + { + g_assert (input_stream != NULL); + g_assert_no_error (error); + } + + if (error) + g_error_free (error); + + if (input_stream) + { + error = NULL; + res = + g_input_stream_close (G_INPUT_STREAM (input_stream), NULL, + &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + } + g_object_unref (child); + } + } + g_object_unref (root); +} + +static void +test_delete (gconstpointer test_data) +{ + GFile *root; + GFile *child; + gboolean res; + GError *error; + guint i; + struct StructureItem item; + + g_assert (test_data != NULL); + log ("\n"); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + res = g_file_query_exists (root, NULL); + g_assert_cmpint (res, ==, TRUE); + + for (i = 0; i < G_N_ELEMENTS (sample_struct); i++) + { + item = sample_struct[i]; + + if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK)) + continue; + + if (((item.extra_flags & TEST_DELETE_NORMAL) == TEST_DELETE_NORMAL) || + ((item.extra_flags & TEST_DELETE_TRASH) == TEST_DELETE_TRASH)) + { + child = file_exists (root, item.filename, &res); + g_assert (child != NULL); + /* we don't care about result here */ + + log (" Deleting %s, path = %s\n", item.filename, + g_file_get_path (child)); + error = NULL; + if ((item.extra_flags & TEST_DELETE_NORMAL) == TEST_DELETE_NORMAL) + res = g_file_delete (child, NULL, &error); + else + res = g_file_trash (child, NULL, &error); + + if ((item.extra_flags & TEST_DELETE_NON_EMPTY) == + TEST_DELETE_NON_EMPTY) + { + g_assert_cmpint (res, ==, FALSE); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY); + } + if ((item.extra_flags & TEST_DELETE_FAILURE) == TEST_DELETE_FAILURE) + { + g_assert_cmpint (res, ==, FALSE); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + } + if ((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) + { + g_assert_cmpint (res, ==, FALSE); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND); + } + + if (error) + { + log (" result = %d, error = %s\n", res, error->message); + g_error_free (error); + } + + g_object_unref (child); + } + } + g_object_unref (root); +} + + +static void +cleanup_dir_recurse (GFile *parent, GFile *root) +{ + gboolean res; + GError *error; + GFileEnumerator *enumerator; + GFileInfo *info; + GFile *descend; + char *relative_path; + + g_assert (root != NULL); + + error = NULL; + enumerator = + g_file_enumerate_children (parent, "*", + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, + &error); + if (! enumerator) + return; + + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + while ((info) && (!error)) + { + descend = g_file_get_child (parent, g_file_info_get_name (info)); + g_assert (descend != NULL); + relative_path = g_file_get_relative_path (root, descend); + g_assert (relative_path != NULL); + + log (" deleting '%s'\n", g_file_info_get_display_name (info)); + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) + cleanup_dir_recurse (descend, root); + + error = NULL; + res = g_file_delete (descend, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + + g_object_unref (descend); + error = NULL; + info = g_file_enumerator_next_file (enumerator, NULL, &error); + } + g_assert_no_error (error); + + error = NULL; + res = g_file_enumerator_close (enumerator, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); +} + +static void +prep_clean_structure (gconstpointer test_data) +{ + GFile *root; + + g_assert (test_data != NULL); + log ("\n Cleaning target testing structure in '%s'...\n", + (char *) test_data); + + root = g_file_new_for_commandline_arg ((char *) test_data); + g_assert (root != NULL); + + cleanup_dir_recurse (root, root); + + g_file_delete (root, NULL, NULL); + + g_object_unref (root); +} + +int +main (int argc, char *argv[]) +{ + static gboolean only_create_struct; + const char *target_path; + GError *error; + GOptionContext *context; + + static GOptionEntry cmd_entries[] = { + {"read-write", 'w', 0, G_OPTION_ARG_NONE, &write_test, + "Perform write tests (incl. structure creation)", NULL}, + {"create-struct", 'c', 0, G_OPTION_ARG_NONE, &only_create_struct, + "Only create testing structure (no tests)", NULL}, + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL}, + {"posix", 'x', 0, G_OPTION_ARG_NONE, &posix_compat, + "Test POSIX-specific features (unix permissions, symlinks)", NULL}, + {NULL} + }; + + verbose = FALSE; + write_test = FALSE; + only_create_struct = FALSE; + target_path = NULL; + posix_compat = FALSE; + + /* strip all gtester-specific args */ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* no extra parameters specified, assume we're executed from glib test suite */ + if (argc < 2) + { + verbose = TRUE; + write_test = TRUE; + only_create_struct = FALSE; + target_path = DEFAULT_TEST_DIR; +#ifdef G_PLATFORM_WIN32 + posix_compat = FALSE; +#else + posix_compat = TRUE; +#endif + } + + /* add trailing args */ + error = NULL; + context = g_option_context_new ("target_path"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_print ("option parsing failed: %s\n", error->message); + return g_test_run (); + } + + /* remaining arg should is the target path; we don't care of the extra args here */ + if (argc >= 2) + target_path = strdup (argv[1]); + + if (! target_path) + { + g_print ("error: target path was not specified\n"); + g_print ("%s", g_option_context_get_help (context, TRUE, NULL)); + return g_test_run (); + } + + + /* Write test - clean target directory first */ + /* this can be also considered as a test - enumerate + delete */ + if (write_test || only_create_struct) + g_test_add_data_func ("/live-g-file/prep_clean_structure", target_path, + prep_clean_structure); + + /* Write test - create new testing structure */ + if (write_test || only_create_struct) + g_test_add_data_func ("/live-g-file/create_structure", target_path, + test_create_structure); + + /* Read test - test the sample structure - expect defined attributes to be there */ + if (!only_create_struct) + g_test_add_data_func ("/live-g-file/test_initial_structure", target_path, + test_initial_structure); + + /* Read test - test traverse the structure - no special file should appear */ + if (!only_create_struct) + g_test_add_data_func ("/live-g-file/test_traverse_structure", target_path, + test_traverse_structure); + + /* Read test - enumerate */ + if (!only_create_struct) + g_test_add_data_func ("/live-g-file/test_enumerate", target_path, + test_enumerate); + + /* Read test - open (g_file_read()) */ + if (!only_create_struct) + g_test_add_data_func ("/live-g-file/test_open", target_path, test_open); + + /* Write test - create */ + if (write_test && (!only_create_struct)) + g_test_add_data_func ("/live-g-file/test_create", target_path, + test_create); + + /* Write test - copy, move */ + if (write_test && (!only_create_struct)) + g_test_add_data_func ("/live-g-file/test_copy_move", target_path, + test_copy_move); + + /* Write test - delete, trash */ + if (write_test && (!only_create_struct)) + g_test_add_data_func ("/live-g-file/test_delete", target_path, + test_delete); + + if (write_test || only_create_struct) + g_test_add_data_func ("/live-g-file/final_clean", target_path, + prep_clean_structure); + + return g_test_run (); + +} diff --git a/tests/gio/live-g-file.txt b/tests/gio/live-g-file.txt new file mode 100644 index 000000000..95da0e0ad --- /dev/null +++ b/tests/gio/live-g-file.txt @@ -0,0 +1,27 @@ +Before you start testing it would be good to explain how it works. + +The script works in three modes: + 1. read-only (no special arguments) - suitable for read-only backends. Just + create the sample structure using the second mode, pack it (tar -p is + preffered to preserve unix modes) and put it on a reachable place. + 2. create-structure - only creates reference structure for later testing + in read-only mode + 3. write mode - full test suite, creates testing structure and performs all + read and write tests. Please note the delete/move tests are included + in this mode and target directory structure is unusable after the script + is finished. + + +To see the list of available parameters just run 'live-g-file --help' + + +Notes: + - it's advised to clean target directory first, otherwise some tests might fail + (i.e. the tests creating testing structure) + + +Tested: + - local filesystem (/tmp/...) + - file:// uri (file:///tmp/...) + - locatest:// gvfs backend (localtest:///tmp/...) + - FAT16 filesystem (no POSIX extensions) diff --git a/tests/gio/memory-input-stream.c b/tests/gio/memory-input-stream.c new file mode 100644 index 000000000..6ffee8683 --- /dev/null +++ b/tests/gio/memory-input-stream.c @@ -0,0 +1,78 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2007 Imendio AB + * Authors: Tim Janik + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +static void +test_read_chunks (void) +{ + const char *data1 = "abcdefghijklmnopqrstuvwxyz"; + const char *data2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const char *result = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char buffer[128]; + gsize bytes_read, pos, len, chunk_size; + GError *error = NULL; + GInputStream *stream; + gboolean res; + + stream = g_memory_input_stream_new (); + + g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream), + data1, -1, NULL); + g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (stream), + data2, -1, NULL); + len = strlen (data1) + strlen (data2); + + for (chunk_size = 1; chunk_size < len - 1; chunk_size++) + { + pos = 0; + while (pos < len) + { + bytes_read = g_input_stream_read (stream, buffer, chunk_size, NULL, &error); + g_assert_no_error (error); + g_assert_cmpint (bytes_read, ==, MIN (chunk_size, len - pos)); + g_assert (strncmp (buffer, result + pos, bytes_read) == 0); + + pos += bytes_read; + } + + g_assert_cmpint (pos, ==, len); + res = g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, &error); + g_assert_cmpint (res, ==, TRUE); + g_assert_no_error (error); + } +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/memory-input-stream/read-chunks", test_read_chunks); + + return g_test_run(); +} diff --git a/tests/gio/memory-output-stream.c b/tests/gio/memory-output-stream.c new file mode 100644 index 000000000..cc1e1ea37 --- /dev/null +++ b/tests/gio/memory-output-stream.c @@ -0,0 +1,100 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc. + * Author: Matthias Clasen + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +static void +test_truncate (void) +{ + GOutputStream *mo; + GDataOutputStream *o; + int i; + GError *error = NULL; + + g_test_bug ("540423"); + + mo = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); + o = g_data_output_stream_new (mo); + for (i = 0; i < 1000; i++) + { + g_data_output_stream_put_byte (o, 1, NULL, &error); + g_assert_no_error (error); + } + g_seekable_truncate (G_SEEKABLE (mo), 0, NULL, &error); + g_assert_no_error (error); + for (i = 0; i < 2000; i++) + { + g_data_output_stream_put_byte (o, 1, NULL, &error); + g_assert_no_error (error); + } + + g_object_unref (o); + g_object_unref (mo); +} + +static void +test_data_size (void) +{ + GOutputStream *mo; + GDataOutputStream *o; + int pos; + + g_test_bug ("540459"); + + mo = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); + o = g_data_output_stream_new (mo); + g_data_output_stream_put_byte (o, 1, NULL, NULL); + pos = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mo)); + g_assert_cmpint (pos, ==, 1); + + g_seekable_seek (G_SEEKABLE (mo), 0, G_SEEK_CUR, NULL, NULL); + pos = g_seekable_tell (G_SEEKABLE (mo)); + g_assert_cmpint (pos, ==, 1); + + g_test_bug ("540461"); + + g_seekable_seek (G_SEEKABLE (mo), 0, G_SEEK_SET, NULL, NULL); + pos = g_seekable_tell (G_SEEKABLE (mo)); + g_assert_cmpint (pos, ==, 0); + + pos = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (mo)); + g_assert_cmpint (pos, ==, 1); + + g_object_unref (o); + g_object_unref (mo); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/"); + + g_test_add_func ("/memory-output-stream/truncate", test_truncate); + g_test_add_func ("/memory-output-stream/get-data-size", test_data_size); + + return g_test_run(); +} diff --git a/tests/gio/readwrite.c b/tests/gio/readwrite.c new file mode 100644 index 000000000..0d561851b --- /dev/null +++ b/tests/gio/readwrite.c @@ -0,0 +1,293 @@ +#include <glib/glib.h> +#include <glib/gstdio.h> +#include <gio/gio.h> +#include <unistd.h> +#include <string.h> + +static const char *original_data = "This is some test data that we can put in a file..."; +static const char *new_data = "new data.."; + +static void +verify_pos (GIOStream *iostream, goffset expected_pos) +{ + goffset pos; + + pos = g_seekable_tell (G_SEEKABLE (iostream)); + g_assert_cmpint (pos, ==, expected_pos); + + pos = g_seekable_tell (G_SEEKABLE (g_io_stream_get_input_stream (iostream))); + g_assert_cmpint (pos, ==, expected_pos); + + pos = g_seekable_tell (G_SEEKABLE (g_io_stream_get_output_stream (iostream))); + g_assert_cmpint (pos, ==, expected_pos); +} + +static void +verify_iostream (GFileIOStream *file_iostream) +{ + gboolean res; + GIOStream *iostream; + GError *error; + GInputStream *in; + GOutputStream *out; + char buffer[1024]; + gsize n_bytes; + char *modified_data; + + iostream = G_IO_STREAM (file_iostream); + + verify_pos (iostream, 0); + + in = g_io_stream_get_input_stream (iostream); + out = g_io_stream_get_output_stream (iostream); + + res = g_input_stream_read_all (in, buffer, 20, &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint ((int)n_bytes, ==, 20); + + g_assert (memcmp (buffer, original_data, 20) == 0); + + verify_pos (iostream, 20); + + res = g_seekable_seek (G_SEEKABLE (iostream), + -10, G_SEEK_END, + NULL, NULL); + g_assert (res); + verify_pos (iostream, strlen (original_data) - 10); + + res = g_input_stream_read_all (in, buffer, 20, &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint ((int)n_bytes, ==, 10); + g_assert (memcmp (buffer, original_data + strlen (original_data) - 10, 10) == 0); + + verify_pos (iostream, strlen (original_data)); + + res = g_seekable_seek (G_SEEKABLE (iostream), + 10, G_SEEK_SET, + NULL, NULL); + + verify_pos (iostream, 10); + + res = g_output_stream_write_all (out, new_data, strlen (new_data), + &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint (n_bytes, ==, strlen (new_data)); + + verify_pos (iostream, 10 + strlen (new_data)); + + res = g_seekable_seek (G_SEEKABLE (iostream), + 0, G_SEEK_SET, + NULL, NULL); + g_assert (res); + verify_pos (iostream, 0); + + res = g_input_stream_read_all (in, buffer, strlen (original_data), &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint ((int)n_bytes, ==, strlen (original_data)); + buffer[n_bytes] = 0; + + modified_data = g_strdup (original_data); + memcpy (modified_data + 10, new_data, strlen (new_data)); + g_assert_cmpstr (buffer, ==, modified_data); + + verify_pos (iostream, strlen (original_data)); + + res = g_seekable_seek (G_SEEKABLE (iostream), + 0, G_SEEK_SET, + NULL, NULL); + g_assert (res); + verify_pos (iostream, 0); + + res = g_output_stream_close (out, NULL, NULL); + g_assert (res); + + res = g_input_stream_read_all (in, buffer, 15, &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint ((int)n_bytes, ==, 15); + g_assert (memcmp (buffer, modified_data, 15) == 0); + + error = NULL; + res = g_output_stream_write_all (out, new_data, strlen (new_data), + &n_bytes, NULL, &error); + g_assert (!res); + g_assert (error != NULL); + g_assert (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)); + + error = NULL; + res = g_io_stream_close (iostream, NULL, &error); + g_assert (res && error == NULL); + + g_free (modified_data); +} + +static void +test_g_file_open_readwrite (void) +{ + char *tmp_file; + int fd; + gboolean res; + GFileIOStream *file_iostream; + char *path; + GFile *file; + GError *error; + + fd = g_file_open_tmp ("readwrite_XXXXXX", + &tmp_file, NULL); + g_assert (fd != -1); + close (fd); + + res = g_file_set_contents (tmp_file, + original_data, -1, NULL); + g_assert (res); + + path = g_build_filename (g_get_home_dir (), "g-a-nonexisting-file", NULL); + file = g_file_new_for_path (path); + g_free (path); + error = NULL; + file_iostream = g_file_open_readwrite (file, NULL, &error); + g_assert (file_iostream == NULL); + g_assert (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)); + g_error_free (error); + g_object_unref (file); + + file = g_file_new_for_path (tmp_file); + error = NULL; + file_iostream = g_file_open_readwrite (file, NULL, &error); + g_assert (file_iostream != NULL); + + verify_iostream (file_iostream); + + g_object_unref (file_iostream); + + g_unlink (tmp_file); + g_free (tmp_file); +} + +static void +test_g_file_create_readwrite (void) +{ + char *tmp_file; + int fd; + gboolean res; + GFileIOStream *file_iostream; + GOutputStream *out; + GFile *file; + GError *error; + gsize n_bytes; + + fd = g_file_open_tmp ("readwrite_XXXXXX", + &tmp_file, NULL); + g_assert (fd != -1); + close (fd); + + file = g_file_new_for_path (tmp_file); + error = NULL; + file_iostream = g_file_create_readwrite (file, 0, NULL, &error); + g_assert (file_iostream == NULL); + g_assert (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)); + + g_unlink (tmp_file); + file_iostream = g_file_create_readwrite (file, 0, NULL, &error); + g_assert (file_iostream != NULL); + + out = g_io_stream_get_output_stream (G_IO_STREAM (file_iostream)); + res = g_output_stream_write_all (out, original_data, strlen (original_data), + &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint (n_bytes, ==, strlen (original_data)); + + res = g_seekable_seek (G_SEEKABLE (file_iostream), + 0, G_SEEK_SET, + NULL, NULL); + g_assert (res); + + verify_iostream (file_iostream); + + g_object_unref (file_iostream); + + g_unlink (tmp_file); + g_free (tmp_file); +} + +static void +test_g_file_replace_readwrite (void) +{ + char *tmp_file, *backup, *data; + int fd; + gboolean res; + GFileIOStream *file_iostream; + GInputStream *in; + GOutputStream *out; + GFile *file; + GError *error; + char buffer[1024]; + gsize n_bytes; + + fd = g_file_open_tmp ("readwrite_XXXXXX", + &tmp_file, NULL); + g_assert (fd != -1); + close (fd); + + res = g_file_set_contents (tmp_file, + new_data, -1, NULL); + g_assert (res); + + file = g_file_new_for_path (tmp_file); + error = NULL; + file_iostream = g_file_replace_readwrite (file, NULL, + TRUE, 0, NULL, &error); + g_assert (file_iostream != NULL); + + in = g_io_stream_get_input_stream (G_IO_STREAM (file_iostream)); + + /* Ensure its empty */ + res = g_input_stream_read_all (in, buffer, sizeof buffer, &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint ((int)n_bytes, ==, 0); + + out = g_io_stream_get_output_stream (G_IO_STREAM (file_iostream)); + res = g_output_stream_write_all (out, original_data, strlen (original_data), + &n_bytes, NULL, NULL); + g_assert (res); + g_assert_cmpint (n_bytes, ==, strlen (original_data)); + + res = g_seekable_seek (G_SEEKABLE (file_iostream), + 0, G_SEEK_SET, + NULL, NULL); + g_assert (res); + + verify_iostream (file_iostream); + + g_object_unref (file_iostream); + + backup = g_strconcat (tmp_file, "~", NULL); + res = g_file_get_contents (backup, + &data, + NULL, NULL); + g_assert (res); + g_assert_cmpstr (data, ==, new_data); + g_free (data); + g_unlink (backup); + g_free (backup); + + g_unlink (tmp_file); + g_free (tmp_file); +} + + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/readwrite/test_g_file_open_readwrite", + test_g_file_open_readwrite); + g_test_add_func ("/readwrite/test_g_file_create_readwrite", + test_g_file_create_readwrite); + g_test_add_func ("/readwrite/test_g_file_replace_readwrite", + test_g_file_replace_readwrite); + + return g_test_run(); +} diff --git a/tests/gio/resolver.c b/tests/gio/resolver.c new file mode 100644 index 000000000..055e152ac --- /dev/null +++ b/tests/gio/resolver.c @@ -0,0 +1,506 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include <glib.h> +#include "glibintl.h" + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <gio/gio.h> + +static GResolver *resolver; +static GCancellable *cancellable; +static GMainLoop *loop; +static int nlookups = 0; + +static void G_GNUC_NORETURN +usage (void) +{ + fprintf (stderr, "Usage: resolver [-t] [-s] [hostname | IP | service/protocol/domain ] ...\n"); + fprintf (stderr, " resolver [-t] [-s] -c [hostname | IP | service/protocol/domain ]\n"); + fprintf (stderr, " Use -t to enable threading.\n"); + fprintf (stderr, " Use -s to do synchronous lookups.\n"); + fprintf (stderr, " Both together will result in simultaneous lookups in multiple threads\n"); + fprintf (stderr, " Use -c (and only a single resolvable argument) to test GSocketConnectable.\n"); + exit (1); +} + +G_LOCK_DEFINE_STATIC (response); + +static void +done_lookup (void) +{ + nlookups--; + if (nlookups == 0) + { + /* In the sync case we need to make sure we don't call + * g_main_loop_quit before the loop is actually running... + */ + g_idle_add ((GSourceFunc)g_main_loop_quit, loop); + } +} + +static void +print_resolved_name (const char *phys, + char *name, + GError *error) +{ + G_LOCK (response); + printf ("Address: %s\n", phys); + if (error) + { + printf ("Error: %s\n", error->message); + g_error_free (error); + } + else + { + printf ("Name: %s\n", name); + g_free (name); + } + printf ("\n"); + + done_lookup (); + G_UNLOCK (response); +} + +static void +print_resolved_addresses (const char *name, + GList *addresses, + GError *error) +{ + char *phys; + GList *a; + + G_LOCK (response); + printf ("Name: %s\n", name); + if (error) + { + printf ("Error: %s\n", error->message); + g_error_free (error); + } + else + { + for (a = addresses; a; a = a->next) + { + phys = g_inet_address_to_string (a->data); + printf ("Address: %s\n", phys); + g_free (phys); + g_object_unref (a->data); + } + g_list_free (addresses); + } + printf ("\n"); + + done_lookup (); + G_UNLOCK (response); +} + +static void +print_resolved_service (const char *service, + GList *targets, + GError *error) +{ + GList *t; + + G_LOCK (response); + printf ("Service: %s\n", service); + if (error) + { + printf ("Error: %s\n", error->message); + g_error_free (error); + } + else + { + for (t = targets; t; t = t->next) + { + printf ("%s:%u (pri %u, weight %u)\n", + g_srv_target_get_hostname (t->data), + g_srv_target_get_port (t->data), + g_srv_target_get_priority (t->data), + g_srv_target_get_weight (t->data)); + g_srv_target_free (t->data); + } + g_list_free (targets); + } + printf ("\n"); + + done_lookup (); + G_UNLOCK (response); +} + +static void +lookup_one_sync (const char *arg) +{ + GError *error = NULL; + + if (strchr (arg, '/')) + { + GList *targets; + /* service/protocol/domain */ + char **parts = g_strsplit (arg, "/", 3); + + if (!parts || !parts[2]) + usage (); + + targets = g_resolver_lookup_service (resolver, + parts[0], parts[1], parts[2], + cancellable, &error); + print_resolved_service (arg, targets, error); + } + else if (g_hostname_is_ip_address (arg)) + { + GInetAddress *addr = g_inet_address_new_from_string (arg); + char *name; + + name = g_resolver_lookup_by_address (resolver, addr, cancellable, &error); + print_resolved_name (arg, name, error); + g_object_unref (addr); + } + else + { + GList *addresses; + + addresses = g_resolver_lookup_by_name (resolver, arg, cancellable, &error); + print_resolved_addresses (arg, addresses, error); + } +} + +static gpointer +lookup_thread (gpointer arg) +{ + lookup_one_sync (arg); + return NULL; +} + +static void +start_threaded_lookups (char **argv, int argc) +{ + int i; + + for (i = 0; i < argc; i++) + g_thread_create (lookup_thread, argv[i], FALSE, NULL); +} + +static void +start_sync_lookups (char **argv, int argc) +{ + int i; + + for (i = 0; i < argc; i++) + lookup_one_sync (argv[i]); +} + +static void +lookup_by_addr_callback (GObject *source, GAsyncResult *result, + gpointer user_data) +{ + const char *phys = user_data; + GError *error = NULL; + char *name; + + name = g_resolver_lookup_by_address_finish (resolver, result, &error); + print_resolved_name (phys, name, error); +} + +static void +lookup_by_name_callback (GObject *source, GAsyncResult *result, + gpointer user_data) +{ + const char *name = user_data; + GError *error = NULL; + GList *addresses; + + addresses = g_resolver_lookup_by_name_finish (resolver, result, &error); + print_resolved_addresses (name, addresses, error); +} + +static void +lookup_service_callback (GObject *source, GAsyncResult *result, + gpointer user_data) +{ + const char *service = user_data; + GError *error = NULL; + GList *targets; + + targets = g_resolver_lookup_service_finish (resolver, result, &error); + print_resolved_service (service, targets, error); +} + +static void +start_async_lookups (char **argv, int argc) +{ + int i; + + for (i = 0; i < argc; i++) + { + if (strchr (argv[i], '/')) + { + /* service/protocol/domain */ + char **parts = g_strsplit (argv[i], "/", 3); + + if (!parts || !parts[2]) + usage (); + + g_resolver_lookup_service_async (resolver, + parts[0], parts[1], parts[2], + cancellable, + lookup_service_callback, argv[i]); + } + else if (g_hostname_is_ip_address (argv[i])) + { + GInetAddress *addr = g_inet_address_new_from_string (argv[i]); + + g_resolver_lookup_by_address_async (resolver, addr, cancellable, + lookup_by_addr_callback, argv[i]); + g_object_unref (addr); + } + else + { + g_resolver_lookup_by_name_async (resolver, argv[i], cancellable, + lookup_by_name_callback, + argv[i]); + } + } +} + +static void +print_connectable_sockaddr (GSocketAddress *sockaddr, + GError *error) +{ + char *phys; + + if (error) + { + printf ("Error: %s\n", error->message); + g_error_free (error); + } + else if (!G_IS_INET_SOCKET_ADDRESS (sockaddr)) + { + printf ("Error: Unexpected sockaddr type '%s'\n", g_type_name_from_instance ((GTypeInstance *)sockaddr)); + g_object_unref (sockaddr); + } + else + { + GInetSocketAddress *isa = G_INET_SOCKET_ADDRESS (sockaddr); + phys = g_inet_address_to_string (g_inet_socket_address_get_address (isa)); + printf ("Address: %s%s%s:%d\n", + strchr (phys, ':') ? "[" : "", phys, strchr (phys, ':') ? "]" : "", + g_inet_socket_address_get_port (isa)); + g_free (phys); + g_object_unref (sockaddr); + } +} + +static void +do_sync_connectable (GSocketAddressEnumerator *enumerator) +{ + GSocketAddress *sockaddr; + GError *error = NULL; + + while ((sockaddr = g_socket_address_enumerator_next (enumerator, cancellable, &error))) + print_connectable_sockaddr (sockaddr, error); + + g_object_unref (enumerator); + done_lookup (); +} + +static void do_async_connectable (GSocketAddressEnumerator *enumerator); + +static void +got_next_async (GObject *source, GAsyncResult *result, gpointer user_data) +{ + GSocketAddressEnumerator *enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source); + GSocketAddress *sockaddr; + GError *error = NULL; + + sockaddr = g_socket_address_enumerator_next_finish (enumerator, result, &error); + if (sockaddr || error) + print_connectable_sockaddr (sockaddr, error); + if (sockaddr) + do_async_connectable (enumerator); + else + { + g_object_unref (enumerator); + done_lookup (); + } +} + +static void +do_async_connectable (GSocketAddressEnumerator *enumerator) +{ + g_socket_address_enumerator_next_async (enumerator, cancellable, + got_next_async, NULL); +} + +static void +do_connectable (const char *arg, gboolean synchronous) +{ + char **parts; + GSocketConnectable *connectable; + GSocketAddressEnumerator *enumerator; + + if (strchr (arg, '/')) + { + /* service/protocol/domain */ + parts = g_strsplit (arg, "/", 3); + if (!parts || !parts[2]) + usage (); + + connectable = g_network_service_new (parts[0], parts[1], parts[2]); + } + else + { + guint16 port; + + parts = g_strsplit (arg, ":", 2); + if (parts && parts[1]) + { + arg = parts[0]; + port = strtoul (parts[1], NULL, 10); + } + else + port = 0; + + if (g_hostname_is_ip_address (arg)) + { + GInetAddress *addr = g_inet_address_new_from_string (arg); + GSocketAddress *sockaddr = g_inet_socket_address_new (addr, port); + + g_object_unref (addr); + connectable = G_SOCKET_CONNECTABLE (sockaddr); + } + else + connectable = g_network_address_new (arg, port); + } + + enumerator = g_socket_connectable_enumerate (connectable); + g_object_unref (connectable); + + if (synchronous) + do_sync_connectable (enumerator); + else + do_async_connectable (enumerator); +} + +#ifdef G_OS_UNIX +static int cancel_fds[2]; + +static void +interrupted (int sig) +{ + signal (SIGINT, SIG_DFL); + write (cancel_fds[1], "x", 1); +} + +static gboolean +async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel) +{ + g_cancellable_cancel (cancel); + return FALSE; +} +#endif + +int +main (int argc, char **argv) +{ + gboolean threaded = FALSE, synchronous = FALSE; + gboolean use_connectable = FALSE; +#ifdef G_OS_UNIX + GIOChannel *chan; + guint watch; +#endif + + /* We can't use GOptionContext because we use the arguments to + * decide whether or not to call g_thread_init(). + */ + while (argc >= 2 && argv[1][0] == '-') + { + if (!strcmp (argv[1], "-t")) + { + g_thread_init (NULL); + threaded = TRUE; + } + else if (!strcmp (argv[1], "-s")) + synchronous = TRUE; + else if (!strcmp (argv[1], "-c")) + use_connectable = TRUE; + else + usage (); + + argv++; + argc--; + } + g_type_init (); + + if (argc < 2 || (argc > 2 && use_connectable)) + usage (); + + resolver = g_resolver_get_default (); + + cancellable = g_cancellable_new (); + +#ifdef G_OS_UNIX + /* Set up cancellation; we want to cancel if the user ^C's the + * program, but we can't cancel directly from an interrupt. + */ + signal (SIGINT, interrupted); + + if (pipe (cancel_fds) == -1) + { + perror ("pipe"); + exit (1); + } + chan = g_io_channel_unix_new (cancel_fds[0]); + watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable); + g_io_channel_unref (chan); +#endif + + nlookups = argc - 1; + loop = g_main_loop_new (NULL, TRUE); + + if (use_connectable) + do_connectable (argv[1], synchronous); + else + { + if (threaded && synchronous) + start_threaded_lookups (argv + 1, argc - 1); + else if (synchronous) + start_sync_lookups (argv + 1, argc - 1); + else + start_async_lookups (argv + 1, argc - 1); + } + + g_main_loop_run (loop); + g_main_loop_unref (loop); + +#ifdef G_OS_UNIX + g_source_remove (watch); +#endif + g_object_unref (cancellable); + + return 0; +} diff --git a/tests/gio/send-data.c b/tests/gio/send-data.c new file mode 100644 index 000000000..d2a702b44 --- /dev/null +++ b/tests/gio/send-data.c @@ -0,0 +1,163 @@ +#include <gio/gio.h> +#include <string.h> +#include <stdio.h> + +GMainLoop *loop; + +int cancel_timeout = 0; +gboolean async = FALSE; +gboolean graceful = FALSE; +static GOptionEntry cmd_entries[] = { + {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, + "Cancel any op after the specified amount of seconds", NULL}, + {"async", 'a', 0, G_OPTION_ARG_NONE, &async, + "Use async ops", NULL}, + {"graceful-disconnect", 'g', 0, G_OPTION_ARG_NONE, &graceful, + "Use graceful disconnect", NULL}, + {NULL} +}; + +static gpointer +cancel_thread (gpointer data) +{ + GCancellable *cancellable = data; + + g_usleep (1000*1000*cancel_timeout); + g_print ("Cancelling\n"); + g_cancellable_cancel (cancellable); + return NULL; +} + +static char * +socket_address_to_string (GSocketAddress *address) +{ + GInetAddress *inet_address; + char *str, *res; + int port; + + inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)); + str = g_inet_address_to_string (inet_address); + port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); + res = g_strdup_printf ("%s:%d", str, port); + g_free (str); + return res; +} + +static void +async_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GAsyncResult **resp = user_data; + *resp = g_object_ref (res); + g_main_loop_quit (loop); +} + + +int +main (int argc, char *argv[]) +{ + GOptionContext *context; + GSocketClient *client; + GSocketConnection *connection; + GSocketAddress *address; + GCancellable *cancellable; + GOutputStream *out; + GError *error = NULL; + char buffer[1000]; + + g_type_init (); + g_thread_init (NULL); + + context = g_option_context_new (" <hostname>[:port] - send data to tcp host"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (argc != 2) + { + g_printerr ("%s: %s\n", argv[0], "Need to specify hostname"); + return 1; + } + + if (async) + loop = g_main_loop_new (NULL, FALSE); + + if (cancel_timeout) + { + cancellable = g_cancellable_new (); + g_thread_create (cancel_thread, cancellable, FALSE, NULL); + } + else + { + cancellable = NULL; + } + + client = g_socket_client_new (); + connection = g_socket_client_connect_to_host (client, + argv[1], + 7777, + cancellable, &error); + if (connection == NULL) + { + g_printerr ("%s can't connect: %s\n", argv[0], error->message); + return 1; + } + + address = g_socket_connection_get_remote_address (connection, &error); + if (!address) + { + g_printerr ("Error getting remote address: %s\n", + error->message); + return 1; + } + g_print ("Connected to address: %s\n", + socket_address_to_string (address)); + g_object_unref (address); + + if (graceful) + g_tcp_connection_set_graceful_disconnect (G_TCP_CONNECTION (connection), TRUE); + + out = g_io_stream_get_output_stream (G_IO_STREAM (connection)); + + while (fgets(buffer, sizeof (buffer), stdin) != NULL) + { + if (!g_output_stream_write_all (out, buffer, strlen (buffer), + NULL, cancellable, &error)) + { + g_warning ("send error: %s\n", error->message); + g_error_free (error); + error = NULL; + } + } + + g_print ("closing stream\n"); + if (async) + { + GAsyncResult *res; + g_io_stream_close_async (G_IO_STREAM (connection), + 0, cancellable, async_cb, &res); + g_main_loop_run (loop); + if (!g_io_stream_close_finish (G_IO_STREAM (connection), + res, &error)) + { + g_object_unref (res); + g_warning ("close error: %s\n", error->message); + return 1; + } + g_object_unref (res); + } + else + { + if (!g_io_stream_close (G_IO_STREAM (connection), cancellable, &error)) + { + g_warning ("close error: %s\n", error->message); + return 1; + } + } + + return 0; +} diff --git a/tests/gio/simple-async-result.c b/tests/gio/simple-async-result.c new file mode 100644 index 000000000..2c4f62b50 --- /dev/null +++ b/tests/gio/simple-async-result.c @@ -0,0 +1,107 @@ +/* + * Copyright © 2009 Ryan Lortie + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +static GObject *got_source; +static GAsyncResult *got_result; +static gpointer got_user_data; + +static void +ensure_destroyed (gpointer obj) +{ + g_object_add_weak_pointer (obj, &obj); + g_object_unref (obj); + g_assert (obj == NULL); +} + +static void +reset (void) +{ + got_source = NULL; + + if (got_result) + ensure_destroyed (got_result); + + got_result = NULL; + got_user_data = NULL; +} + +static void +check (gpointer a, gpointer b, gpointer c) +{ + g_assert (a == got_source); + g_assert (b == got_result); + g_assert (c == got_user_data); +} + +static void +callback_func (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + got_source = source; + got_result = g_object_ref (result); + got_user_data = user_data; +} + +static void +test_simple_async (void) +{ + GSimpleAsyncResult *result; + GObject *a, *b, *c; + + a = g_object_new (G_TYPE_OBJECT, NULL); + b = g_object_new (G_TYPE_OBJECT, NULL); + c = g_object_new (G_TYPE_OBJECT, NULL); + + result = g_simple_async_result_new (a, callback_func, b, test_simple_async); + check (NULL, NULL, NULL); + g_simple_async_result_complete (result); + check (a, result, b); + g_object_unref (result); + + g_assert (g_simple_async_result_is_valid (got_result, a, test_simple_async)); + g_assert (!g_simple_async_result_is_valid (got_result, b, test_simple_async)); + g_assert (!g_simple_async_result_is_valid (got_result, c, test_simple_async)); + g_assert (!g_simple_async_result_is_valid (got_result, b, callback_func)); + g_assert (!g_simple_async_result_is_valid ((gpointer) a, NULL, NULL)); + reset (); + reset (); + reset (); + + result = g_simple_async_result_new (a, callback_func, b, test_simple_async); + check (NULL, NULL, NULL); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + check (NULL, NULL, NULL); + g_main_context_iteration (NULL, FALSE); + check (a, result, b); + reset (); + + ensure_destroyed (a); + ensure_destroyed (b); + ensure_destroyed (c); +} + +int +main (int argc, char **argv) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gio/simple-async-result/test", test_simple_async); + + return g_test_run(); +} diff --git a/tests/gio/sleepy-stream.c b/tests/gio/sleepy-stream.c new file mode 100644 index 000000000..1c6613e4b --- /dev/null +++ b/tests/gio/sleepy-stream.c @@ -0,0 +1,296 @@ +/* + * Copyright © 2009 Codethink Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * See the included COPYING file for more information. + * + * Author: Ryan Lortie <desrt@desrt.ca> + */ + +#include <gio/gio.h> +#include <string.h> + +#define MAX_PIECE_SIZE 100 +#define MAX_PIECES 60 + +static gchar * +cook_piece (void) +{ + char buffer[MAX_PIECE_SIZE * 2]; + gint symbols, i = 0; + + symbols = g_test_rand_int_range (1, MAX_PIECE_SIZE + 1); + + while (symbols--) + { + gint c = g_test_rand_int_range (0, 30); + + switch (c) + { + case 26: + buffer[i++] = '\n'; + case 27: + buffer[i++] = '\r'; + break; + + case 28: + buffer[i++] = '\r'; + case 29: + buffer[i++] = '\n'; + break; + + default: + buffer[i++] = c + 'a'; + break; + } + + g_assert_cmpint (i, <=, sizeof buffer); + } + + return g_strndup (buffer, i); +} + +static gchar ** +cook_pieces (void) +{ + gchar **array; + gint pieces; + + pieces = g_test_rand_int_range (0, MAX_PIECES + 1); + array = g_new (char *, pieces + 1); + array[pieces] = NULL; + + while (pieces--) + array[pieces] = cook_piece (); + + return array; +} + +typedef struct +{ + GInputStream parent_instance; + + gboolean built_to_fail; + gchar **pieces; + gint index; + + const gchar *current; +} SleepyStream; + +typedef GInputStreamClass SleepyStreamClass; + +GType sleepy_stream_get_type (void); + +G_DEFINE_TYPE (SleepyStream, sleepy_stream, G_TYPE_INPUT_STREAM) + +static gssize +sleepy_stream_read (GInputStream *stream, + void *buffer, + gsize length, + GCancellable *cancellable, + GError **error) +{ + SleepyStream *sleepy = (SleepyStream *) stream; + + if (sleepy->pieces[sleepy->index] == NULL) + { + if (sleepy->built_to_fail) + { + g_set_error (error, 0, 0, "fail"); + return -1; + } + else + return 0; + } + else + { + if (!sleepy->current) + sleepy->current = sleepy->pieces[sleepy->index++]; + + length = MIN (strlen (sleepy->current), length); + memcpy (buffer, sleepy->current, length); + + sleepy->current += length; + if (*sleepy->current == '\0') + sleepy->current = NULL; + + return length; + } +} + +static void +sleepy_stream_init (SleepyStream *sleepy) +{ + sleepy->pieces = cook_pieces (); + sleepy->built_to_fail = FALSE; + sleepy->index = 0; +} + +static void +sleepy_stream_finalize (GObject *object) +{ + SleepyStream *sleepy = (SleepyStream *) object; + + g_strfreev (sleepy->pieces); + G_OBJECT_CLASS (sleepy_stream_parent_class) + ->finalize (object); +} + +static void +sleepy_stream_class_init (SleepyStreamClass *class) +{ + G_OBJECT_CLASS (class)->finalize = sleepy_stream_finalize; + class->read_fn = sleepy_stream_read; + + /* no read_async implementation. + * main thread will sleep while read runs in a worker. + */ +} + +static SleepyStream * +sleepy_stream_new (void) +{ + return g_object_new (sleepy_stream_get_type (), NULL); +} + +static gboolean +read_line (GDataInputStream *stream, + GString *string, + const gchar *eol, + GError **error) +{ + gsize length; + int eol_len; + char *str; + + eol_len = 1 + (eol[1] != '\0'); + + str = g_data_input_stream_read_line (stream, &length, NULL, error); + + if (str == NULL) + return FALSE; + + g_assert (strstr (str, eol) == NULL); + g_assert (strlen (str) == length); + + g_string_append (string, str); + g_string_append (string, eol); + g_free (str); + + return TRUE; +} + +static void +build_comparison (GString *str, + SleepyStream *stream) +{ + /* build this for comparison */ + gint i; + + for (i = 0; stream->pieces[i]; i++) + g_string_append (str, stream->pieces[i]); + + if (str->len && str->str[str->len - 1] != '\n') + g_string_append_c (str, '\n'); +} + + +static void +test (void) +{ + SleepyStream *stream = sleepy_stream_new (); + GDataInputStream *data; + GError *error = NULL; + GString *one; + GString *two; + + one = g_string_new (NULL); + two = g_string_new (NULL); + + data = g_data_input_stream_new (G_INPUT_STREAM (stream)); + g_data_input_stream_set_newline_type (data, G_DATA_STREAM_NEWLINE_TYPE_LF); + build_comparison (one, stream); + + while (read_line (data, two, "\n", &error)); + + g_assert_cmpstr (one->str, ==, two->str); + g_string_free (one, TRUE); + g_string_free (two, TRUE); + g_object_unref (stream); + g_object_unref (data); +} + +static GDataInputStream *data; +static GString *one, *two; +static GMainLoop *loop; +static const gchar *eol; + +static void +asynch_ready (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + gsize length; + gchar *str; + + g_assert (data == G_DATA_INPUT_STREAM (object)); + + str = g_data_input_stream_read_line_finish (data, result, &length, &error); + + if (str == NULL) + { + g_main_loop_quit (loop); + if (error) + g_error_free (error); + } + else + { + g_assert (length == strlen (str)); + g_string_append (two, str); + g_string_append (two, eol); + g_free (str); + + /* MOAR!! */ + g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL); + } +} + + +static void +asynch (void) +{ + SleepyStream *sleepy = sleepy_stream_new (); + + data = g_data_input_stream_new (G_INPUT_STREAM (sleepy)); + one = g_string_new (NULL); + two = g_string_new (NULL); + eol = "\n"; + + build_comparison (one, sleepy); + g_data_input_stream_read_line_async (data, 0, NULL, asynch_ready, NULL); + g_main_loop_run (loop = g_main_loop_new (NULL, FALSE)); + + g_assert_cmpstr (one->str, ==, two->str); + g_string_free (one, TRUE); + g_string_free (two, TRUE); + g_object_unref (sleepy); + g_object_unref (data); +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("http://bugzilla.gnome.org/"); + + g_type_init (); + g_test_add_func ("/filter-stream/input", test); + g_test_add_func ("/filter-stream/async", asynch); + + return g_test_run(); +} diff --git a/tests/gio/socket-client.c b/tests/gio/socket-client.c new file mode 100644 index 000000000..8409a5fd4 --- /dev/null +++ b/tests/gio/socket-client.c @@ -0,0 +1,298 @@ +#include <gio/gio.h> +#include <glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +GMainLoop *loop; + +gboolean verbose = FALSE; +gboolean non_blocking = FALSE; +gboolean use_udp = FALSE; +gboolean use_source = FALSE; +int cancel_timeout = 0; + +static GOptionEntry cmd_entries[] = { + {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, + "Cancel any op after the specified amount of seconds", NULL}, + {"udp", 'u', 0, G_OPTION_ARG_NONE, &use_udp, + "Use udp instead of tcp", NULL}, + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + "Be verbose", NULL}, + {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, + "Enable non-blocking i/o", NULL}, + {"use-source", 's', 0, G_OPTION_ARG_NONE, &use_source, + "Use GSource to wait for non-blocking i/o", NULL}, + {NULL} +}; + +static char * +socket_address_to_string (GSocketAddress *address) +{ + GInetAddress *inet_address; + char *str, *res; + int port; + + inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)); + str = g_inet_address_to_string (inet_address); + port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); + res = g_strdup_printf ("%s:%d", str, port); + g_free (str); + return res; +} + +static gboolean +source_ready (gpointer data, + GIOCondition condition) +{ + g_main_loop_quit (loop); + return FALSE; +} + +static void +ensure_condition (GSocket *socket, + const char *where, + GCancellable *cancellable, + GIOCondition condition) +{ + GError *error = NULL; + GSource *source; + + if (!non_blocking) + return; + + if (use_source) + { + source = g_socket_create_source (socket, + condition, + cancellable); + g_source_set_callback (source, + (GSourceFunc) source_ready, + NULL, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + g_main_loop_run (loop); + } + else + { + if (!g_socket_condition_wait (socket, condition, cancellable, &error)) + { + g_printerr ("condition wait error for %s: %s\n", + where, + error->message); + exit (1); + } + } +} + +static gpointer +cancel_thread (gpointer data) +{ + GCancellable *cancellable = data; + + g_usleep (1000*1000*cancel_timeout); + g_print ("Cancelling\n"); + g_cancellable_cancel (cancellable); + return NULL; +} + +int +main (int argc, + char *argv[]) +{ + GSocket *socket; + GSocketAddress *src_address; + GSocketAddress *address; + GSocketType socket_type; + GError *error = NULL; + GOptionContext *context; + GCancellable *cancellable; + GSocketAddressEnumerator *enumerator; + GSocketConnectable *connectable; + + g_thread_init (NULL); + + g_type_init (); + + context = g_option_context_new (" <hostname>[:port] - Test GSocket client stuff"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (argc != 2) + { + g_printerr ("%s: %s\n", argv[0], "Need to specify hostname"); + return 1; + } + + if (cancel_timeout) + { + cancellable = g_cancellable_new (); + g_thread_create (cancel_thread, cancellable, FALSE, NULL); + } + else + { + cancellable = NULL; + } + + loop = g_main_loop_new (NULL, FALSE); + + if (use_udp) + socket_type = G_SOCKET_TYPE_DATAGRAM; + else + socket_type = G_SOCKET_TYPE_STREAM; + + socket = g_socket_new (G_SOCKET_FAMILY_IPV4, socket_type, 0, &error); + if (socket == NULL) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + connectable = g_network_address_parse (argv[1], 7777, &error); + if (connectable == NULL) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + enumerator = g_socket_connectable_enumerate (connectable); + while (TRUE) + { + address = g_socket_address_enumerator_next (enumerator, cancellable, &error); + if (address == NULL) + { + if (error == NULL) + g_printerr ("%s: No more addresses to try\n", argv[0]); + else + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (g_socket_connect (socket, address, cancellable, &error)) + break; + g_printerr ("%s: Connection to %s failed: %s, trying next\n", argv[0], socket_address_to_string (address), error->message); + g_error_free (error); + error = NULL; + + g_object_unref (address); + } + g_object_unref (enumerator); + + g_print ("Connected to %s\n", + socket_address_to_string (address)); + + /* TODO: Test non-blocking connect */ + if (non_blocking) + g_socket_set_blocking (socket, FALSE); + + src_address = g_socket_get_local_address (socket, &error); + if (!src_address) + { + g_printerr ("Error getting local address: %s\n", + error->message); + return 1; + } + g_print ("local address: %s\n", + socket_address_to_string (src_address)); + g_object_unref (src_address); + + while (TRUE) + { + gchar buffer[4096] = { }; + gssize size; + gsize to_send; + + if (fgets (buffer, sizeof buffer, stdin) == NULL) + break; + + to_send = strlen (buffer); + while (to_send > 0) + { + ensure_condition (socket, "send", cancellable, G_IO_OUT); + if (use_udp) + size = g_socket_send_to (socket, address, + buffer, to_send, + cancellable, &error); + else + size = g_socket_send (socket, buffer, to_send, + cancellable, &error); + + if (size < 0) + { + if (g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_WOULD_BLOCK)) + { + g_print ("socket send would block, handling\n"); + g_error_free (error); + error = NULL; + continue; + } + else + { + g_printerr ("Error sending to socket: %s\n", + error->message); + return 1; + } + } + + g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size); + + if (size == 0) + { + g_printerr ("Unexpected short write\n"); + return 1; + } + + to_send -= size; + } + + ensure_condition (socket, "receive", cancellable, G_IO_IN); + if (use_udp) + size = g_socket_receive_from (socket, &src_address, + buffer, sizeof buffer, + cancellable, &error); + else + size = g_socket_receive (socket, buffer, sizeof buffer, + cancellable, &error); + + if (size < 0) + { + g_printerr ("Error receiving from socket: %s\n", + error->message); + return 1; + } + + if (size == 0) + break; + + g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size); + if (use_udp) + g_print (" from %s", socket_address_to_string (src_address)); + g_print ("\n"); + + if (verbose) + g_print ("-------------------------\n" + "%.*s" + "-------------------------\n", + (int)size, buffer); + + } + + g_print ("closing socket\n"); + + if (!g_socket_close (socket, &error)) + { + g_printerr ("Error closing master socket: %s\n", + error->message); + return 1; + } + + g_object_unref (G_OBJECT (socket)); + + return 0; +} diff --git a/tests/gio/socket-server.c b/tests/gio/socket-server.c new file mode 100644 index 000000000..b98c696e4 --- /dev/null +++ b/tests/gio/socket-server.c @@ -0,0 +1,308 @@ +#include <gio/gio.h> +#include <glib.h> +#include <stdlib.h> + +GMainLoop *loop; + +int port = 7777; +gboolean verbose = FALSE; +gboolean dont_reuse_address = FALSE; +gboolean non_blocking = FALSE; +gboolean use_udp = FALSE; +gboolean use_source = FALSE; +int cancel_timeout = 0; + +static GOptionEntry cmd_entries[] = { + {"port", 'p', 0, G_OPTION_ARG_INT, &port, + "Local port to bind to", NULL}, + {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, + "Cancel any op after the specified amount of seconds", NULL}, + {"udp", 'u', 0, G_OPTION_ARG_NONE, &use_udp, + "Use udp instead of tcp", NULL}, + {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, + "Be verbose", NULL}, + {"no-reuse", 0, 0, G_OPTION_ARG_NONE, &dont_reuse_address, + "Don't SOADDRREUSE", NULL}, + {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking, + "Enable non-blocking i/o", NULL}, + {"use-source", 's', 0, G_OPTION_ARG_NONE, &use_source, + "Use GSource to wait for non-blocking i/o", NULL}, + {NULL} +}; + +static char * +socket_address_to_string (GSocketAddress *address) +{ + GInetAddress *inet_address; + char *str, *res; + int the_port; + + inet_address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)); + str = g_inet_address_to_string (inet_address); + the_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (address)); + res = g_strdup_printf ("%s:%d", str, the_port); + g_free (str); + return res; +} + +static gboolean +source_ready (gpointer data, + GIOCondition condition) +{ + g_main_loop_quit (loop); + return FALSE; +} + +static void +ensure_condition (GSocket *socket, + const char *where, + GCancellable *cancellable, + GIOCondition condition) +{ + GError *error = NULL; + GSource *source; + + if (!non_blocking) + return; + + if (use_source) + { + source = g_socket_create_source (socket, + condition, + cancellable); + g_source_set_callback (source, + (GSourceFunc) source_ready, + NULL, NULL); + g_source_attach (source, NULL); + g_source_unref (source); + g_main_loop_run (loop); + } + else + { + if (!g_socket_condition_wait (socket, condition, cancellable, &error)) + { + g_printerr ("condition wait error for %s: %s\n", + where, + error->message); + exit (1); + } + } +} + +static gpointer +cancel_thread (gpointer data) +{ + GCancellable *cancellable = data; + + g_usleep (1000*1000*cancel_timeout); + g_print ("Cancelling\n"); + g_cancellable_cancel (cancellable); + return NULL; +} + +int +main (int argc, + char *argv[]) +{ + GSocket *socket, *new_socket, *recv_socket; + GSocketAddress *src_address; + GSocketAddress *address; + GSocketType socket_type; + GError *error = NULL; + GOptionContext *context; + GCancellable *cancellable; + + g_thread_init (NULL); + + g_type_init (); + + context = g_option_context_new (" - Test GSocket server stuff"); + g_option_context_add_main_entries (context, cmd_entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (cancel_timeout) + { + cancellable = g_cancellable_new (); + g_thread_create (cancel_thread, cancellable, FALSE, NULL); + } + else + { + cancellable = NULL; + } + + loop = g_main_loop_new (NULL, FALSE); + + if (use_udp) + socket_type = G_SOCKET_TYPE_DATAGRAM; + else + socket_type = G_SOCKET_TYPE_STREAM; + + socket = g_socket_new (G_SOCKET_FAMILY_IPV4, socket_type, 0, &error); + + if (socket == NULL) + { + g_printerr ("%s: %s\n", argv[0], error->message); + return 1; + } + + if (non_blocking) + g_socket_set_blocking (socket, FALSE); + + src_address = g_inet_socket_address_new (g_inet_address_new_any (G_SOCKET_FAMILY_IPV4), port); + if (!g_socket_bind (socket, src_address, !dont_reuse_address, &error)) + { + g_printerr ("Can't bind socket: %s\n", error->message); + return 1; + } + + if (!use_udp) + { + if (!g_socket_listen (socket, &error)) + { + g_printerr ("Can't listen on socket: %s\n", error->message); + return 1; + } + + g_print ("listening on port %d...\n", port); + + ensure_condition (socket, "accept", cancellable, G_IO_IN); + new_socket = g_socket_accept (socket, cancellable, &error); + if (!new_socket) + { + g_printerr ("Error accepting socket: %s\n", + error->message); + return 1; + } + + if (non_blocking) + g_socket_set_blocking (new_socket, FALSE); + + address = g_socket_get_remote_address (new_socket, &error); + if (!address) + { + g_printerr ("Error getting remote address: %s\n", + error->message); + return 1; + } + + g_print ("got a new connection from %s\n", + socket_address_to_string (address)); + g_object_unref (address); + + recv_socket = new_socket; + } + else + { + recv_socket = socket; + new_socket = NULL; + } + + + while (TRUE) + { + gchar buffer[4096] = { }; + gssize size; + gsize to_send; + + ensure_condition (recv_socket, "receive", cancellable, G_IO_IN); + if (use_udp) + size = g_socket_receive_from (recv_socket, &address, + buffer, sizeof buffer, + cancellable, &error); + else + size = g_socket_receive (recv_socket, buffer, sizeof buffer, + cancellable, &error); + + if (size < 0) + { + g_printerr ("Error receiving from socket: %s\n", + error->message); + return 1; + } + + if (size == 0) + break; + + g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size); + if (use_udp) + g_print (" from %s", socket_address_to_string (address)); + g_print ("\n"); + + if (verbose) + g_print ("-------------------------\n" + "%.*s\n" + "-------------------------\n", + (int)size, buffer); + + to_send = size; + + while (to_send > 0) + { + ensure_condition (recv_socket, "send", cancellable, G_IO_OUT); + if (use_udp) + size = g_socket_send_to (recv_socket, address, + buffer, to_send, cancellable, &error); + else + size = g_socket_send (recv_socket, buffer, to_send, + cancellable, &error); + + if (size < 0) + { + if (g_error_matches (error, + G_IO_ERROR, + G_IO_ERROR_WOULD_BLOCK)) + { + g_print ("socket send would block, handling\n"); + g_error_free (error); + error = NULL; + continue; + } + else + { + g_printerr ("Error sending to socket: %s\n", + error->message); + return 1; + } + } + + g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size); + + if (size == 0) + { + g_printerr ("Unexpected short write\n"); + return 1; + } + + to_send -= size; + } + } + + g_print ("connection closed\n"); + + if (new_socket) + { + if (!g_socket_close (new_socket, &error)) + { + g_printerr ("Error closing connection socket: %s\n", + error->message); + return 1; + } + + g_object_unref (G_OBJECT (new_socket)); + } + + if (!g_socket_close (socket, &error)) + { + g_printerr ("Error closing master socket: %s\n", + error->message); + return 1; + } + + g_object_unref (G_OBJECT (socket)); + + return 0; +} diff --git a/tests/gio/srvtarget.c b/tests/gio/srvtarget.c new file mode 100644 index 000000000..c7330f9ae --- /dev/null +++ b/tests/gio/srvtarget.c @@ -0,0 +1,158 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2009 Red Hat, Inc. + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> + +#define NUM_TRIALS 250000 + +struct { + const char *order; + int expected, seen; +} ordering[] = { + /* There are 32 legitimate orderings; the result always has to start + * with either "fe" (usually) or "ef" (rarely). For the remaining + * letters, "cbda" is the most likely, with various other orders + * possible, down to "adbc" being the most improbable. However, + * almost all "fe" orderings are more likely than almost any "ef" + * orderings. The complete probability ordering, from most-likely + * to least-likely is something roughly like: + */ + { "fecbda", 0.2468 * NUM_TRIALS, 0}, + { "febcda", 0.1885 * NUM_TRIALS, 0}, + { "fecdba", 0.1346 * NUM_TRIALS, 0}, + { "fedcba", 0.0830 * NUM_TRIALS, 0}, + { "febdca", 0.0706 * NUM_TRIALS, 0}, + { "fedbca", 0.0571 * NUM_TRIALS, 0}, + { "fecbad", 0.0496 * NUM_TRIALS, 0}, + { "febcad", 0.0374 * NUM_TRIALS, 0}, + { "fecabd", 0.0185 * NUM_TRIALS, 0}, + { "fecdab", 0.0136 * NUM_TRIALS, 0}, + { "fecadb", 0.0110 * NUM_TRIALS, 0}, + { "febacd", 0.0108 * NUM_TRIALS, 0}, + { "feacbd", 0.0096 * NUM_TRIALS, 0}, + { "fedcab", 0.0083 * NUM_TRIALS, 0}, + { "feabcd", 0.0073 * NUM_TRIALS, 0}, + { "feacdb", 0.0058 * NUM_TRIALS, 0}, + { "efcbda", 0.0049 * NUM_TRIALS, 0}, + { "febdac", 0.0048 * NUM_TRIALS, 0}, + { "febadc", 0.0043 * NUM_TRIALS, 0}, + { "fedbac", 0.0038 * NUM_TRIALS, 0}, + { "efbcda", 0.0038 * NUM_TRIALS, 0}, + { "feadcb", 0.0036 * NUM_TRIALS, 0}, + { "fedacb", 0.0035 * NUM_TRIALS, 0}, + { "feabdc", 0.0029 * NUM_TRIALS, 0}, + { "feadbc", 0.0026 * NUM_TRIALS, 0}, + { "fedabc", 0.0026 * NUM_TRIALS, 0}, + { "efcdba", 0.0026 * NUM_TRIALS, 0}, + { "efdcba", 0.0017 * NUM_TRIALS, 0}, + { "efbdca", 0.0014 * NUM_TRIALS, 0}, + { "efdbca", 0.0011 * NUM_TRIALS, 0}, + { "efcbad", 0.0010 * NUM_TRIALS, 0}, + { "efbcad", 0.0008 * NUM_TRIALS, 0}, + { "efcabd", 0.0004 * NUM_TRIALS, 0}, + { "efcdab", 0.0003 * NUM_TRIALS, 0}, + { "efcadb", 0.0002 * NUM_TRIALS, 0}, + { "efbacd", 0.0002 * NUM_TRIALS, 0}, + { "efacbd", 0.0002 * NUM_TRIALS, 0}, + { "efdcab", 0.0002 * NUM_TRIALS, 0}, + { "efabcd", 0.0002 * NUM_TRIALS, 0}, + { "efacdb", 0.0001 * NUM_TRIALS, 0}, + { "efbdac", 0.0001 * NUM_TRIALS, 0}, + { "efadcb", 0.0001 * NUM_TRIALS, 0}, + { "efdbac", 0.0001 * NUM_TRIALS, 0}, + { "efbadc", 0.0001 * NUM_TRIALS, 0}, + { "efdacb", 0.0001 * NUM_TRIALS, 0}, + { "efabdc", 0.0001 * NUM_TRIALS, 0}, + { "efadbc", 0.00005 * NUM_TRIALS, 0}, + { "efdabc", 0.00005 * NUM_TRIALS, 0} +}; +#define NUM_ORDERINGS G_N_ELEMENTS (ordering) + +static void +test_srv_target_ordering (void) +{ + GList *targets, *sorted, *t; + char result[7], *p; + int i; + guint o; + + targets = NULL; + /* name, port, priority, weight */ + targets = g_list_append (targets, g_srv_target_new ("a", 0, 2, 0)); + targets = g_list_append (targets, g_srv_target_new ("b", 0, 2, 10)); + targets = g_list_append (targets, g_srv_target_new ("c", 0, 2, 15)); + targets = g_list_append (targets, g_srv_target_new ("d", 0, 2, 5)); + targets = g_list_append (targets, g_srv_target_new ("e", 0, 1, 0)); + targets = g_list_append (targets, g_srv_target_new ("f", 0, 1, 50)); + + for (i = 0; i < NUM_TRIALS; i++) + { + g_random_set_seed (i); + + sorted = g_srv_target_list_sort (g_list_copy (targets)); + + for (t = sorted, p = result; t; t = t->next) + *(p++) = *g_srv_target_get_hostname (t->data); + *p = '\0'; + g_list_free (sorted); + + for (o = 0; o < NUM_ORDERINGS; o++) + { + if (!strcmp (result, ordering[o].order)) + { + ordering[o].seen++; + break; + } + } + + /* Assert that @result matched one of the valid orderings */ + if (o == NUM_ORDERINGS) + { + char *msg = g_strdup_printf ("result '%s' is invalid", result); + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); + } + } + + /* Assert that each ordering appeared roughly the expected + * number of times. + */ + for (o = 0; o < NUM_ORDERINGS; o++) + { + g_assert_cmpint (ordering[o].seen, >, ordering[o].expected / 2); + g_assert_cmpint (ordering[o].seen, <, ordering[o].expected * 2); + } + + g_resolver_free_targets (targets); +} + +int +main (int argc, char **argv) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/srvtarget/srv-target-ordering", test_srv_target_ordering); + + return g_test_run(); +} diff --git a/tests/gio/unix-streams.c b/tests/gio/unix-streams.c new file mode 100644 index 000000000..07c7b9f82 --- /dev/null +++ b/tests/gio/unix-streams.c @@ -0,0 +1,256 @@ +/* GLib testing framework examples and tests + * Copyright (C) 2008 Red Hat, Inc + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include <glib/glib.h> +#include <gio/gio.h> +#include <gio/gunixinputstream.h> +#include <gio/gunixoutputstream.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define DATA "abcdefghijklmnopqrstuvwxyz" + +int writer_pipe[2], reader_pipe[2]; +GCancellable *writer_cancel, *reader_cancel, *main_cancel; +GMainLoop *loop; + +static gpointer +writer_thread (gpointer user_data) +{ + GOutputStream *out; + gssize nwrote, offset; + GError *err = NULL; + + out = g_unix_output_stream_new (writer_pipe[1], TRUE); + + do + { + g_usleep (10); + + offset = 0; + while (offset < (gssize) sizeof (DATA)) + { + nwrote = g_output_stream_write (out, DATA + offset, + sizeof (DATA) - offset, + writer_cancel, &err); + if (nwrote <= 0 || err != NULL) + break; + offset += nwrote; + } + + g_assert (nwrote > 0 || err != NULL); + } + while (err == NULL); + + if (g_cancellable_is_cancelled (writer_cancel)) + { + g_cancellable_cancel (main_cancel); + g_object_unref (out); + return NULL; + } + + g_warning ("writer: %s", err->message); + g_assert_not_reached (); +} + +static gpointer +reader_thread (gpointer user_data) +{ + GInputStream *in; + gssize nread, total; + GError *err = NULL; + char buf[sizeof (DATA)]; + + in = g_unix_input_stream_new (reader_pipe[0], TRUE); + + do + { + total = 0; + while (total < (gssize) sizeof (DATA)) + { + nread = g_input_stream_read (in, buf + total, sizeof (buf) - total, + reader_cancel, &err); + if (nread <= 0 || err != NULL) + break; + total += nread; + } + + if (err) + break; + + if (nread == 0) + { + g_assert (err == NULL); + /* pipe closed */ + g_object_unref (in); + return NULL; + } + + g_assert_cmpstr (buf, ==, DATA); + g_assert (!g_cancellable_is_cancelled (reader_cancel)); + } + while (err == NULL); + + g_warning ("reader: %s", err->message); + g_assert_not_reached (); +} + +char main_buf[sizeof (DATA)]; +gssize main_len, main_offset; + +static void readable (GObject *source, GAsyncResult *res, gpointer user_data); +static void writable (GObject *source, GAsyncResult *res, gpointer user_data); + +static void +do_main_cancel (GOutputStream *out) +{ + g_output_stream_close (out, NULL, NULL); + g_main_loop_quit (loop); +} + +static void +readable (GObject *source, GAsyncResult *res, gpointer user_data) +{ + GInputStream *in = G_INPUT_STREAM (source); + GOutputStream *out = user_data; + GError *err = NULL; + + main_len = g_input_stream_read_finish (in, res, &err); + + if (g_cancellable_is_cancelled (main_cancel)) + { + do_main_cancel (out); + return; + } + + g_assert (err == NULL); + + main_offset = 0; + g_output_stream_write_async (out, main_buf, main_len, + G_PRIORITY_DEFAULT, main_cancel, + writable, in); +} + +static void +writable (GObject *source, GAsyncResult *res, gpointer user_data) +{ + GOutputStream *out = G_OUTPUT_STREAM (source); + GInputStream *in = user_data; + GError *err = NULL; + gssize nwrote; + + nwrote = g_output_stream_write_finish (out, res, &err); + + if (g_cancellable_is_cancelled (main_cancel)) + { + do_main_cancel (out); + return; + } + + g_assert (err == NULL); + g_assert_cmpint (nwrote, <=, main_len - main_offset); + + main_offset += nwrote; + if (main_offset == main_len) + { + g_input_stream_read_async (in, main_buf, sizeof (main_buf), + G_PRIORITY_DEFAULT, main_cancel, + readable, out); + } + else + { + g_output_stream_write_async (out, main_buf + main_offset, + main_len - main_offset, + G_PRIORITY_DEFAULT, main_cancel, + writable, in); + } +} + +static gboolean +timeout (gpointer cancellable) +{ + g_cancellable_cancel (cancellable); + return FALSE; +} + +static void +test_pipe_io (void) +{ + GThread *writer, *reader; + GInputStream *in; + GOutputStream *out; + + /* Split off two (additional) threads, a reader and a writer. From + * the writer thread, write data synchronously in small chunks, + * which gets read asynchronously by the main thread and then + * written asynchronously to the reader thread, which reads it + * synchronously. Eventually a timeout in the main thread will cause + * it to cancel the writer thread, which will in turn cancel the + * read op in the main thread, which will then close the pipe to + * the reader thread, causing the read op to fail. + */ + + g_assert (pipe (writer_pipe) == 0 && pipe (reader_pipe) == 0); + + writer_cancel = g_cancellable_new (); + reader_cancel = g_cancellable_new (); + main_cancel = g_cancellable_new (); + + writer = g_thread_create (writer_thread, NULL, TRUE, NULL); + reader = g_thread_create (reader_thread, NULL, TRUE, NULL); + + in = g_unix_input_stream_new (writer_pipe[0], TRUE); + out = g_unix_output_stream_new (reader_pipe[1], TRUE); + + g_input_stream_read_async (in, main_buf, sizeof (main_buf), + G_PRIORITY_DEFAULT, main_cancel, + readable, out); + + g_timeout_add (500, timeout, writer_cancel); + + loop = g_main_loop_new (NULL, TRUE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_thread_join (reader); + g_thread_join (writer); + + g_object_unref (main_cancel); + g_object_unref (reader_cancel); + g_object_unref (writer_cancel); + g_object_unref (in); + g_object_unref (out); +} + +int +main (int argc, + char *argv[]) +{ + g_thread_init (NULL); + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/unix-streams/pipe-io-test", test_pipe_io); + + return g_test_run(); +} |