mirror of
https://github.com/Cacti/spine.git
synced 2026-11-02 10:07:07 +09:00
Classify numeric, undefined, multipart and Cacti octet results through one parser, normalize undefined agent values before persistence and flush every SNMP batch consistently. Validate whole non-negative uptimes, retain unknown reindex baselines and free script results through their owning pointer. The mocked poll_host suite checks session-profile boundaries, spike handling, output_regex input and completion writes. Originally step 4/14 of the C17/native-correctness series; its preceding steps have merged. The current base is develop. Earlier validation, before this review follow-up, in isolated copied snapshots on Linux arm64: - Clean GCC build and make check: 15 passed, 1 existing prerequisite skips, 0 failures. - ASan/UBSan: 14 passed, 2 existing privilege/allocator-interposition prerequisite skips, 0 failures; no sanitizer findings. - Production source/profile integrity verification against disposable MariaDB and loopback SNMP fixtures passed. - Release archive extracted into a separate source tree and built/tested out of tree; fuzz checks passed. Earlier local profiles retain source hashes for the copied validation tree; GitHub checks validate the published commit. No production database/device, host installation or live privilege change is part of validation. Existing prerequisite skips were retained; no test or finding was suppressed to obtain a pass. Review follow-up (2026-10-09): Restacked on the corrected preceding branches. The SNMP status fix is introduced in #638, the reindex/completion fixes in #640, and focused regression coverage in #650. Restacked tip validation: isolated Linux arm64 build and make check passed (18 passed, 1 setuid prerequisite skip, no failures). GitHub checks validate the updated published commits; earlier profile/archive/sanitizer evidence above belongs to the previous revisions. ## Current merge dependencies Current base: `develop`. Immediate dependency: `develop` (earlier steps already merged). Next open PR: [#644](https://github.com/Cacti/spine/pull/644). Merge order: #641 → #644 → #654 → #646 → #648 → #649 → #650 → #651 → #655 → #658 → #659. This repository uses squash-only merges and deletes merged branches. Record the old parent SHA, squash one PR at a time, then restack and retarget dependents before merging the next PR. Re-run merge validation and checks on the resulting heads; an ordinary clean merge does not replace squash/restack validation. --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
516 lines
17 KiB
C
516 lines
17 KiB
C
/* Drives the shipped poll_host() without a database or an SNMP agent.
|
|
*
|
|
* GNU ld --wrap replaces the database and SNMP calls that poller.o makes into
|
|
* sql.o, snmp.o and the client library with an in-memory fake, so the flush,
|
|
* reindex and completion paths run in an unprivileged "make check". The live
|
|
* suites in tests/test_spine_*.c cover the same paths against real servers.
|
|
*/
|
|
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <setjmp.h>
|
|
#include <cmocka.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "common.h"
|
|
#include "spine.h"
|
|
|
|
#define HOST_COLUMNS 37
|
|
#define REINDEX_COLUMNS 5
|
|
#define ITEM_COLUMNS 21
|
|
#define MAX_FAKE_ROWS 4
|
|
#define MAX_QUERIES 64
|
|
|
|
extern poller_thread_t **details;
|
|
|
|
typedef struct {
|
|
char **rows[MAX_FAKE_ROWS];
|
|
int count;
|
|
int columns;
|
|
int next;
|
|
} fake_result_t;
|
|
|
|
static struct {
|
|
const char *host[HOST_COLUMNS];
|
|
char reindex[MAX_FAKE_ROWS][REINDEX_COLUMNS][BUFSIZE];
|
|
int reindex_count;
|
|
const char *items[MAX_FAKE_ROWS][ITEM_COLUMNS];
|
|
int item_count;
|
|
const char *multi_value;
|
|
const char *engine_uptime;
|
|
const char *legacy_uptime;
|
|
int session_opens;
|
|
int session_timeouts[MAX_QUERIES];
|
|
char session_hostnames[MAX_QUERIES][BUFSIZE];
|
|
char *queries[MAX_QUERIES];
|
|
int query_count;
|
|
int completion_queries;
|
|
int completion_queries_locked;
|
|
} fake;
|
|
|
|
static pool_t fake_pool;
|
|
static int fake_session;
|
|
|
|
static void reset_fake(void) {
|
|
for (int i = 0; i < fake.query_count; i++) free(fake.queries[i]);
|
|
memset(&fake, 0, sizeof(fake));
|
|
}
|
|
|
|
static void capture(const char *query) {
|
|
assert_true(fake.query_count < MAX_QUERIES);
|
|
fake.queries[fake.query_count] = strdup(query);
|
|
assert_non_null(fake.queries[fake.query_count]);
|
|
fake.query_count++;
|
|
}
|
|
|
|
static int captured(const char *needle) {
|
|
int found = 0;
|
|
for (int i = 0; i < fake.query_count; i++) {
|
|
if (strstr(fake.queries[i], needle) != NULL) found++;
|
|
}
|
|
return found;
|
|
}
|
|
|
|
static fake_result_t *make_result(int count, int columns, const char *(*cell)(int, int)) {
|
|
fake_result_t *result = calloc(1, sizeof(*result));
|
|
assert_non_null(result);
|
|
result->count = count;
|
|
result->columns = columns;
|
|
for (int row = 0; row < count; row++) {
|
|
result->rows[row] = calloc((size_t)columns, sizeof(char *));
|
|
assert_non_null(result->rows[row]);
|
|
for (int column = 0; column < columns; column++) {
|
|
const char *value = cell(row, column);
|
|
result->rows[row][column] = value == NULL ? NULL : strdup(value);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const char *host_cell(int row, int column) {
|
|
(void) row;
|
|
return fake.host[column];
|
|
}
|
|
|
|
static const char *reindex_cell(int row, int column) {
|
|
return fake.reindex[row][column];
|
|
}
|
|
|
|
static const char *item_cell(int row, int column) {
|
|
return fake.items[row][column];
|
|
}
|
|
|
|
/* Persist the one write the next cycle reads back: the reindex baseline. */
|
|
static void apply_reindex_update(const char *query) {
|
|
const char *prefix = "UPDATE poller_reindex SET assert_value='";
|
|
if (strncmp(query, prefix, strlen(prefix)) != 0) return;
|
|
const char *value = query + strlen(prefix);
|
|
const char *end = strchr(value, '\'');
|
|
assert_non_null(end);
|
|
snprintf(fake.reindex[0][3], sizeof(fake.reindex[0][3]), "%.*s", (int)(end - value), value);
|
|
}
|
|
|
|
/* Device completion writes must not hold LOCK_THDET while they wait on the
|
|
* server, whichever db_* call carries them. */
|
|
static void observe_completion(const char *query) {
|
|
if (strncmp(query, "UPDATE poller_item SET rrd_next_step", 36) == 0 ||
|
|
strncmp(query, "UPDATE host SET polling_time", 28) == 0 ||
|
|
strncmp(query, "INSERT INTO host_errors", 23) == 0) {
|
|
fake.completion_queries++;
|
|
if (thread_mutex_trylock(LOCK_THDET) == 0) thread_mutex_unlock(LOCK_THDET);
|
|
else fake.completion_queries_locked++;
|
|
}
|
|
}
|
|
|
|
MYSQL_RES *__wrap_db_query(MYSQL *mysql, int type, const char *query) {
|
|
(void) mysql;
|
|
(void) type;
|
|
if (strstr(query, "FROM host WHERE id") != NULL) {
|
|
return (MYSQL_RES *) make_result(1, HOST_COLUMNS, host_cell);
|
|
}
|
|
if (strstr(query, "FROM poller_reindex") != NULL) {
|
|
return (MYSQL_RES *) make_result(fake.reindex_count, REINDEX_COLUMNS, reindex_cell);
|
|
}
|
|
if (strstr(query, "SELECT SQL_NO_CACHE action") != NULL) {
|
|
return (MYSQL_RES *) make_result(fake.item_count, ITEM_COLUMNS, item_cell);
|
|
}
|
|
capture(query);
|
|
observe_completion(query);
|
|
return NULL;
|
|
}
|
|
|
|
int __wrap_db_insert(MYSQL *mysql, int type, const char *query) {
|
|
(void) mysql;
|
|
(void) type;
|
|
capture(query);
|
|
observe_completion(query);
|
|
apply_reindex_update(query);
|
|
return TRUE;
|
|
}
|
|
|
|
void __wrap_db_escape(MYSQL *mysql, char *output, int max_size, const char *input) {
|
|
(void) mysql;
|
|
if (input == NULL || output == NULL || max_size <= 0) return;
|
|
snprintf(output, (size_t) max_size, "%s", input);
|
|
}
|
|
|
|
pool_t *__wrap_db_get_connection(int type) {
|
|
(void) type;
|
|
return &fake_pool;
|
|
}
|
|
|
|
void __wrap_db_release_connection(int type, int id) {
|
|
(void) type;
|
|
(void) id;
|
|
}
|
|
|
|
void __wrap_db_free_result(MYSQL_RES *opaque) {
|
|
fake_result_t *result = (fake_result_t *) opaque;
|
|
if (result == NULL) return;
|
|
for (int row = 0; row < result->count; row++) {
|
|
for (int column = 0; column < result->columns; column++) free(result->rows[row][column]);
|
|
free(result->rows[row]);
|
|
}
|
|
free(result);
|
|
}
|
|
|
|
uint64_t __wrap_mysql_num_rows(MYSQL_RES *opaque) {
|
|
return (uint64_t) ((fake_result_t *) opaque)->count;
|
|
}
|
|
|
|
MYSQL_ROW __wrap_mysql_fetch_row(MYSQL_RES *opaque) {
|
|
fake_result_t *result = (fake_result_t *) opaque;
|
|
if (result->next >= result->count) return NULL;
|
|
return result->rows[result->next++];
|
|
}
|
|
|
|
void *__wrap_snmp_host_init(const snmp_connection_t *options) {
|
|
assert_true(fake.session_opens < MAX_QUERIES);
|
|
fake.session_timeouts[fake.session_opens] = options->snmp_timeout;
|
|
snprintf(fake.session_hostnames[fake.session_opens], BUFSIZE, "%s", options->hostname);
|
|
fake.session_opens++;
|
|
return &fake_session;
|
|
}
|
|
|
|
void __wrap_snmp_host_cleanup(void *session) {
|
|
assert_ptr_equal(session, &fake_session);
|
|
}
|
|
|
|
/* Mirrors snmp_get_base(): a failure is "" when allowed and U otherwise. */
|
|
static char *fake_get(host_t *host, const char *oid, bool should_fail) {
|
|
const char *value = NULL;
|
|
if (strcmp(oid, ".1.3.6.1.6.3.10.2.1.3.0") == 0) value = fake.engine_uptime;
|
|
else if (strcmp(oid, ".1.3.6.1.2.1.1.3.0") == 0) value = fake.legacy_uptime;
|
|
else value = "fake-system-field";
|
|
if (value == NULL) {
|
|
if (should_fail) host->ignore_host = TRUE;
|
|
value = should_fail ? "U" : "";
|
|
}
|
|
char *output = calloc(RESULTS_BUFFER, 1);
|
|
assert_non_null(output);
|
|
snprintf(output, RESULTS_BUFFER, "%s", value);
|
|
return output;
|
|
}
|
|
|
|
char *__wrap_snmp_get_base(host_t *host, char *oid, bool should_fail) {
|
|
return fake_get(host, oid, should_fail);
|
|
}
|
|
|
|
char *__wrap_snmp_get(host_t *host, char *oid) {
|
|
return fake_get(host, oid, TRUE);
|
|
}
|
|
|
|
char *__wrap_snmp_get_allow_fail(host_t *host, char *oid) {
|
|
return fake_get(host, oid, FALSE);
|
|
}
|
|
|
|
void __wrap_snmp_get_multi(host_t *host, const target_t *items, snmp_oids_t *oids, int count) {
|
|
(void) host;
|
|
(void) items;
|
|
for (int i = 0; i < count; i++) {
|
|
snprintf(oids[i].result, sizeof(oids[i].result), "%s", fake.multi_value);
|
|
}
|
|
}
|
|
|
|
int __wrap_ping_host(host_t *host, ping_t *ping) {
|
|
(void) host;
|
|
(void) ping;
|
|
return HOST_UP;
|
|
}
|
|
|
|
static void set_host(const char *community, const char *max_oids, int availability) {
|
|
static char method[16];
|
|
snprintf(method, sizeof(method), "%d", availability);
|
|
fake.host[0] = "47";
|
|
fake.host[1] = "127.0.0.1";
|
|
fake.host[2] = community;
|
|
fake.host[3] = "2";
|
|
fake.host[11] = "161";
|
|
fake.host[12] = "500";
|
|
fake.host[13] = max_oids;
|
|
fake.host[14] = method;
|
|
fake.host[21] = "0";
|
|
fake.host[22] = "0";
|
|
}
|
|
|
|
static void add_reindex(int action, const char *op, const char *assert_value, const char *arg1) {
|
|
int row = fake.reindex_count++;
|
|
snprintf(fake.reindex[row][0], BUFSIZE, "7");
|
|
snprintf(fake.reindex[row][1], BUFSIZE, "%d", action);
|
|
snprintf(fake.reindex[row][2], BUFSIZE, "%s", op);
|
|
snprintf(fake.reindex[row][3], BUFSIZE, "%s", assert_value);
|
|
snprintf(fake.reindex[row][4], BUFSIZE, "%s", arg1);
|
|
}
|
|
|
|
static void add_snmp_item(const char *local_data_id, const char *rrd_name, const char *community,
|
|
const char *version, const char *regex) {
|
|
const char **row = fake.items[fake.item_count++];
|
|
row[0] = "0";
|
|
row[1] = "127.0.0.1";
|
|
row[2] = community;
|
|
row[3] = version;
|
|
row[6] = rrd_name;
|
|
row[8] = ".1.3.6.1.2.1.1.3.0";
|
|
row[11] = local_data_id;
|
|
row[13] = "161";
|
|
row[14] = "500";
|
|
row[20] = regex;
|
|
}
|
|
|
|
static void add_script_item(const char *local_data_id, const char *rrd_name, const char *command) {
|
|
const char **row = fake.items[fake.item_count++];
|
|
row[0] = "1";
|
|
row[6] = rrd_name;
|
|
row[8] = command;
|
|
row[11] = local_data_id;
|
|
}
|
|
|
|
static int run_poll(void) {
|
|
poller_thread_t work = {0};
|
|
poller_thread_t *device = &work;
|
|
int errors = 0;
|
|
work.host_id = 47;
|
|
work.host_thread = 1;
|
|
work.host_threads = 1;
|
|
work.host_data_ids = 0;
|
|
work.host_time_double = get_time_as_double();
|
|
STRNCOPY(work.host_time, "1791158400");
|
|
details = &device;
|
|
poll_host(&work, &errors);
|
|
details = NULL;
|
|
assert_true(work.complete);
|
|
return errors;
|
|
}
|
|
|
|
static int setup(void **state) {
|
|
(void) state;
|
|
reset_fake();
|
|
config_defaults();
|
|
set.poller.poller_id = 1;
|
|
set.poller.threads = 1;
|
|
set.poller.active_profiles = 1;
|
|
set.poller.mode = REMOTE_OFFLINE;
|
|
set.php.script_timeout = 2;
|
|
set.hosts.has_output_regex = TRUE;
|
|
set.snmp.total_snmp_ports = 1;
|
|
fake_pool.free = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
/* Each item leaves through a different flush. 601 goes out when the batch is
|
|
* full (max_oids=1) or when 602's credentials differ; 602 is the final flush.
|
|
* All three apply the same regex, to the normalized value, and spike kill. */
|
|
static void test_flush_paths_agree(void **state) {
|
|
(void) state;
|
|
for (int scenario = 0; scenario < 4; scenario++) {
|
|
bool full_batch = (scenario & 1) != 0;
|
|
bool spike = (scenario & 2) != 0;
|
|
setup(NULL);
|
|
set_host("regression", full_batch ? "1" : "5", AVAIL_NONE);
|
|
add_snmp_item("601", "a", "regression", "2", "^[0-9][0-9]*");
|
|
add_snmp_item("602", "b", "regression", full_batch ? "2" : "1", "^[0-9][0-9]*");
|
|
if (spike) add_reindex(POLLER_ACTION_SCRIPT, "<", "124", "/usr/bin/printf 123");
|
|
fake.multi_value = "00 00 01 0A";
|
|
run_poll();
|
|
const char *value = spike ? "'U')" : "'266')";
|
|
char row[128];
|
|
snprintf(row, sizeof(row), "(601, 'a', FROM_UNIXTIME(1791158400), %s", value);
|
|
print_message("scenario %d full_batch=%d spike=%d\n", scenario, full_batch, spike);
|
|
assert_int_equal(captured(row), 1);
|
|
snprintf(row, sizeof(row), "(602, 'b', FROM_UNIXTIME(1791158400), %s", value);
|
|
assert_int_equal(captured(row), 1);
|
|
}
|
|
}
|
|
|
|
/* A community longer than the old 49-byte key must still batch. */
|
|
static void test_long_community_batches(void **state) {
|
|
static const char community[] = "regression-community-wider-than-the-old-fifty-byte-batch-key";
|
|
(void) state;
|
|
set_host(community, "10", AVAIL_NONE);
|
|
add_snmp_item("611", "a", community, "2", NULL);
|
|
add_snmp_item("612", "b", community, "2", NULL);
|
|
add_snmp_item("613", "c", community, "2", NULL);
|
|
fake.multi_value = "42";
|
|
assert_int_equal(run_poll(), 0);
|
|
/* one session for the device checks, one for the single batch */
|
|
assert_int_equal(fake.session_opens, 2);
|
|
assert_int_equal(captured("'42')"), 1);
|
|
}
|
|
|
|
static void test_snmp_batches_on_hostname(void **state) {
|
|
(void) state;
|
|
set_host("regression", "10", AVAIL_NONE);
|
|
add_snmp_item("621", "a", "regression", "2", NULL);
|
|
add_snmp_item("622", "b", "regression", "2", NULL);
|
|
fake.items[1][1] = "127.0.0.2";
|
|
fake.multi_value = "42";
|
|
assert_int_equal(run_poll(), 0);
|
|
assert_int_equal(fake.session_opens, 3);
|
|
assert_string_equal(fake.session_hostnames[1], "127.0.0.1");
|
|
assert_string_equal(fake.session_hostnames[2], "127.0.0.2");
|
|
}
|
|
|
|
/* SNMPv3 items batch on every credential field; a different context
|
|
* starts a new session. */
|
|
static void test_snmpv3_batches_on_profile(void **state) {
|
|
(void) state;
|
|
set_host("regression", "10", AVAIL_NONE);
|
|
add_snmp_item("631", "a", "", "3", NULL);
|
|
add_snmp_item("632", "b", "", "3", NULL);
|
|
add_snmp_item("633", "c", "", "3", NULL);
|
|
for (int i = 0; i < 3; i++) {
|
|
fake.items[i][4] = "regression-user";
|
|
fake.items[i][5] = "regression-pass";
|
|
fake.items[i][15] = "SHA";
|
|
fake.items[i][16] = "regression-priv";
|
|
fake.items[i][17] = "AES";
|
|
fake.items[i][19] = "80001f8880";
|
|
}
|
|
fake.items[0][18] = "one";
|
|
fake.items[1][18] = "one";
|
|
fake.items[2][18] = "two";
|
|
fake.multi_value = "7";
|
|
assert_int_equal(run_poll(), 0);
|
|
/* device session, then one per distinct profile */
|
|
assert_int_equal(fake.session_opens, 3);
|
|
assert_int_equal(captured("(633, 'c', FROM_UNIXTIME(1791158400), '7')"), 1);
|
|
}
|
|
|
|
/* Otherwise identical items share a session only while their timeouts match. */
|
|
static void test_snmp_batches_on_timeout(void **state) {
|
|
(void) state;
|
|
set_host("regression", "10", AVAIL_NONE);
|
|
add_snmp_item("641", "a", "regression", "2", NULL);
|
|
add_snmp_item("642", "b", "regression", "2", NULL);
|
|
add_snmp_item("643", "c", "regression", "2", NULL);
|
|
fake.items[2][14] = "1500";
|
|
fake.multi_value = "7";
|
|
assert_int_equal(run_poll(), 0);
|
|
assert_int_equal(fake.session_opens, 3);
|
|
assert_int_equal(fake.session_timeouts[1], 500);
|
|
assert_int_equal(fake.session_timeouts[2], 1500);
|
|
assert_int_equal(captured("(643, 'c', FROM_UNIXTIME(1791158400), '7')"), 1);
|
|
}
|
|
|
|
/* Good baseline, unreadable cycle, reboot: the reboot must still fire. */
|
|
static void test_uptime_baseline_survives_unknown(void **state) {
|
|
static const struct {
|
|
const char *engine;
|
|
const char *legacy;
|
|
int commands;
|
|
const char *stored;
|
|
} cycles[] = {
|
|
{"5", NULL, 0, "500"},
|
|
{NULL, NULL, 0, "500"},
|
|
/* a negative engine time is not an uptime; the legacy OID answers */
|
|
{"-5", "600", 0, "600"},
|
|
{"4096 Bytes", "600", 0, "600"},
|
|
{"3.5", "600", 0, "600"},
|
|
{"3e2", "600", 0, "600"},
|
|
{"+3", "600", 0, "600"},
|
|
{"", "4096 Bytes", 0, "600"},
|
|
{"18446744073709551616", "600", 0, "600"},
|
|
{"18446744073709551615", "600", 0, "600"},
|
|
{" \t6\r\n", NULL, 0, "600"},
|
|
{"1", NULL, 1, "100"},
|
|
};
|
|
(void) state;
|
|
set_host("regression", "5", AVAIL_NONE);
|
|
add_reindex(POLLER_ACTION_SNMP, "<", "0", ".1.3.6.1.2.1.1.3.0");
|
|
for (size_t i = 0; i < sizeof(cycles) / sizeof(cycles[0]); i++) {
|
|
for (int q = 0; q < fake.query_count; q++) free(fake.queries[q]);
|
|
fake.query_count = 0;
|
|
fake.engine_uptime = cycles[i].engine;
|
|
fake.legacy_uptime = cycles[i].legacy;
|
|
run_poll();
|
|
print_message("cycle %zu stored=%s\n", i, fake.reindex[0][3]);
|
|
assert_int_equal(captured("REPLACE INTO poller_command"), cycles[i].commands);
|
|
assert_string_equal(fake.reindex[0][3], cycles[i].stored);
|
|
}
|
|
}
|
|
|
|
/* Padded script output is trimmed, then freed through its own pointer; the
|
|
* device's error list starts empty, and completion writes run unlocked. */
|
|
static void test_script_reindex_and_completion(void **state) {
|
|
(void) state;
|
|
set_host("", "5", AVAIL_NONE);
|
|
set.logging.spine_log_level = 1;
|
|
set.poller.active_profiles = 2;
|
|
set.poller.poller_interval = 300;
|
|
add_reindex(POLLER_ACTION_SCRIPT, "=", "122", "/usr/bin/printf ' 123'");
|
|
add_script_item("621", "a", "/usr/bin/printf 123");
|
|
assert_int_equal(run_poll(), 1);
|
|
assert_int_equal(captured("REPLACE INTO poller_command"), 1);
|
|
assert_string_equal(fake.reindex[0][3], "123");
|
|
assert_int_equal(captured("VALUES(47, 1, 1, '')"), 1);
|
|
assert_int_equal(fake.completion_queries, 3);
|
|
assert_int_equal(fake.completion_queries_locked, 0);
|
|
}
|
|
|
|
/* sysUpTimeInstance takes the engine time in ticks when both OIDs read, and
|
|
* ignores an uptime that is not a whole non-negative number. */
|
|
static void test_system_uptime(void **state) {
|
|
static const struct { const char *legacy; const char *engine; const char *stored; } cases[] = {
|
|
{"4200", "42", "snmp_sysUpTimeInstance='4200'"},
|
|
{"4200", "-1", "snmp_sysUpTimeInstance='4200'"},
|
|
{"-5", "42", "snmp_sysUpTimeInstance='0'"},
|
|
{"4096 Bytes", "42", "snmp_sysUpTimeInstance='0'"},
|
|
{"4200", "4096 Bytes", "snmp_sysUpTimeInstance='4200'"},
|
|
{"4200", "18446744073709551615", "snmp_sysUpTimeInstance='4200'"},
|
|
{"18446744073709551616", "42", "snmp_sysUpTimeInstance='0'"},
|
|
{" \t4200\r\n", " \t42\r\n", "snmp_sysUpTimeInstance='4200'"},
|
|
};
|
|
(void) state;
|
|
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
|
setup(NULL);
|
|
set.snmp.mibs = TRUE;
|
|
set_host("regression", "5", AVAIL_SNMP);
|
|
fake.legacy_uptime = cases[i].legacy;
|
|
fake.engine_uptime = cases[i].engine;
|
|
run_poll();
|
|
print_message("case %zu\n", i);
|
|
assert_int_equal(captured(cases[i].stored), 1);
|
|
}
|
|
}
|
|
|
|
int main(void) {
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test_setup(test_flush_paths_agree, setup),
|
|
cmocka_unit_test_setup(test_long_community_batches, setup),
|
|
cmocka_unit_test_setup(test_snmp_batches_on_hostname, setup),
|
|
cmocka_unit_test_setup(test_snmpv3_batches_on_profile, setup),
|
|
cmocka_unit_test_setup(test_snmp_batches_on_timeout, setup),
|
|
cmocka_unit_test_setup(test_uptime_baseline_survives_unknown, setup),
|
|
cmocka_unit_test_setup(test_script_reindex_and_completion, setup),
|
|
cmocka_unit_test_setup(test_system_uptime, setup),
|
|
};
|
|
int failures;
|
|
|
|
init_mutexes();
|
|
assert(spine_permits_init(&available_scripts, 2) == 0);
|
|
failures = cmocka_run_group_tests(tests, NULL, NULL);
|
|
reset_fake();
|
|
return failures;
|
|
}
|