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