/*
* This file is part of Cockpit.
*
* Copyright (C) 2014 Red Hat, Inc.
*
* Cockpit 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.1 of the License, or
* (at your option) any later version.
*
* Cockpit 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 Cockpit; If not, see .
*/
#include "config.h"
#include "cockpitfsread.h"
#include "cockpitfsreplace.h"
#include "cockpitfswatch.h"
#include "cockpitfslist.h"
#include "mock-transport.h"
#include "common/cockpittest.h"
#include "common/cockpitjson.h"
#include
#include
#include
#include
#define TIMEOUT 30
typedef struct {
MockTransport *transport;
CockpitChannel *channel;
gchar *test_dir;
gchar *test_path;
gchar *test_path_2;
gchar *test_link;
gchar *test_subdir;
gchar *problem;
gboolean channel_closed;
} TestCase;
static void
on_channel_close (CockpitChannel *channel,
const gchar *problem,
gpointer user_data)
{
TestCase *tc = user_data;
g_assert (tc->channel_closed == FALSE);
tc->channel_closed = TRUE;
}
static void
on_transport_closed (CockpitTransport *transport,
const gchar *problem,
gpointer user_data)
{
g_assert_not_reached ();
}
static void
setup (TestCase *tc,
gconstpointer data)
{
alarm (TIMEOUT);
tc->transport = mock_transport_new ();
g_signal_connect (tc->transport, "closed", G_CALLBACK (on_transport_closed), NULL);
tc->channel = NULL;
tc->test_dir = g_dir_make_tmp (NULL, NULL);
g_assert (tc->test_dir != NULL);
tc->test_path = g_strdup_printf ("%s/%s", tc->test_dir, "foo");
tc->test_path_2 = g_strdup_printf ("%s/%s", tc->test_dir, "bar");
tc->test_subdir = g_strdup_printf ("%s/%s", tc->test_dir, "subdir");
tc->test_link = g_strdup_printf ("%s/%s", tc->test_dir, "foo-link");
g_assert (unlink (tc->test_path) >= 0 || errno == ENOENT);
g_assert (unlink (tc->test_path_2) >= 0 || errno == ENOENT);
g_assert (unlink (tc->test_link) >= 0 || errno == ENOENT);
g_assert (rmdir (tc->test_subdir) >= 0 || errno == ENOENT);
}
static void
setup_fsread_channel (TestCase *tc,
const gchar *path)
{
tc->channel = cockpit_fsread_open (COCKPIT_TRANSPORT (tc->transport), "1234", path);
tc->channel_closed = FALSE;
g_signal_connect (tc->channel, "closed", G_CALLBACK (on_channel_close), tc);
cockpit_channel_prepare (tc->channel);
}
static void
setup_fsreplace_channel (TestCase *tc,
const gchar *path,
const gchar *tag)
{
tc->channel = cockpit_fsreplace_open (COCKPIT_TRANSPORT (tc->transport), "1234", path, tag);
tc->channel_closed = FALSE;
g_signal_connect (tc->channel, "closed", G_CALLBACK (on_channel_close), tc);
cockpit_channel_prepare (tc->channel);
}
static void
setup_fswatch_channel (TestCase *tc,
const gchar *path)
{
tc->channel = cockpit_fswatch_open (COCKPIT_TRANSPORT (tc->transport), "1234", path);
tc->channel_closed = FALSE;
g_signal_connect (tc->channel, "closed", G_CALLBACK (on_channel_close), tc);
cockpit_channel_prepare (tc->channel);
}
static void
setup_fslist_channel (TestCase *tc,
const gchar *path,
const gboolean watch)
{
tc->channel = cockpit_fslist_open (COCKPIT_TRANSPORT (tc->transport), "1234", path, watch);
tc->channel_closed = FALSE;
g_signal_connect (tc->channel, "closed", G_CALLBACK (on_channel_close), tc);
}
static void
send_string (TestCase *tc,
const gchar *str)
{
GBytes *bytes = g_bytes_new_static (str, strlen (str));
cockpit_transport_emit_recv (COCKPIT_TRANSPORT (tc->transport), "1234", bytes);
g_bytes_unref (bytes);
}
static void
send_done (TestCase *tc)
{
const gchar *message = "{ \"command\": \"done\", \"channel\": \"1234\" }";
GBytes *bytes = g_bytes_new_static (message, strlen (message));
cockpit_transport_emit_recv (COCKPIT_TRANSPORT (tc->transport), NULL, bytes);
g_bytes_unref (bytes);
}
static GBytes *
recv_bytes (TestCase *tc)
{
GBytes *msg;
while ((msg = mock_transport_pop_channel (tc->transport, "1234")) == NULL)
g_main_context_iteration (NULL, TRUE);
return msg;
}
static JsonObject *
recv_json (TestCase *tc)
{
GBytes *msg = recv_bytes (tc);
JsonObject *res = cockpit_json_parse_bytes (msg, NULL);
g_assert (res != NULL);
return res;
}
static JsonObject *
recv_control (TestCase *tc)
{
JsonObject *msg;
while ((msg = mock_transport_pop_control (tc->transport)) == NULL)
g_main_context_iteration (NULL, TRUE);
return msg;
}
static void
close_channel (TestCase *tc,
const gchar *problem)
{
cockpit_channel_close (tc->channel, problem);
}
static void
wait_channel_closed (TestCase *tc)
{
while (tc->channel_closed == FALSE)
g_main_context_iteration (NULL, TRUE);
}
static void
teardown (TestCase *tc,
gconstpointer data)
{
cockpit_assert_expected ();
g_object_unref (tc->transport);
if (tc->channel)
{
g_object_add_weak_pointer (G_OBJECT (tc->channel), (gpointer *)&tc->channel);
g_object_unref (tc->channel);
g_assert (tc->channel == NULL);
}
g_assert (unlink (tc->test_path) >= 0 || errno == ENOENT);
g_assert (unlink (tc->test_path_2) >= 0 || errno == ENOENT);
g_assert (unlink (tc->test_link) >= 0 || errno == ENOENT);
g_assert (rmdir (tc->test_subdir) >= 0 || errno == ENOENT);
g_assert (rmdir (tc->test_dir) >= 0);
g_free (tc->test_path);
g_free (tc->test_path_2);
g_free (tc->test_link);
g_free (tc->test_subdir);
g_free (tc->test_dir);
g_free (tc->problem);
alarm (0);
}
static GBytes *
combine_output (TestCase *tc,
guint *count)
{
GByteArray *combined;
GBytes *block;
if (count)
*count = 0;
combined = g_byte_array_new ();
for (;;)
{
block = mock_transport_pop_channel (tc->transport, "1234");
if (!block)
break;
g_byte_array_append (combined, g_bytes_get_data (block, NULL), g_bytes_get_size (block));
if (count)
(*count)++;
}
return g_byte_array_free_to_bytes (combined);
}
static void
assert_received (TestCase *tc,
const gchar *str)
{
GBytes *data;
data = combine_output (tc, NULL);
cockpit_assert_bytes_eq (data, str, -1);
g_bytes_unref (data);
}
static void
set_contents (const gchar *path,
const gchar *str)
{
g_assert (g_file_set_contents (path, str, -1, NULL));
}
static void
assert_contents (const gchar *path,
const gchar *str)
{
gchar *contents;
g_assert (g_file_get_contents (path, &contents, NULL, NULL));
g_assert_cmpstr (contents, ==, str);
g_free (contents);
}
static void
test_read_simple (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
set_contents (tc->test_path, "Hello!");
tag = cockpit_get_file_tag (tc->test_path);
setup_fsread_channel (tc, tc->test_path);
wait_channel_closed (tc);
assert_received (tc, "Hello!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "done");
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_read_non_existent (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
setup_fsread_channel (tc, "/non/existent");
wait_channel_closed (tc);
assert_received (tc, "");
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, "-");
}
static void
test_read_denied (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
if (geteuid () == 0)
{
cockpit_test_skip ("running as root");
return;
}
set_contents (tc->test_path, "Hello!");
g_assert (chmod (tc->test_path, 0) >= 0);
setup_fsread_channel (tc, tc->test_path);
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "problem"), ==, "access-denied");
}
static void
test_read_changed (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
set_contents (tc->test_path, "Hello!");
setup_fsread_channel (tc, tc->test_path);
{
sleep(1);
FILE *f = fopen (tc->test_path, "w");
g_assert (f != NULL);
fputs ("Goodbye!", f);
fclose (f);
}
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "done");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "problem"), ==, "change-conflict");
}
static void
test_read_replaced (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
set_contents (tc->test_path, "Hello!");
tag = cockpit_get_file_tag (tc->test_path);
setup_fsread_channel (tc, tc->test_path);
{
FILE *f = fopen (tc->test_path_2, "w");
g_assert (f != NULL);
g_assert (fputs ("Goodbye!", f) != EOF);
g_assert (fclose (f) != EOF);
g_assert (rename (tc->test_path_2, tc->test_path) >= 0);
}
wait_channel_closed (tc);
assert_received (tc, "Hello!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "done");
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_read_removed (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
set_contents (tc->test_path, "Hello!");
tag = cockpit_get_file_tag (tc->test_path);
setup_fsread_channel (tc, tc->test_path);
{
g_assert (unlink (tc->test_path) >= 0);
}
wait_channel_closed (tc);
assert_received (tc, "Hello!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "done");
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_read_non_mmappable (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
const gchar *path = "/sys/power/state";
tag = cockpit_get_file_tag (path);
if (g_strcmp0 (tag, "-") == 0)
{
cockpit_test_skip ("No /sys/power/state");
return;
}
setup_fsread_channel (tc, path);
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "done");
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_write_simple (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
setup_fsreplace_channel (tc, tc->test_path, NULL);
send_string (tc, "Hello!");
send_done (tc);
close_channel (tc, NULL);
wait_channel_closed (tc);
assert_contents (tc->test_path, "Hello!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
tag = cockpit_get_file_tag (tc->test_path);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_write_multiple (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
setup_fsreplace_channel (tc, tc->test_path, NULL);
send_string (tc, "Hel");
send_string (tc, "lo!");
send_done (tc);
close_channel (tc, NULL);
wait_channel_closed (tc);
assert_contents (tc->test_path, "Hello!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
tag = cockpit_get_file_tag (tc->test_path);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_write_remove (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
set_contents (tc->test_path, "Goodbye!");
tag = cockpit_get_file_tag (tc->test_path);
setup_fsreplace_channel (tc, tc->test_path, tag);
send_done (tc);
close_channel (tc, NULL);
g_free (tag);
wait_channel_closed (tc);
g_assert (g_file_test (tc->test_path, G_FILE_TEST_EXISTS) == FALSE);
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, "-");
}
static void
test_write_remove_nonexistent (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
g_assert (g_file_test (tc->test_path, G_FILE_TEST_EXISTS) == FALSE);
setup_fsreplace_channel (tc, tc->test_path, "-");
send_done (tc);
close_channel (tc, NULL);
wait_channel_closed (tc);
g_assert (g_file_test (tc->test_path, G_FILE_TEST_EXISTS) == FALSE);
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, "-");
}
static void
test_write_empty (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
set_contents (tc->test_path, "Goodbye!");
tag = cockpit_get_file_tag (tc->test_path);
setup_fsreplace_channel (tc, tc->test_path, tag);
send_string (tc, "");
send_done (tc);
close_channel (tc, NULL);
g_free (tag);
wait_channel_closed (tc);
assert_contents (tc->test_path, "");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
tag = cockpit_get_file_tag (tc->test_path);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_write_denied (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
if (geteuid () == 0)
{
cockpit_test_skip ("running as root");
return;
}
g_assert (chmod (tc->test_dir, 0) >= 0);
setup_fsreplace_channel (tc, tc->test_path, NULL);
send_string (tc, "Hello!");
send_done (tc);
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "problem"), ==, "access-denied");
g_assert (chmod (tc->test_dir, 0777) >= 0);
}
static void
test_write_expect_non_existent (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
setup_fsreplace_channel (tc, tc->test_path, "-");
send_string (tc, "Hello!");
send_done (tc);
close_channel (tc, NULL);
wait_channel_closed (tc);
assert_contents (tc->test_path, "Hello!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
tag = cockpit_get_file_tag (tc->test_path);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_write_expect_non_existent_fail (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
set_contents (tc->test_path, "Goodbye!");
setup_fsreplace_channel (tc, tc->test_path, "-");
send_string (tc, "Hello!");
send_done (tc);
close_channel (tc, NULL);
wait_channel_closed (tc);
assert_contents (tc->test_path, "Goodbye!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "problem"), ==, "change-conflict");
}
static void
test_write_expect_tag (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
set_contents (tc->test_path, "Goodbye!");
tag = cockpit_get_file_tag (tc->test_path);
setup_fsreplace_channel (tc, tc->test_path, tag);
send_string (tc, "Hello!");
send_done (tc);
close_channel (tc, NULL);
g_free (tag);
wait_channel_closed (tc);
assert_contents (tc->test_path, "Hello!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
tag = cockpit_get_file_tag (tc->test_path);
g_assert (json_object_get_member (control, "problem") == NULL);
g_assert_cmpstr (json_object_get_string_member (control, "tag"), ==, tag);
g_free (tag);
}
static void
test_write_expect_tag_fail (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *control;
set_contents (tc->test_path, "Goodbye!");
tag = cockpit_get_file_tag (tc->test_path);
setup_fsreplace_channel (tc, tc->test_path, tag);
send_string (tc, "Hello!");
set_contents (tc->test_path, "Tschüss!");
send_done (tc);
close_channel (tc, NULL);
g_free (tag);
wait_channel_closed (tc);
assert_contents (tc->test_path, "Tschüss!");
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
control = mock_transport_pop_control (tc->transport);
tag = cockpit_get_file_tag (tc->test_path);
g_assert_cmpstr (json_object_get_string_member (control, "problem"), ==, "out-of-date");
g_free (tag);
}
static void
test_watch_simple (TestCase *tc,
gconstpointer unused)
{
gchar *tag;
JsonObject *event;
setup_fswatch_channel (tc, tc->test_path);
set_contents (tc->test_path, "Wake up!");
tag = cockpit_get_file_tag (tc->test_path);
event = recv_json (tc);
g_assert (event != NULL);
/*
* HACK: https://bugzilla.redhat.com/show_bug.cgi?id=1259594
* Some versions of glib2 erroneously emit spurios "delete" events.
*/
if (g_str_equal (json_object_get_string_member (event, "event"), "deleted"))
{
json_object_unref (event);
event = recv_json (tc);
}
g_assert_cmpstr (json_object_get_string_member (event, "event"), ==, "created");
g_assert_cmpstr (json_object_get_string_member (event, "path"), ==, tc->test_path);
g_assert_cmpstr (json_object_get_string_member (event, "tag"), ==, tag);
g_assert_cmpstr (json_object_get_string_member (event, "type"), ==, "file");
json_object_unref (event);
g_free (tag);
}
static void
test_watch_remove (TestCase *tc,
gconstpointer unused)
{
JsonObject *event;
set_contents (tc->test_path, "Hello!");
setup_fswatch_channel (tc, tc->test_path);
g_assert (unlink (tc->test_path) >= 0);
event = recv_json (tc);
g_assert_cmpstr (json_object_get_string_member (event, "event"), ==, "deleted");
g_assert_cmpstr (json_object_get_string_member (event, "path"), ==, tc->test_path);
g_assert_cmpstr (json_object_get_string_member (event, "tag"), ==, "-");
json_object_unref (event);
}
static void
test_watch_directory (TestCase *tc,
gconstpointer unused)
{
JsonObject *event;
setup_fswatch_channel (tc, tc->test_dir);
set_contents (tc->test_path, "Hello!");
g_assert (unlink (tc->test_path) >= 0);
/* We want to see at least "created" and "deleted" for the path, in
that order.
*/
gboolean saw_created = FALSE;
gboolean saw_deleted = FALSE;
while (!(saw_created && saw_deleted) && !tc->channel_closed)
{
event = recv_json (tc);
if (g_strcmp0 (json_object_get_string_member (event, "path"), tc->test_path) == 0)
{
if (g_strcmp0 (json_object_get_string_member (event, "event"), "created") == 0)
{
g_assert (!saw_deleted);
saw_created = TRUE;
}
else if (g_strcmp0 (json_object_get_string_member (event, "event"), "deleted") == 0)
{
g_assert (saw_created);
saw_deleted= TRUE;
}
}
json_object_unref (event);
}
g_assert (saw_created && saw_deleted);
}
static void
test_dir_simple (TestCase *tc,
gconstpointer unused)
{
JsonObject *event, *control;
gchar *base = g_path_get_basename (tc->test_path);
set_contents (tc->test_path, "Hello!");
setup_fslist_channel (tc, tc->test_dir, TRUE);
event = recv_json (tc);
g_assert_cmpstr (json_object_get_string_member (event, "event"), ==, "present");
g_assert_cmpstr (json_object_get_string_member (event, "path"), ==, base);
g_assert_cmpstr (json_object_get_string_member (event, "type"), ==, "file");
json_object_unref (event);
control = recv_control (tc);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
g_free (base);
close_channel (tc, NULL);
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
}
static void
test_dir_simple_no_watch (TestCase *tc,
gconstpointer unused)
{
JsonObject *event, *control;
gchar *base = g_path_get_basename (tc->test_path);
set_contents (tc->test_path, "Hello!");
setup_fslist_channel (tc, tc->test_dir, FALSE);
event = recv_json (tc);
g_assert_cmpstr (json_object_get_string_member (event, "event"), ==, "present");
g_assert_cmpstr (json_object_get_string_member (event, "path"), ==, base);
g_assert_cmpstr (json_object_get_string_member (event, "type"), ==, "file");
json_object_unref (event);
control = recv_control (tc);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
g_free (base);
// channel should be closed
g_assert (tc->channel_closed);
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
}
static void
test_dir_early_close (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
set_contents (tc->test_path, "Hello!");
setup_fslist_channel (tc, tc->test_dir, TRUE);
close_channel (tc, NULL);
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
}
static void
test_dir_watch (TestCase *tc,
gconstpointer unused)
{
JsonObject *event, *control;
setup_fslist_channel (tc, tc->test_dir, TRUE);
control = recv_control (tc);
g_assert_cmpstr (json_object_get_string_member (control, "command"), ==, "ready");
set_contents (tc->test_path, "Hello!");
GFile *dir = g_file_new_for_path (tc->test_subdir);
g_assert (g_file_make_directory (dir, NULL, NULL));
g_object_unref (dir);
GFile *link = g_file_new_for_path (tc->test_link);
g_assert (g_file_make_symbolic_link (link, tc->test_path, NULL, NULL));
g_object_unref (link);
gboolean saw_created = FALSE;
gboolean saw_created_dir = FALSE;
gboolean saw_created_link = FALSE;
gboolean saw_deleted = FALSE;
while (!(saw_created && saw_deleted && saw_created_dir && saw_created_link) && !tc->channel_closed)
{
event = recv_json (tc);
if (g_strcmp0 (json_object_get_string_member (event, "path"), tc->test_path) == 0)
{
if (g_strcmp0 (json_object_get_string_member (event, "event"), "created") == 0)
{
g_assert (!saw_deleted);
g_assert_cmpstr (json_object_get_string_member (event, "type"), ==, "file");
g_assert (unlink (tc->test_path) >= 0);
saw_created = TRUE;
}
else if (g_strcmp0 (json_object_get_string_member (event, "event"), "deleted") == 0)
{
g_assert (saw_created);
saw_deleted= TRUE;
}
}
if (g_strcmp0 (json_object_get_string_member (event, "path"), tc->test_link) == 0)
{
if (g_strcmp0 (json_object_get_string_member (event, "event"), "created") == 0)
{
g_assert_cmpstr (json_object_get_string_member (event, "type"), ==, "link");
g_assert (!saw_created_link);
saw_created_link = TRUE;
}
}
if (g_strcmp0 (json_object_get_string_member (event, "path"), tc->test_subdir) == 0)
{
if (g_strcmp0 (json_object_get_string_member (event, "event"), "created") == 0)
{
g_assert_cmpstr (json_object_get_string_member (event, "type"), ==, "directory");
g_assert (!saw_created_dir);
saw_created_dir = TRUE;
}
}
json_object_unref (event);
}
g_assert (saw_created && saw_deleted && saw_created_link && saw_created_dir);
close_channel (tc, NULL);
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert (json_object_get_member (control, "problem") == NULL);
}
static void
test_dir_list_fail (TestCase *tc,
gconstpointer unused)
{
JsonObject *control;
setup_fslist_channel (tc, tc->test_path, FALSE);
// Channel should close automatically
wait_channel_closed (tc);
control = mock_transport_pop_control (tc->transport);
g_assert_cmpstr (json_object_get_string_member (control, "problem"), ==, "not-found");
}
int
main (int argc,
char *argv[])
{
cockpit_test_init (&argc, &argv);
g_test_add ("/fsread/simple", TestCase, NULL,
setup, test_read_simple, teardown);
g_test_add ("/fsread/non-existent", TestCase, NULL,
setup, test_read_non_existent, teardown);
g_test_add ("/fsread/denied", TestCase, NULL,
setup, test_read_denied, teardown);
g_test_add ("/fsread/changed", TestCase, NULL,
setup, test_read_changed, teardown);
g_test_add ("/fsread/replaced", TestCase, NULL,
setup, test_read_replaced, teardown);
g_test_add ("/fsread/removed", TestCase, NULL,
setup, test_read_removed, teardown);
g_test_add ("/fsread/non-mmappable", TestCase, NULL,
setup, test_read_non_mmappable, teardown);
g_test_add ("/fsreplace/simple", TestCase, NULL,
setup, test_write_simple, teardown);
g_test_add ("/fsreplace/multiple", TestCase, NULL,
setup, test_write_multiple, teardown);
g_test_add ("/fsreplace/remove", TestCase, NULL,
setup, test_write_remove, teardown);
g_test_add ("/fsreplace/remove-nonexistent", TestCase, NULL,
setup, test_write_remove_nonexistent, teardown);
g_test_add ("/fsreplace/empty", TestCase, NULL,
setup, test_write_empty, teardown);
g_test_add ("/fsreplace/denied", TestCase, NULL,
setup, test_write_denied, teardown);
g_test_add ("/fsreplace/expect-non-existent", TestCase, NULL,
setup, test_write_expect_non_existent, teardown);
g_test_add ("/fsreplace/expect-non-existent-fail", TestCase, NULL,
setup, test_write_expect_non_existent_fail, teardown);
g_test_add ("/fsreplace/expect-tag", TestCase, NULL,
setup, test_write_expect_tag, teardown);
g_test_add ("/fsreplace/expect-tag-fail", TestCase, NULL,
setup, test_write_expect_tag_fail, teardown);
g_test_add ("/fswatch/simple", TestCase, NULL,
setup, test_watch_simple, teardown);
g_test_add ("/fswatch/remove", TestCase, NULL,
setup, test_watch_remove, teardown);
g_test_add ("/fswatch/directory", TestCase, NULL,
setup, test_watch_directory, teardown);
g_test_add ("/fslist/simple", TestCase, NULL,
setup, test_dir_simple, teardown);
g_test_add ("/fslist/simple_no_watch", TestCase, NULL,
setup, test_dir_simple_no_watch, teardown);
g_test_add ("/fslist/early-close", TestCase, NULL,
setup, test_dir_early_close, teardown);
g_test_add ("/fslist/watch", TestCase, NULL,
setup, test_dir_watch, teardown);
g_test_add ("/fslist/list_fail", TestCase, NULL,
setup, test_dir_list_fail, teardown);
return g_test_run ();
}