|
|
#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> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void tests_reset_pending(int fd) |
|
|
{ |
|
|
off_t rem = cache_round(fd, 0); |
|
|
if (rem != 0) |
|
|
{ |
|
|
|
|
|
off_t need = (off_t)IO_BUFSIZE - rem; |
|
|
(void) cache_round(fd, need); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static int tests_make_temp_file(off_t size) |
|
|
{ |
|
|
char tmpl[] = "/tmp/dd_invalidate_cache_testXXXXXX"; |
|
|
int fd = mkstemp(tmpl); |
|
|
if (fd >= 0) |
|
|
{ |
|
|
|
|
|
unlink(tmpl); |
|
|
if (size > 0) |
|
|
{ |
|
|
if (ftruncate(fd, size) != 0) |
|
|
{ |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
return fd; |
|
|
} |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
static void tests_restore_stdout(int saved) |
|
|
{ |
|
|
if (saved >= 0) |
|
|
{ |
|
|
(void) dup2(saved, STDOUT_FILENO); |
|
|
close(saved); |
|
|
} |
|
|
} |
|
|
|
|
|
void setUp(void) |
|
|
{ |
|
|
tests_set_pagesize(); |
|
|
|
|
|
i_nocache_eof = false; |
|
|
o_nocache_eof = false; |
|
|
input_seekable = true; |
|
|
input_offset = 0; |
|
|
|
|
|
tests_reset_pending(STDIN_FILENO); |
|
|
tests_reset_pending(STDOUT_FILENO); |
|
|
} |
|
|
|
|
|
void tearDown(void) |
|
|
{ |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_invalidate_cache_stdin_small_len_no_advise(void) |
|
|
{ |
|
|
tests_reset_pending(STDIN_FILENO); |
|
|
input_seekable = true; |
|
|
input_offset = 12345; |
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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"); |
|
|
|
|
|
|
|
|
bool err = false; |
|
|
const char *err_msg = NULL; |
|
|
|
|
|
|
|
|
tests_reset_pending(STDOUT_FILENO); |
|
|
if (cache_round(STDOUT_FILENO, 0) != 0) |
|
|
{ err = true; err_msg = "Pending not reset to 0"; } |
|
|
|
|
|
|
|
|
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"; } |
|
|
|
|
|
|
|
|
o_nocache_eof = true; |
|
|
errno = 0; |
|
|
bool r_eof = invalidate_cache(STDOUT_FILENO, (off_t)0); |
|
|
int e_eof = errno; |
|
|
|
|
|
|
|
|
(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"; } |
|
|
|
|
|
|
|
|
errno = 0; |
|
|
bool r_full = invalidate_cache(STDOUT_FILENO, (off_t)IO_BUFSIZE); |
|
|
int e_full = errno; |
|
|
|
|
|
|
|
|
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)"); |
|
|
|
|
|
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 |
|
|
} |
|
|
|
|
|
|
|
|
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(); |
|
|
} |