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();
}