coreutils / tests /od /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 <errno.h>
#include <locale.h>
/* Target function under test: void usage(int status); declared earlier in od.c */
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 + 1024 > cap) {
size_t ncap = cap * 2;
char *nb = (char *)realloc(buf, ncap);
if (!nb) { free(buf); return NULL; }
buf = nb; cap = ncap;
}
ssize_t n = read(fd, buf + len, cap - len - 1);
if (n < 0) {
if (errno == EINTR) continue;
/* read error */
free(buf);
return NULL;
}
if (n == 0) break; /* EOF */
len += (size_t)n;
}
buf[len] = '\0';
if (out_len) *out_len = len;
return buf;
}
/* Helper to run usage(status) in a child, capturing stdout and stderr. */
static int run_usage_and_capture(int status, char **out_str, char **err_str, int *exit_code)
{
int out_pipe[2];
int err_pipe[2];
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) {
/* fork failed */
close(out_pipe[0]); close(out_pipe[1]);
close(err_pipe[0]); close(err_pipe[1]);
return -1;
}
if (pid == 0) {
/* Child: redirect stdout/stderr, set locale, set program name, call usage */
/* Ensure English output */
setenv("LC_ALL", "C", 1);
setenv("LANGUAGE", "C", 1);
setlocale(LC_ALL, "C");
/* Prepare redirection */
close(out_pipe[0]);
close(err_pipe[0]);
if (dup2(out_pipe[1], STDOUT_FILENO) < 0) _exit(127);
if (dup2(err_pipe[1], STDERR_FILENO) < 0) _exit(127);
close(out_pipe[1]);
close(err_pipe[1]);
/* Ensure program_name is set for formatted messages */
/* set_program_name is declared via headers included earlier in od.c */
extern void set_program_name(const char *);
set_program_name("od");
/* Call the function under test; it will exit(status). */
usage(status);
/* Not reached */
_exit(255);
}
/* Parent: close write ends and read output */
close(out_pipe[1]);
close(err_pipe[1]);
size_t out_len = 0, err_len = 0;
char *out_buf = read_all_from_fd(out_pipe[0], &out_len);
char *err_buf = read_all_from_fd(err_pipe[0], &err_len);
close(out_pipe[0]);
close(err_pipe[0]);
int status_wait = 0;
pid_t w = waitpid(pid, &status_wait, 0);
if (w < 0) {
if (out_buf) free(out_buf);
if (err_buf) free(err_buf);
return -1;
}
int code = -1;
if (WIFEXITED(status_wait)) code = WEXITSTATUS(status_wait);
else if (WIFSIGNALED(status_wait)) code = 128 + WTERMSIG(status_wait);
if (exit_code) *exit_code = code;
if (out_str) *out_str = out_buf; else if (out_buf) free(out_buf);
if (err_str) *err_str = err_buf; else if (err_buf) free(err_buf);
return 0;
}
void setUp(void) {
/* No global setup required */
}
void tearDown(void) {
/* No global teardown required */
}
static void assert_substr(const char *haystack, const char *needle)
{
TEST_ASSERT_NOT_NULL_MESSAGE(haystack, "Captured text 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_help_and_exits_success(void)
{
char *out = NULL; char *err = NULL; int code = -1;
int rc = run_usage_and_capture(EXIT_SUCCESS, &out, &err, &code);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "run_usage_and_capture failed");
/* Verify exit status */
TEST_ASSERT_EQUAL_INT(EXIT_SUCCESS, code);
/* For success case, help goes to stdout, stderr should be empty */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_TRUE_MESSAGE(out[0] != '\0', "stdout should not be empty for --help");
TEST_ASSERT_NOT_NULL(err);
TEST_ASSERT_TRUE_MESSAGE(err[0] == '\0', "stderr should be empty for --help");
/* Verify some key substrings of the help text */
assert_substr(out, "Usage: od ");
assert_substr(out, "--address-radix=");
assert_substr(out, "--endian=");
assert_substr(out, "--width");
free(out);
free(err);
}
void test_usage_failure_outputs_try_help_to_stderr_and_exits_failure(void)
{
char *out = NULL; char *err = NULL; int code = -1;
int rc = run_usage_and_capture(EXIT_FAILURE, &out, &err, &code);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "run_usage_and_capture failed");
TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, code);
/* For failure case, try-help goes to stderr */
TEST_ASSERT_NOT_NULL(out);
/* stdout should be empty or nearly empty */
TEST_ASSERT_TRUE_MESSAGE(out[0] == '\0', "stdout should be empty when emitting try-help");
TEST_ASSERT_NOT_NULL(err);
assert_substr(err, "Try 'od --help' for more information.");
free(out);
free(err);
}
void test_usage_custom_nonzero_status_propagates_exit_code_and_try_help(void)
{
char *out = NULL; char *err = NULL; int code = -1;
/* Use a non-standard nonzero exit status */
int wanted = 77;
int rc = run_usage_and_capture(wanted, &out, &err, &code);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "run_usage_and_capture failed");
TEST_ASSERT_EQUAL_INT(wanted, code);
TEST_ASSERT_NOT_NULL(err);
assert_substr(err, "Try 'od --help' for more information.");
free(out);
free(err);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_usage_success_outputs_help_and_exits_success);
RUN_TEST(test_usage_failure_outputs_try_help_to_stderr_and_exits_failure);
RUN_TEST(test_usage_custom_nonzero_status_propagates_exit_code_and_try_help);
return UNITY_END();
}