File size: 5,254 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 |
#include "../../unity/unity.h"
#include <unistd.h> /* STDIN_FILENO, STDOUT_FILENO */
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>
/* 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();
} |