|
|
#include "../../unity/unity.h" |
|
|
|
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <locale.h> |
|
|
#include <stdint.h> |
|
|
#include <stdbool.h> |
|
|
#include <unistd.h> |
|
|
|
|
|
|
|
|
|
|
|
extern struct field_range_pair *frp; |
|
|
|
|
|
|
|
|
typedef struct { |
|
|
char *out; |
|
|
int retval; |
|
|
int err; |
|
|
} CapResult; |
|
|
|
|
|
static CapResult capture_process_line_call(const char *input, bool newline) |
|
|
{ |
|
|
CapResult r = {0}; |
|
|
|
|
|
|
|
|
char *mutable_line = NULL; |
|
|
size_t inlen = strlen(input); |
|
|
mutable_line = (char *)malloc(inlen + 1); |
|
|
if (!mutable_line) { r.err = 1; return r; } |
|
|
memcpy(mutable_line, input, inlen + 1); |
|
|
|
|
|
|
|
|
FILE *tmp = tmpfile(); |
|
|
if (!tmp) { free(mutable_line); r.err = 2; return r; } |
|
|
fflush(stdout); |
|
|
int saved_fd = dup(fileno(stdout)); |
|
|
if (saved_fd < 0) { fclose(tmp); free(mutable_line); r.err = 3; return r; } |
|
|
if (dup2(fileno(tmp), fileno(stdout)) < 0) { |
|
|
close(saved_fd); fclose(tmp); free(mutable_line); r.err = 4; return r; |
|
|
} |
|
|
|
|
|
|
|
|
int rv = process_line(mutable_line, newline); |
|
|
|
|
|
|
|
|
fflush(stdout); |
|
|
fflush(tmp); |
|
|
fseek(tmp, 0, SEEK_END); |
|
|
long sz = ftell(tmp); |
|
|
if (sz < 0) sz = 0; |
|
|
fseek(tmp, 0, SEEK_SET); |
|
|
char *buf = (char *)malloc((size_t)sz + 1); |
|
|
if (!buf) { |
|
|
|
|
|
dup2(saved_fd, fileno(stdout)); |
|
|
close(saved_fd); |
|
|
fclose(tmp); |
|
|
free(mutable_line); |
|
|
r.err = 5; return r; |
|
|
} |
|
|
size_t rd = fread(buf, 1, (size_t)sz, tmp); |
|
|
buf[rd] = '\0'; |
|
|
|
|
|
|
|
|
dup2(saved_fd, fileno(stdout)); |
|
|
close(saved_fd); |
|
|
fclose(tmp); |
|
|
|
|
|
r.out = buf; |
|
|
r.retval = rv; |
|
|
r.err = 0; |
|
|
|
|
|
free(mutable_line); |
|
|
return r; |
|
|
} |
|
|
|
|
|
|
|
|
static void set_locale_basics(void) |
|
|
{ |
|
|
|
|
|
setlocale(LC_ALL, "C"); |
|
|
|
|
|
|
|
|
|
|
|
extern const char *decimal_point; |
|
|
extern int decimal_point_length; |
|
|
extern const char *thousands_sep; |
|
|
extern int thousands_sep_length; |
|
|
extern unsigned char line_delim; |
|
|
|
|
|
|
|
|
decimal_point = "."; |
|
|
decimal_point_length = 1; |
|
|
thousands_sep = ""; |
|
|
thousands_sep_length = 0; |
|
|
|
|
|
line_delim = '\n'; |
|
|
} |
|
|
|
|
|
static void reset_numfmt_state(void) |
|
|
{ |
|
|
|
|
|
extern const char *delimiter; |
|
|
extern int auto_padding; |
|
|
extern intmax_t padding_width; |
|
|
extern int zero_padding_width; |
|
|
extern long int user_precision; |
|
|
extern const char *suffix; |
|
|
extern const char *unit_separator; |
|
|
extern int grouping; |
|
|
extern enum inval_type inval_style; |
|
|
|
|
|
delimiter = NULL; |
|
|
auto_padding = 0; |
|
|
padding_width = 0; |
|
|
zero_padding_width = 0; |
|
|
user_precision = -1; |
|
|
suffix = NULL; |
|
|
unit_separator = NULL; |
|
|
grouping = 0; |
|
|
inval_style = inval_abort; |
|
|
} |
|
|
|
|
|
|
|
|
static struct field_range_pair FRP_NONE[] = { |
|
|
{ UINTMAX_MAX, 0 } |
|
|
}; |
|
|
|
|
|
static struct field_range_pair FRP_ALL[] = { |
|
|
{ 1, UINTMAX_MAX }, |
|
|
{ UINTMAX_MAX, 0 } |
|
|
}; |
|
|
|
|
|
static struct field_range_pair FRP_FIRST_ONLY[] = { |
|
|
{ 1, 1 }, |
|
|
{ UINTMAX_MAX, 0 } |
|
|
}; |
|
|
|
|
|
void setUp(void) { |
|
|
set_locale_basics(); |
|
|
reset_numfmt_state(); |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void test_process_line_whitespace_preserved_with_newline(void) |
|
|
{ |
|
|
extern const char *delimiter; |
|
|
delimiter = NULL; |
|
|
frp = FRP_NONE; |
|
|
|
|
|
const char *in = " 123 456\t789 "; |
|
|
CapResult r = capture_process_line_call(in, true); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, r.err, "capture failed"); |
|
|
|
|
|
|
|
|
size_t exp_len = strlen(in) + 1; |
|
|
char *exp = (char *)malloc(exp_len + 1); |
|
|
TEST_ASSERT_NOT_NULL(exp); |
|
|
strcpy(exp, in); |
|
|
exp[exp_len - 1] = '\n'; |
|
|
exp[exp_len] = '\0'; |
|
|
|
|
|
TEST_ASSERT_EQUAL_STRING(exp, r.out); |
|
|
TEST_ASSERT_TRUE(r.retval); |
|
|
|
|
|
free(exp); |
|
|
free(r.out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_line_custom_delimiter_simple(void) |
|
|
{ |
|
|
extern const char *delimiter; |
|
|
delimiter = ","; |
|
|
frp = FRP_NONE; |
|
|
|
|
|
const char *in = "a,b,c"; |
|
|
CapResult r = capture_process_line_call(in, true); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, r.err, "capture failed"); |
|
|
|
|
|
char expected[16]; |
|
|
snprintf(expected, sizeof expected, "%s\n", in); |
|
|
|
|
|
TEST_ASSERT_EQUAL_STRING(expected, r.out); |
|
|
TEST_ASSERT_TRUE(r.retval); |
|
|
|
|
|
free(r.out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_line_multibyte_delimiter_no_newline(void) |
|
|
{ |
|
|
extern const char *delimiter; |
|
|
delimiter = "\xE2\x98\x83"; |
|
|
frp = FRP_NONE; |
|
|
|
|
|
const char *in = "x\xE2\x98\x83y\xE2\x98\x83z"; |
|
|
CapResult r = capture_process_line_call(in, false); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, r.err, "capture failed"); |
|
|
|
|
|
TEST_ASSERT_EQUAL_STRING(in, r.out); |
|
|
TEST_ASSERT_TRUE(r.retval); |
|
|
|
|
|
free(r.out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_line_empty_fields_preserved_with_delimiter(void) |
|
|
{ |
|
|
extern const char *delimiter; |
|
|
delimiter = ","; |
|
|
frp = FRP_NONE; |
|
|
|
|
|
const char *in = "a,,b,,"; |
|
|
CapResult r = capture_process_line_call(in, true); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, r.err, "capture failed"); |
|
|
|
|
|
char expected[32]; |
|
|
snprintf(expected, sizeof expected, "%s\n", in); |
|
|
|
|
|
TEST_ASSERT_EQUAL_STRING(expected, r.out); |
|
|
TEST_ASSERT_TRUE(r.retval); |
|
|
|
|
|
free(r.out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_line_invalid_number_included_field_returns_false(void) |
|
|
{ |
|
|
|
|
|
|
|
|
extern enum inval_type inval_style; |
|
|
inval_style = inval_ignore; |
|
|
|
|
|
frp = FRP_FIRST_ONLY; |
|
|
extern const char *delimiter; |
|
|
delimiter = NULL; |
|
|
|
|
|
const char *in = "abc def"; |
|
|
CapResult r = capture_process_line_call(in, true); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, r.err, "capture failed"); |
|
|
|
|
|
char expected[32]; |
|
|
snprintf(expected, sizeof expected, "%s\n", in); |
|
|
|
|
|
TEST_ASSERT_EQUAL_STRING(expected, r.out); |
|
|
TEST_ASSERT_FALSE(r.retval); |
|
|
|
|
|
free(r.out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_process_line_convert_all_fields_valid(void) |
|
|
{ |
|
|
|
|
|
|
|
|
frp = FRP_ALL; |
|
|
extern const char *delimiter; |
|
|
delimiter = NULL; |
|
|
|
|
|
const char *in = "123 456"; |
|
|
CapResult r = capture_process_line_call(in, true); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, r.err, "capture failed"); |
|
|
|
|
|
char expected[32]; |
|
|
snprintf(expected, sizeof expected, "%s\n", in); |
|
|
|
|
|
TEST_ASSERT_EQUAL_STRING(expected, r.out); |
|
|
TEST_ASSERT_TRUE(r.retval); |
|
|
|
|
|
free(r.out); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_process_line_whitespace_preserved_with_newline); |
|
|
RUN_TEST(test_process_line_custom_delimiter_simple); |
|
|
RUN_TEST(test_process_line_multibyte_delimiter_no_newline); |
|
|
RUN_TEST(test_process_line_empty_fields_preserved_with_delimiter); |
|
|
RUN_TEST(test_process_line_invalid_number_included_field_returns_false); |
|
|
RUN_TEST(test_process_line_convert_all_fields_valid); |
|
|
return UNITY_END(); |
|
|
} |