#include "../../unity/unity.h" #include #include #include #include #include #include #include #include /* The test file is included into the program source, so all internal functions and static globals are in scope here. */ /* Helper: run process_field, capture stdout, and return the output string. Also return the boolean result from process_field via ret_valid. Note: No Unity assertions while stdout is redirected. */ static char* run_and_capture_process_field(const char* input, uintmax_t field, bool* ret_valid) { char* mutable = strdup(input); if (!mutable) return NULL; /* Prepare redirection of stdout */ fflush(stdout); int saved_fd = dup(fileno(stdout)); if (saved_fd < 0) { free(mutable); return NULL; } FILE* tmp = tmpfile(); if (!tmp) { free(mutable); close(saved_fd); return NULL; } int tmp_fd = fileno(tmp); if (dup2(tmp_fd, fileno(stdout)) < 0) { free(mutable); fclose(tmp); close(saved_fd); return NULL; } /* Call the function under test */ bool ok = process_field(mutable, field); /* Collect output */ fflush(stdout); long size = 0; if (fseek(tmp, 0, SEEK_END) == 0) size = ftell(tmp); if (size < 0) size = 0; (void)fseek(tmp, 0, SEEK_SET); char* out = (char*)malloc((size_t)size + 1); if (!out) { /* Restore stdout before returning */ dup2(saved_fd, fileno(stdout)); close(saved_fd); fclose(tmp); free(mutable); return NULL; } if (size > 0) fread(out, 1, (size_t)size, tmp); out[size] = '\0'; /* Restore stdout */ dup2(saved_fd, fileno(stdout)); close(saved_fd); fclose(tmp); free(mutable); if (ret_valid) *ret_valid = ok; return out; } void setUp(void) { /* Initialize globals relied upon by parsing/formatting to sane defaults. */ /* Locale-related parsing */ decimal_point = "."; decimal_point_length = 1; thousands_sep = ""; thousands_sep_length = 0; /* Conversion/formatting defaults */ scale_from = scale_none; scale_to = scale_none; round_style = round_from_zero; inval_style = inval_ignore; /* Avoid fatal exits on invalid inputs during tests */ suffix = NULL; unit_separator = NULL; from_unit_size = 1; to_unit_size = 1; grouping = 0; /* Padding/format defaults */ if (padding_buffer) { free(padding_buffer); padding_buffer = NULL; } padding_buffer_size = 0; padding_width = 0; zero_padding_width = 0; user_precision = -1; format_str = NULL; if (format_str_prefix) { free(format_str_prefix); format_str_prefix = NULL; } if (format_str_suffix) { free(format_str_suffix); format_str_suffix = NULL; } auto_padding = 0; delimiter = NULL; line_delim = '\n'; header = 0; debug = false; dev_debug = false; } void tearDown(void) { if (padding_buffer) { free(padding_buffer); padding_buffer = NULL; } if (format_str_prefix) { free(format_str_prefix); format_str_prefix = NULL; } if (format_str_suffix) { free(format_str_suffix); format_str_suffix = NULL; } } /* Test: field not included (frp == NULL implies only field 1 is included). Here we pass field=2, so the function should not process and should output the original text unchanged, returning true. */ void test_process_field_not_included_prints_unchanged_and_true(void) { bool ok = true; const char* input = "abc"; char* out = run_and_capture_process_field(input, 2, &ok); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_STRING("abc", out); free(out); } /* Test: included field with a valid integer (no suffix, no scaling). Should print the same number and return true. */ void test_process_field_included_valid_integer(void) { bool ok = false; const char* input = "123"; char* out = run_and_capture_process_field(input, 1, &ok); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_STRING("123", out); free(out); } /* Test: included field with invalid input (inval_ignore set in setUp). Should print original text, and return false. */ void test_process_field_included_invalid_input(void) { bool ok = true; /* expect false */ const char* input = "not-a-number"; char* out = run_and_capture_process_field(input, 1, &ok); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(ok); TEST_ASSERT_EQUAL_STRING("not-a-number", out); free(out); } /* Test: suffix trimming on input and suffix addition on output. With suffix="B", input "10B" should be parsed as 10, and output should be "10B". */ void test_process_field_suffix_trim_and_add(void) { suffix = "B"; bool ok = false; const char* input = "10B"; char* out = run_and_capture_process_field(input, 1, &ok); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_STRING("10B", out); free(out); } /* Test: unit conversion using from_unit_size/to_unit_size. With from_unit_size=1024 and to_unit_size=1, input "1" -> "1024". */ void test_process_field_unit_conversion(void) { from_unit_size = 1024; to_unit_size = 1; bool ok = false; const char* input = "1"; char* out = run_and_capture_process_field(input, 1, &ok); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_STRING("1024", out); free(out); } /* Test: auto-padding preserves original field width. Input " 5" (3 spaces then '5') with auto_padding=1 should print " 5". */ void test_process_field_auto_padding_preserves_width(void) { auto_padding = 1; bool ok = false; const char* input = " 5"; char* out = run_and_capture_process_field(input, 1, &ok); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_STRING(" 5", out); free(out); } /* Test: included field where no scaling and precision too large to print. Use many fractional digits so that x + precision_used > LDBL_DIG. Expect function to return false and print original text. */ void test_process_field_too_large_precision_returns_false_and_prints_original(void) { /* Create a number with many fractional digits (e.g., 25) */ const char* input = "0.1234567890123456789012345"; bool ok = true; char* out = run_and_capture_process_field(input, 1, &ok); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_FALSE(ok); TEST_ASSERT_EQUAL_STRING(input, out); free(out); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_process_field_not_included_prints_unchanged_and_true); RUN_TEST(test_process_field_included_valid_integer); RUN_TEST(test_process_field_included_invalid_input); RUN_TEST(test_process_field_suffix_trim_and_add); RUN_TEST(test_process_field_unit_conversion); RUN_TEST(test_process_field_auto_padding_preserves_width); RUN_TEST(test_process_field_too_large_precision_returns_false_and_prints_original); return UNITY_END(); }