#include #include #include #include #include #ifdef G_OS_UNIX #include #endif static void test_basic_for_file (GFile *file, const gchar *suffix) { gchar *s; s = g_file_get_basename (file); g_assert_cmpstr (s, ==, "testfile"); g_free (s); s = g_file_get_uri (file); g_assert (g_str_has_prefix (s, "file://")); g_assert (g_str_has_suffix (s, suffix)); g_free (s); g_assert (g_file_has_uri_scheme (file, "file")); s = g_file_get_uri_scheme (file); g_assert_cmpstr (s, ==, "file"); g_free (s); } static void test_basic (void) { GFile *file; file = g_file_new_for_path ("./some/directory/testfile"); test_basic_for_file (file, "/some/directory/testfile"); g_object_unref (file); } static void test_build_filename (void) { GFile *file; file = g_file_new_build_filename (".", "some", "directory", "testfile", NULL); test_basic_for_file (file, "/some/directory/testfile"); g_object_unref (file); file = g_file_new_build_filename ("testfile", NULL); test_basic_for_file (file, "/testfile"); g_object_unref (file); } static void test_parent (void) { GFile *file; GFile *file2; GFile *parent; GFile *root; file = g_file_new_for_path ("./some/directory/testfile"); file2 = g_file_new_for_path ("./some/directory"); root = g_file_new_for_path ("/"); g_assert (g_file_has_parent (file, file2)); parent = g_file_get_parent (file); g_assert (g_file_equal (parent, file2)); g_object_unref (parent); g_assert (g_file_get_parent (root) == NULL); g_object_unref (file); g_object_unref (file2); g_object_unref (root); } static void test_child (void) { GFile *file; GFile *child; GFile *child2; file = g_file_new_for_path ("./some/directory"); child = g_file_get_child (file, "child"); g_assert (g_file_has_parent (child, file)); child2 = g_file_get_child_for_display_name (file, "child2", NULL); g_assert (g_file_has_parent (child2, file)); g_object_unref (child); g_object_unref (child2); g_object_unref (file); } static void test_type (void) { GFile *datapath_f; GFile *file; GFileType type; GError *error = NULL; datapath_f = g_file_new_for_path (g_test_get_dir (G_TEST_DIST)); file = g_file_get_child (datapath_f, "g-icon.c"); type = g_file_query_file_type (file, 0, NULL); g_assert_cmpint (type, ==, G_FILE_TYPE_REGULAR); g_object_unref (file); file = g_file_get_child (datapath_f, "cert-tests"); type = g_file_query_file_type (file, 0, NULL); g_assert_cmpint (type, ==, G_FILE_TYPE_DIRECTORY); g_file_read (file, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY); g_error_free (error); g_object_unref (file); g_object_unref (datapath_f); } static void test_parse_name (void) { GFile *file; gchar *name; file = g_file_new_for_uri ("file://somewhere"); name = g_file_get_parse_name (file); g_assert_cmpstr (name, ==, "file://somewhere"); g_object_unref (file); g_free (name); file = g_file_parse_name ("~foo"); name = g_file_get_parse_name (file); g_assert (name != NULL); g_object_unref (file); g_free (name); } typedef struct { GFile *file; GFileMonitor *monitor; GOutputStream *ostream; GInputStream *istream; GMainLoop *loop; gint buffersize; gint monitor_created; gint monitor_deleted; gint monitor_changed; gchar *monitor_path; gint pos; const gchar *data; gchar *buffer; guint timeout; } CreateDeleteData; static void monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, gpointer user_data) { CreateDeleteData *data = user_data; gchar *path; const gchar *peeked_path; path = g_file_get_path (file); peeked_path = g_file_peek_path (file); g_assert_cmpstr (data->monitor_path, ==, path); g_assert_cmpstr (path, ==, peeked_path); g_free (path); if (event_type == G_FILE_MONITOR_EVENT_CREATED) data->monitor_created++; if (event_type == G_FILE_MONITOR_EVENT_DELETED) data->monitor_deleted++; if (event_type == G_FILE_MONITOR_EVENT_CHANGED) data->monitor_changed++; } static gboolean quit_idle (gpointer user_data) { CreateDeleteData *data = user_data; g_source_remove (data->timeout); g_main_loop_quit (data->loop); return FALSE; } static void iclosed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { CreateDeleteData *data = user_data; GError *error; gboolean ret; error = NULL; ret = g_input_stream_close_finish (data->istream, res, &error); g_assert_no_error (error); g_assert (ret); g_assert (g_input_stream_is_closed (data->istream)); ret = g_file_delete (data->file, NULL, &error); g_assert (ret); g_assert_no_error (error); /* work around file monitor bug: * inotify events are only processed every 1000 ms, regardless * of the rate limit set on the file monitor */ g_timeout_add (2000, quit_idle, data); } static void read_cb (GObject *source, GAsyncResult *res, gpointer user_data) { CreateDeleteData *data = user_data; GError *error; gssize size; error = NULL; size = g_input_stream_read_finish (data->istream, res, &error); g_assert_no_error (error); data->pos += size; if (data->pos < strlen (data->data)) { g_input_stream_read_async (data->istream, data->buffer + data->pos, strlen (data->data) - data->pos, 0, NULL, read_cb, data); } else { g_assert_cmpstr (data->buffer, ==, data->data); g_assert (!g_input_stream_is_closed (data->istream)); g_input_stream_close_async (data->istream, 0, NULL, iclosed_cb, data); } } static void ipending_cb (GObject *source, GAsyncResult *res, gpointer user_data) { CreateDeleteData *data = user_data; GError *error; error = NULL; g_input_stream_read_finish (data->istream, res, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); g_error_free (error); } static void skipped_cb (GObject *source, GAsyncResult *res, gpointer user_data) { CreateDeleteData *data = user_data; GError *error; gssize size; error = NULL; size = g_input_stream_skip_finish (data->istream, res, &error); g_assert_no_error (error); g_assert_cmpint (size, ==, data->pos); g_input_stream_read_async (data->istream, data->buffer + data->pos, strlen (data->data) - data->pos, 0, NULL, read_cb, data); /* check that we get a pending error */ g_input_stream_read_async (data->istream, data->buffer + data->pos, strlen (data->data) - data->pos, 0, NULL, ipending_cb, data); } static void opened_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GFileInputStream *base; CreateDeleteData *data = user_data; GError *error; error = NULL; base = g_file_read_finish (data->file, res, &error); g_assert_no_error (error); if (data->buffersize == 0) data->istream = G_INPUT_STREAM (g_object_ref (base)); else data->istream = g_buffered_input_stream_new_sized (G_INPUT_STREAM (base), data->buffersize); g_object_unref (base); data->buffer = g_new0 (gchar, strlen (data->data) + 1); /* copy initial segment directly, then skip */ memcpy (data->buffer, data->data, 10); data->pos = 10; g_input_stream_skip_async (data->istream, 10, 0, NULL, skipped_cb, data); } static void oclosed_cb (GObject *source, GAsyncResult *res, gpointer user_data) { CreateDeleteData *data = user_data; GError *error; gboolean ret; error = NULL; ret = g_output_stream_close_finish (data->ostream, res, &error); g_assert_no_error (error); g_assert (ret); g_assert (g_output_stream_is_closed (data->ostream)); g_file_read_async (data->file, 0, NULL, opened_cb, data); } static void written_cb (GObject *source, GAsyncResult *res, gpointer user_data) { CreateDeleteData *data = user_data; gssize size; GError *error; error = NULL; size = g_output_stream_write_finish (data->ostream, res, &error); g_assert_no_error (error); data->pos += size; if (data->pos < strlen (data->data)) { g_output_stream_write_async (data->ostream, data->data + data->pos, strlen (data->data) - data->pos, 0, NULL, written_cb, data); } else { g_assert (!g_output_stream_is_closed (data->ostream)); g_output_stream_close_async (data->ostream, 0, NULL, oclosed_cb, data); } } static void opending_cb (GObject *source, GAsyncResult *res, gpointer user_data) { CreateDeleteData *data = user_data; GError *error; error = NULL; g_output_stream_write_finish (data->ostream, res, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); g_error_free (error); } static void created_cb (GObject *source, GAsyncResult *res, gpointer user_data) { GFileOutputStream *base; CreateDeleteData *data = user_data; GError *error; error = NULL; base = g_file_create_finish (G_FILE (source), res, &error); g_assert_no_error (error); g_assert (g_file_query_exists (data->file, NULL)); if (data->buffersize == 0) data->ostream = G_OUTPUT_STREAM (g_object_ref (base)); else data->ostream = g_buffered_output_stream_new_sized (G_OUTPUT_STREAM (base), data->buffersize); g_object_unref (base); g_output_stream_write_async (data->ostream, data->data, strlen (data->data), 0, NULL, written_cb, data); /* check that we get a pending error */ g_output_stream_write_async (data->ostream, data->data, strlen (data->data), 0, NULL, opending_cb, data); } static gboolean stop_timeout (gpointer data) { g_assert_not_reached (); return FALSE; } /* * This test does a fully async create-write-read-delete. * Callbackistan. */ static void test_create_delete (gconstpointer d) { GError *error; CreateDeleteData *data; GFileIOStream *iostream; data = g_new0 (CreateDeleteData, 1); data->buffersize = GPOINTER_TO_INT (d); data->data = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789"; data->pos = 0; data->file = g_file_new_tmp ("g_file_create_delete_XXXXXX", &iostream, NULL); g_assert (data->file != NULL); g_object_unref (iostream); data->monitor_path = g_file_get_path (data->file); remove (data->monitor_path); g_assert (!g_file_query_exists (data->file, NULL)); error = NULL; data->monitor = g_file_monitor_file (data->file, 0, NULL, &error); g_assert_no_error (error); /* This test doesn't work with GPollFileMonitor, because it assumes * that the monitor will notice a create immediately followed by a * delete, rather than coalescing them into nothing. */ /* This test also doesn't work with GKqueueFileMonitor because of * the same reason. Kqueue is able to return a kevent when a file is * created or deleted in a directory. However, the kernel doesn't tell * the program file names, so GKqueueFileMonitor has to calculate the * difference itself. This is usually too slow for rapid file creation * and deletion tests. */ if (strcmp (G_OBJECT_TYPE_NAME (data->monitor), "GPollFileMonitor") == 0 || strcmp (G_OBJECT_TYPE_NAME (data->monitor), "GKqueueFileMonitor") == 0) { g_test_skip ("skipping test for this GFileMonitor implementation"); goto skip; } g_file_monitor_set_rate_limit (data->monitor, 100); g_signal_connect (data->monitor, "changed", G_CALLBACK (monitor_changed), data); data->loop = g_main_loop_new (NULL, FALSE); data->timeout = g_timeout_add (10000, stop_timeout, NULL); g_file_create_async (data->file, 0, 0, NULL, created_cb, data); g_main_loop_run (data->loop); g_assert_cmpint (data->monitor_created, ==, 1); g_assert_cmpint (data->monitor_deleted, ==, 1); g_assert_cmpint (data->monitor_changed, >, 0); g_assert (!g_file_monitor_is_cancelled (data->monitor)); g_file_monitor_cancel (data->monitor); g_assert (g_file_monitor_is_cancelled (data->monitor)); g_main_loop_unref (data->loop); g_object_unref (data->ostream); g_object_unref (data->istream); skip: g_object_unref (data->monitor); g_object_unref (data->file); g_free (data->monitor_path); g_free (data->buffer); g_free (data); } static const gchar *original_data = "/**\n" " * g_file_replace_contents_async:\n" "**/\n"; static const gchar *replace_data = "/**\n" " * g_file_replace_contents_async:\n" " * @file: input #GFile.\n" " * @contents: string of contents to replace the file with.\n" " * @length: the length of @contents in bytes.\n" " * @etag: (nullable): a new entity tag for the @file, or %NULL\n" " * @make_backup: %TRUE if a backup should be created.\n" " * @flags: a set of #GFileCreateFlags.\n" " * @cancellable: optional #GCancellable object, %NULL to ignore.\n" " * @callback: a #GAsyncReadyCallback to call when the request is satisfied\n" " * @user_data: the data to pass to callback function\n" " * \n" " * Starts an asynchronous replacement of @file with the given \n" " * @contents of @length bytes. @etag will replace the document's\n" " * current entity tag.\n" " * \n" " * When this operation has completed, @callback will be called with\n" " * @user_user data, and the operation can be finalized with \n" " * g_file_replace_contents_finish().\n" " * \n" " * If @cancellable is not %NULL, then the operation can be cancelled by\n" " * triggering the cancellable object from another thread. If the operation\n" " * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. \n" " * \n" " * If @make_backup is %TRUE, this function will attempt to \n" " * make a backup of @file.\n" " **/\n"; typedef struct { GFile *file; const gchar *data; GMainLoop *loop; gboolean again; } ReplaceLoadData; static void replaced_cb (GObject *source, GAsyncResult *res, gpointer user_data); static void loaded_cb (GObject *source, GAsyncResult *res, gpointer user_data) { ReplaceLoadData *data = user_data; gboolean ret; GError *error; gchar *contents; gsize length; error = NULL; ret = g_file_load_contents_finish (data->file, res, &contents, &length, NULL, &error); g_assert (ret); g_assert_no_error (error); g_assert_cmpint (length, ==, strlen (data->data)); g_assert_cmpstr (contents, ==, data->data); g_free (contents); if (data->again) { data->again = FALSE; data->data = "pi pa po"; g_file_replace_contents_async (data->file, data->data, strlen (data->data), NULL, FALSE, 0, NULL, replaced_cb, data); } else { error = NULL; ret = g_file_delete (data->file, NULL, &error); g_assert_no_error (error); g_assert (ret); g_assert (!g_file_query_exists (data->file, NULL)); g_main_loop_quit (data->loop); } } static void replaced_cb (GObject *source, GAsyncResult *res, gpointer user_data) { ReplaceLoadData *data = user_data; GError *error; error = NULL; g_file_replace_contents_finish (data->file, res, NULL, &error); g_assert_no_error (error); g_file_load_contents_async (data->file, NULL, loaded_cb, data); } static void test_replace_load (void) { ReplaceLoadData *data; const gchar *path; GFileIOStream *iostream; data = g_new0 (ReplaceLoadData, 1); data->again = TRUE; data->data = replace_data; data->file = g_file_new_tmp ("g_file_replace_load_XXXXXX", &iostream, NULL); g_assert (data->file != NULL); g_object_unref (iostream); path = g_file_peek_path (data->file); remove (path); g_assert (!g_file_query_exists (data->file, NULL)); data->loop = g_main_loop_new (NULL, FALSE); g_file_replace_contents_async (data->file, data->data, strlen (data->data), NULL, FALSE, 0, NULL, replaced_cb, data); g_main_loop_run (data->loop); g_main_loop_unref (data->loop); g_object_unref (data->file); g_free (data); } static void test_replace_cancel (void) { GFile *tmpdir, *file; GFileOutputStream *ostream; GFileEnumerator *fenum; GFileInfo *info; GCancellable *cancellable; gchar *path; gchar *contents; gsize nwrote, length; guint count; GError *error = NULL; g_test_bug ("629301"); path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error); g_assert_no_error (error); tmpdir = g_file_new_for_path (path); g_free (path); file = g_file_get_child (tmpdir, "file"); g_file_replace_contents (file, original_data, strlen (original_data), NULL, FALSE, 0, NULL, NULL, &error); g_assert_no_error (error); ostream = g_file_replace (file, NULL, TRUE, 0, NULL, &error); g_assert_no_error (error); g_output_stream_write_all (G_OUTPUT_STREAM (ostream), replace_data, strlen (replace_data), &nwrote, NULL, &error); g_assert_no_error (error); g_assert_cmpint (nwrote, ==, strlen (replace_data)); /* At this point there should be two files; the original and the * temporary. */ fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error); g_assert_no_error (error); info = g_file_enumerator_next_file (fenum, NULL, &error); g_assert_no_error (error); g_assert (info != NULL); g_object_unref (info); info = g_file_enumerator_next_file (fenum, NULL, &error); g_assert_no_error (error); g_assert (info != NULL); g_object_unref (info); g_file_enumerator_close (fenum, NULL, &error); g_assert_no_error (error); g_object_unref (fenum); /* Also test the g_file_enumerator_iterate() API */ fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error); g_assert_no_error (error); count = 0; while (TRUE) { gboolean ret = g_file_enumerator_iterate (fenum, &info, NULL, NULL, &error); g_assert (ret); g_assert_no_error (error); if (!info) break; count++; } g_assert_cmpint (count, ==, 2); g_file_enumerator_close (fenum, NULL, &error); g_assert_no_error (error); g_object_unref (fenum); /* Now test just getting child from the g_file_enumerator_iterate() API */ fenum = g_file_enumerate_children (tmpdir, "standard::name", 0, NULL, &error); g_assert_no_error (error); count = 0; while (TRUE) { GFile *child; gboolean ret = g_file_enumerator_iterate (fenum, NULL, &child, NULL, &error); g_assert (ret); g_assert_no_error (error); if (!child) break; g_assert (G_IS_FILE (child)); count++; } g_assert_cmpint (count, ==, 2); g_file_enumerator_close (fenum, NULL, &error); g_assert_no_error (error); g_object_unref (fenum); /* Make sure the temporary gets deleted even if we cancel. */ cancellable = g_cancellable_new (); g_cancellable_cancel (cancellable); g_output_stream_close (G_OUTPUT_STREAM (ostream), cancellable, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&error); g_object_unref (cancellable); g_object_unref (ostream); /* Make sure that file contents wasn't actually replaced. */ g_file_load_contents (file, NULL, &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_cmpstr (contents, ==, original_data); g_free (contents); g_file_delete (file, NULL, &error); g_assert_no_error (error); g_object_unref (file); /* This will only succeed if the temp file was deleted. */ g_file_delete (tmpdir, NULL, &error); g_assert_no_error (error); g_object_unref (tmpdir); } static void on_file_deleted (GObject *object, GAsyncResult *result, gpointer user_data) { GError *local_error = NULL; GMainLoop *loop = user_data; (void) g_file_delete_finish ((GFile*)object, result, &local_error); g_assert_no_error (local_error); g_main_loop_quit (loop); } static void test_async_delete (void) { GFile *file; GFileIOStream *iostream; GError *local_error = NULL; GError **error = &local_error; GMainLoop *loop; file = g_file_new_tmp ("g_file_delete_XXXXXX", &iostream, error); g_assert_no_error (local_error); g_object_unref (iostream); g_assert (g_file_query_exists (file, NULL)); loop = g_main_loop_new (NULL, TRUE); g_file_delete_async (file, G_PRIORITY_DEFAULT, NULL, on_file_deleted, loop); g_main_loop_run (loop); g_assert (!g_file_query_exists (file, NULL)); g_main_loop_unref (loop); g_object_unref (file); } static void test_copy_preserve_mode (void) { #ifdef G_OS_UNIX GFile *tmpfile; GFile *dest_tmpfile; GFileInfo *dest_info; GFileIOStream *iostream; GError *local_error = NULL; GError **error = &local_error; guint32 romode = S_IFREG | 0600; guint32 dest_mode; tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX", &iostream, error); g_assert_no_error (local_error); g_io_stream_close ((GIOStream*)iostream, NULL, error); g_assert_no_error (local_error); g_clear_object (&iostream); g_file_set_attribute (tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_ATTRIBUTE_TYPE_UINT32, &romode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error); g_assert_no_error (local_error); dest_tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX", &iostream, error); g_assert_no_error (local_error); g_io_stream_close ((GIOStream*)iostream, NULL, error); g_assert_no_error (local_error); g_clear_object (&iostream); g_file_copy (tmpfile, dest_tmpfile, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA, NULL, NULL, NULL, error); g_assert_no_error (local_error); dest_info = g_file_query_info (dest_tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, error); g_assert_no_error (local_error); dest_mode = g_file_info_get_attribute_uint32 (dest_info, G_FILE_ATTRIBUTE_UNIX_MODE); g_assert_cmpint (dest_mode, ==, romode); (void) g_file_delete (tmpfile, NULL, NULL); (void) g_file_delete (dest_tmpfile, NULL, NULL); g_clear_object (&tmpfile); g_clear_object (&dest_tmpfile); g_clear_object (&dest_info); #else /* if !G_OS_UNIX */ g_test_skip ("File permissions tests can only be run on Unix") #endif } static gchar * splice_to_string (GInputStream *stream, GError **error) { GMemoryOutputStream *buffer = NULL; char *ret = NULL; buffer = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free); if (g_output_stream_splice ((GOutputStream*)buffer, stream, 0, NULL, error) < 0) goto out; if (!g_output_stream_write ((GOutputStream*)buffer, "\0", 1, NULL, error)) goto out; if (!g_output_stream_close ((GOutputStream*)buffer, NULL, error)) goto out; ret = g_memory_output_stream_steal_data (buffer); out: g_clear_object (&buffer); return ret; } static gboolean get_size_from_du (const gchar *path, guint64 *size) { GSubprocess *du; gboolean ok; gchar *result; gchar *endptr; GError *error = NULL; gchar *du_path = NULL; /* If we can’t find du, don’t try and run the test. */ du_path = g_find_program_in_path ("du"); if (du_path == NULL) return FALSE; g_free (du_path); du = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error, "du", "--bytes", "-s", path, NULL); g_assert_no_error (error); result = splice_to_string (g_subprocess_get_stdout_pipe (du), &error); g_assert_no_error (error); *size = g_ascii_strtoll (result, &endptr, 10); g_subprocess_wait (du, NULL, &error); g_assert_no_error (error); ok = g_subprocess_get_successful (du); g_object_unref (du); g_free (result); return ok; } static void test_measure (void) { GFile *file; guint64 size; guint64 num_bytes; guint64 num_dirs; guint64 num_files; GError *error = NULL; gboolean ok; gchar *path; path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL); file = g_file_new_for_path (path); if (!get_size_from_du (path, &size)) { g_test_message ("du not found or fail to run, skipping byte measurement"); size = 0; } ok = g_file_measure_disk_usage (file, G_FILE_MEASURE_APPARENT_SIZE, NULL, NULL, NULL, &num_bytes, &num_dirs, &num_files, &error); g_assert (ok); g_assert_no_error (error); if (size > 0) g_assert_cmpuint (num_bytes, ==, size); g_assert_cmpuint (num_dirs, ==, 6); g_assert_cmpuint (num_files, ==, 31); g_object_unref (file); g_free (path); } typedef struct { guint64 expected_bytes; guint64 expected_dirs; guint64 expected_files; gint progress_count; guint64 progress_bytes; guint64 progress_dirs; guint64 progress_files; } MeasureData; static void measure_progress (gboolean reporting, guint64 current_size, guint64 num_dirs, guint64 num_files, gpointer user_data) { MeasureData *data = user_data; data->progress_count += 1; g_assert_cmpuint (current_size, >=, data->progress_bytes); g_assert_cmpuint (num_dirs, >=, data->progress_dirs); g_assert_cmpuint (num_files, >=, data->progress_files); data->progress_bytes = current_size; data->progress_dirs = num_dirs; data->progress_files = num_files; } static void measure_done (GObject *source, GAsyncResult *res, gpointer user_data) { MeasureData *data = user_data; guint64 num_bytes, num_dirs, num_files; GError *error = NULL; gboolean ok; ok = g_file_measure_disk_usage_finish (G_FILE (source), res, &num_bytes, &num_dirs, &num_files, &error); g_assert (ok); g_assert_no_error (error); if (data->expected_bytes > 0) g_assert_cmpuint (data->expected_bytes, ==, num_bytes); g_assert_cmpuint (data->expected_dirs, ==, num_dirs); g_assert_cmpuint (data->expected_files, ==, num_files); g_assert_cmpuint (data->progress_count, >, 0); g_assert_cmpuint (num_bytes, >=, data->progress_bytes); g_assert_cmpuint (num_dirs, >=, data->progress_dirs); g_assert_cmpuint (num_files, >=, data->progress_files); g_free (data); g_object_unref (source); } static void test_measure_async (void) { gchar *path; GFile *file; MeasureData *data; data = g_new (MeasureData, 1); data->progress_count = 0; data->progress_bytes = 0; data->progress_files = 0; data->progress_dirs = 0; path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL); file = g_file_new_for_path (path); if (!get_size_from_du (path, &data->expected_bytes)) { g_test_message ("du not found or fail to run, skipping byte measurement"); data->expected_bytes = 0; } g_free (path); data->expected_dirs = 6; data->expected_files = 31; g_file_measure_disk_usage_async (file, G_FILE_MEASURE_APPARENT_SIZE, 0, NULL, measure_progress, data, measure_done, data); } static void test_load_bytes (void) { gchar filename[] = "g_file_load_bytes_XXXXXX"; GError *error = NULL; GBytes *bytes; GFile *file; int len; int fd; int ret; fd = g_mkstemp (filename); g_assert_cmpint (fd, !=, -1); len = strlen ("test_load_bytes"); ret = write (fd, "test_load_bytes", len); g_assert_cmpint (ret, ==, len); close (fd); file = g_file_new_for_path (filename); bytes = g_file_load_bytes (file, NULL, NULL, &error); g_assert_no_error (error); g_assert (bytes != NULL); g_assert_cmpint (len, ==, g_bytes_get_size (bytes)); g_assert_cmpstr ("test_load_bytes", ==, (gchar *)g_bytes_get_data (bytes, NULL)); g_file_delete (file, NULL, NULL); g_bytes_unref (bytes); g_object_unref (file); } typedef struct { GMainLoop *main_loop; GFile *file; GBytes *bytes; } LoadBytesAsyncData; static void test_load_bytes_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GFile *file = G_FILE (object); LoadBytesAsyncData *data = user_data; GError *error = NULL; data->bytes = g_file_load_bytes_finish (file, result, NULL, &error); g_assert_no_error (error); g_assert (data->bytes != NULL); g_main_loop_quit (data->main_loop); } static void test_load_bytes_async (void) { LoadBytesAsyncData data = { 0 }; gchar filename[] = "g_file_load_bytes_XXXXXX"; int len; int fd; int ret; fd = g_mkstemp (filename); g_assert_cmpint (fd, !=, -1); len = strlen ("test_load_bytes_async"); ret = write (fd, "test_load_bytes_async", len); g_assert_cmpint (ret, ==, len); close (fd); data.main_loop = g_main_loop_new (NULL, FALSE); data.file = g_file_new_for_path (filename); g_file_load_bytes_async (data.file, NULL, test_load_bytes_cb, &data); g_main_loop_run (data.main_loop); g_assert_cmpint (len, ==, g_bytes_get_size (data.bytes)); g_assert_cmpstr ("test_load_bytes_async", ==, (gchar *)g_bytes_get_data (data.bytes, NULL)); g_file_delete (data.file, NULL, NULL); g_object_unref (data.file); g_bytes_unref (data.bytes); g_main_loop_unref (data.main_loop); } static void test_writev_helper (GOutputVector *vectors, gsize n_vectors, gboolean use_bytes_written, const guint8 *expected_contents, gsize expected_length) { GFile *file; GFileIOStream *iostream = NULL; GOutputStream *ostream; GError *error = NULL; gsize bytes_written = 0; gboolean res; guint8 *contents; gsize length; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); res = g_output_stream_writev_all (ostream, vectors, n_vectors, use_bytes_written ? &bytes_written : NULL, NULL, &error); g_assert_no_error (error); g_assert_true (res); if (use_bytes_written) g_assert_cmpuint (bytes_written, ==, expected_length); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpmem (contents, length, expected_contents, expected_length); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); } /* Test that writev() on local file output streams works on a non-empty vector */ static void test_writev (void) { GOutputVector vectors[3]; const guint8 buffer[] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3}; vectors[0].buffer = buffer; vectors[0].size = 5; vectors[1].buffer = buffer + 5; vectors[1].size = 12; vectors[2].buffer = buffer + 5 + 12; vectors[2].size = 3; test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, buffer, sizeof buffer); } /* Test that writev() on local file output streams works on a non-empty vector without returning bytes_written */ static void test_writev_no_bytes_written (void) { GOutputVector vectors[3]; const guint8 buffer[] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3}; vectors[0].buffer = buffer; vectors[0].size = 5; vectors[1].buffer = buffer + 5; vectors[1].size = 12; vectors[2].buffer = buffer + 5 + 12; vectors[2].size = 3; test_writev_helper (vectors, G_N_ELEMENTS (vectors), FALSE, buffer, sizeof buffer); } /* Test that writev() on local file output streams works on 0 vectors */ static void test_writev_no_vectors (void) { test_writev_helper (NULL, 0, TRUE, NULL, 0); } /* Test that writev() on local file output streams works on empty vectors */ static void test_writev_empty_vectors (void) { GOutputVector vectors[3]; vectors[0].buffer = NULL; vectors[0].size = 0; vectors[1].buffer = NULL; vectors[1].size = 0; vectors[2].buffer = NULL; vectors[2].size = 0; test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, NULL, 0); } /* Test that writev() fails if the sum of sizes in the vector is too big */ static void test_writev_too_big_vectors (void) { GFile *file; GFileIOStream *iostream = NULL; GOutputStream *ostream; GError *error = NULL; gsize bytes_written = 0; gboolean res; guint8 *contents; gsize length; GOutputVector vectors[3]; vectors[0].buffer = (void*) 1; vectors[0].size = G_MAXSIZE / 2; vectors[1].buffer = (void*) 1; vectors[1].size = G_MAXSIZE / 2; vectors[2].buffer = (void*) 1; vectors[2].size = G_MAXSIZE / 2; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); res = g_output_stream_writev_all (ostream, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_assert_cmpuint (bytes_written, ==, 0); g_assert_false (res); g_clear_error (&error); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpmem (contents, length, NULL, 0); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); } typedef struct { gsize bytes_written; GOutputVector *vectors; gsize n_vectors; GError *error; gboolean done; } WritevAsyncData; static void test_writev_async_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GOutputStream *ostream = G_OUTPUT_STREAM (object); WritevAsyncData *data = user_data; GError *error = NULL; gsize bytes_written; gboolean res; res = g_output_stream_writev_finish (ostream, result, &bytes_written, &error); g_assert_true (res); g_assert_no_error (error); data->bytes_written += bytes_written; /* skip vectors that have been written in full */ while (data->n_vectors > 0 && bytes_written >= data->vectors[0].size) { bytes_written -= data->vectors[0].size; ++data->vectors; --data->n_vectors; } /* skip partially written vector data */ if (bytes_written > 0 && data->n_vectors > 0) { data->vectors[0].size -= bytes_written; data->vectors[0].buffer = ((guint8 *) data->vectors[0].buffer) + bytes_written; } if (data->n_vectors > 0) g_output_stream_writev_async (ostream, data->vectors, data->n_vectors, 0, NULL, test_writev_async_cb, &data); } /* Test that writev_async() on local file output streams works on a non-empty vector */ static void test_writev_async (void) { WritevAsyncData data = { 0 }; GFile *file; GFileIOStream *iostream = NULL; GOutputVector vectors[3]; const guint8 buffer[] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3}; GOutputStream *ostream; GError *error = NULL; gboolean res; guint8 *contents; gsize length; vectors[0].buffer = buffer; vectors[0].size = 5; vectors[1].buffer = buffer + 5; vectors[1].size = 12; vectors[2].buffer = buffer + 5 + 12; vectors[2].size = 3; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); data.vectors = vectors; data.n_vectors = G_N_ELEMENTS (vectors); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); g_output_stream_writev_async (ostream, data.vectors, data.n_vectors, 0, NULL, test_writev_async_cb, &data); while (data.n_vectors > 0) g_main_context_iteration (NULL, TRUE); g_assert_cmpuint (data.bytes_written, ==, sizeof buffer); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpmem (contents, length, buffer, sizeof buffer); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); } static void test_writev_all_cb (GObject *object, GAsyncResult *result, gpointer user_data) { GOutputStream *ostream = G_OUTPUT_STREAM (object); WritevAsyncData *data = user_data; g_output_stream_writev_all_finish (ostream, result, &data->bytes_written, &data->error); data->done = TRUE; } /* Test that writev_async_all() on local file output streams works on a non-empty vector */ static void test_writev_async_all (void) { WritevAsyncData data = { 0 }; GFile *file; GFileIOStream *iostream = NULL; GOutputStream *ostream; GOutputVector vectors[3]; const guint8 buffer[] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3}; GError *error = NULL; gboolean res; guint8 *contents; gsize length; vectors[0].buffer = buffer; vectors[0].size = 5; vectors[1].buffer = buffer + 5; vectors[1].size = 12; vectors[2].buffer = buffer + 5 + 12; vectors[2].size = 3; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); while (!data.done) g_main_context_iteration (NULL, TRUE); g_assert_cmpuint (data.bytes_written, ==, sizeof buffer); g_assert_no_error (data.error); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpmem (contents, length, buffer, sizeof buffer); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); } /* Test that writev_async_all() on local file output streams handles cancellation correctly */ static void test_writev_async_all_cancellation (void) { WritevAsyncData data = { 0 }; GFile *file; GFileIOStream *iostream = NULL; GOutputVector vectors[3]; const guint8 buffer[] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3}; GOutputStream *ostream; GError *error = NULL; gboolean res; guint8 *contents; gsize length; GCancellable *cancellable; vectors[0].buffer = buffer; vectors[0].size = 5; vectors[1].buffer = buffer + 5; vectors[1].size = 12; vectors[2].buffer = buffer + 5 + 12; vectors[2].size = 3; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); cancellable = g_cancellable_new (); g_cancellable_cancel (cancellable); g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, cancellable, test_writev_all_cb, &data); while (!data.done) g_main_context_iteration (NULL, TRUE); g_assert_cmpuint (data.bytes_written, ==, 0); g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_clear_error (&data.error); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpuint (length, ==, 0); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); g_object_unref (cancellable); } /* Test that writev_async_all() with empty vectors is handled correctly */ static void test_writev_async_all_empty_vectors (void) { WritevAsyncData data = { 0 }; GFile *file; GFileIOStream *iostream = NULL; GOutputVector vectors[3]; GOutputStream *ostream; GError *error = NULL; gboolean res; guint8 *contents; gsize length; vectors[0].buffer = NULL; vectors[0].size = 0; vectors[1].buffer = NULL; vectors[1].size = 0; vectors[2].buffer = NULL; vectors[2].size = 0; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); while (!data.done) g_main_context_iteration (NULL, TRUE); g_assert_cmpuint (data.bytes_written, ==, 0); g_assert_no_error (data.error); g_clear_error (&data.error); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpuint (length, ==, 0); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); } /* Test that writev_async_all() with no vectors is handled correctly */ static void test_writev_async_all_no_vectors (void) { WritevAsyncData data = { 0 }; GFile *file; GFileIOStream *iostream = NULL; GOutputStream *ostream; GError *error = NULL; gboolean res; guint8 *contents; gsize length; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); g_output_stream_writev_all_async (ostream, NULL, 0, 0, NULL, test_writev_all_cb, &data); while (!data.done) g_main_context_iteration (NULL, TRUE); g_assert_cmpuint (data.bytes_written, ==, 0); g_assert_no_error (data.error); g_clear_error (&data.error); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpuint (length, ==, 0); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); } /* Test that writev_async_all() with too big vectors is handled correctly */ static void test_writev_async_all_too_big_vectors (void) { WritevAsyncData data = { 0 }; GFile *file; GFileIOStream *iostream = NULL; GOutputVector vectors[3]; GOutputStream *ostream; GError *error = NULL; gboolean res; guint8 *contents; gsize length; vectors[0].buffer = (void*) 1; vectors[0].size = G_MAXSIZE / 2; vectors[1].buffer = (void*) 1; vectors[1].size = G_MAXSIZE / 2; vectors[2].buffer = (void*) 1; vectors[2].size = G_MAXSIZE / 2; file = g_file_new_tmp ("g_file_writev_XXXXXX", &iostream, NULL); g_assert_nonnull (file); g_assert_nonnull (iostream); ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream)); g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data); while (!data.done) g_main_context_iteration (NULL, TRUE); g_assert_cmpuint (data.bytes_written, ==, 0); g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); g_clear_error (&data.error); res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error); g_assert_no_error (error); g_assert_true (res); g_object_unref (iostream); res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error); g_assert_no_error (error); g_assert_true (res); g_assert_cmpuint (length, ==, 0); g_free (contents); g_file_delete (file, NULL, NULL); g_object_unref (file); } int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugzilla.gnome.org/"); g_test_add_func ("/file/basic", test_basic); g_test_add_func ("/file/build-filename", test_build_filename); g_test_add_func ("/file/parent", test_parent); g_test_add_func ("/file/child", test_child); g_test_add_func ("/file/type", test_type); g_test_add_func ("/file/parse-name", test_parse_name); g_test_add_data_func ("/file/async-create-delete/0", GINT_TO_POINTER (0), test_create_delete); g_test_add_data_func ("/file/async-create-delete/1", GINT_TO_POINTER (1), test_create_delete); g_test_add_data_func ("/file/async-create-delete/10", GINT_TO_POINTER (10), test_create_delete); g_test_add_data_func ("/file/async-create-delete/25", GINT_TO_POINTER (25), test_create_delete); g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete); g_test_add_func ("/file/replace-load", test_replace_load); g_test_add_func ("/file/replace-cancel", test_replace_cancel); g_test_add_func ("/file/async-delete", test_async_delete); g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode); g_test_add_func ("/file/measure", test_measure); g_test_add_func ("/file/measure-async", test_measure_async); g_test_add_func ("/file/load-bytes", test_load_bytes); g_test_add_func ("/file/load-bytes-async", test_load_bytes_async); g_test_add_func ("/file/writev", test_writev); g_test_add_func ("/file/writev/no-bytes-written", test_writev_no_bytes_written); g_test_add_func ("/file/writev/no-vectors", test_writev_no_vectors); g_test_add_func ("/file/writev/empty-vectors", test_writev_empty_vectors); g_test_add_func ("/file/writev/too-big-vectors", test_writev_too_big_vectors); g_test_add_func ("/file/writev/async", test_writev_async); g_test_add_func ("/file/writev/async_all", test_writev_async_all); g_test_add_func ("/file/writev/async_all-empty-vectors", test_writev_async_all_empty_vectors); g_test_add_func ("/file/writev/async_all-no-vectors", test_writev_async_all_no_vectors); g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors); g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation); return g_test_run (); }