/*
* 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 ();
}