| #include "../../unity/unity.h" |
|
|
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <locale.h> |
| #include <time.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
|
|
| |
| |
|
|
| |
| static char *create_tempfile_with_content(const char *content) |
| { |
| char tmpl[] = "/tmp/tests_batch_convert_XXXXXX"; |
| int fd = mkstemp(tmpl); |
| if (fd == -1) { |
| return NULL; |
| } |
| size_t len = strlen(content); |
| ssize_t w = write(fd, content, len); |
| (void)w; |
| close(fd); |
| return strdup(tmpl); |
| } |
|
|
| |
| |
| typedef struct { |
| int saved_fd; |
| FILE *tmp; |
| } Capture; |
|
|
| static void capture_begin(int std_fd, Capture *cap) |
| { |
| fflush(NULL); |
| cap->saved_fd = dup(std_fd); |
| cap->tmp = tmpfile(); |
| |
| if (cap->tmp) { |
| int tfd = fileno(cap->tmp); |
| dup2(tfd, std_fd); |
| } |
| } |
|
|
| static char *capture_end(int std_fd, Capture *cap) |
| { |
| fflush(NULL); |
| char *buf = NULL; |
| size_t size = 0; |
|
|
| if (cap->tmp) { |
| long cur = ftell(cap->tmp); |
| (void)cur; |
| fseek(cap->tmp, 0, SEEK_SET); |
|
|
| |
| size_t capsz = 1024; |
| buf = (char *)malloc(capsz); |
| if (!buf) buf = NULL; |
| size_t used = 0; |
| while (1) { |
| if (used + 512 > capsz) { |
| capsz *= 2; |
| char *nb = (char *)realloc(buf, capsz); |
| if (!nb) { free(buf); buf = NULL; break; } |
| buf = nb; |
| } |
| size_t n = fread(buf + used, 1, 512, cap->tmp); |
| used += n; |
| if (n < 512) { |
| if (feof(cap->tmp)) break; |
| if (ferror(cap->tmp)) break; |
| } |
| } |
| if (buf) { |
| |
| if (used == 0) { |
| buf[0] = '\0'; |
| } else { |
| buf[used] = '\0'; |
| } |
| } |
| } |
|
|
| |
| if (cap->saved_fd >= 0) |
| dup2(cap->saved_fd, std_fd); |
| if (cap->saved_fd >= 0) |
| close(cap->saved_fd); |
| if (cap->tmp) |
| fclose(cap->tmp); |
|
|
| return buf ? buf : strdup(""); |
| } |
|
|
| |
| typedef struct { |
| int saved_fd0; |
| } StdinSwap; |
|
|
| static int stdin_swap_begin(const char *path, StdinSwap *ss) |
| { |
| fflush(NULL); |
| ss->saved_fd0 = dup(STDIN_FILENO); |
| int fd = open(path, O_RDONLY); |
| if (fd < 0) return -1; |
| if (dup2(fd, STDIN_FILENO) < 0) { |
| close(fd); |
| return -1; |
| } |
| close(fd); |
| return 0; |
| } |
|
|
| static void stdin_swap_end(StdinSwap *ss) |
| { |
| |
| if (ss->saved_fd0 >= 0) { |
| dup2(ss->saved_fd0, STDIN_FILENO); |
| close(ss->saved_fd0); |
| } |
| } |
|
|
| |
| void setUp(void) { |
| |
| setenv("LC_ALL", "C", 1); |
| setlocale(LC_ALL, "C"); |
|
|
| |
| setenv("TZ", "UTC", 1); |
| tzset(); |
|
|
| |
| |
| extern unsigned int parse_datetime_flags; |
| parse_datetime_flags = 0; |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
| void test_batch_convert_single_valid_line(void) |
| { |
| char *path = create_tempfile_with_content("@0\n"); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp file"); |
|
|
| Capture out_cap = { -1, NULL }; |
| Capture err_cap = { -1, NULL }; |
| capture_begin(STDOUT_FILENO, &out_cap); |
| capture_begin(STDERR_FILENO, &err_cap); |
|
|
| timezone_t tz = 0; |
| bool ok = batch_convert(path, "%s", false, tz, "UTC"); |
|
|
| char *out_s = capture_end(STDOUT_FILENO, &out_cap); |
| char *err_s = capture_end(STDERR_FILENO, &err_cap); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out_s); |
| TEST_ASSERT_EQUAL_STRING("0\n", out_s); |
| TEST_ASSERT_NOT_NULL(err_s); |
| TEST_ASSERT_EQUAL_INT(0, (int)strlen(err_s)); |
|
|
| free(out_s); |
| free(err_s); |
| remove(path); |
| free(path); |
| } |
|
|
| |
| |
| void test_batch_convert_multiple_lines_with_invalid(void) |
| { |
| const char *content = "@1\nINVALID\n@2\n"; |
| char *path = create_tempfile_with_content(content); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp file"); |
|
|
| Capture out_cap = { -1, NULL }; |
| Capture err_cap = { -1, NULL }; |
| capture_begin(STDOUT_FILENO, &out_cap); |
| capture_begin(STDERR_FILENO, &err_cap); |
|
|
| timezone_t tz = 0; |
| bool ok = batch_convert(path, "%s", false, tz, "UTC"); |
|
|
| char *out_s = capture_end(STDOUT_FILENO, &out_cap); |
| char *err_s = capture_end(STDERR_FILENO, &err_cap); |
|
|
| TEST_ASSERT_FALSE(ok); |
| TEST_ASSERT_NOT_NULL(out_s); |
| |
| TEST_ASSERT_EQUAL_STRING("1\n2\n", out_s); |
|
|
| TEST_ASSERT_NOT_NULL(err_s); |
| |
| TEST_ASSERT_NOT_EQUAL(NULL, strstr(err_s, "invalid date")); |
| TEST_ASSERT_NOT_EQUAL(NULL, strstr(err_s, "INVALID")); |
|
|
| free(out_s); |
| free(err_s); |
| remove(path); |
| free(path); |
| } |
|
|
| |
| void test_batch_convert_reads_from_stdin(void) |
| { |
| char *path = create_tempfile_with_content("@3\n"); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp file"); |
|
|
| StdinSwap ss; |
| int rc = stdin_swap_begin(path, &ss); |
| TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Failed to swap stdin"); |
|
|
| Capture out_cap = { -1, NULL }; |
| Capture err_cap = { -1, NULL }; |
| capture_begin(STDOUT_FILENO, &out_cap); |
| capture_begin(STDERR_FILENO, &err_cap); |
|
|
| timezone_t tz = 0; |
| bool ok = batch_convert("-", "%s", false, tz, "UTC"); |
|
|
| char *out_s = capture_end(STDOUT_FILENO, &out_cap); |
| char *err_s = capture_end(STDERR_FILENO, &err_cap); |
|
|
| stdin_swap_end(&ss); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out_s); |
| TEST_ASSERT_EQUAL_STRING("3\n", out_s); |
| TEST_ASSERT_NOT_NULL(err_s); |
| TEST_ASSERT_EQUAL_INT(0, (int)strlen(err_s)); |
|
|
| free(out_s); |
| free(err_s); |
| remove(path); |
| free(path); |
| } |
|
|
| |
| void test_batch_convert_use_c_locale(void) |
| { |
| char *path = create_tempfile_with_content("@0\n"); |
| TEST_ASSERT_NOT_NULL(path); |
|
|
| Capture out_cap = { -1, NULL }; |
| capture_begin(STDOUT_FILENO, &out_cap); |
|
|
| timezone_t tz = 0; |
| bool ok = batch_convert(path, "%a", true, tz, "UTC"); |
|
|
| char *out_s = capture_end(STDOUT_FILENO, &out_cap); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out_s); |
| |
| TEST_ASSERT_EQUAL_STRING("Thu\n", out_s); |
|
|
| free(out_s); |
| remove(path); |
| free(path); |
| } |
|
|
| |
| void test_batch_convert_debug_flag_outputs_notice(void) |
| { |
| char *path = create_tempfile_with_content("@0\n"); |
| TEST_ASSERT_NOT_NULL(path); |
|
|
| extern unsigned int parse_datetime_flags; |
| parse_datetime_flags |= PARSE_DATETIME_DEBUG; |
|
|
| Capture out_cap = { -1, NULL }; |
| Capture err_cap = { -1, NULL }; |
| capture_begin(STDOUT_FILENO, &out_cap); |
| capture_begin(STDERR_FILENO, &err_cap); |
|
|
| const char *fmt = "TEST_FORMAT"; |
| timezone_t tz = 0; |
| bool ok = batch_convert(path, fmt, false, tz, "UTC"); |
|
|
| char *out_s = capture_end(STDOUT_FILENO, &out_cap); |
| char *err_s = capture_end(STDERR_FILENO, &err_cap); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out_s); |
| |
| TEST_ASSERT_EQUAL_STRING("TEST_FORMAT\n", out_s); |
|
|
| TEST_ASSERT_NOT_NULL(err_s); |
| |
| TEST_ASSERT_NOT_EQUAL(NULL, strstr(err_s, "output format:")); |
| TEST_ASSERT_NOT_EQUAL(NULL, strstr(err_s, "TEST_FORMAT")); |
|
|
| free(out_s); |
| free(err_s); |
| remove(path); |
| free(path); |
| } |
|
|
| int main(void) |
| { |
| UNITY_BEGIN(); |
| RUN_TEST(test_batch_convert_single_valid_line); |
| RUN_TEST(test_batch_convert_multiple_lines_with_invalid); |
| RUN_TEST(test_batch_convert_reads_from_stdin); |
| RUN_TEST(test_batch_convert_use_c_locale); |
| RUN_TEST(test_batch_convert_debug_flag_outputs_notice); |
| return UNITY_END(); |
| } |