coreutils / tests /cut /tests_for_cut_bytes.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
/* The following globals/types are declared earlier in the cut.c TU and are used here:
- struct field_range_pair
- static unsigned char line_delim;
- static char *output_delimiter_string;
- static size_t output_delimiter_length;
- static char output_delimiter_default[1];
- extern struct field_range_pair *frp;
- static void cut_bytes (FILE *stream);
*/
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)
{
/* Allocate n_pairs + 1 for a sentinel */
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];
}
/* Sentinel to avoid out-of-bounds access in helpers */
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)
{
/* Make pointer-equality match default so cut_bytes inserts no delimiters between ranges */
output_delimiter_string = output_delimiter_default;
output_delimiter_length = 1; /* length value is not used when pointer == default */
}
static void set_output_delim_custom(const char *s)
{
output_delimiter_string = (char *)s;
output_delimiter_length = strlen(s);
}
/* Run cut_bytes on given input bytes with provided field range pairs.
Capture stdout and compare to expected output. */
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)
{
/* Configure line delimiter and ranges */
line_delim = line_delim_ch;
struct field_range_pair *saved_frp = frp;
struct field_range_pair *arr = make_frp(ranges, n_ranges);
frp = arr;
/* Prepare input stream */
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);
/* Capture stdout */
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) {
/* Can't use TEST_ASSERT while redirected; but we haven't redirected yet. */
TEST_FAIL_MESSAGE("dup2 to redirect stdout failed");
}
/* Call function under test while stdout is redirected */
cut_bytes(in);
/* Flush and restore stdout BEFORE assertions */
fflush(stdout);
if (dup2(saved_stdout_fd, fileno(stdout)) < 0) {
/* Now stdout points to capture; error handling minimal */
/* Best effort: close and return */
}
close(saved_stdout_fd);
/* Read captured output */
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);
/* Close files */
fclose(in);
fclose(cap);
/* Restore frp and free */
frp = saved_frp;
free_frp(arr);
/* Assertions */
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) {
/* Default to newline-terminated lines and default output delimiter. */
line_delim = '\n';
set_output_delim_default();
}
void tearDown(void) {
/* Nothing to clean up globally */
}
/* Tests */
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";
/* Select bytes 1 and 3-4; default output delimiter => no insertion */
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";
/* Select bytes 1 and 3-4, with custom delimiter ',' between discontiguous ranges */
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' }; /* No trailing newline */
const char expected[] = { 'b','\n' }; /* Select byte 2; cut_bytes adds newline at EOF */
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)
{
/* Two records: "ab\0" and "cde\0", select second byte of each record. */
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)
{
/* Two lines: "abc\n" and "def\n", select bytes 1 and 3 with custom ":" between ranges.
Expect "a:c\n" then "d:f\n". */
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();
}