File size: 6,210 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
#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();
} |