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