coreutils / tests /comm /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/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
/* usage(int) is provided by the program under test. */
/* program_name is declared in headers included by the program source before this file. */
static char *read_all_from_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 r = read (fd, buf + len, cap - len);
if (r < 0)
{
if (errno == EINTR)
continue;
free (buf);
return NULL;
}
if (r == 0)
break;
len += (size_t) r;
}
/* NUL-terminate to make strstr safe (though len is provided). */
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;
}
static int run_usage_and_capture (int status_code,
char **out_stdout, size_t *out_stdout_len,
char **out_stderr, size_t *out_stderr_len,
int *out_exit_status)
{
int pout[2];
int perr[2];
if (pipe (pout) != 0)
return -1;
if (pipe (perr) != 0)
{
close (pout[0]); close (pout[1]);
return -1;
}
pid_t pid = fork ();
if (pid < 0)
{
close (pout[0]); close (pout[1]);
close (perr[0]); close (perr[1]);
return -1;
}
if (pid == 0)
{
/* Child: redirect stdout/stderr and invoke usage(status_code). */
(void) setenv ("LC_ALL", "C", 1);
if (dup2 (pout[1], STDOUT_FILENO) < 0) _exit (127);
if (dup2 (perr[1], STDERR_FILENO) < 0) _exit (127);
close (pout[0]); close (pout[1]);
close (perr[0]); close (perr[1]);
usage (status_code);
/* Not reached. */
_exit (200);
}
/* Parent: close write ends, read output, wait for child. */
close (pout[1]);
close (perr[1]);
size_t out_len = 0, err_len = 0;
char *out_buf = read_all_from_fd (pout[0], &out_len);
char *err_buf = read_all_from_fd (perr[0], &err_len);
close (pout[0]);
close (perr[0]);
int wstatus = 0;
if (waitpid (pid, &wstatus, 0) < 0)
{
free (out_buf);
free (err_buf);
return -1;
}
int exit_status = -1;
if (WIFEXITED (wstatus))
exit_status = WEXITSTATUS (wstatus);
else
exit_status = -2; /* Abnormal termination. */
if (out_stdout) *out_stdout = out_buf; else free (out_buf);
if (out_stdout_len) *out_stdout_len = out_len;
if (out_stderr) *out_stderr = err_buf; else free (err_buf);
if (out_stderr_len) *out_stderr_len = err_len;
if (out_exit_status) *out_exit_status = exit_status;
return 0;
}
void setUp(void) {
/* No global setup needed. */
}
void tearDown(void) {
/* No global teardown needed. */
}
/* Tests */
void test_usage_exit_zero_prints_full_help(void)
{
/* program_name is provided by the program. Set it so help includes our chosen value. */
program_name = "mycomm";
char *capt_out = NULL;
char *capt_err = NULL;
size_t out_len = 0, err_len = 0;
int exit_st = -1;
int rc = run_usage_and_capture (EXIT_SUCCESS, &capt_out, &out_len, &capt_err, &err_len, &exit_st);
TEST_ASSERT_EQUAL_INT_MESSAGE (0, rc, "Failed to run usage() in child process");
TEST_ASSERT_EQUAL_INT_MESSAGE (0, exit_st, "usage(EXIT_SUCCESS) should exit with status 0");
TEST_ASSERT_NOT_NULL_MESSAGE (capt_out, "Expected captured stdout buffer");
TEST_ASSERT_NOT_NULL_MESSAGE (capt_err, "Expected captured stderr buffer");
/* Full help should be on stdout; stderr should be empty. */
TEST_ASSERT_TRUE_MESSAGE (out_len > 0, "Expected non-empty stdout for full help");
TEST_ASSERT_EQUAL_UINT_MESSAGE (0, err_len, "Expected empty stderr for full help");
/* Check for key substrings in help text (locale forced to C in child). */
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "Usage: mycomm"), "Help must include 'Usage: mycomm'");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "[OPTION]"), "Help must mention [OPTION]");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "Compare sorted files"), "Help should describe comparison");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "--check-order"), "Help should mention --check-order");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "--nocheck-order"), "Help should mention --nocheck-order");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "--output-delimiter"), "Help should mention --output-delimiter");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "--total"), "Help should mention --total");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "--zero-terminated"), "Help should mention --zero-terminated");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "Examples:"), "Help should include Examples section");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "mycomm -12 file1 file2"), "Examples should include 'mycomm -12 file1 file2'");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_out, "mycomm -3 file1 file2"), "Examples should include 'mycomm -3 file1 file2'");
free (capt_out);
free (capt_err);
}
void test_usage_nonzero_status_prints_try_help_to_stderr(void)
{
program_name = "mycomm";
char *capt_out = NULL;
char *capt_err = NULL;
size_t out_len = 0, err_len = 0;
int exit_st = -1;
int expected_status = 7; /* arbitrary non-zero */
int rc = run_usage_and_capture (expected_status, &capt_out, &out_len, &capt_err, &err_len, &exit_st);
TEST_ASSERT_EQUAL_INT_MESSAGE (0, rc, "Failed to run usage() in child process");
TEST_ASSERT_EQUAL_INT_MESSAGE (expected_status, exit_st, "usage(nonzero) should exit with that status");
TEST_ASSERT_NOT_NULL_MESSAGE (capt_out, "Expected captured stdout buffer");
TEST_ASSERT_NOT_NULL_MESSAGE (capt_err, "Expected captured stderr buffer");
/* Error synopsis should be on stderr; stdout should be empty. */
TEST_ASSERT_EQUAL_UINT_MESSAGE (0, out_len, "Expected empty stdout when emitting try-help");
TEST_ASSERT_TRUE_MESSAGE (err_len > 0, "Expected non-empty stderr when emitting try-help");
/* Check the try-help message contains our program_name and '--help'. */
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_err, "Try "), "try-help should contain 'Try '");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_err, "mycomm"), "try-help should include program_name");
TEST_ASSERT_NOT_NULL_MESSAGE (strstr (capt_err, "--help"), "try-help should suggest --help");
free (capt_out);
free (capt_err);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_usage_exit_zero_prints_full_help);
RUN_TEST(test_usage_nonzero_status_prints_try_help_to_stderr);
return UNITY_END();
}