| #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(); |
| } |