File size: 5,411 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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include "../../unity/unity.h"
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

/* The test file is included into the dd translation unit.
   We can access static globals like conversions_mask and
   the static function synchronize_output(void) directly. */

/* Forward declaration of the target (already defined above in the TU). */
static int synchronize_output (void);

/* Access the conversions_mask and flag bits defined in dd.c. */
extern int conversions_mask;        /* visible within the same TU due to inclusion */

/* Flag values should be visible as macros/enum values. */
/* C_FDATASYNC and C_FSYNC are defined in dd.c's enum. */

/* Helpers for stdout redirection. Avoid Unity assertions while redirected. */

static int save_and_redirect_stdout_to_fd(int target_fd, int *saved_fd_out)
{
    if (!saved_fd_out) return -1;
    int saved = dup(STDOUT_FILENO);
    if (saved < 0) return -1;
    if (dup2(target_fd, STDOUT_FILENO) < 0) {
        close(saved);
        return -1;
    }
    *saved_fd_out = saved;
    return 0;
}

static int redirect_stdout_to_tempfile(int *saved_fd_out)
{
    char tmpl[] = "/tmp/unity_dd_XXXXXX";
    int tmpfd = mkstemp(tmpl);
    if (tmpfd < 0)
        return -1;
    /* Unlink so it is removed automatically after close. */
    unlink(tmpl);

    int rc = save_and_redirect_stdout_to_fd(tmpfd, saved_fd_out);
    /* tmpfd now duplicated to STDOUT_FILENO; close original. */
    close(tmpfd);
    return rc;
}

static int redirect_stdout_to_pipe(int *saved_fd_out, int *pipe_read_end_out)
{
    int fds[2];
    if (pipe(fds) != 0)
        return -1;
    /* fds[1] will be new stdout; keep read end around only to close later. */
    int rc = save_and_redirect_stdout_to_fd(fds[1], saved_fd_out);
    /* Close the original write end fd; STDOUT now refers to it. */
    close(fds[1]);
    if (rc != 0) {
        close(fds[0]);
        return -1;
    }
    if (pipe_read_end_out)
        *pipe_read_end_out = fds[0];
    else
        close(fds[0]);
    return 0;
}

static void restore_stdout(int saved_fd)
{
    if (saved_fd >= 0) {
        dup2(saved_fd, STDOUT_FILENO);
        close(saved_fd);
    }
}

void setUp(void) {
    /* Ensure a known state before each test. */
    conversions_mask = 0;
}

void tearDown(void) {
    /* Restore global state if needed. */
    conversions_mask = 0;
}

static void test_synchronize_output_no_flags_returns_success_and_no_change(void)
{
    conversions_mask = 0;
    int ret = synchronize_output();
    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_INT(0, conversions_mask);
}

static void test_synchronize_output_fdatasync_success_clears_and_returns_success(void)
{
    /* Redirect stdout to a regular file so fdatasync/fsync succeed. */
    int saved = -1;
    TEST_ASSERT_EQUAL_INT(0, redirect_stdout_to_tempfile(&saved));

    conversions_mask = C_FDATASYNC;
    int ret = synchronize_output();

    restore_stdout(saved);

    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_INT(0, conversions_mask);
}

static void test_synchronize_output_fsync_success_clears_and_returns_success(void)
{
    int saved = -1;
    TEST_ASSERT_EQUAL_INT(0, redirect_stdout_to_tempfile(&saved));

    conversions_mask = C_FSYNC;
    int ret = synchronize_output();

    restore_stdout(saved);

    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_INT(0, conversions_mask);
}

static void test_synchronize_output_both_flags_success(void)
{
    int saved = -1;
    TEST_ASSERT_EQUAL_INT(0, redirect_stdout_to_tempfile(&saved));

    conversions_mask = C_FDATASYNC | C_FSYNC;
    int ret = synchronize_output();

    restore_stdout(saved);

    TEST_ASSERT_EQUAL_INT(0, ret);
    TEST_ASSERT_EQUAL_INT(0, conversions_mask);
}

static void test_synchronize_output_fdatasync_on_pipe_triggers_fsync_and_fails(void)
{
    /* On a pipe, fdatasync is expected to fail with EINVAL. The code then
       also tries fsync, which also fails on a pipe -> overall failure. */
    int saved = -1;
    int read_end = -1;
    TEST_ASSERT_EQUAL_INT(0, redirect_stdout_to_pipe(&saved, &read_end));

    conversions_mask = C_FDATASYNC;
    int ret = synchronize_output();

    /* Clean up: restore and close pipe read end. */
    restore_stdout(saved);
    if (read_end >= 0) close(read_end);

    TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, ret);
    TEST_ASSERT_EQUAL_INT(0, conversions_mask);
}

static void test_synchronize_output_fsync_on_pipe_fails(void)
{
    int saved = -1;
    int read_end = -1;
    TEST_ASSERT_EQUAL_INT(0, redirect_stdout_to_pipe(&saved, &read_end));

    conversions_mask = C_FSYNC;
    int ret = synchronize_output();

    restore_stdout(saved);
    if (read_end >= 0) close(read_end);

    TEST_ASSERT_EQUAL_INT(EXIT_FAILURE, ret);
    TEST_ASSERT_EQUAL_INT(0, conversions_mask);
}

int main(void)
{
    UNITY_BEGIN();

    RUN_TEST(test_synchronize_output_no_flags_returns_success_and_no_change);
    RUN_TEST(test_synchronize_output_fdatasync_success_clears_and_returns_success);
    RUN_TEST(test_synchronize_output_fsync_success_clears_and_returns_success);
    RUN_TEST(test_synchronize_output_both_flags_success);
    RUN_TEST(test_synchronize_output_fdatasync_on_pipe_triggers_fsync_and_fails);
    RUN_TEST(test_synchronize_output_fsync_on_pipe_fails);

    return UNITY_END();
}