/* * 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 "cockpitmetrics.h" #include "cockpitpcpmetrics.h" #include "mock-transport.h" #include "common/cockpittest.h" #include "common/cockpitjson.h" #include #include #include #include #include #include #include static void init_mock_archives (void) { g_assert (system ("rm -rf mock-archives && mkdir mock-archives") == 0); g_assert (pmiStart ("mock-archives/0", 0) >= 0); g_assert (pmiAddMetric ("mock.value", PM_ID_NULL, PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, pmiUnits (0, 0, 0, 0, 0, 0)) >= 0); g_assert (pmiPutValue ("mock.value", NULL, "10") >= 0); g_assert (pmiWrite (0, 0) >= 0); g_assert (pmiPutValue ("mock.value", NULL, "11") >= 0); g_assert (pmiWrite (1, 0) >= 0); g_assert (pmiPutValue ("mock.value", NULL, "12") >= 0); g_assert (pmiWrite (2, 0) >= 0); g_assert (pmiEnd () >= 0); g_assert (pmiStart ("mock-archives/1", 0) >= 0); g_assert (pmiAddMetric ("mock.value", PM_ID_NULL, PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, pmiUnits (0, 0, 0, 0, 0, 0)) >= 0); g_assert (pmiAddMetric ("mock.late", PM_ID_NULL, PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT, pmiUnits (0, 0, 0, 0, 0, 0)) >= 0); g_assert (pmiPutValue ("mock.value", NULL, "13") >= 0); g_assert (pmiPutValue ("mock.late", NULL, "30") >= 0); g_assert (pmiWrite (3, 0) >= 0); g_assert (pmiPutValue ("mock.value", NULL, "14") >= 0); g_assert (pmiPutValue ("mock.late", NULL, "31") >= 0); g_assert (pmiWrite (4, 0) >= 0); g_assert (pmiPutValue ("mock.value", NULL, "15") >= 0); g_assert (pmiPutValue ("mock.late", NULL, "32") >= 0); g_assert (pmiWrite (5, 0) >= 0); g_assert (pmiEnd () >= 0); } typedef struct AtTeardown { struct AtTeardown *link; void (*func) (void *); void *data; } AtTeardown; typedef struct { MockTransport *transport; CockpitChannel *channel; gchar *problem; gboolean channel_closed; AtTeardown *at_teardown; } 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->problem = g_strdup (problem); 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) { tc->transport = mock_transport_new (); g_signal_connect (tc->transport, "closed", G_CALLBACK (on_transport_closed), NULL); tc->channel = NULL; tc->at_teardown = NULL; } static void at_teardown (TestCase *tc, void *func, void *data) { AtTeardown *item = g_new0 (AtTeardown, 1); item->func = func; item->data = data; item->link = tc->at_teardown; tc->at_teardown = item; } static void setup_metrics_channel_json (TestCase *tc, JsonObject *options) { tc->channel = g_object_new (COCKPIT_TYPE_PCP_METRICS, "transport", tc->transport, "id", "1234", "options", options, NULL); tc->channel_closed = FALSE; g_signal_connect (tc->channel, "closed", G_CALLBACK (on_channel_close), tc); cockpit_channel_prepare (tc->channel); /* Switch off compression by default. Compression is done by * comparing two floating point values for exact equality, and we * can't guarantee that we get the same behavior everywhere. */ cockpit_metrics_set_compress (COCKPIT_METRICS (tc->channel), FALSE); } 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_object (TestCase *tc) { GBytes *msg = recv_bytes (tc); JsonObject *res = cockpit_json_parse_bytes (msg, NULL); g_assert (res != NULL); at_teardown (tc, json_object_unref, res); return res; } static JsonNode * recv_json (TestCase *tc) { GBytes *msg = recv_bytes (tc); gsize length = g_bytes_get_size (msg); JsonNode *res = cockpit_json_parse (g_bytes_get_data (msg, NULL), length, NULL); g_assert (res != NULL); at_teardown (tc, json_node_free, res); return res; } 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); } while (tc->at_teardown) { AtTeardown *item = tc->at_teardown; tc->at_teardown = item->link; item->func (item->data); g_free (item); } g_free (tc->problem); } static JsonObject * json_obj (const gchar *str) { GError *error = NULL; JsonObject *res = cockpit_json_parse_object (str, -1, &error); g_assert_no_error (error); return res; } static void assert_sample_msg (const char *domain, const char *file, int line, const char *func, TestCase *tc, const gchar *json_str) { JsonNode *node = recv_json (tc); g_assert_cmpint (json_node_get_node_type (node), ==, JSON_NODE_ARRAY); _cockpit_assert_json_eq_msg (domain, file, line, func, json_node_get_array (node), json_str); } #define assert_sample(tc, json) \ (assert_sample_msg (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, (tc), (json))) static void test_metrics_single_archive (TestCase *tc, gconstpointer unused) { JsonObject *options = json_obj("{ 'source': '" BUILDDIR "/mock-archives/0'," " 'metrics': [ { 'name': 'mock.value' } ]," " 'interval': 1000" "}"); setup_metrics_channel_json (tc, options); JsonObject *meta = recv_json_object (tc); cockpit_assert_json_eq (json_object_get_array_member (meta, "metrics"), "[ { 'name': 'mock.value', 'units': '', 'semantics': 'instant' } ]"); assert_sample (tc, "[[10],[11],[12]]"); json_object_unref (options); } static void test_metrics_archive_limit (TestCase *tc, gconstpointer unused) { JsonObject *options = json_obj("{ 'source': '" BUILDDIR "/mock-archives/0'," " 'metrics': [ { 'name': 'mock.value' } ]," " 'interval': 1000," " 'limit': 2" "}"); setup_metrics_channel_json (tc, options); JsonObject *meta = recv_json_object (tc); cockpit_assert_json_eq (json_object_get_array_member (meta, "metrics"), "[ { 'name': 'mock.value', 'units': '', 'semantics': 'instant' } ]"); assert_sample (tc, "[[10],[11]]"); json_object_unref (options); } static void test_metrics_archive_timestamp (TestCase *tc, gconstpointer unused) { JsonObject *options = json_obj("{ 'source': '" BUILDDIR "/mock-archives/0'," " 'metrics': [ { 'name': 'mock.value' } ]," " 'interval': 1000," " 'timestamp': 1000" "}"); setup_metrics_channel_json (tc, options); JsonObject *meta = recv_json_object (tc); cockpit_assert_json_eq (json_object_get_array_member (meta, "metrics"), "[ { 'name': 'mock.value', 'units': '', 'semantics': 'instant' } ]"); assert_sample (tc, "[[11],[12]]"); json_object_unref (options); } static void test_metrics_archive_directory (TestCase *tc, gconstpointer unused) { JsonObject *meta; JsonObject *options = json_obj("{ 'source': '" BUILDDIR "/mock-archives'," " 'metrics': [ { 'name': 'mock.value' } ]," " 'interval': 1000" "}"); setup_metrics_channel_json (tc, options); meta = recv_json_object (tc); cockpit_assert_json_eq (json_object_get_array_member (meta, "metrics"), "[ { 'name': 'mock.value', 'units': '', 'semantics': 'instant' } ]"); assert_sample (tc, "[[10],[11],[12]]"); meta = recv_json_object (tc); cockpit_assert_json_eq (json_object_get_array_member (meta, "metrics"), "[ { 'name': 'mock.value', 'units': '', 'semantics': 'instant' } ]"); assert_sample (tc, "[[13],[14],[15]]"); json_object_unref (options); } static void test_metrics_archive_directory_timestamp (TestCase *tc, gconstpointer unused) { JsonObject *meta; JsonObject *options = json_obj("{ 'source': '" BUILDDIR "/mock-archives'," " 'metrics': [ { 'name': 'mock.value' } ]," " 'interval': 1000," " 'timestamp': 4000" "}"); setup_metrics_channel_json (tc, options); meta = recv_json_object (tc); cockpit_assert_json_eq (json_object_get_array_member (meta, "metrics"), "[ { 'name': 'mock.value', 'units': '', 'semantics': 'instant' } ]"); assert_sample (tc, "[[14],[15]]"); json_object_unref (options); } static void test_metrics_archive_directory_late_metric (TestCase *tc, gconstpointer unused) { cockpit_expect_message ("*no such metric: mock.late: Unknown metric name*"); JsonObject *meta; JsonObject *options = json_obj("{ 'source': '" BUILDDIR "/mock-archives'," " 'metrics': [ { 'name': 'mock.late' } ]," " 'interval': 1000" "}"); setup_metrics_channel_json (tc, options); meta = recv_json_object (tc); cockpit_assert_json_eq (json_object_get_array_member (meta, "metrics"), "[ { 'name': 'mock.late', 'units': '', 'semantics': 'instant' } ]"); assert_sample (tc, "[[30],[31],[32]]"); json_object_unref (options); } int main (int argc, char *argv[]) { cockpit_test_init (&argc, &argv); if (chdir (BUILDDIR) < 0) g_assert_not_reached (); init_mock_archives (); g_test_add ("/metrics/single-archive", TestCase, NULL, setup, test_metrics_single_archive, teardown); g_test_add ("/metrics/archive-limit", TestCase, NULL, setup, test_metrics_archive_limit, teardown); g_test_add ("/metrics/archive-timestamp", TestCase, NULL, setup, test_metrics_archive_timestamp, teardown); g_test_add ("/metrics/archive-directory", TestCase, NULL, setup, test_metrics_archive_directory, teardown); g_test_add ("/metrics/archive-directory-timestamp", TestCase, NULL, setup, test_metrics_archive_directory_timestamp, teardown); g_test_add ("/metrics/archive-directory-late-metric", TestCase, NULL, setup, test_metrics_archive_directory_late_metric, teardown); return g_test_run (); }