coreutils / tests /basenc /tests_for_usage.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <locale.h>
/* Helper to read all data from an fd into a malloc'd buffer. */
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 new_cap = cap * 2;
char *new_buf = (char*)realloc(buf, new_cap);
if (!new_buf) {
free(buf);
return NULL;
}
buf = new_buf;
cap = new_cap;
}
ssize_t r = read(fd, buf + len, cap - len);
if (r < 0) {
if (errno == EINTR) continue;
free(buf);
return NULL;
} else if (r == 0) {
break;
} else {
len += (size_t)r;
}
}
/* NUL-terminate for convenience (not counted in out_len). */
if (len == cap) {
char *new_buf = (char*)realloc(buf, cap + 1);
if (!new_buf) {
free(buf);
return NULL;
}
buf = new_buf;
}
buf[len] = '\0';
if (out_len) *out_len = len;
return buf;
}
/* Run usage(status) in a child, capturing stdout and stderr, and the exit code. */
static int run_usage_and_capture(int status,
char **out_buf, size_t *out_len,
char **err_buf, size_t *err_len,
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) {
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 and call usage. No Unity asserts here. */
/* Force C locale for predictable messages (best-effort). */
setenv("LC_ALL", "C", 1);
setenv("LANG", "C", 1);
setlocale(LC_ALL, "C");
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]);
/* Call the function under test; it should exit(status). */
usage(status);
/* Should never reach here. */
_exit(123);
}
/* Parent: close write ends, read outputs, wait for child. */
close(out_pipe[1]);
close(err_pipe[1]);
char *child_out = read_all_from_fd(out_pipe[0], out_len);
char *child_err = read_all_from_fd(err_pipe[0], err_len);
close(out_pipe[0]);
close(err_pipe[0]);
int wstatus = 0;
if (waitpid(pid, &wstatus, 0) < 0) {
if (child_out) free(child_out);
if (child_err) free(child_err);
return -1;
}
int code = -1;
if (WIFEXITED(wstatus)) {
code = WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
/* Encode signal as negative for clarity. */
code = -WTERMSIG(wstatus);
}
if (exit_code) *exit_code = code;
if (out_buf) *out_buf = child_out; else free(child_out);
if (err_buf) *err_buf = child_err; else free(child_err);
return 0;
}
void setUp(void) {
/* No global state to set explicitly. */
}
void tearDown(void) {
/* No global state to clean. */
}
/* Test that usage(EXIT_SUCCESS) prints full help to stdout and exits with 0. */
void test_usage_success_prints_help_and_exits_success(void) {
char *out = NULL, *err = NULL;
size_t outlen = 0, errlen = 0;
int exit_code = -1;
int rc = run_usage_and_capture(EXIT_SUCCESS, &out, &outlen, &err, &errlen, &exit_code);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(0, exit_code);
/* On success, help should go to stdout and stderr should be empty. */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_TRUE(outlen > 0);
TEST_ASSERT_NOT_NULL(err);
TEST_ASSERT_EQUAL_size_t(0, errlen);
/* Look for invariant option names that should be present in basenc help. */
TEST_ASSERT_NOT_NULL(strstr(out, "--decode"));
TEST_ASSERT_NOT_NULL(strstr(out, "--wrap"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base64"));
TEST_ASSERT_NOT_NULL(strstr(out, "--z85")); /* basenc-specific option */
free(out);
free(err);
}
/* Test that usage(EXIT_FAILURE) emits a 'try --help' hint to stderr, and exits with failure. */
void test_usage_failure_emits_try_help_on_stderr_and_exits_failure(void) {
char *out = NULL, *err = NULL;
size_t outlen = 0, errlen = 0;
int exit_code = -1;
int rc = run_usage_and_capture(EXIT_FAILURE, &out, &outlen, &err, &errlen, &exit_code);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, exit_code);
/* On failure, stdout should be empty, stderr should contain the hint with "--help". */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(0, outlen);
TEST_ASSERT_NOT_NULL(err);
TEST_ASSERT_TRUE(errlen > 0);
TEST_ASSERT_NOT_NULL(strstr(err, "--help"));
free(out);
free(err);
}
/* Test that basenc's multi-base options appear in the success help text. */
void test_usage_success_contains_multiple_basenc_modes(void) {
char *out = NULL, *err = NULL;
size_t outlen = 0, errlen = 0;
int exit_code = -1;
int rc = run_usage_and_capture(EXIT_SUCCESS, &out, &outlen, &err, &errlen, &exit_code);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(0, exit_code);
/* Ensure several basenc variants appear in the help text. */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_NOT_NULL(strstr(out, "--base64"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base64url"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base58"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base32"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base32hex"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base16"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base2msbf"));
TEST_ASSERT_NOT_NULL(strstr(out, "--base2lsbf"));
TEST_ASSERT_NOT_NULL(strstr(out, "--z85"));
free(out);
free(err);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_usage_success_prints_help_and_exits_success);
RUN_TEST(test_usage_failure_emits_try_help_on_stderr_and_exits_failure);
RUN_TEST(test_usage_success_contains_multiple_basenc_modes);
return UNITY_END();
}