/* * This file is part of Cockpit. * * Copyright (C) 2016 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 "common/cockpitjson.h" #include "common/cockpitpipe.h" #include "common/cockpitpipetransport.h" #include "common/cockpittest.h" #include static gboolean on_recv_get_bytes (CockpitTransport *transport, const gchar *channel, GBytes *data, gpointer user_data) { GBytes **result = (GBytes **)user_data; g_assert_cmpstr (channel, ==, NULL); g_assert (result != NULL); g_assert (*result == NULL); *result = g_bytes_ref (data); return TRUE; } static void on_closed_not_reached (CockpitTransport *transport, const gchar *problem) { g_assert_cmpstr (problem, ==, NULL); g_assert_not_reached (); } static void test_bridge_init (void) { CockpitTransport *transport; CockpitPipe *pipe; GBytes *bytes = NULL; JsonObject *object; JsonObject *os_release; JsonObject *packages; GError *error = NULL; GList *list; const gchar *argv[] = { BUILDDIR "/cockpit-bridge", NULL }; pipe = cockpit_pipe_spawn (argv, NULL, NULL, COCKPIT_PIPE_FLAGS_NONE); transport = cockpit_pipe_transport_new (pipe); g_object_unref (pipe); g_signal_connect (transport, "recv", G_CALLBACK (on_recv_get_bytes), &bytes); g_signal_connect (transport, "closed", G_CALLBACK (on_closed_not_reached), NULL); while (bytes == NULL) g_main_context_iteration (NULL, TRUE); g_signal_handlers_disconnect_by_func (transport, on_recv_get_bytes, &bytes); g_signal_handlers_disconnect_by_func (transport, on_closed_not_reached, NULL); g_object_unref (transport); object = cockpit_json_parse_bytes (bytes, &error); g_assert_no_error (error); g_bytes_unref (bytes); g_assert_cmpstr (json_object_get_string_member (object, "command"), ==, "init"); /* Make sure we've included /etc/os-release information */ os_release = json_object_get_object_member (object, "os-release"); g_assert (os_release != NULL); g_assert (json_object_has_member (os_release, "NAME")); /* Make sure we have right packages listed */ packages = json_object_get_object_member (object, "packages"); g_assert (packages != NULL); list = json_object_get_members (packages); list = g_list_sort (list, (GCompareFunc)strcmp); g_assert_cmpuint (g_list_length (list), ==, 3); g_assert_cmpstr (list->data, ==, "another"); g_assert_cmpstr (list->next->data, ==, "second"); g_assert_cmpstr (list->next->next->data, ==, "test"); g_list_free (list); json_object_unref (object); } #if 0 static void on_closed_get_problem (CockpitTransport *transport, const gchar *problem, gpointer user_data) { gchar **result = (gchar **)user_data; g_assert (result != NULL); g_assert (*result == NULL); g_assert (problem != NULL); *result = g_strdup (problem); } #endif static void on_closed_set_flag (CockpitTransport *transport, const gchar *problem, gpointer user_data) { gboolean *flag = (gboolean *)user_data; *flag = TRUE; } static gboolean on_control_get_close (CockpitTransport *transport, const gchar *command, const gchar *channel, JsonObject *options, GBytes *payload, gpointer user_data) { JsonObject **result = (JsonObject **)user_data; g_assert (command != NULL); if (g_str_equal (command, "close")) { g_assert (result != NULL); if (*result == NULL) *result = json_object_ref (options); return TRUE; } return FALSE; } typedef struct { const gchar *host; guint64 version; } InitProblem; static void test_bridge_init_problem (gconstpointer user_data) { const InitProblem *fixture = user_data; CockpitTransport *transport; CockpitPipe *pipe; GBytes *bytes; gboolean closed = FALSE; JsonObject *input; g_assert (fixture != NULL); const gchar *argv[] = { BUILDDIR "/cockpit-bridge", NULL }; /* missing version will spawn an expected warning in subprocess */ if (fixture->version == 0) cockpit_test_allow_warnings (); pipe = cockpit_pipe_spawn (argv, NULL, NULL, COCKPIT_PIPE_FLAGS_NONE); transport = cockpit_pipe_transport_new (pipe); g_object_unref (pipe); /* First send the actual init message */ input = json_object_new (); json_object_set_string_member (input, "command", "init"); if (fixture->version != 0) json_object_set_int_member (input, "version", fixture->version); if (fixture->host) json_object_set_string_member (input, "host", fixture->host); bytes = cockpit_json_write_bytes (input); cockpit_transport_send (transport, NULL, bytes); g_bytes_unref (bytes); json_object_unref (input); /* The bridge should terminate */ g_signal_connect (transport, "closed", G_CALLBACK (on_closed_set_flag), &closed); while (!closed) g_main_context_iteration (NULL, TRUE); cockpit_test_reset_warnings (); g_signal_handlers_disconnect_by_func (transport, on_closed_set_flag, &closed); g_object_unref (transport); /* Just checking that it closes by itself here */ } typedef struct { const gchar *host; const gchar *open_host; const gchar *problem; } OpenProblem; static void test_bridge_open_problem (gconstpointer user_data) { const OpenProblem *fixture = user_data; CockpitTransport *transport; CockpitPipe *pipe; GBytes *bytes; JsonObject *object = NULL; JsonObject *input; g_assert (fixture != NULL); g_assert (fixture->problem != NULL); const gchar *argv[] = { BUILDDIR "/cockpit-bridge", NULL }; pipe = cockpit_pipe_spawn (argv, NULL, NULL, COCKPIT_PIPE_FLAGS_NONE); transport = cockpit_pipe_transport_new (pipe); g_object_unref (pipe); /* First send the actual init message */ input = json_object_new (); json_object_set_string_member (input, "command", "init"); json_object_set_int_member (input, "version", 1); if (fixture->host) json_object_set_string_member (input, "host", fixture->host); bytes = cockpit_json_write_bytes (input); cockpit_transport_send (transport, NULL, bytes); g_bytes_unref (bytes); json_object_unref (input); /* Next maybe send an open message */ input = json_object_new (); json_object_set_string_member (input, "command", "open"); json_object_set_string_member (input, "channel", "444"); json_object_set_string_member (input, "payload", "null"); if (fixture->open_host) json_object_set_string_member (input, "host", fixture->open_host); bytes = cockpit_json_write_bytes (input); cockpit_transport_send (transport, NULL, bytes); g_bytes_unref (bytes); json_object_unref (input); /* Listen for a close message */ g_signal_connect (transport, "control", G_CALLBACK (on_control_get_close), &object); while (object == NULL) g_main_context_iteration (NULL, TRUE); g_signal_handlers_disconnect_by_func (transport, on_control_get_close, &object); g_object_unref (transport); g_assert_cmpstr (json_object_get_string_member (object, "problem"), ==, fixture->problem); json_object_unref (object); } static InitProblem bad_version = { .version = 5, }; static InitProblem missing_version = { .version = 0, }; static InitProblem missing_host = { .version = 1, }; static OpenProblem wrong_host = { .host = "marmalade", .open_host = "juggs", .problem = "not-supported", }; int main (int argc, char *argv[]) { g_setenv ("XDG_DATA_DIRS", SRCDIR "/src/bridge/mock-resource/system", TRUE); g_setenv ("XDG_DATA_HOME", SRCDIR "/src/bridge/mock-resource/home", TRUE); cockpit_test_init (&argc, &argv); g_test_add_func ("/bridge/init-message", test_bridge_init); g_test_add_data_func ("/bridge/bad-version", &bad_version, test_bridge_init_problem); g_test_add_data_func ("/bridge/missing-version", &missing_version, test_bridge_init_problem); g_test_add_data_func ("/bridge/missing-host", &missing_host, test_bridge_init_problem); g_test_add_data_func ("/bridge/wrong-host", &wrong_host, test_bridge_open_problem); return g_test_run (); }