#include "../../unity/unity.h" #include #include #include #include #include #include #include /* The test file is included inside dd.c after all internal definitions, so we can access the static variables and functions declared there. */ extern int optind; /* ensure we can set optind */ /* Forward declarations of static functions from dd.c we need to compare. */ static ssize_t iread (int fd, char *buf, idx_t size); static ssize_t iread_fullblock (int fd, char *buf, idx_t size); /* Helper to reset the dd global state between tests. */ static void reset_dd_state(void) { /* Filenames */ input_file = NULL; output_file = NULL; /* Block sizes */ input_blocksize = 0; output_blocksize = 0; conversion_blocksize = 0; /* Skips/Seeks/Counts */ skip_records = 0; skip_bytes = 0; seek_records = 0; seek_bytes = 0; max_records = INTMAX_MAX; max_bytes = 0; /* Flags and conversions */ conversions_mask = 0; input_flags = 0; output_flags = 0; status_level = STATUS_DEFAULT; /* Read path */ warn_partial_read = false; i_nocache = false; o_nocache = false; i_nocache_eof = false; o_nocache_eof = false; iread_fnc = NULL; /* Misc counters/output settings (not strictly needed by scanargs but reset anyway) */ w_partial = 0; w_full = 0; r_partial = 0; r_full = 0; w_bytes = 0; reported_w_bytes = -1; progress_len = 0; r_truncate = 0; translation_needed = false; newline_character = '\n'; space_character = ' '; } void setUp(void) { /* Setup before each test */ } void tearDown(void) { /* Cleanup after each test */ } /* Tests */ void test_scanargs_defaults_sets_twobufs_and_defaults(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char *argv[] = { arg0 }; int argc = 1; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) != 0, "Expected C_TWOBUFS when bs is not specified"); TEST_ASSERT_EQUAL_INT(DEFAULT_BLOCKSIZE, (int)input_blocksize); TEST_ASSERT_EQUAL_INT(DEFAULT_BLOCKSIZE, (int)output_blocksize); TEST_ASSERT_EQUAL_INT(STATUS_DEFAULT, status_level); TEST_ASSERT_EQUAL_PTR_MESSAGE((void*)iread, (void*)iread_fnc, "Default iread_fnc should be iread"); } void test_scanargs_bs_sets_both_block_sizes_and_no_twobufs(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "bs=1024"; char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_EQUAL_INT(1024, (int)input_blocksize); TEST_ASSERT_EQUAL_INT(1024, (int)output_blocksize); TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) == 0, "bs should suppress implicit C_TWOBUFS"); } void test_scanargs_ibs_and_obs_set_independently_and_twobufs_added(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "ibs=4096"; char arg2[] = "obs=8192"; char *argv[] = { arg0, arg1, arg2 }; int argc = 3; scanargs(argc, argv); TEST_ASSERT_EQUAL_INT(4096, (int)input_blocksize); TEST_ASSERT_EQUAL_INT(8192, (int)output_blocksize); TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) != 0, "ibs/obs without bs should add C_TWOBUFS"); } void test_scanargs_iflag_fullblock_sets_fullblock_reader(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "iflag=fullblock"; char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_EQUAL_PTR((void*)iread_fullblock, (void*)iread_fnc); TEST_ASSERT_TRUE_MESSAGE((input_flags & O_FULLBLOCK) == 0, "O_FULLBLOCK should be cleared after setting iread_fnc"); } void test_scanargs_skip_with_B_suffix_splits_into_records_and_bytes(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "ibs=4"; char arg2[] = "skip=10B"; char *argv[] = { arg0, arg1, arg2 }; int argc = 3; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE((input_flags & O_SKIP_BYTES) != 0, "O_SKIP_BYTES should be set when skip uses B suffix"); TEST_ASSERT_EQUAL_INT(2, (int)skip_records); TEST_ASSERT_EQUAL_INT(2, (int)skip_bytes); } void test_scanargs_skip_without_B_suffix_sets_records_only(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "skip=3"; char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE((input_flags & O_SKIP_BYTES) == 0, "O_SKIP_BYTES should not be set without B suffix"); TEST_ASSERT_EQUAL_INT(3, (int)skip_records); TEST_ASSERT_EQUAL_INT(0, (int)skip_bytes); } void test_scanargs_count_with_B_suffix_splits_into_records_and_bytes(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "ibs=10"; char arg2[] = "count=25B"; char *argv[] = { arg0, arg1, arg2 }; int argc = 3; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE((input_flags & O_COUNT_BYTES) != 0, "O_COUNT_BYTES should be set when count uses B suffix"); TEST_ASSERT_EQUAL_INT(2, (int)max_records); TEST_ASSERT_EQUAL_INT(5, (int)max_bytes); } void test_scanargs_count_without_B_suffix_sets_records_only(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "count=7"; char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE((input_flags & O_COUNT_BYTES) == 0, "O_COUNT_BYTES should not be set without B suffix"); TEST_ASSERT_EQUAL_INT(7, (int)max_records); TEST_ASSERT_EQUAL_INT(0, (int)max_bytes); } void test_scanargs_seek_with_B_suffix_splits_into_records_and_bytes(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "obs=8"; char arg2[] = "seek=17B"; char *argv[] = { arg0, arg1, arg2 }; int argc = 3; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE((output_flags & O_SEEK_BYTES) != 0, "O_SEEK_BYTES should be set when seek uses B suffix"); TEST_ASSERT_EQUAL_INT(2, (int)seek_records); TEST_ASSERT_EQUAL_INT(1, (int)seek_bytes); } void test_scanargs_status_progress_and_exclusive_behavior(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "status=none,progress"; /* last wins (exclusive) */ char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_EQUAL_INT(STATUS_PROGRESS, status_level); } void test_scanargs_conv_ucase_and_swab_sets_bits_and_twobufs(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "conv=ucase,swab"; char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_UCASE) != 0, "C_UCASE should be set"); TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_SWAB) != 0, "C_SWAB should be set"); TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) != 0, "C_TWOBUFS should be set (swab implies, and bs not set adds too)"); } void test_scanargs_iflag_nocache_sets_internal_flags_and_clears_input_flag(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "iflag=nocache"; char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_TRUE(i_nocache); TEST_ASSERT_FALSE(i_nocache_eof); /* default max_records!=0 */ TEST_ASSERT_TRUE_MESSAGE((input_flags & O_NOCACHE) == 0, "O_NOCACHE should be cleared from input_flags"); } void test_scanargs_oflag_nocache_sets_internal_flags_and_clears_output_flag(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "oflag=nocache"; char *argv[] = { arg0, arg1 }; int argc = 2; scanargs(argc, argv); TEST_ASSERT_TRUE(o_nocache); TEST_ASSERT_FALSE(o_nocache_eof); /* default max_records!=0 */ TEST_ASSERT_TRUE_MESSAGE((output_flags & O_NOCACHE) == 0, "O_NOCACHE should be cleared from output_flags"); } void test_scanargs_warn_partial_read_set_with_bs_and_small_count(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "bs=100"; char arg2[] = "count=1"; char *argv[] = { arg0, arg1, arg2 }; int argc = 3; scanargs(argc, argv); TEST_ASSERT_TRUE_MESSAGE(warn_partial_read, "warn_partial_read should be true when bs is set and counting records"); } void test_scanargs_warn_partial_read_suppressed_by_iflag_fullblock(void) { reset_dd_state(); optind = 1; char arg0[] = "dd"; char arg1[] = "bs=100"; char arg2[] = "count=1"; char arg3[] = "iflag=fullblock"; char *argv[] = { arg0, arg1, arg2, arg3 }; int argc = 4; scanargs(argc, argv); TEST_ASSERT_FALSE_MESSAGE(warn_partial_read, "warn_partial_read should be false when iflag=fullblock is specified"); TEST_ASSERT_EQUAL_PTR((void*)iread_fullblock, (void*)iread_fnc); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_scanargs_defaults_sets_twobufs_and_defaults); RUN_TEST(test_scanargs_bs_sets_both_block_sizes_and_no_twobufs); RUN_TEST(test_scanargs_ibs_and_obs_set_independently_and_twobufs_added); RUN_TEST(test_scanargs_iflag_fullblock_sets_fullblock_reader); RUN_TEST(test_scanargs_skip_with_B_suffix_splits_into_records_and_bytes); RUN_TEST(test_scanargs_skip_without_B_suffix_sets_records_only); RUN_TEST(test_scanargs_count_with_B_suffix_splits_into_records_and_bytes); RUN_TEST(test_scanargs_count_without_B_suffix_sets_records_only); RUN_TEST(test_scanargs_seek_with_B_suffix_splits_into_records_and_bytes); RUN_TEST(test_scanargs_status_progress_and_exclusive_behavior); RUN_TEST(test_scanargs_conv_ucase_and_swab_sets_bits_and_twobufs); RUN_TEST(test_scanargs_iflag_nocache_sets_internal_flags_and_clears_input_flag); RUN_TEST(test_scanargs_oflag_nocache_sets_internal_flags_and_clears_output_flag); RUN_TEST(test_scanargs_warn_partial_read_set_with_bs_and_small_count); RUN_TEST(test_scanargs_warn_partial_read_suppressed_by_iflag_fullblock); return UNITY_END(); }