File size: 6,814 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 |
#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();
} |