/* * This file is part of Cockpit. * * Copyright (C) 2013 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 "mock-auth.h" #include "cockpithandlers.h" #include "cockpitws.h" #include "common/cockpitconf.h" #include "common/cockpittest.h" #include "common/mock-io-stream.h" #include "common/cockpitwebserver.h" #include #include #include #include #include #include /* * To recalculate the checksums found in this file, do something like: * $ XDG_DATA_DIRS=$PWD/src/bridge/mock-resource/system/ XDG_DATA_HOME=/nonexistant cockpit-bridge --packages */ /* Mock override this from cockpitconf.c */ extern const gchar *cockpit_config_file; #define PASSWORD "this is the password" typedef struct { CockpitHandlerData data; CockpitWebServer *server; CockpitAuth *auth; GHashTable *headers; GIOStream *io; CockpitWebResponse *response; gboolean response_done; GMemoryOutputStream *output; GMemoryInputStream *input; GByteArray *buffer; gchar *scratch; gchar **roots; gchar *login_html; } Test; static void on_ready_get_result (GObject *source, GAsyncResult *result, gpointer user_data) { GAsyncResult **retval = user_data; g_assert (retval != NULL); g_assert (*retval == NULL); *retval = g_object_ref (result); } static void on_web_response_done_set_flag (CockpitWebResponse *response, gboolean reuse, gpointer user_data) { gboolean *flag = user_data; g_assert (flag != NULL); g_assert (*flag == FALSE); *flag = TRUE; } static void base_setup (Test *test) { const gchar *static_roots[] = { SRCDIR "/src/ws", SRCDIR "/src/branding/default", NULL }; GError *error = NULL; test->server = cockpit_web_server_new (NULL, 0, NULL, NULL, &error); g_assert_no_error (error); /* Other test->data fields are fine NULL */ memset (&test->data, 0, sizeof (test->data)); test->auth = cockpit_auth_new (FALSE); test->roots = cockpit_web_response_resolve_roots (static_roots); test->login_html = g_strdup(SRCDIR "/src/ws/login.html"); test->data.auth = test->auth; test->data.branding_roots = (const gchar **)test->roots; test->data.login_html = (const gchar *)test->login_html; test->data.login_po_html = NULL; test->headers = cockpit_web_server_new_table (); test->output = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new (NULL, 0, g_realloc, g_free)); test->input = G_MEMORY_INPUT_STREAM (g_memory_input_stream_new ()); test->io = mock_io_stream_new (G_INPUT_STREAM (test->input), G_OUTPUT_STREAM (test->output)); } static void setup (Test *test, gconstpointer path) { base_setup (test); test->response = cockpit_web_response_new (test->io, path, path, NULL, NULL); g_signal_connect (test->response, "done", G_CALLBACK (on_web_response_done_set_flag), &test->response_done); } static void teardown (Test *test, gconstpointer path) { g_clear_object (&test->auth); g_clear_object (&test->server); g_clear_object (&test->output); g_clear_object (&test->input); g_clear_object (&test->io); g_free (test->login_html); g_hash_table_destroy (test->headers); g_free (test->scratch); g_object_unref (test->response); g_strfreev (test->roots); cockpit_assert_expected (); } static const gchar * output_as_string (Test *test) { while (!test->response_done) g_main_context_iteration (NULL, TRUE); g_free (test->scratch); test->scratch = g_strndup (g_memory_output_stream_get_data (test->output), g_memory_output_stream_get_data_size (test->output)); return test->scratch; } static void test_login_no_cookie (Test *test, gconstpointer path) { gboolean ret; ret = cockpit_handler_default (test->server, path, test->headers, test->response, &test->data); g_assert (ret == TRUE); cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 401 Authentication failed\r\n*"); } static void include_cookie_as_if_client (GHashTable *resp_headers, GHashTable *req_headers) { gchar *cookie; gchar *end; cookie = g_strdup (g_hash_table_lookup (resp_headers, "Set-Cookie")); g_assert (cookie != NULL); end = strchr (cookie, ';'); g_assert (end != NULL); end[0] = '\0'; g_hash_table_insert (req_headers, g_strdup ("Cookie"), cookie); } static void test_login_with_cookie (Test *test, gconstpointer path) { GError *error = NULL; GAsyncResult *result = NULL; JsonObject *response; GHashTable *headers; gboolean ret; headers = mock_auth_basic_header ("me", PASSWORD); cockpit_auth_login_async (test->auth, path, NULL, headers, on_ready_get_result, &result); g_hash_table_unref (headers); while (result == NULL) g_main_context_iteration (NULL, TRUE); response = cockpit_auth_login_finish (test->auth, result, NULL, test->headers, &error); g_object_unref (result); g_assert_no_error (error); g_assert (response != NULL); json_object_unref (response); include_cookie_as_if_client (test->headers, test->headers); ret = cockpit_handler_default (test->server, path, test->headers, test->response, &test->data); g_assert (ret == TRUE); cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 200 OK\r\n*\r\n\r\n{*"); } static void test_login_bad (Test *test, gconstpointer path) { gboolean ret; GHashTable *headers; headers = cockpit_web_server_new_table (); g_hash_table_insert (headers, g_strdup ("Authorization"), g_strdup ("booyah")); ret = cockpit_handler_default (test->server, path, headers, test->response, &test->data); g_hash_table_unref (headers); g_assert (ret == TRUE); cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 401 Authentication failed\r\n*"); } static void test_login_fail (Test *test, gconstpointer path) { gboolean ret; GHashTable *headers; headers = mock_auth_basic_header ("booo", "yah"); ret = cockpit_handler_default (test->server, path, test->headers, test->response, &test->data); g_hash_table_unref (headers); while (!test->response_done) g_main_context_iteration (NULL, TRUE); g_assert (ret == TRUE); cockpit_assert_strmatch (output_as_string (test), "HTTP/1.1 401 Authentication failed\r\n*"); } static GHashTable * split_headers (const gchar *output) { GHashTable *headers; gchar **lines; gchar **parts; gint i; headers = cockpit_web_server_new_table (); lines = g_strsplit (output, "\r\n", -1); for (i = 1; lines[i] != NULL && lines[i][0] != '\0'; i++) { parts = g_strsplit (lines[i], ":", 2); g_hash_table_insert (headers, g_strstrip (parts[0]), g_strstrip (parts[1])); g_free (parts); } g_strfreev (lines); return headers; } static void test_login_accept (Test *test, gconstpointer path) { CockpitWebService *service; gboolean ret; const gchar *output; GHashTable *headers; CockpitCreds *creds; const gchar *token; headers = mock_auth_basic_header ("me", PASSWORD); g_hash_table_insert (headers, g_strdup ("X-Authorize"), g_strdup ("password")); ret = cockpit_handler_default (test->server, path, headers, test->response, &test->data); g_hash_table_unref (headers); g_assert (ret == TRUE); output = output_as_string (test); cockpit_assert_strmatch (output, "HTTP/1.1 200 OK\r\n*"); cockpit_assert_strmatch (output, "*Secure; *"); /* Check that returned cookie that works */ headers = split_headers (output); include_cookie_as_if_client (headers, test->headers); service = cockpit_auth_check_cookie (test->auth, "/cockpit", test->headers); g_assert (service != NULL); creds = cockpit_web_service_get_creds (service); g_assert_cmpstr (cockpit_creds_get_user (creds), ==, "me"); g_assert_cmpstr (g_bytes_get_data (cockpit_creds_get_password (creds), NULL), ==, PASSWORD); token = cockpit_creds_get_csrf_token (creds); g_assert (strstr (output, token)); g_hash_table_destroy (headers); g_object_unref (service); } static void test_favicon_ico (Test *test, gconstpointer path) { const gchar *output; gboolean ret; ret = cockpit_handler_root (test->server, path, test->headers, test->response, &test->data); g_assert (ret == TRUE); output = output_as_string (test); cockpit_assert_strmatch (output, "HTTP/1.1 200 OK\r\n*" "Content-Length: *\r\n" "*"); } static void test_ping (Test *test, gconstpointer path) { const gchar *output; gboolean ret; ret = cockpit_handler_ping (test->server, path, test->headers, test->response, &test->data); g_assert (ret == TRUE); output = output_as_string (test); cockpit_assert_strmatch (output, "HTTP/1.1 200 OK\r\n*" "Access-Control-Allow-Origin: *\r\n*" "\"cockpit\"*"); } typedef struct { const gchar *path; const gchar *org_path; const gchar *auth; const gchar *expect; const gchar *config; gboolean with_home; } DefaultFixture; static void setup_default (Test *test, gconstpointer data) { const DefaultFixture *fixture = data; JsonObject *response; GError *error = NULL; GAsyncResult *result = NULL; GHashTable *headers; cockpit_config_file = fixture->config; if (fixture->config) g_setenv ("XDG_CONFIG_DIRS", fixture->config, TRUE); else g_unsetenv ("XDG_CONFIG_DIRS"); g_setenv ("XDG_DATA_DIRS", SRCDIR "/src/bridge/mock-resource/system", TRUE); if (fixture->with_home) g_setenv ("XDG_DATA_HOME", SRCDIR "/src/bridge/mock-resource/home", TRUE); else g_setenv ("XDG_DATA_HOME", "/nonexistant", TRUE); base_setup (test); test->response = cockpit_web_response_new (test->io, fixture->org_path ? fixture->org_path : fixture->path, fixture->path, NULL, NULL); g_signal_connect (test->response, "done", G_CALLBACK (on_web_response_done_set_flag), &test->response_done); if (fixture->auth) { headers = mock_auth_basic_header ("bridge-user", PASSWORD); cockpit_auth_login_async (test->auth, fixture->auth, NULL, headers, on_ready_get_result, &result); g_hash_table_unref (headers); while (result == NULL) g_main_context_iteration (NULL, TRUE); response = cockpit_auth_login_finish (test->auth, result, NULL, test->headers, &error); g_object_unref (result); g_assert_no_error (error); g_assert (response != NULL); json_object_unref (response); include_cookie_as_if_client (test->headers, test->headers); } } static void teardown_default (Test *test, gconstpointer data) { const DefaultFixture *fixture = data; g_unsetenv ("XDG_DATA_DIRS"); g_unsetenv ("XDG_DATA_HOME"); teardown (test, fixture->path); cockpit_conf_cleanup (); }; static void test_default (Test *test, gconstpointer data) { const DefaultFixture *fixture = data; gboolean ret; ret = cockpit_handler_default (test->server, fixture->path, test->headers, test->response, &test->data); if (fixture->expect) { g_assert (ret == TRUE); cockpit_assert_strmatch (output_as_string (test), fixture->expect); } else { g_assert (ret == FALSE); } } static const DefaultFixture fixture_resource_checksum = { .path = "/cockpit/$060119c2a544d8e5becd0f74f9dcde146b8d99e3/test/sub/file.ext", .auth = "/cockpit", .expect = "HTTP/1.1 200*" "These are the contents of file.ext*" }; static void test_resource_checksum (Test *test, gconstpointer data) { CockpitWebResponse *response; gboolean response_done = FALSE; GInputStream *input; GOutputStream *output; GIOStream *io; gchar *string; const gchar *path; /* Prime the checksums with dummy request */ output = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); input = g_memory_input_stream_new (); io = mock_io_stream_new (input, output); path = "/cockpit/@localhost/checksum"; response = cockpit_web_response_new (io, path, path, NULL, NULL); g_signal_connect (response, "done", G_CALLBACK (on_web_response_done_set_flag), &response_done); g_assert (cockpit_handler_default (test->server, path, test->headers, response, &test->data)); while (!response_done) g_main_context_iteration (NULL, TRUE); string = g_strndup (g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (output)), g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (output))); cockpit_assert_strmatch (string, "HTTP/1.1 200*"); g_free (string); g_object_unref (output); g_object_unref (input); g_object_unref (io); g_object_unref (response); /* And now run the real test */ test_default (test, data); } static const DefaultFixture fixture_shell_path_index = { .path = "/", .org_path = "/path/", .auth = "/cockpit", .with_home = TRUE, .expect = "HTTP/1.1 200*" "*" "In home dir*" }; static const DefaultFixture fixture_shell_path_package = { .path = "/system/host", .org_path = "/path/system/host", .auth = "/cockpit", .expect = "HTTP/1.1 200*" "*" "In system dir*" }; static const DefaultFixture fixture_shell_path_host = { .path = "/@localhost/system/host", .org_path = "/path/@localhost/system/host", .with_home = TRUE, .auth = "/cockpit", .expect = "HTTP/1.1 200*" "*" "In home dir*" }; static const DefaultFixture fixture_shell_path_login = { .path = "/system/host", .org_path = "/path/system/host", .auth = NULL, .expect = "HTTP/1.1 200*" "*" "*" "login-button*" }; static const DefaultFixture fixture_shell_index = { .path = "/", .auth = "/cockpit", .with_home = TRUE, .expect = "HTTP/1.1 200*" "*" "In home dir*" }; static const DefaultFixture fixture_machine_shell_index = { .path = "/=machine", .auth = "/cockpit+=machine", .config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf", .expect = "HTTP/1.1 200*" "*" "In system dir*" }; static const DefaultFixture fixture_shell_configured_index = { .path = "/", .auth = "/cockpit", .with_home = TRUE, .config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf", .expect = "HTTP/1.1 200*" "*" "In system dir*" }; static const DefaultFixture fixture_shell_package = { .path = "/system/host", .auth = "/cockpit", .expect = "HTTP/1.1 200*" "*" "In system dir*" }; static const DefaultFixture fixture_shell_host = { .path = "/@localhost/system/host", .with_home = TRUE, .auth = "/cockpit", .expect = "HTTP/1.1 200*" "*" "In home dir*" }; static const DefaultFixture fixture_shell_host_short = { .path = "/@/system/page", .auth = "/cockpit", .expect = "HTTP/1.1 404*" }; static const DefaultFixture fixture_shell_package_short = { .path = "//page", .auth = "/cockpit", .expect = "HTTP/1.1 404*" }; static const DefaultFixture fixture_machine_shell_package_short = { .path = "/=/", .auth = "/cockpit", .expect = "HTTP/1.1 404*", .config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf" }; static const DefaultFixture fixture_shell_package_invalid = { .path = "/invalid.path/page", .auth = "/cockpit", .expect = "HTTP/1.1 404*" }; static const DefaultFixture fixture_shell_login = { .path = "/system/host", .auth = NULL, .expect = "HTTP/1.1 200*" "*" "*" "login-button*" }; static const DefaultFixture fixture_resource_short = { .path = "/cockpit", .auth = "/cockpit", .expect = "HTTP/1.1 404*" }; static const DefaultFixture fixture_resource_host = { .path = "/cockpit/@localhost/test/sub/file.ext", .auth = "/cockpit", .expect = "HTTP/1.1 200*" "These are the contents of file.ext*" }; static const DefaultFixture fixture_resource_host_short = { .path = "/cockpit/@/test/sub/file.ext", .auth = "/cockpit", .expect = "HTTP/1.1 404*" }; static const DefaultFixture fixture_resource_application = { .path = "/cockpit+application/@localhost/test/sub/file.ext", .auth = "/cockpit+application", .expect = "HTTP/1.1 200*" "These are the contents of file.ext*" }; static const DefaultFixture fixture_resource_application_short = { .path = "/cockpit+/@localhost/test/sub/file.ext", .auth = "/cockpit+", .expect = "HTTP/1.1 401*" }; static const DefaultFixture fixture_resource_missing = { .path = "/cockpit/another/file.html", .auth = "/cockpit", .expect = "HTTP/1.1 404*" }; static const DefaultFixture fixture_resource_auth = { .path = "/cockpit/@localhost/yyy/zzz", .auth = NULL, .expect = "HTTP/1.1 401*" }; static const DefaultFixture fixture_resource_login = { .path = "/cockpit/@localhost/yyy/zzz.html", .auth = NULL, .expect = "HTTP/1.1 200*" "*" "login-button*" }; static const DefaultFixture fixture_static_simple = { .path = "/cockpit/static/branding.css", .auth = "/cockpit", .expect = "HTTP/1.1 200*" "Cache-Control: max-age=31556926, public*" "#badge*" "url(\"logo.png\");*" }; static const DefaultFixture fixture_host_static = { .path = "/cockpit+=host/static/branding.css", .auth = "/cockpit+=host", .config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf", .expect = "HTTP/1.1 200*" "Cache-Control: max-age=86400, private*" "#badge*" "url(\"logo.png\");*" }; static const DefaultFixture fixture_host_static_no_auth = { .path = "/cockpit+=host/static/branding.css", .expect = "HTTP/1.1 403*", .config = SRCDIR "/src/ws/mock-config/cockpit/cockpit.conf" }; static const DefaultFixture fixture_static_application = { .path = "/cockpit+application/static/branding.css", .auth = NULL, .expect = "HTTP/1.1 200*" "Cache-Control: max-age=31556926, public*" "#badge*" "url(\"logo.png\");*" }; static void make_io_streams (GIOStream **io_a, GIOStream **io_b) { GSocket *socket1, *socket2; GError *error = NULL; int fds[2]; if (socketpair (PF_UNIX, SOCK_STREAM, 0, fds) < 0) g_assert_not_reached (); socket1 = g_socket_new_from_fd (fds[0], &error); g_assert_no_error (error); socket2 = g_socket_new_from_fd (fds[1], &error); g_assert_no_error (error); *io_a = G_IO_STREAM (g_socket_connection_factory_create_connection (socket1)); *io_b = G_IO_STREAM (g_socket_connection_factory_create_connection (socket2)); g_object_unref (socket1); g_object_unref (socket2); } static void on_error_not_reached (WebSocketConnection *ws, GError *error, gpointer user_data) { g_assert (error != NULL); /* At this point we know this will fail, but is informative */ g_assert_no_error (error); } static void on_message_get_bytes (WebSocketConnection *ws, WebSocketDataType type, GBytes *message, gpointer user_data) { GBytes **received = user_data; g_assert_cmpint (type, ==, WEB_SOCKET_DATA_TEXT); if (*received != NULL) { gsize length; gconstpointer data = g_bytes_get_data (message, &length); g_test_message ("received unexpected extra message: %.*s", (int)length, (gchar *)data); g_assert_not_reached (); } *received = g_bytes_ref (message); } static void test_socket_unauthenticated (void) { WebSocketConnection *client; GBytes *received = NULL; GIOStream *io_a, *io_b; GBytes *payload; const gchar *problem; const gchar *command; const gchar *unused; gchar *channel; JsonObject *options; make_io_streams (&io_a, &io_b); client = g_object_new (WEB_SOCKET_TYPE_CLIENT, "url", "ws://127.0.0.1/unused", "origin", "http://127.0.0.1", "io-stream", io_a, NULL); g_signal_connect (client, "error", G_CALLBACK (on_error_not_reached), NULL); /* Matching the above origin */ cockpit_ws_default_host_header = "127.0.0.1"; g_assert (cockpit_handler_socket (NULL, "/cockpit/socket", "/cockpit/socket", "GET", io_b, NULL, NULL, NULL)); g_signal_connect (client, "message", G_CALLBACK (on_message_get_bytes), &received); /* Should close right after opening */ while (web_socket_connection_get_ready_state (client) != WEB_SOCKET_STATE_CLOSED) g_main_context_iteration (NULL, TRUE); /* And we should have received a message */ g_assert (received != NULL); payload = cockpit_transport_parse_frame (received, &channel); g_assert (payload != NULL); g_assert (channel == NULL); g_bytes_unref (received); g_assert (cockpit_transport_parse_command (payload, &command, &unused, &options)); g_bytes_unref (payload); g_assert_cmpstr (command, ==, "init"); g_assert (cockpit_json_get_string (options, "problem", NULL, &problem)); g_assert_cmpstr (problem, ==, "no-session"); json_object_unref (options); g_object_unref (client); while (g_main_context_iteration (NULL, FALSE)); g_object_unref (io_a); g_object_unref (io_b); } int main (int argc, char *argv[]) { /* See mock-resource */ cockpit_ws_shell_component = "/another/test.html"; cockpit_ws_session_program = BUILDDIR "/mock-auth-command"; cockpit_test_init (&argc, &argv); g_test_add ("/handlers/login/no-cookie", Test, "/cockpit/login", setup, test_login_no_cookie, teardown); g_test_add ("/handlers/login/with-cookie", Test, "/cockpit+app/login", setup, test_login_with_cookie, teardown); g_test_add ("/handlers/login/post-bad", Test, "/cockpit/login", setup, test_login_bad, teardown); g_test_add ("/handlers/login/post-fail", Test, "/cockpit/login", setup, test_login_fail, teardown); g_test_add ("/handlers/login/post-accept", Test, "/cockpit/login", setup, test_login_accept, teardown); g_test_add ("/handlers/ping", Test, "/ping", setup, test_ping, teardown); g_test_add ("/handlers/shell/index", Test, &fixture_shell_index, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/machine-index", Test, &fixture_machine_shell_index, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/configured_index", Test, &fixture_shell_configured_index, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/package", Test, &fixture_shell_package, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/host", Test, &fixture_shell_host, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/host-short", Test, &fixture_shell_host_short, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/package-short", Test, &fixture_shell_package_short, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/machine-package-short", Test, &fixture_machine_shell_package_short, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/package-invalid", Test, &fixture_shell_package_invalid, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/login", Test, &fixture_shell_login, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/path-index", Test, &fixture_shell_path_index, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/path-package", Test, &fixture_shell_path_package, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/path-host", Test, &fixture_shell_path_host, setup_default, test_default, teardown_default); g_test_add ("/handlers/shell/path-login", Test, &fixture_shell_path_login, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/checksum", Test, &fixture_resource_checksum, setup_default, test_resource_checksum, teardown_default); g_test_add ("/handlers/resource/short", Test, &fixture_resource_short, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/host", Test, &fixture_resource_host, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/host-short", Test, &fixture_resource_host_short, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/application", Test, &fixture_resource_application, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/application-short", Test, &fixture_resource_application_short, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/missing", Test, &fixture_resource_missing, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/auth", Test, &fixture_resource_auth, setup_default, test_default, teardown_default); g_test_add ("/handlers/resource/login", Test, &fixture_resource_login, setup_default, test_default, teardown_default); g_test_add ("/handlers/static/simple", Test, &fixture_static_simple, setup_default, test_default, teardown_default); g_test_add ("/handlers/static/host-static", Test, &fixture_host_static, setup_default, test_default, teardown_default); g_test_add ("/handlers/static/host-static-no-auth", Test, &fixture_host_static_no_auth, setup_default, test_default, teardown_default); g_test_add ("/handlers/static/application", Test, &fixture_static_application, setup_default, test_default, teardown_default); g_test_add ("/handlers/favicon", Test, "/favicon.ico", setup, test_favicon_ico, teardown); g_test_add_func ("/handlers/noauth", test_socket_unauthenticated); return g_test_run (); }