coreutils / tests /dd /tests_for_invalidate_cache.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
/* The test file is included within dd.c, so we can access its internal
symbols and helper functions/state directly (e.g., cache_round, globals). */
/* Helper: get system page size into the program's global page_size. */
static void tests_set_pagesize(void)
{
if (page_size == 0)
{
#if defined(_SC_PAGESIZE)
long ps = sysconf(_SC_PAGESIZE);
if (ps <= 0)
ps = 4096;
page_size = (idx_t) ps;
#else
page_size = 4096;
#endif
}
}
/* Helper: reset cache_round pending for a given fd to 0 without invoking
invalidate_cache (so no syscalls or output_offset changes). */
static void tests_reset_pending(int fd)
{
off_t rem = cache_round(fd, 0);
if (rem != 0)
{
/* Bring the accumulated length up to the next IO_BUFSIZE multiple. */
off_t need = (off_t)IO_BUFSIZE - rem;
(void) cache_round(fd, need);
}
}
/* Helper: create an anonymous temporary file, sized to multiple pages. */
static int tests_make_temp_file(off_t size)
{
char tmpl[] = "/tmp/dd_invalidate_cache_testXXXXXX";
int fd = mkstemp(tmpl);
if (fd >= 0)
{
/* Unlink so it is removed when closed. */
unlink(tmpl);
if (size > 0)
{
if (ftruncate(fd, size) != 0)
{
/* Best effort; ignore error. */
}
}
}
return fd;
}
/* Helper: redirect stdout to fd; returns saved original stdout fd via *saved. */
static int tests_redirect_stdout(int new_fd, int *saved)
{
int s = dup(STDOUT_FILENO);
if (s < 0)
return -1;
if (dup2(new_fd, STDOUT_FILENO) < 0)
{
close(s);
return -1;
}
*saved = s;
return 0;
}
/* Helper: restore stdout from saved fd. */
static void tests_restore_stdout(int saved)
{
if (saved >= 0)
{
(void) dup2(saved, STDOUT_FILENO);
close(saved);
}
}
void setUp(void)
{
tests_set_pagesize();
/* Provide sane defaults for globals used by invalidate_cache */
i_nocache_eof = false;
o_nocache_eof = false;
input_seekable = true;
input_offset = 0;
/* Reset rounding state for both stdin and stdout. */
tests_reset_pending(STDIN_FILENO);
tests_reset_pending(STDOUT_FILENO);
}
void tearDown(void)
{
/* Nothing persistent to clean. */
}
/* 1) Small len on stdin should early-return true (no advisory), and
advance internal pending by len. */
void test_invalidate_cache_stdin_small_len_no_advise(void)
{
tests_reset_pending(STDIN_FILENO);
input_seekable = true;
input_offset = 12345; /* Arbitrary non-negative; shouldn't be used here. */
off_t before = cache_round(STDIN_FILENO, 0);
TEST_ASSERT_EQUAL_INT64(0, (long long)before);
bool ret = invalidate_cache(STDIN_FILENO, (off_t)1);
TEST_ASSERT_TRUE(ret);
off_t after = cache_round(STDIN_FILENO, 0);
TEST_ASSERT_EQUAL_INT64(1, (long long)after);
}
/* 2) len==0, no pending, and nocache_eof==false => early return true. */
void test_invalidate_cache_stdout_noop_eof_when_nocache_eof_false(void)
{
tests_reset_pending(STDOUT_FILENO);
o_nocache_eof = false;
bool ret = invalidate_cache(STDOUT_FILENO, (off_t)0);
TEST_ASSERT_TRUE(ret);
}
/* 3) On stdout backed by a regular file:
- accumulate a non-zero pending via a small len (no advisory expected),
- with o_nocache_eof=true and len==0, we expect an advisory attempt,
returning true if posix_fadvise is available, else false/ENOTSUP,
- then perform a full-chunk advisory with len==IO_BUFSIZE. */
void test_invalidate_cache_stdout_eof_pending_and_full_chunk(void)
{
int tmpfd = tests_make_temp_file((off_t)(page_size * 4));
TEST_ASSERT_MESSAGE(tmpfd >= 0, "Failed to create temporary file");
int saved_out = -1;
int redir = tests_redirect_stdout(tmpfd, &saved_out);
TEST_ASSERT_MESSAGE(redir == 0, "Failed to redirect stdout");
/* Do not use Unity asserts while stdout is redirected beyond this point. */
bool err = false;
const char *err_msg = NULL;
/* Ensure pending==0 for stdout. */
tests_reset_pending(STDOUT_FILENO);
if (cache_round(STDOUT_FILENO, 0) != 0)
{ err = true; err_msg = "Pending not reset to 0"; }
/* Small len: should early-return true and set pending to IO_BUFSIZE-1. */
bool r_small = invalidate_cache(STDOUT_FILENO, (off_t)(IO_BUFSIZE - 1));
off_t pend1 = cache_round(STDOUT_FILENO, 0);
if (!r_small || pend1 != (off_t)(IO_BUFSIZE - 1))
{ err = true; err_msg = "Small len did not set pending as expected"; }
/* EOF advisory with nocache_eof=true and non-zero pending. */
o_nocache_eof = true;
errno = 0;
bool r_eof = invalidate_cache(STDOUT_FILENO, (off_t)0);
int e_eof = errno;
/* Clear pending back to 0 via direct rounding (no advisory). */
(void) cache_round(STDOUT_FILENO, (off_t)1);
if (cache_round(STDOUT_FILENO, 0) != 0)
{ err = true; err_msg = "Failed to clear pending to 0"; }
/* Full-chunk advisory. */
errno = 0;
bool r_full = invalidate_cache(STDOUT_FILENO, (off_t)IO_BUFSIZE);
int e_full = errno;
/* Restore stdout before any Unity asserts. */
tests_restore_stdout(saved_out);
close(tmpfd);
if (err)
TEST_FAIL_MESSAGE(err_msg);
#if HAVE_POSIX_FADVISE
TEST_ASSERT_TRUE_MESSAGE(r_eof, "EOF nocache advisory should succeed (posix_fadvise)");
TEST_ASSERT_TRUE_MESSAGE(r_full, "Full-chunk advisory should succeed (posix_fadvise)");
/* On success, coreutils sets errno to adv_ret (0). */
TEST_ASSERT_EQUAL_INT_MESSAGE(0, e_eof, "errno not 0 after EOF advisory");
TEST_ASSERT_EQUAL_INT_MESSAGE(0, e_full, "errno not 0 after full advisory");
#else
TEST_ASSERT_FALSE_MESSAGE(r_eof, "EOF nocache advisory should fail without posix_fadvise");
TEST_ASSERT_FALSE_MESSAGE(r_full, "Full-chunk advisory should fail without posix_fadvise");
TEST_ASSERT_EQUAL_INT_MESSAGE(ENOTSUP, e_eof, "errno should be ENOTSUP without posix_fadvise (EOF)");
TEST_ASSERT_EQUAL_INT_MESSAGE(ENOTSUP, e_full, "errno should be ENOTSUP without posix_fadvise (full)");
#endif
}
/* 4) stdin not seekable: expect false and errno=ESPIPE when len yields clen>0. */
void test_invalidate_cache_stdin_not_seekable(void)
{
tests_reset_pending(STDIN_FILENO);
input_seekable = false;
errno = 0;
bool ret = invalidate_cache(STDIN_FILENO, (off_t)IO_BUFSIZE);
int e = errno;
TEST_ASSERT_FALSE(ret);
TEST_ASSERT_EQUAL_INT(ESPIPE, e);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_invalidate_cache_stdin_small_len_no_advise);
RUN_TEST(test_invalidate_cache_stdout_noop_eof_when_nocache_eof_false);
RUN_TEST(test_invalidate_cache_stdout_eof_pending_and_full_chunk);
RUN_TEST(test_invalidate_cache_stdin_not_seekable);
return UNITY_END();
}