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