|
|
#include "../../unity/unity.h" |
|
|
|
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <stdbool.h> |
|
|
#include <unistd.h> |
|
|
|
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
|
|
|
extern const char *empty_filler; |
|
|
empty_filler = NULL; |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
extern const char *empty_filler; |
|
|
empty_filler = NULL; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void helper_init_line(struct line *line, |
|
|
const char *const *datas, |
|
|
const size_t *lens, |
|
|
size_t n) |
|
|
{ |
|
|
memset(line, 0, sizeof(*line)); |
|
|
line->nfields = (idx_t)n; |
|
|
line->nfields_allocated = (idx_t)n; |
|
|
if (n) { |
|
|
line->fields = (struct field *)calloc(n, sizeof(struct field)); |
|
|
for (size_t i = 0; i < n; i++) { |
|
|
size_t len = lens ? lens[i] : strlen(datas[i]); |
|
|
char *buf = (char *)malloc(len + 1); |
|
|
if (len) memcpy(buf, datas[i], len); |
|
|
buf[len] = '\0'; |
|
|
line->fields[i].beg = buf; |
|
|
line->fields[i].len = (idx_t)len; |
|
|
} |
|
|
} else { |
|
|
line->fields = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static void helper_free_line(struct line *line) |
|
|
{ |
|
|
if (!line) return; |
|
|
for (idx_t i = 0; i < line->nfields; i++) { |
|
|
free(line->fields[i].beg); |
|
|
line->fields[i].beg = NULL; |
|
|
} |
|
|
free(line->fields); |
|
|
line->fields = NULL; |
|
|
line->nfields = 0; |
|
|
line->nfields_allocated = 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char *capture_prfield_output(idx_t n, const struct line *line, size_t *out_len) |
|
|
{ |
|
|
fflush(stdout); |
|
|
|
|
|
int saved_fd = dup(STDOUT_FILENO); |
|
|
if (saved_fd < 0) { |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
FILE *tmp = tmpfile(); |
|
|
if (!tmp) { |
|
|
close(saved_fd); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
int tmp_fd = fileno(tmp); |
|
|
if (dup2(tmp_fd, STDOUT_FILENO) < 0) { |
|
|
fclose(tmp); |
|
|
close(saved_fd); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
prfield(n, line); |
|
|
fflush(stdout); |
|
|
|
|
|
|
|
|
long sz = 0; |
|
|
if (fseek(tmp, 0, SEEK_END) == 0) { |
|
|
sz = ftell(tmp); |
|
|
if (sz < 0) sz = 0; |
|
|
fseek(tmp, 0, SEEK_SET); |
|
|
} |
|
|
|
|
|
size_t size = (size_t)sz; |
|
|
char *buf = (char *)malloc(size + 1); |
|
|
if (!buf) { |
|
|
|
|
|
dup2(saved_fd, STDOUT_FILENO); |
|
|
close(saved_fd); |
|
|
fclose(tmp); |
|
|
return NULL; |
|
|
} |
|
|
if (size) { |
|
|
size_t r = fread(buf, 1, size, tmp); |
|
|
(void)r; |
|
|
} |
|
|
buf[size] = '\0'; |
|
|
|
|
|
|
|
|
fflush(stdout); |
|
|
dup2(saved_fd, STDOUT_FILENO); |
|
|
close(saved_fd); |
|
|
fclose(tmp); |
|
|
|
|
|
if (out_len) *out_len = size; |
|
|
return buf; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void test_prfield_prints_existing_nonempty_field(void) |
|
|
{ |
|
|
struct line line; |
|
|
const char *datas[] = { "key", "value", "foo" }; |
|
|
helper_init_line(&line, datas, NULL, 3); |
|
|
|
|
|
size_t out_len = 0; |
|
|
char *out = capture_prfield_output((idx_t)1, &line, &out_len); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(strlen("value"), out_len); |
|
|
TEST_ASSERT_EQUAL_STRING_LEN("value", out, out_len); |
|
|
|
|
|
free(out); |
|
|
helper_free_line(&line); |
|
|
} |
|
|
|
|
|
static void test_prfield_uses_empty_filler_for_empty_field(void) |
|
|
{ |
|
|
extern const char *empty_filler; |
|
|
empty_filler = "(nil)"; |
|
|
|
|
|
struct line line; |
|
|
const char *datas[] = { "" }; |
|
|
size_t lens[] = { 0 }; |
|
|
helper_init_line(&line, datas, lens, 1); |
|
|
|
|
|
size_t out_len = 0; |
|
|
char *out = capture_prfield_output((idx_t)0, &line, &out_len); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(strlen("(nil)"), out_len); |
|
|
TEST_ASSERT_EQUAL_STRING_LEN("(nil)", out, out_len); |
|
|
|
|
|
free(out); |
|
|
helper_free_line(&line); |
|
|
} |
|
|
|
|
|
static void test_prfield_prints_nothing_for_empty_field_without_filler(void) |
|
|
{ |
|
|
extern const char *empty_filler; |
|
|
empty_filler = NULL; |
|
|
|
|
|
struct line line; |
|
|
const char *datas[] = { "" }; |
|
|
size_t lens[] = { 0 }; |
|
|
helper_init_line(&line, datas, lens, 1); |
|
|
|
|
|
size_t out_len = 0; |
|
|
char *out = capture_prfield_output((idx_t)0, &line, &out_len); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(0, out_len); |
|
|
TEST_ASSERT_EQUAL_STRING("", out); |
|
|
|
|
|
free(out); |
|
|
helper_free_line(&line); |
|
|
} |
|
|
|
|
|
static void test_prfield_uses_empty_filler_for_missing_field(void) |
|
|
{ |
|
|
extern const char *empty_filler; |
|
|
empty_filler = "[MISSING]"; |
|
|
|
|
|
struct line line; |
|
|
const char *datas[] = { "only-one" }; |
|
|
helper_init_line(&line, datas, NULL, 1); |
|
|
|
|
|
size_t out_len = 0; |
|
|
char *out = capture_prfield_output((idx_t)5, &line, &out_len); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(strlen("[MISSING]"), out_len); |
|
|
TEST_ASSERT_EQUAL_STRING_LEN("[MISSING]", out, out_len); |
|
|
|
|
|
free(out); |
|
|
helper_free_line(&line); |
|
|
} |
|
|
|
|
|
static void test_prfield_prints_nothing_for_missing_field_without_filler(void) |
|
|
{ |
|
|
extern const char *empty_filler; |
|
|
empty_filler = NULL; |
|
|
|
|
|
struct line line; |
|
|
const char *datas[] = { "field0" }; |
|
|
helper_init_line(&line, datas, NULL, 1); |
|
|
|
|
|
size_t out_len = 0; |
|
|
char *out = capture_prfield_output((idx_t)1000, &line, &out_len); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(0, out_len); |
|
|
TEST_ASSERT_EQUAL_STRING("", out); |
|
|
|
|
|
free(out); |
|
|
helper_free_line(&line); |
|
|
} |
|
|
|
|
|
static void test_prfield_handles_binary_field_data(void) |
|
|
{ |
|
|
|
|
|
char bin[4]; |
|
|
bin[0] = 'A'; bin[1] = 'B'; bin[2] = '\0'; bin[3] = 'C'; |
|
|
|
|
|
const char *datas[] = { bin }; |
|
|
size_t lens[] = { 4 }; |
|
|
|
|
|
struct line line; |
|
|
helper_init_line(&line, datas, lens, 1); |
|
|
|
|
|
size_t out_len = 0; |
|
|
char *out = capture_prfield_output((idx_t)0, &line, &out_len); |
|
|
|
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_size_t(4, out_len); |
|
|
TEST_ASSERT_EQUAL_CHAR('A', out[0]); |
|
|
TEST_ASSERT_EQUAL_CHAR('B', out[1]); |
|
|
TEST_ASSERT_EQUAL_CHAR('\0', out[2]); |
|
|
TEST_ASSERT_EQUAL_CHAR('C', out[3]); |
|
|
|
|
|
free(out); |
|
|
helper_free_line(&line); |
|
|
} |
|
|
|
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
|
|
|
RUN_TEST(test_prfield_prints_existing_nonempty_field); |
|
|
RUN_TEST(test_prfield_uses_empty_filler_for_empty_field); |
|
|
RUN_TEST(test_prfield_prints_nothing_for_empty_field_without_filler); |
|
|
RUN_TEST(test_prfield_uses_empty_filler_for_missing_field); |
|
|
RUN_TEST(test_prfield_prints_nothing_for_missing_field_without_filler); |
|
|
RUN_TEST(test_prfield_handles_binary_field_data); |
|
|
|
|
|
return UNITY_END(); |
|
|
} |