coreutils / tests /csplit /tests_for_split_file.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 <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
/* External/internal symbols from the program are visible here,
since this file is included into the same translation unit. */
/* Forward declarations to silence potential warnings (they are defined above). */
static void split_file (void);
/* Test helpers state */
static int start_files_created = 0;
static char test_prefix[64] = {0};
/* Helper: create temp input file with given content and return strdup'ed path */
static char* create_temp_input_file(const char* content) {
char templ[] = "/tmp/csplit_test_input_XXXXXX";
int fd = mkstemp(templ);
if (fd < 0) {
perror("mkstemp");
return NULL;
}
size_t len = strlen(content);
if (len > 0) {
ssize_t w = write(fd, content, len);
if (w < 0 || (size_t)w != len) {
close(fd);
unlink(templ);
perror("write temp input");
return NULL;
}
}
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
close(fd);
unlink(templ);
perror("lseek");
return NULL;
}
close(fd);
return strdup(templ);
}
/* Helper: read entire file into newly malloc'd buffer (null-terminated).
Returns NULL on error. */
static char* read_file_contents(const char* path) {
struct stat st;
if (stat(path, &st) != 0)
return NULL;
size_t sz = (size_t)st.st_size;
FILE* f = fopen(path, "rb");
if (!f)
return NULL;
char* buf = (char*)malloc(sz + 1);
if (!buf) {
fclose(f);
return NULL;
}
size_t r = fread(buf, 1, sz, f);
fclose(f);
if (r != sz) {
free(buf);
return NULL;
}
buf[sz] = '\0';
return buf;
}
/* Helper: build filename for file index i using program's make_filename.
Returns pointer to internal filename_space. */
static const char* outfile_name_for_index(int i) {
/* make_filename writes into filename_space and returns it */
return make_filename(i);
}
/* Reset input buffer state and related globals between tests */
static void reset_input_state(void) {
/* Free any hold area from previous run */
if (hold_area) {
free(hold_area);
hold_area = NULL;
}
hold_count = 0;
have_read_eof = false;
head = NULL;
last_line_number = 0;
current_line = 0;
}
/* Prepare environment before each test call to split_file */
static void prepare_environment(void) {
/* Ensure signals set is empty for critical sections */
sigemptyset(&caught_signals);
/* Avoid printing byte counts to stdout */
suppress_count = true;
/* Do not auto-remove files on error or elide empty files in tests */
remove_files = false;
elide_empty_files = false;
suppress_matched = false;
/* Use numeric suffix with default digits */
digits = 2;
suffix = NULL;
/* Ensure filename_space exists and is large enough */
if (!filename_space) {
filename_space = (char*)malloc(1024);
}
/* Use a stable test prefix unique(ish) to this process */
if (test_prefix[0] == '\0') {
snprintf(test_prefix, sizeof(test_prefix), "utcs_%ld_", (long)getpid());
}
prefix = test_prefix;
/* Reset input stream state */
reset_input_state();
/* Start tracking created files from current state */
start_files_created = files_created;
}
/* Cleanup files created during the test and restore files_created */
static void cleanup_created_files(void) {
int end = files_created;
for (int i = start_files_created; i < end; i++) {
const char* name = outfile_name_for_index(i);
unlink(name);
}
files_created = start_files_created;
}
/* Unity required hooks */
void setUp(void) {
prepare_environment();
}
void tearDown(void) {
cleanup_created_files();
}
/* Test 1: Numeric line-count split: single control "3"
Input: a\nb\nc\nd\ne\n
Expect: two files:
00: "a\nb\n"
01: "c\nd\ne\n"
*/
void test_split_file_linecount_basic(void) {
const char* input = "a\nb\nc\nd\ne\n";
char* path = create_temp_input_file(input);
TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp input");
set_input_file(path);
/* Build controls: one numeric control lines_required=3 */
control_used = 0;
struct control* p = new_control_record();
p->lines_required = 3; /* split before line 3 */
p->argnum = 1; /* arbitrary */
/* Execute */
split_file();
/* Verify two files created */
int created = files_created - start_files_created;
TEST_ASSERT_EQUAL_INT_MESSAGE(2, created, "Expected exactly 2 output files");
const char* f0 = outfile_name_for_index(start_files_created + 0);
const char* f1 = outfile_name_for_index(start_files_created + 1);
char* c0 = read_file_contents(f0);
char* c1 = read_file_contents(f1);
TEST_ASSERT_NOT_NULL_MESSAGE(c0, "Failed to read first output file");
TEST_ASSERT_NOT_NULL_MESSAGE(c1, "Failed to read second output file");
TEST_ASSERT_EQUAL_STRING("a\nb\n", c0);
TEST_ASSERT_EQUAL_STRING("c\nd\ne\n", c1);
free(c0);
free(c1);
unlink(path);
free(path);
}
/* Test 2: Regexp basic split: control '/^c$/'
Input: a\nb\nc\nd\ne\n
Expect: two files:
00: "a\nb\n"
01: "c\nd\ne\n"
*/
void test_split_file_regexp_basic(void) {
const char* input = "a\nb\nc\nd\ne\n";
char* path = create_temp_input_file(input);
TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp input");
set_input_file(path);
/* Build controls: one regexp control '/^c$/' */
control_used = 0;
struct control* p = extract_regexp(1, false, "/^c$/");
(void)p; /* silence unused warning in case */
/* Execute */
split_file();
/* Verify two files created */
int created = files_created - start_files_created;
TEST_ASSERT_EQUAL_INT_MESSAGE(2, created, "Expected exactly 2 output files");
const char* f0 = outfile_name_for_index(start_files_created + 0);
const char* f1 = outfile_name_for_index(start_files_created + 1);
char* c0 = read_file_contents(f0);
char* c1 = read_file_contents(f1);
TEST_ASSERT_NOT_NULL_MESSAGE(c0, "Failed to read first output file");
TEST_ASSERT_NOT_NULL_MESSAGE(c1, "Failed to read second output file");
TEST_ASSERT_EQUAL_STRING("a\nb\n", c0);
TEST_ASSERT_EQUAL_STRING("c\nd\ne\n", c1);
free(c0);
free(c1);
unlink(path);
free(path);
}
/* Test 3: Regexp with suppress_matched: '/^c$/'
suppress_matched = true
Input: a\nb\nc\nd\ne\n
Expect: two files:
00: "a\nb\n"
01: "d\ne\n"
(matching line 'c' is suppressed)
*/
void test_split_file_regexp_suppress_matched(void) {
const char* input = "a\nb\nc\nd\ne\n";
char* path = create_temp_input_file(input);
TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp input");
set_input_file(path);
/* Build controls: one regexp control '/^c$/' */
control_used = 0;
struct control* p = extract_regexp(1, false, "/^c$/");
(void)p;
/* Suppress matched lines */
suppress_matched = true;
/* Execute */
split_file();
/* Verify two files created */
int created = files_created - start_files_created;
TEST_ASSERT_EQUAL_INT_MESSAGE(2, created, "Expected exactly 2 output files");
const char* f0 = outfile_name_for_index(start_files_created + 0);
const char* f1 = outfile_name_for_index(start_files_created + 1);
char* c0 = read_file_contents(f0);
char* c1 = read_file_contents(f1);
TEST_ASSERT_NOT_NULL_MESSAGE(c0, "Failed to read first output file");
TEST_ASSERT_NOT_NULL_MESSAGE(c1, "Failed to read second output file");
TEST_ASSERT_EQUAL_STRING("a\nb\n", c0);
TEST_ASSERT_EQUAL_STRING("d\ne\n", c1);
free(c0);
free(c1);
unlink(path);
free(path);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_split_file_linecount_basic);
RUN_TEST(test_split_file_regexp_basic);
RUN_TEST(test_split_file_regexp_suppress_matched);
return UNITY_END();
}