/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program 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 General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "zbxmocktest.h" #include "zbxmockdata.h" #include "zbxmockdb.h" /* make sure that __wrap_*() prototypes match unwrapped counterparts */ #define zbx_db_vselect __wrap_zbx_db_vselect #define zbx_db_fetch_basic __wrap_zbx_db_fetch_basic #define zbx_db_fetch __wrap_zbx_db_fetch_basic #define zbx_db_free_result __wrap_zbx_db_free_result #include "zbxdb.h" #undef zbx_db_vselect #undef zbx_db_fetch_basic #undef zbx_db_fetch #undef zbx_db_free_result #define __zbx_db_execute __wrap___zbx_db_execute #define zbx_db_execute_multiple_query __wrap_zbx_db_execute_multiple_query #define zbx_db_begin __wrap_zbx_db_begin #define zbx_db_commit __wrap_xbc_db_commit #include "zbxdbhigh.h" #undef __zbx_db_execute #undef zbx_db_execute_multiple_query #undef zbx_db_begin #undef zbx_db_commit #define ZBX_MOCK_DB_RESULT_COLUMNS_MAX 128 typedef struct { char *data_source; int num; } zbx_mockdb_query_t; typedef struct { zbx_hashset_t queries; } zbx_mockdb_t; static zbx_mockdb_t mockdb; struct zbx_db_result { zbx_db_row_t row; char *data_source; /* for error messages */ zbx_mock_handle_t rows; int row_to_fetch; /* for error messages */ int columns; /* to make sure that rows have identical number of columns */ }; zbx_db_result_t __fwd_zbx_db_select(const char *fmt, ...); zbx_db_result_t __wrap_zbx_db_select_n_basic(const char *query, int n); int __wrap___zbx_db_execute(const char *fmt, ...); /* zbx_mockdb_t:queries hashset support */ static zbx_hash_t mockdb_query_hash(const void *data) { const zbx_mockdb_query_t *query = (const zbx_mockdb_query_t *)data; return ZBX_DEFAULT_STRING_HASH_FUNC(query->data_source); } static int mockdb_query_compare(const void *d1, const void *d2) { const zbx_mockdb_query_t *q1 = (const zbx_mockdb_query_t *)d1; const zbx_mockdb_query_t *q2 = (const zbx_mockdb_query_t *)d2; return strcmp(q1->data_source, q2->data_source); } static void mockdb_query_clear(void *data) { zbx_mockdb_query_t *query = (zbx_mockdb_query_t *)data; zbx_free(query->data_source); } /* = , { "_" ,
} */ static char *generate_data_source(const char *sql) { int found = 0; char *data_source = NULL, *ptr_ds; const char *ptr_sql = sql, *ptr_tmp; data_source = zbx_calloc(NULL, 64, sizeof(char *)); ptr_ds = data_source; while ('\0' != *ptr_sql) { if (0 != found) { if (' ' == *ptr_sql || ';' == *ptr_sql) { found = 0; *(ptr_ds++) = ' '; } else *(ptr_ds++) = *ptr_sql; ptr_sql++; } else if (NULL != (ptr_tmp = strstr(ptr_sql, "from "))) { found = 1; ptr_sql = ptr_tmp + strlen("from "); } else if (NULL != (ptr_tmp = strstr(ptr_sql, "join "))) { found = 1; ptr_sql = ptr_tmp + strlen("join "); } else break; } if (ptr_ds == data_source) zbx_free(data_source); /* failed to generate data_source */ else *(ptr_ds - 1) = '\0'; return data_source; } zbx_db_result_t __wrap_zbx_db_vselect(const char *fmt, va_list args) { char *sql = NULL, *data_source = NULL; zbx_mock_error_t error; zbx_mock_handle_t rows; zbx_mockdb_query_t *query, query_local; zbx_db_result_t result = NULL; sql = zbx_dvsprintf(sql, fmt, args); printf("\tSQL: %s\n", sql); if (NULL == (query_local.data_source = generate_data_source(sql))) fail_msg("Cannot generate data source string from SQL query: %s", sql); zbx_free(sql); if (NULL == (query = zbx_hashset_search(&mockdb.queries, &query_local))) { query_local.num = 1; query = zbx_hashset_insert(&mockdb.queries, &query_local, sizeof(query_local)); data_source = zbx_strdup(NULL, query->data_source); } else { query->num++; zbx_free(query_local.data_source); data_source = zbx_dsprintf(NULL, "%s (%d)", query->data_source, query->num); } if (ZBX_MOCK_SUCCESS != (error = zbx_mock_db_rows(data_source, &rows))) fail_msg("Cannot find data for data source \"%s\": %s", data_source, zbx_mock_error_string(error)); result = zbx_malloc(result, sizeof(struct zbx_db_result)); result->row = zbx_malloc(NULL, ZBX_MOCK_DB_RESULT_COLUMNS_MAX * sizeof(char *)); result->data_source = data_source; result->rows = rows; result->row_to_fetch = 1; result->columns = -1; return result; } zbx_db_result_t __fwd_zbx_db_select(const char *fmt, ...) { va_list args; zbx_db_result_t result; va_start(args, fmt); result = __wrap_zbx_db_vselect(fmt, args); va_end(args); return result; } zbx_db_result_t __wrap_zbx_db_select_n_basic(const char *query, int n) { return __fwd_zbx_db_select("%s limit %d", query, n); } zbx_db_row_t __wrap_zbx_db_fetch_basic(zbx_db_result_t result) { zbx_mock_error_t error; zbx_mock_handle_t row, field; int column = 0; if (NULL == result || ZBX_MOCK_END_OF_VECTOR == (error = zbx_mock_vector_element(result->rows, &row))) return NULL; if (ZBX_MOCK_SUCCESS != error) { fail_msg("Cannot fetch row %d for data source \"%s\": %s", result->row_to_fetch, result->data_source, zbx_mock_error_string(error)); } while (ZBX_MOCK_SUCCESS == (error = zbx_mock_vector_element(row, &field))) { if (ZBX_MOCK_DB_RESULT_COLUMNS_MAX <= column) fail_msg("Too many columns for data source \"%s\".", result->data_source); if (ZBX_MOCK_SUCCESS != (error = zbx_mock_string(field, (const char **)&result->row[column]))) break; column++; } if (ZBX_MOCK_END_OF_VECTOR != error) { fail_msg("Cannot get value of column %d, row %d for data source \"%s\": %s", column + 1, result->row_to_fetch, result->data_source, zbx_mock_error_string(error)); } if (0 <= result->columns) { if (column < result->columns) { fail_msg("Too few columns in row %d for data source \"%s\".", result->row_to_fetch, result->data_source); } if (column > result->columns) { fail_msg("Too many columns in row %d for data source \"%s\".", result->row_to_fetch, result->data_source); } } else result->columns = column; while (column < ZBX_MOCK_DB_RESULT_COLUMNS_MAX) result->row[column++] = NULL; result->row_to_fetch++; return result->row; } void __wrap_zbx_db_free_result(zbx_db_result_t result) { if (NULL != result) { zbx_free(result->row); zbx_free(result->data_source); } zbx_free(result); } int __wrap___zbx_db_execute(const char *fmt, ...) { ZBX_UNUSED(fmt); return 0; } int __wrap_zbx_db_execute_multiple_query(const char *query, const char *field_name, zbx_vector_uint64_t *ids) { ZBX_UNUSED(query); ZBX_UNUSED(field_name); ZBX_UNUSED(ids); return SUCCEED; } void __wrap_zbx_db_begin(void) { } int __wrap_zbx_db_commit(void) { return ZBX_DB_OK; } void zbx_mockdb_init(void) { zbx_hashset_create_ext(&mockdb.queries, 0, mockdb_query_hash, mockdb_query_compare, mockdb_query_clear, ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC); } void zbx_mockdb_destroy(void) { zbx_hashset_destroy(&mockdb.queries); }