File size: 9,489 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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>

/* Helper: create a temp file with given data, return fd and path. */
static int create_temp_file_with_data(const void *data, size_t len, char *path_buf, size_t path_buf_sz)
{
    /* Create a template in current directory to avoid platform-specific temp dirs */
    const char *tmpl = "./test_copyfd_src_XXXXXX";
    if (path_buf_sz < strlen(tmpl) + 1)
        return -1;
    strcpy(path_buf, tmpl);
    int fd = mkstemp(path_buf);
    if (fd < 0)
        return -1;

    if (len > 0) {
        ssize_t wr = write(fd, data, len);
        if (wr < 0 || (size_t)wr != len) {
            int saved = errno;
            close(fd);
            unlink(path_buf);
            errno = saved;
            return -1;
        }
    }
    /* Rewind for reading */
    if (lseek(fd, 0, SEEK_SET) < 0) {
        int saved = errno;
        close(fd);
        unlink(path_buf);
        errno = saved;
        return -1;
    }
    return fd;
}

/* Helper: capture stdout to a temp file, run copy_fd, restore stdout, and validate. */
static char *run_and_check_copy_fd(int src_fd,
                                   uintmax_t n_bytes,
                                   enum Copy_fd_status expected_status,
                                   const void *expected_output,
                                   size_t expected_output_len)
{
    /* Flush stdout before redirect */
    fflush(stdout);

    /* Save current stdout */
    int saved_stdout = dup(STDOUT_FILENO);
    if (saved_stdout < 0) {
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "dup(STDOUT) failed: errno=%d", errno);
        return msg;
    }

    /* Create output capture file */
    char out_tmpl[] = "./test_copyfd_out_XXXXXX";
    int out_fd = mkstemp(out_tmpl);
    if (out_fd < 0) {
        close(saved_stdout);
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "mkstemp for stdout capture failed: errno=%d", errno);
        return msg;
    }

    /* Redirect stdout to capture file */
    if (dup2(out_fd, STDOUT_FILENO) < 0) {
        int saved = errno;
        close(out_fd);
        close(saved_stdout);
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "dup2 to redirect stdout failed: errno=%d", saved);
        return msg;
    }
    close(out_fd); /* Now captured by fd 1 */

    /* Call the function under test (no Unity asserts while redirected!) */
    enum Copy_fd_status st = copy_fd(src_fd, n_bytes);

    /* Ensure all data is flushed to the capture file */
    fflush(stdout);

    /* Restore stdout */
    if (dup2(saved_stdout, STDOUT_FILENO) < 0) {
        int saved = errno;
        close(saved_stdout);
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "dup2 to restore stdout failed: errno=%d", saved);
        return msg;
    }
    close(saved_stdout);

    /* Read back captured output */
    int cap_fd = open(out_tmpl, O_RDONLY);
    if (cap_fd < 0) {
        int saved = errno;
        unlink(out_tmpl);
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "open captured file failed: errno=%d", saved);
        return msg;
    }

    struct stat stbuf;
    if (fstat(cap_fd, &stbuf) < 0) {
        int saved = errno;
        close(cap_fd);
        unlink(out_tmpl);
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "fstat captured file failed: errno=%d", saved);
        return msg;
    }

    size_t cap_sz = (size_t)stbuf.st_size;
    char *cap = NULL;
    if (cap_sz > 0) {
        cap = (char*)malloc(cap_sz);
        if (!cap) {
            close(cap_fd);
            unlink(out_tmpl);
            char *msg = (char*)malloc(256);
            snprintf(msg, 256, "malloc(%zu) failed for capture buffer", cap_sz);
            return msg;
        }

        size_t off = 0;
        while (off < cap_sz) {
            ssize_t r = read(cap_fd, cap + off, cap_sz - off);
            if (r < 0) {
                int saved = errno;
                free(cap);
                close(cap_fd);
                unlink(out_tmpl);
                char *msg = (char*)malloc(256);
                snprintf(msg, 256, "read captured file failed: errno=%d", saved);
                return msg;
            }
            if (r == 0) break;
            off += (size_t)r;
        }
        /* cap_sz is from st_size; off should equal cap_sz. */
    }
    close(cap_fd);
    unlink(out_tmpl);

    /* Validate status */
    if (st != expected_status) {
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "Status mismatch: got %d, expected %d", (int)st, (int)expected_status);
        free(cap);
        return msg;
    }

    /* Validate output length */
    if (cap_sz != expected_output_len) {
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "Output length mismatch: got %zu, expected %zu", cap_sz, expected_output_len);
        free(cap);
        return msg;
    }

    /* Validate output content if any */
    if (cap_sz > 0 && memcmp(cap, expected_output, cap_sz) != 0) {
        /* Find first mismatch for easier debugging */
        size_t idx = 0;
        while (idx < cap_sz && ((unsigned char*)cap)[idx] == ((const unsigned char*)expected_output)[idx])
            idx++;
        char *msg = (char*)malloc(256);
        snprintf(msg, 256, "Output content mismatch at byte %zu", idx);
        free(cap);
        return msg;
    }

    free(cap);
    return NULL; /* success */
}

void setUp(void) {
    /* Setup code here, or leave empty */
}

void tearDown(void) {
    /* Cleanup code here, or leave empty */
}

static void fill_pattern(char *buf, size_t len)
{
    for (size_t i = 0; i < len; i++)
        buf[i] = (char)('A' + (i % 26));
}

/* Tests */

void test_copy_fd_zero_bytes_no_output(void)
{
    const char data[] = "ignored";
    char path[64];
    int fd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path));
    TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file");

    /* Request 0 bytes */
    char *err = run_and_check_copy_fd(fd, 0, COPY_FD_OK, "", 0);
    close(fd);
    unlink(path);

    TEST_ASSERT_NULL_MESSAGE(err, err ? err : "");
    free(err);
}

void test_copy_fd_partial_copy(void)
{
    const char data[] = "abcdef";
    const size_t req = 3;
    char path[64];
    int fd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path));
    TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file");

    char *err = run_and_check_copy_fd(fd, req, COPY_FD_OK, data, req);
    close(fd);
    unlink(path);

    TEST_ASSERT_NULL_MESSAGE(err, err ? err : "");
    free(err);
}

void test_copy_fd_exact_copy(void)
{
    const char data[] = "Hello, world!";
    const size_t len = sizeof(data) - 1;
    char path[64];
    int fd = create_temp_file_with_data(data, len, path, sizeof(path));
    TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file");

    char *err = run_and_check_copy_fd(fd, len, COPY_FD_OK, data, len);
    close(fd);
    unlink(path);

    TEST_ASSERT_NULL_MESSAGE(err, err ? err : "");
    free(err);
}

void test_copy_fd_oversized_request_unexpected_eof(void)
{
    const char data[] = "abcd";
    const size_t len = sizeof(data) - 1;
    const uintmax_t req = 10; /* larger than len */
    char path[64];
    int fd = create_temp_file_with_data(data, len, path, sizeof(path));
    TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file");

    /* Expect all available bytes written, status UNEXPECTED_EOF */
    char *err = run_and_check_copy_fd(fd, req, COPY_FD_UNEXPECTED_EOF, data, len);
    close(fd);
    unlink(path);

    TEST_ASSERT_NULL_MESSAGE(err, err ? err : "");
    free(err);
}

void test_copy_fd_read_error_returns_status(void)
{
    /* Create a file, then open it write-only to induce read error */
    const char data[] = "some data";
    char path[64];
    int tmpfd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path));
    TEST_ASSERT_MESSAGE(tmpfd >= 0, "Failed to create temp source file");
    close(tmpfd);

    int wfd = open(path, O_WRONLY);
    TEST_ASSERT_MESSAGE(wfd >= 0, "Failed to open file write-only to induce read error");

    /* Expect READ_ERROR and no output */
    char *err = run_and_check_copy_fd(wfd, 5, COPY_FD_READ_ERROR, "", 0);
    close(wfd);
    unlink(path);

    TEST_ASSERT_NULL_MESSAGE(err, err ? err : "");
    free(err);
}

void test_copy_fd_large_transfer_multiple_buffers(void)
{
    size_t len = (size_t)BUFSIZ * 3 + 123; /* span multiple reads */
    char *data = (char*)malloc(len);
    TEST_ASSERT_NOT_NULL(data);
    fill_pattern(data, len);

    char path[64];
    int fd = create_temp_file_with_data(data, len, path, sizeof(path));
    TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file");

    char *err = run_and_check_copy_fd(fd, len, COPY_FD_OK, data, len);
    close(fd);
    unlink(path);
    free(data);

    TEST_ASSERT_NULL_MESSAGE(err, err ? err : "");
    free(err);
}

int main(void)
{
    UNITY_BEGIN();
    RUN_TEST(test_copy_fd_zero_bytes_no_output);
    RUN_TEST(test_copy_fd_partial_copy);
    RUN_TEST(test_copy_fd_exact_copy);
    RUN_TEST(test_copy_fd_oversized_request_unexpected_eof);
    RUN_TEST(test_copy_fd_read_error_returns_status);
    RUN_TEST(test_copy_fd_large_transfer_multiple_buffers);
    return UNITY_END();
}