#include "../../unity/unity.h" #include #include #include #include #include #include #include #include #include /* 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(); }