coreutils / tests /mktemp /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 <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
/* program_name is provided by coreutils/gnulib */
extern char const *program_name;
void setUp(void) {
/* Ensure predictable locale for help text */
setenv("LC_ALL", "C", 1);
}
void tearDown(void) {
/* nothing */
}
typedef struct {
bool exited;
int exit_status; /* valid if exited == true */
bool signaled;
int term_sig; /* valid if signaled == true */
char *out; /* captured stdout, NUL terminated */
char *err; /* captured stderr, NUL terminated */
} usage_run_result;
/* Read all data from fd into a NUL-terminated buffer. Returns malloc'd string (may be empty). */
static char *read_all_fd(int fd) {
size_t cap = 4096;
size_t len = 0;
char *buf = (char *)malloc(cap);
if (!buf) return NULL;
for (;;) {
if (len + 2048 + 1 > cap) {
size_t new_cap = cap * 2;
char *nb = (char *)realloc(buf, new_cap);
if (!nb) {
free(buf);
return NULL;
}
buf = nb;
cap = new_cap;
}
ssize_t r = read(fd, buf + len, 2048);
if (r > 0) {
len += (size_t)r;
continue;
} else if (r == 0) {
break;
} else {
if (errno == EINTR) continue;
/* On error, stop and return what we have so far */
break;
}
}
buf[len] = '\0';
return buf;
}
/* Helper to run usage(status) in a child, capturing stdout/stderr and exit status. */
static usage_run_result run_usage_with_progname(int status, const char *pname) {
usage_run_result res;
res.exited = false;
res.exit_status = -1;
res.signaled = false;
res.term_sig = 0;
res.out = NULL;
res.err = NULL;
int out_pipe[2];
int err_pipe[2];
if (pipe(out_pipe) < 0) {
TEST_FAIL_MESSAGE("Failed to create stdout pipe");
}
if (pipe(err_pipe) < 0) {
close(out_pipe[0]); close(out_pipe[1]);
TEST_FAIL_MESSAGE("Failed to create stderr pipe");
}
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]);
TEST_FAIL_MESSAGE("fork() failed");
}
if (pid == 0) {
/* Child: redirect stdout/stderr and invoke usage */
/* Set locale to C for predictable messages */
setenv("LC_ALL", "C", 1);
/* Set program_name visible to usage() */
program_name = pname;
/* Redirect stdout and stderr to pipes */
if (dup2(out_pipe[1], STDOUT_FILENO) < 0) _exit(127);
if (dup2(err_pipe[1], STDERR_FILENO) < 0) _exit(127);
/* Close unused fds */
close(out_pipe[0]); close(out_pipe[1]);
close(err_pipe[0]); close(err_pipe[1]);
/* Call the target function; it should exit(status) */
usage(status);
/* Should not return; if it does, exit with a unique code */
_exit(126);
}
/* Parent: close write ends, read from read ends */
close(out_pipe[1]);
close(err_pipe[1]);
/* Read captured outputs */
char *capt_out = read_all_fd(out_pipe[0]);
char *capt_err = read_all_fd(err_pipe[0]);
close(out_pipe[0]);
close(err_pipe[0]);
int wstatus = 0;
pid_t w = waitpid(pid, &wstatus, 0);
if (w < 0) {
if (capt_out) free(capt_out);
if (capt_err) free(capt_err);
TEST_FAIL_MESSAGE("waitpid() failed");
}
if (WIFEXITED(wstatus)) {
res.exited = true;
res.exit_status = WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
res.signaled = true;
res.term_sig = WTERMSIG(wstatus);
}
res.out = capt_out ? capt_out : strdup("");
res.err = capt_err ? capt_err : strdup("");
if (!res.out || !res.err) {
free(res.out);
free(res.err);
TEST_FAIL_MESSAGE("Allocation failure");
}
return res;
}
static void free_usage_run_result(usage_run_result *r) {
if (!r) return;
free(r->out);
free(r->err);
r->out = NULL;
r->err = NULL;
}
/* Simple substring check */
static bool str_contains(const char *haystack, const char *needle) {
if (!haystack || !needle) return false;
return strstr(haystack, needle) != NULL;
}
/* Tests */
void test_usage_success_outputs_help_to_stdout(void) {
usage_run_result r = run_usage_with_progname(EXIT_SUCCESS, "mktemp");
TEST_ASSERT_TRUE_MESSAGE(r.exited, "Child did not exit normally");
TEST_ASSERT_EQUAL_INT_MESSAGE(EXIT_SUCCESS, r.exit_status, "Unexpected exit status for success usage()");
TEST_ASSERT_NOT_NULL(r.out);
TEST_ASSERT_NOT_NULL(r.err);
/* stderr should be empty (or only whitespace); stdout should contain help text */
/* Trim leading/trailing whitespace in stderr for robust check */
bool err_has_nonws = false;
for (const char *p = r.err; *p; ++p) {
if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v')) {
err_has_nonws = true; break;
}
}
TEST_ASSERT_FALSE_MESSAGE(err_has_nonws, "Expected no diagnostics on stderr for successful usage()");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.out, "Usage: mktemp"), "Help should start with 'Usage: mktemp'");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.out, "Create a temporary file or directory"),
"Help should describe purpose");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.out, "-d, --directory"),
"Help should mention --directory option");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.out, "--tmpdir"),
"Help should mention --tmpdir option");
free_usage_run_result(&r);
}
void test_usage_error_try_help_to_stderr(void) {
usage_run_result r = run_usage_with_progname(2, "mktemp");
TEST_ASSERT_TRUE_MESSAGE(r.exited, "Child did not exit normally");
TEST_ASSERT_EQUAL_INT_MESSAGE(2, r.exit_status, "Unexpected exit status for error usage()");
TEST_ASSERT_NOT_NULL(r.out);
TEST_ASSERT_NOT_NULL(r.err);
/* stdout should be empty (or only whitespace); stderr should contain a Try...--help message */
bool out_has_nonws = false;
for (const char *p = r.out; *p; ++p) {
if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v')) {
out_has_nonws = true; break;
}
}
TEST_ASSERT_FALSE_MESSAGE(out_has_nonws, "Expected no help on stdout for error usage()");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.err, "Try 'mktemp"),
"Expected 'Try 'mktemp ...' message on stderr");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.err, "--help"),
"Expected '--help' reference in stderr diagnostic");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.err, "more information"),
"Expected 'more information' phrase in stderr diagnostic");
free_usage_run_result(&r);
}
void test_usage_uses_program_name(void) {
usage_run_result r = run_usage_with_progname(EXIT_SUCCESS, "custom-name");
TEST_ASSERT_TRUE_MESSAGE(r.exited, "Child did not exit normally");
TEST_ASSERT_EQUAL_INT_MESSAGE(EXIT_SUCCESS, r.exit_status, "Unexpected exit status for success usage()");
TEST_ASSERT_TRUE_MESSAGE(str_contains(r.out, "Usage: custom-name"),
"Usage header should reflect custom program_name");
free_usage_run_result(&r);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_usage_success_outputs_help_to_stdout);
RUN_TEST(test_usage_error_try_help_to_stderr);
RUN_TEST(test_usage_uses_program_name);
return UNITY_END();
}