coreutils / tests /csplit /tests_for_load_buffer.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
/* The test file is included at the end of the csplit source, so we have
access to the internal static globals and functions used by load_buffer. */
/* Forward declarations for static symbols from the program (available due to inclusion). */
extern bool have_read_eof;
extern struct buffer_record *head;
extern char *hold_area;
extern idx_t hold_count;
extern intmax_t last_line_number;
extern intmax_t current_line;
/* Static helper from program; available due to inclusion. */
static void free_buffer (struct buffer_record *buf);
static int saved_stdin = -1;
/* Helper: write all bytes */
static int write_all(int fd, const void *buf, size_t len) {
const char *p = (const char*)buf;
size_t off = 0;
while (off < len) {
ssize_t w = write(fd, p + off, len - off);
if (w < 0) {
if (errno == EINTR) continue;
return -1;
}
off += (size_t)w;
}
return 0;
}
/* Helper: set STDIN to a pipe providing 'data' (len bytes). */
static void set_stdin_from_data(const char *data, size_t len) {
int fds[2];
TEST_ASSERT_EQUAL_INT(0, pipe(fds));
/* Write all data then close write-end to signal EOF. */
TEST_ASSERT_EQUAL_INT(0, write_all(fds[1], data, len));
close(fds[1]);
/* Redirect read end to STDIN */
TEST_ASSERT_TRUE(fds[0] >= 0);
TEST_ASSERT_TRUE(saved_stdin >= 0);
TEST_ASSERT_NOT_EQUAL(-1, dup2(fds[0], STDIN_FILENO));
close(fds[0]);
}
/* Helper: free any buffers in the linked list and reset pointer */
static void free_all_buffers(void) {
while (head != NULL) {
struct buffer_record *b = head;
head = head->next;
free_buffer(b);
}
head = NULL;
}
/* Helper: reset all internal state related to buffering/reading. */
static void reset_internal_state(void) {
free_all_buffers();
if (hold_area) {
free(hold_area);
hold_area = NULL;
}
hold_count = 0;
last_line_number = 0;
current_line = 0;
have_read_eof = false;
}
void setUp(void) {
/* Save original stdin once */
if (saved_stdin == -1) {
saved_stdin = dup(STDIN_FILENO);
TEST_ASSERT_TRUE(saved_stdin >= 0);
}
reset_internal_state();
}
void tearDown(void) {
/* Restore stdin */
if (saved_stdin >= 0) {
dup2(saved_stdin, STDIN_FILENO);
}
reset_internal_state();
}
/* Test: empty input -> load_buffer returns false, no buffers created, EOF seen. */
void test_load_buffer_empty_input_returns_false(void) {
set_stdin_from_data("", 0);
bool got = load_buffer();
TEST_ASSERT_FALSE(got);
TEST_ASSERT_EQUAL_PTR(NULL, head);
TEST_ASSERT_TRUE(have_read_eof);
TEST_ASSERT_EQUAL_INT64(0, last_line_number);
}
/* Test: multiple short lines in a single buffer. */
void test_load_buffer_reads_multiple_short_lines(void) {
const char *data = "a\nb\nc\n";
set_stdin_from_data(data, strlen(data));
bool got = load_buffer();
TEST_ASSERT_TRUE(got);
TEST_ASSERT_NOT_NULL(head);
TEST_ASSERT_EQUAL_INT(3, head->num_lines);
TEST_ASSERT_EQUAL_INT64(1, head->start_line);
TEST_ASSERT_EQUAL_INT64(1, head->first_available);
/* Verify contents of lines */
struct line *l = head->line_start;
TEST_ASSERT_NOT_NULL(l);
TEST_ASSERT_TRUE(l->used >= 3); /* All three are in first control block for small count */
struct cstring line0 = l->starts[0];
struct cstring line1 = l->starts[1];
struct cstring line2 = l->starts[2];
TEST_ASSERT_EQUAL_INT(2, (int)line0.len);
TEST_ASSERT_EQUAL_INT(2, (int)line1.len);
TEST_ASSERT_EQUAL_INT(2, (int)line2.len);
TEST_ASSERT_TRUE(memcmp(line0.str, "a\n", 2) == 0);
TEST_ASSERT_TRUE(memcmp(line1.str, "b\n", 2) == 0);
TEST_ASSERT_TRUE(memcmp(line2.str, "c\n", 2) == 0);
/* Because we didn't exhaust input via remove_line(), have_read_eof may be false */
TEST_ASSERT_EQUAL_INT(0, (int)hold_count);
}
/* Test: a very long line exceeding START_SIZE should force buffer growth and still succeed. */
void test_load_buffer_handles_long_line_larger_than_start_size(void) {
size_t long_len = (size_t)START_SIZE * 2 + 10;
char *buf = (char*)malloc(long_len + 1);
TEST_ASSERT_NOT_NULL(buf);
memset(buf, 'x', long_len);
buf[long_len - 1] = 'y'; /* just to vary */
buf[long_len] = '\n';
set_stdin_from_data(buf, long_len + 1);
bool got = load_buffer();
TEST_ASSERT_TRUE(got);
TEST_ASSERT_NOT_NULL(head);
TEST_ASSERT_EQUAL_INT(1, head->num_lines);
struct line *l = head->line_start;
TEST_ASSERT_NOT_NULL(l);
TEST_ASSERT_TRUE(l->used >= 1);
struct cstring ln = l->starts[0];
TEST_ASSERT_EQUAL_INT((int)(long_len + 1), (int)ln.len);
TEST_ASSERT_EQUAL_CHAR('x', ln.str[0]);
TEST_ASSERT_EQUAL_CHAR('y', ln.str[long_len - 1]);
TEST_ASSERT_EQUAL_CHAR('\n', ln.str[long_len]);
TEST_ASSERT_EQUAL_INT(0, (int)hold_count);
free(buf);
}
/* Test: input with incomplete last line should leave remainder in hold_area after first call,
and a second call should create a new buffer holding that final partial line. */
void test_load_buffer_incomplete_last_line_requires_second_call(void) {
const char *data = "foo\nbar";
set_stdin_from_data(data, strlen(data));
bool got1 = load_buffer();
TEST_ASSERT_TRUE(got1);
TEST_ASSERT_NOT_NULL(head);
TEST_ASSERT_EQUAL_INT(1, head->num_lines);
/* After first call, the trailing 'bar' should be in hold_area with hold_count == 3 */
TEST_ASSERT_EQUAL_INT(3, (int)hold_count);
TEST_ASSERT_NOT_NULL(hold_area);
TEST_ASSERT_TRUE(memcmp(hold_area, "bar", 3) == 0);
TEST_ASSERT_FALSE(have_read_eof); /* We haven't seen EOF in the first successful read */
/* Call load_buffer again to process the held partial line (now at EOF) */
bool got2 = load_buffer();
TEST_ASSERT_TRUE(got2);
/* Now head should have the first buffer, and head->next the second buffer with the partial line */
TEST_ASSERT_NOT_NULL(head->next);
struct buffer_record *second = head->next;
TEST_ASSERT_EQUAL_INT(1, second->num_lines);
struct line *l2 = second->line_start;
TEST_ASSERT_NOT_NULL(l2);
struct cstring last = l2->starts[0];
TEST_ASSERT_EQUAL_INT(3, (int)last.len);
TEST_ASSERT_TRUE(memcmp(last.str, "bar", 3) == 0);
TEST_ASSERT_TRUE(have_read_eof);
TEST_ASSERT_EQUAL_INT(0, (int)hold_count);
}
/* Test: single incomplete line only (no newline at all) should still produce one line in a single call. */
void test_load_buffer_single_incomplete_line_only(void) {
const char *data = "xyz";
set_stdin_from_data(data, strlen(data));
bool got = load_buffer();
TEST_ASSERT_TRUE(got);
TEST_ASSERT_NOT_NULL(head);
TEST_ASSERT_EQUAL_INT(1, head->num_lines);
struct line *l = head->line_start;
TEST_ASSERT_NOT_NULL(l);
struct cstring ln = l->starts[0];
TEST_ASSERT_EQUAL_INT(3, (int)ln.len);
TEST_ASSERT_TRUE(memcmp(ln.str, "xyz", 3) == 0);
/* After success, no leftover hold_area */
TEST_ASSERT_EQUAL_INT(0, (int)hold_count);
}
/* Unity main */
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_load_buffer_empty_input_returns_false);
RUN_TEST(test_load_buffer_reads_multiple_short_lines);
RUN_TEST(test_load_buffer_handles_long_line_larger_than_start_size);
RUN_TEST(test_load_buffer_incomplete_last_line_requires_second_call);
RUN_TEST(test_load_buffer_single_incomplete_line_only);
return UNITY_END();
}