File size: 6,082 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
#include "../../unity/unity.h"
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>

/* Helper that captures what write_pending writes to stdout.
   It performs stdout redirection, calls write_pending(outbuf, bpout),
   restores stdout, then reads and returns the captured data.
   On success, returns a malloc'd buffer (possibly empty but non-NULL) with its length in *out_len.
   On failure, returns NULL. Never uses Unity assertions while stdout is redirected. */
static char* capture_write_pending_output(char *outbuf, char **bpout, size_t *out_len)
{
    int saved_stdout = -1;
    int p[2] = {-1, -1};
    char *result = NULL;
    size_t result_len = 0;

    saved_stdout = dup(STDOUT_FILENO);
    if (saved_stdout < 0)
        return NULL;

    if (pipe(p) != 0) {
        close(saved_stdout);
        return NULL;
    }

    if (dup2(p[1], STDOUT_FILENO) < 0) {
        close(saved_stdout);
        close(p[0]);
        close(p[1]);
        return NULL;
    }
    close(p[1]); /* stdout now refers to the pipe's write end */

    /* Call the target while stdout is redirected: no Unity asserts here. */
    write_pending(outbuf, bpout);

    /* Restore stdout before any assertions. */
    if (dup2(saved_stdout, STDOUT_FILENO) < 0) {
        /* Even if restore fails, attempt to close fds to minimize leaks. */
        close(saved_stdout);
        close(p[0]);
        return NULL;
    }
    close(saved_stdout);

    /* Read all output from the pipe. */
    {
        char tmp[1024];
        ssize_t n;
        size_t cap = 0;
        while ((n = read(p[0], tmp, sizeof tmp)) > 0) {
            if (result_len + (size_t)n > cap) {
                size_t new_cap = cap ? cap * 2 : 1024;
                while (new_cap < result_len + (size_t)n)
                    new_cap *= 2;
                char *new_buf = (char*)realloc(result, new_cap);
                if (!new_buf) {
                    free(result);
                    close(p[0]);
                    return NULL;
                }
                result = new_buf;
                cap = new_cap;
            }
            memcpy(result + result_len, tmp, (size_t)n);
            result_len += (size_t)n;
        }
        close(p[0]);
        if (n < 0) {
            free(result);
            return NULL;
        }
    }

    /* Ensure non-NULL return even for zero-length capture, to simplify tests. */
    if (!result) {
        result = (char*)malloc(1);
        if (!result) return NULL;
    }

    if (out_len)
        *out_len = result_len;
    return result;
}

void setUp(void) {
    /* No global setup needed */
}
void tearDown(void) {
    /* No global teardown needed */
}

/* Test: When there is no pending data (bpout == outbuf), nothing is written and bpout is unchanged. */
void test_write_pending_no_pending_data(void)
{
    char outbuf[8] = {0};
    char *bp = outbuf; /* no data pending */

    size_t captured_len = 0;
    char *captured = capture_write_pending_output(outbuf, &bp, &captured_len);
    TEST_ASSERT_NOT_NULL_MESSAGE(captured, "Failed to capture output");
    TEST_ASSERT_EQUAL_size_t(0, captured_len);
    TEST_ASSERT_EQUAL_PTR(outbuf, bp); /* pointer should remain unchanged */
    free(captured);
}

/* Test: With pending data, exactly those bytes are written and bpout is reset to outbuf. */
void test_write_pending_writes_and_resets_pointer(void)
{
    const char *msg = "Hello, world!";
    size_t msg_len = strlen(msg);
    char outbuf[64];
    memcpy(outbuf, msg, msg_len);
    char *bp = outbuf + msg_len;

    size_t captured_len = 0;
    char *captured = capture_write_pending_output(outbuf, &bp, &captured_len);
    TEST_ASSERT_NOT_NULL_MESSAGE(captured, "Failed to capture output");
    TEST_ASSERT_EQUAL_size_t(msg_len, captured_len);
    TEST_ASSERT_EQUAL_MEMORY(msg, captured, msg_len);
    TEST_ASSERT_EQUAL_PTR(outbuf, bp); /* reset to start */
    free(captured);
}

/* Test: Two independent flushes write their own content and reset bp each time. */
void test_write_pending_multiple_independent_flushes(void)
{
    char outbuf[16];

    /* First flush: "ABC" */
    memcpy(outbuf, "ABC", 3);
    char *bp1 = outbuf + 3;
    size_t len1 = 0;
    char *cap1 = capture_write_pending_output(outbuf, &bp1, &len1);
    TEST_ASSERT_NOT_NULL_MESSAGE(cap1, "Failed to capture output (first)");
    TEST_ASSERT_EQUAL_size_t(3, len1);
    TEST_ASSERT_EQUAL_MEMORY("ABC", cap1, 3);
    TEST_ASSERT_EQUAL_PTR(outbuf, bp1);

    /* Second flush: "DEF" */
    memcpy(outbuf, "DEF", 3);
    char *bp2 = outbuf + 3;
    size_t len2 = 0;
    char *cap2 = capture_write_pending_output(outbuf, &bp2, &len2);
    TEST_ASSERT_NOT_NULL_MESSAGE(cap2, "Failed to capture output (second)");
    TEST_ASSERT_EQUAL_size_t(3, len2);
    TEST_ASSERT_EQUAL_MEMORY("DEF", cap2, 3);
    TEST_ASSERT_EQUAL_PTR(outbuf, bp2);

    /* Combined logical result should be concatenation if viewed together. */
    TEST_ASSERT_EQUAL_size_t(3, len1);
    TEST_ASSERT_EQUAL_size_t(3, len2);

    free(cap1);
    free(cap2);
}

/* Test: A moderately large write (8 KiB) succeeds and resets the pointer. */
void test_write_pending_large_payload(void)
{
    const size_t N = 8192; /* keep below typical pipe capacity to avoid blocking */
    char *outbuf = (char*)malloc(N);
    TEST_ASSERT_NOT_NULL(outbuf);

    for (size_t i = 0; i < N; i++)
        outbuf[i] = (char)('a' + (i % 26));

    char *bp = outbuf + N;

    size_t captured_len = 0;
    char *captured = capture_write_pending_output(outbuf, &bp, &captured_len);
    TEST_ASSERT_NOT_NULL_MESSAGE(captured, "Failed to capture output");
    TEST_ASSERT_EQUAL_size_t(N, captured_len);
    TEST_ASSERT_EQUAL_MEMORY(outbuf, captured, N);
    TEST_ASSERT_EQUAL_PTR(outbuf, bp);

    free(captured);
    free(outbuf);
}

int main(void)
{
    UNITY_BEGIN();
    RUN_TEST(test_write_pending_no_pending_data);
    RUN_TEST(test_write_pending_writes_and_resets_pointer);
    RUN_TEST(test_write_pending_multiple_independent_flushes);
    RUN_TEST(test_write_pending_large_payload);
    return UNITY_END();
}