coreutils / tests /join /tests_for_prfields.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 <errno.h>
/* We rely on the program's static globals and the target function being
visible here due to textual inclusion into the same translation unit. */
/* Helper to construct a line with given fields. Allocates memory for the
fields and their contents. */
static void build_line(struct line *ln, const char *const *vals, const idx_t *lens, idx_t n)
{
memset(ln, 0, sizeof(*ln));
ln->nfields = n;
ln->nfields_allocated = n;
if (n == 0) {
ln->fields = NULL;
return;
}
ln->fields = (struct field *)malloc(sizeof(struct field) * n);
for (idx_t i = 0; i < n; ++i) {
idx_t L = lens ? lens[i] : (idx_t)strlen(vals[i]);
char *buf = (char *)malloc((size_t)L);
if (L > 0) {
memcpy(buf, vals[i], (size_t)L);
}
ln->fields[i].beg = buf;
ln->fields[i].len = L;
}
}
/* Helper to free memory allocated by build_line. */
static void free_line(struct line *ln)
{
if (!ln) return;
for (idx_t i = 0; i < ln->nfields; ++i) {
free(ln->fields[i].beg);
ln->fields[i].beg = NULL;
ln->fields[i].len = 0;
}
free(ln->fields);
ln->fields = NULL;
ln->nfields = 0;
ln->nfields_allocated = 0;
}
/* Capture stdout output produced by calling prfields(line, join_field, autocount).
Returns a malloc'd NUL-terminated string on success, or NULL on failure. */
static char *capture_prfields_output(struct line const *line, idx_t join_field, idx_t autocount)
{
char *result = NULL;
FILE *tmp = tmpfile();
if (!tmp) {
return NULL;
}
int out_fd = fileno(stdout);
int saved_fd = dup(out_fd);
if (saved_fd < 0) {
fclose(tmp);
return NULL;
}
int tmp_fd = fileno(tmp);
/* Redirect stdout to tmp. */
fflush(stdout);
if (dup2(tmp_fd, out_fd) < 0) {
close(saved_fd);
fclose(tmp);
return NULL;
}
/* Call the target function while stdout is redirected. */
prfields(line, join_field, autocount);
/* Flush and read back the captured output. */
fflush(stdout);
long size = 0;
if (fseek(tmp, 0, SEEK_END) == 0) {
long endpos = ftell(tmp);
if (endpos >= 0) {
size = endpos;
(void)fseek(tmp, 0, SEEK_SET);
}
}
if (size < 0) size = 0;
result = (char *)malloc((size_t)size + 1);
if (!result) {
/* Restore stdout before returning. */
dup2(saved_fd, out_fd);
close(saved_fd);
fclose(tmp);
return NULL;
}
if (size > 0) {
size_t rd = fread(result, 1, (size_t)size, tmp);
result[rd] = '\0';
if (rd != (size_t)size) {
/* Restore stdout and report failure. */
dup2(saved_fd, out_fd);
close(saved_fd);
fclose(tmp);
free(result);
return NULL;
}
} else {
result[0] = '\0';
}
/* Restore stdout. */
dup2(saved_fd, out_fd);
close(saved_fd);
fclose(tmp);
return result;
}
void setUp(void) {
/* Default settings before each test. */
autoformat = false;
output_separator = " ";
output_seplen = 1;
empty_filler = NULL;
}
void tearDown(void) {
/* Nothing persistent to clean up across tests. */
}
/* Test: basic behavior, skip the join field in the middle. */
static void test_prfields_basic_skip_join_middle(void)
{
const char *vals[] = { "A", "B", "C" };
struct line ln;
build_line(&ln, vals, NULL, 3);
output_separator = ",";
output_seplen = (idx_t)strlen(output_separator);
autoformat = false;
empty_filler = NULL;
char *out = capture_prfields_output(&ln, (idx_t)1, (idx_t)0);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(",A,C", out);
free(out);
free_line(&ln);
}
/* Test: join_field out of range (greater than number of fields) -> no skip, all printed. */
static void test_prfields_join_field_out_of_range_high(void)
{
const char *vals[] = { "A", "B", "C" };
struct line ln;
build_line(&ln, vals, NULL, 3);
output_separator = ",";
output_seplen = 1;
autoformat = false;
char *out = capture_prfields_output(&ln, (idx_t)5, (idx_t)0);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(",A,B,C", out);
free(out);
free_line(&ln);
}
/* Test: no fields present -> no output. */
static void test_prfields_no_fields_no_output(void)
{
struct line ln;
build_line(&ln, NULL, NULL, 0);
output_separator = ",";
output_seplen = 1;
autoformat = false;
char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)0);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("", out);
free(out);
free_line(&ln);
}
/* Test: empty field replaced by empty_filler when printed (non-join field). */
static void test_prfields_empty_field_with_empty_filler(void)
{
const char *vals[] = { "A", "", "C" };
idx_t lens[] = { 1, 0, 1 }; /* explicit lengths to create an empty field */
struct line ln;
build_line(&ln, vals, lens, 3);
output_separator = ",";
output_seplen = 1;
autoformat = false;
empty_filler = "<E>";
/* Use join_field = 0 so fields 1 and 2 are printed. */
char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)0);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(",<E>,C", out);
free(out);
free_line(&ln);
}
/* Test: autoformat extends beyond existing fields and uses empty_filler for missing ones. */
static void test_prfields_autoformat_extends_and_fills(void)
{
const char *vals[] = { "A", "B" };
struct line ln;
build_line(&ln, vals, NULL, 2);
output_separator = ",";
output_seplen = 1;
autoformat = true;
empty_filler = "<E>";
/* join_field = 0 -> print indices 1..3; ln has only index 1, so 2 and 3 use filler */
char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)4);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(",B,<E>,<E>", out);
free(out);
free_line(&ln);
}
/* Test: autoformat limits output to autocount even if there are more fields; join field skipped. */
static void test_prfields_autoformat_limits(void)
{
const char *vals[] = { "A", "B", "C", "D" };
struct line ln;
build_line(&ln, vals, NULL, 4);
output_separator = ",";
output_seplen = 1;
autoformat = true;
empty_filler = NULL;
/* nfields considered = 3 (autocount). join_field = 2 => print 0 and 1 only. */
char *out = capture_prfields_output(&ln, (idx_t)2, (idx_t)3);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(",A,B", out);
free(out);
free_line(&ln);
}
/* Test: custom multi-character separator honors output_seplen. */
static void test_prfields_custom_separator_multichar(void)
{
const char *vals[] = { "X", "Y" };
struct line ln;
build_line(&ln, vals, NULL, 2);
output_separator = "::";
output_seplen = (idx_t)strlen(output_separator);
autoformat = false;
/* join_field = 1 => print only field 0 */
char *out = capture_prfields_output(&ln, (idx_t)1, (idx_t)0);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("::X", out);
free(out);
free_line(&ln);
}
/* Test: autoformat with missing fields and no empty_filler prints only separators for those fields. */
static void test_prfields_autoformat_no_empty_filler_only_separators(void)
{
const char *vals[] = { "A" };
struct line ln;
build_line(&ln, vals, NULL, 1);
output_separator = ",";
output_seplen = 1;
autoformat = true;
empty_filler = NULL;
/* join_field = 0; autocount=3 -> attempt to print fields 1 and 2 (both missing):
separators appear, but no filler text. */
char *out = capture_prfields_output(&ln, (idx_t)0, (idx_t)3);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(",,", out);
free(out);
free_line(&ln);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_prfields_basic_skip_join_middle);
RUN_TEST(test_prfields_join_field_out_of_range_high);
RUN_TEST(test_prfields_no_fields_no_output);
RUN_TEST(test_prfields_empty_field_with_empty_filler);
RUN_TEST(test_prfields_autoformat_extends_and_fills);
RUN_TEST(test_prfields_autoformat_limits);
RUN_TEST(test_prfields_custom_separator_multichar);
RUN_TEST(test_prfields_autoformat_no_empty_filler_only_separators);
return UNITY_END();
}