| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <unistd.h> |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| static int write_bytes(FILE *f, const void *buf, size_t len) { |
| if (len == 0) return 0; |
| size_t nw = fwrite(buf, 1, len, f); |
| return (nw == len) ? 0 : -1; |
| } |
|
|
| static struct field_range_pair *make_frp(const uintmax_t (*pairs)[2], size_t n_pairs) |
| { |
| |
| struct field_range_pair *arr = (struct field_range_pair *)malloc((n_pairs + 1) * sizeof(*arr)); |
| TEST_ASSERT_NOT_NULL(arr); |
| for (size_t i = 0; i < n_pairs; i++) { |
| arr[i].lo = pairs[i][0]; |
| arr[i].hi = pairs[i][1]; |
| } |
| |
| arr[n_pairs].lo = UINTMAX_MAX; |
| arr[n_pairs].hi = UINTMAX_MAX; |
| return arr; |
| } |
|
|
| static void free_frp(struct field_range_pair *arr) |
| { |
| free(arr); |
| } |
|
|
| static void set_output_delim_default(void) |
| { |
| |
| output_delimiter_string = output_delimiter_default; |
| output_delimiter_length = 1; |
| } |
|
|
| static void set_output_delim_custom(const char *s) |
| { |
| output_delimiter_string = (char *)s; |
| output_delimiter_length = strlen(s); |
| } |
|
|
| |
| |
| static void run_cut_bytes_case(const void *input, size_t in_len, |
| const uintmax_t (*ranges)[2], size_t n_ranges, |
| const void *expected, size_t exp_len, |
| unsigned char line_delim_ch) |
| { |
| |
| line_delim = line_delim_ch; |
|
|
| struct field_range_pair *saved_frp = frp; |
| struct field_range_pair *arr = make_frp(ranges, n_ranges); |
| frp = arr; |
|
|
| |
| FILE *in = tmpfile(); |
| TEST_ASSERT_NOT_NULL(in); |
| if (write_bytes(in, input, in_len) != 0) { |
| TEST_FAIL_MESSAGE("Failed to write input to tmpfile"); |
| } |
| fflush(in); |
| fseek(in, 0, SEEK_SET); |
|
|
| |
| fflush(stdout); |
| int saved_stdout_fd = dup(fileno(stdout)); |
| TEST_ASSERT_TRUE_MESSAGE(saved_stdout_fd >= 0, "dup(stdout) failed"); |
|
|
| FILE *cap = tmpfile(); |
| TEST_ASSERT_NOT_NULL(cap); |
| int cap_fd = fileno(cap); |
| TEST_ASSERT_TRUE_MESSAGE(cap_fd >= 0, "fileno(cap) failed"); |
|
|
| if (dup2(cap_fd, fileno(stdout)) < 0) { |
| |
| TEST_FAIL_MESSAGE("dup2 to redirect stdout failed"); |
| } |
|
|
| |
| cut_bytes(in); |
|
|
| |
| fflush(stdout); |
| if (dup2(saved_stdout_fd, fileno(stdout)) < 0) { |
| |
| |
| } |
| close(saved_stdout_fd); |
|
|
| |
| fflush(cap); |
| fseek(cap, 0, SEEK_END); |
| long got_len_l = ftell(cap); |
| TEST_ASSERT_TRUE_MESSAGE(got_len_l >= 0, "ftell on capture failed"); |
| size_t got_len = (size_t)got_len_l; |
| fseek(cap, 0, SEEK_SET); |
|
|
| char *got = (char *)malloc(got_len ? got_len : 1); |
| TEST_ASSERT_NOT_NULL(got); |
| size_t nr = fread(got, 1, got_len, cap); |
| TEST_ASSERT_EQUAL_SIZE_T(got_len, nr); |
|
|
| |
| fclose(in); |
| fclose(cap); |
|
|
| |
| frp = saved_frp; |
| free_frp(arr); |
|
|
| |
| TEST_ASSERT_EQUAL_SIZE_T(exp_len, got_len); |
| if (exp_len == got_len) { |
| TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, got, exp_len); |
| } |
| free(got); |
| } |
|
|
| void setUp(void) { |
| |
| line_delim = '\n'; |
| set_output_delim_default(); |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
|
|
| void test_cut_bytes_select_first_byte_simple_line(void) |
| { |
| const char *input = "abcdef\n"; |
| const char *expected = "a\n"; |
| const uintmax_t ranges[][2] = { {1,1} }; |
| set_output_delim_default(); |
| run_cut_bytes_case(input, strlen(input), |
| ranges, 1, |
| expected, strlen(expected), |
| '\n'); |
| } |
|
|
| void test_cut_bytes_multi_ranges_no_custom_delim(void) |
| { |
| const char *input = "abcde\n"; |
| |
| const char *expected = "acd\n"; |
| const uintmax_t ranges[][2] = { {1,1}, {3,4} }; |
| set_output_delim_default(); |
| run_cut_bytes_case(input, strlen(input), |
| ranges, 2, |
| expected, strlen(expected), |
| '\n'); |
| } |
|
|
| void test_cut_bytes_multi_ranges_with_custom_delim(void) |
| { |
| const char *input = "abcde\n"; |
| |
| const char *expected = "a,cd\n"; |
| const uintmax_t ranges[][2] = { {1,1}, {3,4} }; |
| static const char comma[] = ","; |
| set_output_delim_custom(comma); |
| run_cut_bytes_case(input, strlen(input), |
| ranges, 2, |
| expected, strlen(expected), |
| '\n'); |
| } |
|
|
| void test_cut_bytes_appends_newline_on_eof_without_newline(void) |
| { |
| const char input[] = { 'a','b','c' }; |
| const char expected[] = { 'b','\n' }; |
| const uintmax_t ranges[][2] = { {2,2} }; |
| set_output_delim_default(); |
| run_cut_bytes_case(input, sizeof(input), |
| ranges, 1, |
| expected, sizeof(expected), |
| '\n'); |
| } |
|
|
| void test_cut_bytes_zero_terminated_lines(void) |
| { |
| |
| const char input[] = { 'a','b','\0','c','d','e','\0' }; |
| const char expected[] = { 'b','\0','d','\0' }; |
| const uintmax_t ranges[][2] = { {2,2} }; |
| set_output_delim_default(); |
| run_cut_bytes_case(input, sizeof(input), |
| ranges, 1, |
| expected, sizeof(expected), |
| '\0'); |
| } |
|
|
| void test_cut_bytes_resets_between_lines_and_inserts_delim(void) |
| { |
| |
| |
| const char *input = "abc\ndef\n"; |
| const char *expected = "a:c\nd:f\n"; |
| const uintmax_t ranges[][2] = { {1,1}, {3,3} }; |
| static const char colon[] = ":"; |
| set_output_delim_custom(colon); |
| run_cut_bytes_case(input, strlen(input), |
| ranges, 2, |
| expected, strlen(expected), |
| '\n'); |
| } |
|
|
| void test_cut_bytes_empty_input_produces_no_output(void) |
| { |
| const char *input = ""; |
| const char *expected = ""; |
| const uintmax_t ranges[][2] = { {1,1} }; |
| set_output_delim_default(); |
| run_cut_bytes_case(input, 0, |
| ranges, 1, |
| expected, 0, |
| '\n'); |
| } |
|
|
| int main(void) |
| { |
| UNITY_BEGIN(); |
| RUN_TEST(test_cut_bytes_select_first_byte_simple_line); |
| RUN_TEST(test_cut_bytes_multi_ranges_no_custom_delim); |
| RUN_TEST(test_cut_bytes_multi_ranges_with_custom_delim); |
| RUN_TEST(test_cut_bytes_appends_newline_on_eof_without_newline); |
| RUN_TEST(test_cut_bytes_zero_terminated_lines); |
| RUN_TEST(test_cut_bytes_resets_between_lines_and_inserts_delim); |
| RUN_TEST(test_cut_bytes_empty_input_produces_no_output); |
| return UNITY_END(); |
| } |