File size: 3,996 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
#include "../../unity/unity.h"

#include <errno.h>
#include <sys/stat.h>
#include <stddef.h>
#include <stdint.h>

/*
  Stub machinery to override fstat used by ifstat().
  Since this test file is included into the same translation unit as dd.c,
  defining fstat here will override the libc symbol for references in this TU.
*/

typedef struct {
    int ret;       /* return value for fstat */
    int err;       /* errno to set before returning */
    mode_t mode;   /* st_mode to set on success */
} fstat_plan_t;

static fstat_plan_t g_plan[16];
static int g_plan_len = 0;
static int g_plan_idx = 0;
static int g_fstat_call_count = 0;

static void fstat_stub_reset(void)
{
    g_plan_len = 0;
    g_plan_idx = 0;
    g_fstat_call_count = 0;
}

static void fstat_stub_add(int ret, int err, mode_t mode)
{
    if (g_plan_len < (int)(sizeof g_plan / sizeof g_plan[0])) {
        g_plan[g_plan_len].ret = ret;
        g_plan[g_plan_len].err = err;
        g_plan[g_plan_len].mode = mode;
        g_plan_len++;
    }
}

/* Override fstat/rpl_fstat depending on any macro mapping from system.h.
   If fstat is defined as a macro (e.g., to rpl_fstat), the function
   definition name token below will be macro-expanded accordingly, which is
   what we want so that ifstat() calls land here. */
int fstat(int fd, struct stat *st)
{
    (void)fd; /* Unused in stub */
    g_fstat_call_count++;

    /* Default behavior if plan exhausted: succeed with a generic mode. */
    int ret = 0;
    int err = 0;
    mode_t mode = (mode_t) (S_IFREG | 0644);

    if (g_plan_idx < g_plan_len) {
        ret = g_plan[g_plan_idx].ret;
        err = g_plan[g_plan_idx].err;
        mode = g_plan[g_plan_idx].mode;
        g_plan_idx++;
    }

    errno = err;
    if (ret == 0 && st) {
        /* Set only minimal field we care about. */
        st->st_mode = mode;
    }
    return ret;
}

/* Unity setUp/tearDown */
void setUp(void)
{
    fstat_stub_reset();
    errno = 0;
}

void tearDown(void)
{
    /* Nothing to do */
}

/* Tests */

void test_ifstat_success_immediate(void)
{
    struct stat st;
    st.st_mode = 0;
    mode_t expect_mode = (mode_t)(S_IFREG | 0600);

    fstat_stub_add(0, 0, expect_mode);

    int rc = ifstat(123, &st);
    TEST_ASSERT_EQUAL_INT(0, rc);
    TEST_ASSERT_EQUAL_INT(expect_mode, st.st_mode);
    TEST_ASSERT_EQUAL_INT(1, g_fstat_call_count);
}

void test_ifstat_retries_once_on_EINTR_then_success(void)
{
    struct stat st;
    st.st_mode = 0;
    mode_t expect_mode = (mode_t)(S_IFDIR | 0755);

    fstat_stub_add(-1, EINTR, 0);           /* first call interrupted */
    fstat_stub_add(0, 0, expect_mode);      /* second call succeeds */

    int rc = ifstat(5, &st);
    TEST_ASSERT_EQUAL_INT(0, rc);
    TEST_ASSERT_EQUAL_INT(expect_mode, st.st_mode);
    TEST_ASSERT_EQUAL_INT(2, g_fstat_call_count);
}

void test_ifstat_retries_multiple_EINTR_then_success(void)
{
    struct stat st;
    st.st_mode = 0;
    mode_t expect_mode = (mode_t)(S_IFIFO | 0640);

    fstat_stub_add(-1, EINTR, 0);
    fstat_stub_add(-1, EINTR, 0);
    fstat_stub_add(-1, EINTR, 0);
    fstat_stub_add(0, 0, expect_mode);

    int rc = ifstat(7, &st);
    TEST_ASSERT_EQUAL_INT(0, rc);
    TEST_ASSERT_EQUAL_INT(expect_mode, st.st_mode);
    TEST_ASSERT_EQUAL_INT(4, g_fstat_call_count);
}

void test_ifstat_non_EINTR_error_propagates_no_retry(void)
{
    struct stat st;
    st.st_mode = 0xDEAD; /* sentinel to ensure untouched on error */

    fstat_stub_add(-1, ENOENT, 0);

    errno = 0;
    int rc = ifstat(9, &st);
    TEST_ASSERT_EQUAL_INT(-1, rc);
    TEST_ASSERT_EQUAL_INT(ENOENT, errno);
    TEST_ASSERT_EQUAL_INT(1, g_fstat_call_count);
    TEST_ASSERT_EQUAL_INT(0xDEAD, st.st_mode);
}

int main(void)
{
    UNITY_BEGIN();
    RUN_TEST(test_ifstat_success_immediate);
    RUN_TEST(test_ifstat_retries_once_on_EINTR_then_success);
    RUN_TEST(test_ifstat_retries_multiple_EINTR_then_success);
    RUN_TEST(test_ifstat_non_EINTR_error_propagates_no_retry);
    return UNITY_END();
}