coreutils / tests /join /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 <fcntl.h>
#include <errno.h>
#include <locale.h>
/* Helper: read all data from fd into a newly malloc'd, NUL-terminated buffer.
Returns buffer (caller must free). On error, returns NULL. */
static char* read_all_from_fd(int fd)
{
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 rd = read(fd, buf + len, cap - len);
if (rd > 0)
{
len += (size_t)rd;
continue;
}
else if (rd == 0)
{
break;
}
else
{
if (errno == EINTR) continue;
free(buf);
return NULL;
}
}
/* Ensure NUL-terminated */
if (len == cap)
{
char *nbuf = (char*)realloc(buf, cap + 1);
if (!nbuf) { free(buf); return NULL; }
buf = nbuf;
}
buf[len] = '\0';
return buf;
}
/* Run usage(status) in a child, capture stdout/stderr, collect exit code.
Returns 0 on success (able to run and capture), -1 on error (test infra error).
On success, *out and *err are malloc'd strings (never NULL; empty string if no output).
Caller must free(*out) and free(*err). */
static int run_usage_and_capture(int status, char **out, char **err, int *exit_code)
{
int out_pipe[2] = {-1, -1};
int err_pipe[2] = {-1, -1};
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;
}
else if (pid == 0)
{
/* Child: redirect stdout/stderr to pipes and call usage(status) */
/* Close read ends */
close(out_pipe[0]);
close(err_pipe[0]);
/* Redirect */
if (dup2(out_pipe[1], STDOUT_FILENO) < 0) _exit(127);
if (dup2(err_pipe[1], STDERR_FILENO) < 0) _exit(127);
/* Close original write ends after dup */
close(out_pipe[1]);
close(err_pipe[1]);
/* Call the function under test; it will call exit(status) */
usage(status);
/* Should not reach; just in case */
_exit(128);
}
else
{
/* Parent */
close(out_pipe[1]);
close(err_pipe[1]);
char *captured_out = read_all_from_fd(out_pipe[0]);
char *captured_err = read_all_from_fd(err_pipe[0]);
close(out_pipe[0]);
close(err_pipe[0]);
int wstatus = 0;
if (waitpid(pid, &wstatus, 0) < 0)
{
if (captured_out) free(captured_out);
if (captured_err) free(captured_err);
return -1;
}
/* Ensure non-NULL strings */
if (!captured_out) captured_out = (char*)calloc(1, 1);
if (!captured_err) captured_err = (char*)calloc(1, 1);
if (!captured_out || !captured_err)
{
if (captured_out) free(captured_out);
if (captured_err) free(captured_err);
return -1;
}
*out = captured_out;
*err = captured_err;
if (WIFEXITED(wstatus))
*exit_code = WEXITSTATUS(wstatus);
else if (WIFSIGNALED(wstatus))
*exit_code = 128 + WTERMSIG(wstatus);
else
*exit_code = -1;
return 0;
}
}
/* Unity fixtures */
void setUp(void) {
/* Ensure predictable, English messages */
setlocale(LC_ALL, "C");
/* Ensure program_name is initialized to "join" */
/* set_program_name is declared in earlier includes in this translation unit */
set_program_name("join");
}
void tearDown(void) {
/* nothing */
}
/* Tests */
static void assert_contains(const char* haystack, const char* needle, const char* ctx)
{
if (!haystack || !needle) {
TEST_FAIL_MESSAGE("Internal error: NULL passed to assert_contains");
}
if (strstr(haystack, needle) == NULL) {
char msg[512];
snprintf(msg, sizeof msg, "Expected output to contain '%s' (%s)", needle, ctx ? ctx : "");
TEST_FAIL_MESSAGE(msg);
}
}
void test_usage_exit_success_prints_help_to_stdout(void)
{
char *out = NULL, *err = NULL;
int code = -1;
int rc = run_usage_and_capture(EXIT_SUCCESS, &out, &err, &code);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Failed to invoke/capture usage()");
TEST_ASSERT_EQUAL_INT(0, code);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_NOT_NULL(err);
/* stdout should be non-empty, stderr should be empty */
TEST_ASSERT_TRUE_MESSAGE(strlen(out) > 0, "Expected help text on stdout");
TEST_ASSERT_EQUAL_UINT_MESSAGE(0u, (unsigned)strlen(err), "Expected no stderr for EXIT_SUCCESS");
/* Basic sanity checks for content */
assert_contains(out, "Usage:", "usage header");
assert_contains(out, "join", "program name in usage");
assert_contains(out, "--help", "help option description likely present");
free(out);
free(err);
}
void test_usage_exit_failure_prints_try_help_to_stderr(void)
{
char *out = NULL, *err = NULL;
int code = -1;
int rc = run_usage_and_capture(EXIT_FAILURE, &out, &err, &code);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Failed to invoke/capture usage()");
TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, code);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_NOT_NULL(err);
/* stdout should be empty or nearly empty, stderr should have 'Try' message mentioning --help */
TEST_ASSERT_TRUE_MESSAGE(strlen(out) == 0, "Expected no stdout for non-success status");
assert_contains(err, "--help", "try-help hint");
assert_contains(err, "join", "program name in try-help hint");
free(out);
free(err);
}
void test_usage_help_contains_key_options(void)
{
char *out = NULL, *err = NULL;
int code = -1;
int rc = run_usage_and_capture(EXIT_SUCCESS, &out, &err, &code);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Failed to invoke/capture usage()");
TEST_ASSERT_EQUAL_INT(0, code);
TEST_ASSERT_TRUE_MESSAGE(strlen(out) > 0, "Expected help text on stdout");
TEST_ASSERT_EQUAL_UINT_MESSAGE(0u, (unsigned)strlen(err), "Expected no stderr for EXIT_SUCCESS");
/* Check presence of some key options in the help text */
assert_contains(out, "-a FILENUM", "option -a");
assert_contains(out, "-i, --ignore-case", "option -i");
assert_contains(out, "--check-order", "long option");
assert_contains(out, "--header", "header option");
assert_contains(out, "-z, --zero-terminated", "zero-terminated option");
free(out);
free(err);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_usage_exit_success_prints_help_to_stdout);
RUN_TEST(test_usage_exit_failure_prints_try_help_to_stderr);
RUN_TEST(test_usage_help_contains_key_options);
return UNITY_END();
}