File size: 4,157 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include "../../unity/unity.h"
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

/*
  Test strategy:
  - Provide a mock fsync() in this translation unit to control return
    values and errno across calls.
  - Call the target static function ifsync(int fd) directly.
  - Verify that ifsync retries on EINTR and returns appropriately on
    success or on non-EINTR errors.
  - Ensure only the target function behavior is tested.
*/

/* Mock machinery for fsync */
typedef struct {
    int ret; /* return value for fsync */
    int err; /* errno to set for fsync */
} mock_fsync_step_t;

static mock_fsync_step_t mock_fsync_steps[64];
static int mock_fsync_len = 0;
static int mock_fsync_pos = 0;
static int mock_fsync_calls = 0;

static void mock_fsync_reset(void)
{
    mock_fsync_len = 0;
    mock_fsync_pos = 0;
    mock_fsync_calls = 0;
}

static void mock_fsync_set_steps(const mock_fsync_step_t *steps, int len)
{
    if (len > (int)(sizeof(mock_fsync_steps) / sizeof(mock_fsync_steps[0])))
        len = (int)(sizeof(mock_fsync_steps) / sizeof(mock_fsync_steps[0]));
    for (int i = 0; i < len; i++)
        mock_fsync_steps[i] = steps[i];
    mock_fsync_len = len;
    mock_fsync_pos = 0;
    mock_fsync_calls = 0;
}

/*
  Mocked fsync used by ifsync(). Since this test file is included
  in the same translation unit after the dd source, this definition
  will satisfy references to fsync within this TU for testing.
*/
int fsync(int fd)
{
    (void)fd; /* Not used in mock */
    mock_fsync_calls++;
    if (mock_fsync_pos < mock_fsync_len) {
        int r = mock_fsync_steps[mock_fsync_pos].ret;
        errno = mock_fsync_steps[mock_fsync_pos].err;
        mock_fsync_pos++;
        return r;
    }
    /* Default fall-through if sequence exhausted: succeed */
    errno = 0;
    return 0;
}

void setUp(void)
{
    mock_fsync_reset();
}

void tearDown(void)
{
    /* nothing */
}

/* Tests */

/* Immediate success: ifsync should return 0 and call fsync once. */
void test_ifsync_immediate_success(void)
{
    mock_fsync_step_t seq[] = {
        { 0, 0 }
    };
    mock_fsync_set_steps(seq, 1);

    int rc = ifsync(123);
    TEST_ASSERT_EQUAL_INT(0, rc);
    TEST_ASSERT_EQUAL_INT(1, mock_fsync_calls);
}

/* One EINTR then success: ifsync should retry and return 0; two calls total. */
void test_ifsync_eintr_then_success(void)
{
    mock_fsync_step_t seq[] = {
        { -1, EINTR },
        { 0, 0 }
    };
    mock_fsync_set_steps(seq, 2);

    int rc = ifsync(5);
    TEST_ASSERT_EQUAL_INT(0, rc);
    TEST_ASSERT_EQUAL_INT(2, mock_fsync_calls);
}

/* Multiple EINTRs then success: ensure loop continues appropriately. */
void test_ifsync_multiple_eintr_then_success(void)
{
    mock_fsync_step_t seq[] = {
        { -1, EINTR },
        { -1, EINTR },
        { -1, EINTR },
        { 0, 0 }
    };
    mock_fsync_set_steps(seq, 4);

    int rc = ifsync(7);
    TEST_ASSERT_EQUAL_INT(0, rc);
    TEST_ASSERT_EQUAL_INT(4, mock_fsync_calls);
}

/* Immediate non-EINTR failure: ifsync should return -1 and not retry. */
void test_ifsync_immediate_non_eintr_failure(void)
{
    mock_fsync_step_t seq[] = {
        { -1, EIO }
    };
    mock_fsync_set_steps(seq, 1);

    errno = 0;
    int rc = ifsync(9);
    TEST_ASSERT_EQUAL_INT(-1, rc);
    TEST_ASSERT_EQUAL_INT(EIO, errno);
    TEST_ASSERT_EQUAL_INT(1, mock_fsync_calls);
}

/* EINTR followed by non-EINTR failure: should retry once then return -1. */
void test_ifsync_eintr_then_non_eintr_failure(void)
{
    mock_fsync_step_t seq[] = {
        { -1, EINTR },
        { -1, EFAULT }
    };
    mock_fsync_set_steps(seq, 2);

    errno = 0;
    int rc = ifsync(11);
    TEST_ASSERT_EQUAL_INT(-1, rc);
    TEST_ASSERT_EQUAL_INT(EFAULT, errno);
    TEST_ASSERT_EQUAL_INT(2, mock_fsync_calls);
}

int main(void)
{
    UNITY_BEGIN();
    RUN_TEST(test_ifsync_immediate_success);
    RUN_TEST(test_ifsync_eintr_then_success);
    RUN_TEST(test_ifsync_multiple_eintr_then_success);
    RUN_TEST(test_ifsync_immediate_non_eintr_failure);
    RUN_TEST(test_ifsync_eintr_then_non_eintr_failure);
    return UNITY_END();
}