coreutils / tests /factor /tests_for_usage.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 <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <locale.h>
/* The program includes system.h before including this test, so program_name
should already be declared. We only reference it here. */
static char *read_all_fd(int fd, size_t *out_len)
{
size_t cap = 4096;
size_t len = 0;
char *buf = (char *)malloc(cap);
if (!buf) return NULL;
for (;;) {
if (len == cap) {
size_t ncap = cap * 2;
char *nbuf = (char *)realloc(buf, ncap);
if (!nbuf) { free(buf); return NULL; }
buf = nbuf;
cap = ncap;
}
ssize_t n = read(fd, buf + len, cap - len);
if (n > 0) {
len += (size_t)n;
continue;
} else if (n == 0) {
break;
} else {
if (errno == EINTR) continue;
free(buf);
return NULL;
}
}
/* NUL-terminate for convenience */
if (len == cap) {
char *nbuf = (char *)realloc(buf, cap + 1);
if (!nbuf) { free(buf); return NULL; }
buf = nbuf;
}
buf[len] = '\0';
if (out_len) *out_len = len;
return buf;
}
/* Helper to run usage(status) in a child, capturing stdout/stderr and exitcode. */
static int capture_usage(int status,
char **outp, size_t *outlenp,
char **errp, size_t *errlenp,
int *exitcodep)
{
int pout[2];
int perr[2];
if (pipe(pout) == -1) return -1;
if (pipe(perr) == -1) { close(pout[0]); close(pout[1]); return -1; }
fflush(stdout);
fflush(stderr);
pid_t pid = fork();
if (pid == -1) {
close(pout[0]); close(pout[1]);
close(perr[0]); close(perr[1]);
return -1;
}
if (pid == 0) {
/* Child: redirect stdout/stderr and call usage */
/* Ensure locale is predictable so gettext returns English strings. */
setlocale(LC_ALL, "C");
/* Set program_name to expected value for messages. */
extern char const *program_name;
program_name = "factor";
/* Redirect */
close(pout[0]);
close(perr[0]);
if (dup2(pout[1], STDOUT_FILENO) == -1) _exit(127);
if (dup2(perr[1], STDERR_FILENO) == -1) _exit(127);
close(pout[1]);
close(perr[1]);
/* Call function under test; it will exit(status). */
usage(status);
/* Should not reach here, but ensure child exits. */
_exit(127);
}
/* Parent */
close(pout[1]);
close(perr[1]);
char *out = read_all_fd(pout[0], outlenp);
char *err = read_all_fd(perr[0], errlenp);
close(pout[0]);
close(perr[0]);
int wstatus = 0;
if (waitpid(pid, &wstatus, 0) == -1) {
free(out); free(err);
return -1;
}
int code = 0;
if (WIFEXITED(wstatus)) code = WEXITSTATUS(wstatus);
else if (WIFSIGNALED(wstatus)) code = 128 + WTERMSIG(wstatus);
else code = -1;
if (exitcodep) *exitcodep = code;
if (outp) *outp = out; else free(out);
if (errp) *errp = err; else free(err);
return 0;
}
void setUp(void) {
/* nothing */
}
void tearDown(void) {
/* nothing */
}
static void assert_contains(const char *haystack, const char *needle)
{
TEST_ASSERT_NOT_NULL_MESSAGE(haystack, "haystack is NULL");
TEST_ASSERT_NOT_NULL_MESSAGE(needle, "needle is NULL");
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(haystack, needle), "Expected substring not found");
}
void test_usage_success_outputs_usage_and_exits_zero(void)
{
char *out = NULL, *err = NULL;
size_t outlen = 0, errlen = 0;
int exitcode = -1;
int rc = capture_usage(EXIT_SUCCESS, &out, &outlen, &err, &errlen, &exitcode);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Validate exit code */
TEST_ASSERT_EQUAL_INT(EXIT_SUCCESS, exitcode);
/* Should write to stdout, not stderr */
TEST_ASSERT(out != NULL);
TEST_ASSERT(outlen > 0);
TEST_ASSERT(err != NULL);
TEST_ASSERT_EQUAL_UINT64(0, (unsigned long long)errlen);
/* Check for key substrings */
assert_contains(out, "Usage: factor ");
assert_contains(out, "[OPTION]");
assert_contains(out, "-h, --exponents");
free(out);
free(err);
}
void test_usage_nonzero_writes_try_help_to_stderr_and_exits_status(void)
{
char *out = NULL, *err = NULL;
size_t outlen = 0, errlen = 0;
int exitcode = -1;
int rc = capture_usage(EXIT_FAILURE, &out, &outlen, &err, &errlen, &exitcode);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Exit code should match provided status */
TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, exitcode);
/* Should write to stderr, not stdout */
TEST_ASSERT(out != NULL);
TEST_ASSERT_EQUAL_UINT64(0, (unsigned long long)outlen);
TEST_ASSERT(err != NULL);
TEST_ASSERT(errlen > 0);
/* Check try-help style content contains program name and --help */
assert_contains(err, "factor");
assert_contains(err, "--help");
free(out);
free(err);
}
void test_usage_propagates_arbitrary_nonzero_exit_code(void)
{
char *out = NULL, *err = NULL;
size_t outlen = 0, errlen = 0;
int exitcode = -1;
int status = 42; /* within 8-bit exit code range */
int rc = capture_usage(status, &out, &outlen, &err, &errlen, &exitcode);
TEST_ASSERT_EQUAL_INT(0, rc);
/* Exit code should be exactly the provided status */
TEST_ASSERT_EQUAL_INT(status, exitcode);
/* Nonzero status: expect message on stderr */
TEST_ASSERT(errlen > 0);
free(out);
free(err);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_usage_success_outputs_usage_and_exits_zero);
RUN_TEST(test_usage_nonzero_writes_try_help_to_stderr_and_exits_status);
RUN_TEST(test_usage_propagates_arbitrary_nonzero_exit_code);
return UNITY_END();
}