| #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(); |
| } |