coreutils / tests /dd /tests_for_diagnose.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 <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
/* Unity setUp/tearDown */
void setUp(void) {
/* no-op */
}
void tearDown(void) {
/* no-op */
}
/* Helper: read all from fd into malloc'd buffer, return NUL-terminated string */
static char *read_all_into_string(int fd) {
size_t cap = 1024;
size_t len = 0;
char *buf = (char *)malloc(cap);
if (!buf) return NULL;
for (;;) {
if (len + 512 > cap) {
size_t ncap = cap * 2;
char *nbuf = (char *)realloc(buf, ncap);
if (!nbuf) { free(buf); return NULL; }
cap = ncap; buf = nbuf;
}
ssize_t n = read(fd, buf + len, 512);
if (n < 0) {
if (errno == EINTR) continue;
break;
}
if (n == 0) break;
len += (size_t)n;
}
buf[len] = '\0';
return buf;
}
/* Helper: capture stderr while executing a callback that writes to stderr. */
static char *capture_stderr_and_call(void (*cb)(void)) {
int pipefd[2];
if (pipe(pipefd) != 0) {
return NULL;
}
int saved_stderr = dup(STDERR_FILENO);
if (saved_stderr < 0) {
close(pipefd[0]); close(pipefd[1]);
return NULL;
}
/* Redirect stderr to pipe write end */
if (dup2(pipefd[1], STDERR_FILENO) < 0) {
close(pipefd[0]); close(pipefd[1]); close(saved_stderr);
return NULL;
}
/* We can close this extra descriptor; fd 2 now points to the write end */
close(pipefd[1]);
/* Call the provided function that triggers output to stderr */
cb();
/* Ensure all stdio buffers to stderr are flushed before restoring */
fflush(stderr);
/* Restore stderr */
dup2(saved_stderr, STDERR_FILENO);
close(saved_stderr);
/* Read captured output */
char *out = read_all_into_string(pipefd[0]);
close(pipefd[0]);
return out;
}
/* Simple substring check */
static bool str_contains(const char *haystack, const char *needle) {
if (!haystack || !needle) return false;
return strstr(haystack, needle) != NULL;
}
/* Access the static variable progress_len and static function diagnose directly,
since this test file is included in the same translation unit as dd.c. */
extern int progress_len; /* declared in dd.c as static; available via inclusion */
/* diagnose is defined earlier in the same translation unit */
/* Helper to call diagnose with varargs via a closure-like wrapper */
static int g_errnum;
static const char *g_fmt;
static const char *g_sarg;
static int g_iarg;
static void call_diagnose_s_i(void) {
diagnose(g_errnum, g_fmt, g_sarg, g_iarg);
}
static void call_diagnose_s(void) {
diagnose(g_errnum, g_fmt, g_sarg);
}
static void call_diagnose_literal(void) {
diagnose(g_errnum, "Literal message");
}
void test_diagnose_inserts_newline_when_progress_pending(void) {
progress_len = 5; /* simulate an in-progress status line */
g_errnum = 0; g_fmt = "Test %s"; g_sarg = "NL";
char *out = capture_stderr_and_call(call_diagnose_s);
TEST_ASSERT_NOT_NULL(out);
/* First character should be a newline due to pending progress */
TEST_ASSERT_TRUE_MESSAGE(out[0] == '\n', "diagnose should prepend a newline when progress_len>0");
/* It should contain our formatted message */
TEST_ASSERT_TRUE(str_contains(out, "Test NL"));
/* progress_len must reset to 0 */
TEST_ASSERT_EQUAL_INT(0, progress_len);
free(out);
}
void test_diagnose_no_leading_newline_without_progress(void) {
progress_len = 0;
g_errnum = 0; g_fmt = "Hello %s"; g_sarg = "World";
char *out = capture_stderr_and_call(call_diagnose_s);
TEST_ASSERT_NOT_NULL(out);
/* Should not start with a newline when no progress pending */
TEST_ASSERT_TRUE_MESSAGE(out[0] != '\n', "diagnose should not prepend newline when progress_len==0");
TEST_ASSERT_TRUE(str_contains(out, "Hello World"));
TEST_ASSERT_EQUAL_INT(0, progress_len);
free(out);
}
void test_diagnose_includes_errno_message_when_errnum_nonzero(void) {
progress_len = 0;
g_errnum = EINVAL; g_fmt = "Bad option %s"; g_sarg = "--foo";
const char *errtxt = strerror(g_errnum);
char *out = capture_stderr_and_call(call_diagnose_s);
TEST_ASSERT_NOT_NULL(out);
/* Check our message passed through */
TEST_ASSERT_TRUE(str_contains(out, "Bad option --foo"));
/* Check errno text is included (localized string acceptable) */
TEST_ASSERT_TRUE_MESSAGE(str_contains(out, errtxt), "diagnose should include strerror text when errnum != 0");
free(out);
}
void test_diagnose_varargs_formatting_with_int_and_string(void) {
progress_len = 0;
g_errnum = 0; g_fmt = "Value:%d;Name:%s"; g_sarg = "Z"; g_iarg = 123;
char *out = capture_stderr_and_call(call_diagnose_s_i);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_TRUE(str_contains(out, "Value:123;Name:Z"));
free(out);
}
void test_diagnose_resets_progress_len_even_on_simple_message(void) {
progress_len = 2;
g_errnum = 0;
char *out = capture_stderr_and_call(call_diagnose_literal);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_INT(0, progress_len);
TEST_ASSERT_TRUE(str_contains(out, "Literal message"));
free(out);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_diagnose_inserts_newline_when_progress_pending);
RUN_TEST(test_diagnose_no_leading_newline_without_progress);
RUN_TEST(test_diagnose_includes_errno_message_when_errnum_nonzero);
RUN_TEST(test_diagnose_varargs_formatting_with_int_and_string);
RUN_TEST(test_diagnose_resets_progress_len_even_on_simple_message);
return UNITY_END();
}