LCOV - code coverage report
Current view: top level - test - test_metrics.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 124 124 100.0 %
Date: 2021-11-24 03:28:48 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2020-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file test_metrics.c
       6             :  * \brief Test lib/metrics and feature/metrics functionalities
       7             :  */
       8             : 
       9             : #define CONFIG_PRIVATE
      10             : #define CONNECTION_PRIVATE
      11             : #define MAINLOOP_PRIVATE
      12             : #define METRICS_STORE_ENTRY_PRIVATE
      13             : 
      14             : #include "test/test.h"
      15             : #include "test/test_helpers.h"
      16             : #include "test/log_test_helpers.h"
      17             : 
      18             : #include "app/config/config.h"
      19             : 
      20             : #include "core/mainloop/connection.h"
      21             : #include "core/mainloop/mainloop.h"
      22             : #include "core/or/connection_st.h"
      23             : #include "core/or/policies.h"
      24             : #include "core/or/port_cfg_st.h"
      25             : 
      26             : #include "feature/metrics/metrics.h"
      27             : 
      28             : #include "lib/encoding/confline.h"
      29             : #include "lib/metrics/metrics_store.h"
      30             : 
      31             : #define TEST_METRICS_ENTRY_NAME    "entryA"
      32             : #define TEST_METRICS_ENTRY_HELP    "Description of entryA"
      33             : #define TEST_METRICS_ENTRY_LABEL_1 "label=farfadet"
      34             : #define TEST_METRICS_ENTRY_LABEL_2 "label=ponki"
      35             : 
      36             : static void
      37           2 : set_metrics_port(or_options_t *options)
      38             : {
      39           2 :   const char *port = "MetricsPort 9035"; /* Default to 127.0.0.1 */
      40           2 :   const char *policy = "MetricsPortPolicy accept 1.2.3.4";
      41             : 
      42           2 :   config_get_lines(port, &options->MetricsPort_lines, 0);
      43           2 :   config_get_lines(policy, &options->MetricsPortPolicy, 0);
      44             : 
      45             :   /* Parse and validate policy. */
      46           2 :   policies_parse_from_options(options);
      47           2 : }
      48             : 
      49             : static void
      50           1 : test_config(void *arg)
      51             : {
      52           1 :   char *err_msg = NULL;
      53           1 :   tor_addr_t addr;
      54           1 :   smartlist_t *ports = smartlist_new();
      55           1 :   or_options_t *options = get_options_mutable();
      56             : 
      57           1 :   (void) arg;
      58             : 
      59           1 :   set_metrics_port(options);
      60             : 
      61           1 :   int ret = metrics_parse_ports(options, ports, &err_msg);
      62           1 :   tt_int_op(ret, OP_EQ, 0);
      63           1 :   tt_int_op(smartlist_len(ports), OP_EQ, 1);
      64             : 
      65             :   /* Validate the configured port. */
      66           1 :   const port_cfg_t *cfg = smartlist_get(ports, 0);
      67           1 :   tt_assert(tor_addr_eq_ipv4h(&cfg->addr, 0x7f000001));
      68           1 :   tt_int_op(cfg->port, OP_EQ, 9035);
      69           1 :   tt_int_op(cfg->type, OP_EQ, CONN_TYPE_METRICS_LISTENER);
      70             : 
      71             :   /* Address of the policy should be permitted. */
      72           1 :   tor_addr_from_ipv4h(&addr, 0x01020304); /* 1.2.3.4 */
      73           1 :   ret = metrics_policy_permits_address(&addr);
      74           1 :   tt_int_op(ret, OP_EQ, true);
      75             : 
      76             :   /* Anything else, should not. */
      77           1 :   tor_addr_from_ipv4h(&addr, 0x01020305); /* 1.2.3.5 */
      78           1 :   ret = metrics_policy_permits_address(&addr);
      79           1 :   tt_int_op(ret, OP_EQ, false);
      80             : 
      81           1 :  done:
      82           2 :   SMARTLIST_FOREACH(ports, port_cfg_t *, c, port_cfg_free(c));
      83           1 :   smartlist_free(ports);
      84           1 :   or_options_free(options);
      85           1 :   tor_free(err_msg);
      86           1 : }
      87             : 
      88             : static char _c_buf[256];
      89             : #define CONTAINS(conn, msg) \
      90             :   do { \
      91             :     tt_int_op(buf_datalen(conn->outbuf), OP_EQ, (strlen(msg))); \
      92             :     memset(_c_buf, 0, sizeof(_c_buf)); \
      93             :     buf_get_bytes(conn->outbuf, _c_buf, (strlen(msg))); \
      94             :     tt_str_op(_c_buf, OP_EQ, (msg)); \
      95             :     tt_int_op(buf_datalen(conn->outbuf), OP_EQ, 0); \
      96             :   } while (0)
      97             : 
      98             : #define WRITE(conn, msg) \
      99             :   buf_add(conn->inbuf, (msg), (strlen(msg)));
     100             : 
     101             : /* Free the previous conn object if any and allocate a new connection. In
     102             :  * order to be allowed, set its address to 1.2.3.4 as per the policy. */
     103             : #define NEW_ALLOWED_CONN()                              \
     104             :   do {                                                  \
     105             :     close_closeable_connections();                      \
     106             :     conn = connection_new(CONN_TYPE_METRICS, AF_INET);  \
     107             :     tor_addr_from_ipv4h(&conn->addr, 0x01020304);       \
     108             :   } while (0)
     109             : 
     110             : static void
     111           1 : test_connection(void *arg)
     112             : {
     113           1 :   int ret;
     114           1 :   connection_t *conn = NULL;
     115           1 :   or_options_t *options = get_options_mutable();
     116             : 
     117           1 :   (void) arg;
     118             : 
     119             :   /* Notice that in this test, we will allocate a new connection at every test
     120             :    * case. This is because the metrics_connection_process_inbuf() marks for
     121             :    * close the connection in case of an error and thus we can't call again an
     122             :    * inbuf process function on a marked for close connection. */
     123             : 
     124           1 :   tor_init_connection_lists();
     125             : 
     126             :   /* Setup policy. */
     127           1 :   set_metrics_port(options);
     128             : 
     129             :   /* Set 1.2.3.5 IP, we should get rejected. */
     130           1 :   NEW_ALLOWED_CONN();
     131           1 :   tor_addr_from_ipv4h(&conn->addr, 0x01020305);
     132           1 :   ret = metrics_connection_process_inbuf(conn);
     133           1 :   tt_int_op(ret, OP_EQ, -1);
     134             : 
     135             :   /* No HTTP request yet. */
     136           1 :   NEW_ALLOWED_CONN();
     137           1 :   ret = metrics_connection_process_inbuf(conn);
     138           1 :   tt_int_op(ret, OP_EQ, 0);
     139           1 :   connection_free_minimal(conn);
     140             : 
     141             :   /* Bad request. */
     142           1 :   NEW_ALLOWED_CONN();
     143           1 :   WRITE(conn, "HTTP 4.7\r\n\r\n");
     144           1 :   ret = metrics_connection_process_inbuf(conn);
     145           1 :   tt_int_op(ret, OP_EQ, -1);
     146           1 :   CONTAINS(conn, "HTTP/1.0 400 Bad Request\r\n\r\n");
     147             : 
     148             :   /* Path not found. */
     149           1 :   NEW_ALLOWED_CONN();
     150           1 :   WRITE(conn, "GET /badpath HTTP/1.0\r\n\r\n");
     151           1 :   ret = metrics_connection_process_inbuf(conn);
     152           1 :   tt_int_op(ret, OP_EQ, -1);
     153           1 :   CONTAINS(conn, "HTTP/1.0 404 Not Found\r\n\r\n");
     154             : 
     155             :   /* Method not allowed. */
     156           1 :   NEW_ALLOWED_CONN();
     157           1 :   WRITE(conn, "POST /something HTTP/1.0\r\n\r\n");
     158           1 :   ret = metrics_connection_process_inbuf(conn);
     159           1 :   tt_int_op(ret, OP_EQ, -1);
     160           1 :   CONTAINS(conn, "HTTP/1.0 405 Method Not Allowed\r\n\r\n");
     161             : 
     162             :   /* Ask for metrics. The content should be above 0. We don't test the
     163             :    * validity of the returned content but it is certainly not an error. */
     164           1 :   NEW_ALLOWED_CONN();
     165           1 :   WRITE(conn, "GET /metrics HTTP/1.0\r\n\r\n");
     166           1 :   ret = metrics_connection_process_inbuf(conn);
     167           1 :   tt_int_op(ret, OP_EQ, 0);
     168           1 :   tt_int_op(buf_datalen(conn->outbuf), OP_GT, 0);
     169             : 
     170           1 :  done:
     171           1 :   or_options_free(options);
     172           1 :   connection_free_minimal(conn);
     173           1 : }
     174             : 
     175             : static void
     176           1 : test_prometheus(void *arg)
     177             : {
     178           1 :   metrics_store_t *store = NULL;
     179           1 :   metrics_store_entry_t *entry = NULL;
     180           1 :   buf_t *buf = buf_new();
     181           1 :   char *output = NULL;
     182             : 
     183           1 :   (void) arg;
     184             : 
     185             :   /* Fresh new store. No entries. */
     186           1 :   store = metrics_store_new();
     187           1 :   tt_assert(store);
     188             : 
     189             :   /* Add entry and validate its content. */
     190           1 :   entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
     191             :                             TEST_METRICS_ENTRY_NAME,
     192             :                             TEST_METRICS_ENTRY_HELP);
     193           1 :   tt_assert(entry);
     194           1 :   metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1);
     195             : 
     196           1 :   static const char *expected =
     197             :     "# HELP " TEST_METRICS_ENTRY_NAME " " TEST_METRICS_ENTRY_HELP "\n"
     198             :     "# TYPE " TEST_METRICS_ENTRY_NAME " counter\n"
     199             :     TEST_METRICS_ENTRY_NAME "{" TEST_METRICS_ENTRY_LABEL_1 "} 0\n";
     200             : 
     201           1 :   metrics_store_get_output(METRICS_FORMAT_PROMETHEUS, store, buf);
     202           1 :   output = buf_extract(buf, NULL);
     203           1 :   tt_str_op(expected, OP_EQ, output);
     204             : 
     205           1 :  done:
     206           1 :   buf_free(buf);
     207           1 :   tor_free(output);
     208           1 :   metrics_store_free(store);
     209           1 : }
     210             : 
     211             : static void
     212           1 : test_store(void *arg)
     213             : {
     214           1 :   metrics_store_t *store = NULL;
     215           1 :   metrics_store_entry_t *entry = NULL;
     216             : 
     217           1 :   (void) arg;
     218             : 
     219             :   /* Fresh new store. No entries. */
     220           1 :   store = metrics_store_new();
     221           1 :   tt_assert(store);
     222           1 :   tt_assert(!metrics_store_get_all(store, TEST_METRICS_ENTRY_NAME));
     223             : 
     224             :   /* Add entry and validate its content. */
     225           1 :   entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
     226             :                             TEST_METRICS_ENTRY_NAME,
     227             :                             TEST_METRICS_ENTRY_HELP);
     228           1 :   tt_assert(entry);
     229           1 :   tt_int_op(entry->type, OP_EQ, METRICS_TYPE_COUNTER);
     230           1 :   tt_str_op(entry->name, OP_EQ, TEST_METRICS_ENTRY_NAME);
     231           1 :   tt_str_op(entry->help, OP_EQ, TEST_METRICS_ENTRY_HELP);
     232           1 :   tt_uint_op(entry->u.counter.value, OP_EQ, 0);
     233             : 
     234             :   /* Access the entry. */
     235           1 :   tt_assert(metrics_store_get_all(store, TEST_METRICS_ENTRY_NAME));
     236             : 
     237             :   /* Add a label to the entry to make it unique. */
     238           1 :   metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1);
     239           1 :   tt_int_op(metrics_store_entry_has_label(entry, TEST_METRICS_ENTRY_LABEL_1),
     240             :             OP_EQ, true);
     241             : 
     242             :   /* Update entry's value. */
     243           1 :   metrics_store_entry_update(entry, 42);
     244           1 :   tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 42);
     245           1 :   metrics_store_entry_update(entry, 42);
     246           1 :   tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 84);
     247           1 :   metrics_store_entry_reset(entry);
     248           1 :   tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0);
     249             : 
     250             :   /* Add a new entry of same name but different label. */
     251             :   /* Add entry and validate its content. */
     252           1 :   entry = metrics_store_add(store, METRICS_TYPE_COUNTER,
     253             :                             TEST_METRICS_ENTRY_NAME,
     254             :                             TEST_METRICS_ENTRY_HELP);
     255           1 :   tt_assert(entry);
     256           1 :   metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_2);
     257             : 
     258             :   /* Make sure _both_ entries are there. */
     259           1 :   const smartlist_t *entries =
     260           1 :     metrics_store_get_all(store, TEST_METRICS_ENTRY_NAME);
     261           1 :   tt_assert(entries);
     262           1 :   tt_int_op(smartlist_len(entries), OP_EQ, 2);
     263             : 
     264           1 :  done:
     265           1 :   metrics_store_free(store);
     266           1 : }
     267             : 
     268             : struct testcase_t metrics_tests[] = {
     269             : 
     270             :   { "config", test_config, TT_FORK, NULL, NULL },
     271             :   { "connection", test_connection, TT_FORK, NULL, NULL },
     272             :   { "prometheus", test_prometheus, TT_FORK, NULL, NULL },
     273             :   { "store", test_store, TT_FORK, NULL, NULL },
     274             : 
     275             :   END_OF_TESTCASES
     276             : };
     277             : 

Generated by: LCOV version 1.14