coreutils / tests /mkfifo /tests_for_usage.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
/* Unity required hooks */
void setUp(void) {
/* Force a consistent locale for help text */
setenv("LC_ALL", "C", 1);
setenv("LANGUAGE", "C", 1);
setenv("LANG", "C", 1);
}
void tearDown(void) {
/* nothing */
}
/* Helper to read all data from fd into a malloc'd buffer; NUL-terminate. */
static int read_all_fd(int fd, char **out_buf, size_t *out_len) {
size_t cap = 1024;
size_t len = 0;
char *buf = (char *)malloc(cap + 1);
if (!buf) return -1;
for (;;) {
if (len == cap) {
size_t ncap = cap * 2;
char *nbuf = (char *)realloc(buf, ncap + 1);
if (!nbuf) {
free(buf);
return -1;
}
buf = nbuf;
cap = ncap;
}
ssize_t n = read(fd, buf + len, cap - len);
if (n < 0) {
if (errno == EINTR) continue;
free(buf);
return -1;
}
if (n == 0) break;
len += (size_t)n;
}
buf[len] = '\0';
*out_buf = buf;
if (out_len) *out_len = len;
return 0;
}
/* Run usage(status_arg) in a child, capturing stdout, stderr, and exit status.
Returns NULL on success, or a malloc'd error message string on failure. */
static char *run_usage_capture(int status_arg,
char **out_stdout, size_t *out_stdout_len,
char **out_stderr, size_t *out_stderr_len,
int *out_exit_code) {
int out_pipe[2] = {-1,-1};
int err_pipe[2] = {-1,-1};
if (pipe(out_pipe) < 0) {
char *msg;
asprintf(&msg, "pipe(out) failed: %s", strerror(errno));
return msg;
}
if (pipe(err_pipe) < 0) {
close(out_pipe[0]); close(out_pipe[1]);
char *msg;
asprintf(&msg, "pipe(err) failed: %s", strerror(errno));
return msg;
}
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]);
char *msg;
asprintf(&msg, "fork failed: %s", strerror(errno));
return msg;
}
if (pid == 0) {
/* Child: redirect stdout/stderr */
/* Ensure locale is C for predictable output */
setenv("LC_ALL", "C", 1);
setenv("LANGUAGE", "C", 1);
setenv("LANG", "C", 1);
/* Connect pipes */
if (dup2(out_pipe[1], STDOUT_FILENO) < 0) _exit(255);
if (dup2(err_pipe[1], STDERR_FILENO) < 0) _exit(255);
/* Close fds not needed in child */
close(out_pipe[0]); close(out_pipe[1]);
close(err_pipe[0]); close(err_pipe[1]);
/* Call the function under test; it will exit(status_arg). */
usage(status_arg);
/* Should not reach here; ensure child terminates */
_exit(254);
}
/* Parent: close write ends, read outputs */
close(out_pipe[1]);
close(err_pipe[1]);
char *captured_out = NULL;
char *captured_err = NULL;
size_t len_out = 0, len_err = 0;
int rc_out = read_all_fd(out_pipe[0], &captured_out, &len_out);
int rc_err = read_all_fd(err_pipe[0], &captured_err, &len_err);
close(out_pipe[0]);
close(err_pipe[0]);
int wstatus = 0;
pid_t w = waitpid(pid, &wstatus, 0);
if (w < 0) {
free(captured_out);
free(captured_err);
char *msg;
asprintf(&msg, "waitpid failed: %s", strerror(errno));
return msg;
}
if (rc_out != 0 || rc_err != 0) {
free(captured_out);
free(captured_err);
char *msg;
asprintf(&msg, "read_all_fd failed (out=%d, err=%d)", rc_out, rc_err);
return msg;
}
int exit_code = -1;
if (WIFEXITED(wstatus)) {
exit_code = WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
free(captured_out);
free(captured_err);
char *msg;
asprintf(&msg, "child terminated by signal %d", WTERMSIG(wstatus));
return msg;
} else {
free(captured_out);
free(captured_err);
char *msg;
asprintf(&msg, "child did not exit normally");
return msg;
}
if (out_stdout) *out_stdout = captured_out; else free(captured_out);
if (out_stderr) *out_stderr = captured_err; else free(captured_err);
if (out_stdout_len) *out_stdout_len = len_out;
if (out_stderr_len) *out_stderr_len = len_err;
if (out_exit_code) *out_exit_code = exit_code;
return NULL;
}
static int str_contains(const char *haystack, const char *needle) {
if (!haystack || !needle) return 0;
return strstr(haystack, needle) != NULL;
}
/* Tests */
void test_usage_success_outputs_help(void) {
char *out = NULL, *err = NULL;
size_t out_len = 0, err_len = 0;
int exit_code = -1;
char *msg = run_usage_capture(0, &out, &out_len, &err, &err_len, &exit_code);
TEST_ASSERT_NULL_MESSAGE(msg, msg ? msg : "");
/* Exit status must be 0 */
TEST_ASSERT_EQUAL_INT(0, exit_code);
/* Full help should go to stdout, not stderr */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_TRUE(out_len > 0);
TEST_ASSERT_NOT_NULL(err);
TEST_ASSERT_EQUAL_UINT64(0, (unsigned long long)err_len);
/* Check for key substrings in help text */
TEST_ASSERT_TRUE_MESSAGE(str_contains(out, "Usage:"), "Help text missing 'Usage:'");
TEST_ASSERT_TRUE_MESSAGE(str_contains(out, "Create named pipes"), "Help text missing description line");
TEST_ASSERT_TRUE_MESSAGE(str_contains(out, "-m, --mode=MODE"), "Help text missing mode option");
TEST_ASSERT_TRUE_MESSAGE(str_contains(out, "--context"), "Help text missing context option");
TEST_ASSERT_TRUE_MESSAGE(str_contains(out, "-Z"), "Help text missing -Z option");
free(out);
free(err);
}
void test_usage_nonzero_shows_try_help_and_exit_status_1(void) {
char *out = NULL, *err = NULL;
size_t out_len = 0, err_len = 0;
int exit_code = -1;
char *msg = run_usage_capture(1, &out, &out_len, &err, &err_len, &exit_code);
TEST_ASSERT_NULL_MESSAGE(msg, msg ? msg : "");
TEST_ASSERT_EQUAL_INT(1, exit_code);
/* Expect message on stderr, none on stdout */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_UINT64(0, (unsigned long long)out_len);
TEST_ASSERT_NOT_NULL(err);
TEST_ASSERT_TRUE(err_len > 0);
TEST_ASSERT_TRUE_MESSAGE(str_contains(err, "Try"), "stderr missing 'Try' message");
TEST_ASSERT_TRUE_MESSAGE(str_contains(err, "--help"), "stderr missing '--help' hint");
free(out);
free(err);
}
void test_usage_nonzero_propagates_exit_status_77(void) {
char *out = NULL, *err = NULL;
size_t out_len = 0, err_len = 0;
int exit_code = -1;
char *msg = run_usage_capture(77, &out, &out_len, &err, &err_len, &exit_code);
TEST_ASSERT_NULL_MESSAGE(msg, msg ? msg : "");
TEST_ASSERT_EQUAL_INT(77, exit_code);
/* Same pattern: message on stderr, stdout empty */
TEST_ASSERT_EQUAL_UINT64(0, (unsigned long long)out_len);
TEST_ASSERT_TRUE(err_len > 0);
TEST_ASSERT_TRUE_MESSAGE(str_contains(err, "--help"), "stderr missing '--help' hint");
free(out);
free(err);
}
/* Unity main */
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_usage_success_outputs_help);
RUN_TEST(test_usage_nonzero_shows_try_help_and_exit_status_1);
RUN_TEST(test_usage_nonzero_propagates_exit_status_77);
return UNITY_END();
}