coreutils / tests /cat /tests_for_write_pending.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#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();
}