#include <pg_query.h>
#include "src/pg_query_internal.h"
#include "test/framework/main.h"
#include <string.h>
const size_t ARBITRARY_LENGTH_LIMIT = 500;
int
TEST_INIT_impl(TestState * test_state, const char *func, size_t line)
{
if ((test_state->wanted_test != NULL) && (strcmp(test_state->wanted_test, func) != 0))
return 1;
printf("%s (line %li)\n", func, line);
return 0;
}
int
TEST_BOUNDED_STRCMP(char *s1, char *s2)
{
if (s1 == NULL || s2 == NULL)
return (s1 == s2) ? 0 : -1;
return strncmp(s1, s2, ARBITRARY_LENGTH_LIMIT);
}
void
TEST_ASSERT_NULL_impl(TestState * test_state, char *actual_str, void *actual)
{
if (actual == NULL)
TEST_PASS();
else
TEST_FAIL(" FAIL: expected %s to be NULL\n", actual_str);
}
void
TEST_ASSERT_STR_EQUAL_impl(TestState * test_state, char *actual_str, char *actual, char *expected)
{
if (TEST_BOUNDED_STRCMP(actual, expected) == 0)
test_state->passed++;
else
{
printf(" FAIL: Expected `actual` (%s) and `expected` to be equivalent.\n", actual_str);
printf(" actual: %s\n", actual);
printf(" expected: %s\n\n", expected);
test_state->failed++;
}
}
void
TEST_ASSERT_LIST_LENGTH_impl(TestState * test_state, char *lst_str, List *lst, size_t expected_len)
{
size_t actual_len = list_length(lst);
if (actual_len == expected_len)
{
test_state->passed++;
}
else
{
printf(" FAIL: expected %s to have length %li\n", lst_str, expected_len);
printf(" actual length is %li\n\n", actual_len);
test_state->failed++;
}
}
static char *
str_list_to_buf(char **list, size_t list_len)
{
size_t len = 3;
len += 2 * list_len;
len += 2 * (list_len - 1);
for (int i = 0; i < list_len; i++)
len += strlen(list[i]);
char *buf = malloc(sizeof(char) * len);
size_t offset = snprintf(buf, len, "[");
for (int i = 0; i < list_len; i++)
{
offset += snprintf(buf + offset, len - offset, "\"%s\"", list[i]);
if (i < (list_len - 1))
offset += snprintf(buf + offset, len - offset, ", ");
}
offset += snprintf(buf + offset, len - offset, "]");
return buf;
}
void
TEST_ASSERT_LIST_EQUAL_impl(TestState * test_state, char *actual_str, List *actual, List *expected)
{
size_t actual_len = list_length(actual);
size_t expected_len = list_length(expected);
char **act_ary = malloc(sizeof(char *) * actual_len);
char **exp_ary = malloc(sizeof(char *) * expected_len);
ListCell *lc = NULL;
foreach(lc, actual)
{
size_t i = foreach_current_index(lc);
act_ary[i] = lfirst(lc);
}
foreach(lc, expected)
{
size_t i = foreach_current_index(lc);
exp_ary[i] = lfirst(lc);
}
qsort(act_ary, actual_len, sizeof(char *), pg_qsort_strcmp);
qsort(exp_ary, expected_len, sizeof(char *), pg_qsort_strcmp);
int failed = 0;
if (actual_len != expected_len)
{
failed = 1;
}
if (failed == 0)
{
for (size_t i = 0; i < actual_len; i++)
{
if (strcmp(act_ary[i], exp_ary[i]) != 0)
{
failed = 1;
break;
}
}
}
if (failed)
{
test_state->failed++;
printf(" FAIL: Expected `actual` and `expected` to be equivalent.\n");
char *act_str = str_list_to_buf(act_ary, actual_len);
printf(" actual: %s\n", act_str);
free(act_str);
char *exp_str = str_list_to_buf(exp_ary, expected_len);
printf(" expected: %s\n\n", exp_str);
free(exp_str);
}
else
test_state->passed++;
free(act_ary);
free(exp_ary);
}
static
int
test_run_impl(int argc, char *argv[], TestFn * tests[], TestCleanupFn * test_cleanup, bool use_mctx)
{
TestState test_state = {0};
bool fail_fast = false;
for (size_t i = 1; i < argc; i++)
{
fail_fast = true;
if (strncmp(argv[i], "-ff", 4) != 0) {
test_state.wanted_test = argv[i];
break;
}
}
pg_query_init();
for (size_t i = 0; tests[i] != NULL; i++)
{
MemoryContext ctx;
if (use_mctx)
ctx = pg_query_enter_memory_context();
tests[i] (&test_state);
if (test_cleanup != NULL)
test_cleanup();
if (use_mctx)
pg_query_exit_memory_context(ctx);
if (fail_fast && test_state.failed > 0)
break;
}
bool failed = (test_state.failed > 0);
printf("\ntest result: %s. %li passed; %li failed; %li skipped\n", failed ? "FAILED" : "ok", test_state.passed, test_state.failed, test_state.skipped);
pg_query_exit();
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
}
int
test_run(int argc, char *argv[], TestFn * tests[], TestCleanupFn * test_cleanup)
{
return test_run_impl(argc, argv, tests, test_cleanup, false);
}
int
test_run_with_mcxt(int argc, char *argv[], TestFn * tests[], TestCleanupFn * test_cleanup)
{
return test_run_impl(argc, argv, tests, test_cleanup, true);
}