coreutils / tests /cut /tests_for_usage.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>
/* Unity fixtures */
void setUp(void) {
/* Setup code here, or leave empty */
}
void tearDown(void) {
/* Cleanup code here, or leave empty */
}
/* Helper: read all data from an fd into a NUL-terminated malloc'd buffer.
Returns a malloc'd string (possibly empty), or NULL on error. */
static char* read_all_from_fd(int fd) {
size_t cap = 4096;
size_t len = 0;
char *buf = (char*)malloc(cap + 1);
if (!buf) return NULL;
for (;;) {
if (len == cap) {
size_t new_cap = cap * 2;
char *new_buf = (char*)realloc(buf, new_cap + 1);
if (!new_buf) {
free(buf);
return NULL;
}
buf = new_buf;
cap = new_cap;
}
ssize_t n = read(fd, buf + len, cap - len);
if (n < 0) {
if (errno == EINTR) continue;
free(buf);
return NULL;
}
if (n == 0) break;
len += (size_t)n;
}
buf[len] = '\0';
return buf;
}
/* Helper: run usage(status) in a child process with stdout/stderr captured.
On return, *out_str and *err_str are malloc'd strings (never NULL, but may be empty).
Returns the child's exit status (0-255), or -1 on error/unexpected termination. */
static int run_usage_and_capture(int status, char **out_str, char **err_str) {
int out_pipe[2];
int err_pipe[2];
if (pipe(out_pipe) < 0) return -1;
if (pipe(err_pipe) < 0) {
close(out_pipe[0]); close(out_pipe[1]);
return -1;
}
fflush(stdout);
fflush(stderr);
pid_t pid = fork();
if (pid < 0) {
close(out_pipe[0]); close(out_pipe[1]);
close(err_pipe[0]); close(err_pipe[1]);
return -1;
}
if (pid == 0) {
/* Child: redirect stdout/stderr, set locale/program_name, and call usage. */
/* Ensure C locale for stable messages. */
setenv("LC_ALL", "C", 1);
/* Ensure program_name is set appropriately for usage output. */
set_program_name("cut");
/* Redirect stdout and stderr to pipes. */
close(out_pipe[0]); /* close read end */
close(err_pipe[0]); /* close read end */
if (dup2(out_pipe[1], STDOUT_FILENO) < 0) _exit(127);
if (dup2(err_pipe[1], STDERR_FILENO) < 0) _exit(127);
/* Close the now-duplicated original fds. */
close(out_pipe[1]);
close(err_pipe[1]);
/* Call the target function; this will call exit(status). */
usage(status);
/* Should not reach here; be defensive. */
_exit(255);
}
/* Parent: close write ends, read all output, wait for child. */
close(out_pipe[1]);
close(err_pipe[1]);
char *out_buf = read_all_from_fd(out_pipe[0]);
char *err_buf = read_all_from_fd(err_pipe[0]);
close(out_pipe[0]);
close(err_pipe[0]);
if (!out_buf) out_buf = strdup("");
if (!err_buf) err_buf = strdup("");
int wstatus = 0;
int exit_status = -1;
if (waitpid(pid, &wstatus, 0) >= 0) {
if (WIFEXITED(wstatus)) {
exit_status = WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
/* Encode signal-based termination in a conventional way. */
exit_status = 128 + WTERMSIG(wstatus);
}
}
*out_str = out_buf;
*err_str = err_buf;
return exit_status;
}
/* Simple substring check */
static int contains_substr(const char *haystack, const char *needle) {
if (!haystack || !needle) return 0;
return strstr(haystack, needle) != NULL;
}
/* Tests */
static void test_usage_success_outputs_help_and_exits_0(void) {
char *out = NULL;
char *err = NULL;
int es = run_usage_and_capture(EXIT_SUCCESS, &out, &err);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, es, "usage should exit with status 0 on success");
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_NOT_NULL(err);
/* stderr should be empty on success */
TEST_ASSERT_TRUE_MESSAGE(strlen(err) == 0, "stderr should be empty for successful usage");
/* stdout should contain key help text components */
TEST_ASSERT_TRUE_MESSAGE(contains_substr(out, "Usage:"), "Help text should contain 'Usage:'");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(out, "cut"), "Help text should mention program name 'cut'");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(out, "-b, --bytes=LIST"), "Help should mention bytes option");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(out, "-f, --fields=LIST"), "Help should mention fields option");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(out, "--complement"), "Help should mention --complement");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(out, "--zero-terminated"), "Help should mention --zero-terminated");
free(out);
free(err);
}
static void test_usage_error_prints_try_help_to_stderr_and_exits_status(void) {
char *out = NULL;
char *err = NULL;
int desired_status = 2;
int es = run_usage_and_capture(desired_status, &out, &err);
TEST_ASSERT_EQUAL_INT_MESSAGE(desired_status, es, "usage should exit with the provided nonzero status");
/* stdout should be empty, stderr should contain 'Try', '--help', and program name */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_NOT_NULL(err);
TEST_ASSERT_TRUE_MESSAGE(strlen(out) == 0, "stdout should be empty for error usage");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(err, "Try"), "stderr should contain 'Try' hint");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(err, "--help"), "stderr should suggest --help");
TEST_ASSERT_TRUE_MESSAGE(contains_substr(err, "cut"), "stderr should mention program name 'cut'");
free(out);
free(err);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_usage_success_outputs_help_and_exits_0);
RUN_TEST(test_usage_error_prints_try_help_to_stderr_and_exits_status);
return UNITY_END();
}