#include "../../unity/unity.h" #include /* STDIN_FILENO, STDOUT_FILENO */ #include #include #include /* We test the static function cache_round(int fd, off_t len) that is visible here because this test file is included into dd.c. */ /* Helper: read current pending remainder for a given fd without modifying it. */ static off_t get_pending_for_fd(int fd) { return cache_round(fd, 0); } /* Helper: reset the internal pending remainder for a given fd back to 0. */ static void reset_pending_for_fd(int fd) { off_t p = cache_round(fd, 0); if (p != 0) { off_t need = (off_t)IO_BUFSIZE - p; /* need is in (0, IO_BUFSIZE-1] when p != 0 */ (void)cache_round(fd, need); /* Now pending should be 0. */ } } /* Helper: assert equality as 64-bit signed integers for portability. */ static void assert_off_t_eq(off_t actual, intmax_t expected) { TEST_ASSERT_EQUAL_INT64((int64_t)expected, (int64_t)actual); } void setUp(void) { /* Ensure both buckets start clean for independent tests. */ reset_pending_for_fd(STDIN_FILENO); reset_pending_for_fd(STDOUT_FILENO); } void tearDown(void) { /* Leave state clean for good hygiene. */ reset_pending_for_fd(STDIN_FILENO); reset_pending_for_fd(STDOUT_FILENO); } /* Basic sanity: IO_BUFSIZE > 0 and initial pending is zero for both fds. */ void test_cache_round_initial_state(void) { TEST_ASSERT(IO_BUFSIZE > 0); off_t p_in = get_pending_for_fd(STDIN_FILENO); off_t p_out = get_pending_for_fd(STDOUT_FILENO); assert_off_t_eq(p_in, 0); assert_off_t_eq(p_out, 0); } /* Accumulate two pieces that sum to exactly one IO_BUFSIZE on STDIN. */ void test_cache_round_accumulate_and_round_stdin(void) { TEST_ASSERT(IO_BUFSIZE > 1); off_t part1 = (off_t)(IO_BUFSIZE / 2); if (part1 == 0) part1 = 1; /* Fallback if IO_BUFSIZE == 1 (shouldn't happen). */ off_t part2 = (off_t)IO_BUFSIZE - part1; off_t r1 = cache_round(STDIN_FILENO, part1); assert_off_t_eq(r1, 0); /* Not enough to round yet. */ assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), part1); off_t r2 = cache_round(STDIN_FILENO, part2); assert_off_t_eq(r2, IO_BUFSIZE); /* Now we've crossed a full buffer. */ assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), 0); /* remainder cleared */ } /* Ensure STDIN and STDOUT buckets are independent. */ void test_cache_round_independent_stdin_stdout(void) { /* Seed STDIN with some remainder. */ off_t r_in1 = cache_round(STDIN_FILENO, 1); assert_off_t_eq(r_in1, 0); assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), 1); /* STDOUT should be unaffected. */ assert_off_t_eq(get_pending_for_fd(STDOUT_FILENO), 0); /* Now operate on STDOUT; it should have its own remainder. */ off_t r_out1 = cache_round(STDOUT_FILENO, (off_t)IO_BUFSIZE + 3); assert_off_t_eq(r_out1, IO_BUFSIZE); assert_off_t_eq(get_pending_for_fd(STDOUT_FILENO), 3); /* Verify STDIN remainder unchanged. */ assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), 1); } /* len == 0 should return the current pending remainder and not modify it. */ void test_cache_round_len_zero_returns_pending(void) { /* Create a known pending remainder for STDIN. */ off_t r = cache_round(STDIN_FILENO, 5); assert_off_t_eq(r, 0); off_t p1 = cache_round(STDIN_FILENO, 0); assert_off_t_eq(p1, 5); /* Calling again should not change it. */ off_t p2 = cache_round(STDIN_FILENO, 0); assert_off_t_eq(p2, 5); } /* Exact multiple input should be returned as-is and remainder cleared. */ void test_cache_round_exact_multiple_returns_len_and_clears_pending(void) { off_t len = (off_t)IO_BUFSIZE * 3; off_t r = cache_round(STDIN_FILENO, len); assert_off_t_eq(r, len); assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), 0); /* Follow-up partial should accumulate fresh. */ off_t r2 = cache_round(STDIN_FILENO, 2); assert_off_t_eq(r2, 0); assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), 2); } /* Overflow path: when pending + len would overflow intmax_t, c_pending becomes INTMAX_MAX; verify return and remainder reflect that value. */ void test_cache_round_overflow_behavior(void) { /* Seed a small pending remainder to force overflow on next addition. */ (void)cache_round(STDIN_FILENO, 1); /* pending := 1 */ assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), 1); /* Now add INTMAX_MAX; ckd_add will overflow and c_pending := INTMAX_MAX. */ off_t r = cache_round(STDIN_FILENO, (off_t)INTMAX_MAX); intmax_t remainder = (intmax_t)(INTMAX_MAX % (intmax_t)IO_BUFSIZE); intmax_t expected_return = (intmax_t)INTMAX_MAX - remainder; assert_off_t_eq(r, expected_return); assert_off_t_eq(get_pending_for_fd(STDIN_FILENO), remainder); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_cache_round_initial_state); RUN_TEST(test_cache_round_accumulate_and_round_stdin); RUN_TEST(test_cache_round_independent_stdin_stdout); RUN_TEST(test_cache_round_len_zero_returns_pending); RUN_TEST(test_cache_round_exact_multiple_returns_len_and_clears_pending); RUN_TEST(test_cache_round_overflow_behavior); return UNITY_END(); }