|
|
#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> |
|
|
|
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
xwrite_stdout(in_buf, n_bytes); |
|
|
|
|
|
|
|
|
fflush(stdout); |
|
|
|
|
|
|
|
|
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'; |
|
|
} else { |
|
|
captured = NULL; |
|
|
} |
|
|
|
|
|
rc = 0; |
|
|
|
|
|
cleanup: |
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
static void test_xwrite_stdout_large_write(void) |
|
|
{ |
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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) { |
|
|
|
|
|
int p[2]; |
|
|
if (pipe(p) != 0) { |
|
|
_exit(127); |
|
|
} |
|
|
|
|
|
fflush(stdout); |
|
|
(void)dup2(p[0], fileno(stdout)); |
|
|
close(p[0]); |
|
|
close(p[1]); |
|
|
|
|
|
|
|
|
xwrite_stdout("X", 1); |
|
|
|
|
|
|
|
|
_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(); |
|
|
} |