File size: 5,538 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 |
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
/* Unity hooks */
void setUp(void) {
/* no-op */
}
void tearDown(void) {
/* no-op */
}
/* Helper: capture what xwrite_stdout writes to stdout into a malloc'd buffer.
Returns 0 on success, nonzero on failure to set up capture.
On success, *out_buf is allocated (may be NULL if out_len==0),
and *out_len is set to number of captured bytes. Caller must free(*out_buf). */
static int capture_xwrite_stdout_output(const char *in_buf, size_t n_bytes,
char **out_buf, size_t *out_len)
{
int rc = -1;
int saved_stdout = -1;
FILE *tmp = NULL;
int tmpfd = -1;
char *captured = NULL;
size_t captured_len = 0;
if (!out_buf || !out_len)
return -1;
*out_buf = NULL;
*out_len = 0;
fflush(stdout);
saved_stdout = dup(fileno(stdout));
if (saved_stdout < 0)
goto cleanup;
tmp = tmpfile();
if (!tmp)
goto cleanup;
tmpfd = fileno(tmp);
if (tmpfd < 0)
goto cleanup;
if (dup2(tmpfd, fileno(stdout)) < 0)
goto cleanup;
/* Perform the write while stdout is redirected. Do not call Unity here. */
xwrite_stdout(in_buf, n_bytes);
/* Flush redirected stdout and read back the data. */
fflush(stdout);
/* Determine size */
long end_pos;
if (fseek(tmp, 0, SEEK_END) != 0)
goto cleanup;
end_pos = ftell(tmp);
if (end_pos < 0)
goto cleanup;
captured_len = (size_t)end_pos;
if (fseek(tmp, 0, SEEK_SET) != 0)
goto cleanup;
if (captured_len > 0) {
captured = (char *)malloc(captured_len + 1);
if (!captured)
goto cleanup;
size_t nread = fread(captured, 1, captured_len, tmp);
if (nread != captured_len)
goto cleanup;
captured[captured_len] = '\0'; /* for convenience; content may be binary */
} else {
captured = NULL; /* nothing written */
}
rc = 0;
cleanup:
/* Restore stdout before any assertions by the caller. */
if (saved_stdout >= 0) {
fflush(stdout);
dup2(saved_stdout, fileno(stdout));
close(saved_stdout);
saved_stdout = -1;
}
if (tmp) {
fclose(tmp);
tmp = NULL;
}
if (rc == 0) {
*out_buf = captured;
*out_len = captured_len;
} else {
free(captured);
}
return rc;
}
/* Test: writing non-zero data writes exactly those bytes */
static void test_xwrite_stdout_writes_expected_bytes(void)
{
const char *msg = "Hello, world!";
char *out = NULL;
size_t out_len = 0;
int rc = capture_xwrite_stdout_output(msg, 13, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_TRUE(out_len == 13);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_INT(0, memcmp(out, msg, 13));
free(out);
}
/* Test: n_bytes == 0 should not write anything (non-null buffer) */
static void test_xwrite_stdout_zero_len_writes_nothing(void)
{
const char *msg = "ABC";
char *out = NULL;
size_t out_len = 0;
int rc = capture_xwrite_stdout_output(msg, 0, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_TRUE(out_len == 0);
TEST_ASSERT_NULL(out);
}
/* Test: n_bytes == 0 with NULL buffer should be a no-op and not crash */
static void test_xwrite_stdout_null_buffer_and_zero_len_safe(void)
{
char *out = NULL;
size_t out_len = 0;
int rc = capture_xwrite_stdout_output(NULL, 0, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_TRUE(out_len == 0);
TEST_ASSERT_NULL(out);
}
/* Test: large write (larger than BUFSIZ) is fully written */
static void test_xwrite_stdout_large_write(void)
{
/* Create a large buffer */
size_t len = (size_t)BUFSIZ * 3 + 17;
char *in = (char *)malloc(len);
TEST_ASSERT_NOT_NULL(in);
for (size_t i = 0; i < len; i++) {
in[i] = (char)('A' + (i % 26));
}
char *out = NULL;
size_t out_len = 0;
int rc = capture_xwrite_stdout_output(in, len, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_TRUE(out_len == len);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_INT(0, memcmp(out, in, len));
free(in);
free(out);
}
/* Test: on write error, xwrite_stdout should terminate with EXIT_FAILURE.
We simulate error by redirecting stdout in a child process to a non-writable FD. */
static void test_xwrite_stdout_when_write_fails_exits_with_failure(void)
{
fflush(stdout);
fflush(stderr);
pid_t pid = fork();
TEST_ASSERT_TRUE(pid >= 0);
if (pid == 0) {
/* Child: make stdout unwritable by dup'ing the read end of a pipe onto it. */
int p[2];
if (pipe(p) != 0) {
_exit(127);
}
/* p[0] is read end (not writable); p[1] is write end */
fflush(stdout);
(void)dup2(p[0], fileno(stdout));
close(p[0]);
close(p[1]);
/* This should fail to fwrite and thus call error(EXIT_FAILURE, ...) */
xwrite_stdout("X", 1);
/* We should never reach here; if we do, indicate unexpected success */
_exit(0);
} else {
int status = 0;
pid_t w = waitpid(pid, &status, 0);
TEST_ASSERT_TRUE(w == pid);
TEST_ASSERT_TRUE(WIFEXITED(status));
TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, WEXITSTATUS(status));
}
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_xwrite_stdout_writes_expected_bytes);
RUN_TEST(test_xwrite_stdout_zero_len_writes_nothing);
RUN_TEST(test_xwrite_stdout_null_buffer_and_zero_len_safe);
RUN_TEST(test_xwrite_stdout_large_write);
RUN_TEST(test_xwrite_stdout_when_write_fails_exits_with_failure);
return UNITY_END();
} |