tests: Add tests for g_file_set_contents_full()

These exercise all the code paths I can manage without adding a load of
machinery to inject faults into `write()`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #1302
This commit is contained in:
Philip Withnall 2020-07-16 13:16:15 +01:00
parent f35f9e918a
commit 5bf3849099

View File

@ -950,6 +950,257 @@ test_set_contents (void)
g_free (name);
}
static void
test_set_contents_full (void)
{
GFileSetContentsFlags flags_mask =
G_FILE_SET_CONTENTS_ONLY_EXISTING |
G_FILE_SET_CONTENTS_DURABLE |
G_FILE_SET_CONTENTS_CONSISTENT;
gint flags;
const struct
{
enum
{
EXISTING_FILE_NONE,
EXISTING_FILE_REGULAR,
#ifndef G_OS_WIN32
EXISTING_FILE_SYMLINK,
#endif
EXISTING_FILE_DIRECTORY,
}
existing_file;
int new_mode; /* only relevant if @existing_file is %EXISTING_FILE_NONE */
gboolean use_strlen;
gboolean expected_success;
GFileError expected_error;
}
tests[] =
{
{ EXISTING_FILE_NONE, 0644, FALSE, TRUE, 0 },
{ EXISTING_FILE_NONE, 0644, TRUE, TRUE, 0 },
{ EXISTING_FILE_NONE, 0600, FALSE, TRUE, 0 },
{ EXISTING_FILE_REGULAR, 0644, FALSE, TRUE, 0 },
#ifndef G_OS_WIN32
{ EXISTING_FILE_SYMLINK, 0644, FALSE, TRUE, 0 },
#endif
{ EXISTING_FILE_DIRECTORY, 0644, FALSE, FALSE, G_FILE_ERROR_ISDIR },
};
gsize i;
g_test_summary ("Test g_file_set_contents_full() with various flags");
for (flags = 0; flags < (gint) flags_mask; flags++)
{
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
GError *error = NULL;
gchar *file_name = NULL, *link_name = NULL, *dir_name = NULL;
const gchar *set_contents_name;
gchar *buf = NULL;
gsize len;
gboolean ret;
GStatBuf statbuf;
g_test_message ("Flags %d and test %" G_GSIZE_FORMAT, flags, i);
switch (tests[i].existing_file)
{
case EXISTING_FILE_REGULAR:
#ifndef G_OS_WIN32
case EXISTING_FILE_SYMLINK:
#endif
{
gint fd;
fd = g_file_open_tmp (NULL, &file_name, &error);
g_assert_no_error (error);
write (fd, "a", 1);
g_assert_no_errno (g_fsync (fd));
close (fd);
#ifndef G_OS_WIN32
/* Pass an existing symlink to g_file_set_contents_full() to see
* what it does. */
if (tests[i].existing_file == EXISTING_FILE_SYMLINK)
{
link_name = g_strconcat (file_name, ".link", NULL);
g_assert_no_errno (symlink (file_name, link_name));
set_contents_name = link_name;
}
else
#endif /* !G_OS_WIN32 */
{
set_contents_name = file_name;
}
break;
}
case EXISTING_FILE_DIRECTORY:
{
dir_name = g_dir_make_tmp ("glib-fileutils-set-contents-full-XXXXXX", &error);
g_assert_no_error (error);
set_contents_name = dir_name;
break;
}
case EXISTING_FILE_NONE:
{
file_name = g_build_filename (g_get_tmp_dir (), "glib-file-set-contents-full-test", NULL);
g_remove (file_name);
g_assert_false (g_file_test (file_name, G_FILE_TEST_EXISTS));
set_contents_name = file_name;
break;
}
default:
{
g_assert_not_reached ();
}
}
/* Set the file contents */
ret = g_file_set_contents_full (set_contents_name, "b",
tests[i].use_strlen ? -1 : 1,
flags, tests[i].new_mode, &error);
if (!tests[i].expected_success)
{
g_assert_error (error, G_FILE_ERROR, tests[i].expected_error);
g_assert_false (ret);
g_clear_error (&error);
}
else
{
g_assert_no_error (error);
g_assert_true (ret);
/* Check the contents and mode were set correctly. The mode isnt
* changed on existing files. */
ret = g_file_get_contents (set_contents_name, &buf, &len, &error);
g_assert_no_error (error);
g_assert_true (ret);
g_assert_cmpstr (buf, ==, "b");
g_assert_cmpuint (len, ==, 1);
g_free (buf);
g_assert_no_errno (g_lstat (set_contents_name, &statbuf));
if (tests[i].existing_file == EXISTING_FILE_NONE)
g_assert_cmpint (statbuf.st_mode & ~S_IFMT, ==, tests[i].new_mode);
#ifndef G_OS_WIN32
if (tests[i].existing_file == EXISTING_FILE_SYMLINK)
{
gchar *target_contents = NULL;
/* If the @set_contents_name was a symlink, it should now be a
* regular file, and the file it pointed to should not have
* changed. */
g_assert_cmpint (statbuf.st_mode & S_IFMT, ==, S_IFREG);
g_file_get_contents (file_name, &target_contents, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (target_contents, ==, "a");
g_free (target_contents);
}
#endif /* !G_OS_WIN32 */
}
if (dir_name != NULL)
g_rmdir (dir_name);
if (link_name != NULL)
g_remove (link_name);
if (file_name != NULL)
g_remove (file_name);
g_free (dir_name);
g_free (link_name);
g_free (file_name);
}
}
}
static void
test_set_contents_full_read_only_file (void)
{
gint fd;
GError *error = NULL;
gchar *file_name = NULL;
gboolean ret;
g_test_summary ("Test g_file_set_contents_full() on a read-only file");
/* Cant test this with different #GFileSetContentsFlags as they all have
* different behaviours wrt replacing the file while noticing/ignoring the
* existing file permissions. */
fd = g_file_open_tmp (NULL, &file_name, &error);
g_assert_no_error (error);
write (fd, "a", 1);
g_assert_no_errno (g_fsync (fd));
close (fd);
g_assert_no_errno (chmod (file_name, 0200));
/* Set the file contents */
ret = g_file_set_contents_full (file_name, "b", 1, G_FILE_SET_CONTENTS_NONE, 0644, &error);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES);
g_assert_false (ret);
g_clear_error (&error);
g_remove (file_name);
g_free (file_name);
}
static void
test_set_contents_full_read_only_directory (void)
{
GFileSetContentsFlags flags_mask =
G_FILE_SET_CONTENTS_ONLY_EXISTING |
G_FILE_SET_CONTENTS_DURABLE |
G_FILE_SET_CONTENTS_CONSISTENT;
gint flags;
g_test_summary ("Test g_file_set_contents_full() on a file in a read-only directory");
for (flags = 0; flags < (gint) flags_mask; flags++)
{
gint fd;
GError *error = NULL;
gchar *dir_name = NULL;
gchar *file_name = NULL;
gboolean ret;
g_test_message ("Flags %d", flags);
dir_name = g_dir_make_tmp ("glib-file-set-contents-full-rodir-XXXXXX", &error);
g_assert_no_error (error);
file_name = g_build_filename (dir_name, "file", NULL);
fd = g_open (file_name, O_CREAT | O_RDWR, 0644);
g_assert_cmpint (fd, >=, 0);
write (fd, "a", 1);
g_assert_no_errno (g_fsync (fd));
close (fd);
g_assert_no_errno (chmod (dir_name, 0));
/* Set the file contents */
ret = g_file_set_contents_full (file_name, "b", 1, flags, 0644, &error);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES);
g_assert_false (ret);
g_clear_error (&error);
g_remove (file_name);
g_unlink (dir_name);
g_free (file_name);
g_free (dir_name);
}
}
static void
test_read_link (void)
{
@ -1545,6 +1796,9 @@ main (int argc,
g_test_add_func ("/fileutils/mkstemp", test_mkstemp);
g_test_add_func ("/fileutils/mkdtemp", test_mkdtemp);
g_test_add_func ("/fileutils/set-contents", test_set_contents);
g_test_add_func ("/fileutils/set-contents-full", test_set_contents_full);
g_test_add_func ("/fileutils/set-contents-full/read-only-file", test_set_contents_full_read_only_file);
g_test_add_func ("/fileutils/set-contents-full/read-only-directory", test_set_contents_full_read_only_directory);
g_test_add_func ("/fileutils/read-link", test_read_link);
g_test_add_func ("/fileutils/stdio-wrappers", test_stdio_wrappers);
g_test_add_func ("/fileutils/fopen-modes", test_fopen_modes);