/* * 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 #include "config.h" #include "cockpitchannel.h" #include "cockpithttpstream.h" #include "cockpitpackages.h" #include "mock-transport.h" #include "common/cockpitlog.h" #include "common/cockpitjson.h" #include "common/cockpittest.h" #include #include #include /* * To recalculate the checksums found in this file, do something like: * $ XDG_DATA_DIRS=$PWD/src/bridge/mock-resource/glob/ XDG_DATA_HOME=/nonexistant cockpit-bridge --packages */ extern const gchar **cockpit_bridge_data_dirs; extern const gchar *cockpit_bridge_local_address; extern gint cockpit_bridge_packages_port; typedef struct { CockpitPackages *packages; MockTransport *transport; CockpitChannel *channel; gchar *problem; gboolean closed; } TestCase; typedef struct { const gchar *datadirs[8]; const gchar *path; const gchar *accept[8]; const gchar *expect; const gchar *headers[8]; gboolean cacheable; gboolean no_packages_init; } Fixture; static void on_channel_close (CockpitChannel *channel, const gchar *problem, gpointer user_data) { TestCase *tc = user_data; g_assert (tc->closed == FALSE); tc->closed = TRUE; tc->problem = g_strdup (problem); } static void on_transport_closed (CockpitTransport *transport, const gchar *problem, gpointer user_data) { g_assert_not_reached (); } static void setup (TestCase *tc, gconstpointer data) { const Fixture *fixture = data; JsonObject *options; JsonObject *headers; const gchar *control; gchar *accept; GBytes *bytes; guint i; g_assert (fixture != NULL); if (fixture->expect) cockpit_expect_warning (fixture->expect); if (fixture->datadirs[0]) { cockpit_bridge_data_dirs = (const gchar **)fixture->datadirs; } else { cockpit_expect_message ("incompatible: package requires a later version of cockpit: 999.5*"); cockpit_expect_message ("requires: package has an unknown requirement: unknown"); } tc->packages = cockpit_packages_new (); tc->transport = mock_transport_new (); g_signal_connect (tc->transport, "closed", G_CALLBACK (on_transport_closed), NULL); options = json_object_new (); json_object_set_int_member (options, "port", cockpit_bridge_packages_port); json_object_set_string_member (options, "payload", "http-stream1"); json_object_set_string_member (options, "method", "GET"); json_object_set_string_member (options, "path", fixture->path); headers = json_object_new (); if (fixture->accept[0]) { accept = g_strjoinv (", ", (gchar **)fixture->accept); json_object_set_string_member (headers, "Accept-Language", accept); g_free (accept); } if (!fixture->cacheable) json_object_set_string_member (headers, "Pragma", "no-cache"); for (i = 0; i < G_N_ELEMENTS (fixture->headers); i += 2) { if (fixture->headers[i]) json_object_set_string_member (headers, fixture->headers[i], fixture->headers[i + 1]); } json_object_set_object_member (options, "headers", headers); tc->channel = g_object_new (COCKPIT_TYPE_HTTP_STREAM, "transport", tc->transport, "id", "444", "options", options, NULL); json_object_unref (options); /* Tell HTTP we have no more data to send */ control = "{\"command\": \"done\", \"channel\": \"444\"}"; bytes = g_bytes_new_static (control, strlen (control)); cockpit_transport_emit_recv (COCKPIT_TRANSPORT (tc->transport), NULL, bytes); g_bytes_unref (bytes); g_signal_connect (tc->channel, "closed", G_CALLBACK (on_channel_close), tc); } static void teardown (TestCase *tc, gconstpointer data) { cockpit_assert_expected (); g_object_unref (tc->transport); g_object_add_weak_pointer (G_OBJECT (tc->channel), (gpointer *)&tc->channel); g_object_unref (tc->channel); g_assert (tc->channel == NULL); g_free (tc->problem); cockpit_packages_free (tc->packages); cockpit_bridge_data_dirs = NULL; } static const Fixture fixture_simple = { .path = "/test/sub/file.ext", }; static void test_simple (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_simple); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Cache-Control\":\"no-cache, no-store\"}}" "These are the contents of file.ext\nOh marmalaaade\n", -1); g_assert_cmpuint (count, ==, 2); g_bytes_unref (data); } static const Fixture fixture_forwarded = { .path = "/another/test.html", .headers = { "X-Forwarded-Proto", "https", "X-Forwarded-Host", "blah:9090" }, }; static void test_forwarded (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_forwarded); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Content-Security-Policy\":\"default-src 'self' https://blah:9090; connect-src 'self' https://blah:9090 ws: wss:\",\"Content-Type\":\"text/html\",\"Cache-Control\":\"no-cache, no-store\",\"Access-Control-Allow-Origin\":\"https://blah:9090\"}}\x0A\x0AIn home dir\x0A\x0AIn home dir\x0A\x0A", -1); g_assert_cmpuint (count, ==, 2); g_bytes_unref (data); } static const Fixture fixture_pig = { .path = "/another/test.html", .accept = { "pig" }, }; static void test_localized_translated (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_pig); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Content-Security-Policy\":\"default-src 'self'; connect-src 'self' ws: wss:\",\"Content-Type\":\"text/html\",\"Cache-Control\":\"no-cache, no-store\"}}\n\nInlay omehay irday\n\nInlay omehay irday\n\n", -1); g_assert_cmpuint (count, ==, 2); g_bytes_unref (data); } static const Fixture fixture_unknown = { .path = "/another/test.html", .accept = { "unknown" }, }; static void test_localized_unknown (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_unknown); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Content-Security-Policy\":\"default-src 'self'; connect-src 'self' ws: wss:\",\"Content-Type\":\"text/html\",\"Cache-Control\":\"no-cache, no-store\"}}\n\nIn home dir\n\nIn home dir\n\n", -1); g_assert_cmpuint (count, ==, 2); g_bytes_unref (data); } static const Fixture fixture_prefer_region = { .path = "/another/test.html", .accept = { "pig-pen" }, }; static void test_localized_prefer_region (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_prefer_region); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Content-Security-Policy\":\"default-src 'self'; connect-src 'self' ws: wss:\",\"Content-Type\":\"text/html\",\"Cache-Control\":\"no-cache, no-store\"}}\n\nInway omeha irda\n\nInway omeha irda\n\n", -1); g_assert_cmpuint (count, ==, 2); g_bytes_unref (data); } static const Fixture fixture_fallback = { .path = "/another/test.html", .accept = { "pig-barn" }, }; static void test_localized_fallback (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_fallback); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Content-Security-Policy\":\"default-src 'self'; connect-src 'self' ws: wss:\",\"Content-Type\":\"text/html\",\"Cache-Control\":\"no-cache, no-store\"}}\n\nInlay omehay irday\n\nInlay omehay irday\n\n", -1); g_assert_cmpuint (count, ==, 2); g_bytes_unref (data); } static const Fixture fixture_version = { .path = "/incompatible/test.html", }; static void test_incompatible_version (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_version); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":503,\"reason\":\"This package requires Cockpit version 999.5 or later\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}This package requires Cockpit version 999.5 or laterThis package requires Cockpit version 999.5 or later\n", -1); g_bytes_unref (data); } static const Fixture fixture_requires = { .path = "/requires/test.html", }; static void test_incompatible_requires (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; g_assert (fixture == &fixture_requires); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":503,\"reason\":\"This package is not compatible with this version of Cockpit\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}This package is not compatible with this version of CockpitThis package is not compatible with this version of Cockpit\n", -1); g_bytes_unref (data); } static const Fixture fixture_large = { .path = "/test/sub/COPYING", }; static void test_large (TestCase *tc, gconstpointer fixture) { GError *error = NULL; const gchar *prefix; gchar *contents; gsize length; GBytes *data; GBytes *sub; guint count; g_assert (fixture == &fixture_large); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); g_file_get_contents (SRCDIR "/src/bridge/mock-resource/system/cockpit/test/sub/COPYING", &contents, &length, &error); g_assert_no_error (error); data = mock_transport_combine_output (tc->transport, "444", &count); /* Should not have been sent as one block */ g_assert_cmpuint (count, ==, 8); prefix = "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Cache-Control\":\"no-cache, no-store\"}}"; g_assert_cmpuint (g_bytes_get_size (data), >, strlen (prefix)); g_assert (strncmp (g_bytes_get_data (data, NULL), prefix, strlen (prefix)) == 0); sub = g_bytes_new_from_bytes (data, strlen (prefix), g_bytes_get_size (data) - strlen (prefix)); cockpit_assert_bytes_eq (sub, contents, length); g_bytes_unref (sub); g_bytes_unref (data); g_free (contents); } static const Fixture fixture_listing = { .path = "/manifests.json", }; static void test_listing (TestCase *tc, gconstpointer fixture) { JsonObject *object; GError *error = NULL; GBytes *message; JsonNode *node; g_assert (fixture == &fixture_listing); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); message = mock_transport_pop_channel (tc->transport, "444"); object = cockpit_json_parse_bytes (message, &error); g_assert_no_error (error); cockpit_assert_json_eq (object, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"Cache-Control\":\"no-cache, no-store\",\"Content-Type\":\"application/json\"}}"); json_object_unref (object); message = mock_transport_pop_channel (tc->transport, "444"); node = cockpit_json_parse (g_bytes_get_data (message, NULL), g_bytes_get_size (message), &error); g_assert_no_error (error); cockpit_assert_json_eq (json_node_get_object (node), "{" " \"another\": {" " \"name\" : \"another\"," " \"description\" : \"another\"," " \"bridges\": [{ \"match\": {\"host\": null }," " \"problem\": \"not-supported\"}]" " }," " \"second\": {" " \"description\": \"second dummy description\"," " \"priority\": 2," " \"bridges\": [{ \"match\": { \"second\": null }, \"problem\": \"never-a-second\"}]" " }," " \"test\": {" " \"name\": \"test\"," " \"priority\": 15," " \"description\" : \"dummy\"," " \"bridges\": [{ \"match\": { \"blah\": \"test*\" }," " \"spawn\": [\"/usr/bin/cat\"]," " \"environ\": [\"TEST_ENV=test\"]}," " { \"match\": { \"blah\": \"marmalade*\"}," " \"problem\": \"bogus-channel\"}]" " }," " \"incompatible\": {" " \"description\" : \"incompatible package\"," " \"requires\" : { \"cockpit\" : \"999.5\" }" " }," " \"requires\": {" " \"description\" : \"requires package\"," " \"requires\" : { \"unknown\" : \"requirement\" }" " }" "}"); json_node_free (node); } static const Fixture fixture_not_found = { .path = "/test/sub/not-found", }; static void test_not_found (TestCase *tc, gconstpointer fixture) { GBytes *data; g_assert (fixture == &fixture_not_found); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); data = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (data, "{\"status\":404,\"reason\":\"Not Found\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}", -1); } static const Fixture fixture_unknown_package = { .path = "/unknownpackage/sub/not-found", }; static void test_unknown_package (TestCase *tc, gconstpointer fixture) { GBytes *data; g_assert (fixture == &fixture_unknown_package); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); data = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (data, "{\"status\":404,\"reason\":\"Not Found\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}", -1); } static const Fixture fixture_no_path = { .path = "/test" }; static void test_no_path (TestCase *tc, gconstpointer fixture) { GBytes *data; g_assert (fixture == &fixture_no_path); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); data = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (data, "{\"status\":404,\"reason\":\"Not Found\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}", -1); } static const Fixture fixture_bad_path = { .path = "../test/sub/file.ext" }; static void test_bad_path (TestCase *tc, gconstpointer fixture) { GBytes *data; g_assert (fixture == &fixture_bad_path); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); data = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (data, "{\"status\":404,\"reason\":\"Not Found\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}", -1); } static const Fixture fixture_no_package = { .path = "test" }; static void test_no_package (TestCase *tc, gconstpointer fixture) { GBytes *data; g_assert (fixture == &fixture_no_package); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); data = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (data, "{\"status\":404,\"reason\":\"Not Found\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}", -1); } static const Fixture fixture_bad_package = { .path = "/%%package/test" }; static void test_bad_package (TestCase *tc, gconstpointer fixture) { GBytes *data; g_assert (fixture == &fixture_bad_package); cockpit_expect_message ("invalid 'package' name: %%package"); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); data = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (data, "{\"status\":404,\"reason\":\"Not Found\",\"headers\":{\"Content-Type\":\"text/html; charset=utf8\"}}", -1); } static void test_bad_receive (TestCase *tc, gconstpointer fixture) { GBytes *bad; cockpit_expect_message ("444: channel received message after done"); /* A resource2 channel should never have payload sent to it */ bad = g_bytes_new_static ("bad", 3); cockpit_transport_emit_recv (COCKPIT_TRANSPORT (tc->transport), "444", bad); g_bytes_unref (bad); while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, "protocol-error"); } static const Fixture fixture_list_bad_name = { .datadirs = { SRCDIR "/src/bridge/mock-resource/bad-package", NULL }, .expect = "*package*invalid*name*", .path = "/manifests.json" }; static void test_list_bad_name (TestCase *tc, gconstpointer fixture) { GBytes *data; guint count; while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); data = mock_transport_combine_output (tc->transport, "444", &count); cockpit_assert_bytes_eq (data, "{\"status\":200,\"reason\":\"OK\",\"headers\":" "{\"X-Cockpit-Pkg-Checksum\":\"524d07b284cda92c86a908c67014ee882a80193b\",\"Content-Type\":\"application/json\",\"ETag\":\"\\\"$524d07b284cda92c86a908c67014ee882a80193b\\\"\"}}" "{\".checksum\":\"524d07b284cda92c86a908c67014ee882a80193b\",\"ok\":{\".checksum\":\"524d07b284cda92c86a908c67014ee882a80193b\"}}", -1); g_assert_cmpuint (count, ==, 2); g_bytes_unref (data); } static const Fixture fixture_glob = { .datadirs = { SRCDIR "/src/bridge/mock-resource/glob", NULL }, .path = "/*/file.txt" }; static void test_glob (TestCase *tc, gconstpointer fixture) { GError *error = NULL; GBytes *message; JsonObject *object; while (tc->closed == FALSE) g_main_context_iteration (NULL, TRUE); g_assert_cmpstr (tc->problem, ==, NULL); message = mock_transport_pop_channel (tc->transport, "444"); object = cockpit_json_parse_bytes (message, &error); g_assert_no_error (error); cockpit_assert_json_eq (object, "{\"status\":200,\"reason\":\"OK\",\"headers\":{\"X-Cockpit-Pkg-Checksum\":\"4f2a5a7bb5bf355776e1fc83831b1d846914182e\",\"Content-Type\":\"text/plain\"}}"); json_object_unref (object); message = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (message, "a\n", 2); message = mock_transport_pop_channel (tc->transport, "444"); cockpit_assert_bytes_eq (message, "b\n", 2); } static void setup_basic (TestCase *tc, gconstpointer data) { const Fixture *fixture = data; if (fixture && fixture->datadirs[0]) { cockpit_bridge_data_dirs = (const gchar **)fixture->datadirs; } else { cockpit_expect_message ("incompatible: package requires a later version of cockpit: 999.5*"); cockpit_expect_message ("requires: package has an unknown requirement: unknown"); } if (!fixture || !fixture->no_packages_init) tc->packages = cockpit_packages_new (); } static void teardown_basic (TestCase *tc, gconstpointer data) { cockpit_assert_expected (); cockpit_packages_free (tc->packages); cockpit_bridge_data_dirs = NULL; } static void test_resolve (TestCase *tc, gconstpointer fixture) { gchar *path; path = cockpit_packages_resolve (tc->packages, "test", "/sub/file.ext", NULL); g_assert_cmpstr (SRCDIR "/src/bridge/mock-resource/system/cockpit/test-priority/sub/file.ext", ==, path); g_free (path); } static void test_resolve_bad_dots (TestCase *tc, gconstpointer fixture) { gchar *path; cockpit_expect_message ("invalid 'path' used as a resource: *"); path = cockpit_packages_resolve (tc->packages, "test", "../test/sub/file.ext", NULL); g_assert (path == NULL); } static void test_resolve_bad_path (TestCase *tc, gconstpointer fixture) { gchar *path; cockpit_expect_message ("invalid 'path' used as a resource: *"); path = cockpit_packages_resolve (tc->packages, "test", "/sub/#file.ext", NULL); g_assert (path == NULL); } static void test_resolve_bad_package (TestCase *tc, gconstpointer fixture) { gchar *path; cockpit_expect_message ("invalid 'package' name: *"); path = cockpit_packages_resolve (tc->packages, "#test", "/sub/file.ext", NULL); g_assert (path == NULL); } static void test_resolve_not_found (TestCase *tc, gconstpointer fixture) { gchar *path; path = cockpit_packages_resolve (tc->packages, "unknown", "/sub/file.ext", NULL); g_assert (path == NULL); } static int compar_str (const void *pa, const void *pb) { return strcmp (*(const char**)pa, *(const char**)pb); } static void test_get_names (TestCase *tc, gconstpointer fixture) { gchar **names; gchar *result; names = cockpit_packages_get_names (tc->packages); g_assert (names != NULL); qsort (names, g_strv_length (names), sizeof (gchar *), compar_str); result = g_strjoinv (", ", names); /* Note that unavailable packages are not included */ g_assert_cmpstr (result, ==, "another, second, test"); g_free (result); g_free (names); } static void test_get_bridges (TestCase *tc, gconstpointer fixture) { GList *bridges, *l; JsonObject *bridge; guint i; bridges = cockpit_packages_get_bridges (tc->packages); g_assert (bridges != NULL); for (i = 0, l = bridges; l != NULL; l = g_list_next (l), i++) { bridge = l->data; switch (i) { case 0: cockpit_assert_json_eq (json_object_get_object_member (bridge, "match"), "{ \"blah\": \"test*\" }"); cockpit_assert_json_eq (json_object_get_array_member (bridge, "environ"), "[\"TEST_ENV=test\"]"); cockpit_assert_json_eq (json_object_get_array_member (bridge, "spawn"), "[\"/usr/bin/cat\"]"); break; case 1: cockpit_assert_json_eq (json_object_get_object_member (bridge, "match"), "{ \"blah\": \"marmalade*\" }"); g_assert_cmpstr (json_object_get_string_member (bridge, "problem"), ==, "bogus-channel"); break; case 2: cockpit_assert_json_eq (json_object_get_object_member (bridge, "match"), "{ \"second\": null }"); g_assert_cmpstr (json_object_get_string_member (bridge, "problem"), ==, "never-a-second"); break; case 3: cockpit_assert_json_eq (json_object_get_object_member (bridge, "match"), "{ \"host\": null }"); g_assert_cmpstr (json_object_get_string_member (bridge, "problem"), ==, "not-supported"); break; default: g_assert_not_reached (); } } g_assert_cmpint (i, ==, 4); g_list_free (bridges); } static const Fixture fixture_bad_bridges = { .datadirs = { SRCDIR "/src/bridge/mock-resource/bad-bridges", NULL }, }; static void test_get_bridges_broken (TestCase *tc, gconstpointer fixture) { GList *bridges; g_assert (fixture == &fixture_bad_bridges); cockpit_expect_message ("missing-match: missing \"match\" field in package manifest"); cockpit_expect_message ("broken-problem: invalid \"problem\" field in package manifest"); cockpit_expect_message ("broken-environ: invalid \"environ\" field in package manifest"); cockpit_expect_message ("broken-spawn: invalid \"spawn\" field in package manifest"); cockpit_expect_message ("broken-match: invalid \"match\" field in package manifest"); cockpit_expect_message ("broken-bridges: invalid \"bridges\" field in package manifest"); cockpit_expect_message ("broken-bridge: invalid bridge in \"bridges\" field in package manifest"); bridges = cockpit_packages_get_bridges (tc->packages); g_assert (bridges == NULL); } static const Fixture fixture_reload = { .no_packages_init = TRUE, .datadirs = { BUILDDIR "/src/bridge/mock-resource/reload", NULL }, }; __attribute__((format(printf, 1, 2))) static void systemf (const gchar *fmt, ...) { gchar *cmd; va_list ap; va_start (ap, fmt); cmd = g_strdup_vprintf (fmt, ap); va_end (ap); g_assert (system (cmd) == 0); g_free (cmd); } static void setup_reload_packages (const gchar *datadir, const gchar *variant) { const gchar *srcdir = SRCDIR "/src/bridge/mock-resource/reload"; systemf ("mkdir -p $(dirname '%s') && rm -rf '%s' && ln -sf '%s.%s' '%s'", datadir, datadir, srcdir, variant, datadir); } static void teardown_reload_packages (const gchar *datadir) { systemf ("rm -f '%s'", datadir); } static void assert_manifest_checksum (TestCase *tc, const gchar *name, const gchar *expected) { JsonObject *json; const gchar *checksum; json = cockpit_packages_peek_json (tc->packages); if (name) g_assert (cockpit_json_get_object (json, name, NULL, &json)); if (expected) { g_assert (cockpit_json_get_string (json, ".checksum", NULL, &checksum)); g_assert_cmpstr (checksum, ==, expected); } else g_assert (json == NULL); } static void test_reload_added (TestCase *tc, gconstpointer data) { const Fixture *fixture = data; const gchar *datadir; cockpit_bridge_data_dirs = (const gchar **)fixture->datadirs; datadir = cockpit_bridge_data_dirs[0]; setup_reload_packages (datadir, "old"); tc->packages = cockpit_packages_new (); assert_manifest_checksum (tc, NULL, "0e4445bda678eede7c520a0a0b87aae56e7570cf"); assert_manifest_checksum (tc, "old", "0e4445bda678eede7c520a0a0b87aae56e7570cf"); setup_reload_packages (datadir, "new"); cockpit_packages_reload (tc->packages); assert_manifest_checksum (tc, NULL, "0e4445bda678eede7c520a0a0b87aae56e7570cf"); assert_manifest_checksum (tc, "old", "0e4445bda678eede7c520a0a0b87aae56e7570cf"); assert_manifest_checksum (tc, "new", "516e9877b1255fa22f18c869e1715f39dd4b39ec"); teardown_reload_packages (datadir); } static void test_reload_removed (TestCase *tc, gconstpointer data) { const Fixture *fixture = data; const gchar *datadir; cockpit_bridge_data_dirs = (const gchar **)fixture->datadirs; datadir = cockpit_bridge_data_dirs[0]; setup_reload_packages (datadir, "new"); tc->packages = cockpit_packages_new (); assert_manifest_checksum (tc, NULL, "516e9877b1255fa22f18c869e1715f39dd4b39ec"); assert_manifest_checksum (tc, "old", "516e9877b1255fa22f18c869e1715f39dd4b39ec"); assert_manifest_checksum (tc, "new", "516e9877b1255fa22f18c869e1715f39dd4b39ec"); setup_reload_packages (datadir, "old"); cockpit_packages_reload (tc->packages); assert_manifest_checksum (tc, NULL, "516e9877b1255fa22f18c869e1715f39dd4b39ec"); assert_manifest_checksum (tc, "old", "516e9877b1255fa22f18c869e1715f39dd4b39ec"); assert_manifest_checksum (tc, "new", NULL); teardown_reload_packages (datadir); } static void test_reload_updated (TestCase *tc, gconstpointer data) { const Fixture *fixture = data; const gchar *datadir; cockpit_bridge_data_dirs = (const gchar **)fixture->datadirs; datadir = cockpit_bridge_data_dirs[0]; setup_reload_packages (datadir, "old"); tc->packages = cockpit_packages_new (); assert_manifest_checksum (tc, NULL, "0e4445bda678eede7c520a0a0b87aae56e7570cf"); assert_manifest_checksum (tc, "old", "0e4445bda678eede7c520a0a0b87aae56e7570cf"); setup_reload_packages (datadir, "updated"); cockpit_packages_reload (tc->packages); assert_manifest_checksum (tc, NULL, "0e4445bda678eede7c520a0a0b87aae56e7570cf"); assert_manifest_checksum (tc, "old", "252178c3fba5843c8c3bb4ce7733b405741cedee"); teardown_reload_packages (datadir); } 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_bridge_local_address = "127.0.0.1"; cockpit_test_init (&argc, &argv); g_test_add ("/packages/simple", TestCase, &fixture_simple, setup, test_simple, teardown); g_test_add ("/packages/forwarded", TestCase, &fixture_forwarded, setup, test_forwarded, teardown); g_test_add ("/packages/localized-translated", TestCase, &fixture_pig, setup, test_localized_translated, teardown); g_test_add ("/packages/localized-unknown", TestCase, &fixture_unknown, setup, test_localized_unknown, teardown); g_test_add ("/packages/localized-prefer-region", TestCase, &fixture_prefer_region, setup, test_localized_prefer_region, teardown); g_test_add ("/packages/localized-fallback", TestCase, &fixture_fallback, setup, test_localized_fallback, teardown); g_test_add ("/packages/incompatible/version", TestCase, &fixture_version, setup, test_incompatible_version, teardown); g_test_add ("/packages/incompatible/requires", TestCase, &fixture_requires, setup, test_incompatible_requires, teardown); g_test_add ("/packages/large", TestCase, &fixture_large, setup, test_large, teardown); g_test_add ("/packages/listing", TestCase, &fixture_listing, setup, test_listing, teardown); g_test_add ("/packages/not-found", TestCase, &fixture_not_found, setup, test_not_found, teardown); g_test_add ("/packages/unknown-package", TestCase, &fixture_unknown_package, setup, test_unknown_package, teardown); g_test_add ("/packages/bad-receive", TestCase, &fixture_large, setup, test_bad_receive, teardown); g_test_add ("/packages/no-path", TestCase, &fixture_no_path, setup, test_no_path, teardown); g_test_add ("/packages/bad-path", TestCase, &fixture_bad_path, setup, test_bad_path, teardown); g_test_add ("/packages/no-package", TestCase, &fixture_no_package, setup, test_no_package, teardown); g_test_add ("/packages/bad-package", TestCase, &fixture_bad_package, setup, test_bad_package, teardown); g_test_add ("/packages/listing-bad-name", TestCase, &fixture_list_bad_name, setup, test_list_bad_name, teardown); g_test_add ("/packages/glob", TestCase, &fixture_glob, setup, test_glob, teardown); g_test_add ("/packages/resolve/simple", TestCase, NULL, setup_basic, test_resolve, teardown_basic); g_test_add ("/packages/resolve/bad-dots", TestCase, NULL, setup_basic, test_resolve_bad_dots, teardown_basic); g_test_add ("/packages/resolve/bad-path", TestCase, NULL, setup_basic, test_resolve_bad_path, teardown_basic); g_test_add ("/packages/resolve/bad-package", TestCase, NULL, setup_basic, test_resolve_bad_package, teardown_basic); g_test_add ("/packages/resolve/not-found", TestCase, NULL, setup_basic, test_resolve_not_found, teardown_basic); g_test_add ("/packages/get-names", TestCase, NULL, setup_basic, test_get_names, teardown_basic); g_test_add ("/packages/get-bridges/normal", TestCase, NULL, setup_basic, test_get_bridges, teardown_basic); g_test_add ("/packages/get-bridges/broken", TestCase, &fixture_bad_bridges, setup_basic, test_get_bridges_broken, teardown_basic); g_test_add ("/packages/reload/added", TestCase, &fixture_reload, setup_basic, test_reload_added, teardown_basic); g_test_add ("/packages/reload/removed", TestCase, &fixture_reload, setup_basic, test_reload_removed, teardown_basic); g_test_add ("/packages/reload/updated", TestCase, &fixture_reload, setup_basic, test_reload_updated, teardown_basic); return g_test_run (); }