coreutils / tests /dd /tests_for_process_signals.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 <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
/* The test file is included directly into dd.c, so all static symbols from dd
(like process_signals, info_signal_count, status_level, progress_len, etc.)
are visible here. We must not redeclare them. */
static char *capture_stderr_begin(int *saved_fd, int pipefd[2])
{
if (pipe(pipefd) != 0)
return NULL;
fflush(stderr);
*saved_fd = dup(STDERR_FILENO);
if (*saved_fd < 0)
return NULL;
if (dup2(pipefd[1], STDERR_FILENO) < 0)
return NULL;
close(pipefd[1]); /* writer now at fd 2 */
return (char *)1; /* non-NULL indicates success */
}
static char *capture_stderr_end(int saved_fd, int pipefd[2])
{
/* Restore stderr */
fflush(stderr);
if (dup2(saved_fd, STDERR_FILENO) >= 0) {
close(saved_fd);
}
/* Read all from pipefd[0] */
char *buf = NULL;
size_t cap = 0;
size_t len = 0;
for (;;) {
char tmp[256];
ssize_t n = read(pipefd[0], tmp, sizeof(tmp));
if (n <= 0) break;
if (len + (size_t)n + 1 > cap) {
size_t ncap = cap ? cap * 2 : 512;
while (ncap < len + (size_t)n + 1) ncap *= 2;
char *nbuf = (char *)realloc(buf, ncap);
if (!nbuf) {
free(buf);
buf = NULL;
break;
}
buf = nbuf;
cap = ncap;
}
memcpy(buf + len, tmp, (size_t)n);
len += (size_t)n;
buf[len] = '\0';
}
close(pipefd[0]);
if (!buf) {
buf = (char *)malloc(1);
if (buf) buf[0] = '\0';
}
return buf;
}
void setUp(void) {
/* Ensure no pending interrupt and default state before each test. */
/* These are static globals from dd.c; accessible here. */
extern sig_atomic_t volatile interrupt_signal;
extern sig_atomic_t volatile info_signal_count;
extern int status_level;
extern int progress_len;
interrupt_signal = 0;
info_signal_count = 0;
status_level = 3; /* STATUS_DEFAULT, will be overridden in tests as needed */
progress_len = 0;
}
void tearDown(void) {
/* Nothing to clean */
}
/* Test that info signals are all processed (count decremented to 0)
and that no output occurs when status_level == STATUS_NONE. */
void test_process_signals_info_only_decrements_to_zero(void)
{
extern void process_signals(void);
extern sig_atomic_t volatile info_signal_count;
extern sig_atomic_t volatile interrupt_signal;
extern int status_level;
info_signal_count = 5;
interrupt_signal = 0;
status_level = 1; /* STATUS_NONE: suppress output */
process_signals();
TEST_ASSERT_EQUAL_INT_MESSAGE(0, (int)info_signal_count, "info_signal_count should be 0 after processing");
TEST_ASSERT_EQUAL_INT_MESSAGE(0, (int)interrupt_signal, "interrupt_signal should remain 0");
}
/* Test that when info_signal_count is zero, process_signals is a no-op and
produces no output (use STATUS_NOXFER to ensure print_stats would print if called). */
void test_process_signals_no_info_no_output(void)
{
extern void process_signals(void);
extern sig_atomic_t volatile info_signal_count;
extern int status_level;
info_signal_count = 0;
status_level = 2; /* STATUS_NOXFER: would print records if called */
int saved;
int pf[2];
TEST_ASSERT_NOT_NULL_MESSAGE(capture_stderr_begin(&saved, pf), "Failed to start capturing stderr");
process_signals();
char *out = capture_stderr_end(saved, pf);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_UINT_MESSAGE(0, (unsigned)strlen(out), "No output expected when no info signals");
free(out);
}
/* Test that info signals cause print_stats to be called the correct number of times
by checking the number of newline-terminated lines emitted to stderr when
status_level == STATUS_NOXFER (which prints exactly two lines per call). */
void test_process_signals_info_only_prints_expected_lines(void)
{
extern void process_signals(void);
extern sig_atomic_t volatile info_signal_count;
extern int status_level;
extern int progress_len;
info_signal_count = 2;
status_level = 2; /* STATUS_NOXFER */
progress_len = 0; /* ensure no leading newline from prior progress */
int saved;
int pf[2];
TEST_ASSERT_NOT_NULL_MESSAGE(capture_stderr_begin(&saved, pf), "Failed to start capturing stderr");
process_signals();
char *out = capture_stderr_end(saved, pf);
TEST_ASSERT_NOT_NULL(out);
/* Count newlines; expect 2 lines per info signal -> 4 newlines total. */
size_t nl = 0;
for (char *p = out; *p; ++p) if (*p == '\n') nl++;
TEST_ASSERT_EQUAL_UINT_MESSAGE(4, nl, "Expected exactly two lines per info signal (total 4 newlines)");
free(out);
}
/* Test that if there was a pending progress line (progress_len > 0),
print_stats first emits a newline before the records, when signaled.
We assert the first byte of captured stderr is a newline. */
void test_process_signals_progress_line_newline_emitted_first(void)
{
extern void process_signals(void);
extern sig_atomic_t volatile info_signal_count;
extern int status_level;
extern int progress_len;
info_signal_count = 1;
status_level = 2; /* STATUS_NOXFER */
progress_len = 7; /* simulate in-progress status line */
int saved;
int pf[2];
TEST_ASSERT_NOT_NULL_MESSAGE(capture_stderr_begin(&saved, pf), "Failed to start capturing stderr");
process_signals();
char *out = capture_stderr_end(saved, pf);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_TRUE_MESSAGE(out[0] == '\n', "Expected leading newline when progress_len > 0");
free(out);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_process_signals_info_only_decrements_to_zero);
RUN_TEST(test_process_signals_no_info_no_output);
RUN_TEST(test_process_signals_info_only_prints_expected_lines);
RUN_TEST(test_process_signals_progress_line_newline_emitted_first);
return UNITY_END();
}